mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-06-02 18:17:11 +08:00
Compare commits
8 Commits
2.0.7
...
dev-rag-en
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c65196e44 | ||
|
|
f5798bfe90 | ||
|
|
0e556b3468 | ||
|
|
31820f56e7 | ||
|
|
fd88828abd | ||
|
|
ae11159918 | ||
|
|
472a8605c0 | ||
|
|
e1760ba211 |
270
README.md
270
README.md
@@ -29,7 +29,7 @@
|
||||
- ✅ **工具系统:** 内置文件读写、终端执行、浏览器操作、定时任务等工具,Agent 自主调用以完成复杂任务
|
||||
- ✅ **CLI系统:** 提供终端命令和对话命令,支持进程管理、技能安装、配置修改等操作
|
||||
- ✅ **多模态消息:** 支持对文本、图片、语音、文件等多类型消息进行解析、处理、生成、发送等操作
|
||||
- ✅ **多模型支持:** 支持 OpenAI, Claude, Gemini, DeepSeek, MiniMax、GLM、Qwen、Kimi、Doubao 等国内外主流模型厂商
|
||||
- ✅ **多模型支持:** 支持 DeepSeek、MiniMax、Claude、Gemini、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>
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 : '');
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.0.6
|
||||
2.0.7
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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: ''
|
||||
|
||||
@@ -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 天,期间重启服务也无需重新登录。密码也支持在控制台的「配置」页面中在线修改。
|
||||
|
||||
|
||||
@@ -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` 并持久保存。
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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).
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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)を参照してください。
|
||||
|
||||
|
||||
@@ -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` に永続的に保存されます。
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
方法2: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"
|
||||
|
||||
@@ -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">
|
||||
統合マルチモデルインターフェース + ナレッジベース
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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">
|
||||
多模型统一接口 + 知识库
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
50
run.sh
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user