bahi-bh commited on
Commit
ac3850e
·
verified ·
1 Parent(s): 0f4cc05

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +274 -240
main.py CHANGED
@@ -1,12 +1,9 @@
1
  import os
2
  import json
3
  import time
4
- import inspect
5
- import asyncio
6
  import logging
7
-
8
  import g4f
9
- import g4f.Provider as Provider
10
 
11
  from fastapi import FastAPI, HTTPException, Header
12
  from fastapi.responses import StreamingResponse
@@ -14,26 +11,39 @@ from fastapi.middleware.cors import CORSMiddleware
14
  from pydantic import BaseModel
15
  from typing import List, Optional
16
 
17
- logging.basicConfig(level=logging.INFO)
 
 
 
 
 
 
 
18
  logger=logging.getLogger(__name__)
19
 
20
- app=FastAPI(title="G4F Dynamic API")
 
 
 
 
 
 
21
 
22
  app.add_middleware(
23
  CORSMiddleware,
24
  allow_origins=["*"],
 
25
  allow_methods=["*"],
26
  allow_headers=["*"]
27
  )
28
 
29
  API_KEY=os.getenv(
30
  "API_KEY",
31
- "secret"
32
  )
33
 
34
- # ======================
35
- # Models
36
- # ======================
37
 
38
  class ChatMessage(BaseModel):
39
  role:str
@@ -41,385 +51,409 @@ class ChatMessage(BaseModel):
41
 
42
 
43
  class ChatRequest(BaseModel):
44
-
45
- model:str="gpt-4o"
46
-
47
  messages:List[ChatMessage]
48
-
49
  stream:bool=False
50
-
51
  provider:Optional[str]=None
 
 
52
 
53
 
54
- # ======================
55
- # auth
56
- # ======================
57
 
58
- def verify(auth):
59
-
60
- if auth!=f"Bearer {API_KEY}":
61
 
 
62
  raise HTTPException(
63
  status_code=401,
64
- detail="Unauthorized"
65
  )
66
 
 
67
 
68
- # ======================
69
- # provider discovery
70
- # ======================
71
-
72
- SKIP={
73
 
74
- "BaseProvider",
75
- "RetryProvider",
76
- "AsyncProvider",
77
- "IterListProvider",
78
- "ProviderType",
79
- "CreateResult"
80
 
81
- }
 
 
 
 
82
 
83
 
84
- PROVIDERS={}
85
- MODEL_MAP={}
86
 
 
87
 
88
- def collect_models(cls):
89
 
90
- found=[]
91
 
92
- attrs=[
93
 
94
- "default_model",
95
- "model",
96
- "models",
97
- "text_models",
98
- "vision_models"
99
 
100
- ]
101
 
102
- for attr in attrs:
 
 
 
 
103
 
104
- v=getattr(
105
- cls,
106
- attr,
107
- None
108
- )
109
 
110
- if not v:
111
- continue
 
 
 
112
 
113
- if isinstance(v,str):
114
 
115
- found.append(v)
 
 
 
 
116
 
117
- elif isinstance(v,(list,tuple,set)):
118
 
119
- found.extend(
120
- [str(x) for x in v]
 
 
121
  )
122
 
123
- elif isinstance(v,dict):
124
 
125
- found.extend(
126
- list(v.keys())
 
 
127
  )
128
 
129
- return list(
130
- set(found)
131
- )
132
 
 
133
 
134
- def build():
135
 
136
- global PROVIDERS
137
- global MODEL_MAP
138
 
139
- PROVIDERS={}
140
- MODEL_MAP={}
141
 
142
- for name in dir(Provider):
 
 
143
 
144
- if name.startswith("_"):
145
- continue
146
 
147
- if name in SKIP:
148
- continue
149
 
150
- try:
 
 
 
151
 
152
- cls=getattr(
153
- Provider,
154
- name
155
- )
 
 
 
 
 
 
 
 
156
 
157
- if not inspect.isclass(cls):
158
- continue
159
-
160
- if not bool(
161
- getattr(
162
- cls,
163
- "working",
164
- False
165
- )
166
- ):
167
- continue
168
-
169
- if bool(
170
- getattr(
171
- cls,
172
- "needs_auth",
173
- False
174
- )
175
- ):
176
- continue
177
-
178
- models=collect_models(
179
- cls
180
  )
181
 
182
- if not models:
183
- continue
 
184
 
185
- PROVIDERS[name]=models
186
 
187
- for m in models:
188
 
189
- if m not in MODEL_MAP:
 
 
 
 
 
190
 
191
- MODEL_MAP[m]=name
192
 
193
- except:
194
- pass
195
 
 
196
 
197
- build()
198
 
 
 
 
 
 
199
 
200
- # ======================
201
- # health
202
- # ======================
203
 
204
- @app.get("/")
 
205
 
206
- async def health():
207
 
208
- return{
209
 
210
- "status":"online",
211
 
212
- "providers":len(PROVIDERS),
213
 
214
- "models":len(MODEL_MAP)
215
 
216
- }
 
 
 
217
 
 
 
218
 
219
- # ======================
220
- # models
221
- # ======================
222
 
223
- @app.get("/v1/models")
224
 
225
- async def models(
226
- authorization:str=Header(None)
 
 
227
  ):
228
 
229
- verify(
230
  authorization
231
  )
232
 
233
- return{
234
-
235
- "object":"list",
236
 
237
- "data":[
238
 
239
  {
240
-
241
- "id":m,
242
-
243
- "object":"model",
244
-
245
- "owned_by":MODEL_MAP[m]
246
-
247
  }
248
 
249
- for m in sorted(
250
- MODEL_MAP.keys()
251
- )
252
-
253
  ]
254
- }
255
 
256
 
257
- # ======================
258
- # chat
259
- # ======================
260
 
261
- @app.post("/v1/chat/completions")
262
 
263
- async def chat(
264
-
265
- body:ChatRequest,
 
 
266
 
267
- authorization:str=Header(None)
268
 
269
- ):
270
-
271
- verify(
272
- authorization
273
- )
274
 
275
- messages=[
276
 
277
- {
 
 
278
 
279
- "role":m.role,
280
 
281
- "content":m.content
282
 
283
- }
284
 
285
- for m in body.messages
286
- ]
287
 
288
- provider=None
289
 
290
- if body.provider:
291
 
292
- provider=getattr(
293
- Provider,
294
- body.provider,
295
- None
296
- )
297
 
298
- elif body.model in MODEL_MAP:
299
 
300
- provider=getattr(
301
- Provider,
302
- MODEL_MAP[
303
- body.model
304
- ],
305
- None
306
- )
307
 
308
- # ==================
309
- # stream
310
- # ==================
311
 
312
- if body.stream:
313
 
314
- def generate():
 
315
 
316
- try:
 
317
 
318
- response=g4f.ChatCompletion.create(
 
319
 
320
- model=body.model,
 
 
 
 
 
 
 
 
 
321
 
322
- provider=provider,
 
 
323
 
324
- messages=messages,
325
 
326
- stream=True
327
 
328
- )
329
 
330
- for chunk in response:
 
 
331
 
332
- payload={
333
 
334
- "id":"chatcmpl",
 
 
 
335
 
336
- "object":"chat.completion.chunk",
337
 
338
- "created":int(
339
- time.time()
340
- ),
341
 
342
- "model":body.model,
343
 
344
- "choices":[{
345
 
346
- "delta":{
 
 
347
 
348
- "content":str(
349
- chunk
350
- )
351
- }
352
 
353
- }]
354
- }
355
 
356
- yield f"data:{json.dumps(payload)}\n\n"
357
 
358
- yield "data:[DONE]\n\n"
359
 
360
- except Exception as e:
361
 
362
- logger.error(e)
 
363
 
364
- yield f"data:{json.dumps({'error':str(e)})}\n\n"
 
365
 
 
366
 
367
- return StreamingResponse(
368
 
369
- generate(),
370
 
371
- media_type="text/event-stream"
372
 
373
- )
 
374
 
 
 
375
 
376
- try:
377
 
378
- response=await asyncio.to_thread(
 
379
 
380
- g4f.ChatCompletion.create,
381
 
382
- model=body.model,
 
383
 
384
- provider=provider,
385
 
386
- messages=messages
387
 
 
 
 
388
  )
389
 
390
- return{
391
 
392
- "id":"chatcmpl",
393
 
394
- "object":"chat.completion",
 
 
 
395
 
396
- "created":int(time.time()),
397
 
398
- "model":body.model,
 
 
399
 
400
- "choices":[{
401
 
402
- "index":0,
403
 
404
- "message":{
405
 
406
- "role":"assistant",
407
 
408
- "content":str(
409
- response
410
- )
411
 
 
 
 
412
  }
413
 
414
- }]
 
 
 
 
 
 
 
415
 
416
  }
417
 
418
  except Exception as e:
419
 
420
- logger.exception(e)
421
 
422
- raise HTTPException(
423
- status_code=500,
424
- detail=str(e)
425
- )
 
 
1
  import os
2
  import json
3
  import time
 
 
4
  import logging
5
+ import asyncio
6
  import g4f
 
7
 
8
  from fastapi import FastAPI, HTTPException, Header
9
  from fastapi.responses import StreamingResponse
 
11
  from pydantic import BaseModel
12
  from typing import List, Optional
13
 
14
+
15
+ # ================= Logging =================
16
+
17
+ logging.basicConfig(
18
+ level=logging.INFO,
19
+ format="%(asctime)s %(levelname)s %(message)s"
20
+ )
21
+
22
  logger=logging.getLogger(__name__)
23
 
24
+
25
+ # ================= App =================
26
+
27
+ app=FastAPI(
28
+ title="G4F Smart Gateway",
29
+ version="2.0"
30
+ )
31
 
32
  app.add_middleware(
33
  CORSMiddleware,
34
  allow_origins=["*"],
35
+ allow_credentials=False,
36
  allow_methods=["*"],
37
  allow_headers=["*"]
38
  )
39
 
40
  API_KEY=os.getenv(
41
  "API_KEY",
42
+ "your_fallback_secret"
43
  )
44
 
45
+
46
+ # ================= Models =================
 
47
 
48
  class ChatMessage(BaseModel):
49
  role:str
 
51
 
52
 
53
  class ChatRequest(BaseModel):
54
+ model:str="gpt-4o-mini"
 
 
55
  messages:List[ChatMessage]
 
56
  stream:bool=False
 
57
  provider:Optional[str]=None
58
+ temperature:Optional[float]=0.7
59
+ max_tokens:Optional[int]=2048
60
 
61
 
62
+ # ================= Auth =================
 
 
63
 
64
+ def verify_key(auth:str):
 
 
65
 
66
+ if not auth:
67
  raise HTTPException(
68
  status_code=401,
69
+ detail="Missing Authorization"
70
  )
71
 
72
+ parts=auth.split()
73
 
74
+ if len(parts)!=2:
75
+ raise HTTPException(
76
+ status_code=401,
77
+ detail="Malformed Authorization"
78
+ )
79
 
80
+ if parts[0]!="Bearer":
81
+ raise HTTPException(
82
+ status_code=401,
83
+ detail="Invalid Authorization"
84
+ )
 
85
 
86
+ if parts[1]!=API_KEY:
87
+ raise HTTPException(
88
+ status_code=401,
89
+ detail="Invalid API Key"
90
+ )
91
 
92
 
93
+ # ================= Provider Selection =================
 
94
 
95
+ def choose_provider(model:str):
96
 
97
+ model=model.lower()
98
 
99
+ try:
100
 
101
+ if "qwen" in model:
102
 
103
+ return getattr(
104
+ g4f.Provider,
105
+ "DeepInfra",
106
+ None
107
+ )
108
 
109
+ elif "perplexity" in model:
110
 
111
+ return getattr(
112
+ g4f.Provider,
113
+ "PerplexityLabs",
114
+ None
115
+ )
116
 
117
+ elif "llama" in model:
 
 
 
 
118
 
119
+ return getattr(
120
+ g4f.Provider,
121
+ "DeepInfra",
122
+ None
123
+ )
124
 
125
+ elif "claude" in model:
126
 
127
+ return getattr(
128
+ g4f.Provider,
129
+ "OpenaiChat",
130
+ None
131
+ )
132
 
133
+ elif "gemini" in model:
134
 
135
+ return getattr(
136
+ g4f.Provider,
137
+ "OpenaiChat",
138
+ None
139
  )
140
 
141
+ elif "gpt" in model:
142
 
143
+ return getattr(
144
+ g4f.Provider,
145
+ "OpenaiChat",
146
+ None
147
  )
148
 
149
+ except:
150
+ pass
 
151
 
152
+ return None
153
 
 
154
 
155
+ # ================= Health =================
 
156
 
157
+ @app.get("/")
158
+ async def root():
159
 
160
+ return {
161
+ "status":"online"
162
+ }
163
 
 
 
164
 
165
+ # ================= Models =================
 
166
 
167
+ @app.get("/v1/models")
168
+ async def models(
169
+ authorization:str=Header(None)
170
+ ):
171
 
172
+ verify_key(
173
+ authorization
174
+ )
175
+
176
+ try:
177
+
178
+ found=[]
179
+
180
+ if hasattr(
181
+ g4f.models,
182
+ "ModelUtils"
183
+ ):
184
 
185
+ found=list(
186
+ g4f.models.ModelUtils.convert.keys()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  )
188
 
189
+ found=sorted(
190
+ list(set(found))
191
+ )
192
 
193
+ if not found:
194
 
195
+ found=[
196
 
197
+ "gpt-4o-mini",
198
+ "gpt-4",
199
+ "gpt-3.5-turbo",
200
+ "qwen-2.5-72b",
201
+ "llama-3-70b",
202
+ "perplexity"
203
 
204
+ ]
205
 
206
+ return {
 
207
 
208
+ "object":"list",
209
 
210
+ "data":[
211
 
212
+ {
213
+ "id":m,
214
+ "object":"model",
215
+ "owned_by":"g4f"
216
+ }
217
 
218
+ for m in found
 
 
219
 
220
+ ]
221
+ }
222
 
223
+ except Exception as e:
224
 
225
+ logger.exception(e)
226
 
227
+ return {
228
 
229
+ "object":"list",
230
 
231
+ "data":[
232
 
233
+ {
234
+ "id":"gpt-4o-mini",
235
+ "object":"model"
236
+ }
237
 
238
+ ]
239
+ }
240
 
 
 
 
241
 
242
+ # ================= Chat =================
243
 
244
+ @app.post("/v1/chat/completions")
245
+ async def chat(
246
+ body:ChatRequest,
247
+ authorization:str=Header(None)
248
  ):
249
 
250
+ verify_key(
251
  authorization
252
  )
253
 
254
+ try:
 
 
255
 
256
+ messages=[
257
 
258
  {
259
+ "role":m.role,
260
+ "content":m.content
 
 
 
 
 
261
  }
262
 
263
+ for m in body.messages
 
 
 
264
  ]
 
265
 
266
 
267
+ provider=None
 
 
268
 
269
+ if body.provider:
270
 
271
+ provider=getattr(
272
+ g4f.Provider,
273
+ body.provider,
274
+ None
275
+ )
276
 
277
+ else:
278
 
279
+ provider=choose_provider(
280
+ body.model
281
+ )
 
 
282
 
 
283
 
284
+ logger.info(
285
+ f"Model={body.model} Provider={provider}"
286
+ )
287
 
 
288
 
289
+ # ========= STREAM =========
290
 
291
+ if body.stream:
292
 
293
+ def generate():
 
294
 
295
+ try:
296
 
297
+ response=g4f.ChatCompletion.create(
298
 
299
+ model=body.model,
300
+ messages=messages,
301
+ provider=provider,
302
+ stream=True
 
303
 
304
+ )
305
 
306
+ for chunk in response:
 
 
 
 
 
 
307
 
308
+ payload={
 
 
309
 
310
+ "id":"chatcmpl",
311
 
312
+ "object":
313
+ "chat.completion.chunk",
314
 
315
+ "created":
316
+ int(time.time()),
317
 
318
+ "model":
319
+ body.model,
320
 
321
+ "choices":[
322
+ {
323
+ "delta":{
324
+ "content":
325
+ str(chunk)
326
+ },
327
+ "index":0
328
+ }
329
+ ]
330
+ }
331
 
332
+ yield (
333
+ f"data: {json.dumps(payload)}\n\n"
334
+ )
335
 
336
+ yield "data: [DONE]\n\n"
337
 
338
+ except Exception as e:
339
 
340
+ logger.exception(e)
341
 
342
+ yield (
343
+ f"data:{json.dumps({'error':str(e)})}\n\n"
344
+ )
345
 
 
346
 
347
+ return StreamingResponse(
348
+ generate(),
349
+ media_type="text/event-stream"
350
+ )
351
 
 
352
 
353
+ # ========= NORMAL =========
 
 
354
 
355
+ response=await asyncio.to_thread(
356
 
357
+ g4f.ChatCompletion.create,
358
 
359
+ model=body.model,
360
+ messages=messages,
361
+ provider=provider
362
 
363
+ )
 
 
 
364
 
 
 
365
 
366
+ return {
367
 
368
+ "id":"chatcmpl",
369
 
370
+ "object":"chat.completion",
371
 
372
+ "created":
373
+ int(time.time()),
374
 
375
+ "model":
376
+ body.model,
377
 
378
+ "choices":[
379
 
380
+ {
381
 
382
+ "index":0,
383
 
384
+ "message":{
385
 
386
+ "role":
387
+ "assistant",
388
 
389
+ "content":
390
+ str(response)
391
 
392
+ },
393
 
394
+ "finish_reason":
395
+ "stop"
396
 
397
+ }
398
 
399
+ ]
400
+ }
401
 
402
+ except Exception as e:
403
 
404
+ logger.exception(e)
405
 
406
+ raise HTTPException(
407
+ status_code=500,
408
+ detail=str(e)
409
  )
410
 
 
411
 
412
+ # ================= Test =================
413
 
414
+ @app.get("/test/{model}")
415
+ async def test_model(
416
+ model:str
417
+ ):
418
 
419
+ try:
420
 
421
+ provider=choose_provider(
422
+ model
423
+ )
424
 
425
+ result=await asyncio.to_thread(
426
 
427
+ g4f.ChatCompletion.create,
428
 
429
+ model=model,
430
 
431
+ provider=provider,
432
 
433
+ messages=[
 
 
434
 
435
+ {
436
+ "role":"user",
437
+ "content":"hello"
438
  }
439
 
440
+ ]
441
+ )
442
+
443
+ return {
444
+
445
+ "model":model,
446
+ "provider":str(provider),
447
+ "working":True
448
 
449
  }
450
 
451
  except Exception as e:
452
 
453
+ return {
454
 
455
+ "model":model,
456
+ "working":False,
457
+ "error":str(e)
458
+
459
+ }