Spaces:
Running
Running
| """ | |
| Sixfinger Backend API - FRONTEND UYUMLU VERSİYON | |
| Ultra-fast AI Chat Backend with Multi-Model Support | |
| Supports: Groq, DeepInfra, LLM7.io | |
| """ | |
| import os | |
| import time | |
| import json | |
| import logging | |
| import requests | |
| from typing import Optional, Dict, Any, Generator | |
| from datetime import datetime | |
| from fastapi import FastAPI, HTTPException, Header, Request | |
| from fastapi.responses import StreamingResponse, JSONResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel, Field | |
| from groq import Groq | |
| from openai import OpenAI | |
| # ========== CONFIGURATION ========== | |
| API_VERSION = "1.1.0" | |
| GROQ_API_KEY = os.getenv("GROQ_API_KEY", "") | |
| # ========== API PROVIDERS ========== | |
| PROVIDERS = { | |
| "groq": { | |
| "name": "Groq", | |
| "type": "groq", | |
| "requires_key": True | |
| }, | |
| "deepinfra": { | |
| "name": "DeepInfra", | |
| "type": "deepinfra", | |
| "base_url": "https://api.deepinfra.com/v1/openai/chat/completions", | |
| "requires_key": False | |
| }, | |
| "llm7": { | |
| "name": "LLM7.io", | |
| "type": "openai_compatible", | |
| "base_url": "https://api.llm7.io/v1", | |
| "api_key": "unused", | |
| "requires_key": False | |
| } | |
| } | |
| # ========== MODEL MAPPING ========== | |
| MODELS = { | |
| # ============ FREE PLAN MODELS ============ | |
| # Groq Models (Free) | |
| "llama-8b-instant": { | |
| "provider": "groq", | |
| "model_id": "llama-3.1-8b-instant", | |
| "display_name": "Llama 3.1 8B Instant", | |
| "size": "8B", | |
| "language": "Multilingual", | |
| "speed": "⚡⚡⚡", | |
| "description": "Hızlı ve hafif genel amaçlı model", | |
| "plans": ["free", "starter", "pro", "plus"], | |
| "daily_limit": 14400 | |
| }, | |
| "allam-2-7b": { | |
| "provider": "groq", | |
| "model_id": "llama-3.1-8b-instant", | |
| "display_name": "Allam 2 7B", | |
| "size": "7B", | |
| "language": "Turkish/Arabic", | |
| "speed": "⚡⚡", | |
| "description": "Türkçe ve Arapça optimizeli model", | |
| "plans": ["free", "starter", "pro", "plus"], | |
| "daily_limit": 300 | |
| }, | |
| # DeepInfra Models (Free) | |
| "llama4-maverick": { | |
| "provider": "deepinfra", | |
| "model_id": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-Turbo", | |
| "display_name": "Llama 4 Maverick 17B", | |
| "size": "17B", | |
| "language": "Multilingual", | |
| "speed": "⚡⚡", | |
| "description": "Meta'nın en yeni hızlı ve yetenekli modeli", | |
| "plans": ["free", "starter", "pro", "plus"], | |
| "daily_limit": 1000 | |
| }, | |
| "qwen3-coder": { | |
| "provider": "deepinfra", | |
| "model_id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo", | |
| "display_name": "Qwen3 Coder 480B", | |
| "size": "480B", | |
| "language": "Multilingual", | |
| "speed": "⚡", | |
| "description": "Kod yazma uzmanı dev model", | |
| "plans": ["free", "starter", "pro", "plus"], | |
| "daily_limit": 500 | |
| }, | |
| "deepseek-r1": { | |
| "provider": "deepinfra", | |
| "model_id": "deepseek-ai/DeepSeek-R1-0528-Turbo", | |
| "display_name": "DeepSeek R1 Turbo", | |
| "size": "Unknown", | |
| "language": "Multilingual", | |
| "speed": "⚡", | |
| "description": "Muhakeme ve zeka odaklı model", | |
| "plans": ["free", "starter", "pro", "plus"], | |
| "daily_limit": 500 | |
| }, | |
| # ============ STARTER PLAN MODELS ============ | |
| # LLM7.io Models (Starter+) | |
| "gpt4-nano": { | |
| "provider": "llm7", | |
| "model_id": "gpt-4.1-nano-2025-04-14", | |
| "display_name": "GPT-4.1 Nano", | |
| "size": "Nano", | |
| "language": "Multilingual", | |
| "speed": "⚡⚡⚡", | |
| "description": "OpenAI GPT-4 tabanlı hızlı model", | |
| "plans": ["starter", "pro", "plus"], | |
| "daily_limit": 1000 | |
| }, | |
| # Groq Models (Starter+) | |
| "qwen3-32b": { | |
| "provider": "groq", | |
| "model_id": "llama-3.3-70b-versatile", | |
| "display_name": "Qwen3 32B", | |
| "size": "32B", | |
| "language": "Turkish/Chinese", | |
| "speed": "⚡⚡", | |
| "description": "Türkçe ve Çince optimize edilmiş model", | |
| "plans": ["starter", "pro", "plus"], | |
| "daily_limit": 1000 | |
| }, | |
| "llama-70b": { | |
| "provider": "groq", | |
| "model_id": "llama-3.3-70b-versatile", | |
| "display_name": "Llama 3.3 70B", | |
| "size": "70B", | |
| "language": "Multilingual", | |
| "speed": "⚡⚡", | |
| "description": "Güçlü ve çok yönlü büyük model", | |
| "plans": ["starter", "pro", "plus"], | |
| "daily_limit": 1000 | |
| }, | |
| "llama-maverick-17b": { | |
| "provider": "groq", | |
| "model_id": "llama-3.1-8b-instant", | |
| "display_name": "Llama Maverick 17B", | |
| "size": "17B", | |
| "language": "Multilingual", | |
| "speed": "⚡⚡", | |
| "description": "Deneysel maverick model", | |
| "plans": ["starter", "pro", "plus"], | |
| "daily_limit": 1000 | |
| }, | |
| "llama-scout-17b": { | |
| "provider": "groq", | |
| "model_id": "llama-3.1-8b-instant", | |
| "display_name": "Llama Scout 17B", | |
| "size": "17B", | |
| "language": "Multilingual", | |
| "speed": "⚡⚡⚡", | |
| "description": "Keşif odaklı hızlı model", | |
| "plans": ["starter", "pro", "plus"], | |
| "daily_limit": 1000 | |
| }, | |
| "gpt-oss-20b": { | |
| "provider": "groq", | |
| "model_id": "llama-3.1-8b-instant", | |
| "display_name": "GPT-OSS 20B", | |
| "size": "20B", | |
| "language": "Multilingual", | |
| "speed": "⚡⚡", | |
| "description": "Açık kaynak GPT alternatifleri", | |
| "plans": ["starter", "pro", "plus"], | |
| "daily_limit": 1000 | |
| }, | |
| # ============ PRO PLAN MODELS ============ | |
| "gpt-oss-120b": { | |
| "provider": "groq", | |
| "model_id": "llama-3.3-70b-versatile", | |
| "display_name": "GPT-OSS 120B", | |
| "size": "120B", | |
| "language": "Multilingual", | |
| "speed": "⚡⚡", | |
| "description": "En büyük açık kaynak model", | |
| "plans": ["pro", "plus"], | |
| "daily_limit": 1000 | |
| }, | |
| "kimi-k2": { | |
| "provider": "groq", | |
| "model_id": "llama-3.3-70b-versatile", | |
| "display_name": "Kimi K2", | |
| "size": "Unknown", | |
| "language": "Chinese", | |
| "speed": "⚡⚡", | |
| "description": "Çince uzmanı güçlü model", | |
| "plans": ["pro", "plus"], | |
| "daily_limit": 1000 | |
| } | |
| } | |
| # Plan bazlı otomatik model seçimi | |
| DEFAULT_MODELS = { | |
| "free": "llama4-maverick", | |
| "starter": "gpt4-nano", | |
| "pro": "llama-70b", | |
| "plus": "gpt-oss-120b" | |
| } | |
| # ========== LOGGING ========== | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='[%(asctime)s] %(levelname)s: %(message)s', | |
| datefmt='%Y-%m-%d %H:%M:%S' | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # ========== FASTAPI APP ========== | |
| app = FastAPI( | |
| title="Sixfinger Backend API", | |
| version=API_VERSION, | |
| description="Ultra-fast AI Chat Backend with Multi-Provider Support", | |
| docs_url="/docs", | |
| redoc_url="/redoc" | |
| ) | |
| # CORS | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # ========== API CLIENTS ========== | |
| # Groq Client | |
| groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None | |
| # LLM7 Client | |
| llm7_client = OpenAI( | |
| base_url=PROVIDERS["llm7"]["base_url"], | |
| api_key=PROVIDERS["llm7"]["api_key"] | |
| ) | |
| # ========== PYDANTIC MODELS ========== | |
| class ChatRequest(BaseModel): | |
| prompt: str = Field(..., description="User's message") | |
| max_tokens: int = Field(default=300, ge=1, le=4000) | |
| temperature: float = Field(default=0.7, ge=0, le=2) | |
| top_p: float = Field(default=0.9, ge=0, le=1) | |
| system_prompt: Optional[str] = None | |
| history: Optional[list] = None | |
| class ChatResponse(BaseModel): | |
| response: str | |
| model: str | |
| model_key: str | |
| model_size: str | |
| model_language: str | |
| provider: str | |
| attempts: int | |
| usage: Dict[str, int] | |
| parameters: Dict[str, Any] | |
| # ========== HELPER FUNCTIONS ========== | |
| def get_allowed_models(plan: str) -> list: | |
| """Plan'a göre izin verilen modelleri döndür""" | |
| return [k for k, v in MODELS.items() if plan in v["plans"]] | |
| def select_model(plan: str, preferred_model: Optional[str] = None) -> str: | |
| """Model seçimi yap""" | |
| allowed_models = get_allowed_models(plan) | |
| if preferred_model and preferred_model in allowed_models: | |
| return preferred_model | |
| default = DEFAULT_MODELS.get(plan, "llama-8b-instant") | |
| return default if default in allowed_models else allowed_models[0] | |
| def build_messages(prompt: str, system_prompt: Optional[str], history: Optional[list]) -> list: | |
| """Chat messages listesi oluştur""" | |
| messages = [] | |
| if system_prompt: | |
| messages.append({"role": "system", "content": system_prompt}) | |
| else: | |
| messages.append({"role": "system", "content": "Sen yardımcı bir asistansın. Adın SixFinger."}) | |
| if history: | |
| for msg in history: | |
| if "role" in msg and "content" in msg: | |
| messages.append(msg) | |
| messages.append({"role": "user", "content": prompt}) | |
| return messages | |
| # ========== PROVIDER-SPECIFIC API CALLS ========== | |
| def call_groq_api( | |
| model_id: str, | |
| messages: list, | |
| max_tokens: int, | |
| temperature: float, | |
| top_p: float, | |
| stream: bool = False | |
| ): | |
| """Groq API'ye istek at""" | |
| if not groq_client: | |
| raise HTTPException(status_code=500, detail="Groq API key not configured") | |
| try: | |
| response = groq_client.chat.completions.create( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=max_tokens, | |
| temperature=temperature, | |
| top_p=top_p, | |
| stream=stream | |
| ) | |
| return response | |
| except Exception as e: | |
| logger.error(f"Groq API error: {e}") | |
| raise HTTPException(status_code=500, detail=f"Groq API error: {str(e)}") | |
| def call_deepinfra_api( | |
| model_id: str, | |
| messages: list, | |
| max_tokens: int, | |
| temperature: float, | |
| top_p: float, | |
| stream: bool = False | |
| ) -> Dict[str, Any]: | |
| """DeepInfra API'ye istek at (non-streaming)""" | |
| url = PROVIDERS["deepinfra"]["base_url"] | |
| headers = { | |
| "Content-Type": "application/json", | |
| "X-Deepinfra-Source": "web-page" | |
| } | |
| data = { | |
| "model": model_id, | |
| "messages": messages, | |
| "max_tokens": max_tokens, | |
| "temperature": temperature, | |
| "top_p": top_p, | |
| "stream": stream | |
| } | |
| try: | |
| if stream: | |
| return requests.post(url, headers=headers, json=data, stream=True) | |
| else: | |
| response = requests.post(url, headers=headers, json=data) | |
| response.raise_for_status() | |
| return response.json() | |
| except Exception as e: | |
| logger.error(f"DeepInfra API error: {e}") | |
| raise HTTPException(status_code=500, detail=f"DeepInfra API error: {str(e)}") | |
| def call_llm7_api( | |
| model_id: str, | |
| messages: list, | |
| max_tokens: int, | |
| temperature: float, | |
| top_p: float, | |
| stream: bool = False | |
| ): | |
| """LLM7.io API'ye istek at""" | |
| try: | |
| response = llm7_client.chat.completions.create( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=max_tokens, | |
| temperature=temperature, | |
| top_p=top_p, | |
| stream=stream | |
| ) | |
| return response | |
| except Exception as e: | |
| logger.error(f"LLM7 API error: {e}") | |
| raise HTTPException(status_code=500, detail=f"LLM7 API error: {str(e)}") | |
| def call_api( | |
| provider: str, | |
| model_id: str, | |
| messages: list, | |
| max_tokens: int, | |
| temperature: float, | |
| top_p: float, | |
| stream: bool = False | |
| ): | |
| """Universal API caller - provider'a göre yönlendir""" | |
| if provider == "groq": | |
| return call_groq_api(model_id, messages, max_tokens, temperature, top_p, stream) | |
| elif provider == "deepinfra": | |
| return call_deepinfra_api(model_id, messages, max_tokens, temperature, top_p, stream) | |
| elif provider == "llm7": | |
| return call_llm7_api(model_id, messages, max_tokens, temperature, top_p, stream) | |
| else: | |
| raise HTTPException(status_code=400, detail=f"Unknown provider: {provider}") | |
| # ========== ENDPOINTS ========== | |
| def health_check(): | |
| """Health check endpoint""" | |
| return { | |
| "status": "healthy", | |
| "version": API_VERSION, | |
| "timestamp": datetime.now().isoformat(), | |
| "providers": { | |
| "groq": bool(GROQ_API_KEY), | |
| "deepinfra": True, | |
| "llm7": True | |
| } | |
| } | |
| def chat( | |
| request: ChatRequest, | |
| x_user_plan: str = Header(default="free", alias="X-User-Plan"), | |
| x_model: Optional[str] = Header(default=None, alias="X-Model") | |
| ): | |
| """ | |
| Normal chat endpoint (JSON response) | |
| Frontend'e TAM UYUMLU format | |
| """ | |
| start_time = time.time() | |
| # Model seçimi | |
| model_key = select_model(x_user_plan, x_model) | |
| model_config = MODELS[model_key] | |
| provider = model_config["provider"] | |
| model_id = model_config["model_id"] | |
| logger.info(f"Chat request: plan={x_user_plan}, model={model_key}, provider={provider}") | |
| # Messages | |
| messages = build_messages( | |
| request.prompt, | |
| request.system_prompt, | |
| request.history | |
| ) | |
| try: | |
| # Provider'a göre API call | |
| if provider == "deepinfra": | |
| # DeepInfra non-streaming response | |
| response_data = call_api( | |
| provider=provider, | |
| model_id=model_id, | |
| messages=messages, | |
| max_tokens=request.max_tokens, | |
| temperature=request.temperature, | |
| top_p=request.top_p, | |
| stream=False | |
| ) | |
| content = response_data["choices"][0]["message"]["content"] | |
| usage = response_data.get("usage", {}) | |
| usage = { | |
| "prompt_tokens": usage.get("prompt_tokens", 0), | |
| "completion_tokens": usage.get("completion_tokens", 0), | |
| "total_tokens": usage.get("total_tokens", 0) | |
| } | |
| else: | |
| # Groq veya LLM7 response | |
| response = call_api( | |
| provider=provider, | |
| model_id=model_id, | |
| messages=messages, | |
| max_tokens=request.max_tokens, | |
| temperature=request.temperature, | |
| top_p=request.top_p, | |
| stream=False | |
| ) | |
| content = response.choices[0].message.content | |
| usage = { | |
| "prompt_tokens": getattr(response.usage, 'prompt_tokens', 0), | |
| "completion_tokens": getattr(response.usage, 'completion_tokens', 0), | |
| "total_tokens": getattr(response.usage, 'total_tokens', 0) | |
| } | |
| elapsed = time.time() - start_time | |
| logger.info(f"Chat completed: provider={provider}, tokens={usage['total_tokens']}, time={elapsed:.2f}s") | |
| return { | |
| "response": content, | |
| "model": model_id, | |
| "model_key": model_key, | |
| "model_size": model_config["size"], | |
| "model_language": model_config["language"], | |
| "provider": provider, | |
| "attempts": 1, | |
| "usage": usage, | |
| "parameters": { | |
| "max_tokens": request.max_tokens, | |
| "temperature": request.temperature, | |
| "top_p": request.top_p | |
| } | |
| } | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Chat error: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| def chat_stream( | |
| request: ChatRequest, | |
| x_user_plan: str = Header(default="free", alias="X-User-Plan"), | |
| x_model: Optional[str] = Header(default=None, alias="X-Model") | |
| ): | |
| """ | |
| Streaming chat endpoint (SSE) | |
| Tüm provider'ları destekler | |
| """ | |
| model_key = select_model(x_user_plan, x_model) | |
| model_config = MODELS[model_key] | |
| provider = model_config["provider"] | |
| model_id = model_config["model_id"] | |
| logger.info(f"Stream request: plan={x_user_plan}, model={model_key}, provider={provider}") | |
| messages = build_messages( | |
| request.prompt, | |
| request.system_prompt, | |
| request.history | |
| ) | |
| def generate_groq(): | |
| """Groq streaming generator""" | |
| try: | |
| yield f"data: {json.dumps({'info': f'Using {model_key} via Groq'})}\n\n" | |
| response = call_groq_api( | |
| model_id=model_id, | |
| messages=messages, | |
| max_tokens=request.max_tokens, | |
| temperature=request.temperature, | |
| top_p=request.top_p, | |
| stream=True | |
| ) | |
| total_tokens = 0 | |
| prompt_tokens = 0 | |
| completion_tokens = 0 | |
| for chunk in response: | |
| if chunk.choices[0].delta.content: | |
| text = chunk.choices[0].delta.content | |
| yield f"data: {json.dumps({'text': text})}\n\n" | |
| if hasattr(chunk, 'x_groq') and hasattr(chunk.x_groq, 'usage'): | |
| usage_data = chunk.x_groq.usage | |
| prompt_tokens = getattr(usage_data, 'prompt_tokens', 0) | |
| completion_tokens = getattr(usage_data, 'completion_tokens', 0) | |
| total_tokens = getattr(usage_data, 'total_tokens', 0) | |
| yield f"data: {json.dumps({'done': True, 'model_key': model_key, 'provider': 'groq', 'attempts': 1, 'usage': {'prompt_tokens': prompt_tokens, 'completion_tokens': completion_tokens, 'total_tokens': total_tokens}})}\n\n" | |
| except Exception as e: | |
| logger.error(f"Groq stream error: {e}") | |
| yield f"data: {json.dumps({'error': str(e)})}\n\n" | |
| def generate_deepinfra(): | |
| """DeepInfra streaming generator""" | |
| try: | |
| yield f"data: {json.dumps({'info': f'Using {model_key} via DeepInfra'})}\n\n" | |
| url = PROVIDERS["deepinfra"]["base_url"] | |
| headers = { | |
| "Content-Type": "application/json", | |
| "X-Deepinfra-Source": "web-page" | |
| } | |
| data = { | |
| "model": model_id, | |
| "messages": messages, | |
| "max_tokens": request.max_tokens, | |
| "temperature": request.temperature, | |
| "top_p": request.top_p, | |
| "stream": True | |
| } | |
| response = requests.post(url, headers=headers, json=data, stream=True) | |
| total_completion_tokens = 0 | |
| for line in response.iter_lines(): | |
| if line: | |
| decoded = line.decode('utf-8') | |
| if decoded.startswith("data: "): | |
| content = decoded[6:] | |
| if content == "[DONE]": | |
| break | |
| try: | |
| json_data = json.loads(content) | |
| delta = json_data.get("choices", [{}])[0].get("delta", {}) | |
| if "content" in delta: | |
| token = delta["content"] | |
| yield f"data: {json.dumps({'text': token})}\n\n" | |
| total_completion_tokens += 1 | |
| except json.JSONDecodeError: | |
| continue | |
| yield f"data: {json.dumps({'done': True, 'model_key': model_key, 'provider': 'deepinfra', 'attempts': 1, 'usage': {'prompt_tokens': 0, 'completion_tokens': total_completion_tokens, 'total_tokens': total_completion_tokens}})}\n\n" | |
| except Exception as e: | |
| logger.error(f"DeepInfra stream error: {e}") | |
| yield f"data: {json.dumps({'error': str(e)})}\n\n" | |
| def generate_llm7(): | |
| """LLM7.io streaming generator""" | |
| try: | |
| yield f"data: {json.dumps({'info': f'Using {model_key} via LLM7.io'})}\n\n" | |
| stream = llm7_client.chat.completions.create( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=request.max_tokens, | |
| temperature=request.temperature, | |
| top_p=request.top_p, | |
| stream=True | |
| ) | |
| total_completion_tokens = 0 | |
| for chunk in stream: | |
| if chunk.choices[0].delta.content: | |
| text = chunk.choices[0].delta.content | |
| yield f"data: {json.dumps({'text': text})}\n\n" | |
| total_completion_tokens += 1 | |
| yield f"data: {json.dumps({'done': True, 'model_key': model_key, 'provider': 'llm7', 'attempts': 1, 'usage': {'prompt_tokens': 0, 'completion_tokens': total_completion_tokens, 'total_tokens': total_completion_tokens}})}\n\n" | |
| except Exception as e: | |
| logger.error(f"LLM7 stream error: {e}") | |
| yield f"data: {json.dumps({'error': str(e)})}\n\n" | |
| # Provider'a göre generator seç | |
| if provider == "groq": | |
| generator = generate_groq() | |
| elif provider == "deepinfra": | |
| generator = generate_deepinfra() | |
| elif provider == "llm7": | |
| generator = generate_llm7() | |
| else: | |
| raise HTTPException(status_code=400, detail=f"Unknown provider: {provider}") | |
| return StreamingResponse( | |
| generator, | |
| media_type="text/event-stream", | |
| headers={ | |
| "Cache-Control": "no-cache", | |
| "X-Accel-Buffering": "no", | |
| "Connection": "keep-alive" | |
| } | |
| ) | |
| def list_models(x_user_plan: str = Header(default="free", alias="X-User-Plan")): | |
| """ | |
| Kullanıcının erişebileceği modelleri listele | |
| """ | |
| allowed_models = get_allowed_models(x_user_plan) | |
| models_info = [] | |
| for model_key in allowed_models: | |
| config = MODELS[model_key] | |
| models_info.append({ | |
| "key": model_key, | |
| "display_name": config.get("display_name", model_key), | |
| "size": config["size"], | |
| "language": config["language"], | |
| "speed": config["speed"], | |
| "description": config.get("description", ""), | |
| "provider": config["provider"], | |
| "daily_limit": config["daily_limit"] | |
| }) | |
| # Provider'a göre grupla | |
| grouped = {} | |
| for model in models_info: | |
| provider = model["provider"] | |
| if provider not in grouped: | |
| grouped[provider] = [] | |
| grouped[provider].append(model) | |
| return { | |
| "plan": x_user_plan, | |
| "total_models": len(models_info), | |
| "models": models_info, | |
| "models_by_provider": grouped, | |
| "default_model": DEFAULT_MODELS.get(x_user_plan, "llama-8b-instant"), | |
| "providers": list(grouped.keys()) | |
| } | |
| def list_providers(): | |
| """Mevcut API provider'larını listele""" | |
| return { | |
| "providers": [ | |
| { | |
| "id": "groq", | |
| "name": "Groq", | |
| "status": "active" if GROQ_API_KEY else "inactive", | |
| "description": "Ultra-fast inference with Groq LPU" | |
| }, | |
| { | |
| "id": "deepinfra", | |
| "name": "DeepInfra", | |
| "status": "active", | |
| "description": "Free tier AI models - Llama 4, Qwen3 Coder, DeepSeek" | |
| }, | |
| { | |
| "id": "llm7", | |
| "name": "LLM7.io", | |
| "status": "active", | |
| "description": "GPT-4 based models - Free tier available" | |
| } | |
| ] | |
| } | |
| async def http_exception_handler(request: Request, exc: HTTPException): | |
| """Custom HTTP exception handler""" | |
| return JSONResponse( | |
| status_code=exc.status_code, | |
| content={ | |
| "error": exc.detail, | |
| "status_code": exc.status_code | |
| } | |
| ) | |
| async def general_exception_handler(request: Request, exc: Exception): | |
| """General exception handler""" | |
| logger.error(f"Unhandled exception: {exc}") | |
| return JSONResponse( | |
| status_code=500, | |
| content={ | |
| "error": "Internal server error", | |
| "detail": str(exc) | |
| } | |
| ) | |
| # ========== STARTUP/SHUTDOWN ========== | |
| async def startup_event(): | |
| logger.info("🚀 Sixfinger Backend API started") | |
| logger.info(f"📦 Version: {API_VERSION}") | |
| logger.info(f"🔑 Groq API: {'✅ Configured' if GROQ_API_KEY else '❌ Not configured'}") | |
| logger.info(f"🌐 DeepInfra: ✅ Active (Free tier)") | |
| logger.info(f"🌐 LLM7.io: ✅ Active (Free tier)") | |
| logger.info(f"🤖 Total Models: {len(MODELS)}") | |
| # Plan başına model sayısı | |
| for plan in ["free", "starter", "pro", "plus"]: | |
| count = len(get_allowed_models(plan)) | |
| logger.info(f" └─ {plan.upper()} plan: {count} models") | |
| async def shutdown_event(): | |
| logger.info("👋 Sixfinger Backend API shutting down") | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run( | |
| "main:app", | |
| host="0.0.0.0", | |
| port=8000, | |
| reload=True, | |
| log_level="info" | |
| ) |