feat: add DeepSeek official API support and capture reasoning content

- Add DeepSeek provider routing: deepseek-chat/deepseek-reasoner use
  api.deepseek.com, other models use SiliconFlow
- Add stream_with_reasoning() using raw OpenAI SDK to capture
  reasoning_content (langchain_openai strips this field)
- RAG chain and conversation chain both use stream_with_reasoning
  for proper reasoning display in thinking models
- Frontend model selector: grouped by provider (DeepSeek official +
  SiliconFlow), default changed to deepseek-chat
- Regenerate message converted to streaming with reasoning capture
- Minor UI: globals.css additions, chat store refactoring

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 12:37:31 +08:00
parent 51d6fd22c0
commit ecd5c7ff31
9 changed files with 385 additions and 215 deletions
+10 -14
View File
@@ -24,7 +24,9 @@ class ConversationChain:
system_prompt: 系统提示词
model: 可选的模型名称,如 deepseek-ai/DeepSeek-V3, Qwen/QwQ-32B
"""
self.llm = get_llm_client(model=model).llm
_sf_client = get_llm_client(model=model)
self.llm = _sf_client.llm
self.client = _sf_client
self.system_prompt = system_prompt or "你是一个专业的国土空间规划知识问答助手。请基于你的知识回答用户的问题。"
# 创建带历史的Prompt模板
@@ -94,24 +96,18 @@ class ConversationChain:
yield {"type": "thinking", "stage": "generating", "message": "正在生成回答..."}
# 直接流式调用 LLM 以捕获推理内容
prompt_messages = await self.prompt.ainvoke({
# 直接使用原始 OpenAI SDK 捕获推理内容
prompt_value = await self.prompt.ainvoke({
"question": question,
"chat_history": history_messages
})
prompt_messages = prompt_value.to_messages()
content_started = False
async for chunk in self.llm.astream(prompt_messages):
# 捕获推理内容(DeepSeek-R1/QwQ 等推理模型)
if hasattr(chunk, 'additional_kwargs') and 'reasoning_content' in chunk.additional_kwargs:
reasoning_text = chunk.additional_kwargs['reasoning_content']
if reasoning_text:
yield {"type": "thinking", "stage": "reasoning", "message": reasoning_text}
elif hasattr(chunk, 'reasoning_content') and chunk.reasoning_content:
yield {"type": "thinking", "stage": "reasoning", "message": chunk.reasoning_content}
content = chunk.content if hasattr(chunk, 'content') else str(chunk)
if content:
async for event_type, content in self.client.stream_with_reasoning(prompt_messages):
if event_type == "reasoning":
yield {"type": "thinking", "stage": "reasoning", "message": content}
elif event_type == "content":
if not content_started:
content_started = True
yield {"type": "chunk", "content": content}