fix: 停止迁移复活默认模型供应商/模型;AI聊天媒体生成改为异步(修复视频请求失败)

- migrations/002_init_data.sql: 移除 model_providers/models 的 INSERT IGNORE 种子,重启容器不再复活 deepseek/openai/通义千问等默认供应商与模型,改由用户在后台大模型库自行维护
- chat.py: 图像/视频生成分支改为立即返回占位(generating=true),后台 asyncio 任务轮询 AGNES 并回写消息内容,避免最长 5 分钟同步轮询阻塞 HTTP 请求导致前端「发送失败:请求失败」
- ChatPage.tsx: 发送后若 assistant_message.generating,每 3s 轮询 getMessages 直至内容更新;页面刷新时若仍有「⏳」占位继续轮询;卸载清理定时器
This commit is contained in:
QihuAI Design committed 2026-07-14 21:48:39 +08:00
1 parent b7f76c3e92
commit 5d1370156e
3 files changed
+121 -21

No files matched your search

+51 -4
View File
@@ -184,6 +184,39 @@ async def _generate_media(db, ai_model, prompt: str, user_id: str = None) -> str
return "⚠️ 未知的媒体模型类型。"
async def _generate_media_task(ai_msg_id: str, ai_model_id: str, prompt: str, user_id: str):
"""后台异步执行图像/视频生成,完成后回写消息内容。
目的是把最长 5 分钟的 AGNES 视频轮询从 HTTP 请求中剥离,避免请求被客户端/
代理超时断开(表现为前端「发送失败:请求失败」)。send_message 立即返回占位
内容(generating 标记),本任务独立在事件循环中轮询并在完成后 merge 回写消息。
使用独立的 SessionLocal 会话,因为请求级 db 在响应返回后即被 FastAPI 关闭。
"""
db = SessionLocal()
try:
ai_model = db.query(AIModel).filter(AIModel.id == ai_model_id).first()
if not ai_model:
logger.warning(f"[_generate_media_task] 模型不存在: {ai_model_id}")
return
try:
result = await _generate_media(db, ai_model, prompt, user_id)
except Exception as e:
result = f"⚠️ 媒体生成失败:{str(e)[:200]}"
# 回写消息内容(占位 → 最终富文本/错误)
msg = db.query(Message).filter(Message.id == ai_msg_id).first()
if msg:
msg.content = result
msg.token_used = len(result) // 2
db.commit()
logger.info(f"[_generate_media_task] 消息 {ai_msg_id} 已回写内容")
except Exception as e:
logger.warning(f"[_generate_media_task] 后台生成异常: {e}")
finally:
try:
db.close()
except Exception:
pass
# ============================================================
# Request Models
@@ -1073,11 +1106,15 @@ async def send_message(
if ai_model:
# ====== 图像/视频生成模型分支 ======
caps = [c.lower() for c in (ai_model.capabilities or [])] if ai_model else []
media_generating = False
if ai_model and ("image" in caps or "video" in caps):
try:
ai_content = await _generate_media(db, ai_model, req.content, current_user.id)
except Exception as e:
ai_content = f"⚠️ 媒体生成失败:{str(e)[:200]}"
# 异步生成:立即返回占位内容,后台任务轮询 AGNES 并回写,
# 避免最长 5 分钟的视频轮询阻塞 HTTP 请求导致客户端超时断开。
media_generating = True
if "video" in caps:
ai_content = "⏳ 视频生成中…(通常 1~3 分钟,完成后自动刷新)"
else:
ai_content = "⏳ 图像生成中…(完成后自动刷新)"
elif ai_model and ai_model.endpoint:
provider = db.query(ModelProvider).filter(ModelProvider.id == ai_model.provider_id).first()
api_key = ai_model.api_key or (provider.api_key if provider else None)
@@ -1312,6 +1349,15 @@ async def send_message(
db.refresh(user_msg)
db.refresh(ai_msg)
# 媒体生成改为后台异步:立即返回占位,后台任务完成后回写消息内容
if media_generating:
try:
asyncio.create_task(
_generate_media_task(ai_msg.id, ai_model.id, req.content, current_user.id)
)
except Exception as e:
logger.warning(f"[_generate_media_task] 启动后台任务失败: {e}")
return {
"code": 0,
"data": {
@@ -1323,6 +1369,7 @@ async def send_message(
"id": ai_msg.id, "role": ai_msg.role, "content": ai_msg.content,
"token_used": ai_msg.token_used, "created_at": str(ai_msg.created_at),
"used_skills": used_skills,
"generating": media_generating,
},
},
"message": "发送成功",