mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 16:00:51 +00:00
feat(ai-agents): add anthropic-messages support for custom account apiType (#12013)
This commit is contained in:
@@ -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{
|
||||
|
||||
+32
-14
@@ -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
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -708,7 +708,6 @@ const message = {
|
||||
saveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
customModelHelper: 'カスタムモデルアカウントでは、モデル名は必ず custom/ で始めてください',
|
||||
customProviderHelper: 'カスタムモデルプロバイダーでは、アカウントの有効性を検証しません',
|
||||
},
|
||||
model: {
|
||||
|
||||
@@ -703,7 +703,6 @@ const message = {
|
||||
saveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
customModelHelper: '사용자 정의 모델 계정의 모델명은 반드시 custom/ 로 시작해야 합니다',
|
||||
customProviderHelper: '사용자 정의 모델 공급자는 계정 사용 가능 여부를 검증하지 않습니다',
|
||||
},
|
||||
model: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -711,7 +711,6 @@ const message = {
|
||||
saveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
customModelHelper: 'Для пользовательской учетной записи модели имя модели должно начинаться с custom/',
|
||||
customProviderHelper: 'Для пользовательского провайдера модели доступность учетной записи не проверяется',
|
||||
},
|
||||
model: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -694,7 +694,6 @@ const message = {
|
||||
saveSuccess: '保存成功',
|
||||
pairingCodeRequired: '請輸入配對碼',
|
||||
pairingApproveSuccess: '配對成功',
|
||||
customModelHelper: '自訂模型帳號,模型需固定以 custom/ 開頭',
|
||||
customProviderHelper: '自訂模型供應商不驗證帳號是否可用',
|
||||
},
|
||||
model: {
|
||||
|
||||
@@ -678,7 +678,6 @@ const message = {
|
||||
feishuSaveSuccess: '保存成功',
|
||||
pairingCodeRequired: '请输入配对码',
|
||||
pairingApproveSuccess: '配对成功',
|
||||
customModelHelper: '自定义模型账号,模型固定以 custom/ 开头',
|
||||
customProviderHelper: '自定义模型供应商不验证账号是否可用',
|
||||
},
|
||||
model: {
|
||||
|
||||
@@ -60,9 +60,6 @@
|
||||
<el-select v-else v-model="form.model" filterable @change="handleModelChange">
|
||||
<el-option v-for="item in filteredModels" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<span class="input-help" v-if="form.provider == 'custom'">
|
||||
{{ $t('aiTools.agents.customModelHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('aiTools.agents.baseUrl')" v-if="form.accountId" prop="baseURL">
|
||||
<el-input v-model="form.baseURL" disabled />
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
<el-select v-else v-model="form.model" filterable>
|
||||
<el-option v-for="item in modelOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<span class="input-help" v-if="provdier == 'custom'">
|
||||
{{ $t('aiTools.agents.customModelHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="saveModel">
|
||||
@@ -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;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<el-select v-model="form.apiType">
|
||||
<el-option label="openai-completions" value="openai-completions" />
|
||||
<el-option label="openai-responses" value="openai-responses" />
|
||||
<el-option label="anthropic-messages" value="anthropic-messages" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="Max Tokens" prop="maxTokens" v-if="form.provider === 'custom'">
|
||||
|
||||
Reference in New Issue
Block a user