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:
1 parent
b7f76c3e92
commit
5d1370156e
3 files changed
+121
-21
No files matched your search
@@ -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": "发送成功",
|
||||
|
||||
Reference in new issue
Block a user