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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -75
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, FileResponse
4
  from pydantic import BaseModel
5
  from typing import List, Optional, AsyncGenerator
6
  import json
@@ -8,34 +8,44 @@ import time
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,6 +57,7 @@ app.add_middleware(
47
  # =====================================================
48
  # MODELS
49
  # =====================================================
 
50
  class Message(BaseModel):
51
  role: str
52
  content: str
@@ -59,113 +70,128 @@ class ChatRequest(BaseModel):
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,38 +204,38 @@ async def chat_completions(req: Request, body: ChatRequest):
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,20 +243,22 @@ async def chat_completions(req: Request, body: ChatRequest):
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
- )
 
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
  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
  # =====================================================
58
  # MODELS
59
  # =====================================================
60
+
61
  class Message(BaseModel):
62
  role: str
63
  content: str
 
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
  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
  }
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
+ )