Skip to content

Commit 3830f76

Browse files
committed
feat: add custom model provider
1 parent 83f778f commit 3830f76

13 files changed

Lines changed: 238 additions & 18 deletions

File tree

channel/web/chat.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,9 @@ <h3 class="font-semibold text-slate-800 dark:text-slate-100" data-i18n="config_m
448448
</div>
449449
<div class="cfg-dropdown-menu"></div>
450450
</div>
451+
<div id="cfg-custom-tip" class="mt-1.5 text-xs text-slate-400 dark:text-slate-500 hidden">
452+
<i class="fas fa-info-circle mr-1"></i><span data-i18n="config_custom_tip">接口需遵循 OpenAI API 协议</span>
453+
</div>
451454
</div>
452455
<!-- Model -->
453456
<div>

channel/web/static/js/console.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const I18N = {
4545
config_save: '保存', config_saved: '已保存',
4646
config_save_error: '保存失败',
4747
config_custom_option: '自定义...',
48+
config_custom_tip: '接口需遵循 OpenAI API 协议',
4849
config_security: '安全设置', config_password: '访问密码',
4950
config_password_hint: '留空则不启用密码保护',
5051
config_password_changed: '密码已更新,请重新登录',
@@ -130,6 +131,7 @@ const I18N = {
130131
config_save: 'Save', config_saved: 'Saved',
131132
config_save_error: 'Save failed',
132133
config_custom_option: 'Custom...',
134+
config_custom_tip: 'API must follow OpenAI protocol.',
133135
config_security: 'Security', config_password: 'Password',
134136
config_password_hint: 'Leave empty to disable password protection',
135137
config_password_changed: 'Password updated, please re-login',
@@ -2158,6 +2160,9 @@ function onProviderChange(pid) {
21582160
const p = configProviders[cfgProviderValue];
21592161
if (!p) return;
21602162

2163+
const customTip = document.getElementById('cfg-custom-tip');
2164+
if (customTip) customTip.classList.toggle('hidden', cfgProviderValue !== 'custom');
2165+
21612166
const modelEl = document.getElementById('cfg-model-select');
21622167
const modelOpts = (p.models || []).map(m => ({ value: m, label: m }));
21632168
modelOpts.push({ value: '__custom__', label: t('config_custom_option') });

channel/web/web_channel.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,15 +801,22 @@ class ConfigHandler:
801801
"api_base_default": None,
802802
"models": _RECOMMENDED_MODELS,
803803
}),
804+
("custom", {
805+
"label": "自定义",
806+
"api_key_field": "custom_api_key",
807+
"api_base_key": "custom_api_base",
808+
"api_base_default": "",
809+
"models": [],
810+
}),
804811
])
805812

806813
EDITABLE_KEYS = {
807814
"model", "bot_type", "use_linkai",
808815
"open_ai_api_base", "deepseek_api_base", "claude_api_base", "gemini_api_base",
809-
"zhipu_ai_api_base", "moonshot_base_url", "ark_base_url",
816+
"zhipu_ai_api_base", "moonshot_base_url", "ark_base_url", "custom_api_base",
810817
"open_ai_api_key", "deepseek_api_key", "claude_api_key", "gemini_api_key",
811818
"zhipu_ai_api_key", "dashscope_api_key", "moonshot_api_key",
812-
"ark_api_key", "minimax_api_key", "linkai_api_key",
819+
"ark_api_key", "minimax_api_key", "linkai_api_key", "custom_api_key",
813820
"agent_max_context_tokens", "agent_max_context_turns", "agent_max_steps",
814821
"enable_thinking", "web_password",
815822
}

common/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
MOONSHOT = "moonshot"
1515
MiniMax = "minimax"
1616
DEEPSEEK = "deepseek"
17+
CUSTOM = "custom" # custom OpenAI-compatible API, bot_type won't auto-switch on model change
1718
MODELSCOPE = "modelscope"
1819

1920
# 模型列表

config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
"open_ai_api_base": "https://api.openai.com/v1",
1818
"claude_api_base": "https://api.anthropic.com/v1", # claude api base
1919
"gemini_api_base": "https://generativelanguage.googleapis.com", # gemini api base
20+
"custom_api_key": "", # custom OpenAI-compatible provider api key (used when bot_type is "custom")
21+
"custom_api_base": "", # custom OpenAI-compatible provider api base (used when bot_type is "custom")
2022
"proxy": "", # openai使用的代理
2123
# chatgpt模型, 当use_azure_chatgpt为true时,其名称为Azure上model deployment名称
2224
"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文件
23-
"bot_type": "", # 可选配置,使用兼容openai格式的三方服务时候,需填"openai"(历史值"chatGPT"仍兼容)。bot具体名称详见common/const.py文件,如不填根据model名称判断
25+
"bot_type": "", # 可选配置,使用兼容openai格式的三方服务时候,需填"openai"或"custom"(custom模式下切换模型不会自动切换bot_type)。bot具体名称详见common/const.py文件,如不填根据model名称判断
2426
"use_azure_chatgpt": False, # 是否使用azure的chatgpt
2527
"azure_deployment_id": "", # azure 模型部署名称
2628
"azure_api_version": "", # azure api版本

docs/docs.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@
8282
"models/openai",
8383
"models/deepseek",
8484
"models/linkai",
85-
"models/coding-plan"
85+
"models/coding-plan",
86+
"models/custom"
8687
]
8788
}
8889
]
@@ -257,7 +258,8 @@
257258
"en/models/openai",
258259
"en/models/deepseek",
259260
"en/models/linkai",
260-
"en/models/coding-plan"
261+
"en/models/coding-plan",
262+
"en/models/custom"
261263
]
262264
}
263265
]
@@ -432,7 +434,8 @@
432434
"ja/models/openai",
433435
"ja/models/deepseek",
434436
"ja/models/linkai",
435-
"ja/models/coding-plan"
437+
"ja/models/coding-plan",
438+
"ja/models/custom"
436439
]
437440
}
438441
]

docs/en/models/custom.mdx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
title: Custom
3+
description: Custom provider for third-party APIs and local models
4+
---
5+
6+
For models accessed via OpenAI-compatible APIs, such as:
7+
8+
- **Third-party API proxies**: Use a unified API Base to call multiple models
9+
- **Local models**: Models deployed locally via Ollama, vLLM, LocalAI, etc.
10+
- **Private deployments**: Self-hosted model services within your organization
11+
12+
<Note>
13+
Unlike the `openai` provider, switching models under the Custom provider will not auto-switch the provider type. Your custom API address is always preserved.
14+
</Note>
15+
16+
## Configuration
17+
18+
### Third-party API Proxy
19+
20+
```json
21+
{
22+
"bot_type": "custom",
23+
"model": "deepseek-chat",
24+
"custom_api_key": "YOUR_API_KEY",
25+
"custom_api_base": "https://{your-proxy.com}/v1"
26+
}
27+
```
28+
29+
| Parameter | Description |
30+
| --- | --- |
31+
| `bot_type` | Must be set to `custom` |
32+
| `model` | Model name, any model supported by your proxy service |
33+
| `custom_api_key` | API key provided by your proxy service |
34+
| `custom_api_base` | API base URL, must be OpenAI-compatible |
35+
36+
### Local Models
37+
38+
Local models typically don't require an API key — just set the API base:
39+
40+
```json
41+
{
42+
"bot_type": "custom",
43+
"model": "qwen3.5:27b",
44+
"custom_api_base": "http://localhost:11434/v1"
45+
}
46+
```
47+
48+
Common local deployment tools and their default addresses:
49+
50+
| Tool | Default API Base |
51+
| --- | --- |
52+
| [Ollama](https://ollama.com) | `http://localhost:11434/v1` |
53+
| [vLLM](https://docs.vllm.ai) | `http://localhost:8000/v1` |
54+
| [LocalAI](https://localai.io) | `http://localhost:8080/v1` |
55+
56+
## Switching Models
57+
58+
Under the Custom provider, switching models only changes `model` without affecting `bot_type` or the API address:
59+
60+
```
61+
/config model qwen3.5:27b
62+
```

docs/ja/models/custom.mdx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
title: カスタム
3+
description: サードパーティAPIやローカルモデル向けのカスタムプロバイダー設定
4+
---
5+
6+
OpenAI互換プロトコルでアクセスするモデルサービスに適用します:
7+
8+
- **サードパーティAPIプロキシ**:統一APIベースで複数モデルを呼び出し
9+
- **ローカルモデル**:Ollama、vLLM、LocalAIなどでローカルにデプロイされたモデル
10+
- **プライベートデプロイ**:組織内でホストされたモデルサービス
11+
12+
<Note>
13+
`openai` プロバイダーとの違い:カスタムプロバイダーでは `/config model` でモデルを切り替えてもプロバイダータイプは自動切り替えされず、カスタムAPIアドレスが常に保持されます。
14+
</Note>
15+
16+
## 設定方法
17+
18+
### サードパーティAPIプロキシ
19+
20+
```json
21+
{
22+
"bot_type": "custom",
23+
"model": "deepseek-chat",
24+
"custom_api_key": "YOUR_API_KEY",
25+
"custom_api_base": "https://{your-proxy.com}/v1"
26+
}
27+
```
28+
29+
| パラメータ | 説明 |
30+
| --- | --- |
31+
| `bot_type` | `custom` に設定必須 |
32+
| `model` | モデル名、プロキシサービスがサポートする任意のモデル名 |
33+
| `custom_api_key` | プロキシサービスが提供するAPIキー |
34+
| `custom_api_base` | APIアドレス、OpenAI互換プロトコルが必要 |
35+
36+
### ローカルモデル
37+
38+
ローカルモデルは通常APIキー不要で、APIベースのみ設定します:
39+
40+
```json
41+
{
42+
"bot_type": "custom",
43+
"model": "qwen3.5:27b",
44+
"custom_api_base": "http://localhost:11434/v1"
45+
}
46+
```
47+
48+
一般的なローカルデプロイツールとデフォルトアドレス:
49+
50+
| ツール | デフォルトAPIベース |
51+
| --- | --- |
52+
| [Ollama](https://ollama.com) | `http://localhost:11434/v1` |
53+
| [vLLM](https://docs.vllm.ai) | `http://localhost:8000/v1` |
54+
| [LocalAI](https://localai.io) | `http://localhost:8080/v1` |
55+
56+
## モデル切り替え
57+
58+
カスタムプロバイダーではモデル切り替え時に `model` のみ変更され、`bot_type` やAPIアドレスは変わりません:
59+
60+
```
61+
/config model qwen3.5:27b
62+
```

docs/models/custom.mdx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
title: 自定义
3+
description: 自定义厂商配置,适用于第三方 API 代理和本地模型
4+
---
5+
6+
适用于通过 OpenAI 兼容协议接入的第三方模型服务或本地部署的模型,例如:
7+
8+
- **第三方 API 代理**:使用统一的 API Base 调用多种模型
9+
- **本地模型**:通过 Ollama、vLLM、LocalAI 等工具在本地部署的模型
10+
- **私有化部署**:企业内部部署的模型服务
11+
12+
<Note>
13+
`openai` 厂商的区别:选择自定义厂商后,通过 `/config model` 切换模型时,不会自动切换厂商类型,始终使用自定义的 API 地址。
14+
</Note>
15+
16+
## 配置方式
17+
18+
### 第三方 API 代理
19+
20+
```json
21+
{
22+
"bot_type": "custom",
23+
"model": "deepseek-chat",
24+
"custom_api_key": "YOUR_API_KEY",
25+
"custom_api_base": "https://{your-proxy.com}/v1"
26+
}
27+
```
28+
29+
| 参数 | 说明 |
30+
| --- | --- |
31+
| `bot_type` | 必须设为 `custom` |
32+
| `model` | 模型名称,可填写代理服务支持的任意模型名 |
33+
| `custom_api_key` | API 密钥,由代理服务提供 |
34+
| `custom_api_base` | API 地址,由代理服务提供,需兼容 OpenAI 协议 |
35+
36+
### 本地模型
37+
38+
本地模型通常不需要 API Key,只需填写 API Base 即可:
39+
40+
```json
41+
{
42+
"bot_type": "custom",
43+
"model": "qwen3.5:27b",
44+
"custom_api_base": "http://localhost:11434/v1"
45+
}
46+
```
47+
48+
常见的本地部署工具及默认地址:
49+
50+
| 工具 | 默认 API Base |
51+
| --- | --- |
52+
| [Ollama](https://ollama.com) | `http://localhost:11434/v1` |
53+
| [vLLM](https://docs.vllm.ai) | `http://localhost:8000/v1` |
54+
| [LocalAI](https://localai.io) | `http://localhost:8080/v1` |
55+
56+
## 切换模型
57+
58+
自定义厂商下切换模型时,只会修改 `model`,不会改变 `bot_type` 和 API 地址:
59+
60+
```
61+
/config model qwen3.5:27b
62+
```

docs/models/index.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ CowAgent 支持国内外主流厂商的大语言模型,模型接口实现在
5353
<Card title="LinkAI" href="/models/linkai">
5454
多模型统一接口 + 知识库
5555
</Card>
56+
<Card title="自定义" href="/models/custom">
57+
第三方代理、本地模型等
58+
</Card>
5659
</CardGroup>
5760

5861

0 commit comments

Comments
 (0)