Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions astrbot/core/agent/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ class AssistantMessageSegment(Message):
"""A message segment from the assistant."""

role: Literal["assistant"] = "assistant"
reasoning_content: str | None = None
"""The reasoning content from the assistant, if available (e.g., DeepSeek thinking mode)."""


class ToolCallMessageSegment(Message):
Expand Down
1 change: 1 addition & 0 deletions astrbot/core/agent/runners/tool_loop_agent_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ async def step(self):
tool_calls_info=AssistantMessageSegment(
tool_calls=llm_resp.to_openai_to_calls_model(),
content=llm_resp.completion_text,
reasoning_content=llm_resp.reasoning_content or "",
),
tool_calls_result=tool_call_result_blocks,
)
Expand Down
8 changes: 7 additions & 1 deletion astrbot/core/config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -932,13 +932,14 @@ class ChatProviderTemplate(TypedDict):
"DeepSeek": {
"id": "deepseek",
"provider": "deepseek",
"type": "openai_chat_completion",
"type": "deepseek_chat_completion",
"provider_type": "chat_completion",
"enable": True,
"key": [],
"api_base": "https://api.deepseek.com/v1",
"timeout": 120,
"custom_headers": {},
"ds_thinking_tool_call": False,
},
"Zhipu": {
"id": "zhipu",
Expand Down Expand Up @@ -1709,6 +1710,11 @@ class ChatProviderTemplate(TypedDict):
"type": "bool",
"hint": "启用后所有函数工具将全部失效",
},
"ds_thinking_tool_call": {
"description": "思考中调用工具",
"type": "bool",
"hint": "启用后,DeepSeek 模型可以在思考过程中调用工具,实现多轮思考-工具调用循环。",
},
"gm_url_context": {
"description": "启用URL上下文功能",
"type": "bool",
Expand Down
4 changes: 4 additions & 0 deletions astrbot/core/provider/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,10 @@ def dynamic_import_provider(self, type: str):
from .sources.openai_source import (
ProviderOpenAIOfficial as ProviderOpenAIOfficial,
)
case "deepseek_chat_completion":
from .sources.deepseek_source import (
ProviderDeepSeek as ProviderDeepSeek,
)
case "zhipu_chat_completion":
from .sources.zhipu_source import ProviderZhipu as ProviderZhipu
case "groq_chat_completion":
Expand Down
112 changes: 112 additions & 0 deletions astrbot/core/provider/sources/deepseek_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
from astrbot.core.agent.tool import ToolSet
from astrbot.core.provider.entities import LLMResponse

from ..entities import ProviderType
from ..register import register_provider_adapter
from .openai_source import ProviderOpenAIOfficial


@register_provider_adapter(
"deepseek_chat_completion",
"DeepSeek API Chat Completion 提供商适配器",
ProviderType.CHAT_COMPLETION,
default_config_tmpl={
"id": "deepseek",
"provider": "deepseek",
"type": "deepseek_chat_completion",
"provider_type": "chat_completion",
"enable": True,
"key": [],
"api_base": "https://api.deepseek.com/v1",
"timeout": 120,
"custom_headers": {},
"hint": "DeepSeek 官方 API,支持思考模式。",
},
provider_display_name="DeepSeek",
)
class ProviderDeepSeek(ProviderOpenAIOfficial):
def _should_enable_thinking(self, payloads: dict) -> bool:
"""判断是否应该启用思考模式
规则:
1. deepseek-chat 模型无条件关闭思维链
2. deepseek-reasoner 模型无条件开启思维链
3. 其他模型根据 ds_thinking_tool_call 配置决定
"""
model = payloads.get("model", "").lower()

# deepseek-chat 强制关闭
if "deepseek-chat" in model:
return False

# deepseek-reasoner 强制开启
if "deepseek-reasoner" in model or "reasoner" in model:
return True

# 其他模型根据配置决定
return self.provider_config.get("ds_thinking_tool_call", False)

async def _query_stream(
self,
payloads: dict,
tools: ToolSet | None,
):
# 判断是否启用思考模式
ds_thinking_enabled = self._should_enable_thinking(payloads)

if ds_thinking_enabled:
messages = payloads.get("messages", [])

# DeepSeek API 要求:思考模式下每条助手消息必须有 reasoning_content
# 先确保每条助手消息都有这个字段
for msg in messages:
if msg.get("role") == "assistant":
if "reasoning_content" not in msg:
# 工具调用消息等特殊消息初始化为空字符串
msg["reasoning_content"] = ""

# 清理历史消息中的 reasoning_content
# 只有最后一条是 user 时才清空(说明是新问题)
if messages and messages[-1].get("role") == "user":
for msg in messages:
if msg.get("role") == "assistant" and "reasoning_content" in msg:
msg["reasoning_content"] = ""

# 添加 thinking 参数(父类会自动把它放到 extra_body)
payloads["thinking"] = {"type": "enabled"}

# 调用父类方法
async for response in super()._query_stream(payloads, tools):
yield response

async def _query(
self,
payloads: dict,
tools: ToolSet | None,
) -> LLMResponse:
# 判断是否启用思考模式
ds_thinking_enabled = self._should_enable_thinking(payloads)

if ds_thinking_enabled:
messages = payloads.get("messages", [])

# DeepSeek API 要求:思考模式下每条助手消息必须有 reasoning_content
# 先确保每条助手消息都有这个字段
for msg in messages:
if msg.get("role") == "assistant":
if "reasoning_content" not in msg:
# 工具调用消息等特殊消息初始化为空字符串
msg["reasoning_content"] = ""

# 清理历史消息中的 reasoning_content
# 只有最后一条是 user 时才清空(说明是新问题)
if messages and messages[-1].get("role") == "user":
for msg in messages:
if msg.get("role") == "assistant" and "reasoning_content" in msg:
msg["reasoning_content"] = ""

# 添加 thinking 参数
payloads["thinking"] = {"type": "enabled"}

# 调用父类方法
return await super()._query(payloads, tools)