mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-06-02 00:57:41 +08:00
feat: add custom model provider
This commit is contained in:
@@ -448,6 +448,9 @@
|
||||
</div>
|
||||
<div class="cfg-dropdown-menu"></div>
|
||||
</div>
|
||||
<div id="cfg-custom-tip" class="mt-1.5 text-xs text-slate-400 dark:text-slate-500 hidden">
|
||||
<i class="fas fa-info-circle mr-1"></i><span data-i18n="config_custom_tip">接口需遵循 OpenAI API 协议</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Model -->
|
||||
<div>
|
||||
|
||||
@@ -45,6 +45,7 @@ const I18N = {
|
||||
config_save: '保存', config_saved: '已保存',
|
||||
config_save_error: '保存失败',
|
||||
config_custom_option: '自定义...',
|
||||
config_custom_tip: '接口需遵循 OpenAI API 协议',
|
||||
config_security: '安全设置', config_password: '访问密码',
|
||||
config_password_hint: '留空则不启用密码保护',
|
||||
config_password_changed: '密码已更新,请重新登录',
|
||||
@@ -130,6 +131,7 @@ const I18N = {
|
||||
config_save: 'Save', config_saved: 'Saved',
|
||||
config_save_error: 'Save failed',
|
||||
config_custom_option: 'Custom...',
|
||||
config_custom_tip: 'API must follow OpenAI protocol.',
|
||||
config_security: 'Security', config_password: 'Password',
|
||||
config_password_hint: 'Leave empty to disable password protection',
|
||||
config_password_changed: 'Password updated, please re-login',
|
||||
@@ -2158,6 +2160,9 @@ function onProviderChange(pid) {
|
||||
const p = configProviders[cfgProviderValue];
|
||||
if (!p) return;
|
||||
|
||||
const customTip = document.getElementById('cfg-custom-tip');
|
||||
if (customTip) customTip.classList.toggle('hidden', cfgProviderValue !== 'custom');
|
||||
|
||||
const modelEl = document.getElementById('cfg-model-select');
|
||||
const modelOpts = (p.models || []).map(m => ({ value: m, label: m }));
|
||||
modelOpts.push({ value: '__custom__', label: t('config_custom_option') });
|
||||
|
||||
@@ -801,15 +801,22 @@ class ConfigHandler:
|
||||
"api_base_default": None,
|
||||
"models": _RECOMMENDED_MODELS,
|
||||
}),
|
||||
("custom", {
|
||||
"label": "自定义",
|
||||
"api_key_field": "custom_api_key",
|
||||
"api_base_key": "custom_api_base",
|
||||
"api_base_default": "",
|
||||
"models": [],
|
||||
}),
|
||||
])
|
||||
|
||||
EDITABLE_KEYS = {
|
||||
"model", "bot_type", "use_linkai",
|
||||
"open_ai_api_base", "deepseek_api_base", "claude_api_base", "gemini_api_base",
|
||||
"zhipu_ai_api_base", "moonshot_base_url", "ark_base_url",
|
||||
"zhipu_ai_api_base", "moonshot_base_url", "ark_base_url", "custom_api_base",
|
||||
"open_ai_api_key", "deepseek_api_key", "claude_api_key", "gemini_api_key",
|
||||
"zhipu_ai_api_key", "dashscope_api_key", "moonshot_api_key",
|
||||
"ark_api_key", "minimax_api_key", "linkai_api_key",
|
||||
"ark_api_key", "minimax_api_key", "linkai_api_key", "custom_api_key",
|
||||
"agent_max_context_tokens", "agent_max_context_turns", "agent_max_steps",
|
||||
"enable_thinking", "web_password",
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ ZHIPU_AI = "zhipu"
|
||||
MOONSHOT = "moonshot"
|
||||
MiniMax = "minimax"
|
||||
DEEPSEEK = "deepseek"
|
||||
CUSTOM = "custom" # custom OpenAI-compatible API, bot_type won't auto-switch on model change
|
||||
MODELSCOPE = "modelscope"
|
||||
|
||||
# 模型列表
|
||||
|
||||
@@ -17,10 +17,12 @@ available_setting = {
|
||||
"open_ai_api_base": "https://api.openai.com/v1",
|
||||
"claude_api_base": "https://api.anthropic.com/v1", # claude api base
|
||||
"gemini_api_base": "https://generativelanguage.googleapis.com", # gemini api base
|
||||
"custom_api_key": "", # custom OpenAI-compatible provider api key (used when bot_type is "custom")
|
||||
"custom_api_base": "", # custom OpenAI-compatible provider api base (used when bot_type is "custom")
|
||||
"proxy": "", # openai使用的代理
|
||||
# chatgpt模型, 当use_azure_chatgpt为true时,其名称为Azure上model deployment名称
|
||||
"model": "gpt-3.5-turbo", # 可选择: gpt-4o, pt-4o-mini, gpt-4-turbo, claude-3-sonnet, wenxin, moonshot, qwen-turbo, xunfei, glm-4, minimax, gemini等模型,全部可选模型详见common/const.py文件
|
||||
"bot_type": "", # 可选配置,使用兼容openai格式的三方服务时候,需填"openai"(历史值"chatGPT"仍兼容)。bot具体名称详见common/const.py文件,如不填根据model名称判断
|
||||
"bot_type": "", # 可选配置,使用兼容openai格式的三方服务时候,需填"openai"或"custom"(custom模式下切换模型不会自动切换bot_type)。bot具体名称详见common/const.py文件,如不填根据model名称判断
|
||||
"use_azure_chatgpt": False, # 是否使用azure的chatgpt
|
||||
"azure_deployment_id": "", # azure 模型部署名称
|
||||
"azure_api_version": "", # azure api版本
|
||||
|
||||
@@ -82,7 +82,8 @@
|
||||
"models/openai",
|
||||
"models/deepseek",
|
||||
"models/linkai",
|
||||
"models/coding-plan"
|
||||
"models/coding-plan",
|
||||
"models/custom"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -257,7 +258,8 @@
|
||||
"en/models/openai",
|
||||
"en/models/deepseek",
|
||||
"en/models/linkai",
|
||||
"en/models/coding-plan"
|
||||
"en/models/coding-plan",
|
||||
"en/models/custom"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -432,7 +434,8 @@
|
||||
"ja/models/openai",
|
||||
"ja/models/deepseek",
|
||||
"ja/models/linkai",
|
||||
"ja/models/coding-plan"
|
||||
"ja/models/coding-plan",
|
||||
"ja/models/custom"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
62
docs/en/models/custom.mdx
Normal file
62
docs/en/models/custom.mdx
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Custom
|
||||
description: Custom provider for third-party APIs and local models
|
||||
---
|
||||
|
||||
For models accessed via OpenAI-compatible APIs, such as:
|
||||
|
||||
- **Third-party API proxies**: Use a unified API Base to call multiple models
|
||||
- **Local models**: Models deployed locally via Ollama, vLLM, LocalAI, etc.
|
||||
- **Private deployments**: Self-hosted model services within your organization
|
||||
|
||||
<Note>
|
||||
Unlike the `openai` provider, switching models under the Custom provider will not auto-switch the provider type. Your custom API address is always preserved.
|
||||
</Note>
|
||||
|
||||
## Configuration
|
||||
|
||||
### Third-party API Proxy
|
||||
|
||||
```json
|
||||
{
|
||||
"bot_type": "custom",
|
||||
"model": "deepseek-chat",
|
||||
"custom_api_key": "YOUR_API_KEY",
|
||||
"custom_api_base": "https://{your-proxy.com}/v1"
|
||||
}
|
||||
```
|
||||
|
||||
| Parameter | Description |
|
||||
| --- | --- |
|
||||
| `bot_type` | Must be set to `custom` |
|
||||
| `model` | Model name, any model supported by your proxy service |
|
||||
| `custom_api_key` | API key provided by your proxy service |
|
||||
| `custom_api_base` | API base URL, must be OpenAI-compatible |
|
||||
|
||||
### Local Models
|
||||
|
||||
Local models typically don't require an API key — just set the API base:
|
||||
|
||||
```json
|
||||
{
|
||||
"bot_type": "custom",
|
||||
"model": "qwen3.5:27b",
|
||||
"custom_api_base": "http://localhost:11434/v1"
|
||||
}
|
||||
```
|
||||
|
||||
Common local deployment tools and their default addresses:
|
||||
|
||||
| Tool | Default API Base |
|
||||
| --- | --- |
|
||||
| [Ollama](https://ollama.com) | `http://localhost:11434/v1` |
|
||||
| [vLLM](https://docs.vllm.ai) | `http://localhost:8000/v1` |
|
||||
| [LocalAI](https://localai.io) | `http://localhost:8080/v1` |
|
||||
|
||||
## Switching Models
|
||||
|
||||
Under the Custom provider, switching models only changes `model` without affecting `bot_type` or the API address:
|
||||
|
||||
```
|
||||
/config model qwen3.5:27b
|
||||
```
|
||||
62
docs/ja/models/custom.mdx
Normal file
62
docs/ja/models/custom.mdx
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: カスタム
|
||||
description: サードパーティAPIやローカルモデル向けのカスタムプロバイダー設定
|
||||
---
|
||||
|
||||
OpenAI互換プロトコルでアクセスするモデルサービスに適用します:
|
||||
|
||||
- **サードパーティAPIプロキシ**:統一APIベースで複数モデルを呼び出し
|
||||
- **ローカルモデル**:Ollama、vLLM、LocalAIなどでローカルにデプロイされたモデル
|
||||
- **プライベートデプロイ**:組織内でホストされたモデルサービス
|
||||
|
||||
<Note>
|
||||
`openai` プロバイダーとの違い:カスタムプロバイダーでは `/config model` でモデルを切り替えてもプロバイダータイプは自動切り替えされず、カスタムAPIアドレスが常に保持されます。
|
||||
</Note>
|
||||
|
||||
## 設定方法
|
||||
|
||||
### サードパーティAPIプロキシ
|
||||
|
||||
```json
|
||||
{
|
||||
"bot_type": "custom",
|
||||
"model": "deepseek-chat",
|
||||
"custom_api_key": "YOUR_API_KEY",
|
||||
"custom_api_base": "https://{your-proxy.com}/v1"
|
||||
}
|
||||
```
|
||||
|
||||
| パラメータ | 説明 |
|
||||
| --- | --- |
|
||||
| `bot_type` | `custom` に設定必須 |
|
||||
| `model` | モデル名、プロキシサービスがサポートする任意のモデル名 |
|
||||
| `custom_api_key` | プロキシサービスが提供するAPIキー |
|
||||
| `custom_api_base` | APIアドレス、OpenAI互換プロトコルが必要 |
|
||||
|
||||
### ローカルモデル
|
||||
|
||||
ローカルモデルは通常APIキー不要で、APIベースのみ設定します:
|
||||
|
||||
```json
|
||||
{
|
||||
"bot_type": "custom",
|
||||
"model": "qwen3.5:27b",
|
||||
"custom_api_base": "http://localhost:11434/v1"
|
||||
}
|
||||
```
|
||||
|
||||
一般的なローカルデプロイツールとデフォルトアドレス:
|
||||
|
||||
| ツール | デフォルトAPIベース |
|
||||
| --- | --- |
|
||||
| [Ollama](https://ollama.com) | `http://localhost:11434/v1` |
|
||||
| [vLLM](https://docs.vllm.ai) | `http://localhost:8000/v1` |
|
||||
| [LocalAI](https://localai.io) | `http://localhost:8080/v1` |
|
||||
|
||||
## モデル切り替え
|
||||
|
||||
カスタムプロバイダーではモデル切り替え時に `model` のみ変更され、`bot_type` やAPIアドレスは変わりません:
|
||||
|
||||
```
|
||||
/config model qwen3.5:27b
|
||||
```
|
||||
62
docs/models/custom.mdx
Normal file
62
docs/models/custom.mdx
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: 自定义
|
||||
description: 自定义厂商配置,适用于第三方 API 代理和本地模型
|
||||
---
|
||||
|
||||
适用于通过 OpenAI 兼容协议接入的第三方模型服务或本地部署的模型,例如:
|
||||
|
||||
- **第三方 API 代理**:使用统一的 API Base 调用多种模型
|
||||
- **本地模型**:通过 Ollama、vLLM、LocalAI 等工具在本地部署的模型
|
||||
- **私有化部署**:企业内部部署的模型服务
|
||||
|
||||
<Note>
|
||||
与 `openai` 厂商的区别:选择自定义厂商后,通过 `/config model` 切换模型时,不会自动切换厂商类型,始终使用自定义的 API 地址。
|
||||
</Note>
|
||||
|
||||
## 配置方式
|
||||
|
||||
### 第三方 API 代理
|
||||
|
||||
```json
|
||||
{
|
||||
"bot_type": "custom",
|
||||
"model": "deepseek-chat",
|
||||
"custom_api_key": "YOUR_API_KEY",
|
||||
"custom_api_base": "https://{your-proxy.com}/v1"
|
||||
}
|
||||
```
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| `bot_type` | 必须设为 `custom` |
|
||||
| `model` | 模型名称,可填写代理服务支持的任意模型名 |
|
||||
| `custom_api_key` | API 密钥,由代理服务提供 |
|
||||
| `custom_api_base` | API 地址,由代理服务提供,需兼容 OpenAI 协议 |
|
||||
|
||||
### 本地模型
|
||||
|
||||
本地模型通常不需要 API Key,只需填写 API Base 即可:
|
||||
|
||||
```json
|
||||
{
|
||||
"bot_type": "custom",
|
||||
"model": "qwen3.5:27b",
|
||||
"custom_api_base": "http://localhost:11434/v1"
|
||||
}
|
||||
```
|
||||
|
||||
常见的本地部署工具及默认地址:
|
||||
|
||||
| 工具 | 默认 API Base |
|
||||
| --- | --- |
|
||||
| [Ollama](https://ollama.com) | `http://localhost:11434/v1` |
|
||||
| [vLLM](https://docs.vllm.ai) | `http://localhost:8000/v1` |
|
||||
| [LocalAI](https://localai.io) | `http://localhost:8080/v1` |
|
||||
|
||||
## 切换模型
|
||||
|
||||
自定义厂商下切换模型时,只会修改 `model`,不会改变 `bot_type` 和 API 地址:
|
||||
|
||||
```
|
||||
/config model qwen3.5:27b
|
||||
```
|
||||
@@ -53,6 +53,9 @@ CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在
|
||||
<Card title="LinkAI" href="/models/linkai">
|
||||
多模型统一接口 + 知识库
|
||||
</Card>
|
||||
<Card title="自定义" href="/models/custom">
|
||||
第三方代理、本地模型等
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ def create_bot(bot_type):
|
||||
from models.deepseek.deepseek_bot import DeepSeekBot
|
||||
return DeepSeekBot()
|
||||
|
||||
elif bot_type in (const.OPENAI, const.CHATGPT): # OpenAI-compatible API
|
||||
elif bot_type in (const.OPENAI, const.CHATGPT, const.CUSTOM): # OpenAI-compatible API
|
||||
from models.chatgpt.chat_gpt_bot import ChatGPTBot
|
||||
return ChatGPTBot()
|
||||
|
||||
|
||||
@@ -23,7 +23,12 @@ from models.baidu.baidu_wenxin_session import BaiduWenxinSession
|
||||
class ChatGPTBot(Bot, OpenAIImage, OpenAICompatibleBot):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# set the default api_key
|
||||
# set the default api_key / api_base based on bot_type
|
||||
if conf().get("bot_type") == "custom":
|
||||
openai.api_key = conf().get("custom_api_key", "")
|
||||
if conf().get("custom_api_base"):
|
||||
openai.api_base = conf().get("custom_api_base")
|
||||
else:
|
||||
openai.api_key = conf().get("open_ai_api_key")
|
||||
if conf().get("open_ai_api_base"):
|
||||
openai.api_base = conf().get("open_ai_api_base")
|
||||
@@ -56,9 +61,10 @@ class ChatGPTBot(Bot, OpenAIImage, OpenAICompatibleBot):
|
||||
|
||||
def get_api_config(self):
|
||||
"""Get API configuration for OpenAI-compatible base class"""
|
||||
is_custom = conf().get("bot_type") == "custom"
|
||||
return {
|
||||
'api_key': conf().get("open_ai_api_key"),
|
||||
'api_base': conf().get("open_ai_api_base"),
|
||||
'api_key': conf().get("custom_api_key") if is_custom else conf().get("open_ai_api_key"),
|
||||
'api_base': conf().get("custom_api_base") if is_custom else conf().get("open_ai_api_base"),
|
||||
'model': conf().get("model", "gpt-3.5-turbo"),
|
||||
'default_temperature': conf().get("temperature", 0.9),
|
||||
'default_top_p': conf().get("top_p", 1.0),
|
||||
@@ -166,9 +172,10 @@ class ChatGPTBot(Bot, OpenAIImage, OpenAICompatibleBot):
|
||||
mime_type = mime_type_map.get(extension, "image/jpeg")
|
||||
|
||||
# Get model and API config
|
||||
is_custom = conf().get("bot_type") == "custom"
|
||||
model = context.get("gpt_model") or conf().get("model", "gpt-4o")
|
||||
api_key = context.get("openai_api_key") or conf().get("open_ai_api_key")
|
||||
api_base = conf().get("open_ai_api_base")
|
||||
api_key = context.get("openai_api_key") or (conf().get("custom_api_key") if is_custom else conf().get("open_ai_api_key"))
|
||||
api_base = conf().get("custom_api_base") if is_custom else conf().get("open_ai_api_base")
|
||||
|
||||
# Build vision request
|
||||
messages = [
|
||||
|
||||
@@ -381,6 +381,9 @@ class CowCliPlugin(Plugin):
|
||||
updates = {key: new_val}
|
||||
|
||||
if key == "model" and conf().get("bot_type"):
|
||||
from common import const
|
||||
current_bot_type = conf().get("bot_type")
|
||||
if current_bot_type not in (const.CUSTOM,):
|
||||
resolved = self._resolve_bot_type_for_model(str(new_val))
|
||||
if resolved:
|
||||
updates["bot_type"] = resolved
|
||||
|
||||
Reference in New Issue
Block a user