From 8993e8ad3efa7880029d97acc678fcc9fa0b7fb9 Mon Sep 17 00:00:00 2001 From: zhayujie Date: Wed, 18 Mar 2026 15:40:49 +0800 Subject: [PATCH] feat: release 2.0.3 --- README.md | 21 ++++---------- docker/docker-compose.yml | 43 +++++++++++++++++++---------- docs/guide/manual-install.mdx | 10 ++++++- docs/releases/v2.0.3.mdx | 6 ++++ models/claudeapi/claude_api_bot.py | 15 ++++++++-- models/dashscope/dashscope_bot.py | 10 +++++-- models/doubao/doubao_bot.py | 18 +++++++----- models/gemini/google_gemini_bot.py | 25 ++++++++--------- models/minimax/minimax_bot.py | 24 ++++++++-------- models/modelscope/modelscope_bot.py | 10 +++++-- models/moonshot/moonshot_bot.py | 18 +++++++----- 11 files changed, 121 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 57e85fc..f89ab37 100644 --- a/README.md +++ b/README.md @@ -102,9 +102,9 @@ bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh) 项目支持国内外主流厂商的模型接口,可选模型及配置说明参考:[模型说明](#模型说明)。 -> 注:Agent模式下推荐使用以下模型,可根据效果及成本综合选择:MiniMax-M2.5、glm-5、kimi-k2.5、qwen3.5-plus、claude-sonnet-4-6、gemini-3.1-pro-preview、gpt-5.4 +> 注:Agent模式下推荐使用以下模型,可根据效果及成本综合选择:MiniMax-M2.5、glm-5、kimi-k2.5、qwen3.5-plus、claude-sonnet-4-6、gemini-3.1-pro-preview、gpt-5.4、gpt-5.4-mini -同时支持使用 **LinkAI平台** 接口,可灵活切换 OpenAI、Claude、Gemini、DeepSeek、Qwen、Kimi 等多种常用模型,并支持知识库、工作流、插件等Agent能力,参考 [接口文档](https://docs.link-ai.tech/platform/api)。 +同时支持使用 **LinkAI平台** 接口,支持上述全部模型,并支持知识库、工作流、插件等Agent技能,参考 [接口文档](https://docs.link-ai.tech/platform/api)。 ### 2.环境安装 @@ -226,8 +226,9 @@ nohup python3 app.py & tail -f nohup.out 执行后程序运行于服务器后台,可通过 `ctrl+c` 关闭日志,不会影响后台程序的运行。使用 `ps -ef | grep app.py | grep -v grep` 命令可查看运行于后台的进程,如果想要重新启动程序可以先 `kill` 掉对应的进程。 日志关闭后如果想要再次打开只需输入 `tail -f nohup.out`。 -此外,项目的 `scripts` 目录下有一键运行、关闭程序的脚本供使用。 运行后默认channel为web,通过可以通过修改配置文件进行切换。 +此外,项目根目录下的 `run.sh` 脚本支持一键启动和管理服务,包括 `./run.sh start`、`./run.sh stop`、`./run.sh restart`、`./run.sh logs` 等命令,执行 `./run.sh help` 可查看全部用法。 +> 如果需要通过浏览器访问Web控制台,请确保服务器的 `9899` 端口已在防火墙或安全组中放行,建议仅对指定IP开放以保证安全。 ### 3.Docker部署 @@ -257,17 +258,7 @@ sudo docker compose up -d # 若docker-compose为 1.X 版本,则执行 sudo docker logs -f chatgpt-on-wechat ``` -**(3) 插件使用** - -如果需要在docker容器中修改插件配置,可通过挂载的方式完成,将 [插件配置文件](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/plugins/config.json.template) -重命名为 `config.json`,放置于 `docker-compose.yml` 相同目录下,并在 `docker-compose.yml` 中的 `chatgpt-on-wechat` 部分下添加 `volumes` 映射: - -``` -volumes: - - ./config.json:/app/plugins/config.json -``` -**注**:使用docker方式部署的详细教程可以参考:[docker部署CoW项目](https://www.wangpc.cc/ai/docker-deploy-cow/) - +> 如果需要通过浏览器访问Web控制台,请确保服务器的 `9899` 端口已在防火墙或安全组中放行,建议仅对指定IP开放以保证安全。 ## 模型说明 @@ -613,7 +604,7 @@ API Key创建:在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
-Coding Plan (编程包月套餐) +Coding Plan Coding Plan 是各厂商推出的编程包月套餐,所有厂商均可通过 OpenAI 兼容方式接入: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8944d70..3b76227 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -5,22 +5,37 @@ services: container_name: chatgpt-on-wechat security_opt: - seccomp:unconfined + ports: + - "9899:9899" environment: CHANNEL_TYPE: 'web' - OPEN_AI_API_KEY: 'YOUR API KEY' - MODEL: '' - PROXY: '' - SINGLE_CHAT_PREFIX: '["bot", "@bot"]' - SINGLE_CHAT_REPLY_PREFIX: '"[bot] "' - GROUP_CHAT_PREFIX: '["@bot"]' - GROUP_NAME_WHITE_LIST: '["ChatGPT测试群", "ChatGPT测试群2"]' - IMAGE_CREATE_PREFIX: '["画", "看", "找"]' - CONVERSATION_MAX_TOKENS: 1000 - SPEECH_RECOGNITION: 'False' - CHARACTER_DESC: '你是基于大语言模型的AI智能助手,旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。' - EXPIRES_IN_SECONDS: 3600 - USE_GLOBAL_PLUGIN_CONFIG: 'True' + MODEL: 'MiniMax-M2.5' + MINIMAX_API_KEY: '' + ZHIPU_AI_API_KEY: '' + ARK_API_KEY: '' + MOONSHOT_API_KEY: '' + DASHSCOPE_API_KEY: '' + CLAUDE_API_KEY: '' + CLAUDE_API_BASE: 'https://api.anthropic.com/v1' + OPEN_AI_API_KEY: '' + OPEN_AI_API_BASE: 'https://api.openai.com/v1' + GEMINI_API_KEY: '' + GEMINI_API_BASE: 'https://generativelanguage.googleapis.com' + VOICE_TO_TEXT: 'openai' + TEXT_TO_VOICE: 'openai' + VOICE_REPLY_VOICE: 'False' + SPEECH_RECOGNITION: 'True' + GROUP_SPEECH_RECOGNITION: 'False' USE_LINKAI: 'False' - AGENT: 'True' LINKAI_API_KEY: '' LINKAI_APP_CODE: '' + FEISHU_APP_ID: '' + FEISHU_APP_SECRET: '' + DINGTALK_CLIENT_ID: '' + DINGTALK_CLIENT_SECRET: '' + WECOM_BOT_ID: '' + WECOM_BOT_SECRET: '' + AGENT: 'True' + AGENT_MAX_CONTEXT_TOKENS: 40000 + AGENT_MAX_CONTEXT_TURNS: 20 + AGENT_MAX_STEPS: 15 diff --git a/docs/guide/manual-install.mdx b/docs/guide/manual-install.mdx index 311bbe2..c2b8542 100644 --- a/docs/guide/manual-install.mdx +++ b/docs/guide/manual-install.mdx @@ -56,9 +56,13 @@ python3 app.py nohup python3 app.py & tail -f nohup.out ``` + + 如果在服务器上部署,需要在防火墙或安全组中放行 `9899` 端口才能通过浏览器访问 Web 控制台,建议仅对指定IP开放以保证安全。 + + ## Docker 部署 -使用 Docker 部署无需下载源码和安装依赖。Agent 模式下更推荐使用源码部署以获得更多系统访问能力。 +使用 Docker 部署无需下载源码和安装依赖。Agent模式下更推荐使用源码部署以获得更多系统访问能力。 需要安装 [Docker](https://docs.docker.com/engine/install/) 和 docker-compose。 @@ -84,6 +88,10 @@ sudo docker compose up -d sudo docker logs -f chatgpt-on-wechat ``` + + 如果在服务器上部署,需要在防火墙或安全组中放行 `9899` 端口才能通过浏览器访问 Web 控制台,建议仅对指定IP开放以保证安全。 + + ## 核心配置项 ```json diff --git a/docs/releases/v2.0.3.mdx b/docs/releases/v2.0.3.mdx index 8f5f115..283e253 100644 --- a/docs/releases/v2.0.3.mdx +++ b/docs/releases/v2.0.3.mdx @@ -55,6 +55,12 @@ Web 控制台对话界面支持文件和图片上传,可直接发送文件给 - **图片识别**:将图片识别(Image Vision)从 Skill 重构为内置 Tool,新增独立的图片视觉提供方(Vision Provider)配置,提升稳定性和可维护性 ([a50fafa](https://github.com/zhayujie/chatgpt-on-wechat/commit/a50fafa), [3b8b562](https://github.com/zhayujie/chatgpt-on-wechat/commit/3b8b562)) - **网页抓取**:将网页抓取(Web Fetch)从 Skill 重构为内置 Tool,支持远程文档文件(PDF、Word、Excel、PPT)的下载和解析 ([ccb9030](https://github.com/zhayujie/chatgpt-on-wechat/commit/ccb9030), [fa61744](https://github.com/zhayujie/chatgpt-on-wechat/commit/fa61744)) +## 🐳 Docker 部署优化 + +- **配置模板对齐**:`docker-compose.yml` 环境变量与 `config-template.json` 对齐,补充完整的模型 API Key 和 Agent 等配置项 +- **Web 控制台端口映射**:新增 `9899` 端口映射,Docker 部署后可通过浏览器访问 Web 控制台 +- **配置热更新**:各模型 Bot 的 API Key 和 API Base 改为实时读取,通过 Web 控制台修改配置后无需重启即可生效 + ## ⚡ 性能优化 - **启动加速**:飞书通道采用懒加载方式导入依赖,避免 4-10 秒的启动延迟 ([924dc79](https://github.com/zhayujie/chatgpt-on-wechat/commit/924dc79)) diff --git a/models/claudeapi/claude_api_bot.py b/models/claudeapi/claude_api_bot.py index f85978a..5dcf917 100644 --- a/models/claudeapi/claude_api_bot.py +++ b/models/claudeapi/claude_api_bot.py @@ -30,11 +30,20 @@ user_session = dict() class ClaudeAPIBot(Bot, OpenAIImage): def __init__(self): super().__init__() - self.api_key = conf().get("claude_api_key") - self.api_base = conf().get("claude_api_base") or "https://api.anthropic.com/v1" - self.proxy = conf().get("proxy", None) self.sessions = SessionManager(BaiduWenxinSession, model=conf().get("model") or "text-davinci-003") + @property + def api_key(self): + return conf().get("claude_api_key") + + @property + def api_base(self): + return conf().get("claude_api_base") or "https://api.anthropic.com/v1" + + @property + def proxy(self): + return conf().get("proxy", None) + def reply(self, query, context=None): # acquire reply content if context and context.type: diff --git a/models/dashscope/dashscope_bot.py b/models/dashscope/dashscope_bot.py index 26cf7db..1b67731 100644 --- a/models/dashscope/dashscope_bot.py +++ b/models/dashscope/dashscope_bot.py @@ -35,10 +35,14 @@ class DashscopeBot(Bot): super().__init__() self.sessions = SessionManager(DashscopeSession, model=conf().get("model") or "qwen-plus") self.model_name = conf().get("model") or "qwen-plus" - self.api_key = conf().get("dashscope_api_key") - if self.api_key: - os.environ["DASHSCOPE_API_KEY"] = self.api_key self.client = dashscope.Generation + api_key = conf().get("dashscope_api_key") + if api_key: + os.environ["DASHSCOPE_API_KEY"] = api_key + + @property + def api_key(self): + return conf().get("dashscope_api_key") @staticmethod def _is_multimodal_model(model_name: str) -> bool: diff --git a/models/doubao/doubao_bot.py b/models/doubao/doubao_bot.py index 987d718..cfe4ba5 100644 --- a/models/doubao/doubao_bot.py +++ b/models/doubao/doubao_bot.py @@ -24,13 +24,17 @@ class DoubaoBot(Bot): "temperature": conf().get("temperature", 0.8), "top_p": conf().get("top_p", 1.0), } - self.api_key = conf().get("ark_api_key") - self.base_url = conf().get("ark_base_url", "https://ark.cn-beijing.volces.com/api/v3") - # Ensure base_url does not end with /chat/completions - if self.base_url.endswith("/chat/completions"): - self.base_url = self.base_url.rsplit("/chat/completions", 1)[0] - if self.base_url.endswith("/"): - self.base_url = self.base_url.rstrip("/") + + @property + def api_key(self): + return conf().get("ark_api_key") + + @property + def base_url(self): + url = conf().get("ark_base_url", "https://ark.cn-beijing.volces.com/api/v3") + if url.endswith("/chat/completions"): + url = url.rsplit("/chat/completions", 1)[0] + return url.rstrip("/") def reply(self, query, context=None): # acquire reply content diff --git a/models/gemini/google_gemini_bot.py b/models/gemini/google_gemini_bot.py index 62e4068..3521a84 100644 --- a/models/gemini/google_gemini_bot.py +++ b/models/gemini/google_gemini_bot.py @@ -28,21 +28,18 @@ class GoogleGeminiBot(Bot): def __init__(self): super().__init__() - self.api_key = conf().get("gemini_api_key") - # 复用chatGPT的token计算方式 self.sessions = SessionManager(ChatGPTSession, model=conf().get("model") or "gpt-3.5-turbo") - self.model = conf().get("model") or "gemini-pro" - if self.model == "gemini": - self.model = "gemini-pro" - - # 支持自定义API base地址 - self.api_base = conf().get("gemini_api_base", "").strip() - if self.api_base: - # 移除末尾的斜杠 - self.api_base = self.api_base.rstrip('/') - logger.info(f"[Gemini] Using custom API base: {self.api_base}") - else: - self.api_base = "https://generativelanguage.googleapis.com" + + @property + def api_key(self): + return conf().get("gemini_api_key") + + @property + def api_base(self): + base = conf().get("gemini_api_base", "").strip() + if base: + return base.rstrip('/') + return "https://generativelanguage.googleapis.com" def reply(self, query, context: Context = None) -> Reply: session_id = None diff --git a/models/minimax/minimax_bot.py b/models/minimax/minimax_bot.py index ba700c2..af80e79 100644 --- a/models/minimax/minimax_bot.py +++ b/models/minimax/minimax_bot.py @@ -24,21 +24,19 @@ class MinimaxBot(Bot): "temperature": conf().get("temperature", 0.3), "top_p": conf().get("top_p", 0.95), } - # Use unified key name: minimax_api_key - self.api_key = conf().get("minimax_api_key") - if not self.api_key: - # Fallback to old key name for backward compatibility - self.api_key = conf().get("Minimax_api_key") - if self.api_key: - logger.warning("[MINIMAX] 'Minimax_api_key' is deprecated, please use 'minimax_api_key' instead") - - # REST API endpoint - # Use Chinese endpoint by default, users can override in config - # International users should set: "minimax_api_base": "https://api.minimax.io/v1" - self.api_base = conf().get("minimax_api_base", "https://api.minimaxi.com/v1") - self.sessions = SessionManager(MinimaxSession, model=const.MiniMax) + @property + def api_key(self): + key = conf().get("minimax_api_key") + if not key: + key = conf().get("Minimax_api_key") + return key + + @property + def api_base(self): + return conf().get("minimax_api_base", "https://api.minimaxi.com/v1") + def reply(self, query, context: Context = None) -> Reply: # acquire reply content logger.info("[MINIMAX] query={}".format(query)) diff --git a/models/modelscope/modelscope_bot.py b/models/modelscope/modelscope_bot.py index 2994abd..e6d26fb 100644 --- a/models/modelscope/modelscope_bot.py +++ b/models/modelscope/modelscope_bot.py @@ -26,8 +26,14 @@ class ModelScopeBot(Bot): "temperature": conf().get("temperature", 0.3), # 如果设置,值域须为 [0, 1] 我们推荐 0.3,以达到较合适的效果。 "top_p": conf().get("top_p", 1.0), # 使用默认值 } - self.api_key = conf().get("modelscope_api_key") - self.base_url = conf().get("modelscope_base_url", "https://api-inference.modelscope.cn/v1/chat/completions") + + @property + def api_key(self): + return conf().get("modelscope_api_key") + + @property + def base_url(self): + return conf().get("modelscope_base_url", "https://api-inference.modelscope.cn/v1/chat/completions") """ 需要获取ModelScope支持API-inference的模型名称列表,请到魔搭社区官网模型中心查看 https://modelscope.cn/models?filter=inference_type&page=1。 或者使用命令 curl https://api-inference.modelscope.cn/v1/models 对模型列表和ID进行获取。查看commend/const.py文件也可以获取模型列表。 diff --git a/models/moonshot/moonshot_bot.py b/models/moonshot/moonshot_bot.py index 027483d..ded011c 100644 --- a/models/moonshot/moonshot_bot.py +++ b/models/moonshot/moonshot_bot.py @@ -26,13 +26,17 @@ class MoonshotBot(Bot): "temperature": conf().get("temperature", 0.3), "top_p": conf().get("top_p", 1.0), } - self.api_key = conf().get("moonshot_api_key") - self.base_url = conf().get("moonshot_base_url", "https://api.moonshot.cn/v1") - # Ensure base_url does not end with /chat/completions (backward compat) - if self.base_url.endswith("/chat/completions"): - self.base_url = self.base_url.rsplit("/chat/completions", 1)[0] - if self.base_url.endswith("/"): - self.base_url = self.base_url.rstrip("/") + + @property + def api_key(self): + return conf().get("moonshot_api_key") + + @property + def base_url(self): + url = conf().get("moonshot_base_url", "https://api.moonshot.cn/v1") + if url.endswith("/chat/completions"): + url = url.rsplit("/chat/completions", 1)[0] + return url.rstrip("/") def reply(self, query, context=None): # acquire reply content