Compare commits

...

8 Commits

40 changed files with 1205 additions and 391 deletions

270
README.md
View File

@@ -29,7 +29,7 @@
-**工具系统:** 内置文件读写、终端执行、浏览器操作、定时任务等工具Agent 自主调用以完成复杂任务 -**工具系统:** 内置文件读写、终端执行、浏览器操作、定时任务等工具Agent 自主调用以完成复杂任务
-**CLI系统** 提供终端命令和对话命令,支持进程管理、技能安装、配置修改等操作 -**CLI系统** 提供终端命令和对话命令,支持进程管理、技能安装、配置修改等操作
-**多模态消息:** 支持对文本、图片、语音、文件等多类型消息进行解析、处理、生成、发送等操作 -**多模态消息:** 支持对文本、图片、语音、文件等多类型消息进行解析、处理、生成、发送等操作
-**多模型支持:** 支持 OpenAI, Claude, Gemini, DeepSeek, MiniMax、GLM、Qwen、Kimi、Doubao 等国内外主流模型厂商 -**多模型支持:** 支持 DeepSeek、MiniMax、ClaudeGemini、OpenAI、GLM、Qwen、Doubao、Kimi 等国内外主流模型厂商
-**多通道接入:** 支持运行在本地计算机或服务器可集成到微信、飞书、钉钉、企业微信、QQ、微信公众号、网页中使用 -**多通道接入:** 支持运行在本地计算机或服务器可集成到微信、飞书、钉钉、企业微信、QQ、微信公众号、网页中使用
## 声明 ## 声明
@@ -115,7 +115,7 @@ irm https://cdn.link-ai.tech/code/cow/run.ps1 | iex
项目支持国内外主流厂商的模型接口,可选模型及配置说明参考:[模型说明](#模型说明)。 项目支持国内外主流厂商的模型接口,可选模型及配置说明参考:[模型说明](#模型说明)。
> Agent 模式下推荐使用以下模型可根据效果及成本综合选择MiniMax-M2.7、glm-5.1、kimi-k2.6、qwen3.5-plus、claude-sonnet-4-6、gemini-3.1-pro-preview、gpt-5.4、gpt-5.4-mini > Agent 模式下推荐使用以下模型,可根据效果及成本综合选择:deepseek-v4-flash、MiniMax-M2.7、glm-5.1、kimi-k2.6、qwen3.5-plus、claude-sonnet-4-6、gemini-3.1-pro-preview、gpt-5.4、gpt-5.4-mini
同时支持使用 **LinkAI 平台** 接口,支持上述全部模型,并支持知识库、工作流、插件等 Agent 技能,参考 [接口文档](https://docs.link-ai.tech/platform/api)。 同时支持使用 **LinkAI 平台** 接口,支持上述全部模型,并支持知识库、工作流、插件等 Agent 技能,参考 [接口文档](https://docs.link-ai.tech/platform/api)。
@@ -182,7 +182,9 @@ cow install-browser
# config.json 文件内容示例 # config.json 文件内容示例
{ {
"channel_type": "weixin", # 接入渠道类型,默认为 weixin, 支持修改为 feishu,dingtalk,wecom_bot,qq,wechatcom_app,wechatmp_service,wechatmp,terminal "channel_type": "weixin", # 接入渠道类型,默认为 weixin, 支持修改为 feishu,dingtalk,wecom_bot,qq,wechatcom_app,wechatmp_service,wechatmp,terminal
"model": "MiniMax-M2.7", # 模型名称 "model": "deepseek-v4-flash", # 模型名称
"deepseek_api_key": "", # DeepSeek API Key
"deepseek_api_base": "https://api.deepseek.com/v1", # DeepSeek API 地址
"minimax_api_key": "", # MiniMax API Key "minimax_api_key": "", # MiniMax API Key
"zhipu_ai_api_key": "", # 智谱 GLM API Key "zhipu_ai_api_key": "", # 智谱 GLM API Key
"moonshot_api_key": "", # Kimi/Moonshot API Key "moonshot_api_key": "", # Kimi/Moonshot API Key
@@ -192,8 +194,6 @@ cow install-browser
"claude_api_base": "https://api.anthropic.com/v1", # Claude API 地址,修改可接入三方代理平台 "claude_api_base": "https://api.anthropic.com/v1", # Claude API 地址,修改可接入三方代理平台
"gemini_api_key": "", # Gemini API Key "gemini_api_key": "", # Gemini API Key
"gemini_api_base": "https://generativelanguage.googleapis.com", # Gemini API 地址 "gemini_api_base": "https://generativelanguage.googleapis.com", # Gemini API 地址
"deepseek_api_key": "", # DeepSeek API Key
"deepseek_api_base": "https://api.deepseek.com/v1", # DeepSeek API 地址,可修改为第三方代理
"open_ai_api_key": "", # OpenAI API Key "open_ai_api_key": "", # OpenAI API Key
"open_ai_api_base": "https://api.openai.com/v1", # OpenAI API 地址 "open_ai_api_base": "https://api.openai.com/v1", # OpenAI API 地址
"linkai_api_key": "", # LinkAI API Key "linkai_api_key": "", # LinkAI API Key
@@ -208,7 +208,7 @@ cow install-browser
"agent_max_context_tokens": 50000, # Agent 模式下最大上下文 tokens超出将自动智能压缩处理 "agent_max_context_tokens": 50000, # Agent 模式下最大上下文 tokens超出将自动智能压缩处理
"agent_max_context_turns": 20, # Agent 模式下最大上下文记忆轮次,一问一答为一轮,超出后智能压缩处理 "agent_max_context_turns": 20, # Agent 模式下最大上下文记忆轮次,一问一答为一轮,超出后智能压缩处理
"agent_max_steps": 20, # Agent 模式下单次任务的最大决策步数,超出后将停止继续调用工具 "agent_max_steps": 20, # Agent 模式下单次任务的最大决策步数,超出后将停止继续调用工具
"enable_thinking": false # 是否启用深度思考,开启后 Web 端展示模型推理过程,关闭后可加速响应 "enable_thinking": false # 是否启用深度思考模式
} }
``` ```
@@ -226,7 +226,7 @@ cow install-browser
<details> <details>
<summary>2. 其他配置</summary> <summary>2. 其他配置</summary>
+ `model`: 模型名称Agent 模式下推荐使用 `MiniMax-M2.7``glm-5.1``kimi-k2.6``qwen3.6-plus``claude-sonnet-4-6``gemini-3.1-pro-preview`,全部模型名称参考[common/const.py](https://github.com/zhayujie/CowAgent/blob/master/common/const.py)文件 + `model`: 模型名称Agent 模式下推荐使用 `deepseek-v4-flash``MiniMax-M2.7``glm-5.1``kimi-k2.6``qwen3.6-plus``claude-sonnet-4-6``gemini-3.1-pro-preview`,全部模型名称参考[common/const.py](https://github.com/zhayujie/CowAgent/blob/master/common/const.py)文件
+ `character_desc`:普通对话模式下的机器人系统提示词。在 Agent 模式下该配置不生效,由工作空间中的文件内容构成。 + `character_desc`:普通对话模式下的机器人系统提示词。在 Agent 模式下该配置不生效,由工作空间中的文件内容构成。
+ `subscribe_msg`:订阅消息,公众号和企业微信 channel 中请填写,当被订阅时会自动回复, 可使用特殊占位符。目前支持的占位符有{trigger_prefix},在程序中它会自动替换成 bot 的触发词。 + `subscribe_msg`:订阅消息,公众号和企业微信 channel 中请填写,当被订阅时会自动回复, 可使用特殊占位符。目前支持的占位符有{trigger_prefix},在程序中它会自动替换成 bot 的触发词。
</details> </details>
@@ -314,44 +314,36 @@ sudo docker logs -f chatgpt-on-wechat
推荐通过 Web 控制台在线管理模型配置,无需手动编辑文件,详见 [模型文档](https://docs.cowagent.ai/models)。以下是手动修改 `config.json` 配置模型的说明: 推荐通过 Web 控制台在线管理模型配置,无需手动编辑文件,详见 [模型文档](https://docs.cowagent.ai/models)。以下是手动修改 `config.json` 配置模型的说明:
<details> <details>
<summary>OpenAI</summary> <summary>DeepSeek</summary>
1. API Key 创建:在 [OpenAI平台](https://platform.openai.com/api-keys) 创建 API Key 1. API Key 创建:在 [DeepSeek 平台](https://platform.deepseek.com/api_keys) 创建 API Key
2. 填写配置 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`
</details>
<details>
<summary>LinkAI</summary>
1. API Key 创建:在 [LinkAI平台](https://link-ai.tech/console/interface) 创建 API Key
2. 填写配置
```json ```json
{ {
"model": "gpt-5.4-mini", "model": "deepseek-v4-flash",
"use_linkai": true, "deepseek_api_key": "sk-xxxxxxxxxxx"
"linkai_api_key": "YOUR API KEY" }
```
- `model`: 推荐填写 `deepseek-v4-flash``deepseek-v4-pro`
- `deepseek_api_key`: DeepSeek 平台的 API Key
- `deepseek_api_base`: 可选,默认为 `https://api.deepseek.com/v1`,可修改为第三方代理地址
方式二OpenAI 兼容方式接入:
```json
{
"model": "deepseek-v4-flash",
"bot_type": "openai",
"open_ai_api_key": "sk-xxxxxxxxxxx",
"open_ai_api_base": "https://api.deepseek.com/v1"
} }
``` ```
+ `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)中的全部模型均可使用
</details> </details>
<details> <details>
@@ -383,6 +375,56 @@ sudo docker logs -f chatgpt-on-wechat
- `open_ai_api_key`: MiniMax 平台的 API-KEY - `open_ai_api_key`: MiniMax 平台的 API-KEY
</details> </details>
<details>
<summary>Claude</summary>
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`
</details>
<details>
<summary>Gemini</summary>
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`
</details>
<details>
<summary>OpenAI</summary>
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`
</details>
<details> <details>
<summary>智谱AI (GLM)</summary> <summary>智谱AI (GLM)</summary>
@@ -441,35 +483,6 @@ sudo docker logs -f chatgpt-on-wechat
- `open_ai_api_key`: 通义千问的 API-KEY - `open_ai_api_key`: 通义千问的 API-KEY
</details> </details>
<details>
<summary>Kimi (Moonshot)</summary>
方式一:官方接入,配置如下:
```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
</details>
<details> <details>
<summary>豆包 (Doubao)</summary> <summary>豆包 (Doubao)</summary>
@@ -489,67 +502,74 @@ sudo docker logs -f chatgpt-on-wechat
</details> </details>
<details> <details>
<summary>Claude</summary> <summary>Kimi (Moonshot)</summary>
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
</details>
<details>
<summary>ModelScope</summary>
```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)
</details>
<details>
<summary>LinkAI</summary>
1. API Key 创建:在 [LinkAI平台](https://link-ai.tech/console/interface) 创建 API Key
2. 填写配置 2. 填写配置
```json ```json
{ {
"model": "claude-sonnet-4-6", "model": "gpt-5.4-mini",
"claude_api_key": "YOUR_API_KEY" "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)中的全部模型均可使用
</details> </details>
<details>
<summary>Gemini</summary>
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`
</details>
<details>
<summary>DeepSeek</summary>
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"
}
```
</details>
<details> <details>
<summary>Azure</summary> <summary>Azure</summary>
@@ -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) ,因模型而已 - `open_ai_api_key`: 讯飞星火平台的[APIPassword](https://console.xfyun.cn/services/bm3) ,因模型而已
</details> </details>
<details>
<summary>ModelScope</summary>
```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)
</details>
<details> <details>
<summary>Coding Plan</summary> <summary>Coding Plan</summary>

View File

@@ -110,6 +110,23 @@ class AgentStreamExecutor:
logger.error(f"Event callback error: {e}") logger.error(f"Event callback error: {e}")
def _is_thinking_enabled(self) -> bool: def _is_thinking_enabled(self) -> bool:
"""Whether deep-thinking mode is on at the model layer.
Mirrors the global toggle used by ``bridge.agent_bridge`` when deciding
whether to send ``thinking={"type": "enabled"}`` to the model. Used for
logging and reasoning-update event emission across all channels.
"""
from config import conf
return bool(conf().get("enable_thinking", False))
def _should_render_thinking_inline(self) -> bool:
"""Whether ``<think>...</think>`` blocks embedded directly in ``content``
(MiniMax, some third-party proxies) should be surfaced to the channel.
Only the Web console can render them in a collapsible panel. IM channels
(WeChat/WeCom/DingTalk/Feishu) must strip them, otherwise users see raw
XML tags in their chat.
"""
from config import conf from config import conf
channel_type = getattr(self.model, 'channel_type', '') or '' channel_type = getattr(self.model, 'channel_type', '') or ''
return conf().get("enable_thinking", False) and channel_type == 'web' return conf().get("enable_thinking", False) and channel_type == 'web'
@@ -119,13 +136,15 @@ class AgentStreamExecutor:
Handle <think>...</think> blocks in content returned by some LLM providers Handle <think>...</think> blocks in content returned by some LLM providers
(e.g., MiniMax). (e.g., MiniMax).
- When thinking is enabled: remove the tags but keep the content inside. - When inline thinking rendering is allowed (Web + thinking enabled):
- When thinking is disabled: remove both the tags and the content entirely. remove only the tags, keep the content inside.
- Otherwise (IM channels, or thinking disabled globally): remove both
the tags and the content entirely.
""" """
if not text: if not text:
return text return text
import re import re
if self._is_thinking_enabled(): if self._should_render_thinking_inline():
text = re.sub(r'<think>', '', text) text = re.sub(r'<think>', '', text)
text = re.sub(r'</think>', '', text) text = re.sub(r'</think>', '', text)
else: else:

View File

@@ -167,13 +167,15 @@ class AgentLLMModel(LLMModel):
if session_id: if session_id:
kwargs['session_id'] = session_id kwargs['session_id'] = session_id
# Determine thinking: respect global config, then channel_type # Thinking mode is a global toggle independent of the channel.
# IM channels (WeChat/WeCom/DingTalk/Feishu) won't render the
# reasoning trace, but still benefit from the higher answer
# quality the thinking pass produces.
from config import conf from config import conf
global_thinking = conf().get("enable_thinking", False) kwargs['thinking'] = (
if not global_thinking: {"type": "enabled"} if conf().get("enable_thinking", False)
kwargs['thinking'] = {"type": "disabled"} else {"type": "disabled"}
else: )
kwargs['thinking'] = {"type": "enabled"} if channel_type == "web" else {"type": "disabled"}
response = self.bot.call_with_tools(**kwargs) response = self.bot.call_with_tools(**kwargs)
return self._format_response(response) return self._format_response(response)
@@ -220,13 +222,15 @@ class AgentLLMModel(LLMModel):
if session_id: if session_id:
kwargs['session_id'] = session_id kwargs['session_id'] = session_id
# Determine thinking: respect global config, then channel_type # Thinking mode is a global toggle independent of the channel.
# IM channels (WeChat/WeCom/DingTalk/Feishu) won't render the
# reasoning trace, but still benefit from the higher answer
# quality the thinking pass produces.
from config import conf from config import conf
global_thinking = conf().get("enable_thinking", False) kwargs['thinking'] = (
if not global_thinking: {"type": "enabled"} if conf().get("enable_thinking", False)
kwargs['thinking'] = {"type": "disabled"} else {"type": "disabled"}
else: )
kwargs['thinking'] = {"type": "enabled"} if channel_type == "web" else {"type": "disabled"}
stream = self.bot.call_with_tools(**kwargs) stream = self.bot.call_with_tools(**kwargs)

View File

@@ -1068,12 +1068,10 @@
} }
.cfg-tip:hover { color: #64748b; } .cfg-tip:hover { color: #64748b; }
.dark .cfg-tip:hover { color: #cbd5e1; } .dark .cfg-tip:hover { color: #cbd5e1; }
.cfg-tip::after { /* Floating tooltip portal — appended to <body> by JS so it isn't clipped
content: attr(data-tooltip); by overflow:hidden ancestors. */
position: absolute; .cfg-tip-floating {
left: 50%; position: fixed;
bottom: calc(100% + 6px);
transform: translateX(-50%);
padding: 6px 10px; padding: 6px 10px;
border-radius: 8px; border-radius: 8px;
font-size: 12px; font-size: 12px;
@@ -1086,13 +1084,13 @@
opacity: 0; opacity: 0;
pointer-events: none; pointer-events: none;
transition: opacity 0.15s; transition: opacity 0.15s;
z-index: 50; z-index: 9999;
} }
.dark .cfg-tip::after { .dark .cfg-tip-floating {
background: #334155; background: #334155;
color: #f1f5f9; color: #f1f5f9;
} }
.cfg-tip:hover::after { .cfg-tip-floating.show {
opacity: 1; opacity: 1;
} }

View File

@@ -38,7 +38,7 @@ const I18N = {
config_max_tokens: '最大上下文 Token', config_max_tokens_hint: '对话中 Agent 能输入的最大 Token 长度,超过后会智能压缩处理', config_max_tokens: '最大上下文 Token', config_max_tokens_hint: '对话中 Agent 能输入的最大 Token 长度,超过后会智能压缩处理',
config_max_turns: '最大记忆轮次', config_max_turns_hint: '一问一答为一轮,超过后会智能压缩处理', config_max_turns: '最大记忆轮次', config_max_turns_hint: '一问一答为一轮,超过后会智能压缩处理',
config_max_steps: '最大执行步数', config_max_steps_hint: '单次对话中 Agent 最多调用工具的次数', config_max_steps: '最大执行步数', config_max_steps_hint: '单次对话中 Agent 最多调用工具的次数',
config_enable_thinking: '深度思考', config_enable_thinking_hint: '启用后在 Web 端展示模型推理过程', config_enable_thinking: '深度思考', config_enable_thinking_hint: '是否启用深度思考模式',
config_channel_type: '通道类型', config_channel_type: '通道类型',
config_provider: '模型厂商', config_model_name: '模型', config_provider: '模型厂商', config_model_name: '模型',
config_custom_model_hint: '输入自定义模型名称', config_custom_model_hint: '输入自定义模型名称',
@@ -124,7 +124,7 @@ const I18N = {
config_max_tokens: 'Max Context Tokens', config_max_tokens_hint: 'Max tokens the Agent can input per conversation, auto-compressed when exceeded', config_max_tokens: 'Max Context Tokens', config_max_tokens_hint: 'Max tokens the Agent can input per conversation, auto-compressed when exceeded',
config_max_turns: 'Max Memory Turns', config_max_turns_hint: 'One Q&A pair = one turn, auto-compressed when exceeded', config_max_turns: 'Max Memory Turns', config_max_turns_hint: 'One Q&A pair = one turn, auto-compressed when exceeded',
config_max_steps: 'Max Steps', config_max_steps_hint: 'Max tool calls the Agent can make in a single conversation', config_max_steps: 'Max Steps', config_max_steps_hint: 'Max tool calls the Agent can make in a single conversation',
config_enable_thinking: 'Deep Thinking', config_enable_thinking_hint: 'Show model reasoning on web console', config_enable_thinking: 'Deep Thinking', config_enable_thinking_hint: 'Enable deep thinking mode',
config_channel_type: 'Channel Type', config_channel_type: 'Channel Type',
config_provider: 'Provider', config_model_name: 'Model', config_provider: 'Provider', config_model_name: 'Model',
config_custom_model_hint: 'Enter custom model name', config_custom_model_hint: 'Enter custom model name',
@@ -204,6 +204,7 @@ function applyI18n() {
document.querySelectorAll('[data-tip-key]').forEach(el => { document.querySelectorAll('[data-tip-key]').forEach(el => {
el.setAttribute('data-tooltip', t(el.dataset.tipKey)); el.setAttribute('data-tooltip', t(el.dataset.tipKey));
}); });
installCfgTipPortal();
const langLabel = document.getElementById('lang-label'); const langLabel = document.getElementById('lang-label');
if (langLabel) langLabel.textContent = currentLang === 'zh' ? '中文' : 'EN'; if (langLabel) langLabel.textContent = currentLang === 'zh' ? '中文' : 'EN';
} }
@@ -215,6 +216,54 @@ function toggleLanguage() {
_applyInputTooltips(); _applyInputTooltips();
} }
// Floating tooltip portal for [data-tip-key] elements. Tooltip nodes are
// appended to <body> so they aren't clipped by overflow:hidden ancestors
// (e.g. the config panel's scroll container).
let _cfgTipPortalEl = null;
let _cfgTipPortalInstalled = false;
function installCfgTipPortal() {
if (_cfgTipPortalInstalled) return;
_cfgTipPortalInstalled = true;
const showTip = (target) => {
const text = target.getAttribute('data-tooltip');
if (!text) return;
if (!_cfgTipPortalEl) {
_cfgTipPortalEl = document.createElement('div');
_cfgTipPortalEl.className = 'cfg-tip-floating';
document.body.appendChild(_cfgTipPortalEl);
}
_cfgTipPortalEl.textContent = text;
const rect = target.getBoundingClientRect();
// Render once to measure, then position above the target, centered.
_cfgTipPortalEl.style.left = '0px';
_cfgTipPortalEl.style.top = '0px';
_cfgTipPortalEl.classList.add('show');
const tipRect = _cfgTipPortalEl.getBoundingClientRect();
let left = rect.left + rect.width / 2 - tipRect.width / 2;
// Clamp horizontally to the viewport with an 8px gutter.
left = Math.max(8, Math.min(left, window.innerWidth - tipRect.width - 8));
const top = rect.top - tipRect.height - 6;
_cfgTipPortalEl.style.left = left + 'px';
_cfgTipPortalEl.style.top = top + 'px';
};
const hideTip = () => {
if (_cfgTipPortalEl) _cfgTipPortalEl.classList.remove('show');
};
document.addEventListener('mouseover', (e) => {
const target = e.target.closest('[data-tip-key]');
if (target) showTip(target);
});
document.addEventListener('mouseout', (e) => {
const target = e.target.closest('[data-tip-key]');
if (target) hideTip();
});
// Hide on scroll/resize so the tooltip doesn't drift away from its anchor.
window.addEventListener('scroll', hideTip, true);
window.addEventListener('resize', hideTip);
}
// ===================================================================== // =====================================================================
// Theme // Theme
// ===================================================================== // =====================================================================
@@ -2407,12 +2456,17 @@ function onProviderChange(pid) {
} }
// API Base // API Base
const apiBaseInput = document.getElementById('cfg-api-base');
if (p.api_base_key) { if (p.api_base_key) {
document.getElementById('cfg-api-base-wrap').classList.remove('hidden'); document.getElementById('cfg-api-base-wrap').classList.remove('hidden');
document.getElementById('cfg-api-base').value = configApiBases[p.api_base_key] || p.api_base_default || ''; apiBaseInput.value = configApiBases[p.api_base_key] || p.api_base_default || '';
// Hint the version-path tail (e.g. /v1) so users are reminded to
// include it themselves. We don't auto-rewrite anything server-side.
apiBaseInput.placeholder = p.api_base_placeholder || 'https://...';
} else { } else {
document.getElementById('cfg-api-base-wrap').classList.add('hidden'); document.getElementById('cfg-api-base-wrap').classList.add('hidden');
document.getElementById('cfg-api-base').value = ''; apiBaseInput.value = '';
apiBaseInput.placeholder = 'https://...';
} }
onModelSelectChange(modelOpts[0] ? modelOpts[0].value : ''); onModelSelectChange(modelOpts[0] ? modelOpts[0].value : '');

View File

@@ -770,58 +770,50 @@ class ChatHandler:
class ConfigHandler: class ConfigHandler:
_RECOMMENDED_MODELS = [ _RECOMMENDED_MODELS = [
const.DEEPSEEK_V4_FLASH, const.DEEPSEEK_V4_PRO, const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER,
const.MINIMAX_M2_7_HIGHSPEED, const.MINIMAX_M2_7, const.MINIMAX_M2_5, const.MINIMAX_M2_1, const.MINIMAX_M2_1_LIGHTNING, 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.CLAUDE_4_6_SONNET, const.CLAUDE_4_7_OPUS, const.CLAUDE_4_6_OPUS, const.CLAUDE_4_5_SONNET, 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.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.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,
] ]
# Generic placeholder hints surfaced in the web console. We deliberately
# show the version-path tail (e.g. "/v1") so users are reminded to type
# the full base URL. The form is intentionally vague (`...../v1`) so it
# never looks like a real default a user might paste verbatim — and we
# never auto-rewrite anything on the server side.
_PLACEHOLDER_V1 = "https://...../v1"
_PLACEHOLDER_ZHIPU = "https://...../api/paas/v4"
_PLACEHOLDER_DOUBAO = "https://...../api/v3"
_PLACEHOLDER_GEMINI = "https://....."
PROVIDER_MODELS = OrderedDict([ PROVIDER_MODELS = OrderedDict([
("deepseek", {
"label": "DeepSeek",
"api_key_field": "deepseek_api_key",
"api_base_key": "deepseek_api_base",
"api_base_default": "https://api.deepseek.com/v1",
"api_base_placeholder": _PLACEHOLDER_V1,
"models": [const.DEEPSEEK_V4_FLASH, const.DEEPSEEK_V4_PRO, const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER],
}),
("minimax", { ("minimax", {
"label": "MiniMax", "label": "MiniMax",
"api_key_field": "minimax_api_key", "api_key_field": "minimax_api_key",
"api_base_key": None, "api_base_key": None,
"api_base_default": None, "api_base_default": None,
"api_base_placeholder": "",
"models": [const.MINIMAX_M2_7, const.MINIMAX_M2_7_HIGHSPEED, const.MINIMAX_M2_5, const.MINIMAX_M2_1, const.MINIMAX_M2_1_LIGHTNING], "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],
}),
("claudeAPI", { ("claudeAPI", {
"label": "Claude", "label": "Claude",
"api_key_field": "claude_api_key", "api_key_field": "claude_api_key",
"api_base_key": "claude_api_base", "api_base_key": "claude_api_base",
"api_base_default": "https://api.anthropic.com/v1", "api_base_default": "https://api.anthropic.com/v1",
"api_base_placeholder": _PLACEHOLDER_V1,
"models": [const.CLAUDE_4_6_SONNET, const.CLAUDE_4_7_OPUS, const.CLAUDE_4_6_OPUS, const.CLAUDE_4_5_SONNET], "models": [const.CLAUDE_4_6_SONNET, const.CLAUDE_4_7_OPUS, const.CLAUDE_4_6_OPUS, const.CLAUDE_4_5_SONNET],
}), }),
("gemini", { ("gemini", {
@@ -829,6 +821,7 @@ class ConfigHandler:
"api_key_field": "gemini_api_key", "api_key_field": "gemini_api_key",
"api_base_key": "gemini_api_base", "api_base_key": "gemini_api_base",
"api_base_default": "https://generativelanguage.googleapis.com", "api_base_default": "https://generativelanguage.googleapis.com",
"api_base_placeholder": _PLACEHOLDER_GEMINI,
"models": [const.GEMINI_31_FLASH_LITE_PRE, const.GEMINI_31_PRO_PRE, const.GEMINI_3_FLASH_PRE], "models": [const.GEMINI_31_FLASH_LITE_PRE, const.GEMINI_31_PRO_PRE, const.GEMINI_3_FLASH_PRE],
}), }),
("openai", { ("openai", {
@@ -836,20 +829,47 @@ class ConfigHandler:
"api_key_field": "open_ai_api_key", "api_key_field": "open_ai_api_key",
"api_base_key": "open_ai_api_base", "api_base_key": "open_ai_api_base",
"api_base_default": "https://api.openai.com/v1", "api_base_default": "https://api.openai.com/v1",
"api_base_placeholder": _PLACEHOLDER_V1,
"models": [const.GPT_54, const.GPT_54_MINI, const.GPT_54_NANO, const.GPT_5, const.GPT_41, const.GPT_4o], "models": [const.GPT_54, const.GPT_54_MINI, const.GPT_54_NANO, const.GPT_5, const.GPT_41, const.GPT_4o],
}), }),
("deepseek", { ("zhipu", {
"label": "DeepSeek", "label": "智谱AI",
"api_key_field": "deepseek_api_key", "api_key_field": "zhipu_ai_api_key",
"api_base_key": "deepseek_api_base", "api_base_key": "zhipu_ai_api_base",
"api_base_default": "https://api.deepseek.com/v1", "api_base_default": "https://open.bigmodel.cn/api/paas/v4",
"models": [const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER], "api_base_placeholder": _PLACEHOLDER_ZHIPU,
"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,
"api_base_placeholder": "",
"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",
"api_base_placeholder": _PLACEHOLDER_DOUBAO,
"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",
"api_base_placeholder": _PLACEHOLDER_V1,
"models": [const.KIMI_K2_6, const.KIMI_K2_5, const.KIMI_K2],
}), }),
("modelscope", { ("modelscope", {
"label": "ModelScope", "label": "ModelScope",
"api_key_field": "modelscope_api_key", "api_key_field": "modelscope_api_key",
"api_base_key": None, "api_base_key": None,
"api_base_default": None, "api_base_default": None,
"api_base_placeholder": "",
"models": [const.QWEN3_5_27B, const.QWEN3_235B_A22B_INSTRUCT_2507], "models": [const.QWEN3_5_27B, const.QWEN3_235B_A22B_INSTRUCT_2507],
}), }),
("linkai", { ("linkai", {
@@ -857,6 +877,7 @@ class ConfigHandler:
"api_key_field": "linkai_api_key", "api_key_field": "linkai_api_key",
"api_base_key": None, "api_base_key": None,
"api_base_default": None, "api_base_default": None,
"api_base_placeholder": "",
"models": _RECOMMENDED_MODELS, "models": _RECOMMENDED_MODELS,
}), }),
("custom", { ("custom", {
@@ -864,6 +885,7 @@ class ConfigHandler:
"api_key_field": "custom_api_key", "api_key_field": "custom_api_key",
"api_base_key": "custom_api_base", "api_base_key": "custom_api_base",
"api_base_default": "", "api_base_default": "",
"api_base_placeholder": _PLACEHOLDER_V1,
"models": [], "models": [],
}), }),
]) ])
@@ -912,6 +934,7 @@ class ConfigHandler:
"models": p["models"], "models": p["models"],
"api_base_key": p["api_base_key"], "api_base_key": p["api_base_key"],
"api_base_default": p["api_base_default"], "api_base_default": p["api_base_default"],
"api_base_placeholder": p.get("api_base_placeholder", ""),
"api_key_field": p.get("api_key_field"), "api_key_field": p.get("api_key_field"),
} }

View File

@@ -1 +1 @@
2.0.6 2.0.7

View File

@@ -82,6 +82,8 @@ TTS_1_HD = "tts-1-hd"
# DeepSeek # DeepSeek
DEEPSEEK_CHAT = "deepseek-chat" # DeepSeek-V3对话模型 DEEPSEEK_CHAT = "deepseek-chat" # DeepSeek-V3对话模型
DEEPSEEK_REASONER = "deepseek-reasoner" # DeepSeek-R1模型 DEEPSEEK_REASONER = "deepseek-reasoner" # DeepSeek-R1模型
DEEPSEEK_V4_FLASH = "deepseek-v4-flash" # DeepSeek V4 Flash - 默认推荐 (思考模式 + 工具调用)
DEEPSEEK_V4_PRO = "deepseek-v4-pro" # DeepSeek V4 Pro - 复杂任务更强 (思考模式 + 工具调用)
# Qwen (通义千问 - 阿里云 DashScope) # Qwen (通义千问 - 阿里云 DashScope)
QWEN_TURBO = "qwen-turbo" QWEN_TURBO = "qwen-turbo"
@@ -154,15 +156,21 @@ MODELSCOPE_MODEL_LIST = ["deepseek-ai/DeepSeek-R1-0528", "deepseek-ai/DeepSeek-R
MODEL_LIST = [ MODEL_LIST = [
# DeepSeek
DEEPSEEK_V4_FLASH, DEEPSEEK_V4_PRO, DEEPSEEK_CHAT, DEEPSEEK_REASONER,
# MiniMax
MiniMax, MINIMAX_M2_7, MINIMAX_M2_7_HIGHSPEED, MINIMAX_M2_5, MINIMAX_M2_1, MINIMAX_M2_1_LIGHTNING, MINIMAX_M2, MINIMAX_ABAB6_5,
# Claude # 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, 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_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", "claude", "claude-3-haiku", "claude-3-sonnet", "claude-3-opus", "claude-3.5-sonnet",
# Gemini # 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_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, GEMINI_20_FLASH, GEMINI_20_flash_exp, GEMINI_15_PRO, GEMINI_15_flash, GEMINI_PRO, GEMINI,
# OpenAI # OpenAI
GPT35, GPT35_0125, GPT35_1106, "gpt-3.5-turbo-16k", GPT35, GPT35_0125, GPT35_1106, "gpt-3.5-turbo-16k",
GPT4, GPT4_06_13, GPT4_32k, GPT4_32k_06_13, 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_5, GPT_5_MINI, GPT_5_NANO,
GPT_54, GPT_54_MINI, GPT_54_NANO, GPT_54, GPT_54_MINI, GPT_54_NANO,
O1, O1_MINI, 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, 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, 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", MOONSHOT, "moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k",
KIMI_K2_6, KIMI_K2_5, KIMI_K2, KIMI_K2_6, KIMI_K2_5, KIMI_K2,
# Doubao # ModelScope
DOUBAO, DOUBAO_SEED_2_CODE, DOUBAO_SEED_2_PRO, DOUBAO_SEED_2_LITE, DOUBAO_SEED_2_MINI, MODELSCOPE,
# LinkAI
LINKAI_35, LINKAI_4_TURBO, LINKAI_4o,
# 其他模型 # 其他模型
WEN_XIN, WEN_XIN_4, XUNFEI, 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 MODEL_LIST = MODEL_LIST + GITEE_AI_MODEL_LIST + MODELSCOPE_MODEL_LIST

View File

@@ -1,6 +1,8 @@
{ {
"channel_type": "weixin", "channel_type": "weixin",
"model": "MiniMax-M2.7", "model": "deepseek-v4-flash",
"deepseek_api_key": "",
"deepseek_api_base": "https://api.deepseek.com/v1",
"minimax_api_key": "", "minimax_api_key": "",
"zhipu_ai_api_key": "", "zhipu_ai_api_key": "",
"ark_api_key": "", "ark_api_key": "",
@@ -31,5 +33,6 @@
"agent_max_context_tokens": 50000, "agent_max_context_tokens": 50000,
"agent_max_context_turns": 20, "agent_max_context_turns": 20,
"agent_max_steps": 20, "agent_max_steps": 20,
"enable_thinking": false,
"knowledge": true "knowledge": true
} }

View File

@@ -196,6 +196,8 @@ available_setting = {
"minimax_api_key": "", "minimax_api_key": "",
"Minimax_group_id": "", "Minimax_group_id": "",
"Minimax_base_url": "", "Minimax_base_url": "",
"deepseek_api_key": "",
"deepseek_api_base": "https://api.deepseek.com/v1",
"web_port": 9899, "web_port": 9899,
"web_password": "", # Web console password; empty means no authentication required "web_password": "", # Web console password; empty means no authentication required
"web_session_expire_days": 30, # Auth session expiry in days "web_session_expire_days": 30, # Auth session expiry in days
@@ -204,7 +206,7 @@ available_setting = {
"agent_max_context_tokens": 50000, # Agent模式下最大上下文tokens "agent_max_context_tokens": 50000, # Agent模式下最大上下文tokens
"agent_max_context_turns": 20, # Agent模式下最大上下文记忆轮次 "agent_max_context_turns": 20, # Agent模式下最大上下文记忆轮次
"agent_max_steps": 20, # Agent模式下单次运行最大决策步数 "agent_max_steps": 20, # Agent模式下单次运行最大决策步数
"enable_thinking": False, # Whether to enable deep thinking for web channel "enable_thinking": False, # Enable deep-thinking mode for thinking-capable models
"knowledge": True, # 是否开启知识库功能 "knowledge": True, # 是否开启知识库功能
# Per-skill runtime config. Nested keys are flattened to env vars at startup # Per-skill runtime config. Nested keys are flattened to env vars at startup
# using the rule: skill[<name>][<key>] -> SKILL_<NAME>_<KEY> # using the rule: skill[<name>][<key>] -> SKILL_<NAME>_<KEY>
@@ -382,6 +384,8 @@ def load_config():
"gemini_api_base": "GEMINI_API_BASE", "gemini_api_base": "GEMINI_API_BASE",
"minimax_api_key": "MINIMAX_API_KEY", "minimax_api_key": "MINIMAX_API_KEY",
"minimax_api_base": "MINIMAX_API_BASE", "minimax_api_base": "MINIMAX_API_BASE",
"deepseek_api_key": "DEEPSEEK_API_KEY",
"deepseek_api_base": "DEEPSEEK_API_BASE",
"zhipu_ai_api_key": "ZHIPU_AI_API_KEY", "zhipu_ai_api_key": "ZHIPU_AI_API_KEY",
"zhipu_ai_api_base": "ZHIPU_AI_API_BASE", "zhipu_ai_api_base": "ZHIPU_AI_API_BASE",
"moonshot_api_key": "MOONSHOT_API_KEY", "moonshot_api_key": "MOONSHOT_API_KEY",

View File

@@ -9,7 +9,9 @@ services:
- "9899:9899" - "9899:9899"
environment: environment:
CHANNEL_TYPE: 'weixin' CHANNEL_TYPE: 'weixin'
MODEL: 'MiniMax-M2.7' MODEL: 'deepseek-v4-flash'
DEEPSEEK_API_KEY: ''
DEEPSEEK_API_BASE: 'https://api.deepseek.com/v1'
MINIMAX_API_KEY: '' MINIMAX_API_KEY: ''
ZHIPU_AI_API_KEY: '' ZHIPU_AI_API_KEY: ''
ARK_API_KEY: '' ARK_API_KEY: ''

View File

@@ -22,7 +22,7 @@ Web 控制台是 CowAgent 的默认通道,启动后会自动运行,通过浏
| `web_port` | Web 服务监听端口 | `9899` | | `web_port` | Web 服务监听端口 | `9899` |
| `web_password` | 访问密码,留空表示不启用密码保护 | `""` | | `web_password` | 访问密码,留空表示不启用密码保护 | `""` |
| `web_session_expire_days` | 登录会话有效天数 | `30` | | `web_session_expire_days` | 登录会话有效天数 | `30` |
| `enable_thinking` | 是否启用深度思考,开启后 Web 端展示推理过程,关闭可加速响应 | `false` | | `enable_thinking` | 是否启用深度思考模式 | `false` |
配置密码后,访问控制台时需先输入密码完成登录。登录状态默认保持 30 天,期间重启服务也无需重新登录。密码也支持在控制台的「配置」页面中在线修改。 配置密码后,访问控制台时需先输入密码完成登录。登录状态默认保持 30 天,期间重启服务也无需重新登录。密码也支持在控制台的「配置」页面中在线修改。

View File

@@ -58,18 +58,18 @@ Session: 12 messages | 8 skills loaded
**修改配置项:** **修改配置项:**
```text ```text
/config model deepseek-chat /config model deepseek-v4-flash
``` ```
**支持修改的配置项:** **支持修改的配置项:**
| 配置项 | 说明 | 示例值 | | 配置项 | 说明 | 示例值 |
| --- | --- | --- | | --- | --- | --- |
| `model` | AI 模型名称 | `deepseek-chat` | | `model` | AI 模型名称 | `deepseek-v4-flash` |
| `agent_max_context_tokens` | 最大上下文 tokens | `40000` | | `agent_max_context_tokens` | 最大上下文 tokens | `40000` |
| `agent_max_context_turns` | 最大上下文记忆轮次 | `30` | | `agent_max_context_turns` | 最大上下文记忆轮次 | `30` |
| `agent_max_steps` | 单次任务最大决策步数 | `15` | | `agent_max_steps` | 单次任务最大决策步数 | `15` |
| `enable_thinking` | 是否启用深度思考 | `true` / `false` | | `enable_thinking` | 是否启用深度思考模式 | `true` / `false` |
<Note> <Note>
修改 `model` 时,系统会自动匹配对应的模型调用方式。配置会写入 `config.json` 并持久保存。 修改 `model` 时,系统会自动匹配对应的模型调用方式。配置会写入 `config.json` 并持久保存。

View File

@@ -72,15 +72,15 @@
"group": "模型配置", "group": "模型配置",
"pages": [ "pages": [
"models/index", "models/index",
"models/deepseek",
"models/minimax", "models/minimax",
"models/glm",
"models/qwen",
"models/kimi",
"models/doubao",
"models/claude", "models/claude",
"models/gemini", "models/gemini",
"models/openai", "models/openai",
"models/deepseek", "models/glm",
"models/qwen",
"models/doubao",
"models/kimi",
"models/linkai", "models/linkai",
"models/coding-plan", "models/coding-plan",
"models/custom" "models/custom"
@@ -257,15 +257,15 @@
"group": "Model Configuration", "group": "Model Configuration",
"pages": [ "pages": [
"en/models/index", "en/models/index",
"en/models/deepseek",
"en/models/minimax", "en/models/minimax",
"en/models/glm",
"en/models/qwen",
"en/models/kimi",
"en/models/doubao",
"en/models/claude", "en/models/claude",
"en/models/gemini", "en/models/gemini",
"en/models/openai", "en/models/openai",
"en/models/deepseek", "en/models/glm",
"en/models/qwen",
"en/models/doubao",
"en/models/kimi",
"en/models/linkai", "en/models/linkai",
"en/models/coding-plan", "en/models/coding-plan",
"en/models/custom" "en/models/custom"
@@ -441,15 +441,15 @@
"group": "モデル設定", "group": "モデル設定",
"pages": [ "pages": [
"ja/models/index", "ja/models/index",
"ja/models/deepseek",
"ja/models/minimax", "ja/models/minimax",
"ja/models/glm",
"ja/models/qwen",
"ja/models/kimi",
"ja/models/doubao",
"ja/models/claude", "ja/models/claude",
"ja/models/gemini", "ja/models/gemini",
"ja/models/openai", "ja/models/openai",
"ja/models/deepseek", "ja/models/glm",
"ja/models/qwen",
"ja/models/doubao",
"ja/models/kimi",
"ja/models/linkai", "ja/models/linkai",
"ja/models/coding-plan", "ja/models/coding-plan",
"ja/models/custom" "ja/models/custom"

View File

@@ -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. -**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. -**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. -**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 DeepSeek, MiniMax, 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. -**Multi-platform Deployment**: Runs on local computers or servers, integrable into WeChat, Web, Feishu, DingTalk, WeChat Official Account, and WeCom applications.
## Disclaimer ## Disclaimer
@@ -164,15 +164,15 @@ Supports mainstream model providers. Recommended models for Agent mode:
| Provider | Recommended Model | | Provider | Recommended Model |
| --- | --- | | --- | --- |
| DeepSeek | `deepseek-v4-flash` |
| MiniMax | `MiniMax-M2.7` | | MiniMax | `MiniMax-M2.7` |
| GLM | `glm-5.1` |
| Kimi | `kimi-k2.6` |
| Doubao | `doubao-seed-2-0-code-preview-260215` |
| Qwen | `qwen3.6-plus` |
| Claude | `claude-sonnet-4-6` | | Claude | `claude-sonnet-4-6` |
| Gemini | `gemini-3.1-pro-preview` | | Gemini | `gemini-3.1-pro-preview` |
| OpenAI | `gpt-5.4` | | 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). For detailed configuration of each model, see the [Models documentation](https://docs.cowagent.ai/en/models/index).

View File

@@ -44,18 +44,18 @@ View or modify runtime configuration. Changes take effect immediately without re
**Modify a config item:** **Modify a config item:**
```text ```text
/config model deepseek-chat /config model deepseek-v4-flash
``` ```
**Configurable items:** **Configurable items:**
| Item | Description | Example | | Item | Description | Example |
| --- | --- | --- | | --- | --- | --- |
| `model` | AI model name | `deepseek-chat` | | `model` | AI model name | `deepseek-v4-flash` |
| `agent_max_context_tokens` | Max context tokens | `40000` | | `agent_max_context_tokens` | Max context tokens | `40000` |
| `agent_max_context_turns` | Max context memory turns | `30` | | `agent_max_context_turns` | Max context memory turns | `30` |
| `agent_max_steps` | Max decision steps per task | `15` | | `agent_max_steps` | Max decision steps per task | `15` |
| `enable_thinking` | Enable deep thinking | `true` / `false` | | `enable_thinking` | Enable deep thinking mode | `true` / `false` |
<Note> <Note>
When changing `model`, the system automatically matches the corresponding model API. Configuration is persisted to `config.json`. When changing `model`, the system automatically matches the corresponding model API. Configuration is persisted to `config.json`.

View File

@@ -121,7 +121,8 @@ sudo docker logs -f chatgpt-on-wechat
```json ```json
{ {
"channel_type": "web", "channel_type": "web",
"model": "MiniMax-M2.5", "model": "deepseek-v4-flash",
"deepseek_api_key": "",
"agent": true, "agent": true,
"agent_workspace": "~/cow", "agent_workspace": "~/cow",
"agent_max_context_tokens": 40000, "agent_max_context_tokens": 40000,
@@ -133,7 +134,7 @@ sudo docker logs -f chatgpt-on-wechat
| Parameter | Description | Default | | Parameter | Description | Default |
| --- | --- | --- | | --- | --- | --- |
| `channel_type` | Channel type | `web` | | `channel_type` | Channel type | `web` |
| `model` | Model name | `MiniMax-M2.5` | | `model` | Model name | `deepseek-v4-flash` |
| `agent` | Enable Agent mode | `true` | | `agent` | Enable Agent mode | `true` |
| `agent_workspace` | Agent workspace path | `~/cow` | | `agent_workspace` | Agent workspace path | `~/cow` |
| `agent_max_context_tokens` | Max context tokens | `40000` | | `agent_max_context_tokens` | Max context tokens | `40000` |

View File

@@ -20,7 +20,7 @@ For models accessed via OpenAI-compatible APIs, such as:
```json ```json
{ {
"bot_type": "custom", "bot_type": "custom",
"model": "deepseek-chat", "model": "deepseek-v4-flash",
"custom_api_key": "YOUR_API_KEY", "custom_api_key": "YOUR_API_KEY",
"custom_api_base": "https://{your-proxy.com}/v1" "custom_api_base": "https://{your-proxy.com}/v1"
} }

View File

@@ -7,26 +7,57 @@ Option 1: Native integration (recommended):
```json ```json
{ {
"model": "deepseek-chat", "model": "deepseek-v4-flash",
"deepseek_api_key": "YOUR_API_KEY" "deepseek_api_key": "YOUR_API_KEY"
} }
``` ```
| Parameter | Description | | Parameter | Description |
| --- | --- | | --- | --- |
| `model` | `deepseek-chat` (DeepSeek-V3.2, non-thinking mode), `deepseek-reasoner` (DeepSeek-R1, thinking mode) | | `model` | Supports `deepseek-v4-flash` (default) and `deepseek-v4-pro` |
| `deepseek_api_key` | Create at [DeepSeek Platform](https://platform.deepseek.com/api_keys) | | `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 | | `deepseek_api_base` | Optional, defaults to `https://api.deepseek.com/v1`. Can be changed to a third-party proxy |
## Model Selection
| Model | Use Case |
| --- | --- |
| `deepseek-v4-flash` | Default: fast and cost-effective |
| `deepseek-v4-pro` | Stronger on complex tasks |
## Thinking Mode
The V4 series (`deepseek-v4-flash` / `deepseek-v4-pro`) supports an explicit "thinking mode": the model emits a chain-of-thought (`reasoning_content`) before the final answer to improve answer quality.
### Toggle
Controlled by the global `enable_thinking` setting:
```json
{
"enable_thinking": true
}
```
- `true`: thinking is on across all channels. The Web console renders the reasoning trace; IM channels (WeChat / WeCom / DingTalk / Feishu) don't render it but still benefit from higher answer quality.
- `false`: thinking off, faster responses with lower first-token latency.
### Notes
- **Sampling parameters**: under thinking mode, `temperature`, `top_p`, `presence_penalty`, and `frequency_penalty` are silently ignored by the server (no error). CowAgent skips sending them automatically.
- **Multi-turn tool calls**: once the history contains any tool-call turn, DeepSeek requires `reasoning_content` on every assistant message. CowAgent handles the round-trip automatically, including across mid-session toggles of the thinking switch.
<Tip>
Start with `deepseek-v4-flash`; switch to `deepseek-v4-pro` for harder tasks; enable `enable_thinking` when you want deeper reasoning.
</Tip>
Option 2: OpenAI-compatible configuration: Option 2: OpenAI-compatible configuration:
```json ```json
{ {
"model": "deepseek-chat", "model": "deepseek-v4-flash",
"bot_type": "openai", "bot_type": "openai",
"open_ai_api_key": "YOUR_API_KEY", "open_ai_api_key": "YOUR_API_KEY",
"open_ai_api_base": "https://api.deepseek.com/v1" "open_ai_api_base": "https://api.deepseek.com/v1"
} }
``` ```

View File

@@ -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. CowAgent supports mainstream LLMs from domestic and international providers. Model interfaces are implemented in the project's `models/` directory.
<Note> <Note>
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: deepseek-v4-flash, MiniMax-M2.7, claude-sonnet-4-6, gemini-3.1-pro-preview, glm-5.1, qwen3.6-plus, kimi-k2.6
</Note> </Note>
## Configuration ## Configuration
@@ -18,21 +18,12 @@ You can also use the [LinkAI](https://link-ai.tech) platform interface to flexib
## Supported Models ## Supported Models
<CardGroup cols={2}> <CardGroup cols={2}>
<Card title="DeepSeek" href="/en/models/deepseek">
deepseek-v4-flash, deepseek-v4-pro, and more
</Card>
<Card title="MiniMax" href="/en/models/minimax"> <Card title="MiniMax" href="/en/models/minimax">
MiniMax-M2.7 and other series models MiniMax-M2.7 and other series models
</Card> </Card>
<Card title="GLM (Zhipu AI)" href="/en/models/glm">
glm-5.1, glm-5-turbo, glm-5 and other series models
</Card>
<Card title="Qwen (Tongyi Qianwen)" href="/en/models/qwen">
qwen3.6-plus, qwen3-max and more
</Card>
<Card title="Kimi" href="/en/models/kimi">
kimi-k2.6, kimi-k2.5, kimi-k2 and more
</Card>
<Card title="Doubao (ByteDance)" href="/en/models/doubao">
doubao-seed series models
</Card>
<Card title="Claude" href="/en/models/claude"> <Card title="Claude" href="/en/models/claude">
claude-sonnet-4-6 and more claude-sonnet-4-6 and more
</Card> </Card>
@@ -42,8 +33,17 @@ You can also use the [LinkAI](https://link-ai.tech) platform interface to flexib
<Card title="OpenAI" href="/en/models/openai"> <Card title="OpenAI" href="/en/models/openai">
gpt-5.4, gpt-4.1, o-series and more gpt-5.4, gpt-4.1, o-series and more
</Card> </Card>
<Card title="DeepSeek" href="/en/models/deepseek"> <Card title="GLM (Zhipu AI)" href="/en/models/glm">
deepseek-chat, deepseek-reasoner glm-5.1, glm-5-turbo, glm-5 and other series models
</Card>
<Card title="Qwen (Tongyi Qianwen)" href="/en/models/qwen">
qwen3.6-plus, qwen3-max and more
</Card>
<Card title="Doubao (ByteDance)" href="/en/models/doubao">
doubao-seed series models
</Card>
<Card title="Kimi" href="/en/models/kimi">
kimi-k2.6, kimi-k2.5, kimi-k2 and more
</Card> </Card>
<Card title="LinkAI" href="/en/models/linkai"> <Card title="LinkAI" href="/en/models/linkai">
Unified multi-model interface + knowledge base Unified multi-model interface + knowledge base

View File

@@ -3,7 +3,7 @@ title: LinkAI
description: Unified access to multiple models via LinkAI platform description: Unified access to multiple models via LinkAI platform
--- ---
The [LinkAI](https://link-ai.tech) platform lets you flexibly switch between OpenAI, Claude, Gemini, DeepSeek, Qwen, Kimi, and other models, with support for knowledge base, workflows, plugins, and other Agent capabilities. The [LinkAI](https://link-ai.tech) platform lets you flexibly switch between OpenAI, Claude, Gemini, DeepSeek, MiniMax, Qwen, Kimi, and other models, with support for knowledge base, workflows, plugins, and other Agent capabilities.
```json ```json
{ {

View File

@@ -139,7 +139,8 @@ sudo docker logs -f chatgpt-on-wechat
```json ```json
{ {
"channel_type": "web", "channel_type": "web",
"model": "MiniMax-M2.7", "model": "deepseek-v4-flash",
"deepseek_api_key": "",
"agent": true, "agent": true,
"agent_workspace": "~/cow", "agent_workspace": "~/cow",
"agent_max_context_tokens": 40000, "agent_max_context_tokens": 40000,
@@ -152,8 +153,9 @@ sudo docker logs -f chatgpt-on-wechat
```yaml ```yaml
environment: environment:
CHANNEL_TYPE: 'web' CHANNEL_TYPE: 'web'
MODEL: 'MiniMax-M2.7' MODEL: 'deepseek-v4-flash'
MINIMAX_API_KEY: 'your-api-key' DEEPSEEK_API_KEY: 'your-api-key'
DEEPSEEK_API_BASE: 'https://api.deepseek.com/v1'
AGENT: 'True' AGENT: 'True'
AGENT_MAX_CONTEXT_TOKENS: 40000 AGENT_MAX_CONTEXT_TOKENS: 40000
AGENT_MAX_CONTEXT_TURNS: 30 AGENT_MAX_CONTEXT_TURNS: 30
@@ -165,7 +167,7 @@ sudo docker logs -f chatgpt-on-wechat
| 参数 | 环境变量 | 说明 | 默认值 | | 参数 | 环境变量 | 说明 | 默认值 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `channel_type` | `CHANNEL_TYPE` | 接入渠道类型 | `web` | | `channel_type` | `CHANNEL_TYPE` | 接入渠道类型 | `web` |
| `model` | `MODEL` | 模型名称 | `MiniMax-M2.5` | | `model` | `MODEL` | 模型名称 | `deepseek-v4-flash` |
| `agent` | `AGENT` | 是否启用 Agent 模式 | `true` | | `agent` | `AGENT` | 是否启用 Agent 模式 | `true` |
| `agent_workspace` | - | Agent 工作空间路径 | `~/cow` | | `agent_workspace` | - | Agent 工作空间路径 | `~/cow` |
| `agent_max_context_tokens` | `AGENT_MAX_CONTEXT_TOKENS` | 最大上下文 tokens | `40000` | | `agent_max_context_tokens` | `AGENT_MAX_CONTEXT_TOKENS` | 最大上下文 tokens | `40000` |

View File

@@ -81,5 +81,5 @@ Agent 的工作空间默认位于 `~/cow` 目录,用于存储系统提示词
| `agent_max_context_tokens` | 最大上下文 token 数 | `50000` | | `agent_max_context_tokens` | 最大上下文 token 数 | `50000` |
| `agent_max_context_turns` | 最大上下文记忆轮次 | `20` | | `agent_max_context_turns` | 最大上下文记忆轮次 | `20` |
| `agent_max_steps` | 单次任务最大决策步数 | `20` | | `agent_max_steps` | 单次任务最大决策步数 | `20` |
| `enable_thinking` | 是否启用深度思考,开启后 Web 端展示推理过程,关闭可加速响应 | `false` | | `enable_thinking` | 是否启用深度思考模式 | `false` |
| `knowledge` | 是否启用个人知识库 | `true` | | `knowledge` | 是否启用个人知识库 | `true` |

View File

@@ -28,7 +28,7 @@
-**ツールシステム**: ファイル読み書き、ターミナル実行、ブラウザ操作、スケジュールタスク、メッセージ送信などの組み込みツールを提供。Agentが自律的に呼び出して複雑なタスクを完了します。 -**ツールシステム**: ファイル読み書き、ターミナル実行、ブラウザ操作、スケジュールタスク、メッセージ送信などの組み込みツールを提供。Agentが自律的に呼び出して複雑なタスクを完了します。
-**CLIシステム**: ターミナルコマンドとチャットコマンドを提供し、プロセス管理、Skillインストール、設定変更などの操作をサポートします。 -**CLIシステム**: ターミナルコマンドとチャットコマンドを提供し、プロセス管理、Skillインストール、設定変更などの操作をサポートします。
-**マルチモーダルメッセージ**: テキスト、画像、音声、ファイルなど、さまざまなメッセージタイプの解析・処理・生成・送信に対応しています。 -**マルチモーダルメッセージ**: テキスト、画像、音声、ファイルなど、さまざまなメッセージタイプの解析・処理・生成・送信に対応しています。
-**複数モデル対応**: OpenAI、Claude、Gemini、DeepSeek、MiniMax、GLM、Qwen、Kimi、Doubaoなど、主要なモデルプロバイダーに対応しています。 -**複数モデル対応**: DeepSeek、MiniMax、Claude、Gemini、OpenAI、GLM、Qwen、Doubao、Kimiなど、主要なモデルプロバイダーに対応しています。
-**マルチプラットフォームデプロイ**: ローカルPCやサーバー上で実行でき、WeChat、Web、Feishu、DingTalk、WeChat公式アカウント、WeComアプリケーションに統合可能です。 -**マルチプラットフォームデプロイ**: ローカルPCやサーバー上で実行でき、WeChat、Web、Feishu、DingTalk、WeChat公式アカウント、WeComアプリケーションに統合可能です。
## 免責事項 ## 免責事項
@@ -164,15 +164,15 @@ sudo docker logs -f chatgpt-on-wechat
| プロバイダー | 推奨モデル | | プロバイダー | 推奨モデル |
| --- | --- | | --- | --- |
| DeepSeek | `deepseek-v4-flash` |
| MiniMax | `MiniMax-M2.7` | | MiniMax | `MiniMax-M2.7` |
| GLM | `glm-5.1` |
| Kimi | `kimi-k2.6` |
| Doubao | `doubao-seed-2-0-code-preview-260215` |
| Qwen | `qwen3.6-plus` |
| Claude | `claude-sonnet-4-6` | | Claude | `claude-sonnet-4-6` |
| Gemini | `gemini-3.1-pro-preview` | | Gemini | `gemini-3.1-pro-preview` |
| OpenAI | `gpt-5.4` | | 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)を参照してください。 各モデルの詳細設定については、[モデルドキュメント](https://docs.cowagent.ai/en/models/index)を参照してください。

View File

@@ -44,18 +44,18 @@ description: ステータスの確認、設定管理、コンテキスト制御
**設定項目を変更:** **設定項目を変更:**
```text ```text
/config model deepseek-chat /config model deepseek-v4-flash
``` ```
**変更可能な設定項目:** **変更可能な設定項目:**
| 項目 | 説明 | 例 | | 項目 | 説明 | 例 |
| --- | --- | --- | | --- | --- | --- |
| `model` | AI モデル名 | `deepseek-chat` | | `model` | AI モデル名 | `deepseek-v4-flash` |
| `agent_max_context_tokens` | 最大コンテキストトークン数 | `40000` | | `agent_max_context_tokens` | 最大コンテキストトークン数 | `40000` |
| `agent_max_context_turns` | 最大コンテキスト記憶ターン数 | `30` | | `agent_max_context_turns` | 最大コンテキスト記憶ターン数 | `30` |
| `agent_max_steps` | タスクごとの最大判断ステップ数 | `15` | | `agent_max_steps` | タスクごとの最大判断ステップ数 | `15` |
| `enable_thinking` | ディープシンキングの有効化 | `true` / `false` | | `enable_thinking` | ディープシンキングモードの有効化 | `true` / `false` |
<Note> <Note>
`model` を変更すると、システムが対応するモデル API を自動的にマッチングします。設定は `config.json` に永続的に保存されます。 `model` を変更すると、システムが対応するモデル API を自動的にマッチングします。設定は `config.json` に永続的に保存されます。

View File

@@ -121,7 +121,8 @@ sudo docker logs -f chatgpt-on-wechat
```json ```json
{ {
"channel_type": "web", "channel_type": "web",
"model": "MiniMax-M2.5", "model": "deepseek-v4-flash",
"deepseek_api_key": "",
"agent": true, "agent": true,
"agent_workspace": "~/cow", "agent_workspace": "~/cow",
"agent_max_context_tokens": 40000, "agent_max_context_tokens": 40000,
@@ -133,7 +134,7 @@ sudo docker logs -f chatgpt-on-wechat
| パラメータ | 説明 | デフォルト値 | | パラメータ | 説明 | デフォルト値 |
| --- | --- | --- | | --- | --- | --- |
| `channel_type` | チャネルタイプ | `web` | | `channel_type` | チャネルタイプ | `web` |
| `model` | モデル名 | `MiniMax-M2.5` | | `model` | モデル名 | `deepseek-v4-flash` |
| `agent` | Agent モードを有効化 | `true` | | `agent` | Agent モードを有効化 | `true` |
| `agent_workspace` | Agent のワークスペースパス | `~/cow` | | `agent_workspace` | Agent のワークスペースパス | `~/cow` |
| `agent_max_context_tokens` | 最大コンテキストトークン数 | `40000` | | `agent_max_context_tokens` | 最大コンテキストトークン数 | `40000` |

View File

@@ -20,7 +20,7 @@ OpenAI互換プロトコルでアクセスするモデルサービスに適用
```json ```json
{ {
"bot_type": "custom", "bot_type": "custom",
"model": "deepseek-chat", "model": "deepseek-v4-flash",
"custom_api_key": "YOUR_API_KEY", "custom_api_key": "YOUR_API_KEY",
"custom_api_base": "https://{your-proxy.com}/v1" "custom_api_base": "https://{your-proxy.com}/v1"
} }

View File

@@ -7,22 +7,55 @@ description: DeepSeekモデルの設定
```json ```json
{ {
"model": "deepseek-chat", "model": "deepseek-v4-flash",
"deepseek_api_key": "YOUR_API_KEY" "deepseek_api_key": "YOUR_API_KEY"
} }
``` ```
| パラメータ | 説明 | | パラメータ | 説明 |
| --- | --- | | --- | --- |
| `model` | `deepseek-chat`DeepSeek-V3.2、非思考モード)、`deepseek-reasoner`DeepSeek-R1、思考モード | | `model` | `deepseek-v4-flash`(デフォルト)、`deepseek-v4-pro` をサポート |
| `deepseek_api_key` | [DeepSeek Platform](https://platform.deepseek.com/api_keys)で作成 | | `deepseek_api_key` | [DeepSeek Platform](https://platform.deepseek.com/api_keys) で作成 |
| `deepseek_api_base` | オプション、デフォルトは `https://api.deepseek.com/v1`。サードパーティプロキシに変更可能 | | `deepseek_api_base` | オプション、デフォルトは `https://api.deepseek.com/v1`。サードパーティプロキシに変更可能 |
## モデルの選び方
| モデル | 適用シーン |
| --- | --- |
| `deepseek-v4-flash` | デフォルト推奨、高速・低コスト |
| `deepseek-v4-pro` | 複雑なタスクでより強力 |
## 思考モード
V4シリーズ`deepseek-v4-flash` / `deepseek-v4-pro`)は明示的な「思考モード」をサポートします。最終回答の前に思考内容(`reasoning_content`)を出力することで、回答品質を高めます。
### スイッチ
グローバル設定 `enable_thinking` で制御します:
```json
{
"enable_thinking": true
}
```
- `true`すべてのチャネルで思考モードがオン。Webコンソールでは思考過程を表示し、IMチャネルWeChat / WeCom / DingTalk / Feishuでは表示されないものの、回答品質の向上というメリットを得られます。
- `false`:思考オフ、応答が速く、初回トークンの遅延も低くなります。
### 注意事項
- **サンプリングパラメータ**:思考モード時は `temperature`、`top_p`、`presence_penalty`、`frequency_penalty` がサーバ側で無視されますエラーにはなりません。CowAgentは自動的に送信をスキップします。
- **マルチターンのツール呼び出し**履歴にツール呼び出しが含まれる場合、DeepSeekはすべてのassistantメッセージに `reasoning_content` を返送するよう要求します。CowAgentが自動でラウンドトリップ処理を行うため、セッション途中で思考スイッチを切り替えてもエラーになりません。
<Tip>
通常は `deepseek-v4-flash` を使い、難しいタスクでは `deepseek-v4-pro` に切り替え、深い思考が必要な時は `enable_thinking` を有効にしてください。
</Tip>
方法2OpenAI互換方式 方法2OpenAI互換方式
```json ```json
{ {
"model": "deepseek-chat", "model": "deepseek-v4-flash",
"bot_type": "openai", "bot_type": "openai",
"open_ai_api_key": "YOUR_API_KEY", "open_ai_api_key": "YOUR_API_KEY",
"open_ai_api_base": "https://api.deepseek.com/v1" "open_ai_api_base": "https://api.deepseek.com/v1"

View File

@@ -6,7 +6,7 @@ description: CowAgentがサポートするモデルとおすすめの選択肢
CowAgentは国内外の主要なLLMをサポートしています。モデルインターフェースはプロジェクトの`models/`ディレクトリに実装されています。 CowAgentは国内外の主要なLLMをサポートしています。モデルインターフェースはプロジェクトの`models/`ディレクトリに実装されています。
<Note> <Note>
Agent モードでは、品質とコストのバランスから以下のモデルをおすすめします: MiniMax-M2.7、glm-5.1、kimi-k2.6、qwen3.6-plus、claude-sonnet-4-6、gemini-3.1-pro-preview Agent モードでは、品質とコストのバランスから以下のモデルをおすすめします: deepseek-v4-flash、MiniMax-M2.7、claude-sonnet-4-6、gemini-3.1-pro-preview、glm-5.1、qwen3.6-plus、kimi-k2.6
</Note> </Note>
## 設定 ## 設定
@@ -18,21 +18,12 @@ CowAgentは国内外の主要なLLMをサポートしています。モデルイ
## サポートモデル ## サポートモデル
<CardGroup cols={2}> <CardGroup cols={2}>
<Card title="DeepSeek" href="/ja/models/deepseek">
deepseek-v4-flash、deepseek-v4-pro など
</Card>
<Card title="MiniMax" href="/ja/models/minimax"> <Card title="MiniMax" href="/ja/models/minimax">
MiniMax-M2.7およびその他のシリーズモデル MiniMax-M2.7およびその他のシリーズモデル
</Card> </Card>
<Card title="GLM (智谱AI)" href="/ja/models/glm">
glm-5.1、glm-5-turbo、glm-5およびその他のシリーズモデル
</Card>
<Card title="Qwen (通义千问)" href="/ja/models/qwen">
qwen3.6-plus、qwen3-maxなど
</Card>
<Card title="Kimi" href="/ja/models/kimi">
kimi-k2.6、kimi-k2.5、kimi-k2など
</Card>
<Card title="Doubao (ByteDance)" href="/ja/models/doubao">
doubao-seedシリーズモデル
</Card>
<Card title="Claude" href="/ja/models/claude"> <Card title="Claude" href="/ja/models/claude">
claude-sonnet-4-6など claude-sonnet-4-6など
</Card> </Card>
@@ -42,8 +33,17 @@ CowAgentは国内外の主要なLLMをサポートしています。モデルイ
<Card title="OpenAI" href="/ja/models/openai"> <Card title="OpenAI" href="/ja/models/openai">
gpt-5.4、gpt-4.1、oシリーズなど gpt-5.4、gpt-4.1、oシリーズなど
</Card> </Card>
<Card title="DeepSeek" href="/ja/models/deepseek"> <Card title="GLM (智谱AI)" href="/ja/models/glm">
deepseek-chat、deepseek-reasoner glm-5.1、glm-5-turbo、glm-5およびその他のシリーズモデル
</Card>
<Card title="Qwen (通义千问)" href="/ja/models/qwen">
qwen3.6-plus、qwen3-maxなど
</Card>
<Card title="Doubao (ByteDance)" href="/ja/models/doubao">
doubao-seedシリーズモデル
</Card>
<Card title="Kimi" href="/ja/models/kimi">
kimi-k2.6、kimi-k2.5、kimi-k2など
</Card> </Card>
<Card title="LinkAI" href="/ja/models/linkai"> <Card title="LinkAI" href="/ja/models/linkai">
統合マルチモデルインターフェース + ナレッジベース 統合マルチモデルインターフェース + ナレッジベース

View File

@@ -3,7 +3,7 @@ title: LinkAI
description: LinkAIプラットフォームで複数モデルに統合アクセス description: LinkAIプラットフォームで複数モデルに統合アクセス
--- ---
[LinkAI](https://link-ai.tech)プラットフォームでは、OpenAI、Claude、Gemini、DeepSeek、Qwen、Kimiなどのモデルを柔軟に切り替えることができ、ナレッジベース、ワークフロー、プラグイン、その他のAgent機能をサポートしています。 [LinkAI](https://link-ai.tech)プラットフォームでは、OpenAI、Claude、Gemini、DeepSeek、MiniMax、Qwen、Kimiなどのモデルを柔軟に切り替えることができ、ナレッジベース、ワークフロー、プラグイン、その他のAgent機能をサポートしています。
```json ```json
{ {

View File

@@ -7,25 +7,57 @@ description: DeepSeek 模型配置
```json ```json
{ {
"model": "deepseek-chat", "model": "deepseek-v4-flash",
"deepseek_api_key": "YOUR_API_KEY" "deepseek_api_key": "YOUR_API_KEY"
} }
``` ```
| 参数 | 说明 | | 参数 | 说明 |
| --- | --- | | --- | --- |
| `model` | `deepseek-chat`DeepSeek-V3.2,非思考模式)、`deepseek-reasoner`DeepSeek-R1思考模式 | | `model` | 支持 `deepseek-v4-flash`(默认)、`deepseek-v4-pro` |
| `deepseek_api_key` | 在 [DeepSeek 平台](https://platform.deepseek.com/api_keys) 创建 | | `deepseek_api_key` | 在 [DeepSeek 平台](https://platform.deepseek.com/api_keys) 创建 |
| `deepseek_api_base` | 可选,默认为 `https://api.deepseek.com/v1`,可修改为第三方代理地址 | | `deepseek_api_base` | 可选,默认为 `https://api.deepseek.com/v1`,可修改为第三方代理地址 |
## 模型选择
| 模型 | 适用场景 |
| --- | --- |
| `deepseek-v4-flash` | 默认推荐,速度快、成本低 |
| `deepseek-v4-pro` | 更智能、复杂任务效果更强 |
## 思考模式
V4 系列(`deepseek-v4-flash` / `deepseek-v4-pro`)支持显式的"思考模式":模型在输出最终回答前,先输出一段思维链(`reasoning_content`),从而提升答案质量。
### 开关
通过全局配置 `enable_thinking` 控制:
```json
{
"enable_thinking": true
}
```
- `true`所有渠道下模型都会先思考再作答。Web 控制台会展示思考过程IM 渠道(微信 / 企微 / 钉钉 / 飞书)虽不展示但同样获得更好答案。
- `false`:关闭思考,响应更快,首字延迟更低。
### 行为说明
- **采样参数**:思考模式下 `temperature`、`top_p`、`presence_penalty`、`frequency_penalty` 会被服务端忽略不会报错CowAgent 会自动跳过传入。
- **多轮工具调用**当历史中包含工具调用时DeepSeek 要求所有 assistant 消息必须回传 `reasoning_content`。CowAgent 会自动处理回传逻辑,跨轮次切换思考开关也不会出错。
<Tip>
默认使用 `deepseek-v4-flash`;复杂任务可使用 `deepseek-v4-pro`;需要深度思考可开启 `enable_thinking`。
</Tip>
方式二OpenAI 兼容方式接入: 方式二OpenAI 兼容方式接入:
```json ```json
{ {
"model": "deepseek-chat", "model": "deepseek-v4-flash",
"bot_type": "openai", "bot_type": "openai",
"open_ai_api_key": "YOUR_API_KEY", "open_ai_api_key": "YOUR_API_KEY",
"open_ai_api_base": "https://api.deepseek.com/v1" "open_ai_api_base": "https://api.deepseek.com/v1"
} }
``` ```

View File

@@ -6,7 +6,7 @@ description: CowAgent 支持的模型及推荐选择
CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在项目的 `models/` 目录下。 CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在项目的 `models/` 目录下。
<Note> <Note>
Agent 模式下推荐使用以下模型,可根据效果及成本综合选择:MiniMax-M2.7、glm-5.1、kimi-k2.6、qwen3.6-plus、claude-sonnet-4-6、gemini-3.1-pro-preview Agent 模式下推荐使用以下模型,可根据效果及成本综合选择:deepseek-v4-flash、MiniMax-M2.7、claude-sonnet-4-6、gemini-3.1-pro-preview、glm-5.1、qwen3.6-plus、kimi-k2.6
同时支持使用 [LinkAI](https://link-ai.tech) 平台接口,可灵活切换多种模型,并支持知识库、工作流、插件等 Agent 能力。 同时支持使用 [LinkAI](https://link-ai.tech) 平台接口,可灵活切换多种模型,并支持知识库、工作流、插件等 Agent 能力。
</Note> </Note>
@@ -23,21 +23,12 @@ CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在
## 支持的模型 ## 支持的模型
<CardGroup cols={2}> <CardGroup cols={2}>
<Card title="DeepSeek" href="/models/deepseek">
deepseek-v4-flash、deepseek-v4-pro 等
</Card>
<Card title="MiniMax" href="/models/minimax"> <Card title="MiniMax" href="/models/minimax">
MiniMax-M2.7 等系列模型 MiniMax-M2.7 等系列模型
</Card> </Card>
<Card title="智谱 GLM" href="/models/glm">
glm-5.1、glm-5-turbo、glm-5 等系列模型
</Card>
<Card title="通义千问 Qwen" href="/models/qwen">
qwen3.6-plus、qwen3-max 等
</Card>
<Card title="Kimi" href="/models/kimi">
kimi-k2.6、kimi-k2.5、kimi-k2 等
</Card>
<Card title="豆包 Doubao" href="/models/doubao">
doubao-seed 系列模型
</Card>
<Card title="Claude" href="/models/claude"> <Card title="Claude" href="/models/claude">
claude-sonnet-4-6 等 claude-sonnet-4-6 等
</Card> </Card>
@@ -47,8 +38,17 @@ CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在
<Card title="OpenAI" href="/models/openai"> <Card title="OpenAI" href="/models/openai">
gpt-5.4、gpt-4.1、o 系列等 gpt-5.4、gpt-4.1、o 系列等
</Card> </Card>
<Card title="DeepSeek" href="/models/deepseek"> <Card title="智谱 GLM" href="/models/glm">
deepseek-chat、deepseek-reasoner glm-5.1、glm-5-turbo、glm-5 等系列模型
</Card>
<Card title="通义千问 Qwen" href="/models/qwen">
qwen3.6-plus、qwen3-max 等
</Card>
<Card title="豆包 Doubao" href="/models/doubao">
doubao-seed 系列模型
</Card>
<Card title="Kimi" href="/models/kimi">
kimi-k2.6、kimi-k2.5、kimi-k2 等
</Card> </Card>
<Card title="LinkAI" href="/models/linkai"> <Card title="LinkAI" href="/models/linkai">
多模型统一接口 + 知识库 多模型统一接口 + 知识库

View File

@@ -3,7 +3,7 @@ title: LinkAI
description: 通过 LinkAI 平台统一接入多种模型 description: 通过 LinkAI 平台统一接入多种模型
--- ---
通过 [LinkAI](https://link-ai.tech) 平台可灵活切换 OpenAI、Claude、Gemini、DeepSeek、Qwen、Kimi 等多种模型,并支持知识库、工作流、插件等 Agent 能力。 通过 [LinkAI](https://link-ai.tech) 平台可灵活切换 OpenAI、Claude、Gemini、DeepSeek、MiniMax、Qwen、Kimi 等多种模型,并支持知识库、工作流、插件等 Agent 能力。
```json ```json
{ {

View File

@@ -2,9 +2,27 @@
""" """
DeepSeek Bot — fully OpenAI-compatible, uses its own API key / base config. 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-flash (V4 Flash, default; thinking mode + tool calls)
- deepseek-v4-pro (V4 Pro, stronger on complex tasks)
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 import time
from typing import Optional
import requests import requests
from models.bot import Bot from models.bot import Bot
@@ -25,9 +43,9 @@ class DeepSeekBot(Bot, OpenAICompatibleBot):
super().__init__() super().__init__()
self.sessions = SessionManager( self.sessions = SessionManager(
DeepSeekSession, DeepSeekSession,
model=conf().get("model") or const.DEEPSEEK_CHAT, model=conf().get("model") or const.DEEPSEEK_V4_FLASH,
) )
conf_model = conf().get("model") or const.DEEPSEEK_CHAT conf_model = conf().get("model") or const.DEEPSEEK_V4_FLASH
self.args = { self.args = {
"model": conf_model, "model": conf_model,
"temperature": conf().get("temperature", 0.7), "temperature": conf().get("temperature", 0.7),
@@ -56,13 +74,32 @@ class DeepSeekBot(Bot, OpenAICompatibleBot):
return { return {
"api_key": self.api_key, "api_key": self.api_key,
"api_base": self.api_base, "api_base": self.api_base,
"model": conf().get("model", const.DEEPSEEK_CHAT), "model": conf().get("model", const.DEEPSEEK_V4_FLASH),
"default_temperature": conf().get("temperature", 0.7), "default_temperature": conf().get("temperature", 0.7),
"default_top_p": conf().get("top_p", 1.0), "default_top_p": conf().get("top_p", 1.0),
"default_frequency_penalty": conf().get("frequency_penalty", 0.0), "default_frequency_penalty": conf().get("frequency_penalty", 0.0),
"default_presence_penalty": conf().get("presence_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) ---------- # ---------- simple chat (non-agent mode) ----------
def reply(self, query, context=None): def reply(self, query, context=None):
@@ -112,13 +149,16 @@ class DeepSeekBot(Bot, OpenAICompatibleBot):
def reply_text(self, session, args=None, retry_count: int = 0) -> dict: def reply_text(self, session, args=None, retry_count: int = 0) -> dict:
try: try:
headers = { headers = self._build_headers()
"Content-Type": "application/json", body = dict(args) if args else dict(self.args)
"Authorization": "Bearer " + self.api_key,
}
body = args.copy()
body["messages"] = session.messages 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( res = requests.post(
f"{self.api_base}/chat/completions", f"{self.api_base}/chat/completions",
headers=headers, headers=headers,
@@ -158,3 +198,483 @@ class DeepSeekBot(Bot, OpenAICompatibleBot):
if retry_count < 2: if retry_count < 2:
return self.reply_text(session, args, retry_count + 1) return self.reply_text(session, args, retry_count + 1)
return {"completion_tokens": 0, "content": "我现在有点累了,等会再来吧"} 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, once any assistant turn in the history triggered a tool
call, DeepSeek requires `reasoning_content` on **every subsequent
assistant message** (not just the tool-call one) until the next user
turn — and in fact the API enforces this for the whole history when
thinking mode is enabled. Missing `reasoning_content` on any
assistant message returns 400. We back-fill an empty string when the
trace was not captured (e.g. history recorded while thinking was
disabled, or upstream proxy stripped the field).
"""
if not messages:
return []
# Determine whether the history contains any tool-call assistant turn.
# If so, every assistant message must carry `reasoning_content`.
has_tool_call_history = False
for msg in messages:
if msg.get("role") != "assistant":
continue
if msg.get("tool_calls"):
has_tool_call_history = True
break
content = msg.get("content")
if isinstance(content, list) and any(
isinstance(b, dict) and b.get("type") == "tool_use" for b in content
):
has_tool_call_history = True
break
converted = []
for msg in messages:
role = msg.get("role")
content = msg.get("content")
# Pass-through path for non-list content (e.g. plain string).
# Back-fill `reasoning_content` on assistant messages whenever the
# history contains any tool-call turn.
if not isinstance(content, list):
if (
role == "assistant"
and isinstance(msg, dict)
and has_tool_call_history
and "reasoning_content" not in msg
):
patched = dict(msg)
patched["reasoning_content"] = ""
converted.append(patched)
else:
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 every assistant
# message once the history contains any tool-call turn (see
# outer comment). Use empty string as fallback when the trace
# was not captured — DeepSeek validates field presence, not
# value; non-thinking backends silently ignore it.
if reasoning_parts:
openai_msg["reasoning_content"] = "\n".join(reasoning_parts)
elif has_tool_call_history:
openai_msg["reasoning_content"] = ""
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_FLASH)
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)}

View File

@@ -3,7 +3,7 @@ from common.log import logger
class DeepSeekSession(Session): 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-flash"):
super().__init__(session_id, system_prompt) super().__init__(session_id, system_prompt)
self.model = model self.model = model
self.reset() self.reset()

View File

@@ -686,7 +686,75 @@ def _handle_linkai_stream_response(self, base_url, headers, body):
"status_code": 500 "status_code": 500
} }
def _linkai_convert_messages_to_openai_format(self, messages):
"""
Override the base OpenAI-compatible conversion to round-trip
``reasoning_content`` on assistant messages.
Internally, the agent layer keeps the model's reasoning trace as a
Claude-style ``thinking`` content block on the assistant message. The
base converter drops that block. For thinking-capable models proxied via
LinkAI (DeepSeek V4, Kimi K2 thinking, …), the upstream API requires
the trace to be echoed back as a top-level ``reasoning_content`` field
on every assistant turn that contained tool calls — otherwise the next
request returns 400. We re-emit it for every assistant turn (it's
silently ignored on plain text turns).
"""
openai_messages = OpenAICompatibleBot._convert_messages_to_openai_format(self, messages)
if not messages:
return openai_messages
# DeepSeek (proxied via LinkAI) requires `reasoning_content` on EVERY
# assistant message once the history contains any tool-call turn — not
# just the tool-call turn itself. Detect that condition first.
has_tool_call_history = False
for src in messages:
if src.get("role") != "assistant":
continue
if src.get("tool_calls"):
has_tool_call_history = True
break
content = src.get("content")
if isinstance(content, list) and any(
isinstance(b, dict) and b.get("type") == "tool_use" for b in content
):
has_tool_call_history = True
break
# Walk the original Claude messages to collect each assistant turn's
# reasoning text, then attach it to the matching converted entry.
dst_idx = 0
for src in messages:
if src.get("role") != "assistant":
continue
content = src.get("content")
reasoning_parts = []
if isinstance(content, list):
reasoning_parts = [
b.get("thinking", "") for b in content
if isinstance(b, dict) and b.get("type") == "thinking"
]
# Locate the corresponding assistant entry in the converted list.
while dst_idx < len(openai_messages) and openai_messages[dst_idx].get("role") != "assistant":
dst_idx += 1
if dst_idx >= len(openai_messages):
break
dst_msg = openai_messages[dst_idx]
if reasoning_parts:
dst_msg["reasoning_content"] = "\n".join(reasoning_parts)
elif has_tool_call_history:
# Fallback when the trace was lost (proxy stripped it, model
# switched mid-session, thinking toggled on after tool calls).
# DeepSeek-style backends validate field presence, not value;
# non-thinking backends silently ignore the empty string.
dst_msg["reasoning_content"] = ""
dst_idx += 1
return openai_messages
# Attach methods to LinkAIBot class # Attach methods to LinkAIBot class
LinkAIBot.call_with_tools = _linkai_call_with_tools LinkAIBot.call_with_tools = _linkai_call_with_tools
LinkAIBot._handle_linkai_sync_response = _handle_linkai_sync_response LinkAIBot._handle_linkai_sync_response = _handle_linkai_sync_response
LinkAIBot._handle_linkai_stream_response = _handle_linkai_stream_response LinkAIBot._handle_linkai_stream_response = _handle_linkai_stream_response
LinkAIBot._convert_messages_to_openai_format = _linkai_convert_messages_to_openai_format

50
run.sh
View File

@@ -309,26 +309,27 @@ select_model() {
echo -e "${CYAN}${BOLD}=========================================${NC}" echo -e "${CYAN}${BOLD}=========================================${NC}"
echo -e "${CYAN}${BOLD} Select AI Model${NC}" echo -e "${CYAN}${BOLD} Select AI Model${NC}"
echo -e "${CYAN}${BOLD}=========================================${NC}" echo -e "${CYAN}${BOLD}=========================================${NC}"
echo -e "${YELLOW}1) MiniMax (MiniMax-M2.7, MiniMax-M2.5, etc.)${NC}" echo -e "${YELLOW}1) DeepSeek (deepseek-v4-flash, deepseek-v4-pro, etc.)${NC}"
echo -e "${YELLOW}2) Zhipu AI (glm-5.1, glm-5-turbo, glm-5, etc.)${NC}" echo -e "${YELLOW}2) MiniMax (MiniMax-M2.7, MiniMax-M2.5, etc.)${NC}"
echo -e "${YELLOW}3) Kimi (kimi-k2.6, kimi-k2.5, kimi-k2, 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) Doubao (doubao-seed-2-0-code-preview-260215, etc.)${NC}" echo -e "${YELLOW}4) Gemini (gemini-3.1-flash-lite-preview, gemini-3.1-pro-preview, etc.)${NC}"
echo -e "${YELLOW}5) Qwen (qwen3.6-plus, qwen3.5-plus, qwen3-max, qwq-plus, etc.)${NC}" echo -e "${YELLOW}5) OpenAI GPT (gpt-5.4, gpt-5.2, gpt-4.1, etc.)${NC}"
echo -e "${YELLOW}6) Claude (claude-sonnet-4-6, claude-opus-4-7, claude-opus-4-6, etc.)${NC}" echo -e "${YELLOW}6) Zhipu AI (glm-5.1, glm-5-turbo, glm-5, etc.)${NC}"
echo -e "${YELLOW}7) Gemini (gemini-3.1-flash-lite-preview, gemini-3.1-pro-preview, etc.)${NC}" echo -e "${YELLOW}7) Qwen (qwen3.6-plus, qwen3.5-plus, qwen3-max, qwq-plus, etc.)${NC}"
echo -e "${YELLOW}8) OpenAI GPT (gpt-5.4, gpt-5.2, gpt-4.1, etc.)${NC}" echo -e "${YELLOW}8) Doubao (doubao-seed-2-0-code-preview-260215, etc.)${NC}"
echo -e "${YELLOW}9) LinkAI (access multiple models via one API)${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 "" echo ""
while true; do while true; do
read -p "Enter your choice [press Enter for default: 1 - MiniMax]: " model_choice read -p "Enter your choice [press Enter for default: 1 - DeepSeek]: " model_choice
model_choice=${model_choice:-1} model_choice=${model_choice:-1}
case "$model_choice" in case "$model_choice" in
1|2|3|4|5|6|7|8|9) 1|2|3|4|5|6|7|8|9|10)
break break
;; ;;
*) *)
echo -e "${RED}Invalid choice. Please enter 1-9.${NC}" echo -e "${RED}Invalid choice. Please enter 1-10.${NC}"
;; ;;
esac esac
done done
@@ -356,25 +357,26 @@ read_api_base() {
# Configure model # Configure model
configure_model() { configure_model() {
case "$model_choice" in case "$model_choice" in
1) read_model_config "MiniMax" "MiniMax-M2.7" "MINIMAX_KEY" ;; 1) read_model_config "DeepSeek" "deepseek-v4-flash" "DEEPSEEK_KEY" ;;
2) read_model_config "Zhipu AI" "glm-5.1" "ZHIPU_KEY" ;; 2) read_model_config "MiniMax" "MiniMax-M2.7" "MINIMAX_KEY" ;;
3) read_model_config "Kimi (Moonshot)" "kimi-k2.6" "MOONSHOT_KEY" ;; 3)
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)
read_model_config "Claude" "claude-sonnet-4-6" "CLAUDE_KEY" read_model_config "Claude" "claude-sonnet-4-6" "CLAUDE_KEY"
read_api_base "CLAUDE_BASE" "https://api.anthropic.com/v1" read_api_base "CLAUDE_BASE" "https://api.anthropic.com/v1"
;; ;;
7) 4)
read_model_config "Gemini" "gemini-3.1-pro-preview" "GEMINI_KEY" read_model_config "Gemini" "gemini-3.1-pro-preview" "GEMINI_KEY"
read_api_base "GEMINI_BASE" "https://generativelanguage.googleapis.com" read_api_base "GEMINI_BASE" "https://generativelanguage.googleapis.com"
;; ;;
8) 5)
read_model_config "OpenAI GPT" "gpt-5.4" "OPENAI_KEY" read_model_config "OpenAI GPT" "gpt-5.4" "OPENAI_KEY"
read_api_base "OPENAI_BASE" "https://api.openai.com/v1" read_api_base "OPENAI_BASE" "https://api.openai.com/v1"
;; ;;
9) 6) read_model_config "Zhipu AI" "glm-5.1" "ZHIPU_KEY" ;;
read_model_config "LinkAI" "MiniMax-M2.7" "LINKAI_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" "deepseek-v4-flash" "LINKAI_KEY"
USE_LINKAI="true" USE_LINKAI="true"
;; ;;
esac esac
@@ -511,6 +513,8 @@ create_config_file() {
ARK_KEY="${ARK_KEY:-}" \ ARK_KEY="${ARK_KEY:-}" \
DASHSCOPE_KEY="${DASHSCOPE_KEY:-}" \ DASHSCOPE_KEY="${DASHSCOPE_KEY:-}" \
MINIMAX_KEY="${MINIMAX_KEY:-}" \ MINIMAX_KEY="${MINIMAX_KEY:-}" \
DEEPSEEK_KEY="${DEEPSEEK_KEY:-}" \
DEEPSEEK_BASE="${DEEPSEEK_BASE:-https://api.deepseek.com/v1}" \
USE_LINKAI="${USE_LINKAI:-false}" \ USE_LINKAI="${USE_LINKAI:-false}" \
LINKAI_KEY="${LINKAI_KEY:-}" \ LINKAI_KEY="${LINKAI_KEY:-}" \
FEISHU_APP_ID="${FEISHU_APP_ID:-}" \ FEISHU_APP_ID="${FEISHU_APP_ID:-}" \
@@ -545,6 +549,8 @@ base = {
'ark_api_key': e('ARK_KEY', ''), 'ark_api_key': e('ARK_KEY', ''),
'dashscope_api_key': e('DASHSCOPE_KEY', ''), 'dashscope_api_key': e('DASHSCOPE_KEY', ''),
'minimax_api_key': e('MINIMAX_KEY', ''), 'minimax_api_key': e('MINIMAX_KEY', ''),
'deepseek_api_key': e('DEEPSEEK_KEY', ''),
'deepseek_api_base': e('DEEPSEEK_BASE'),
'voice_to_text': 'openai', 'voice_to_text': 'openai',
'text_to_voice': 'openai', 'text_to_voice': 'openai',
'voice_reply_voice': False, 'voice_reply_voice': False,

View File

@@ -169,36 +169,38 @@ function Install-Dependencies {
# ── model selection ────────────────────────────────────────────── # ── model selection ──────────────────────────────────────────────
$ModelChoices = @{ $ModelChoices = @{
"1" = @{ Provider = "MiniMax"; Default = "MiniMax-M2.7"; Key = "MINIMAX_KEY" } "1" = @{ Provider = "DeepSeek"; Default = "deepseek-v4-flash"; Key = "DEEPSEEK_KEY" }
"2" = @{ Provider = "Zhipu AI"; Default = "glm-5.1"; Key = "ZHIPU_KEY" } "2" = @{ Provider = "MiniMax"; Default = "MiniMax-M2.7"; Key = "MINIMAX_KEY" }
"3" = @{ Provider = "Kimi (Moonshot)"; Default = "kimi-k2.6"; Key = "MOONSHOT_KEY" } "3" = @{ Provider = "Zhipu AI"; Default = "glm-5.1"; Key = "ZHIPU_KEY" }
"4" = @{ Provider = "Doubao (Volcengine Ark)"; Default = "doubao-seed-2-0-code-preview-260215"; Key = "ARK_KEY" } "4" = @{ Provider = "Kimi (Moonshot)"; Default = "kimi-k2.6"; Key = "MOONSHOT_KEY" }
"5" = @{ Provider = "Qwen (DashScope)"; Default = "qwen3.6-plus"; Key = "DASHSCOPE_KEY" } "5" = @{ Provider = "Doubao (Volcengine Ark)"; Default = "doubao-seed-2-0-code-preview-260215"; Key = "ARK_KEY" }
"6" = @{ Provider = "Claude"; Default = "claude-sonnet-4-6"; Key = "CLAUDE_KEY"; Base = "https://api.anthropic.com/v1" } "6" = @{ Provider = "Qwen (DashScope)"; Default = "qwen3.6-plus"; Key = "DASHSCOPE_KEY" }
"7" = @{ Provider = "Gemini"; Default = "gemini-3.1-pro-preview"; Key = "GEMINI_KEY"; Base = "https://generativelanguage.googleapis.com" } "7" = @{ Provider = "Claude"; Default = "claude-sonnet-4-6"; Key = "CLAUDE_KEY"; Base = "https://api.anthropic.com/v1" }
"8" = @{ Provider = "OpenAI GPT"; Default = "gpt-5.4"; Key = "OPENAI_KEY"; Base = "https://api.openai.com/v1" } "8" = @{ Provider = "Gemini"; Default = "gemini-3.1-pro-preview"; Key = "GEMINI_KEY"; Base = "https://generativelanguage.googleapis.com" }
"9" = @{ Provider = "LinkAI"; Default = "MiniMax-M2.7"; Key = "LINKAI_KEY" } "9" = @{ Provider = "OpenAI GPT"; Default = "gpt-5.4"; Key = "OPENAI_KEY"; Base = "https://api.openai.com/v1" }
"10" = @{ Provider = "LinkAI"; Default = "deepseek-v4-flash"; Key = "LINKAI_KEY" }
} }
function Select-Model { function Select-Model {
Write-Info "=========================================" Write-Info "========================================="
Write-Info " Select AI Model" Write-Info " Select AI Model"
Write-Info "=========================================" Write-Info "========================================="
Write-Host "1) MiniMax (MiniMax-M2.7, MiniMax-M2.5, etc.)" Write-Host "1) DeepSeek (deepseek-v4-flash, deepseek-v4-pro, etc.)"
Write-Host "2) Zhipu AI (glm-5.1, glm-5-turbo, glm-5, etc.)" Write-Host "2) MiniMax (MiniMax-M2.7, MiniMax-M2.5, etc.)"
Write-Host "3) Kimi (kimi-k2.6, kimi-k2.5, kimi-k2, etc.)" Write-Host "3) Zhipu AI (glm-5.1, glm-5-turbo, glm-5, etc.)"
Write-Host "4) Doubao (doubao-seed-2-0-code-preview-260215, etc.)" Write-Host "4) Kimi (kimi-k2.6, kimi-k2.5, kimi-k2, etc.)"
Write-Host "5) Qwen (qwen3.6-plus, qwen3.5-plus, qwen3-max, qwq-plus, etc.)" Write-Host "5) Doubao (doubao-seed-2-0-code-preview-260215, etc.)"
Write-Host "6) Claude (claude-sonnet-4-6, claude-opus-4-6, etc.)" Write-Host "6) Qwen (qwen3.6-plus, qwen3.5-plus, qwen3-max, qwq-plus, etc.)"
Write-Host "7) Gemini (gemini-3.1-flash-lite-preview, gemini-3.1-pro-preview, etc.)" Write-Host "7) Claude (claude-sonnet-4-6, claude-opus-4-6, etc.)"
Write-Host "8) OpenAI GPT (gpt-5.4, gpt-5.2, gpt-4.1, etc.)" Write-Host "8) Gemini (gemini-3.1-flash-lite-preview, gemini-3.1-pro-preview, etc.)"
Write-Host "9) LinkAI (access multiple models via one API)" Write-Host "9) OpenAI GPT (gpt-5.4, gpt-5.2, gpt-4.1, etc.)"
Write-Host "10) LinkAI (access multiple models via one API)"
Write-Host "" Write-Host ""
do { do {
$choice = Read-Host "Enter your choice [default: 1 - MiniMax]" $choice = Read-Host "Enter your choice [default: 1 - DeepSeek]"
if (-not $choice) { $choice = "1" } if (-not $choice) { $choice = "1" }
} while ($choice -notmatch '^[1-9]$') } while ($choice -notmatch '^([1-9]|10)$')
$m = $ModelChoices[$choice] $m = $ModelChoices[$choice]
Write-Cow "Configuring $($m.Provider)..." Write-Cow "Configuring $($m.Provider)..."
@@ -208,7 +210,7 @@ function Select-Model {
if (-not $model) { $model = $m.Default } if (-not $model) { $model = $m.Default }
$script:ModelName = $model $script:ModelName = $model
$script:KeyName = $m.Key $script:KeyName = $m.Key
$script:UseLinkai = ($choice -eq "9") $script:UseLinkai = ($choice -eq "10")
if ($m.Base) { if ($m.Base) {
$base = Read-Host "Enter API Base URL [default: $($m.Base)]" $base = Read-Host "Enter API Base URL [default: $($m.Base)]"
@@ -302,6 +304,8 @@ function New-ConfigFile {
ark_api_key = "" ark_api_key = ""
dashscope_api_key = "" dashscope_api_key = ""
minimax_api_key = "" minimax_api_key = ""
deepseek_api_key = ""
deepseek_api_base = "https://api.deepseek.com/v1"
voice_to_text = "openai" voice_to_text = "openai"
text_to_voice = "openai" text_to_voice = "openai"
voice_reply_voice = $false voice_reply_voice = $false
@@ -326,6 +330,7 @@ function New-ConfigFile {
ARK_KEY = "ark_api_key" ARK_KEY = "ark_api_key"
DASHSCOPE_KEY = "dashscope_api_key" DASHSCOPE_KEY = "dashscope_api_key"
MINIMAX_KEY = "minimax_api_key" MINIMAX_KEY = "minimax_api_key"
DEEPSEEK_KEY = "deepseek_api_key"
LINKAI_KEY = "linkai_api_key" LINKAI_KEY = "linkai_api_key"
} }
if ($keyMap.ContainsKey($KeyName)) { if ($keyMap.ContainsKey($KeyName)) {
@@ -334,9 +339,9 @@ function New-ConfigFile {
# Set API base if provided # Set API base if provided
$baseMap = @{ $baseMap = @{
"6" = "claude_api_base" "7" = "claude_api_base"
"7" = "gemini_api_base" "8" = "gemini_api_base"
"8" = "open_ai_api_base" "9" = "open_ai_api_base"
} }
if ($ApiBase -and $baseMap.ContainsKey($ModelChoice)) { if ($ApiBase -and $baseMap.ContainsKey($ModelChoice)) {
$config[$baseMap[$ModelChoice]] = $ApiBase $config[$baseMap[$ModelChoice]] = $ApiBase

View File

@@ -45,7 +45,7 @@ python <base_dir>/scripts/generate.py '<json_args>'
| `size` | string | no | auto | `512` / `1K` / `2K` / `3K` / `4K`, or pixel value (`1024x1024`) | | `size` | string | no | auto | `512` / `1K` / `2K` / `3K` / `4K`, or pixel value (`1024x1024`) |
| `aspect_ratio` | string | no | null | `1:1` / `3:2` / `2:3` / `16:9` / `9:16` / `21:9` (some backends also support extreme ratios like `1:4` / `8:1`) | | `aspect_ratio` | string | no | null | `1:1` / `3:2` / `2:3` / `16:9` / `9:16` / `21:9` (some backends also support extreme ratios like `1:4` / `8:1`) |
**Higher `quality` and larger `size` cost more and run slower.** Default to omitting both (`auto`) so the model picks a balanced setting. Only raise them when the user explicitly asks for high quality / a poster / print-ready output. For quick previews or chat scenarios prefer `quality=low` + `size=1K`. **Higher `quality` and larger `size` cost more and run slower.** In normal cases, when the user does not explicitly specify, `low` or `medium` is sufficient. Only use `high` when the user asks for it.
### Example — generate ### Example — generate

View File

@@ -1095,7 +1095,9 @@ def main():
sys.exit(1) sys.exit(1)
try: try:
args = json.loads(sys.argv[1]) raw = sys.argv[1]
raw = raw.replace('\u201c', '"').replace('\u201d', '"').replace('\u2018', "'").replace('\u2019', "'")
args = json.loads(raw)
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
print(json.dumps({"error": f"Invalid JSON: {e}"})) print(json.dumps({"error": f"Invalid JSON: {e}"}))
sys.exit(1) sys.exit(1)