bahi-bh commited on
Commit
7b6bfa6
·
verified ·
1 Parent(s): 94bcda1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +306 -130
app.py CHANGED
@@ -1,221 +1,397 @@
1
- import time
2
- import random
3
- import traceback
4
-
5
- from fastapi import FastAPI, Header
6
  from pydantic import BaseModel
7
- import uvicorn
 
 
 
 
 
8
 
 
9
  from g4f.client import Client
10
- from g4f.Provider import __providers__
11
 
12
- MODEL = "gpt-4o-mini"
13
 
14
- MAX_RETRIES = 10
15
- TIMEOUT = 60
 
16
 
17
- OPENAI_API_KEY = "sk-your-key"
 
18
 
19
- SAFE_PROVIDERS = []
20
 
21
- for provider in __providers__:
 
 
22
 
23
- try:
24
 
25
- name = provider.__name__.lower()
26
 
27
- if not getattr(provider, "working", False):
28
- continue
 
29
 
30
- if getattr(provider, "needs_auth", False):
31
- continue
 
 
32
 
33
- if getattr(provider, "use_nodriver", False):
34
- continue
35
 
36
- blocked = [
37
- "openai",
38
- "qwen",
39
- "copilot",
40
- "gemini",
41
- "claude",
42
- ]
43
 
44
- if any(x in name for x in blocked):
45
- continue
 
 
 
 
 
46
 
47
- SAFE_PROVIDERS.append(provider)
48
 
49
- except:
50
- pass
 
51
 
52
- random.shuffle(SAFE_PROVIDERS)
 
 
53
 
54
- print(f"[+] SAFE PROVIDERS: {len(SAFE_PROVIDERS)}")
55
 
56
- client = Client()
 
 
 
 
 
57
 
58
 
59
- class SmartG4F:
 
 
60
 
61
- def __init__(self):
62
 
63
- self.good = []
64
- self.bad = {}
65
 
66
- def mark_bad(self, provider, cooldown=300):
 
 
67
 
68
- self.bad[provider.__name__] = time.time() + cooldown
 
 
 
 
69
 
70
- def is_bad(self, provider):
71
 
72
- expire = self.bad.get(provider.__name__)
 
 
 
 
73
 
74
- if not expire:
75
- return False
76
 
77
- if time.time() > expire:
78
- del self.bad[provider.__name__]
79
- return False
80
 
81
- return True
 
 
82
 
83
- def provider_pool(self):
 
84
 
85
- providers = []
 
 
 
 
86
 
87
- providers.extend(self.good)
88
 
89
- for p in SAFE_PROVIDERS:
 
 
90
 
91
- if p not in providers and not self.is_bad(p):
92
- providers.append(p)
93
 
94
- random.shuffle(providers)
95
 
96
- return providers
97
 
98
- def ask(self, prompt):
99
 
100
- errors = []
101
 
102
- for attempt in range(MAX_RETRIES):
103
 
104
- pool = self.provider_pool()
 
 
 
 
 
105
 
106
- if not pool:
107
 
108
- return {
109
- "success": False,
110
- "error": "No providers available"
111
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- provider = random.choice(pool)
114
 
115
- print(f"[TRY {attempt+1}] {provider.__name__}")
116
 
117
  try:
118
 
 
 
119
  response = client.chat.completions.create(
120
- model=MODEL,
121
- provider=provider,
122
- messages=[
123
- {
124
- "role": "user",
125
- "content": prompt
126
- }
127
- ],
128
- timeout=TIMEOUT,
129
  )
130
 
131
- text = response.choices[0].message.content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
- if not text:
134
- raise Exception("Empty response")
 
 
 
 
 
 
135
 
136
- if provider not in self.good:
137
- self.good.append(provider)
138
 
139
- return {
140
- "success": True,
141
- "provider": provider.__name__,
142
- "response": text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  }
144
 
 
 
 
 
 
 
 
 
145
  except Exception as e:
146
 
147
- err = str(e).lower()
 
 
 
 
 
 
 
 
 
148
 
149
- traceback.print_exc()
 
 
 
 
150
 
151
- errors.append({
152
- "provider": provider.__name__,
153
- "error": str(e)
154
- })
155
 
156
- if "429" in err:
 
 
 
 
 
 
 
 
157
 
158
- self.mark_bad(provider, 600)
159
- time.sleep(random.randint(10, 20))
 
160
 
161
- elif "cloudflare" in err:
162
 
163
- self.mark_bad(provider, 1200)
164
- time.sleep(20)
165
 
166
- elif "timeout" in err:
 
 
 
 
167
 
168
- self.mark_bad(provider, 300)
169
 
170
- else:
171
 
172
- self.mark_bad(provider, 180)
 
 
 
 
 
173
 
174
- continue
175
 
176
- return {
177
- "success": False,
178
- "errors": errors
179
- }
180
 
 
181
 
182
- app = FastAPI()
 
 
183
 
184
- ai = SmartG4F()
185
 
 
186
 
187
- class Query(BaseModel):
188
- prompt: str
189
 
 
 
 
 
 
 
 
 
 
 
190
 
191
- @app.get("/")
192
- async def home():
 
 
 
193
 
194
- return {
195
- "status": "running",
196
- "providers": len(SAFE_PROVIDERS)
197
- }
198
 
 
199
 
200
- @app.post("/ask")
201
- async def ask(
202
- query: Query,
203
- authorization: str = Header(None)
204
- ):
205
 
206
- if authorization != f"Bearer {OPENAI_API_KEY}":
207
- return {
208
- "success": False,
209
- "error": "Invalid API Key"
210
- }
211
 
212
- return ai.ask(query.prompt)
213
 
 
 
 
214
 
215
  if __name__ == "__main__":
216
 
 
 
217
  uvicorn.run(
218
  app,
219
  host="0.0.0.0",
220
- port=7860
 
221
  )
 
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
6
+
7
+ import json
8
+ import time
9
+ import uuid
10
+ import logging
11
 
12
+ import g4f
13
  from g4f.client import Client
 
14
 
 
15
 
16
+ # =====================================================
17
+ # LOGGING
18
+ # =====================================================
19
 
20
+ logging.basicConfig(level=logging.INFO)
21
+ logger = logging.getLogger(__name__)
22
 
 
23
 
24
+ # =====================================================
25
+ # CONFIG
26
+ # =====================================================
27
 
28
+ API_KEY = "sk-your-secret-key"
29
 
 
30
 
31
+ # =====================================================
32
+ # FASTAPI
33
+ # =====================================================
34
 
35
+ app = FastAPI(
36
+ title="Universal AI Gateway",
37
+ version="5.3.0"
38
+ )
39
 
 
 
40
 
41
+ # =====================================================
42
+ # CORS
43
+ # =====================================================
 
 
 
 
44
 
45
+ app.add_middleware(
46
+ CORSMiddleware,
47
+ allow_origins=["*"],
48
+ allow_credentials=True,
49
+ allow_methods=["*"],
50
+ allow_headers=["*"],
51
+ )
52
 
 
53
 
54
+ # =====================================================
55
+ # MODELS
56
+ # =====================================================
57
 
58
+ class Message(BaseModel):
59
+ role: str
60
+ content: str
61
 
 
62
 
63
+ class ChatRequest(BaseModel):
64
+ model: str
65
+ messages: List[Message]
66
+ stream: bool = False
67
+ temperature: Optional[float] = 0.7
68
+ max_tokens: Optional[int] = 4096
69
 
70
 
71
+ # =====================================================
72
+ # AUTH
73
+ # =====================================================
74
 
75
+ def verify_api_key(req: Request):
76
 
77
+ auth = req.headers.get("Authorization")
 
78
 
79
+ # السماح بدون مفتاح للاختبار
80
+ if not auth:
81
+ return True
82
 
83
+ if not auth.startswith("Bearer "):
84
+ raise HTTPException(
85
+ status_code=401,
86
+ detail="Invalid Authorization Format"
87
+ )
88
 
89
+ token = auth.replace("Bearer ", "").strip()
90
 
91
+ if token != API_KEY:
92
+ raise HTTPException(
93
+ status_code=403,
94
+ detail="Invalid API Key"
95
+ )
96
 
97
+ return True
 
98
 
 
 
 
99
 
100
+ # =====================================================
101
+ # ROOT
102
+ # =====================================================
103
 
104
+ @app.get("/")
105
+ async def root():
106
 
107
+ return {
108
+ "status": "online",
109
+ "service": "Universal AI Gateway",
110
+ "version": "5.3.0"
111
+ }
112
 
 
113
 
114
+ # =====================================================
115
+ # MODELS
116
+ # =====================================================
117
 
118
+ @app.get("/v1/models")
119
+ async def get_models():
120
 
121
+ models_data = []
122
 
123
+ try:
124
 
125
+ if hasattr(g4f.models, "_all_models"):
126
 
127
+ all_models = list(g4f.models._all_models)
128
 
129
+ for model in all_models:
130
 
131
+ models_data.append({
132
+ "id": str(model),
133
+ "object": "model",
134
+ "created": int(time.time()),
135
+ "owned_by": "g4f"
136
+ })
137
 
138
+ except Exception as e:
139
 
140
+ logger.error(f"Models error: {e}")
141
+
142
+ return {
143
+ "object": "list",
144
+ "data": models_data
145
+ }
146
+
147
+
148
+ # =====================================================
149
+ # CHAT COMPLETIONS
150
+ # =====================================================
151
+
152
+ @app.post("/v1/chat/completions")
153
+ async def chat_completions(
154
+ req: Request,
155
+ body: ChatRequest
156
+ ):
157
+
158
+ verify_api_key(req)
159
+
160
+ messages = [
161
+ {
162
+ "role": m.role,
163
+ "content": m.content
164
+ }
165
+ for m in body.messages
166
+ ]
167
+
168
+ logger.info(
169
+ f"Request model={body.model} stream={body.stream}"
170
+ )
171
+
172
+ # =================================================
173
+ # STREAMING
174
+ # =================================================
175
+
176
+ if body.stream:
177
 
178
+ def generate_stream():
179
 
180
+ chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
181
 
182
  try:
183
 
184
+ client = Client()
185
+
186
  response = client.chat.completions.create(
187
+ model=body.model,
188
+ messages=messages,
189
+ stream=True
 
 
 
 
 
 
190
  )
191
 
192
+ has_content = False
193
+
194
+ for chunk in response:
195
+
196
+ try:
197
+
198
+ content = ""
199
+
200
+ if (
201
+ hasattr(chunk, "choices")
202
+ and chunk.choices
203
+ and len(chunk.choices) > 0
204
+ and hasattr(chunk.choices[0], "delta")
205
+ and chunk.choices[0].delta
206
+ and chunk.choices[0].delta.content
207
+ ):
208
+ content = chunk.choices[0].delta.content
209
+
210
+ if not content:
211
+ continue
212
+
213
+ has_content = True
214
+
215
+ payload = {
216
+ "id": chunk_id,
217
+ "object": "chat.completion.chunk",
218
+ "created": int(time.time()),
219
+ "model": body.model,
220
+ "choices": [
221
+ {
222
+ "index": 0,
223
+ "delta": {
224
+ "content": content
225
+ },
226
+ "finish_reason": None
227
+ }
228
+ ]
229
+ }
230
 
231
+ yield (
232
+ "data: "
233
+ + json.dumps(
234
+ payload,
235
+ ensure_ascii=False
236
+ )
237
+ + "\n\n"
238
+ )
239
 
240
+ except Exception as chunk_error:
 
241
 
242
+ logger.error(
243
+ f"Chunk error: {chunk_error}"
244
+ )
245
+
246
+ if not has_content:
247
+
248
+ error_payload = {
249
+ "error": {
250
+ "message": "Provider returned empty stream",
251
+ "type": "empty_stream"
252
+ }
253
+ }
254
+
255
+ yield (
256
+ "data: "
257
+ + json.dumps(error_payload)
258
+ + "\n\n"
259
+ )
260
+
261
+ final_payload = {
262
+ "id": chunk_id,
263
+ "object": "chat.completion.chunk",
264
+ "created": int(time.time()),
265
+ "model": body.model,
266
+ "choices": [
267
+ {
268
+ "index": 0,
269
+ "delta": {},
270
+ "finish_reason": "stop"
271
+ }
272
+ ]
273
  }
274
 
275
+ yield (
276
+ "data: "
277
+ + json.dumps(final_payload)
278
+ + "\n\n"
279
+ )
280
+
281
+ yield "data: [DONE]\n\n"
282
+
283
  except Exception as e:
284
 
285
+ logger.error(
286
+ f"Streaming error: {e}"
287
+ )
288
+
289
+ error_payload = {
290
+ "error": {
291
+ "message": str(e),
292
+ "type": "server_error"
293
+ }
294
+ }
295
 
296
+ yield (
297
+ "data: "
298
+ + json.dumps(error_payload)
299
+ + "\n\n"
300
+ )
301
 
302
+ yield "data: [DONE]\n\n"
 
 
 
303
 
304
+ return StreamingResponse(
305
+ generate_stream(),
306
+ media_type="text/event-stream",
307
+ headers={
308
+ "Cache-Control": "no-cache",
309
+ "Connection": "keep-alive",
310
+ "X-Accel-Buffering": "no"
311
+ }
312
+ )
313
 
314
+ # =================================================
315
+ # NORMAL RESPONSE
316
+ # =================================================
317
 
318
+ try:
319
 
320
+ client = Client()
 
321
 
322
+ response = client.chat.completions.create(
323
+ model=body.model,
324
+ messages=messages,
325
+ stream=False
326
+ )
327
 
328
+ assistant_message = ""
329
 
330
+ try:
331
 
332
+ assistant_message = (
333
+ response
334
+ .choices[0]
335
+ .message
336
+ .content
337
+ )
338
 
339
+ except Exception:
340
 
341
+ assistant_message = str(response)
 
 
 
342
 
343
+ return JSONResponse({
344
 
345
+ "id": (
346
+ f"chatcmpl-{uuid.uuid4().hex}"
347
+ ),
348
 
349
+ "object": "chat.completion",
350
 
351
+ "created": int(time.time()),
352
 
353
+ "model": body.model,
 
354
 
355
+ "choices": [
356
+ {
357
+ "index": 0,
358
+ "message": {
359
+ "role": "assistant",
360
+ "content": assistant_message
361
+ },
362
+ "finish_reason": "stop"
363
+ }
364
+ ],
365
 
366
+ "usage": {
367
+ "prompt_tokens": 0,
368
+ "completion_tokens": 0,
369
+ "total_tokens": 0
370
+ }
371
 
372
+ })
 
 
 
373
 
374
+ except Exception as e:
375
 
376
+ logger.error(f"Chat error: {e}")
 
 
 
 
377
 
378
+ raise HTTPException(
379
+ status_code=500,
380
+ detail=str(e)
381
+ )
 
382
 
 
383
 
384
+ # =====================================================
385
+ # RUN
386
+ # =====================================================
387
 
388
  if __name__ == "__main__":
389
 
390
+ import uvicorn
391
+
392
  uvicorn.run(
393
  app,
394
  host="0.0.0.0",
395
+ port=7860,
396
+ log_level="info"
397
  )