chore(align): sync Git with running container as authoritative source
One-time alignment to close drift between the running container (qihu-aiflow / image qihuaiflow:with-deps) and this Git repository. Container hotfixes previously persisted only via `docker commit` are now captured in Git so Git becomes the single source of truth. Backend: - chat_service.py: accumulated fixes incl. EigenFlux README guidance - chat_context.py / chat_media.py: URL/web-repo context injection - api/url_reader.py: NEW SSRF-safe web/README reader (Issue #725) - skills_admin.py & Skills.tsx: 14-scene capability unification - pgvector_search.py, orchestrator.py, platform_knowledge.py, market.py, admin/models.py: drift sync Frontend: - ChatPage.tsx: collapsible AI think/act block (Issue #724) - MarketPage / EvolutionDashboard / TokenDashboard styling sync Migrations: - 018_normalize_marketapp_scene.sql: fixed capability allowlist (prevents capability wipe on restart) Alignment performed 2026-07-23 19:43:56. No deletions; secrets/venv/node_modules/ caches were not touched (tracked-source-only copy).
This commit is contained in:
1 parent
4a95657030
commit
9d67b0544c
1 file changed
+59
@@ -1366,6 +1366,24 @@ async def _send_message_core(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ===== Model Failover: Auto 模式下自动切换备选模型 =====
|
||||||
|
_try_models = [ai_model] if (ai_model and ai_model.is_enabled) else []
|
||||||
|
if not model_id or model_id == "auto":
|
||||||
|
# Auto 模式:附加其他已启用的文本模型作为备选
|
||||||
|
_fallbacks = db.query(AIModel).filter(
|
||||||
|
AIModel.is_enabled == True,
|
||||||
|
AIModel.id != (ai_model.id if ai_model else "")
|
||||||
|
).order_by(AIModel.is_default.desc()).limit(5).all()
|
||||||
|
_try_models.extend([m for m in _fallbacks if m not in _try_models])
|
||||||
|
_retry_log = []
|
||||||
|
_all_failed = False
|
||||||
|
|
||||||
|
for _try_model in _try_models:
|
||||||
|
ai_model = _try_model
|
||||||
if ai_model:
|
if ai_model:
|
||||||
|
|
||||||
|
|
||||||
@@ -3098,6 +3116,47 @@ async def _send_message_core(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ===== Post-check: 失败则切换备选,成功则跳出 =====
|
||||||
|
_is_error = (
|
||||||
|
ai_content and (
|
||||||
|
"AI模型调用失败" in str(ai_content)
|
||||||
|
or "AI服务暂时不可用" in str(ai_content)
|
||||||
|
or "AI模型返回了空响应" in str(ai_content)
|
||||||
|
or "该模型缺少API密钥" in str(ai_content)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if _is_error:
|
||||||
|
_retry_log.append(f"{ai_model.name}: {str(ai_content)[:60]}")
|
||||||
|
logger.warning(
|
||||||
|
f"[Failover] 模型 {ai_model.name} 失败,切换至备选..."
|
||||||
|
)
|
||||||
|
continue # try next model
|
||||||
|
|
||||||
|
# Success — exit failover loop
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Loop completed without break = all models failed
|
||||||
|
_all_failed = True
|
||||||
|
|
||||||
|
# ===== All models failed? =====
|
||||||
|
if _all_failed:
|
||||||
|
_names = ", ".join([m.name for m in _try_models])
|
||||||
|
_details = "; ".join(_retry_log[-3:]) if _retry_log else "未知错误"
|
||||||
|
ai_content = (
|
||||||
|
f"已尝试 {len(_try_models)} 个模型({_names})均调用失败。\n"
|
||||||
|
f"最后错误: {_details}\n"
|
||||||
|
f"请稍后重试或检查模型配置。"
|
||||||
|
)
|
||||||
|
logger.error(f"[Failover] 全部 {len(_try_models)} 个模型均失败: {_details}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user