Update app.py
Browse files
app.py
CHANGED
|
@@ -201,6 +201,22 @@ def execute_tool(name: str, inp: Dict[str, Any]) -> str:
|
|
| 201 |
# G4F CALL — sync، يُشغَّل في thread pool
|
| 202 |
# =========================================================
|
| 203 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
def _g4f_call_sync(messages: List[Dict], target_model: str) -> str:
|
| 205 |
"""
|
| 206 |
محاولة g4f مع كل providers بالترتيب.
|
|
@@ -208,7 +224,9 @@ def _g4f_call_sync(messages: List[Dict], target_model: str) -> str:
|
|
| 208 |
"""
|
| 209 |
mapped = CLAUDE_TO_G4F.get(target_model, target_model)
|
| 210 |
|
|
|
|
| 211 |
for pobj, pmodels in PROVIDER_CHAIN:
|
|
|
|
| 212 |
try_models = [mapped] + [m for m in pmodels if m != mapped]
|
| 213 |
|
| 214 |
for model in try_models[:4]: # max 4 per provider
|
|
@@ -225,28 +243,47 @@ def _g4f_call_sync(messages: List[Dict], target_model: str) -> str:
|
|
| 225 |
text = str(result or "")
|
| 226 |
text = text.strip()
|
| 227 |
|
| 228 |
-
#
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
("choices" in lower_text and "nonetype" in lower_text) or
|
| 234 |
-
"object has no attribute" in lower_text or
|
| 235 |
-
"color-red-500" in lower_text or
|
| 236 |
-
"var(--color-red-500)" in lower_text
|
| 237 |
-
):
|
| 238 |
-
logger.warning("Rejected invalid/error response from provider %s (model: %s): %s",
|
| 239 |
-
type(pobj).__name__, model, text[:150])
|
| 240 |
-
continue # تخطي هذا المزود فوراً وجرب التالي في السلسلة
|
| 241 |
|
| 242 |
logger.info("Success: provider=%s model=%s len=%d",
|
| 243 |
-
|
| 244 |
return text
|
| 245 |
except Exception as e:
|
| 246 |
-
logger.debug("Failed %s/%s: %s",
|
| 247 |
continue
|
| 248 |
|
| 249 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
|
| 251 |
|
| 252 |
async def g4f_call(messages: List[Dict], model: str) -> str:
|
|
@@ -347,7 +384,7 @@ async def health():
|
|
| 347 |
return {
|
| 348 |
"status": "ok",
|
| 349 |
"cookies": COOKIE_STATUS,
|
| 350 |
-
"providers": [type(p).__name__ for p, _ in PROVIDER_CHAIN],
|
| 351 |
}
|
| 352 |
|
| 353 |
|
|
|
|
| 201 |
# G4F CALL — sync، يُشغَّل في thread pool
|
| 202 |
# =========================================================
|
| 203 |
|
| 204 |
+
def _is_invalid_response(text: str) -> bool:
|
| 205 |
+
"""تحليل النص والتحقق مما إذا كان يمثل رسالة خطأ داخلية من g4f أو HTML تالف."""
|
| 206 |
+
if not text or len(text) <= 2:
|
| 207 |
+
return True
|
| 208 |
+
lower_text = text.lower()
|
| 209 |
+
if "choices" in lower_text and "nonetype" in lower_text:
|
| 210 |
+
return True
|
| 211 |
+
if "object has no attribute" in lower_text:
|
| 212 |
+
return True
|
| 213 |
+
if "color-red-500" in lower_text or "var(--color-red-500)" in lower_text:
|
| 214 |
+
return True
|
| 215 |
+
if "error" in lower_text and "style=" in lower_text:
|
| 216 |
+
return True
|
| 217 |
+
return False
|
| 218 |
+
|
| 219 |
+
|
| 220 |
def _g4f_call_sync(messages: List[Dict], target_model: str) -> str:
|
| 221 |
"""
|
| 222 |
محاولة g4f مع كل providers بالترتيب.
|
|
|
|
| 224 |
"""
|
| 225 |
mapped = CLAUDE_TO_G4F.get(target_model, target_model)
|
| 226 |
|
| 227 |
+
# ── 1. محاولة استخدام السلسلة المحددة للمزودين أولاً ──
|
| 228 |
for pobj, pmodels in PROVIDER_CHAIN:
|
| 229 |
+
pname = pobj.__name__ if hasattr(pobj, "__name__") else type(pobj).__name__
|
| 230 |
try_models = [mapped] + [m for m in pmodels if m != mapped]
|
| 231 |
|
| 232 |
for model in try_models[:4]: # max 4 per provider
|
|
|
|
| 243 |
text = str(result or "")
|
| 244 |
text = text.strip()
|
| 245 |
|
| 246 |
+
# التحقق الجراحي من الاستجابة التالفة
|
| 247 |
+
if _is_invalid_response(text):
|
| 248 |
+
logger.warning("Rejected invalid response from provider %s (model: %s): %s",
|
| 249 |
+
pname, model, text[:150])
|
| 250 |
+
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
|
| 252 |
logger.info("Success: provider=%s model=%s len=%d",
|
| 253 |
+
pname, model, len(text))
|
| 254 |
return text
|
| 255 |
except Exception as e:
|
| 256 |
+
logger.debug("Failed %s/%s: %s", pname, model, e)
|
| 257 |
continue
|
| 258 |
|
| 259 |
+
# ── 2. الإنقاذ التلقائي الفوري (Auto-Routing Fallback) في حال تعطل السلسلة بالكامل ──
|
| 260 |
+
logger.warning("All explicit providers failed. Attempting automatic fallback routing...")
|
| 261 |
+
fallback_models = [mapped, "qwen-3-235b", "qwen-3-32b"]
|
| 262 |
+
|
| 263 |
+
for model in fallback_models:
|
| 264 |
+
try:
|
| 265 |
+
# هنا نقوم بالاستدعاء بدون تحديد مزود (provider) لندع g4f يوجه الطلب ذاتياً لأي مزود عالمي شغال
|
| 266 |
+
result = g4f.ChatCompletion.create(
|
| 267 |
+
model=model,
|
| 268 |
+
messages=messages,
|
| 269 |
+
stream=False,
|
| 270 |
+
)
|
| 271 |
+
if hasattr(result, "__iter__") and not isinstance(result, str):
|
| 272 |
+
text = "".join(str(c) for c in result if c)
|
| 273 |
+
else:
|
| 274 |
+
text = str(result or "")
|
| 275 |
+
text = text.strip()
|
| 276 |
+
|
| 277 |
+
if not _is_invalid_response(text):
|
| 278 |
+
logger.info("Auto-routing Fallback SUCCESS: model=%s len=%d", model, len(text))
|
| 279 |
+
return text
|
| 280 |
+
else:
|
| 281 |
+
logger.warning("Auto-routing Fallback rejected invalid response for model %s", model)
|
| 282 |
+
except Exception as e:
|
| 283 |
+
logger.debug("Auto-routing fallback failed for model %s: %s", model, e)
|
| 284 |
+
continue
|
| 285 |
+
|
| 286 |
+
raise RuntimeError("All providers and automatic fallbacks failed")
|
| 287 |
|
| 288 |
|
| 289 |
async def g4f_call(messages: List[Dict], model: str) -> str:
|
|
|
|
| 384 |
return {
|
| 385 |
"status": "ok",
|
| 386 |
"cookies": COOKIE_STATUS,
|
| 387 |
+
"providers": [p.__name__ if hasattr(p, "__name__") else type(p).__name__ for p, _ in PROVIDER_CHAIN],
|
| 388 |
}
|
| 389 |
|
| 390 |
|