Compare commits

...

8 Commits

40 changed files with 1205 additions and 391 deletions

270
README.md
View File

@@ -29,7 +29,7 @@
-**工具系统:** 内置文件读写、终端执行、浏览器操作、定时任务等工具Agent 自主调用以完成复杂任务
-**CLI系统** 提供终端命令和对话命令,支持进程管理、技能安装、配置修改等操作
-**多模态消息:** 支持对文本、图片、语音、文件等多类型消息进行解析、处理、生成、发送等操作
-**多模型支持:** 支持 OpenAI, Claude, Gemini, DeepSeek, MiniMax、GLM、Qwen、Kimi、Doubao 等国内外主流模型厂商
-**多模型支持:** 支持 DeepSeek、MiniMax、ClaudeGemini、OpenAI、GLM、Qwen、Doubao、Kimi 等国内外主流模型厂商
-**多通道接入:** 支持运行在本地计算机或服务器可集成到微信、飞书、钉钉、企业微信、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)。
@@ -182,7 +182,9 @@ cow install-browser
# config.json 文件内容示例
{
"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
"zhipu_ai_api_key": "", # 智谱 GLM 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 地址,修改可接入三方代理平台
"gemini_api_key": "", # Gemini API Key
"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_base": "https://api.openai.com/v1", # OpenAI API 地址
"linkai_api_key": "", # LinkAI API Key
@@ -208,7 +208,7 @@ cow install-browser
"agent_max_context_tokens": 50000, # Agent 模式下最大上下文 tokens超出将自动智能压缩处理
"agent_max_context_turns": 20, # Agent 模式下最大上下文记忆轮次,一问一答为一轮,超出后智能压缩处理
"agent_max_steps": 20, # Agent 模式下单次任务的最大决策步数,超出后将停止继续调用工具
"enable_thinking": false # 是否启用深度思考,开启后 Web 端展示模型推理过程,关闭后可加速响应
"enable_thinking": false # 是否启用深度思考模式
}
```
@@ -226,7 +226,7 @@ cow install-browser
<details>
<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 模式下该配置不生效,由工作空间中的文件内容构成。
+ `subscribe_msg`:订阅消息,公众号和企业微信 channel 中请填写,当被订阅时会自动回复, 可使用特殊占位符。目前支持的占位符有{trigger_prefix},在程序中它会自动替换成 bot 的触发词。
</details>
@@ -314,44 +314,36 @@ sudo docker logs -f chatgpt-on-wechat
推荐通过 Web 控制台在线管理模型配置,无需手动编辑文件,详见 [模型文档](https://docs.cowagent.ai/models)。以下是手动修改 `config.json` 配置模型的说明:
<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. 填写配置
```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
{
"model": "gpt-5.4-mini",
"use_linkai": true,
"linkai_api_key": "YOUR API KEY"
"model": "deepseek-v4-flash",
"deepseek_api_key": "sk-xxxxxxxxxxx"
}
```
- `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>
@@ -383,6 +375,56 @@ sudo docker logs -f chatgpt-on-wechat
- `open_ai_api_key`: MiniMax 平台的 API-KEY
</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>
<summary>智谱AI (GLM)</summary>
@@ -441,35 +483,6 @@ sudo docker logs -f chatgpt-on-wechat
- `open_ai_api_key`: 通义千问的 API-KEY
</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>
<summary>豆包 (Doubao)</summary>
@@ -489,67 +502,74 @@ sudo docker logs -f chatgpt-on-wechat
</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. 填写配置
```json
{
"model": "claude-sonnet-4-6",
"claude_api_key": "YOUR_API_KEY"
"model": "gpt-5.4-mini",
"use_linkai": true,
"linkai_api_key": "YOUR API KEY"
}
```
- `model`: 参考 [官方模型ID](https://docs.anthropic.com/en/docs/about-claude/models/overview#model-aliases) ,支持 `claude-sonnet-4-6、claude-opus-4-7、claude-opus-4-6、claude-sonnet-4-5、claude-sonnet-4-0、claude-opus-4-0、claude-3-5-sonnet-latest`
+ `use_linkai`: 是否使用 LinkAI 接口,默认关闭,设置为 true 后可对接 LinkAI 平台的模型,并使用知识库、工作流、数据库、插件等丰富的 Agent 技能
+ `linkai_api_key`: LinkAI 平台的 API Key可在 [控制台](https://link-ai.tech/console/interface) 中创建
+ `model`: [模型列表](https://link-ai.tech/console/models)中的全部模型均可使用
</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>
<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) ,因模型而已
</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>Coding Plan</summary>

View File

@@ -110,6 +110,23 @@ class AgentStreamExecutor:
logger.error(f"Event callback error: {e}")
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
channel_type = getattr(self.model, 'channel_type', '') or ''
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
(e.g., MiniMax).
- When thinking is enabled: remove the tags but keep the content inside.
- When thinking is disabled: remove both the tags and the content entirely.
- When inline thinking rendering is allowed (Web + thinking enabled):
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:
return text
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)
else:

View File

@@ -167,13 +167,15 @@ class AgentLLMModel(LLMModel):
if 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
global_thinking = conf().get("enable_thinking", False)
if not global_thinking:
kwargs['thinking'] = {"type": "disabled"}
else:
kwargs['thinking'] = {"type": "enabled"} if channel_type == "web" else {"type": "disabled"}
kwargs['thinking'] = (
{"type": "enabled"} if conf().get("enable_thinking", False)
else {"type": "disabled"}
)
response = self.bot.call_with_tools(**kwargs)
return self._format_response(response)
@@ -220,13 +222,15 @@ class AgentLLMModel(LLMModel):
if 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
global_thinking = conf().get("enable_thinking", False)
if not global_thinking:
kwargs['thinking'] = {"type": "disabled"}
else:
kwargs['thinking'] = {"type": "enabled"} if channel_type == "web" else {"type": "disabled"}
kwargs['thinking'] = (
{"type": "enabled"} if conf().get("enable_thinking", False)
else {"type": "disabled"}
)
stream = self.bot.call_with_tools(**kwargs)

View File

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

View File

@@ -38,7 +38,7 @@ const I18N = {
config_max_tokens: '最大上下文 Token', config_max_tokens_hint: '对话中 Agent 能输入的最大 Token 长度,超过后会智能压缩处理',
config_max_turns: '最大记忆轮次', config_max_turns_hint: '一问一答为一轮,超过后会智能压缩处理',
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_provider: '模型厂商', config_model_name: '模型',
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_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_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_provider: 'Provider', config_model_name: 'Model',
config_custom_model_hint: 'Enter custom model name',
@@ -204,6 +204,7 @@ function applyI18n() {
document.querySelectorAll('[data-tip-key]').forEach(el => {
el.setAttribute('data-tooltip', t(el.dataset.tipKey));
});
installCfgTipPortal();
const langLabel = document.getElementById('lang-label');
if (langLabel) langLabel.textContent = currentLang === 'zh' ? '中文' : 'EN';
}
@@ -215,6 +216,54 @@ function toggleLanguage() {
_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
// =====================================================================
@@ -2407,12 +2456,17 @@ function onProviderChange(pid) {
}
// API Base
const apiBaseInput = document.getElementById('cfg-api-base');
if (p.api_base_key) {
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 {
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 : '');

View File

@@ -770,58 +770,50 @@ class ChatHandler:
class ConfigHandler:
_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.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.GEMINI_31_FLASH_LITE_PRE, const.GEMINI_31_PRO_PRE, const.GEMINI_3_FLASH_PRE,
const.GPT_54, const.GPT_54_MINI, const.GPT_54_NANO, const.GPT_5, const.GPT_41, const.GPT_4o,
const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER,
const.GLM_5_1, const.GLM_5_TURBO, const.GLM_5, const.GLM_4_7,
const.QWEN36_PLUS, const.QWEN35_PLUS, const.QWEN3_MAX,
const.DOUBAO_SEED_2_PRO, const.DOUBAO_SEED_2_CODE,
const.KIMI_K2_6, const.KIMI_K2_5, const.KIMI_K2,
]
# 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([
("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", {
"label": "MiniMax",
"api_key_field": "minimax_api_key",
"api_base_key": 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],
}),
("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", {
"label": "Claude",
"api_key_field": "claude_api_key",
"api_base_key": "claude_api_base",
"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],
}),
("gemini", {
@@ -829,6 +821,7 @@ class ConfigHandler:
"api_key_field": "gemini_api_key",
"api_base_key": "gemini_api_base",
"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],
}),
("openai", {
@@ -836,20 +829,47 @@ class ConfigHandler:
"api_key_field": "open_ai_api_key",
"api_base_key": "open_ai_api_base",
"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],
}),
("deepseek", {
"label": "DeepSeek",
"api_key_field": "deepseek_api_key",
"api_base_key": "deepseek_api_base",
"api_base_default": "https://api.deepseek.com/v1",
"models": [const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER],
("zhipu", {
"label": "智谱AI",
"api_key_field": "zhipu_ai_api_key",
"api_base_key": "zhipu_ai_api_base",
"api_base_default": "https://open.bigmodel.cn/api/paas/v4",
"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", {
"label": "ModelScope",
"api_key_field": "modelscope_api_key",
"api_base_key": None,
"api_base_default": None,
"api_base_placeholder": "",
"models": [const.QWEN3_5_27B, const.QWEN3_235B_A22B_INSTRUCT_2507],
}),
("linkai", {
@@ -857,6 +877,7 @@ class ConfigHandler:
"api_key_field": "linkai_api_key",
"api_base_key": None,
"api_base_default": None,
"api_base_placeholder": "",
"models": _RECOMMENDED_MODELS,
}),
("custom", {
@@ -864,6 +885,7 @@ class ConfigHandler:
"api_key_field": "custom_api_key",
"api_base_key": "custom_api_base",
"api_base_default": "",
"api_base_placeholder": _PLACEHOLDER_V1,
"models": [],
}),
])
@@ -912,6 +934,7 @@ class ConfigHandler:
"models": p["models"],
"api_base_key": p["api_base_key"],
"api_base_default": p["api_base_default"],
"api_base_placeholder": p.get("api_base_placeholder", ""),
"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_CHAT = "deepseek-chat" # DeepSeek-V3对话模型
DEEPSEEK_REASONER = "deepseek-reasoner" # DeepSeek-R1模型
DEEPSEEK_V4_FLASH = "deepseek-v4-flash" # DeepSeek V4 Flash - 默认推荐 (思考模式 + 工具调用)
DEEPSEEK_V4_PRO = "deepseek-v4-pro" # DeepSeek V4 Pro - 复杂任务更强 (思考模式 + 工具调用)
# Qwen (通义千问 - 阿里云 DashScope)
QWEN_TURBO = "qwen-turbo"
@@ -154,15 +156,21 @@ MODELSCOPE_MODEL_LIST = ["deepseek-ai/DeepSeek-R1-0528", "deepseek-ai/DeepSeek-R
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
CLAUDE3, CLAUDE_4_6_SONNET, CLAUDE_4_7_OPUS, CLAUDE_4_6_OPUS, CLAUDE_4_OPUS, CLAUDE_4_5_SONNET, CLAUDE_4_SONNET, CLAUDE_3_OPUS, CLAUDE_3_OPUS_0229,
CLAUDE_35_SONNET, CLAUDE_35_SONNET_1022, CLAUDE_35_SONNET_0620, CLAUDE_3_SONNET, CLAUDE_3_HAIKU,
CLAUDE3, CLAUDE_4_6_SONNET, CLAUDE_4_7_OPUS, CLAUDE_4_6_OPUS, CLAUDE_4_OPUS, CLAUDE_4_5_SONNET, CLAUDE_4_SONNET, CLAUDE_3_OPUS, CLAUDE_3_OPUS_0229,
CLAUDE_35_SONNET, CLAUDE_35_SONNET_1022, CLAUDE_35_SONNET_0620, CLAUDE_3_SONNET, CLAUDE_3_HAIKU,
"claude", "claude-3-haiku", "claude-3-sonnet", "claude-3-opus", "claude-3.5-sonnet",
# Gemini
GEMINI_31_FLASH_LITE_PRE, GEMINI_31_PRO_PRE, GEMINI_3_PRO_PRE, GEMINI_3_FLASH_PRE, GEMINI_25_PRO_PRE, GEMINI_25_FLASH_PRE,
GEMINI_20_FLASH, GEMINI_20_flash_exp, GEMINI_15_PRO, GEMINI_15_flash, GEMINI_PRO, GEMINI,
# OpenAI
GPT35, GPT35_0125, GPT35_1106, "gpt-3.5-turbo-16k",
GPT4, GPT4_06_13, GPT4_32k, GPT4_32k_06_13,
@@ -172,31 +180,29 @@ MODEL_LIST = [
GPT_5, GPT_5_MINI, GPT_5_NANO,
GPT_54, GPT_54_MINI, GPT_54_NANO,
O1, O1_MINI,
# DeepSeek
DEEPSEEK_CHAT, DEEPSEEK_REASONER,
# Qwen
QWEN36_PLUS, QWEN35_PLUS, QWEN3_MAX, QWEN_MAX, QWEN_PLUS, QWEN_TURBO, QWEN_LONG,
# MiniMax
MiniMax, MINIMAX_M2_7, MINIMAX_M2_7_HIGHSPEED, MINIMAX_M2_5, MINIMAX_M2_1, MINIMAX_M2_1_LIGHTNING, MINIMAX_M2, MINIMAX_ABAB6_5,
# GLM
# GLM (智谱AI)
ZHIPU_AI, GLM_5_1, GLM_5_TURBO, GLM_5, GLM_4, GLM_4_PLUS, GLM_4_flash, GLM_4_LONG, GLM_4_ALLTOOLS,
GLM_4_0520, GLM_4_AIR, GLM_4_AIRX, GLM_4_7,
# Kimi
# Qwen (通义千问)
QWEN36_PLUS, QWEN35_PLUS, QWEN3_MAX, QWEN_MAX, QWEN_PLUS, QWEN_TURBO, QWEN_LONG,
# Doubao (豆包)
DOUBAO, DOUBAO_SEED_2_CODE, DOUBAO_SEED_2_PRO, DOUBAO_SEED_2_LITE, DOUBAO_SEED_2_MINI,
# Kimi (Moonshot)
MOONSHOT, "moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k",
KIMI_K2_6, KIMI_K2_5, KIMI_K2,
# Doubao
DOUBAO, DOUBAO_SEED_2_CODE, DOUBAO_SEED_2_PRO, DOUBAO_SEED_2_LITE, DOUBAO_SEED_2_MINI,
# ModelScope
MODELSCOPE,
# LinkAI
LINKAI_35, LINKAI_4_TURBO, LINKAI_4o,
# 其他模型
WEN_XIN, WEN_XIN_4, XUNFEI,
LINKAI_35, LINKAI_4_TURBO, LINKAI_4o,
MODELSCOPE
]
MODEL_LIST = MODEL_LIST + GITEE_AI_MODEL_LIST + MODELSCOPE_MODEL_LIST

View File

@@ -1,6 +1,8 @@
{
"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": "",
"zhipu_ai_api_key": "",
"ark_api_key": "",
@@ -31,5 +33,6 @@
"agent_max_context_tokens": 50000,
"agent_max_context_turns": 20,
"agent_max_steps": 20,
"enable_thinking": false,
"knowledge": true
}

View File

@@ -196,6 +196,8 @@ available_setting = {
"minimax_api_key": "",
"Minimax_group_id": "",
"Minimax_base_url": "",
"deepseek_api_key": "",
"deepseek_api_base": "https://api.deepseek.com/v1",
"web_port": 9899,
"web_password": "", # Web console password; empty means no authentication required
"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_turns": 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, # 是否开启知识库功能
# Per-skill runtime config. Nested keys are flattened to env vars at startup
# using the rule: skill[<name>][<key>] -> SKILL_<NAME>_<KEY>
@@ -382,6 +384,8 @@ def load_config():
"gemini_api_base": "GEMINI_API_BASE",
"minimax_api_key": "MINIMAX_API_KEY",
"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_base": "ZHIPU_AI_API_BASE",
"moonshot_api_key": "MOONSHOT_API_KEY",

View File

@@ -9,7 +9,9 @@ services:
- "9899:9899"
environment:
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: ''
ZHIPU_AI_API_KEY: ''
ARK_API_KEY: ''

View File

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

View File

@@ -58,18 +58,18 @@ Session: 12 messages | 8 skills loaded
**修改配置项:**
```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_turns` | 最大上下文记忆轮次 | `30` |
| `agent_max_steps` | 单次任务最大决策步数 | `15` |
| `enable_thinking` | 是否启用深度思考 | `true` / `false` |
| `enable_thinking` | 是否启用深度思考模式 | `true` / `false` |
<Note>
修改 `model` 时,系统会自动匹配对应的模型调用方式。配置会写入 `config.json` 并持久保存。

View File

@@ -72,15 +72,15 @@
"group": "模型配置",
"pages": [
"models/index",
"models/deepseek",
"models/minimax",
"models/glm",
"models/qwen",
"models/kimi",
"models/doubao",
"models/claude",
"models/gemini",
"models/openai",
"models/deepseek",
"models/glm",
"models/qwen",
"models/doubao",
"models/kimi",
"models/linkai",
"models/coding-plan",
"models/custom"
@@ -257,15 +257,15 @@
"group": "Model Configuration",
"pages": [
"en/models/index",
"en/models/deepseek",
"en/models/minimax",
"en/models/glm",
"en/models/qwen",
"en/models/kimi",
"en/models/doubao",
"en/models/claude",
"en/models/gemini",
"en/models/openai",
"en/models/deepseek",
"en/models/glm",
"en/models/qwen",
"en/models/doubao",
"en/models/kimi",
"en/models/linkai",
"en/models/coding-plan",
"en/models/custom"
@@ -441,15 +441,15 @@
"group": "モデル設定",
"pages": [
"ja/models/index",
"ja/models/deepseek",
"ja/models/minimax",
"ja/models/glm",
"ja/models/qwen",
"ja/models/kimi",
"ja/models/doubao",
"ja/models/claude",
"ja/models/gemini",
"ja/models/openai",
"ja/models/deepseek",
"ja/models/glm",
"ja/models/qwen",
"ja/models/doubao",
"ja/models/kimi",
"ja/models/linkai",
"ja/models/coding-plan",
"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.
-**CLI System**: Provides terminal commands and in-chat commands for process management, skill installation, configuration, and more.
-**Multimodal Messages**: Supports parsing, processing, generating, and sending text, images, voice, files, and other message types.
-**Multiple Model Support**: Supports OpenAI, Claude, Gemini, DeepSeek, MiniMax, GLM, Qwen, Kimi, Doubao, and other mainstream model providers.
-**Multiple Model Support**: Supports 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.
## Disclaimer
@@ -164,15 +164,15 @@ Supports mainstream model providers. Recommended models for Agent mode:
| Provider | Recommended Model |
| --- | --- |
| DeepSeek | `deepseek-v4-flash` |
| 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` |
| Gemini | `gemini-3.1-pro-preview` |
| OpenAI | `gpt-5.4` |
| DeepSeek | `deepseek-chat` |
| GLM | `glm-5.1` |
| Qwen | `qwen3.6-plus` |
| Doubao | `doubao-seed-2-0-code-preview-260215` |
| Kimi | `kimi-k2.6` |
For detailed configuration of each model, see the [Models documentation](https://docs.cowagent.ai/en/models/index).

View File

@@ -44,18 +44,18 @@ View or modify runtime configuration. Changes take effect immediately without re
**Modify a config item:**
```text
/config model deepseek-chat
/config model deepseek-v4-flash
```
**Configurable items:**
| 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_turns` | Max context memory turns | `30` |
| `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>
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
{
"channel_type": "web",
"model": "MiniMax-M2.5",
"model": "deepseek-v4-flash",
"deepseek_api_key": "",
"agent": true,
"agent_workspace": "~/cow",
"agent_max_context_tokens": 40000,
@@ -133,7 +134,7 @@ sudo docker logs -f chatgpt-on-wechat
| Parameter | Description | Default |
| --- | --- | --- |
| `channel_type` | Channel type | `web` |
| `model` | Model name | `MiniMax-M2.5` |
| `model` | Model name | `deepseek-v4-flash` |
| `agent` | Enable Agent mode | `true` |
| `agent_workspace` | Agent workspace path | `~/cow` |
| `agent_max_context_tokens` | Max context tokens | `40000` |

View File

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

View File

@@ -7,26 +7,57 @@ Option 1: Native integration (recommended):
```json
{
"model": "deepseek-chat",
"model": "deepseek-v4-flash",
"deepseek_api_key": "YOUR_API_KEY"
}
```
| 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_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:
```json
{
"model": "deepseek-chat",
"model": "deepseek-v4-flash",
"bot_type": "openai",
"open_ai_api_key": "YOUR_API_KEY",
"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.
<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>
## Configuration
@@ -18,21 +18,12 @@ You can also use the [LinkAI](https://link-ai.tech) platform interface to flexib
## Supported Models
<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">
MiniMax-M2.7 and other series models
</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">
claude-sonnet-4-6 and more
</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">
gpt-5.4, gpt-4.1, o-series and more
</Card>
<Card title="DeepSeek" href="/en/models/deepseek">
deepseek-chat, deepseek-reasoner
<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="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 title="LinkAI" href="/en/models/linkai">
Unified multi-model interface + knowledge base

View File

@@ -3,7 +3,7 @@ title: LinkAI
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
{

View File

@@ -139,7 +139,8 @@ sudo docker logs -f chatgpt-on-wechat
```json
{
"channel_type": "web",
"model": "MiniMax-M2.7",
"model": "deepseek-v4-flash",
"deepseek_api_key": "",
"agent": true,
"agent_workspace": "~/cow",
"agent_max_context_tokens": 40000,
@@ -152,8 +153,9 @@ sudo docker logs -f chatgpt-on-wechat
```yaml
environment:
CHANNEL_TYPE: 'web'
MODEL: 'MiniMax-M2.7'
MINIMAX_API_KEY: 'your-api-key'
MODEL: 'deepseek-v4-flash'
DEEPSEEK_API_KEY: 'your-api-key'
DEEPSEEK_API_BASE: 'https://api.deepseek.com/v1'
AGENT: 'True'
AGENT_MAX_CONTEXT_TOKENS: 40000
AGENT_MAX_CONTEXT_TURNS: 30
@@ -165,7 +167,7 @@ sudo docker logs -f chatgpt-on-wechat
| 参数 | 环境变量 | 说明 | 默认值 |
| --- | --- | --- | --- |
| `channel_type` | `CHANNEL_TYPE` | 接入渠道类型 | `web` |
| `model` | `MODEL` | 模型名称 | `MiniMax-M2.5` |
| `model` | `MODEL` | 模型名称 | `deepseek-v4-flash` |
| `agent` | `AGENT` | 是否启用 Agent 模式 | `true` |
| `agent_workspace` | - | Agent 工作空间路径 | `~/cow` |
| `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_turns` | 最大上下文记忆轮次 | `20` |
| `agent_max_steps` | 单次任务最大决策步数 | `20` |
| `enable_thinking` | 是否启用深度思考,开启后 Web 端展示推理过程,关闭可加速响应 | `false` |
| `enable_thinking` | 是否启用深度思考模式 | `false` |
| `knowledge` | 是否启用个人知识库 | `true` |

View File

@@ -28,7 +28,7 @@
-**ツールシステム**: ファイル読み書き、ターミナル実行、ブラウザ操作、スケジュールタスク、メッセージ送信などの組み込みツールを提供。Agentが自律的に呼び出して複雑なタスクを完了します。
-**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アプリケーションに統合可能です。
## 免責事項
@@ -164,15 +164,15 @@ sudo docker logs -f chatgpt-on-wechat
| プロバイダー | 推奨モデル |
| --- | --- |
| DeepSeek | `deepseek-v4-flash` |
| 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` |
| Gemini | `gemini-3.1-pro-preview` |
| OpenAI | `gpt-5.4` |
| DeepSeek | `deepseek-chat` |
| GLM | `glm-5.1` |
| Qwen | `qwen3.6-plus` |
| Doubao | `doubao-seed-2-0-code-preview-260215` |
| Kimi | `kimi-k2.6` |
各モデルの詳細設定については、[モデルドキュメント](https://docs.cowagent.ai/en/models/index)を参照してください。

View File

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

View File

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

View File

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

View File

@@ -7,22 +7,55 @@ description: DeepSeekモデルの設定
```json
{
"model": "deepseek-chat",
"model": "deepseek-v4-flash",
"deepseek_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `deepseek-chat`DeepSeek-V3.2、非思考モード)、`deepseek-reasoner`DeepSeek-R1、思考モード |
| `deepseek_api_key` | [DeepSeek Platform](https://platform.deepseek.com/api_keys)で作成 |
| `model` | `deepseek-v4-flash`(デフォルト)、`deepseek-v4-pro` をサポート |
| `deepseek_api_key` | [DeepSeek Platform](https://platform.deepseek.com/api_keys) で作成 |
| `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互換方式
```json
{
"model": "deepseek-chat",
"model": "deepseek-v4-flash",
"bot_type": "openai",
"open_ai_api_key": "YOUR_API_KEY",
"open_ai_api_base": "https://api.deepseek.com/v1"

View File

@@ -6,7 +6,7 @@ description: CowAgentがサポートするモデルとおすすめの選択肢
CowAgentは国内外の主要なLLMをサポートしています。モデルインターフェースはプロジェクトの`models/`ディレクトリに実装されています。
<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>
## 設定
@@ -18,21 +18,12 @@ CowAgentは国内外の主要なLLMをサポートしています。モデルイ
## サポートモデル
<CardGroup cols={2}>
<Card title="DeepSeek" href="/ja/models/deepseek">
deepseek-v4-flash、deepseek-v4-pro など
</Card>
<Card title="MiniMax" href="/ja/models/minimax">
MiniMax-M2.7およびその他のシリーズモデル
</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">
claude-sonnet-4-6など
</Card>
@@ -42,8 +33,17 @@ CowAgentは国内外の主要なLLMをサポートしています。モデルイ
<Card title="OpenAI" href="/ja/models/openai">
gpt-5.4、gpt-4.1、oシリーズなど
</Card>
<Card title="DeepSeek" href="/ja/models/deepseek">
deepseek-chat、deepseek-reasoner
<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="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 title="LinkAI" href="/ja/models/linkai">
統合マルチモデルインターフェース + ナレッジベース

View File

@@ -3,7 +3,7 @@ title: 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
{

View File

@@ -7,25 +7,57 @@ description: DeepSeek 模型配置
```json
{
"model": "deepseek-chat",
"model": "deepseek-v4-flash",
"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_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 兼容方式接入:
```json
{
"model": "deepseek-chat",
"model": "deepseek-v4-flash",
"bot_type": "openai",
"open_ai_api_key": "YOUR_API_KEY",
"open_ai_api_base": "https://api.deepseek.com/v1"
}
```

View File

@@ -6,7 +6,7 @@ description: CowAgent 支持的模型及推荐选择
CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在项目的 `models/` 目录下。
<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 能力。
</Note>
@@ -23,21 +23,12 @@ CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在
## 支持的模型
<CardGroup cols={2}>
<Card title="DeepSeek" href="/models/deepseek">
deepseek-v4-flash、deepseek-v4-pro 等
</Card>
<Card title="MiniMax" href="/models/minimax">
MiniMax-M2.7 等系列模型
</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">
claude-sonnet-4-6 等
</Card>
@@ -47,8 +38,17 @@ CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在
<Card title="OpenAI" href="/models/openai">
gpt-5.4、gpt-4.1、o 系列等
</Card>
<Card title="DeepSeek" href="/models/deepseek">
deepseek-chat、deepseek-reasoner
<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="豆包 Doubao" href="/models/doubao">
doubao-seed 系列模型
</Card>
<Card title="Kimi" href="/models/kimi">
kimi-k2.6、kimi-k2.5、kimi-k2 等
</Card>
<Card title="LinkAI" href="/models/linkai">
多模型统一接口 + 知识库

View File

@@ -3,7 +3,7 @@ title: 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
{

View File

@@ -2,9 +2,27 @@
"""
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
from typing import Optional
import requests
from models.bot import Bot
@@ -25,9 +43,9 @@ class DeepSeekBot(Bot, OpenAICompatibleBot):
super().__init__()
self.sessions = SessionManager(
DeepSeekSession,
model=conf().get("model") or const.DEEPSEEK_CHAT,
model=conf().get("model") or const.DEEPSEEK_V4_FLASH,
)
conf_model = conf().get("model") or const.DEEPSEEK_CHAT
conf_model = conf().get("model") or const.DEEPSEEK_V4_FLASH
self.args = {
"model": conf_model,
"temperature": conf().get("temperature", 0.7),
@@ -56,13 +74,32 @@ class DeepSeekBot(Bot, OpenAICompatibleBot):
return {
"api_key": self.api_key,
"api_base": self.api_base,
"model": conf().get("model", const.DEEPSEEK_CHAT),
"model": conf().get("model", const.DEEPSEEK_V4_FLASH),
"default_temperature": conf().get("temperature", 0.7),
"default_top_p": conf().get("top_p", 1.0),
"default_frequency_penalty": conf().get("frequency_penalty", 0.0),
"default_presence_penalty": conf().get("presence_penalty", 0.0),
}
@staticmethod
def _model_supports_thinking(model_name: str) -> bool:
"""V4 series models expose the explicit `thinking` switch."""
if not model_name:
return False
m = model_name.lower()
return m.startswith("deepseek-v4")
@staticmethod
def _is_reasoner_model(model_name: str) -> bool:
"""deepseek-reasoner (R1) always thinks internally; no toggle."""
return bool(model_name) and "reasoner" in model_name.lower()
def _build_headers(self) -> dict:
return {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}",
}
# ---------- simple chat (non-agent mode) ----------
def reply(self, query, context=None):
@@ -112,13 +149,16 @@ class DeepSeekBot(Bot, OpenAICompatibleBot):
def reply_text(self, session, args=None, retry_count: int = 0) -> dict:
try:
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer " + self.api_key,
}
body = args.copy()
headers = self._build_headers()
body = dict(args) if args else dict(self.args)
body["messages"] = session.messages
# Thinking mode ignores temperature/top_p/penalties — strip to avoid noise.
model_name = str(body.get("model", ""))
if self._model_supports_thinking(model_name) or self._is_reasoner_model(model_name):
for k in ("temperature", "top_p", "presence_penalty", "frequency_penalty"):
body.pop(k, None)
res = requests.post(
f"{self.api_base}/chat/completions",
headers=headers,
@@ -158,3 +198,483 @@ class DeepSeekBot(Bot, OpenAICompatibleBot):
if retry_count < 2:
return self.reply_text(session, args, retry_count + 1)
return {"completion_tokens": 0, "content": "我现在有点累了,等会再来吧"}
# ==================== Agent mode support ====================
def call_with_tools(self, messages, tools=None, stream: bool = False, **kwargs):
"""
Call DeepSeek API with tool support for agent integration.
Handles:
- Claude → OpenAI message/tool format conversion (with reasoning_content round-trip)
- System prompt injection
- Streaming SSE with tool_calls + reasoning_content delta
- Thinking mode toggle and reasoning_effort for V4 models
"""
try:
converted_messages = self._convert_messages_to_openai_format(messages)
system_prompt = kwargs.pop("system", None)
if system_prompt:
if not converted_messages or converted_messages[0].get("role") != "system":
converted_messages.insert(0, {"role": "system", "content": system_prompt})
else:
converted_messages[0] = {"role": "system", "content": system_prompt}
converted_tools = None
if tools:
converted_tools = self._convert_tools_to_openai_format(tools)
model = kwargs.pop("model", None) or self.args["model"]
max_tokens = kwargs.pop("max_tokens", None)
request_body = {
"model": model,
"messages": converted_messages,
"stream": stream,
}
if max_tokens is not None:
request_body["max_tokens"] = max_tokens
if converted_tools:
request_body["tools"] = converted_tools
request_body["tool_choice"] = kwargs.pop("tool_choice", "auto")
# Thinking mode (V4 only). Honour the toggle propagated by agent_bridge.
thinking_param = kwargs.pop("thinking", None)
reasoning_effort = kwargs.pop("reasoning_effort", None)
thinking_active = False
if self._model_supports_thinking(model):
# Default to enabled per DeepSeek docs unless caller explicitly disables.
thinking_param = thinking_param or {"type": "enabled"}
request_body["thinking"] = thinking_param
thinking_active = thinking_param.get("type") == "enabled"
if thinking_active:
# Default to "high"; allow caller override (e.g. "max" for heavy agent loops).
request_body["reasoning_effort"] = reasoning_effort or "high"
elif self._is_reasoner_model(model):
# R1 thinks unconditionally — no `thinking` field, but reasoning_content still flows.
thinking_active = True
# Strip params silently ignored under thinking mode to keep the wire clean.
if thinking_active:
for k in ("temperature", "top_p", "presence_penalty", "frequency_penalty"):
request_body.pop(k, None)
kwargs.pop(k, None)
else:
# Non-thinking path: forward standard sampling controls.
temperature = kwargs.pop("temperature", None)
if temperature is not None:
request_body["temperature"] = temperature
top_p = kwargs.pop("top_p", None)
if top_p is not None:
request_body["top_p"] = top_p
logger.debug(
f"[DEEPSEEK] API call: model={model}, "
f"tools={len(converted_tools) if converted_tools else 0}, "
f"stream={stream}, thinking={thinking_active}"
)
if stream:
return self._handle_stream_response(request_body)
else:
return self._handle_sync_response(request_body)
except Exception as e:
logger.error(f"[DEEPSEEK] call_with_tools error: {e}")
import traceback
logger.error(traceback.format_exc())
def error_generator():
yield {"error": True, "message": str(e), "status_code": 500}
return error_generator()
# -------------------- streaming --------------------
def _handle_stream_response(self, request_body: dict):
"""Stream SSE chunks from DeepSeek and yield OpenAI-format deltas (with reasoning_content)."""
try:
headers = self._build_headers()
url = f"{self.api_base}/chat/completions"
response = requests.post(url, headers=headers, json=request_body, stream=True, timeout=180)
if response.status_code != 200:
error_msg = response.text
logger.error(f"[DEEPSEEK] API error: status={response.status_code}, msg={error_msg}")
yield {"error": True, "message": error_msg, "status_code": response.status_code}
return
current_tool_calls = {}
finish_reason = None
for line in response.iter_lines():
if not line:
continue
line = line.decode("utf-8")
if line.startswith("data: "):
data_str = line[6:]
elif line.startswith("data:"):
data_str = line[5:]
else:
continue
if data_str.strip() == "[DONE]":
break
try:
chunk = json.loads(data_str)
except json.JSONDecodeError as e:
logger.warning(f"[DEEPSEEK] JSON decode error: {e}, data: {data_str[:200]}")
continue
if chunk.get("error"):
error_data = chunk["error"]
error_msg = error_data.get("message", "Unknown error") if isinstance(error_data, dict) else str(error_data)
logger.error(f"[DEEPSEEK] stream error: {error_msg}")
yield {"error": True, "message": error_msg, "status_code": 500}
return
if not chunk.get("choices"):
continue
choice = chunk["choices"][0]
delta = choice.get("delta", {})
if choice.get("finish_reason"):
finish_reason = choice["finish_reason"]
# Reasoning content (thinking mode). Forward as its own delta so
# agent_stream.py can stitch it into a `thinking` block.
if delta.get("reasoning_content"):
yield {
"choices": [{
"index": 0,
"delta": {
"role": "assistant",
"reasoning_content": delta["reasoning_content"],
},
"finish_reason": None,
}]
}
if delta.get("content"):
yield {
"choices": [{
"index": 0,
"delta": {
"role": "assistant",
"content": delta["content"],
},
}]
}
if "tool_calls" in delta and delta["tool_calls"]:
for tool_call_chunk in delta["tool_calls"]:
index = tool_call_chunk.get("index", 0)
if index not in current_tool_calls:
current_tool_calls[index] = {
"id": tool_call_chunk.get("id", ""),
"name": tool_call_chunk.get("function", {}).get("name", ""),
"arguments": "",
}
if "function" in tool_call_chunk and "arguments" in tool_call_chunk["function"]:
current_tool_calls[index]["arguments"] += tool_call_chunk["function"]["arguments"]
yield {
"choices": [{
"index": 0,
"delta": {"tool_calls": [tool_call_chunk]},
}]
}
yield {
"choices": [{
"index": 0,
"delta": {},
"finish_reason": finish_reason,
}]
}
except requests.exceptions.Timeout:
logger.error("[DEEPSEEK] Request timeout")
yield {"error": True, "message": "Request timeout", "status_code": 500}
except Exception as e:
logger.error(f"[DEEPSEEK] stream response error: {e}")
import traceback
logger.error(traceback.format_exc())
yield {"error": True, "message": str(e), "status_code": 500}
# -------------------- sync --------------------
def _handle_sync_response(self, request_body: dict):
"""Single-shot response. Yields a Claude-format dict for symmetry with stream path."""
try:
headers = self._build_headers()
request_body.pop("stream", None)
url = f"{self.api_base}/chat/completions"
response = requests.post(url, headers=headers, json=request_body, timeout=180)
if response.status_code != 200:
error_msg = response.text
logger.error(f"[DEEPSEEK] API error: status={response.status_code}, msg={error_msg}")
yield {"error": True, "message": error_msg, "status_code": response.status_code}
return
result = response.json()
message = result["choices"][0]["message"]
finish_reason = result["choices"][0]["finish_reason"]
response_data = {"role": "assistant", "content": []}
# Surface reasoning as a `thinking` block so the agent layer can persist it
# and round-trip it on tool-call turns (required by DeepSeek API).
if message.get("reasoning_content"):
response_data["content"].append({
"type": "thinking",
"thinking": message["reasoning_content"],
})
if message.get("content"):
response_data["content"].append({
"type": "text",
"text": message["content"],
})
if message.get("tool_calls"):
for tool_call in message["tool_calls"]:
try:
tool_input = json.loads(tool_call["function"]["arguments"])
except (json.JSONDecodeError, TypeError):
tool_input = {}
response_data["content"].append({
"type": "tool_use",
"id": tool_call["id"],
"name": tool_call["function"]["name"],
"input": tool_input,
})
if finish_reason == "tool_calls":
response_data["stop_reason"] = "tool_use"
elif finish_reason == "stop":
response_data["stop_reason"] = "end_turn"
else:
response_data["stop_reason"] = finish_reason
yield response_data
except requests.exceptions.Timeout:
logger.error("[DEEPSEEK] Request timeout")
yield {"error": True, "message": "Request timeout", "status_code": 500}
except Exception as e:
logger.error(f"[DEEPSEEK] sync response error: {e}")
import traceback
logger.error(traceback.format_exc())
yield {"error": True, "message": str(e), "status_code": 500}
# -------------------- format conversion --------------------
def _convert_messages_to_openai_format(self, messages):
"""
Convert Claude-format messages (content blocks) to OpenAI format.
Crucially, 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):
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)
self.model = model
self.reset()

View File

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

View File

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

View File

@@ -1095,7 +1095,9 @@ def main():
sys.exit(1)
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:
print(json.dumps({"error": f"Invalid JSON: {e}"}))
sys.exit(1)