bahi-bh commited on
Commit
c2ccacf
·
verified ·
1 Parent(s): 782bb13

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +406 -0
app.py ADDED
@@ -0,0 +1,406 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ x_api_key = req.headers.get("x-api-key")
79
+
80
+ # السماح بدون مفتاح للاختبار
81
+ if not auth and not x_api_key:
82
+ return True
83
+
84
+ token = None
85
+
86
+ if auth:
87
+
88
+ if not auth.startswith("Bearer "):
89
+ raise HTTPException(
90
+ status_code=401,
91
+ detail="Invalid Authorization Format"
92
+ )
93
+
94
+ token = auth.replace("Bearer ", "").strip()
95
+
96
+ elif x_api_key:
97
+
98
+ token = x_api_key.strip()
99
+
100
+ if token != API_KEY:
101
+ raise HTTPException(
102
+ status_code=403,
103
+ detail="Invalid API Key"
104
+ )
105
+
106
+ return True
107
+
108
+
109
+ # =====================================================
110
+ # ROOT
111
+ # =====================================================
112
+
113
+ @app.get("/")
114
+ async def root():
115
+
116
+ return {
117
+ "status": "online",
118
+ "service": "Universal AI Gateway",
119
+ "version": "5.3.0"
120
+ }
121
+
122
+
123
+ # =====================================================
124
+ # MODELS
125
+ # =====================================================
126
+
127
+ @app.get("/v1/models")
128
+ async def get_models():
129
+
130
+ models_data = []
131
+
132
+ try:
133
+
134
+ if hasattr(g4f.models, "_all_models"):
135
+
136
+ all_models = list(g4f.models._all_models)
137
+
138
+ for model in all_models:
139
+
140
+ models_data.append({
141
+ "id": str(model),
142
+ "object": "model",
143
+ "created": int(time.time()),
144
+ "owned_by": "g4f"
145
+ })
146
+
147
+ except Exception as e:
148
+
149
+ logger.error(f"Models error: {e}")
150
+
151
+ return {
152
+ "object": "list",
153
+ "data": models_data
154
+ }
155
+
156
+
157
+ # =====================================================
158
+ # CHAT COMPLETIONS
159
+ # =====================================================
160
+
161
+ @app.post("/v1/chat/completions")
162
+ async def chat_completions(
163
+ req: Request,
164
+ body: ChatRequest
165
+ ):
166
+
167
+ verify_api_key(req)
168
+
169
+ messages = [
170
+ {
171
+ "role": m.role,
172
+ "content": m.content
173
+ }
174
+ for m in body.messages
175
+ ]
176
+
177
+ logger.info(
178
+ f"Request model={body.model} stream={body.stream}"
179
+ )
180
+
181
+ # =================================================
182
+ # STREAMING
183
+ # =================================================
184
+
185
+ if body.stream:
186
+
187
+ def generate_stream():
188
+
189
+ chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
190
+
191
+ try:
192
+
193
+ client = Client()
194
+
195
+ response = client.chat.completions.create(
196
+ model=body.model,
197
+ messages=messages,
198
+ stream=True
199
+ )
200
+
201
+ has_content = False
202
+
203
+ for chunk in response:
204
+
205
+ try:
206
+
207
+ content = ""
208
+
209
+ if (
210
+ hasattr(chunk, "choices")
211
+ and chunk.choices
212
+ and len(chunk.choices) > 0
213
+ and hasattr(chunk.choices[0], "delta")
214
+ and chunk.choices[0].delta
215
+ and chunk.choices[0].delta.content
216
+ ):
217
+ content = chunk.choices[0].delta.content
218
+
219
+ if not content:
220
+ continue
221
+
222
+ has_content = True
223
+
224
+ payload = {
225
+ "id": chunk_id,
226
+ "object": "chat.completion.chunk",
227
+ "created": int(time.time()),
228
+ "model": body.model,
229
+ "choices": [
230
+ {
231
+ "index": 0,
232
+ "delta": {
233
+ "content": content
234
+ },
235
+ "finish_reason": None
236
+ }
237
+ ]
238
+ }
239
+
240
+ yield (
241
+ "data: "
242
+ + json.dumps(
243
+ payload,
244
+ ensure_ascii=False
245
+ )
246
+ + "\n\n"
247
+ )
248
+
249
+ except Exception as chunk_error:
250
+
251
+ logger.error(
252
+ f"Chunk error: {chunk_error}"
253
+ )
254
+
255
+ if not has_content:
256
+
257
+ error_payload = {
258
+ "error": {
259
+ "message": "Provider returned empty stream",
260
+ "type": "empty_stream"
261
+ }
262
+ }
263
+
264
+ yield (
265
+ "data: "
266
+ + json.dumps(error_payload)
267
+ + "\n\n"
268
+ )
269
+
270
+ final_payload = {
271
+ "id": chunk_id,
272
+ "object": "chat.completion.chunk",
273
+ "created": int(time.time()),
274
+ "model": body.model,
275
+ "choices": [
276
+ {
277
+ "index": 0,
278
+ "delta": {},
279
+ "finish_reason": "stop"
280
+ }
281
+ ]
282
+ }
283
+
284
+ yield (
285
+ "data: "
286
+ + json.dumps(final_payload)
287
+ + "\n\n"
288
+ )
289
+
290
+ yield "data: [DONE]\n\n"
291
+
292
+ except Exception as e:
293
+
294
+ logger.error(
295
+ f"Streaming error: {e}"
296
+ )
297
+
298
+ error_payload = {
299
+ "error": {
300
+ "message": str(e),
301
+ "type": "server_error"
302
+ }
303
+ }
304
+
305
+ yield (
306
+ "data: "
307
+ + json.dumps(error_payload)
308
+ + "\n\n"
309
+ )
310
+
311
+ yield "data: [DONE]\n\n"
312
+
313
+ return StreamingResponse(
314
+ generate_stream(),
315
+ media_type="text/event-stream",
316
+ headers={
317
+ "Cache-Control": "no-cache",
318
+ "Connection": "keep-alive",
319
+ "X-Accel-Buffering": "no"
320
+ }
321
+ )
322
+
323
+ # =================================================
324
+ # NORMAL RESPONSE
325
+ # =================================================
326
+
327
+ try:
328
+
329
+ client = Client()
330
+
331
+ response = client.chat.completions.create(
332
+ model=body.model,
333
+ messages=messages,
334
+ stream=False
335
+ )
336
+
337
+ assistant_message = ""
338
+
339
+ try:
340
+
341
+ assistant_message = (
342
+ response
343
+ .choices[0]
344
+ .message
345
+ .content
346
+ )
347
+
348
+ except Exception:
349
+
350
+ assistant_message = str(response)
351
+
352
+ return JSONResponse({
353
+
354
+ "id": (
355
+ f"chatcmpl-{uuid.uuid4().hex}"
356
+ ),
357
+
358
+ "object": "chat.completion",
359
+
360
+ "created": int(time.time()),
361
+
362
+ "model": body.model,
363
+
364
+ "choices": [
365
+ {
366
+ "index": 0,
367
+ "message": {
368
+ "role": "assistant",
369
+ "content": assistant_message
370
+ },
371
+ "finish_reason": "stop"
372
+ }
373
+ ],
374
+
375
+ "usage": {
376
+ "prompt_tokens": 0,
377
+ "completion_tokens": 0,
378
+ "total_tokens": 0
379
+ }
380
+
381
+ })
382
+
383
+ except Exception as e:
384
+
385
+ logger.error(f"Chat error: {e}")
386
+
387
+ raise HTTPException(
388
+ status_code=500,
389
+ detail=str(e)
390
+ )
391
+
392
+
393
+ # =====================================================
394
+ # RUN
395
+ # =====================================================
396
+
397
+ if __name__ == "__main__":
398
+
399
+ import uvicorn
400
+
401
+ uvicorn.run(
402
+ app,
403
+ host="0.0.0.0",
404
+ port=7860,
405
+ log_level="info"
406
+ )