From 8cead06d7ba8d61832a4cd7fa4e266309d76d46e Mon Sep 17 00:00:00 2001
From: CityFun <31820853+zhengkunwang223@users.noreply.github.com>
Date: Mon, 2 Mar 2026 13:43:06 +0800
Subject: [PATCH] feat(ai-agents): add anthropic-messages support for custom
account apiType (#12013)
---
agent/app/provider/catalog.go | 2 +-
agent/app/service/agents.go | 46 +++++++++++++------
frontend/src/lang/modules/en.ts | 1 -
frontend/src/lang/modules/es-es.ts | 1 -
frontend/src/lang/modules/ja.ts | 1 -
frontend/src/lang/modules/ko.ts | 1 -
frontend/src/lang/modules/ms.ts | 1 -
frontend/src/lang/modules/pt-br.ts | 1 -
frontend/src/lang/modules/ru.ts | 1 -
frontend/src/lang/modules/tr.ts | 1 -
frontend/src/lang/modules/zh-Hant.ts | 1 -
frontend/src/lang/modules/zh.ts | 1 -
.../src/views/ai/agents/agent/add/index.vue | 7 +--
.../ai/agents/agent/config/tabs/model.vue | 7 +--
.../src/views/ai/agents/model/add/index.vue | 1 +
15 files changed, 38 insertions(+), 35 deletions(-)
diff --git a/agent/app/provider/catalog.go b/agent/app/provider/catalog.go
index a94c67bd8..624a626b1 100644
--- a/agent/app/provider/catalog.go
+++ b/agent/app/provider/catalog.go
@@ -137,7 +137,7 @@ var catalog = map[string]Meta{
Key: "kimi-coding",
DisplayName: "Kimi Coding",
Sort: 100,
- DefaultBaseURL: "https://api.moonshot.cn/anthropic/v1",
+ DefaultBaseURL: "https://kimi-code.moonshot.cn/api",
EnvKey: "KIMI_API_KEY",
Enabled: true,
Models: []Model{
diff --git a/agent/app/service/agents.go b/agent/app/service/agents.go
index cdc53eda3..680ae3f6e 100644
--- a/agent/app/service/agents.go
+++ b/agent/app/service/agents.go
@@ -94,6 +94,14 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
return nil, buserr.New("ErrAgentApiKeyRequired")
}
apiType, maxTokens, contextWindow := resolveRuntimeParams(provider, account.APIType, account.MaxTokens, account.ContextWindow)
+ runtimeModel := strings.TrimSpace(req.Model)
+ if provider == "custom" {
+ primaryID := customPrimaryModelID(req.Model)
+ if primaryID == "" {
+ primaryID = normalizeCustomModel(req.Model)
+ }
+ runtimeModel = "custom/" + primaryID
+ }
if err := checkPortExist(req.WebUIPort); err != nil {
return nil, err
}
@@ -121,7 +129,7 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
}
params := map[string]interface{}{
"PROVIDER": provider,
- "MODEL": req.Model,
+ "MODEL": runtimeModel,
"API_TYPE": apiType,
"MAX_TOKENS": maxTokens,
"CONTEXT_WINDOW": contextWindow,
@@ -291,7 +299,7 @@ func (a AgentService) UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error
if modelName == "" {
return buserr.New("ErrAgentProviderMismatch")
}
- if !strings.HasPrefix(modelName, provider+"/") {
+ if provider != "custom" && !strings.HasPrefix(modelName, provider+"/") {
return buserr.New("ErrAgentProviderMismatch")
}
baseURL := strings.TrimSpace(account.BaseURL)
@@ -526,7 +534,7 @@ func (a AgentService) VerifyAccount(req dto.AgentAccountVerifyReq) error {
if provider == "ollama" {
return nil
}
- if provider == "custom" {
+ if provider == "custom" || provider == "kimi-coding" {
return nil
}
return verifyProvider(provider, baseURL, apiKey)
@@ -1412,20 +1420,15 @@ func writeOpenclawConfig(confDir, provider, modelName, apiType string, maxTokens
},
}
} else if provider == "custom" {
- primary := modelName
- if !strings.Contains(primary, "/") {
- primary = "custom/" + strings.TrimSpace(primary)
+ customModelID := normalizeCustomModel(modelName)
+ primaryID := customPrimaryModelID(customModelID)
+ if primaryID == "" {
+ primaryID = customModelID
}
+ primary := "custom/" + primaryID
cfg.Agents.Defaults.Model.Primary = primary
base := strings.TrimSpace(baseURL)
plainKey := strings.TrimSpace(apiKey)
- if !strings.Contains(modelName, "/") {
- modelName = primary
- }
- customModelID := modelID
- if parts := strings.SplitN(modelName, "/", 2); len(parts) == 2 {
- customModelID = parts[1]
- }
useAPIType, useMaxTokens, useContextWindow := resolveRuntimeParams(provider, apiType, maxTokens, contextWindow)
cfg.Models = &modelsConfig{
Mode: "merge",
@@ -1702,6 +1705,21 @@ func normalizeCustomModel(modelName string) string {
return trim
}
+func customPrimaryModelID(modelName string) string {
+ trim := normalizeCustomModel(modelName)
+ if trim == "" {
+ return ""
+ }
+ parts := strings.Split(trim, "/")
+ for i := len(parts) - 1; i >= 0; i-- {
+ part := strings.TrimSpace(parts[i])
+ if part != "" {
+ return part
+ }
+ }
+ return ""
+}
+
func normalizeAPIType(apiType string) string {
trim := strings.ToLower(strings.TrimSpace(apiType))
if trim == "" {
@@ -1712,7 +1730,7 @@ func normalizeAPIType(apiType string) string {
func isSupportedAPIType(apiType string) bool {
switch normalizeAPIType(apiType) {
- case "openai-completions", "openai-responses":
+ case "openai-completions", "openai-responses", "anthropic-messages":
return true
default:
return false
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index 187bdedb7..d4cff108e 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -720,7 +720,6 @@ const message = {
saveSuccess: 'Saved successfully',
pairingCodeRequired: 'Enter pairing code',
pairingApproveSuccess: 'Pairing approved successfully',
- customModelHelper: 'For custom model accounts, model names must start with custom/.',
customProviderHelper: 'Custom model providers do not validate whether the account is available.',
},
model: {
diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts
index a4582a510..cc4255f94 100644
--- a/frontend/src/lang/modules/es-es.ts
+++ b/frontend/src/lang/modules/es-es.ts
@@ -716,7 +716,6 @@ const message = {
saveSuccess: 'Saved successfully',
pairingCodeRequired: 'Please enter pairing code',
pairingApproveSuccess: 'Pairing approved successfully',
- customModelHelper: 'En la cuenta de modelo personalizada, el nombre del modelo debe empezar por custom/',
customProviderHelper: 'En el proveedor de modelo personalizado no se valida si la cuenta está disponible',
},
model: {
diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts
index 557cdd933..187edf912 100644
--- a/frontend/src/lang/modules/ja.ts
+++ b/frontend/src/lang/modules/ja.ts
@@ -708,7 +708,6 @@ const message = {
saveSuccess: 'Saved successfully',
pairingCodeRequired: 'Please enter pairing code',
pairingApproveSuccess: 'Pairing approved successfully',
- customModelHelper: 'カスタムモデルアカウントでは、モデル名は必ず custom/ で始めてください',
customProviderHelper: 'カスタムモデルプロバイダーでは、アカウントの有効性を検証しません',
},
model: {
diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts
index 7f5bbfce4..e821ee927 100644
--- a/frontend/src/lang/modules/ko.ts
+++ b/frontend/src/lang/modules/ko.ts
@@ -703,7 +703,6 @@ const message = {
saveSuccess: 'Saved successfully',
pairingCodeRequired: 'Please enter pairing code',
pairingApproveSuccess: 'Pairing approved successfully',
- customModelHelper: '사용자 정의 모델 계정의 모델명은 반드시 custom/ 로 시작해야 합니다',
customProviderHelper: '사용자 정의 모델 공급자는 계정 사용 가능 여부를 검증하지 않습니다',
},
model: {
diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts
index 860125a24..37d36ca17 100644
--- a/frontend/src/lang/modules/ms.ts
+++ b/frontend/src/lang/modules/ms.ts
@@ -719,7 +719,6 @@ const message = {
saveSuccess: 'Saved successfully',
pairingCodeRequired: 'Please enter pairing code',
pairingApproveSuccess: 'Pairing approved successfully',
- customModelHelper: 'Akaun model tersuai, nama model mesti bermula dengan custom/',
customProviderHelper: 'Penyedia model tersuai tidak mengesahkan sama ada akaun boleh digunakan',
},
model: {
diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts
index fd6f66621..12efb52eb 100644
--- a/frontend/src/lang/modules/pt-br.ts
+++ b/frontend/src/lang/modules/pt-br.ts
@@ -716,7 +716,6 @@ const message = {
saveSuccess: 'Saved successfully',
pairingCodeRequired: 'Please enter pairing code',
pairingApproveSuccess: 'Pairing approved successfully',
- customModelHelper: 'Conta de modelo personalizada: o nome do modelo deve começar com custom/',
customProviderHelper: 'Provedores de modelo personalizados não validam se a conta está disponível',
},
model: {
diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts
index c0026d64f..e0959a799 100644
--- a/frontend/src/lang/modules/ru.ts
+++ b/frontend/src/lang/modules/ru.ts
@@ -711,7 +711,6 @@ const message = {
saveSuccess: 'Saved successfully',
pairingCodeRequired: 'Please enter pairing code',
pairingApproveSuccess: 'Pairing approved successfully',
- customModelHelper: 'Для пользовательской учетной записи модели имя модели должно начинаться с custom/',
customProviderHelper: 'Для пользовательского провайдера модели доступность учетной записи не проверяется',
},
model: {
diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts
index 52d6c0273..1b0d2ec3b 100644
--- a/frontend/src/lang/modules/tr.ts
+++ b/frontend/src/lang/modules/tr.ts
@@ -725,7 +725,6 @@ const message = {
saveSuccess: 'Saved successfully',
pairingCodeRequired: 'Please enter pairing code',
pairingApproveSuccess: 'Pairing approved successfully',
- customModelHelper: 'Özel model hesabında model adı custom/ ile başlamalıdır',
customProviderHelper: 'Özel model sağlayıcısında hesabın kullanılabilirliği doğrulanmaz',
},
model: {
diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts
index f1e78fc9c..22c203065 100644
--- a/frontend/src/lang/modules/zh-Hant.ts
+++ b/frontend/src/lang/modules/zh-Hant.ts
@@ -694,7 +694,6 @@ const message = {
saveSuccess: '保存成功',
pairingCodeRequired: '請輸入配對碼',
pairingApproveSuccess: '配對成功',
- customModelHelper: '自訂模型帳號,模型需固定以 custom/ 開頭',
customProviderHelper: '自訂模型供應商不驗證帳號是否可用',
},
model: {
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index 38120e99b..fd3058ce0 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -678,7 +678,6 @@ const message = {
feishuSaveSuccess: '保存成功',
pairingCodeRequired: '请输入配对码',
pairingApproveSuccess: '配对成功',
- customModelHelper: '自定义模型账号,模型固定以 custom/ 开头',
customProviderHelper: '自定义模型供应商不验证账号是否可用',
},
model: {
diff --git a/frontend/src/views/ai/agents/agent/add/index.vue b/frontend/src/views/ai/agents/agent/add/index.vue
index 5949a10b6..e7d7b9ca3 100644
--- a/frontend/src/views/ai/agents/agent/add/index.vue
+++ b/frontend/src/views/ai/agents/agent/add/index.vue
@@ -60,9 +60,6 @@
-
- {{ $t('aiTools.agents.customModelHelper') }}
-
@@ -256,7 +253,7 @@ const handleAccountChange = () => {
form.maxTokens = selected.maxTokens || 8192;
form.contextWindow = selected.contextWindow || 128000;
if (selected.provider === 'custom' && selected.model && !manualModel.value) {
- form.model = `custom/${selected.model}`;
+ form.model = selected.model;
}
}
setDefaultModel();
@@ -274,7 +271,7 @@ const setDefaultModel = () => {
if (form.provider === 'custom') {
const selected = accountOptions.value.find((item) => item.id === form.accountId);
if (selected?.model && !form.model) {
- form.model = `custom/${selected.model}`;
+ form.model = selected.model;
}
}
};
diff --git a/frontend/src/views/ai/agents/agent/config/tabs/model.vue b/frontend/src/views/ai/agents/agent/config/tabs/model.vue
index b947612b3..768622d2c 100644
--- a/frontend/src/views/ai/agents/agent/config/tabs/model.vue
+++ b/frontend/src/views/ai/agents/agent/config/tabs/model.vue
@@ -15,9 +15,6 @@
-
- {{ $t('aiTools.agents.customModelHelper') }}
-
@@ -96,7 +93,7 @@ const handleAccountChange = () => {
provdier.value = selected.provider;
if (selected.provider === 'custom') {
form.manualModel = true;
- form.model = selected.model ? `custom/${selected.model}` : form.model;
+ form.model = selected.model || form.model;
modelOptions.value = [];
return;
}
@@ -148,7 +145,7 @@ const load = async (agent: AI.AgentItem) => {
form.model = modelOptions.value.length > 0 ? modelOptions.value[0].id : '';
}
if (currentAccount.provider === 'custom' && currentAccount.model && !form.model) {
- form.model = `custom/${currentAccount.model}`;
+ form.model = currentAccount.model;
}
} finally {
loading.value = false;
diff --git a/frontend/src/views/ai/agents/model/add/index.vue b/frontend/src/views/ai/agents/model/add/index.vue
index 56f6d5186..c930d19e4 100644
--- a/frontend/src/views/ai/agents/model/add/index.vue
+++ b/frontend/src/views/ai/agents/model/add/index.vue
@@ -31,6 +31,7 @@
+