bahi-bh commited on
Commit
e4ec4a1
·
verified ·
1 Parent(s): dbd67cd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +124 -74
app.py CHANGED
@@ -74,15 +74,7 @@ def get_provider(name: str):
74
  return None
75
 
76
  REAL_PROVIDERS: Dict[str, Any] = {
77
- "Perplexity": get_provider("Perplexity") or get_provider("PerplexityAi"),
78
- "Copilot": get_provider("Copilot"),
79
  "Qwen": get_provider("Qwen"),
80
- "Blackbox": get_provider("Blackbox"),
81
- "DeepSeek": get_provider("DeepSeek"),
82
- "HuggingFace": get_provider("HuggingFace"),
83
- "Cloudflare": get_provider("Cloudflare"),
84
- "You": get_provider("You"),
85
- "Bing": get_provider("Bing"),
86
  }
87
 
88
  REAL_PROVIDERS = {k: v for k, v in REAL_PROVIDERS.items() if v}
@@ -91,15 +83,7 @@ REAL_PROVIDERS = {k: v for k, v in REAL_PROVIDERS.items() if v}
91
  # MODELS
92
  # =====================================================
93
  PROVIDER_MODELS_FALLBACK: Dict[str, List[str]] = {
94
- "Perplexity": ["sonar", "sonar-pro", "gpt-4o", "llama-3"],
95
- "Copilot": ["gpt-4o", "gpt-4", "turbo"],
96
  "Qwen": ["Qwen/Qwen3.6-35B-A3B"],
97
- "Blackbox": ["gpt-4o", "claude-3", "gemini-pro", "llama-3"],
98
- "DeepSeek": ["deepseek-chat", "deepseek-coder"],
99
- "HuggingFace": ["meta-llama", "mistral", "qwen", "gemma"],
100
- "Cloudflare": ["llama-3", "mistral", "qwen"],
101
- "You": ["gpt-4o", "claude", "llama-3"],
102
- "Bing": ["gpt-4o", "gpt-4"],
103
  }
104
 
105
  CLAUDE_MODEL_MAP: Dict[str, str] = {
@@ -167,7 +151,7 @@ def discover_provider_models(provider_obj: Any, provider_name: str) -> List[str]
167
  if not candidates:
168
  candidates = PROVIDER_MODELS_FALLBACK.get(
169
  provider_name,
170
- ["gpt-4o"]
171
  )
172
 
173
  seen = set()
@@ -250,8 +234,9 @@ def ask(message: str, history, provider_name: str, model_name: str):
250
  yield ""
251
  return
252
 
253
- # تحويل نماذج Claude إلى Qwen3.6-35B-A3B
254
- model_name = CLAUDE_MODEL_MAP.get(model_name, model_name)
 
255
 
256
  key = f"{provider_name}|{model_name}|{message}"
257
 
@@ -297,70 +282,49 @@ def ask(message: str, history, provider_name: str, model_name: str):
297
  "content": message
298
  })
299
 
300
- fallback_providers = [
301
- provider_name,
302
- "Perplexity",
303
- "Copilot",
304
- "Blackbox",
305
- "DeepSeek",
306
- "Bing",
307
- "You",
308
- "Qwen"
309
- ]
310
-
311
- used = []
312
-
313
- for pname in fallback_providers:
314
- if pname in used:
315
- continue
316
 
317
- used.append(pname)
 
 
318
 
319
- pobj = REAL_PROVIDERS.get(pname)
 
320
 
321
- if not pobj:
322
- continue
 
 
323
 
324
- if pname not in _PROVIDER_MODEL_CACHE:
325
- _PROVIDER_MODEL_CACHE[pname] = discover_provider_models(
326
- pobj,
327
- pname
 
 
 
328
  )
329
 
330
- model_candidates = [model_name] + [
331
- x for x in _PROVIDER_MODEL_CACHE[pname]
332
- if x != model_name
333
- ]
334
-
335
- for m in model_candidates[:12]:
336
- try:
337
- stream = g4f.ChatCompletion.create(
338
- model=m,
339
- provider=pobj,
340
- messages=msgs,
341
- stream=True
342
- )
343
-
344
- text = ""
345
 
346
- for chunk in stream:
347
- c = clean_stream(chunk)
348
 
349
- if c is None or c == "":
350
- continue
351
 
352
- text += c
353
- yield text
354
 
355
- if text.strip():
356
- _cache_set(key, text)
357
- return
358
 
359
- except Exception as e:
360
- logger.warning(e)
361
- continue
362
 
363
- yield "❌ Failed with all providers."
364
 
365
  # =====================================================
366
  # FASTAPI
@@ -372,10 +336,22 @@ API_KEY = os.getenv("API_KEY", "mysecretkey123")
372
 
373
  class ChatRequest(BaseModel):
374
  message: str
375
- provider: str = "Perplexity"
376
- model: str = "sonar"
377
  history: List[Any] = []
378
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  def verify_api_key(request: Request):
380
  auth = request.headers.get("Authorization")
381
  if auth and auth.startswith("Bearer "):
@@ -401,7 +377,9 @@ async def root():
401
  "GET /health": "التحقق من صحة الخادم",
402
  "GET /providers": "قائمة المزودين والنماذج (يتطلب مفتاح)",
403
  "POST /chat": "إرسال رسالة والحصول على رد (يتطلب مفتاح)",
404
- "POST /chat/stream": "إرسال رسالة والحصول على رد متدفق (يتطلب مفتاح)"
 
 
405
  },
406
  "authentication": "Bearer YOUR_API_KEY or X-API-Key: YOUR_API_KEY",
407
  "status": "✅ Server is working"
@@ -444,6 +422,78 @@ async def get_providers(request: Request):
444
 
445
  return JSONResponse(content={"providers": providers_info})
446
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  if __name__ == "__main__":
448
  import uvicorn
449
  port = int(os.getenv("PORT", "7860"))
 
74
  return None
75
 
76
  REAL_PROVIDERS: Dict[str, Any] = {
 
 
77
  "Qwen": get_provider("Qwen"),
 
 
 
 
 
 
78
  }
79
 
80
  REAL_PROVIDERS = {k: v for k, v in REAL_PROVIDERS.items() if v}
 
83
  # MODELS
84
  # =====================================================
85
  PROVIDER_MODELS_FALLBACK: Dict[str, List[str]] = {
 
 
86
  "Qwen": ["Qwen/Qwen3.6-35B-A3B"],
 
 
 
 
 
 
87
  }
88
 
89
  CLAUDE_MODEL_MAP: Dict[str, str] = {
 
151
  if not candidates:
152
  candidates = PROVIDER_MODELS_FALLBACK.get(
153
  provider_name,
154
+ ["Qwen/Qwen3.6-35B-A3B"]
155
  )
156
 
157
  seen = set()
 
234
  yield ""
235
  return
236
 
237
+ # تحويل نماذج Claude الوهمية إلى Qwen3.6-35B-A3B
238
+ model_name = CLAUDE_MODEL_MAP.get(model_name, "Qwen/Qwen3.6-35B-A3B")
239
+ provider_name = "Qwen"
240
 
241
  key = f"{provider_name}|{model_name}|{message}"
242
 
 
282
  "content": message
283
  })
284
 
285
+ pobj = REAL_PROVIDERS.get("Qwen")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
+ if not pobj:
288
+ yield "❌ Qwen provider not available."
289
+ return
290
 
291
+ if "Qwen" not in _PROVIDER_MODEL_CACHE:
292
+ _PROVIDER_MODEL_CACHE["Qwen"] = discover_provider_models(pobj, "Qwen")
293
 
294
+ model_candidates = ["Qwen/Qwen3.6-35B-A3B"] + [
295
+ x for x in _PROVIDER_MODEL_CACHE["Qwen"]
296
+ if x != "Qwen/Qwen3.6-35B-A3B"
297
+ ]
298
 
299
+ for m in model_candidates[:12]:
300
+ try:
301
+ stream = g4f.ChatCompletion.create(
302
+ model=m,
303
+ provider=pobj,
304
+ messages=msgs,
305
+ stream=True
306
  )
307
 
308
+ text = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
 
310
+ for chunk in stream:
311
+ c = clean_stream(chunk)
312
 
313
+ if c is None or c == "":
314
+ continue
315
 
316
+ text += c
317
+ yield text
318
 
319
+ if text.strip():
320
+ _cache_set(key, text)
321
+ return
322
 
323
+ except Exception as e:
324
+ logger.warning(e)
325
+ continue
326
 
327
+ yield "❌ Failed with Qwen provider."
328
 
329
  # =====================================================
330
  # FASTAPI
 
336
 
337
  class ChatRequest(BaseModel):
338
  message: str
339
+ provider: str = "Qwen"
340
+ model: str = "claude-sonnet-4-6"
341
  history: List[Any] = []
342
 
343
+ # =====================================================
344
+ # MCP SUPPORT
345
+ # =====================================================
346
+ class MCPToolCall(BaseModel):
347
+ tool: str
348
+ parameters: Dict[str, Any] = {}
349
+
350
+ class MCPRequest(BaseModel):
351
+ messages: List[Dict[str, Any]]
352
+ tools: List[Dict[str, Any]] = []
353
+ model: str = "claude-sonnet-4-6"
354
+
355
  def verify_api_key(request: Request):
356
  auth = request.headers.get("Authorization")
357
  if auth and auth.startswith("Bearer "):
 
377
  "GET /health": "التحقق من صحة الخادم",
378
  "GET /providers": "قائمة المزودين والنماذج (يتطلب مفتاح)",
379
  "POST /chat": "إرسال رسالة والحصول على رد (يتطلب مفتاح)",
380
+ "POST /chat/stream": "إرسال رسالة والحصول على رد متدفق (يتطلب مفتاح)",
381
+ "POST /v1/messages": "MCP / Claude-compatible endpoint (يتطلب مفتاح)",
382
+ "GET /v1/models": "قائمة النماذج بصيغة OpenAI (يتطلب مفتاح)"
383
  },
384
  "authentication": "Bearer YOUR_API_KEY or X-API-Key: YOUR_API_KEY",
385
  "status": "✅ Server is working"
 
422
 
423
  return JSONResponse(content={"providers": providers_info})
424
 
425
+ # =====================================================
426
+ # MCP / Claude-compatible endpoints
427
+ # =====================================================
428
+
429
+ @app.get("/v1/models")
430
+ async def list_models(request: Request):
431
+ verify_api_key(request)
432
+ models = [
433
+ {"id": "claude-opus-4-6", "object": "model", "owned_by": "anthropic"},
434
+ {"id": "claude-sonnet-4-6", "object": "model", "owned_by": "anthropic"},
435
+ {"id": "claude-haiku-4-5", "object": "model", "owned_by": "anthropic"},
436
+ ]
437
+ return JSONResponse(content={"object": "list", "data": models})
438
+
439
+ @app.post("/v1/messages")
440
+ async def mcp_messages(request: Request):
441
+ verify_api_key(request)
442
+
443
+ body = await request.json()
444
+ model = body.get("model", "claude-sonnet-4-6")
445
+ messages = body.get("messages", [])
446
+ stream = body.get("stream", False)
447
+ max_tokens = body.get("max_tokens", 4096)
448
+
449
+ # استخراج آخر رسالة كـ message والباقي كـ history
450
+ if not messages:
451
+ raise HTTPException(status_code=400, detail="messages is required")
452
+
453
+ last_msg = messages[-1]
454
+ history = messages[:-1]
455
+
456
+ # دعم content كـ string أو list
457
+ if isinstance(last_msg.get("content"), list):
458
+ user_text = " ".join(
459
+ block.get("text", "")
460
+ for block in last_msg["content"]
461
+ if isinstance(block, dict) and block.get("type") == "text"
462
+ )
463
+ else:
464
+ user_text = last_msg.get("content", "")
465
+
466
+ if stream:
467
+ async def generate_stream():
468
+ full = ""
469
+ for chunk in ask(user_text, history, "Qwen", model):
470
+ delta = chunk[len(full):]
471
+ full = chunk
472
+ if delta:
473
+ event = {
474
+ "type": "content_block_delta",
475
+ "delta": {"type": "text_delta", "text": delta}
476
+ }
477
+ yield f"data: {json.dumps(event)}\n\n"
478
+ yield f"data: {json.dumps({'type': 'message_stop'})}\n\n"
479
+
480
+ return StreamingResponse(generate_stream(), media_type="text/event-stream")
481
+
482
+ else:
483
+ full_response = ""
484
+ for chunk in ask(user_text, history, "Qwen", model):
485
+ full_response = chunk
486
+
487
+ return JSONResponse(content={
488
+ "id": f"msg_{int(time.time())}",
489
+ "type": "message",
490
+ "role": "assistant",
491
+ "model": model,
492
+ "content": [{"type": "text", "text": full_response}],
493
+ "stop_reason": "end_turn",
494
+ "usage": {"input_tokens": 0, "output_tokens": 0}
495
+ })
496
+
497
  if __name__ == "__main__":
498
  import uvicorn
499
  port = int(os.getenv("PORT", "7860"))