bahi-bh commited on
Commit
d9707fb
·
verified ·
1 Parent(s): 3526d27

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -0
app.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import g4f
3
+ import logging
4
+ from fastapi import FastAPI, HTTPException, Header, Request
5
+ from fastapi.responses import StreamingResponse
6
+ from fastapi.middleware.cors import CORSMiddleware
7
+ from pydantic import BaseModel
8
+ from typing import List, Optional
9
+
10
+ # --- Rate Limiting (Proxy Aware) ---
11
+ from slowapi import Limiter, _rate_limit_exceeded_handler
12
+ from slowapi.errors import RateLimitExceeded
13
+
14
+ def get_real_ip(request: Request) -> str:
15
+ """استخراج عنوان IP الحقيقي للمستخدم خلف خوادم Hugging Face"""
16
+ forwarded_for = request.headers.get("X-Forwarded-For")
17
+ if forwarded_for:
18
+ return forwarded_for.split(",")[0].strip()
19
+ return request.client.host if request.client else "127.0.0.1"
20
+
21
+ limiter = Limiter(key_func=get_real_ip)
22
+
23
+ # --- Logging ---
24
+ logging.basicConfig(level=logging.INFO)
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # --- App Setup ---
28
+ app = FastAPI(title="G4F Gateway", version="1.0", docs_url=None, redoc_url=None) # إخفاء مسارات التوثيق في الإنتاج لتقليل السطح الهجومي
29
+ app.state.limiter = limiter
30
+ app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
31
+
32
+ API_KEY = os.getenv("API_KEY", "your_fallback_secret")
33
+
34
+ app.add_middleware(
35
+ CORSMiddleware,
36
+ allow_origins=["*"],
37
+ allow_credentials=False,
38
+ allow_methods=["*"],
39
+ allow_headers=["*"],
40
+ )
41
+
42
+ # --- Models ---
43
+ class ChatMessage(BaseModel):
44
+ role: str
45
+ content: str
46
+
47
+ class ChatRequest(BaseModel):
48
+ model: str = "gpt-4o-mini"
49
+ messages: List[ChatMessage]
50
+ stream: bool = False
51
+ provider: Optional[str] = None
52
+
53
+ # --- Auth ---
54
+ def verify_key(auth: str):
55
+ if not auth:
56
+ raise HTTPException(status_code=401, detail="Missing Authorization header")
57
+
58
+ parts = auth.split()
59
+ if len(parts) != 2 or parts[0] != "Bearer":
60
+ raise HTTPException(status_code=401, detail="Malformed Authorization header")
61
+
62
+ if parts[1] != API_KEY:
63
+ raise HTTPException(status_code=401, detail="Invalid API Key")
64
+
65
+ # --- Routes ---
66
+ @app.post("/v1/chat/completions")
67
+ @limiter.limit("20/minute")
68
+ async def chat(request: Request, body: ChatRequest, authorization: str = Header(None)):
69
+ verify_key(authorization)
70
+
71
+ messages = [{"role": m.role, "content": m.content} for m in body.messages]
72
+
73
+ provider = None
74
+ if body.provider:
75
+ provider = getattr(g4f.Provider, body.provider, None)
76
+ if provider is None:
77
+ raise HTTPException(status_code=400, detail="Invalid provider")
78
+
79
+ try:
80
+ # التعامل الآمن مع Streaming
81
+ if body.stream:
82
+ def generate():
83
+ try:
84
+ response = g4f.ChatCompletion.create(
85
+ model=body.model,
86
+ messages=messages,
87
+ provider=provider,
88
+ stream=True
89
+ )
90
+ for chunk in response:
91
+ if chunk:
92
+ yield f"data: {chunk}\n\n"
93
+ yield "data: [DONE]\n\n"
94
+ except Exception as stream_e:
95
+ logger.error(f"Streaming failure: {stream_e}")
96
+ yield f"data: {{\"error\": \"Provider stream disconnected\"}}\n\n"
97
+
98
+ return StreamingResponse(generate(), media_type="text/event-stream")
99
+
100
+ # الاستجابة العادية (Asynchronous)
101
+ response = await g4f.ChatCompletion.create_async(
102
+ model=body.model,
103
+ messages=messages,
104
+ provider=provider
105
+ )
106
+
107
+ return {
108
+ "id": "chatcmpl-custom",
109
+ "object": "chat.completion",
110
+ "choices": [
111
+ {
112
+ "index": 0,
113
+ "message": {
114
+ "role": "assistant",
115
+ "content": response
116
+ },
117
+ "finish_reason": "stop"
118
+ }
119
+ ]
120
+ }
121
+
122
+ except Exception as e:
123
+ logger.exception(f"G4F Exception: {e}")
124
+ raise HTTPException(status_code=503, detail="Provider currently unavailable")
125
+
126
+ @app.get("/")
127
+ @limiter.limit("5/minute")
128
+ def health(request: Request):
129
+ return {"status": "online"}