bahi-bh commited on
Commit
7f65c09
·
verified ·
1 Parent(s): c1213b9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -103
app.py CHANGED
@@ -1,6 +1,6 @@
1
  from fastapi import FastAPI, Request, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
3
- from fastapi.responses import StreamingResponse, JSONResponse
4
  from pydantic import BaseModel
5
  from typing import List, Optional, AsyncGenerator
6
  import json
@@ -8,44 +8,34 @@ import time
8
  import uuid
9
  import logging
10
  import asyncio
 
11
 
12
- # استيراد g4f بالطريقة الصحيحة
13
  import g4f
14
  from g4f import ChatCompletion
15
- from g4f.Provider import (
16
- Bing,
17
- OpenaiChat,
18
- Gemini,
19
- You,
20
- AIChat
21
- )
22
 
23
  # =====================================================
24
  # LOGGING
25
  # =====================================================
26
-
27
  logging.basicConfig(level=logging.INFO)
28
  logger = logging.getLogger(__name__)
29
 
30
  # =====================================================
31
  # CONFIG
32
  # =====================================================
33
-
34
- API_KEY = "sk-your-secret-key" # غيّر هذا
35
 
36
  # =====================================================
37
  # FASTAPI
38
  # =====================================================
39
-
40
  app = FastAPI(
41
- title="Universal AI Gateway",
42
- version="5.3.0"
43
  )
44
 
45
  # =====================================================
46
  # CORS
47
  # =====================================================
48
-
49
  app.add_middleware(
50
  CORSMiddleware,
51
  allow_origins=["*"],
@@ -57,7 +47,6 @@ app.add_middleware(
57
  # =====================================================
58
  # MODELS
59
  # =====================================================
60
-
61
  class Message(BaseModel):
62
  role: str
63
  content: str
@@ -70,128 +59,113 @@ class ChatRequest(BaseModel):
70
  max_tokens: Optional[int] = 4096
71
 
72
  # =====================================================
73
- # AUTH
74
  # =====================================================
75
-
76
  def verify_api_key(req: Request):
77
- # للاختبار: السماح بدون مفتاح
78
  auth = req.headers.get("Authorization")
79
- if not auth:
80
- return True
81
 
82
- if not auth.startswith("Bearer "):
83
- raise HTTPException(status_code=401, detail="Invalid Authorization Format")
 
84
 
85
- token = auth.replace("Bearer ", "").strip()
86
- if token != API_KEY:
 
 
 
 
 
87
  raise HTTPException(status_code=403, detail="Invalid API Key")
88
 
89
  return True
90
 
91
  # =====================================================
92
- # ROOT
93
  # =====================================================
94
-
95
  @app.get("/")
96
- async def root():
97
- return {
98
- "status": "online",
99
- "service": "Universal AI Gateway",
100
- "version": "5.3.0",
101
- "providers": ["Bing", "OpenaiChat", "Gemini", "You"]
102
- }
103
 
104
  # =====================================================
105
- # MODELS
106
  # =====================================================
107
-
108
  @app.get("/v1/models")
109
  async def get_models():
110
- # قائمة النماذج المدعومة فعلياً
111
  models_data = [
112
  {"id": "gpt-3.5-turbo", "object": "model", "owned_by": "g4f"},
113
  {"id": "gpt-4", "object": "model", "owned_by": "g4f"},
114
  {"id": "gpt-4o-mini", "object": "model", "owned_by": "g4f"},
115
- {"id": "gemini", "object": "model", "owned_by": "g4f"},
 
 
116
  ]
117
-
118
- return {
119
- "object": "list",
120
- "data": models_data
121
- }
122
 
123
  # =====================================================
124
  # CHAT COMPLETIONS
125
  # =====================================================
126
-
127
  @app.post("/v1/chat/completions")
128
- async def chat_completions(
129
- req: Request,
130
- body: ChatRequest
131
- ):
132
- # التحقق من المفتاح
133
  verify_api_key(req)
134
 
135
- # تحويل الرسائل
136
- messages = [
137
- {"role": m.role, "content": m.content}
138
- for m in body.messages
139
- ]
140
 
141
  logger.info(f"Request: model={body.model}, stream={body.stream}")
142
- logger.info(f"Messages: {messages}")
143
 
144
  # =================================================
145
  # STREAMING
146
  # =================================================
147
-
148
  if body.stream:
149
  async def generate_stream() -> AsyncGenerator[str, None]:
150
  chunk_id = f"chatcmpl-{uuid.uuid4().hex[:8]}"
151
 
152
  try:
153
- # استخدام ChatCompletion.create مع stream=True
154
- response = await ChatCompletion.create_async(
155
- model=body.model,
156
- messages=messages,
157
- stream=True,
158
- provider=Bing # جرب Bing أو OpenaiChat
 
 
159
  )
160
 
161
  async for chunk in response:
162
  if chunk:
 
 
 
163
  payload = {
164
  "id": chunk_id,
165
  "object": "chat.completion.chunk",
166
  "created": int(time.time()),
167
  "model": body.model,
168
- "choices": [
169
- {
170
- "index": 0,
171
- "delta": {"content": chunk},
172
- "finish_reason": None
173
- }
174
- ]
175
  }
176
  yield f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
177
 
178
- # إرسال إشارة النهاية
179
  final_payload = {
180
  "id": chunk_id,
181
  "object": "chat.completion.chunk",
182
  "created": int(time.time()),
183
  "model": body.model,
184
- "choices": [
185
- {
186
- "index": 0,
187
- "delta": {},
188
- "finish_reason": "stop"
189
- }
190
- ]
191
  }
192
  yield f"data: {json.dumps(final_payload)}\n\n"
193
  yield "data: [DONE]\n\n"
194
 
 
 
 
 
 
195
  except Exception as e:
196
  logger.error(f"Streaming error: {e}", exc_info=True)
197
  error_msg = {"error": {"message": str(e), "type": "stream_error"}}
@@ -204,38 +178,38 @@ async def chat_completions(
204
  headers={
205
  "Cache-Control": "no-cache",
206
  "Connection": "keep-alive",
207
- "Access-Control-Allow-Origin": "*"
208
  }
209
  )
210
 
211
  # =================================================
212
  # NORMAL RESPONSE
213
  # =================================================
214
-
215
  try:
216
- # استخدام ChatCompletion.create_async
217
- response = await ChatCompletion.create_async(
218
- model=body.model,
219
- messages=messages,
220
- stream=False,
221
- provider=Bing # جرب مزوداً مختلفاً إذا فشل
 
222
  )
223
 
 
 
 
224
  return JSONResponse({
225
  "id": f"chatcmpl-{uuid.uuid4().hex[:8]}",
226
  "object": "chat.completion",
227
  "created": int(time.time()),
228
  "model": body.model,
229
- "choices": [
230
- {
231
- "index": 0,
232
- "message": {
233
- "role": "assistant",
234
- "content": response
235
- },
236
- "finish_reason": "stop"
237
- }
238
- ],
239
  "usage": {
240
  "prompt_tokens": 0,
241
  "completion_tokens": 0,
@@ -243,22 +217,20 @@ async def chat_completions(
243
  }
244
  })
245
 
 
 
246
  except Exception as e:
247
  logger.error(f"Chat error: {e}", exc_info=True)
248
- raise HTTPException(
249
- status_code=500,
250
- detail=f"Provider error: {str(e)}"
251
- )
252
 
253
  # =====================================================
254
  # RUN
255
  # =====================================================
256
-
257
  if __name__ == "__main__":
258
  import uvicorn
259
  uvicorn.run(
260
  app,
261
  host="0.0.0.0",
262
- port=7860,
263
  log_level="info"
264
- )
 
1
  from fastapi import FastAPI, Request, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import StreamingResponse, JSONResponse, FileResponse
4
  from pydantic import BaseModel
5
  from typing import List, Optional, AsyncGenerator
6
  import json
 
8
  import uuid
9
  import logging
10
  import asyncio
11
+ import os
12
 
13
+ # استيراد g4f
14
  import g4f
15
  from g4f import ChatCompletion
 
 
 
 
 
 
 
16
 
17
  # =====================================================
18
  # LOGGING
19
  # =====================================================
 
20
  logging.basicConfig(level=logging.INFO)
21
  logger = logging.getLogger(__name__)
22
 
23
  # =====================================================
24
  # CONFIG
25
  # =====================================================
26
+ API_KEY = os.environ.get("API_KEY", "sk-your-secret-key") # يدعم متغيرات البيئة في HF
 
27
 
28
  # =====================================================
29
  # FASTAPI
30
  # =====================================================
 
31
  app = FastAPI(
32
+ title="9Router AI Gateway",
33
+ version="6.0.0"
34
  )
35
 
36
  # =====================================================
37
  # CORS
38
  # =====================================================
 
39
  app.add_middleware(
40
  CORSMiddleware,
41
  allow_origins=["*"],
 
47
  # =====================================================
48
  # MODELS
49
  # =====================================================
 
50
  class Message(BaseModel):
51
  role: str
52
  content: str
 
59
  max_tokens: Optional[int] = 4096
60
 
61
  # =====================================================
62
+ # AUTH (متوافق مع الواجهة)
63
  # =====================================================
 
64
  def verify_api_key(req: Request):
 
65
  auth = req.headers.get("Authorization")
66
+ cf_auth = req.headers.get("cf-aig-authorization") # دعم Cloudflare Tunnel من الواجهة
 
67
 
68
+ # إذا لم يتم إرسال أي مفتاح، نسمح بالمرور (للاختبار المحلي)
69
+ if not auth and not cf_auth:
70
+ return True
71
 
72
+ token = ""
73
+ if auth and auth.startswith("Bearer "):
74
+ token = auth.replace("Bearer ", "").strip()
75
+ elif cf_auth and cf_auth.startswith("Bearer "):
76
+ token = cf_auth.replace("Bearer ", "").strip()
77
+
78
+ if token and token != API_KEY:
79
  raise HTTPException(status_code=403, detail="Invalid API Key")
80
 
81
  return True
82
 
83
  # =====================================================
84
+ # SERVE UI (تقديم واجهة 9Router)
85
  # =====================================================
 
86
  @app.get("/")
87
+ async def serve_ui():
88
+ # هذا سيقوم بعرض ملف index.html تلقائياً عند فتح رابط الـ Space
89
+ return FileResponse("index.html")
 
 
 
 
90
 
91
  # =====================================================
92
+ # API MODELS
93
  # =====================================================
 
94
  @app.get("/v1/models")
95
  async def get_models():
 
96
  models_data = [
97
  {"id": "gpt-3.5-turbo", "object": "model", "owned_by": "g4f"},
98
  {"id": "gpt-4", "object": "model", "owned_by": "g4f"},
99
  {"id": "gpt-4o-mini", "object": "model", "owned_by": "g4f"},
100
+ {"id": "gpt-4o", "object": "model", "owned_by": "g4f"},
101
+ {"id": "claude-3-haiku", "object": "model", "owned_by": "g4f"},
102
+ {"id": "gemini-pro", "object": "model", "owned_by": "g4f"},
103
  ]
104
+ return {"object": "list", "data": models_data}
 
 
 
 
105
 
106
  # =====================================================
107
  # CHAT COMPLETIONS
108
  # =====================================================
 
109
  @app.post("/v1/chat/completions")
110
+ async def chat_completions(req: Request, body: ChatRequest):
 
 
 
 
111
  verify_api_key(req)
112
 
113
+ messages = [{"role": m.role, "content": m.content} for m in body.messages]
 
 
 
 
114
 
115
  logger.info(f"Request: model={body.model}, stream={body.stream}")
 
116
 
117
  # =================================================
118
  # STREAMING
119
  # =================================================
 
120
  if body.stream:
121
  async def generate_stream() -> AsyncGenerator[str, None]:
122
  chunk_id = f"chatcmpl-{uuid.uuid4().hex[:8]}"
123
 
124
  try:
125
+ # إضافة Timeout وإزالة المزود الثابت ليختار g4f الأفضل
126
+ response = await asyncio.wait_for(
127
+ ChatCompletion.create_async(
128
+ model=body.model,
129
+ messages=messages,
130
+ stream=True,
131
+ ),
132
+ timeout=60.0
133
  )
134
 
135
  async for chunk in response:
136
  if chunk:
137
+ # إجبار التحويل إلى نص لمنع انهيار JSON
138
+ content = str(chunk) if not isinstance(chunk, str) else chunk
139
+
140
  payload = {
141
  "id": chunk_id,
142
  "object": "chat.completion.chunk",
143
  "created": int(time.time()),
144
  "model": body.model,
145
+ "choices": [{
146
+ "index": 0,
147
+ "delta": {"content": content},
148
+ "finish_reason": None
149
+ }]
 
 
150
  }
151
  yield f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
152
 
153
+ # إشارة النهاية
154
  final_payload = {
155
  "id": chunk_id,
156
  "object": "chat.completion.chunk",
157
  "created": int(time.time()),
158
  "model": body.model,
159
+ "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]
 
 
 
 
 
 
160
  }
161
  yield f"data: {json.dumps(final_payload)}\n\n"
162
  yield "data: [DONE]\n\n"
163
 
164
+ except asyncio.TimeoutError:
165
+ logger.error("Streaming timeout")
166
+ error_msg = {"error": {"message": "Provider timeout", "type": "timeout"}}
167
+ yield f"data: {json.dumps(error_msg)}\n\n"
168
+ yield "data: [DONE]\n\n"
169
  except Exception as e:
170
  logger.error(f"Streaming error: {e}", exc_info=True)
171
  error_msg = {"error": {"message": str(e), "type": "stream_error"}}
 
178
  headers={
179
  "Cache-Control": "no-cache",
180
  "Connection": "keep-alive",
 
181
  }
182
  )
183
 
184
  # =================================================
185
  # NORMAL RESPONSE
186
  # =================================================
 
187
  try:
188
+ response = await asyncio.wait_for(
189
+ ChatCompletion.create_async(
190
+ model=body.model,
191
+ messages=messages,
192
+ stream=False,
193
+ ),
194
+ timeout=90.0
195
  )
196
 
197
+ # إجبار التحول لنص
198
+ response_text = str(response) if not isinstance(response, str) else response
199
+
200
  return JSONResponse({
201
  "id": f"chatcmpl-{uuid.uuid4().hex[:8]}",
202
  "object": "chat.completion",
203
  "created": int(time.time()),
204
  "model": body.model,
205
+ "choices": [{
206
+ "index": 0,
207
+ "message": {
208
+ "role": "assistant",
209
+ "content": response_text
210
+ },
211
+ "finish_reason": "stop"
212
+ }],
 
 
213
  "usage": {
214
  "prompt_tokens": 0,
215
  "completion_tokens": 0,
 
217
  }
218
  })
219
 
220
+ except asyncio.TimeoutError:
221
+ raise HTTPException(status_code=504, detail="Provider timed out")
222
  except Exception as e:
223
  logger.error(f"Chat error: {e}", exc_info=True)
224
+ raise HTTPException(status_code=500, detail=f"Provider error: {str(e)}")
 
 
 
225
 
226
  # =====================================================
227
  # RUN
228
  # =====================================================
 
229
  if __name__ == "__main__":
230
  import uvicorn
231
  uvicorn.run(
232
  app,
233
  host="0.0.0.0",
234
+ port=7860, # المنفذ المطلوب لـ Hugging Face
235
  log_level="info"
236
+ )