From 472a8605c0d64b66dd0e6dc81fa5e8169d9acfe3 Mon Sep 17 00:00:00 2001 From: zhayujie Date: Fri, 24 Apr 2026 11:35:38 +0800 Subject: [PATCH] feat(models): support deepseek-v4-pro and deepseek-v4-flash --- README.md | 284 ++++++++-------- channel/web/web_channel.py | 76 ++--- common/const.py | 44 +-- docs/cli/general.mdx | 4 +- docs/en/README.md | 12 +- docs/en/cli/general.mdx | 4 +- docs/en/models/custom.mdx | 2 +- docs/en/models/deepseek.mdx | 12 +- docs/en/models/index.mdx | 28 +- docs/ja/README.md | 12 +- docs/ja/cli/general.mdx | 4 +- docs/ja/models/custom.mdx | 2 +- docs/ja/models/deepseek.mdx | 10 +- docs/ja/models/index.mdx | 28 +- docs/models/deepseek.mdx | 11 +- docs/models/index.mdx | 28 +- models/deepseek/deepseek_bot.py | 500 +++++++++++++++++++++++++++- models/deepseek/deepseek_session.py | 2 +- run.sh | 45 ++- 19 files changed, 808 insertions(+), 300 deletions(-) diff --git a/README.md b/README.md index 3092c52f..4975ea72 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ - ✅ **工具系统:** 内置文件读写、终端执行、浏览器操作、定时任务等工具,Agent 自主调用以完成复杂任务 - ✅ **CLI系统:** 提供终端命令和对话命令,支持进程管理、技能安装、配置修改等操作 - ✅ **多模态消息:** 支持对文本、图片、语音、文件等多类型消息进行解析、处理、生成、发送等操作 -- ✅ **多模型支持:** 支持 OpenAI, Claude, Gemini, DeepSeek, MiniMax、GLM、Qwen、Kimi、Doubao 等国内外主流模型厂商 +- ✅ **多模型支持:** 支持 MiniMax、DeepSeek、Claude、Gemini、OpenAI、GLM、Qwen、Doubao、Kimi 等国内外主流模型厂商 - ✅ **多通道接入:** 支持运行在本地计算机或服务器,可集成到微信、飞书、钉钉、企业微信、QQ、微信公众号、网页中使用 ## 声明 @@ -313,47 +313,6 @@ sudo docker logs -f chatgpt-on-wechat 推荐通过 Web 控制台在线管理模型配置,无需手动编辑文件,详见 [模型文档](https://docs.cowagent.ai/models)。以下是手动修改 `config.json` 配置模型的说明: -
-OpenAI - -1. API Key 创建:在 [OpenAI平台](https://platform.openai.com/api-keys) 创建 API Key - -2. 填写配置 - -```json -{ - "model": "gpt-5.4", - "open_ai_api_key": "YOUR_API_KEY", - "open_ai_api_base": "https://api.openai.com/v1", - "bot_type": "openai" -} -``` - - - `model`: 与 OpenAI 接口的 [model参数](https://platform.openai.com/docs/models) 一致,支持包括 gpt-5.4、gpt-5.4-mini、gpt-5.4-nano、o 系列、gpt-4.1 等模型,Agent 模式推荐使用 `gpt-5.4`、`gpt-5.4-mini` - - `open_ai_api_base`: 如果需要接入第三方代理接口,可通过修改该参数进行接入 - - `bot_type`: 使用 OpenAI 相关模型时无需填写。当使用第三方代理接口接入 Claude 等非 OpenAI 官方模型时,该参数设为 `openai` -
- -
-LinkAI - -1. API Key 创建:在 [LinkAI平台](https://link-ai.tech/console/interface) 创建 API Key - -2. 填写配置 - -```json -{ - "model": "gpt-5.4-mini", - "use_linkai": true, - "linkai_api_key": "YOUR API KEY" -} -``` - -+ `use_linkai`: 是否使用 LinkAI 接口,默认关闭,设置为 true 后可对接 LinkAI 平台的模型,并使用知识库、工作流、数据库、插件等丰富的 Agent 技能 -+ `linkai_api_key`: LinkAI 平台的 API Key,可在 [控制台](https://link-ai.tech/console/interface) 中创建 -+ `model`: [模型列表](https://link-ai.tech/console/models)中的全部模型均可使用 -
-
MiniMax @@ -383,6 +342,89 @@ sudo docker logs -f chatgpt-on-wechat - `open_ai_api_key`: MiniMax 平台的 API-KEY
+
+DeepSeek + +1. API Key 创建:在 [DeepSeek 平台](https://platform.deepseek.com/api_keys) 创建 API Key + +2. 填写配置 + +方式一:官方接入(推荐): + +```json +{ + "model": "deepseek-v4-pro", + "deepseek_api_key": "sk-xxxxxxxxxxx" +} +``` + + - `model`: 推荐填写 `deepseek-v4-pro`、`deepseek-v4-flash` + - `deepseek_api_key`: DeepSeek 平台的 API Key + - `deepseek_api_base`: 可选,默认为 `https://api.deepseek.com/v1`,可修改为第三方代理地址 + +方式二:OpenAI 兼容方式接入: + +```json +{ + "model": "deepseek-v4-pro", + "bot_type": "openai", + "open_ai_api_key": "sk-xxxxxxxxxxx", + "open_ai_api_base": "https://api.deepseek.com/v1" +} +``` + +
+ +
+Claude + +1. API Key 创建:在 [Claude控制台](https://console.anthropic.com/settings/keys) 创建 API Key + +2. 填写配置 + +```json +{ + "model": "claude-sonnet-4-6", + "claude_api_key": "YOUR_API_KEY" +} +``` + - `model`: 参考 [官方模型ID](https://docs.anthropic.com/en/docs/about-claude/models/overview#model-aliases) ,支持 `claude-sonnet-4-6、claude-opus-4-7、claude-opus-4-6、claude-sonnet-4-5、claude-sonnet-4-0、claude-opus-4-0、claude-3-5-sonnet-latest` 等 +
+ +
+Gemini + +API Key 创建:在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn) 创建 API Key ,配置如下 +```json +{ + "model": "gemini-3.1-flash-lite-preview", + "gemini_api_key": "" +} +``` + - `model`: 参考[官方文档-模型列表](https://ai.google.dev/gemini-api/docs/models?hl=zh-cn),支持 `gemini-3.1-flash-lite-preview、gemini-3.1-pro-preview、gemini-3-flash-preview、gemini-3-pro-preview` 等 +
+ +
+OpenAI + +1. API Key 创建:在 [OpenAI平台](https://platform.openai.com/api-keys) 创建 API Key + +2. 填写配置 + +```json +{ + "model": "gpt-5.4", + "open_ai_api_key": "YOUR_API_KEY", + "open_ai_api_base": "https://api.openai.com/v1", + "bot_type": "openai" +} +``` + + - `model`: 与 OpenAI 接口的 [model参数](https://platform.openai.com/docs/models) 一致,支持包括 gpt-5.4、gpt-5.4-mini、gpt-5.4-nano、o 系列、gpt-4.1 等模型,Agent 模式推荐使用 `gpt-5.4`、`gpt-5.4-mini` + - `open_ai_api_base`: 如果需要接入第三方代理接口,可通过修改该参数进行接入 + - `bot_type`: 使用 OpenAI 相关模型时无需填写。当使用第三方代理接口接入 Claude 等非 OpenAI 官方模型时,该参数设为 `openai` +
+
智谱AI (GLM) @@ -441,35 +483,6 @@ sudo docker logs -f chatgpt-on-wechat - `open_ai_api_key`: 通义千问的 API-KEY
-
-Kimi (Moonshot) - -方式一:官方接入,配置如下: - -```json -{ - "model": "kimi-k2.6", - "moonshot_api_key": "" -} -``` - - `model`: 可填写 `kimi-k2.6、kimi-k2.5、kimi-k2、moonshot-v1-8k、moonshot-v1-32k、moonshot-v1-128k` - - `moonshot_api_key`: Moonshot 的 API-KEY,在 [控制台](https://platform.moonshot.cn/console/api-keys) 创建 - -方式二:OpenAI 兼容方式接入,配置如下: -```json -{ - "bot_type": "openai", - "model": "kimi-k2.6", - "open_ai_api_base": "https://api.moonshot.cn/v1", - "open_ai_api_key": "" -} -``` -- `bot_type`: OpenAI 兼容方式 -- `model`: 可填写 `kimi-k2.6、kimi-k2.5、kimi-k2、moonshot-v1-8k、moonshot-v1-32k、moonshot-v1-128k` -- `open_ai_api_base`: Moonshot 的 BASE URL -- `open_ai_api_key`: Moonshot 的 API-KEY -
-
豆包 (Doubao) @@ -489,67 +502,74 @@ sudo docker logs -f chatgpt-on-wechat
-Claude +Kimi (Moonshot) -1. API Key 创建:在 [Claude控制台](https://console.anthropic.com/settings/keys) 创建 API Key +方式一:官方接入,配置如下: + +```json +{ + "model": "kimi-k2.6", + "moonshot_api_key": "" +} +``` + - `model`: 可填写 `kimi-k2.6、kimi-k2.5、kimi-k2、moonshot-v1-8k、moonshot-v1-32k、moonshot-v1-128k` + - `moonshot_api_key`: Moonshot 的 API-KEY,在 [控制台](https://platform.moonshot.cn/console/api-keys) 创建 + +方式二:OpenAI 兼容方式接入,配置如下: +```json +{ + "bot_type": "openai", + "model": "kimi-k2.6", + "open_ai_api_base": "https://api.moonshot.cn/v1", + "open_ai_api_key": "" +} +``` +- `bot_type`: OpenAI 兼容方式 +- `model`: 可填写 `kimi-k2.6、kimi-k2.5、kimi-k2、moonshot-v1-8k、moonshot-v1-32k、moonshot-v1-128k` +- `open_ai_api_base`: Moonshot 的 BASE URL +- `open_ai_api_key`: Moonshot 的 API-KEY +
+ +
+ModelScope + +```json +{ + "bot_type": "modelscope", + "model": "Qwen/QwQ-32B", + "modelscope_api_key": "your_api_key", + "modelscope_base_url": "https://api-inference.modelscope.cn/v1/chat/completions", + "text_to_image": "MusePublic/489_ckpt_FLUX_1" +} +``` + +- `bot_type`: modelscope 接口格式 +- `model`: 参考[模型列表](https://www.modelscope.cn/models?filter=inference_type&page=1) +- `modelscope_api_key`: 参考 [官方文档-访问令牌](https://modelscope.cn/docs/accounts/token) ,在 [控制台](https://modelscope.cn/my/myaccesstoken) +- `modelscope_base_url`: modelscope 平台的 BASE URL +- `text_to_image`: 图像生成模型,参考[模型列表](https://www.modelscope.cn/models?filter=inference_type&page=1) +
+ +
+LinkAI + +1. API Key 创建:在 [LinkAI平台](https://link-ai.tech/console/interface) 创建 API Key 2. 填写配置 ```json { - "model": "claude-sonnet-4-6", - "claude_api_key": "YOUR_API_KEY" + "model": "gpt-5.4-mini", + "use_linkai": true, + "linkai_api_key": "YOUR API KEY" } ``` - - `model`: 参考 [官方模型ID](https://docs.anthropic.com/en/docs/about-claude/models/overview#model-aliases) ,支持 `claude-sonnet-4-6、claude-opus-4-7、claude-opus-4-6、claude-sonnet-4-5、claude-sonnet-4-0、claude-opus-4-0、claude-3-5-sonnet-latest` 等 + ++ `use_linkai`: 是否使用 LinkAI 接口,默认关闭,设置为 true 后可对接 LinkAI 平台的模型,并使用知识库、工作流、数据库、插件等丰富的 Agent 技能 ++ `linkai_api_key`: LinkAI 平台的 API Key,可在 [控制台](https://link-ai.tech/console/interface) 中创建 ++ `model`: [模型列表](https://link-ai.tech/console/models)中的全部模型均可使用
-
-Gemini - -API Key 创建:在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn) 创建 API Key ,配置如下 -```json -{ - "model": "gemini-3.1-flash-lite-preview", - "gemini_api_key": "" -} -``` - - `model`: 参考[官方文档-模型列表](https://ai.google.dev/gemini-api/docs/models?hl=zh-cn),支持 `gemini-3.1-flash-lite-preview、gemini-3.1-pro-preview、gemini-3-flash-preview、gemini-3-pro-preview` 等 -
- -
-DeepSeek - -1. API Key 创建:在 [DeepSeek 平台](https://platform.deepseek.com/api_keys) 创建 API Key - -2. 填写配置 - -方式一:官方接入(推荐): - -```json -{ - "model": "deepseek-chat", - "deepseek_api_key": "sk-xxxxxxxxxxx" -} -``` - - - `model`: 可填 `deepseek-chat、deepseek-reasoner`,分别对应的是 DeepSeek-V3.2(非思考模式)和 DeepSeek-R1(思考模式) - - `deepseek_api_key`: DeepSeek 平台的 API Key - - `deepseek_api_base`: 可选,默认为 `https://api.deepseek.com/v1`,可修改为第三方代理地址 - -方式二:OpenAI 兼容方式接入: - -```json -{ - "model": "deepseek-chat", - "bot_type": "openai", - "open_ai_api_key": "sk-xxxxxxxxxxx", - "open_ai_api_base": "https://api.deepseek.com/v1" -} -``` - -
-
Azure @@ -642,26 +662,6 @@ API Key 创建:在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn - `open_ai_api_key`: 讯飞星火平台的[APIPassword](https://console.xfyun.cn/services/bm3) ,因模型而已
-
-ModelScope - -```json -{ - "bot_type": "modelscope", - "model": "Qwen/QwQ-32B", - "modelscope_api_key": "your_api_key", - "modelscope_base_url": "https://api-inference.modelscope.cn/v1/chat/completions", - "text_to_image": "MusePublic/489_ckpt_FLUX_1" -} -``` - -- `bot_type`: modelscope 接口格式 -- `model`: 参考[模型列表](https://www.modelscope.cn/models?filter=inference_type&page=1) -- `modelscope_api_key`: 参考 [官方文档-访问令牌](https://modelscope.cn/docs/accounts/token) ,在 [控制台](https://modelscope.cn/my/myaccesstoken) -- `modelscope_base_url`: modelscope 平台的 BASE URL -- `text_to_image`: 图像生成模型,参考[模型列表](https://www.modelscope.cn/models?filter=inference_type&page=1) -
-
Coding Plan diff --git a/channel/web/web_channel.py b/channel/web/web_channel.py index 2c21ceb9..ad1301aa 100644 --- a/channel/web/web_channel.py +++ b/channel/web/web_channel.py @@ -771,14 +771,14 @@ class ConfigHandler: _RECOMMENDED_MODELS = [ const.MINIMAX_M2_7_HIGHSPEED, const.MINIMAX_M2_7, const.MINIMAX_M2_5, const.MINIMAX_M2_1, const.MINIMAX_M2_1_LIGHTNING, - const.GLM_5_1, const.GLM_5_TURBO, const.GLM_5, const.GLM_4_7, - const.QWEN36_PLUS, const.QWEN35_PLUS, const.QWEN3_MAX, - const.KIMI_K2_6, const.KIMI_K2_5, const.KIMI_K2, - const.DOUBAO_SEED_2_PRO, const.DOUBAO_SEED_2_CODE, + const.DEEPSEEK_V4_PRO, const.DEEPSEEK_V4_FLASH, const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER, const.CLAUDE_4_6_SONNET, const.CLAUDE_4_7_OPUS, const.CLAUDE_4_6_OPUS, const.CLAUDE_4_5_SONNET, const.GEMINI_31_FLASH_LITE_PRE, const.GEMINI_31_PRO_PRE, const.GEMINI_3_FLASH_PRE, const.GPT_54, const.GPT_54_MINI, const.GPT_54_NANO, const.GPT_5, const.GPT_41, const.GPT_4o, - const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER, + const.GLM_5_1, const.GLM_5_TURBO, const.GLM_5, const.GLM_4_7, + const.QWEN36_PLUS, const.QWEN35_PLUS, const.QWEN3_MAX, + const.DOUBAO_SEED_2_PRO, const.DOUBAO_SEED_2_CODE, + const.KIMI_K2_6, const.KIMI_K2_5, const.KIMI_K2, ] PROVIDER_MODELS = OrderedDict([ @@ -789,33 +789,12 @@ class ConfigHandler: "api_base_default": None, "models": [const.MINIMAX_M2_7, const.MINIMAX_M2_7_HIGHSPEED, const.MINIMAX_M2_5, const.MINIMAX_M2_1, const.MINIMAX_M2_1_LIGHTNING], }), - ("zhipu", { - "label": "智谱AI", - "api_key_field": "zhipu_ai_api_key", - "api_base_key": "zhipu_ai_api_base", - "api_base_default": "https://open.bigmodel.cn/api/paas/v4", - "models": [const.GLM_5_1, const.GLM_5_TURBO, const.GLM_5, const.GLM_4_7], - }), - ("dashscope", { - "label": "通义千问", - "api_key_field": "dashscope_api_key", - "api_base_key": None, - "api_base_default": None, - "models": [const.QWEN36_PLUS, const.QWEN35_PLUS, const.QWEN3_MAX], - }), - ("moonshot", { - "label": "Kimi", - "api_key_field": "moonshot_api_key", - "api_base_key": "moonshot_base_url", - "api_base_default": "https://api.moonshot.cn/v1", - "models": [const.KIMI_K2_6, const.KIMI_K2_5, const.KIMI_K2], - }), - ("doubao", { - "label": "豆包", - "api_key_field": "ark_api_key", - "api_base_key": "ark_base_url", - "api_base_default": "https://ark.cn-beijing.volces.com/api/v3", - "models": [const.DOUBAO_SEED_2_PRO, const.DOUBAO_SEED_2_CODE], + ("deepseek", { + "label": "DeepSeek", + "api_key_field": "deepseek_api_key", + "api_base_key": "deepseek_api_base", + "api_base_default": "https://api.deepseek.com/v1", + "models": [const.DEEPSEEK_V4_PRO, const.DEEPSEEK_V4_FLASH, const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER], }), ("claudeAPI", { "label": "Claude", @@ -838,12 +817,33 @@ class ConfigHandler: "api_base_default": "https://api.openai.com/v1", "models": [const.GPT_54, const.GPT_54_MINI, const.GPT_54_NANO, const.GPT_5, const.GPT_41, const.GPT_4o], }), - ("deepseek", { - "label": "DeepSeek", - "api_key_field": "deepseek_api_key", - "api_base_key": "deepseek_api_base", - "api_base_default": "https://api.deepseek.com/v1", - "models": [const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER], + ("zhipu", { + "label": "智谱AI", + "api_key_field": "zhipu_ai_api_key", + "api_base_key": "zhipu_ai_api_base", + "api_base_default": "https://open.bigmodel.cn/api/paas/v4", + "models": [const.GLM_5_1, const.GLM_5_TURBO, const.GLM_5, const.GLM_4_7], + }), + ("dashscope", { + "label": "通义千问", + "api_key_field": "dashscope_api_key", + "api_base_key": None, + "api_base_default": None, + "models": [const.QWEN36_PLUS, const.QWEN35_PLUS, const.QWEN3_MAX], + }), + ("doubao", { + "label": "豆包", + "api_key_field": "ark_api_key", + "api_base_key": "ark_base_url", + "api_base_default": "https://ark.cn-beijing.volces.com/api/v3", + "models": [const.DOUBAO_SEED_2_PRO, const.DOUBAO_SEED_2_CODE], + }), + ("moonshot", { + "label": "Kimi", + "api_key_field": "moonshot_api_key", + "api_base_key": "moonshot_base_url", + "api_base_default": "https://api.moonshot.cn/v1", + "models": [const.KIMI_K2_6, const.KIMI_K2_5, const.KIMI_K2], }), ("modelscope", { "label": "ModelScope", diff --git a/common/const.py b/common/const.py index 94b7c955..b9b945fe 100644 --- a/common/const.py +++ b/common/const.py @@ -82,6 +82,8 @@ TTS_1_HD = "tts-1-hd" # DeepSeek DEEPSEEK_CHAT = "deepseek-chat" # DeepSeek-V3对话模型 DEEPSEEK_REASONER = "deepseek-reasoner" # DeepSeek-R1模型 +DEEPSEEK_V4_FLASH = "deepseek-v4-flash" # DeepSeek V4 Flash - 思考模式 + 工具调用 +DEEPSEEK_V4_PRO = "deepseek-v4-pro" # DeepSeek V4 Pro - Agent推荐模型 (思考模式 + 工具调用) # Qwen (通义千问 - 阿里云 DashScope) QWEN_TURBO = "qwen-turbo" @@ -154,15 +156,21 @@ MODELSCOPE_MODEL_LIST = ["deepseek-ai/DeepSeek-R1-0528", "deepseek-ai/DeepSeek-R MODEL_LIST = [ + # MiniMax + MiniMax, MINIMAX_M2_7, MINIMAX_M2_7_HIGHSPEED, MINIMAX_M2_5, MINIMAX_M2_1, MINIMAX_M2_1_LIGHTNING, MINIMAX_M2, MINIMAX_ABAB6_5, + + # DeepSeek + DEEPSEEK_V4_PRO, DEEPSEEK_V4_FLASH, DEEPSEEK_CHAT, DEEPSEEK_REASONER, + # Claude - CLAUDE3, CLAUDE_4_6_SONNET, CLAUDE_4_7_OPUS, CLAUDE_4_6_OPUS, CLAUDE_4_OPUS, CLAUDE_4_5_SONNET, CLAUDE_4_SONNET, CLAUDE_3_OPUS, CLAUDE_3_OPUS_0229, - CLAUDE_35_SONNET, CLAUDE_35_SONNET_1022, CLAUDE_35_SONNET_0620, CLAUDE_3_SONNET, CLAUDE_3_HAIKU, + CLAUDE3, CLAUDE_4_6_SONNET, CLAUDE_4_7_OPUS, CLAUDE_4_6_OPUS, CLAUDE_4_OPUS, CLAUDE_4_5_SONNET, CLAUDE_4_SONNET, CLAUDE_3_OPUS, CLAUDE_3_OPUS_0229, + CLAUDE_35_SONNET, CLAUDE_35_SONNET_1022, CLAUDE_35_SONNET_0620, CLAUDE_3_SONNET, CLAUDE_3_HAIKU, "claude", "claude-3-haiku", "claude-3-sonnet", "claude-3-opus", "claude-3.5-sonnet", - + # Gemini GEMINI_31_FLASH_LITE_PRE, GEMINI_31_PRO_PRE, GEMINI_3_PRO_PRE, GEMINI_3_FLASH_PRE, GEMINI_25_PRO_PRE, GEMINI_25_FLASH_PRE, GEMINI_20_FLASH, GEMINI_20_flash_exp, GEMINI_15_PRO, GEMINI_15_flash, GEMINI_PRO, GEMINI, - + # OpenAI GPT35, GPT35_0125, GPT35_1106, "gpt-3.5-turbo-16k", GPT4, GPT4_06_13, GPT4_32k, GPT4_32k_06_13, @@ -172,31 +180,29 @@ MODEL_LIST = [ GPT_5, GPT_5_MINI, GPT_5_NANO, GPT_54, GPT_54_MINI, GPT_54_NANO, O1, O1_MINI, - - # DeepSeek - DEEPSEEK_CHAT, DEEPSEEK_REASONER, - - # Qwen - QWEN36_PLUS, QWEN35_PLUS, QWEN3_MAX, QWEN_MAX, QWEN_PLUS, QWEN_TURBO, QWEN_LONG, - - # MiniMax - MiniMax, MINIMAX_M2_7, MINIMAX_M2_7_HIGHSPEED, MINIMAX_M2_5, MINIMAX_M2_1, MINIMAX_M2_1_LIGHTNING, MINIMAX_M2, MINIMAX_ABAB6_5, - # GLM + # GLM (智谱AI) ZHIPU_AI, GLM_5_1, GLM_5_TURBO, GLM_5, GLM_4, GLM_4_PLUS, GLM_4_flash, GLM_4_LONG, GLM_4_ALLTOOLS, GLM_4_0520, GLM_4_AIR, GLM_4_AIRX, GLM_4_7, - # Kimi + # Qwen (通义千问) + QWEN36_PLUS, QWEN35_PLUS, QWEN3_MAX, QWEN_MAX, QWEN_PLUS, QWEN_TURBO, QWEN_LONG, + + # Doubao (豆包) + DOUBAO, DOUBAO_SEED_2_CODE, DOUBAO_SEED_2_PRO, DOUBAO_SEED_2_LITE, DOUBAO_SEED_2_MINI, + + # Kimi (Moonshot) MOONSHOT, "moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k", KIMI_K2_6, KIMI_K2_5, KIMI_K2, - # Doubao - DOUBAO, DOUBAO_SEED_2_CODE, DOUBAO_SEED_2_PRO, DOUBAO_SEED_2_LITE, DOUBAO_SEED_2_MINI, + # ModelScope + MODELSCOPE, + + # LinkAI + LINKAI_35, LINKAI_4_TURBO, LINKAI_4o, # 其他模型 WEN_XIN, WEN_XIN_4, XUNFEI, - LINKAI_35, LINKAI_4_TURBO, LINKAI_4o, - MODELSCOPE ] MODEL_LIST = MODEL_LIST + GITEE_AI_MODEL_LIST + MODELSCOPE_MODEL_LIST diff --git a/docs/cli/general.mdx b/docs/cli/general.mdx index 62d69ad2..5b972da0 100644 --- a/docs/cli/general.mdx +++ b/docs/cli/general.mdx @@ -58,14 +58,14 @@ Session: 12 messages | 8 skills loaded **修改配置项:** ```text -/config model deepseek-chat +/config model deepseek-v4-pro ``` **支持修改的配置项:** | 配置项 | 说明 | 示例值 | | --- | --- | --- | -| `model` | AI 模型名称 | `deepseek-chat` | +| `model` | AI 模型名称 | `deepseek-v4-pro` | | `agent_max_context_tokens` | 最大上下文 tokens | `40000` | | `agent_max_context_turns` | 最大上下文记忆轮次 | `30` | | `agent_max_steps` | 单次任务最大决策步数 | `15` | diff --git a/docs/en/README.md b/docs/en/README.md index c1c59f97..a61d47e0 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -28,7 +28,7 @@ - ✅ **Tool System**: Built-in tools for file I/O, terminal execution, browser automation, scheduled tasks, messaging, and more — autonomously invoked by the Agent. - ✅ **CLI System**: Provides terminal commands and in-chat commands for process management, skill installation, configuration, and more. - ✅ **Multimodal Messages**: Supports parsing, processing, generating, and sending text, images, voice, files, and other message types. -- ✅ **Multiple Model Support**: Supports OpenAI, Claude, Gemini, DeepSeek, MiniMax, GLM, Qwen, Kimi, Doubao, and other mainstream model providers. +- ✅ **Multiple Model Support**: Supports MiniMax, DeepSeek, Claude, Gemini, OpenAI, GLM, Qwen, Doubao, Kimi, and other mainstream model providers. - ✅ **Multi-platform Deployment**: Runs on local computers or servers, integrable into WeChat, Web, Feishu, DingTalk, WeChat Official Account, and WeCom applications. ## Disclaimer @@ -165,14 +165,14 @@ Supports mainstream model providers. Recommended models for Agent mode: | Provider | Recommended Model | | --- | --- | | MiniMax | `MiniMax-M2.7` | -| GLM | `glm-5.1` | -| Kimi | `kimi-k2.6` | -| Doubao | `doubao-seed-2-0-code-preview-260215` | -| Qwen | `qwen3.6-plus` | +| DeepSeek | `deepseek-v4-pro` | | Claude | `claude-sonnet-4-6` | | Gemini | `gemini-3.1-pro-preview` | | OpenAI | `gpt-5.4` | -| DeepSeek | `deepseek-chat` | +| GLM | `glm-5.1` | +| Qwen | `qwen3.6-plus` | +| Doubao | `doubao-seed-2-0-code-preview-260215` | +| Kimi | `kimi-k2.6` | For detailed configuration of each model, see the [Models documentation](https://docs.cowagent.ai/en/models/index). diff --git a/docs/en/cli/general.mdx b/docs/en/cli/general.mdx index 3f55425e..8d6dce68 100644 --- a/docs/en/cli/general.mdx +++ b/docs/en/cli/general.mdx @@ -44,14 +44,14 @@ View or modify runtime configuration. Changes take effect immediately without re **Modify a config item:** ```text -/config model deepseek-chat +/config model deepseek-v4-pro ``` **Configurable items:** | Item | Description | Example | | --- | --- | --- | -| `model` | AI model name | `deepseek-chat` | +| `model` | AI model name | `deepseek-v4-pro` | | `agent_max_context_tokens` | Max context tokens | `40000` | | `agent_max_context_turns` | Max context memory turns | `30` | | `agent_max_steps` | Max decision steps per task | `15` | diff --git a/docs/en/models/custom.mdx b/docs/en/models/custom.mdx index 94bc825c..d422c333 100644 --- a/docs/en/models/custom.mdx +++ b/docs/en/models/custom.mdx @@ -20,7 +20,7 @@ For models accessed via OpenAI-compatible APIs, such as: ```json { "bot_type": "custom", - "model": "deepseek-chat", + "model": "deepseek-v4-pro", "custom_api_key": "YOUR_API_KEY", "custom_api_base": "https://{your-proxy.com}/v1" } diff --git a/docs/en/models/deepseek.mdx b/docs/en/models/deepseek.mdx index ae765e3b..e39b4db3 100644 --- a/docs/en/models/deepseek.mdx +++ b/docs/en/models/deepseek.mdx @@ -7,26 +7,28 @@ Option 1: Native integration (recommended): ```json { - "model": "deepseek-chat", + "model": "deepseek-v4-pro", "deepseek_api_key": "YOUR_API_KEY" } ``` | Parameter | Description | | --- | --- | -| `model` | `deepseek-chat` (DeepSeek-V3.2, non-thinking mode), `deepseek-reasoner` (DeepSeek-R1, thinking mode) | +| `model` | `deepseek-v4-pro` (V4 Pro, thinking mode + tool calls, Agent recommended), `deepseek-v4-flash` (V4 Flash, thinking mode + tool calls), `deepseek-chat` (DeepSeek-V3.2, non-thinking), `deepseek-reasoner` (DeepSeek-R1, thinking mode) | | `deepseek_api_key` | Create at [DeepSeek Platform](https://platform.deepseek.com/api_keys) | | `deepseek_api_base` | Optional, defaults to `https://api.deepseek.com/v1`. Can be changed to a third-party proxy | + + The V4 series (`deepseek-v4-pro`, `deepseek-v4-flash`) supports thinking mode together with tool calls, enabling multi-round reasoning + tool orchestration in Agent mode. The thinking switch is controlled by the global `enable_thinking` setting, and `reasoning_content` is automatically round-tripped on tool-call turns. + + Option 2: OpenAI-compatible configuration: ```json { - "model": "deepseek-chat", + "model": "deepseek-v4-pro", "bot_type": "openai", "open_ai_api_key": "YOUR_API_KEY", "open_ai_api_base": "https://api.deepseek.com/v1" } ``` - - diff --git a/docs/en/models/index.mdx b/docs/en/models/index.mdx index dab4f434..ecac9729 100644 --- a/docs/en/models/index.mdx +++ b/docs/en/models/index.mdx @@ -6,7 +6,7 @@ description: Supported models and recommended choices for CowAgent CowAgent supports mainstream LLMs from domestic and international providers. Model interfaces are implemented in the project's `models/` directory. - For Agent mode, the following models are recommended based on quality and cost: MiniMax-M2.7, glm-5.1, kimi-k2.6, qwen3.6-plus, claude-sonnet-4-6, gemini-3.1-pro-preview + For Agent mode, the following models are recommended based on quality and cost: MiniMax-M2.7, deepseek-v4-pro, claude-sonnet-4-6, gemini-3.1-pro-preview, glm-5.1, qwen3.6-plus, kimi-k2.6 ## Configuration @@ -21,17 +21,8 @@ You can also use the [LinkAI](https://link-ai.tech) platform interface to flexib MiniMax-M2.7 and other series models - - glm-5.1, glm-5-turbo, glm-5 and other series models - - - qwen3.6-plus, qwen3-max and more - - - kimi-k2.6, kimi-k2.5, kimi-k2 and more - - - doubao-seed series models + + deepseek-v4-pro, deepseek-v4-flash, deepseek-chat, deepseek-reasoner claude-sonnet-4-6 and more @@ -42,8 +33,17 @@ You can also use the [LinkAI](https://link-ai.tech) platform interface to flexib gpt-5.4, gpt-4.1, o-series and more - - deepseek-chat, deepseek-reasoner + + glm-5.1, glm-5-turbo, glm-5 and other series models + + + qwen3.6-plus, qwen3-max and more + + + doubao-seed series models + + + kimi-k2.6, kimi-k2.5, kimi-k2 and more Unified multi-model interface + knowledge base diff --git a/docs/ja/README.md b/docs/ja/README.md index ad01cdd7..4404622a 100644 --- a/docs/ja/README.md +++ b/docs/ja/README.md @@ -28,7 +28,7 @@ - ✅ **ツールシステム**: ファイル読み書き、ターミナル実行、ブラウザ操作、スケジュールタスク、メッセージ送信などの組み込みツールを提供。Agentが自律的に呼び出して複雑なタスクを完了します。 - ✅ **CLIシステム**: ターミナルコマンドとチャットコマンドを提供し、プロセス管理、Skillインストール、設定変更などの操作をサポートします。 - ✅ **マルチモーダルメッセージ**: テキスト、画像、音声、ファイルなど、さまざまなメッセージタイプの解析・処理・生成・送信に対応しています。 -- ✅ **複数モデル対応**: OpenAI、Claude、Gemini、DeepSeek、MiniMax、GLM、Qwen、Kimi、Doubaoなど、主要なモデルプロバイダーに対応しています。 +- ✅ **複数モデル対応**: MiniMax、DeepSeek、Claude、Gemini、OpenAI、GLM、Qwen、Doubao、Kimiなど、主要なモデルプロバイダーに対応しています。 - ✅ **マルチプラットフォームデプロイ**: ローカルPCやサーバー上で実行でき、WeChat、Web、Feishu、DingTalk、WeChat公式アカウント、WeComアプリケーションに統合可能です。 ## 免責事項 @@ -165,14 +165,14 @@ sudo docker logs -f chatgpt-on-wechat | プロバイダー | 推奨モデル | | --- | --- | | MiniMax | `MiniMax-M2.7` | -| GLM | `glm-5.1` | -| Kimi | `kimi-k2.6` | -| Doubao | `doubao-seed-2-0-code-preview-260215` | -| Qwen | `qwen3.6-plus` | +| DeepSeek | `deepseek-v4-pro` | | Claude | `claude-sonnet-4-6` | | Gemini | `gemini-3.1-pro-preview` | | OpenAI | `gpt-5.4` | -| DeepSeek | `deepseek-chat` | +| GLM | `glm-5.1` | +| Qwen | `qwen3.6-plus` | +| Doubao | `doubao-seed-2-0-code-preview-260215` | +| Kimi | `kimi-k2.6` | 各モデルの詳細設定については、[モデルドキュメント](https://docs.cowagent.ai/en/models/index)を参照してください。 diff --git a/docs/ja/cli/general.mdx b/docs/ja/cli/general.mdx index d65c3464..de597b01 100644 --- a/docs/ja/cli/general.mdx +++ b/docs/ja/cli/general.mdx @@ -44,14 +44,14 @@ description: ステータスの確認、設定管理、コンテキスト制御 **設定項目を変更:** ```text -/config model deepseek-chat +/config model deepseek-v4-pro ``` **変更可能な設定項目:** | 項目 | 説明 | 例 | | --- | --- | --- | -| `model` | AI モデル名 | `deepseek-chat` | +| `model` | AI モデル名 | `deepseek-v4-pro` | | `agent_max_context_tokens` | 最大コンテキストトークン数 | `40000` | | `agent_max_context_turns` | 最大コンテキスト記憶ターン数 | `30` | | `agent_max_steps` | タスクごとの最大判断ステップ数 | `15` | diff --git a/docs/ja/models/custom.mdx b/docs/ja/models/custom.mdx index 7746faa9..f03c244a 100644 --- a/docs/ja/models/custom.mdx +++ b/docs/ja/models/custom.mdx @@ -20,7 +20,7 @@ OpenAI互換プロトコルでアクセスするモデルサービスに適用 ```json { "bot_type": "custom", - "model": "deepseek-chat", + "model": "deepseek-v4-pro", "custom_api_key": "YOUR_API_KEY", "custom_api_base": "https://{your-proxy.com}/v1" } diff --git a/docs/ja/models/deepseek.mdx b/docs/ja/models/deepseek.mdx index 168c459d..9d9242c6 100644 --- a/docs/ja/models/deepseek.mdx +++ b/docs/ja/models/deepseek.mdx @@ -7,22 +7,26 @@ description: DeepSeekモデルの設定 ```json { - "model": "deepseek-chat", + "model": "deepseek-v4-pro", "deepseek_api_key": "YOUR_API_KEY" } ``` | パラメータ | 説明 | | --- | --- | -| `model` | `deepseek-chat`(DeepSeek-V3.2、非思考モード)、`deepseek-reasoner`(DeepSeek-R1、思考モード) | +| `model` | `deepseek-v4-pro`(V4 Pro、思考モード + ツール呼び出し、Agent推奨)、`deepseek-v4-flash`(V4 Flash、思考モード + ツール呼び出し)、`deepseek-chat`(DeepSeek-V3.2、非思考モード)、`deepseek-reasoner`(DeepSeek-R1、思考モード) | | `deepseek_api_key` | [DeepSeek Platform](https://platform.deepseek.com/api_keys)で作成 | | `deepseek_api_base` | オプション、デフォルトは `https://api.deepseek.com/v1`。サードパーティプロキシに変更可能 | + + V4シリーズ(`deepseek-v4-pro`、`deepseek-v4-flash`)は思考モードとツール呼び出しに対応しており、Agentモードでの多段思考とツール連携が可能です。思考のオン/オフはグローバル設定 `enable_thinking` で制御され、ツール呼び出しのターンでは `reasoning_content` が自動的にAPIへ往復されます。 + + 方法2:OpenAI互換方式: ```json { - "model": "deepseek-chat", + "model": "deepseek-v4-pro", "bot_type": "openai", "open_ai_api_key": "YOUR_API_KEY", "open_ai_api_base": "https://api.deepseek.com/v1" diff --git a/docs/ja/models/index.mdx b/docs/ja/models/index.mdx index 2d231a19..33f5121f 100644 --- a/docs/ja/models/index.mdx +++ b/docs/ja/models/index.mdx @@ -6,7 +6,7 @@ description: CowAgentがサポートするモデルとおすすめの選択肢 CowAgentは国内外の主要なLLMをサポートしています。モデルインターフェースはプロジェクトの`models/`ディレクトリに実装されています。 - Agent モードでは、品質とコストのバランスから以下のモデルをおすすめします: MiniMax-M2.7、glm-5.1、kimi-k2.6、qwen3.6-plus、claude-sonnet-4-6、gemini-3.1-pro-preview + Agent モードでは、品質とコストのバランスから以下のモデルをおすすめします: MiniMax-M2.7、deepseek-v4-pro、claude-sonnet-4-6、gemini-3.1-pro-preview、glm-5.1、qwen3.6-plus、kimi-k2.6 ## 設定 @@ -21,17 +21,8 @@ CowAgentは国内外の主要なLLMをサポートしています。モデルイ MiniMax-M2.7およびその他のシリーズモデル - - glm-5.1、glm-5-turbo、glm-5およびその他のシリーズモデル - - - qwen3.6-plus、qwen3-maxなど - - - kimi-k2.6、kimi-k2.5、kimi-k2など - - - doubao-seedシリーズモデル + + deepseek-v4-pro、deepseek-v4-flash、deepseek-chat、deepseek-reasoner claude-sonnet-4-6など @@ -42,8 +33,17 @@ CowAgentは国内外の主要なLLMをサポートしています。モデルイ gpt-5.4、gpt-4.1、oシリーズなど - - deepseek-chat、deepseek-reasoner + + glm-5.1、glm-5-turbo、glm-5およびその他のシリーズモデル + + + qwen3.6-plus、qwen3-maxなど + + + doubao-seedシリーズモデル + + + kimi-k2.6、kimi-k2.5、kimi-k2など 統合マルチモデルインターフェース + ナレッジベース diff --git a/docs/models/deepseek.mdx b/docs/models/deepseek.mdx index e7d23e69..e87e55f4 100644 --- a/docs/models/deepseek.mdx +++ b/docs/models/deepseek.mdx @@ -7,25 +7,28 @@ description: DeepSeek 模型配置 ```json { - "model": "deepseek-chat", + "model": "deepseek-v4-pro", "deepseek_api_key": "YOUR_API_KEY" } ``` | 参数 | 说明 | | --- | --- | -| `model` | `deepseek-chat`(DeepSeek-V3.2,非思考模式)、`deepseek-reasoner`(DeepSeek-R1,思考模式) | +| `model` | `deepseek-v4-pro`(V4 Pro,思考模式 + 工具调用,Agent 推荐)、`deepseek-v4-flash`(V4 Flash,思考模式 + 工具调用)、`deepseek-chat`(DeepSeek-V3.2,非思考模式)、`deepseek-reasoner`(DeepSeek-R1,思考模式) | | `deepseek_api_key` | 在 [DeepSeek 平台](https://platform.deepseek.com/api_keys) 创建 | | `deepseek_api_base` | 可选,默认为 `https://api.deepseek.com/v1`,可修改为第三方代理地址 | + + V4 系列模型(`deepseek-v4-pro`、`deepseek-v4-flash`)支持思考模式与工具调用,可在 Agent 模式下进行多轮思考与工具协同。思考开关由全局 `enable_thinking` 配置控制,工具调用轮次的 `reasoning_content` 会自动回传给 API。 + + 方式二:OpenAI 兼容方式接入: ```json { - "model": "deepseek-chat", + "model": "deepseek-v4-pro", "bot_type": "openai", "open_ai_api_key": "YOUR_API_KEY", "open_ai_api_base": "https://api.deepseek.com/v1" } ``` - diff --git a/docs/models/index.mdx b/docs/models/index.mdx index 01169347..7a677561 100644 --- a/docs/models/index.mdx +++ b/docs/models/index.mdx @@ -6,7 +6,7 @@ description: CowAgent 支持的模型及推荐选择 CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在项目的 `models/` 目录下。 - Agent 模式下推荐使用以下模型,可根据效果及成本综合选择:MiniMax-M2.7、glm-5.1、kimi-k2.6、qwen3.6-plus、claude-sonnet-4-6、gemini-3.1-pro-preview + Agent 模式下推荐使用以下模型,可根据效果及成本综合选择:MiniMax-M2.7、deepseek-v4-pro、claude-sonnet-4-6、gemini-3.1-pro-preview、glm-5.1、qwen3.6-plus、kimi-k2.6 同时支持使用 [LinkAI](https://link-ai.tech) 平台接口,可灵活切换多种模型,并支持知识库、工作流、插件等 Agent 能力。 @@ -26,17 +26,8 @@ CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在 MiniMax-M2.7 等系列模型 - - glm-5.1、glm-5-turbo、glm-5 等系列模型 - - - qwen3.6-plus、qwen3-max 等 - - - kimi-k2.6、kimi-k2.5、kimi-k2 等 - - - doubao-seed 系列模型 + + deepseek-v4-pro、deepseek-v4-flash、deepseek-chat、deepseek-reasoner claude-sonnet-4-6 等 @@ -47,8 +38,17 @@ CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在 gpt-5.4、gpt-4.1、o 系列等 - - deepseek-chat、deepseek-reasoner + + glm-5.1、glm-5-turbo、glm-5 等系列模型 + + + qwen3.6-plus、qwen3-max 等 + + + doubao-seed 系列模型 + + + kimi-k2.6、kimi-k2.5、kimi-k2 等 多模型统一接口 + 知识库 diff --git a/models/deepseek/deepseek_bot.py b/models/deepseek/deepseek_bot.py index 033e0216..9f03771b 100644 --- a/models/deepseek/deepseek_bot.py +++ b/models/deepseek/deepseek_bot.py @@ -2,9 +2,26 @@ """ DeepSeek Bot — fully OpenAI-compatible, uses its own API key / base config. + +Supported models: +- deepseek-chat (V3, no thinking) +- deepseek-reasoner (R1, built-in reasoning, no `thinking` switch) +- deepseek-v4-flash (V4, supports thinking mode + tool calls) +- deepseek-v4-pro (V4, supports thinking mode + tool calls, agent recommended) + +Thinking mode notes (for V4 models): +- Toggle: ``{"thinking": {"type": "enabled" | "disabled"}}`` (default: enabled) +- Effort: ``reasoning_effort`` ∈ {"high", "max"} (low/medium → high, xhigh → max) +- In thinking mode, ``temperature``/``top_p``/``presence_penalty``/``frequency_penalty`` + are silently ignored by the server; we drop them locally to avoid confusion. +- ``reasoning_content`` is returned alongside ``content``. For turns that triggered + tool calls, ``reasoning_content`` MUST be echoed back in subsequent requests, or + the API returns 400. """ +import json import time +from typing import Optional import requests from models.bot import Bot @@ -25,9 +42,9 @@ class DeepSeekBot(Bot, OpenAICompatibleBot): super().__init__() self.sessions = SessionManager( DeepSeekSession, - model=conf().get("model") or const.DEEPSEEK_CHAT, + model=conf().get("model") or const.DEEPSEEK_V4_PRO, ) - conf_model = conf().get("model") or const.DEEPSEEK_CHAT + conf_model = conf().get("model") or const.DEEPSEEK_V4_PRO self.args = { "model": conf_model, "temperature": conf().get("temperature", 0.7), @@ -56,13 +73,32 @@ class DeepSeekBot(Bot, OpenAICompatibleBot): return { "api_key": self.api_key, "api_base": self.api_base, - "model": conf().get("model", const.DEEPSEEK_CHAT), + "model": conf().get("model", const.DEEPSEEK_V4_PRO), "default_temperature": conf().get("temperature", 0.7), "default_top_p": conf().get("top_p", 1.0), "default_frequency_penalty": conf().get("frequency_penalty", 0.0), "default_presence_penalty": conf().get("presence_penalty", 0.0), } + @staticmethod + def _model_supports_thinking(model_name: str) -> bool: + """V4 series models expose the explicit `thinking` switch.""" + if not model_name: + return False + m = model_name.lower() + return m.startswith("deepseek-v4") + + @staticmethod + def _is_reasoner_model(model_name: str) -> bool: + """deepseek-reasoner (R1) always thinks internally; no toggle.""" + return bool(model_name) and "reasoner" in model_name.lower() + + def _build_headers(self) -> dict: + return { + "Content-Type": "application/json", + "Authorization": f"Bearer {self.api_key}", + } + # ---------- simple chat (non-agent mode) ---------- def reply(self, query, context=None): @@ -112,13 +148,16 @@ class DeepSeekBot(Bot, OpenAICompatibleBot): def reply_text(self, session, args=None, retry_count: int = 0) -> dict: try: - headers = { - "Content-Type": "application/json", - "Authorization": "Bearer " + self.api_key, - } - body = args.copy() + headers = self._build_headers() + body = dict(args) if args else dict(self.args) body["messages"] = session.messages + # Thinking mode ignores temperature/top_p/penalties — strip to avoid noise. + model_name = str(body.get("model", "")) + if self._model_supports_thinking(model_name) or self._is_reasoner_model(model_name): + for k in ("temperature", "top_p", "presence_penalty", "frequency_penalty"): + body.pop(k, None) + res = requests.post( f"{self.api_base}/chat/completions", headers=headers, @@ -158,3 +197,448 @@ class DeepSeekBot(Bot, OpenAICompatibleBot): if retry_count < 2: return self.reply_text(session, args, retry_count + 1) return {"completion_tokens": 0, "content": "我现在有点累了,等会再来吧"} + + # ==================== Agent mode support ==================== + + def call_with_tools(self, messages, tools=None, stream: bool = False, **kwargs): + """ + Call DeepSeek API with tool support for agent integration. + + Handles: + - Claude → OpenAI message/tool format conversion (with reasoning_content round-trip) + - System prompt injection + - Streaming SSE with tool_calls + reasoning_content delta + - Thinking mode toggle and reasoning_effort for V4 models + """ + try: + converted_messages = self._convert_messages_to_openai_format(messages) + + system_prompt = kwargs.pop("system", None) + if system_prompt: + if not converted_messages or converted_messages[0].get("role") != "system": + converted_messages.insert(0, {"role": "system", "content": system_prompt}) + else: + converted_messages[0] = {"role": "system", "content": system_prompt} + + converted_tools = None + if tools: + converted_tools = self._convert_tools_to_openai_format(tools) + + model = kwargs.pop("model", None) or self.args["model"] + max_tokens = kwargs.pop("max_tokens", None) + + request_body = { + "model": model, + "messages": converted_messages, + "stream": stream, + } + if max_tokens is not None: + request_body["max_tokens"] = max_tokens + + if converted_tools: + request_body["tools"] = converted_tools + request_body["tool_choice"] = kwargs.pop("tool_choice", "auto") + + # Thinking mode (V4 only). Honour the toggle propagated by agent_bridge. + thinking_param = kwargs.pop("thinking", None) + reasoning_effort = kwargs.pop("reasoning_effort", None) + thinking_active = False + + if self._model_supports_thinking(model): + # Default to enabled per DeepSeek docs unless caller explicitly disables. + thinking_param = thinking_param or {"type": "enabled"} + request_body["thinking"] = thinking_param + thinking_active = thinking_param.get("type") == "enabled" + if thinking_active: + # Default to "high"; allow caller override (e.g. "max" for heavy agent loops). + request_body["reasoning_effort"] = reasoning_effort or "high" + elif self._is_reasoner_model(model): + # R1 thinks unconditionally — no `thinking` field, but reasoning_content still flows. + thinking_active = True + + # Strip params silently ignored under thinking mode to keep the wire clean. + if thinking_active: + for k in ("temperature", "top_p", "presence_penalty", "frequency_penalty"): + request_body.pop(k, None) + kwargs.pop(k, None) + else: + # Non-thinking path: forward standard sampling controls. + temperature = kwargs.pop("temperature", None) + if temperature is not None: + request_body["temperature"] = temperature + top_p = kwargs.pop("top_p", None) + if top_p is not None: + request_body["top_p"] = top_p + + logger.debug( + f"[DEEPSEEK] API call: model={model}, " + f"tools={len(converted_tools) if converted_tools else 0}, " + f"stream={stream}, thinking={thinking_active}" + ) + + if stream: + return self._handle_stream_response(request_body) + else: + return self._handle_sync_response(request_body) + + except Exception as e: + logger.error(f"[DEEPSEEK] call_with_tools error: {e}") + import traceback + logger.error(traceback.format_exc()) + + def error_generator(): + yield {"error": True, "message": str(e), "status_code": 500} + return error_generator() + + # -------------------- streaming -------------------- + + def _handle_stream_response(self, request_body: dict): + """Stream SSE chunks from DeepSeek and yield OpenAI-format deltas (with reasoning_content).""" + try: + headers = self._build_headers() + url = f"{self.api_base}/chat/completions" + response = requests.post(url, headers=headers, json=request_body, stream=True, timeout=180) + + if response.status_code != 200: + error_msg = response.text + logger.error(f"[DEEPSEEK] API error: status={response.status_code}, msg={error_msg}") + yield {"error": True, "message": error_msg, "status_code": response.status_code} + return + + current_tool_calls = {} + finish_reason = None + + for line in response.iter_lines(): + if not line: + continue + + line = line.decode("utf-8") + if line.startswith("data: "): + data_str = line[6:] + elif line.startswith("data:"): + data_str = line[5:] + else: + continue + if data_str.strip() == "[DONE]": + break + + try: + chunk = json.loads(data_str) + except json.JSONDecodeError as e: + logger.warning(f"[DEEPSEEK] JSON decode error: {e}, data: {data_str[:200]}") + continue + + if chunk.get("error"): + error_data = chunk["error"] + error_msg = error_data.get("message", "Unknown error") if isinstance(error_data, dict) else str(error_data) + logger.error(f"[DEEPSEEK] stream error: {error_msg}") + yield {"error": True, "message": error_msg, "status_code": 500} + return + + if not chunk.get("choices"): + continue + choice = chunk["choices"][0] + delta = choice.get("delta", {}) + + if choice.get("finish_reason"): + finish_reason = choice["finish_reason"] + + # Reasoning content (thinking mode). Forward as its own delta so + # agent_stream.py can stitch it into a `thinking` block. + if delta.get("reasoning_content"): + yield { + "choices": [{ + "index": 0, + "delta": { + "role": "assistant", + "reasoning_content": delta["reasoning_content"], + }, + "finish_reason": None, + }] + } + + if delta.get("content"): + yield { + "choices": [{ + "index": 0, + "delta": { + "role": "assistant", + "content": delta["content"], + }, + }] + } + + if "tool_calls" in delta and delta["tool_calls"]: + for tool_call_chunk in delta["tool_calls"]: + index = tool_call_chunk.get("index", 0) + if index not in current_tool_calls: + current_tool_calls[index] = { + "id": tool_call_chunk.get("id", ""), + "name": tool_call_chunk.get("function", {}).get("name", ""), + "arguments": "", + } + if "function" in tool_call_chunk and "arguments" in tool_call_chunk["function"]: + current_tool_calls[index]["arguments"] += tool_call_chunk["function"]["arguments"] + + yield { + "choices": [{ + "index": 0, + "delta": {"tool_calls": [tool_call_chunk]}, + }] + } + + yield { + "choices": [{ + "index": 0, + "delta": {}, + "finish_reason": finish_reason, + }] + } + + except requests.exceptions.Timeout: + logger.error("[DEEPSEEK] Request timeout") + yield {"error": True, "message": "Request timeout", "status_code": 500} + except Exception as e: + logger.error(f"[DEEPSEEK] stream response error: {e}") + import traceback + logger.error(traceback.format_exc()) + yield {"error": True, "message": str(e), "status_code": 500} + + # -------------------- sync -------------------- + + def _handle_sync_response(self, request_body: dict): + """Single-shot response. Yields a Claude-format dict for symmetry with stream path.""" + try: + headers = self._build_headers() + request_body.pop("stream", None) + url = f"{self.api_base}/chat/completions" + response = requests.post(url, headers=headers, json=request_body, timeout=180) + + if response.status_code != 200: + error_msg = response.text + logger.error(f"[DEEPSEEK] API error: status={response.status_code}, msg={error_msg}") + yield {"error": True, "message": error_msg, "status_code": response.status_code} + return + + result = response.json() + message = result["choices"][0]["message"] + finish_reason = result["choices"][0]["finish_reason"] + + response_data = {"role": "assistant", "content": []} + + # Surface reasoning as a `thinking` block so the agent layer can persist it + # and round-trip it on tool-call turns (required by DeepSeek API). + if message.get("reasoning_content"): + response_data["content"].append({ + "type": "thinking", + "thinking": message["reasoning_content"], + }) + + if message.get("content"): + response_data["content"].append({ + "type": "text", + "text": message["content"], + }) + + if message.get("tool_calls"): + for tool_call in message["tool_calls"]: + try: + tool_input = json.loads(tool_call["function"]["arguments"]) + except (json.JSONDecodeError, TypeError): + tool_input = {} + response_data["content"].append({ + "type": "tool_use", + "id": tool_call["id"], + "name": tool_call["function"]["name"], + "input": tool_input, + }) + + if finish_reason == "tool_calls": + response_data["stop_reason"] = "tool_use" + elif finish_reason == "stop": + response_data["stop_reason"] = "end_turn" + else: + response_data["stop_reason"] = finish_reason + + yield response_data + + except requests.exceptions.Timeout: + logger.error("[DEEPSEEK] Request timeout") + yield {"error": True, "message": "Request timeout", "status_code": 500} + except Exception as e: + logger.error(f"[DEEPSEEK] sync response error: {e}") + import traceback + logger.error(traceback.format_exc()) + yield {"error": True, "message": str(e), "status_code": 500} + + # -------------------- format conversion -------------------- + + def _convert_messages_to_openai_format(self, messages): + """ + Convert Claude-format messages (content blocks) to OpenAI format. + + Crucially, for any assistant turn with tool_use, the accompanying `thinking` + block must be re-emitted as `reasoning_content` — DeepSeek returns 400 if + omitted on tool-call rounds. + """ + if not messages: + return [] + + converted = [] + + for msg in messages: + role = msg.get("role") + content = msg.get("content") + + if isinstance(content, str): + converted.append(msg) + continue + + if not isinstance(content, list): + converted.append(msg) + continue + + if role == "user": + has_tool_result = any( + isinstance(b, dict) and b.get("type") == "tool_result" for b in content + ) + if has_tool_result: + text_parts = [] + tool_results = [] + + for block in content: + if not isinstance(block, dict): + continue + if block.get("type") == "text": + text_parts.append(block.get("text", "")) + elif block.get("type") == "tool_result": + tool_call_id = block.get("tool_use_id") or "" + result_content = block.get("content", "") + if not isinstance(result_content, str): + result_content = json.dumps(result_content, ensure_ascii=False) + tool_results.append({ + "role": "tool", + "tool_call_id": tool_call_id, + "content": result_content, + }) + + converted.extend(tool_results) + + if text_parts: + converted.append({"role": "user", "content": "\n".join(text_parts)}) + else: + converted.append(msg) + + elif role == "assistant": + openai_msg = {"role": "assistant"} + text_parts = [] + tool_calls = [] + reasoning_parts = [] + + for block in content: + if not isinstance(block, dict): + continue + btype = block.get("type") + if btype == "text": + text_parts.append(block.get("text", "")) + elif btype == "tool_use": + tool_calls.append({ + "id": block.get("id"), + "type": "function", + "function": { + "name": block.get("name"), + "arguments": json.dumps(block.get("input", {})), + }, + }) + elif btype == "thinking": + reasoning_parts.append(block.get("thinking", "")) + + if text_parts: + openai_msg["content"] = "\n".join(text_parts) + elif not tool_calls: + openai_msg["content"] = "" + + if tool_calls: + openai_msg["tool_calls"] = tool_calls + if not text_parts: + openai_msg["content"] = None + + # Round-trip reasoning_content: required for tool-call turns, + # harmless (server-ignored) for plain text turns. + if reasoning_parts: + openai_msg["reasoning_content"] = "\n".join(reasoning_parts) + + converted.append(openai_msg) + else: + converted.append(msg) + + return converted + + def _convert_tools_to_openai_format(self, tools): + """ + Convert tools from Claude format to OpenAI format. + + Claude: {name, description, input_schema} + OpenAI: {type: "function", function: {name, description, parameters}} + """ + if not tools: + return None + + converted = [] + for tool in tools: + if "type" in tool and tool["type"] == "function": + converted.append(tool) + else: + converted.append({ + "type": "function", + "function": { + "name": tool.get("name"), + "description": tool.get("description"), + "parameters": tool.get("input_schema", {}), + }, + }) + return converted + + # -------------------- vision -------------------- + + def call_vision(self, image_url: str, question: str, + model: Optional[str] = None, + max_tokens: int = 1000) -> dict: + """Analyse an image via DeepSeek's OpenAI-compatible /chat/completions endpoint.""" + try: + vision_model = model or self.args.get("model", const.DEEPSEEK_V4_PRO) + payload = { + "model": vision_model, + "max_tokens": max_tokens, + "messages": [{ + "role": "user", + "content": [ + {"type": "text", "text": question}, + {"type": "image_url", "image_url": {"url": image_url}}, + ], + }], + } + headers = self._build_headers() + resp = requests.post( + f"{self.api_base}/chat/completions", + headers=headers, json=payload, timeout=60, + ) + if resp.status_code != 200: + return {"error": True, "message": f"HTTP {resp.status_code}: {resp.text[:300]}"} + data = resp.json() + if "error" in data: + return {"error": True, "message": data["error"].get("message", str(data["error"]))} + content = data.get("choices", [{}])[0].get("message", {}).get("content", "") + usage = data.get("usage", {}) + return { + "model": vision_model, + "content": content, + "usage": { + "prompt_tokens": usage.get("prompt_tokens", 0), + "completion_tokens": usage.get("completion_tokens", 0), + "total_tokens": usage.get("total_tokens", 0), + }, + } + except Exception as e: + logger.error(f"[DEEPSEEK] call_vision error: {e}") + return {"error": True, "message": str(e)} diff --git a/models/deepseek/deepseek_session.py b/models/deepseek/deepseek_session.py index 9500c8f4..0518d2fb 100644 --- a/models/deepseek/deepseek_session.py +++ b/models/deepseek/deepseek_session.py @@ -3,7 +3,7 @@ from common.log import logger class DeepSeekSession(Session): - def __init__(self, session_id, system_prompt=None, model="deepseek-chat"): + def __init__(self, session_id, system_prompt=None, model="deepseek-v4-pro"): super().__init__(session_id, system_prompt) self.model = model self.reset() diff --git a/run.sh b/run.sh index 5bfacf51..82a26124 100755 --- a/run.sh +++ b/run.sh @@ -310,25 +310,26 @@ select_model() { echo -e "${CYAN}${BOLD} Select AI Model${NC}" echo -e "${CYAN}${BOLD}=========================================${NC}" echo -e "${YELLOW}1) MiniMax (MiniMax-M2.7, MiniMax-M2.5, etc.)${NC}" - echo -e "${YELLOW}2) Zhipu AI (glm-5.1, glm-5-turbo, glm-5, etc.)${NC}" - echo -e "${YELLOW}3) Kimi (kimi-k2.6, kimi-k2.5, kimi-k2, etc.)${NC}" - echo -e "${YELLOW}4) Doubao (doubao-seed-2-0-code-preview-260215, etc.)${NC}" - echo -e "${YELLOW}5) Qwen (qwen3.6-plus, qwen3.5-plus, qwen3-max, qwq-plus, etc.)${NC}" - echo -e "${YELLOW}6) Claude (claude-sonnet-4-6, claude-opus-4-7, claude-opus-4-6, etc.)${NC}" - echo -e "${YELLOW}7) Gemini (gemini-3.1-flash-lite-preview, gemini-3.1-pro-preview, etc.)${NC}" - echo -e "${YELLOW}8) OpenAI GPT (gpt-5.4, gpt-5.2, gpt-4.1, etc.)${NC}" - echo -e "${YELLOW}9) LinkAI (access multiple models via one API)${NC}" + echo -e "${YELLOW}2) DeepSeek (deepseek-v4-pro, deepseek-v4-flash, etc.)${NC}" + echo -e "${YELLOW}3) Claude (claude-sonnet-4-6, claude-opus-4-7, claude-opus-4-6, etc.)${NC}" + echo -e "${YELLOW}4) Gemini (gemini-3.1-flash-lite-preview, gemini-3.1-pro-preview, etc.)${NC}" + echo -e "${YELLOW}5) OpenAI GPT (gpt-5.4, gpt-5.2, gpt-4.1, etc.)${NC}" + echo -e "${YELLOW}6) Zhipu AI (glm-5.1, glm-5-turbo, glm-5, etc.)${NC}" + echo -e "${YELLOW}7) Qwen (qwen3.6-plus, qwen3.5-plus, qwen3-max, qwq-plus, etc.)${NC}" + echo -e "${YELLOW}8) Doubao (doubao-seed-2-0-code-preview-260215, etc.)${NC}" + echo -e "${YELLOW}9) Kimi (kimi-k2.6, kimi-k2.5, kimi-k2, etc.)${NC}" + echo -e "${YELLOW}10) LinkAI (access multiple models via one API)${NC}" echo "" while true; do read -p "Enter your choice [press Enter for default: 1 - MiniMax]: " model_choice model_choice=${model_choice:-1} case "$model_choice" in - 1|2|3|4|5|6|7|8|9) + 1|2|3|4|5|6|7|8|9|10) break ;; *) - echo -e "${RED}Invalid choice. Please enter 1-9.${NC}" + echo -e "${RED}Invalid choice. Please enter 1-10.${NC}" ;; esac done @@ -357,23 +358,27 @@ read_api_base() { configure_model() { case "$model_choice" in 1) read_model_config "MiniMax" "MiniMax-M2.7" "MINIMAX_KEY" ;; - 2) read_model_config "Zhipu AI" "glm-5.1" "ZHIPU_KEY" ;; - 3) read_model_config "Kimi (Moonshot)" "kimi-k2.6" "MOONSHOT_KEY" ;; - 4) read_model_config "Doubao (Volcengine Ark)" "doubao-seed-2-0-code-preview-260215" "ARK_KEY" ;; - 5) read_model_config "Qwen (DashScope)" "qwen3.6-plus" "DASHSCOPE_KEY" ;; - 6) + 2) + read_model_config "DeepSeek" "deepseek-v4-pro" "DEEPSEEK_KEY" + read_api_base "DEEPSEEK_BASE" "https://api.deepseek.com/v1" + ;; + 3) read_model_config "Claude" "claude-sonnet-4-6" "CLAUDE_KEY" read_api_base "CLAUDE_BASE" "https://api.anthropic.com/v1" ;; - 7) + 4) read_model_config "Gemini" "gemini-3.1-pro-preview" "GEMINI_KEY" read_api_base "GEMINI_BASE" "https://generativelanguage.googleapis.com" ;; - 8) + 5) read_model_config "OpenAI GPT" "gpt-5.4" "OPENAI_KEY" read_api_base "OPENAI_BASE" "https://api.openai.com/v1" ;; - 9) + 6) read_model_config "Zhipu AI" "glm-5.1" "ZHIPU_KEY" ;; + 7) read_model_config "Qwen (DashScope)" "qwen3.6-plus" "DASHSCOPE_KEY" ;; + 8) read_model_config "Doubao (Volcengine Ark)" "doubao-seed-2-0-code-preview-260215" "ARK_KEY" ;; + 9) read_model_config "Kimi (Moonshot)" "kimi-k2.6" "MOONSHOT_KEY" ;; + 10) read_model_config "LinkAI" "MiniMax-M2.7" "LINKAI_KEY" USE_LINKAI="true" ;; @@ -511,6 +516,8 @@ create_config_file() { ARK_KEY="${ARK_KEY:-}" \ DASHSCOPE_KEY="${DASHSCOPE_KEY:-}" \ MINIMAX_KEY="${MINIMAX_KEY:-}" \ + DEEPSEEK_KEY="${DEEPSEEK_KEY:-}" \ + DEEPSEEK_BASE="${DEEPSEEK_BASE:-https://api.deepseek.com/v1}" \ USE_LINKAI="${USE_LINKAI:-false}" \ LINKAI_KEY="${LINKAI_KEY:-}" \ FEISHU_APP_ID="${FEISHU_APP_ID:-}" \ @@ -545,6 +552,8 @@ base = { 'ark_api_key': e('ARK_KEY', ''), 'dashscope_api_key': e('DASHSCOPE_KEY', ''), 'minimax_api_key': e('MINIMAX_KEY', ''), + 'deepseek_api_key': e('DEEPSEEK_KEY', ''), + 'deepseek_api_base': e('DEEPSEEK_BASE'), 'voice_to_text': 'openai', 'text_to_voice': 'openai', 'voice_reply_voice': False,