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:
QihuAI Design committed 2026-07-23 19:43:56 +08:00
1 parent 4a95657030
commit 9d67b0544c
1 file changed
+59
+59
View File
@@ -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}")