fix: propagate real message IDs via SSE and fix regenerate model parameter

- Add message_id and user_message_id to SSE done events so frontend
  can replace temp IDs with real DB IDs, fixing "消息不存在" on regenerate
- Replace Body(default=None) with RegenerateRequest Pydantic model for
  proper JSON body parsing, fixing 422 Unprocessable Content
- Frontend always sends model in regenerate request body
- Pass selected model through message-item → regenerateMessage chain
- Various UI refinements to chat sidebar, quick questions, and layout

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 09:00:31 +08:00
parent 53a9380d7a
commit 0251a5e3ff
10 changed files with 549 additions and 858 deletions
+44 -17
View File
@@ -282,9 +282,10 @@ async def stream_message(
)
db.add(assistant_message)
db.commit()
yield f"data: {json.dumps({'type': 'done', 'session_id': session.id}, ensure_ascii=False)}\n\n"
db.refresh(assistant_message)
yield f"data: {json.dumps({'type': 'done', 'session_id': session.id, 'message_id': assistant_message.id, 'user_message_id': user_message.id}, ensure_ascii=False)}\n\n"
else:
# 普通模式:使用LangChain 1.0对话链
print(f"[DEBUG-CHAT] 普通模式 - 使用LangChain流式对话链")
@@ -321,9 +322,10 @@ async def stream_message(
)
db.add(assistant_message)
db.commit()
yield f"data: {json.dumps({'type': 'done', 'session_id': session.id}, ensure_ascii=False)}\n\n"
db.refresh(assistant_message)
yield f"data: {json.dumps({'type': 'done', 'session_id': session.id, 'message_id': assistant_message.id, 'user_message_id': user_message.id}, ensure_ascii=False)}\n\n"
except Exception as e:
yield f"data: {json.dumps({'error': str(e)})}\n\n"
finally:
@@ -555,6 +557,11 @@ class MessageFeedbackRequest(BaseModel):
feedback: str # like, dislike
class RegenerateRequest(BaseModel):
"""重新生成请求模型"""
model: Optional[str] = None
@router.put("/messages/{message_id}")
async def edit_message(
message_id: int,
@@ -588,21 +595,40 @@ async def edit_message(
async def regenerate_message(
message_id: int,
current_user: str = Depends(get_current_user),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
request: RegenerateRequest = None,
):
"""重新生成AI回复"""
# 获取原始消息
original_message = db.query(ChatMessage).join(ChatSession).filter(
"""重新生成AI回复 — 支持 user 和 assistant 消息 ID"""
model = request.model if request else None
# 查找目标消息
target_message = db.query(ChatMessage).join(ChatSession).filter(
ChatMessage.id == message_id,
ChatMessage.role == "user",
ChatSession.user_id == get_user_id_by_username(db, current_user)
).first()
if not original_message:
if not target_message:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="消息不存在"
)
# 如果传入的是 assistant 消息,找到同 session 中前一条 user 消息
if target_message.role == "assistant":
original_message = db.query(ChatMessage).filter(
ChatMessage.session_id == target_message.session_id,
ChatMessage.role == "user",
ChatMessage.created_at < target_message.created_at
).order_by(ChatMessage.created_at.desc()).first()
if not original_message:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="找不到对应的用户消息"
)
# 删除该 assistant 消息本身
db.delete(target_message)
else:
original_message = target_message
# 删除该消息之后的所有消息
later_messages = db.query(ChatMessage).filter(
@@ -620,7 +646,8 @@ async def regenerate_message(
original_message.content,
original_message.session_id,
db,
None # No knowledge base filtering for regeneration
None, # No knowledge base filtering for regeneration
model
)
# 创建新的AI回复
@@ -679,7 +706,7 @@ async def feedback_message(
return {"message": "反馈提交成功", "feedback": request.feedback}
async def run_rag_workflow_with_context(question: str, session_id: int, db: Session, knowledge_base_ids: Optional[List[int]] = None) -> Dict[str, Any]:
async def run_rag_workflow_with_context(question: str, session_id: int, db: Session, knowledge_base_ids: Optional[List[int]] = None, model: Optional[str] = None) -> Dict[str, Any]:
"""运行带上下文的RAG工作流"""
try:
# 获取会话历史消息作为上下文
@@ -696,7 +723,7 @@ async def run_rag_workflow_with_context(question: str, session_id: int, db: Sess
context_messages.append({"role": "assistant", "content": msg.content})
# 运行RAG工作流
rag_chain = create_rag_chain(knowledge_base_ids=knowledge_base_ids)
rag_chain = create_rag_chain(knowledge_base_ids=knowledge_base_ids, model=model)
result = rag_chain.invoke(question)
# 如果有上下文,增强回答
@@ -710,5 +737,5 @@ async def run_rag_workflow_with_context(question: str, session_id: int, db: Sess
except Exception as e:
print(f"RAG工作流执行失败: {str(e)}")
# 降级到基础问答
rag_chain = create_rag_chain()
rag_chain = create_rag_chain(model=model)
return rag_chain.invoke(question)