Spaces:
Paused
Paused
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| """ | |
| G4F Chat API Server โ FastAPI + g4f + Streaming SSE | |
| """ | |
| import g4f | |
| import asyncio | |
| import json | |
| from fastapi import FastAPI, Request, HTTPException | |
| from fastapi.responses import HTMLResponse, StreamingResponse, JSONResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pathlib import Path | |
| import uvicorn | |
| import logging | |
| # ุฅุนุฏุงุฏ ุงูุณุฌูุงุช | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| app = FastAPI(title="G4F Chat API", version="2.1") | |
| # โ ุชูุนูู CORS | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # โ ุฏุงูุฉ ุฌูุจ ุงูู ุฒูุฏูู ูุงููู ุงุฐุฌ (ุขู ูุฉ) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def get_providers_and_models(): | |
| """ุฌูุจ ูุงุฆู ุฉ ุงูู ุฒูุฏูู ุงูุนุงู ููู ู ุน ูู ุงุฐุฌูู """ | |
| providers_data = {} | |
| try: | |
| # ู ุญุงููุฉ ุฌูุจ ุงูู ุฒูุฏูู ู ู g4f | |
| provider_list = getattr(g4f.Provider, '__providers__', []) | |
| for provider in provider_list: | |
| try: | |
| name = getattr(provider, '__name__', None) | |
| if not name or not getattr(provider, 'working', False): | |
| continue | |
| # ุฌูุจ ุงููู ุงุฐุฌ ุงูู ุฏุนูู ุฉ | |
| models = [] | |
| if hasattr(provider, 'models') and provider.models: | |
| models = list(provider.models) if isinstance(provider.models, (list, tuple)) else [provider.models] | |
| elif hasattr(provider, 'model') and provider.model: | |
| models = [provider.model] if isinstance(provider.model, str) else list(provider.model) | |
| else: | |
| models = ['default'] | |
| providers_data[name] = { | |
| 'models': models, | |
| 'supports_stream': getattr(provider, 'supports_stream', True), | |
| 'needs_auth': getattr(provider, 'needs_auth', False), | |
| 'url': getattr(provider, 'url', ''), | |
| } | |
| except Exception as e: | |
| logger.warning(f"ุชุฎุทู ุงูู ุฒููุฏ: {e}") | |
| continue | |
| except Exception as e: | |
| logger.error(f"ุฎุทุฃ ูู ุฌูุจ ุงูู ุฒูุฏูู: {e}") | |
| # ูุงุฆู ุฉ ุงุญุชูุงุทูุฉ ูู ุญุงู ุงููุดู | |
| return { | |
| 'g4f': { | |
| 'models': ['gpt-4o', 'gpt-4o-mini', 'claude-3-5-sonnet', 'llama-3.1-70b'], | |
| 'supports_stream': True, | |
| 'needs_auth': False, | |
| 'url': '', | |
| } | |
| } | |
| return providers_data | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # โ Endpoint: ูุงุฆู ุฉ ุงูู ุฒูุฏูู ูุงููู ุงุฐุฌ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| async def list_providers(): | |
| """ุฅุฑุฌุงุน ุงูู ุฒูุฏูู ูุงููู ุงุฐุฌ ุงูู ุชุงุญุฉ ุจุตูุบุฉ JSON""" | |
| try: | |
| data = get_providers_and_models() | |
| return JSONResponse(content=data) | |
| except Exception as e: | |
| logger.error(f"Error in /api/providers: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # โ Endpoint: ุงูุฏุฑุฏุดุฉ ุงูุนุงุฏูุฉ (Non-streaming) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| async def chat(request: Request): | |
| """ู ุนุงูุฌุฉ ุทูุจ ุงูุฏุฑุฏุดุฉ ุงูุนุงุฏู""" | |
| try: | |
| body = await request.json() | |
| messages = body.get("messages", []) | |
| model = body.get("model", "gpt-4o-mini") | |
| provider_name = body.get("provider", None) | |
| web_search = body.get("web_search", False) | |
| if not messages: | |
| raise HTTPException(status_code=400, detail="messages list is empty") | |
| # ุชุญุถูุฑ ู ุนุงู ูุงุช ุงูุงุณุชุฏุนุงุก | |
| kwargs = { | |
| "model": model, | |
| "messages": messages, | |
| "stream": False, | |
| } | |
| # ุฅุถุงูุฉ ุงูู ุฒููุฏ ุฅุฐุง ุญูุฏูุฏ | |
| if provider_name and provider_name != "Auto": | |
| try: | |
| provider_cls = getattr(g4f.Provider, provider_name, None) | |
| if provider_cls: | |
| kwargs["provider"] = provider_cls | |
| except AttributeError: | |
| pass | |
| # ุฅุถุงูุฉ ุฃุฏุงุฉ ุงูุจุญุซ ุฅุฐุง ููุนููุช | |
| if web_search and hasattr(g4f, 'tools') and hasattr(g4f.tools, 'search'): | |
| kwargs["tool_calls"] = [g4f.tools.search] | |
| # ุชูููุฐ ุงูุทูุจ ูู thread ู ููุตู ูุชุฌูุจ ุญุฌุจ ุงูู event loop | |
| loop = asyncio.get_running_loop() | |
| response = await loop.run_in_executor( | |
| None, | |
| lambda: g4f.ChatCompletion.create(**kwargs) | |
| ) | |
| # ู ุนุงูุฌุฉ ุงูุฑุฏ (ูุฏ ูููู ูุตุงู ุฃู ู ูููุฏุงู) | |
| if hasattr(response, '__iter__') and not isinstance(response, (str, bytes)): | |
| response = "".join(str(chunk) for chunk in response if chunk) | |
| return JSONResponse({ | |
| "response": str(response), | |
| "model": model, | |
| "provider": provider_name or "Auto" | |
| }) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Chat error: {e}", exc_info=True) | |
| raise HTTPException(status_code=500, detail=f"Server error: {str(e)[:200]}") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # โ Endpoint: ุงูุฏุฑุฏุดุฉ ุงูู ุชุฏููุฉ (Streaming SSE) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| async def chat_stream(request: Request): | |
| """ู ุนุงูุฌุฉ ุทูุจ ุงูุฏุฑุฏุดุฉ ู ุน ุงูุจุซ ุงูู ุจุงุดุฑ (Server-Sent Events)""" | |
| try: | |
| body = await request.json() | |
| messages = body.get("messages", []) | |
| model = body.get("model", "gpt-4o-mini") | |
| provider_name = body.get("provider", None) | |
| web_search = body.get("web_search", False) | |
| if not messages: | |
| raise HTTPException(status_code=400, detail="messages list is empty") | |
| # ุชุญุถูุฑ ู ุนุงู ูุงุช ุงูุงุณุชุฏุนุงุก ููุจุซ | |
| kwargs = { | |
| "model": model, | |
| "messages": messages, | |
| "stream": True, | |
| } | |
| if provider_name and provider_name != "Auto": | |
| try: | |
| provider_cls = getattr(g4f.Provider, provider_name, None) | |
| if provider_cls: | |
| kwargs["provider"] = provider_cls | |
| except AttributeError: | |
| pass | |
| if web_search and hasattr(g4f, 'tools') and hasattr(g4f.tools, 'search'): | |
| kwargs["tool_calls"] = [g4f.tools.search] | |
| async def event_generator(): | |
| """ู ูููุฏ ุฃุญุฏุงุซ SSE""" | |
| try: | |
| loop = asyncio.get_running_loop() | |
| # ุชูููุฐ g4f ูู thread ู ููุตู | |
| response = await loop.run_in_executor( | |
| None, | |
| lambda: g4f.ChatCompletion.create(**kwargs) | |
| ) | |
| # ู ุนุงูุฌุฉ ุงูุฑุฏ ุญุณุจ ููุนู | |
| if isinstance(response, str): | |
| # ุฑุฏ ูุตู ู ุจุงุดุฑ | |
| yield f" {json.dumps({'chunk': response, 'type': 'text'})}\n\n" | |
| elif hasattr(response, '__iter__'): | |
| # ุฑุฏ ู ุชุฏูู (generator) | |
| for chunk in response: | |
| if chunk: | |
| chunk_str = str(chunk).strip() | |
| if chunk_str: | |
| yield f" {json.dumps({'chunk': chunk_str, 'type': 'chunk'})}\n\n" | |
| await asyncio.sleep(0) # Yield control to event loop | |
| else: | |
| # ู ุนุงูุฌุฉ ุบูุฑ ู ุชููุนุฉ | |
| yield f" {json.dumps({'chunk': str(response), 'type': 'text'})}\n\n" | |
| except Exception as e: | |
| logger.error(f"Stream error: {e}", exc_info=True) | |
| yield f" {json.dumps({'error': str(e)[:300], 'type': 'error'})}\n\n" | |
| finally: | |
| # ุฅุดุงุฑุฉ ููุงูุฉ ุงูุจุซ | |
| yield " [DONE]\n\n" | |
| return StreamingResponse( | |
| event_generator(), | |
| media_type="text/event-stream", | |
| headers={ | |
| "Cache-Control": "no-cache, no-transform", | |
| "X-Accel-Buffering": "no", | |
| "Connection": "keep-alive", | |
| }, | |
| ) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Stream endpoint error: {e}", exc_info=True) | |
| raise HTTPException(status_code=500, detail=f"Streaming error: {str(e)[:200]}") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # โ ุชูุฏูู ุงููุงุฌูุฉ ุงูุฃู ุงู ูุฉ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| async def root(): | |
| """ุชูุฏูู ู ูู index.html""" | |
| html_path = Path(__file__).parent / "index.html" | |
| if html_path.exists(): | |
| content = html_path.read_text(encoding="utf-8") | |
| return HTMLResponse(content=content) | |
| return HTMLResponse( | |
| content="<h1 style='color:#fff;background:#111;padding:20px'>โ index.html not found</h1>", | |
| status_code=404 | |
| ) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # โ ููุทุฉ ุงูุฏุฎูู | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| if __name__ == "__main__": | |
| import sys | |
| port = int(sys.argv[1]) if len(sys.argv) > 1 else 7860 | |
| host = "0.0.0.0" | |
| print(f"\n๐ G4F Chat Server starting...") | |
| print(f"๐ URL: http://{host if host != '0.0.0.0' else 'localhost'}:{port}") | |
| print(f"๐ฆ g4f version: {getattr(g4f, '__version__', 'unknown')}") | |
| print(f"โก Press Ctrl+C to stop\n") | |
| uvicorn.run( | |
| app, | |
| host=host, | |
| port=port, | |
| log_level="info", | |
| access_log=True, | |
| ) |