mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 08:00:53 +00:00
feat: agent model account support xiaomi (#12242)
https://github.com/1Panel-dev/1Panel/issues/12220
This commit is contained in:
@@ -114,6 +114,19 @@ var catalog = map[string]Meta{
|
||||
{ID: "minimax/MiniMax-M2.5-highspeed", Name: "MiniMax M2.5 highspeed"},
|
||||
},
|
||||
},
|
||||
"xiaomi": {
|
||||
Key: "xiaomi",
|
||||
DisplayName: "Xiaomi",
|
||||
Sort: 46,
|
||||
DefaultBaseURL: "https://api.xiaomimimo.com/anthropic",
|
||||
EnvKey: "XIAOMI_API_KEY",
|
||||
Models: []Model{
|
||||
{ID: "xiaomi/mimo-v2-pro", Name: "Xiaomi MiMo V2 Pro", ContextWindow: 262144, MaxTokens: 8192, Reasoning: false, Input: []string{"text"}},
|
||||
{ID: "xiaomi/mimo-v2-omni", Name: "Xiaomi MiMo V2 Omni", ContextWindow: 262144, MaxTokens: 8192, Reasoning: false, Input: []string{"text"}},
|
||||
{ID: "xiaomi/mimo-v2-tts", Name: "Xiaomi MiMo V2 TTS", ContextWindow: 262144, MaxTokens: 8192, Reasoning: false, Input: []string{"text"}},
|
||||
{ID: "xiaomi/mimo-v2-flash", Name: "Xiaomi MiMo V2 Flash", ContextWindow: 262144, MaxTokens: 8192, Reasoning: false, Input: []string{"text"}},
|
||||
},
|
||||
},
|
||||
"kimi": {
|
||||
Key: "kimi",
|
||||
DisplayName: "Kimi (CN)",
|
||||
|
||||
@@ -34,6 +34,8 @@ func BuildOpenClawPatch(provider, modelName, apiType string, maxTokens, contextW
|
||||
return buildArkPatch(modelID, maxTokens, contextWindow, baseURL, apiKey), nil
|
||||
case "minimax":
|
||||
return buildMiniMaxPatch(modelID, baseURL, apiKey), nil
|
||||
case "xiaomi":
|
||||
return buildXiaomiPatch(modelID, baseURL, apiKey), nil
|
||||
case "custom", "vllm":
|
||||
return buildCustomPatch(provider, modelName, apiType, maxTokens, contextWindow, baseURL, apiKey), nil
|
||||
case "ollama":
|
||||
@@ -142,6 +144,22 @@ func buildMiniMaxPatch(modelID, baseURL, apiKey string) *OpenClawPatch {
|
||||
}
|
||||
}
|
||||
|
||||
func buildXiaomiPatch(modelID, baseURL, apiKey string) *OpenClawPatch {
|
||||
normalizedID := strings.TrimSpace(modelID)
|
||||
return &OpenClawPatch{
|
||||
PrimaryModel: "xiaomi/" + normalizedID,
|
||||
Models: providerModels("xiaomi", strings.TrimSpace(apiKey), firstNonEmpty(strings.TrimSpace(baseURL), "https://api.xiaomimimo.com/anthropic"), "anthropic-messages", map[string]interface{}{
|
||||
"id": normalizedID,
|
||||
"name": normalizedID,
|
||||
"reasoning": false,
|
||||
"input": []string{"text"},
|
||||
"contextWindow": 262144,
|
||||
"maxTokens": 8192,
|
||||
"cost": map[string]interface{}{},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func buildCustomPatch(provider, modelName, apiType string, maxTokens, contextWindow int, baseURL, apiKey string) *OpenClawPatch {
|
||||
customModelID := normalizeCustomModel(modelName)
|
||||
return &OpenClawPatch{
|
||||
|
||||
@@ -142,6 +142,27 @@ func BuildVerifyRequest(provider, baseURL, apiKey string) VerifyRequest {
|
||||
}},
|
||||
}},
|
||||
})
|
||||
case "xiaomi":
|
||||
request.Method = http.MethodPost
|
||||
headers["x-api-key"] = apiKey
|
||||
headers["anthropic-version"] = "2023-06-01"
|
||||
headers["Content-Type"] = "application/json"
|
||||
if strings.Contains(base, "/v1") {
|
||||
request.URL = base + "/messages"
|
||||
} else {
|
||||
request.URL = base + "/v1/messages"
|
||||
}
|
||||
request.Body = mustJSON(map[string]interface{}{
|
||||
"model": "mimo-v2-flash",
|
||||
"max_tokens": 1,
|
||||
"messages": []map[string]interface{}{{
|
||||
"role": "user",
|
||||
"content": []map[string]string{{
|
||||
"type": "text",
|
||||
"text": "test",
|
||||
}},
|
||||
}},
|
||||
})
|
||||
default:
|
||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
||||
if strings.Contains(base, "/v1") {
|
||||
|
||||
@@ -81,7 +81,7 @@ func verifyResolvedAgentAccount(input resolvedAgentAccountVerification) error {
|
||||
}
|
||||
|
||||
func resolveAgentAccountAPIType(provider, apiType, fallbackAPIType string) (string, error) {
|
||||
if provider == "minimax" {
|
||||
if provider == "minimax" || provider == "xiaomi" {
|
||||
return "anthropic-messages", nil
|
||||
}
|
||||
resolvedAPIType := normalizeAPIType(apiType)
|
||||
@@ -1919,6 +1919,8 @@ func fixedProviderBaseURL(provider string) (string, bool) {
|
||||
return providerDefaultBaseURL(provider)
|
||||
case "minimax":
|
||||
return providerDefaultBaseURL(provider)
|
||||
case "xiaomi":
|
||||
return providerDefaultBaseURL(provider)
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
|
||||
@@ -694,8 +694,6 @@ const message = {
|
||||
accountModelsRequired: 'Configure at least one model',
|
||||
accountModelsDuplicate: 'Duplicate models exist in the catalog',
|
||||
modelPool: 'Model Pool',
|
||||
modelPoolHelper:
|
||||
'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',
|
||||
modelInputTypes: 'Input Types',
|
||||
reasoning: 'Reasoning Model',
|
||||
token: 'Token',
|
||||
|
||||
@@ -702,8 +702,6 @@ const message = {
|
||||
accountModelsRequired: 'Configure at least one model',
|
||||
accountModelsDuplicate: 'Duplicate models exist in the catalog',
|
||||
modelPool: 'Model Pool',
|
||||
modelPoolHelper:
|
||||
'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',
|
||||
modelInputTypes: 'Input Types',
|
||||
reasoning: 'Reasoning Model',
|
||||
token: 'Token',
|
||||
|
||||
@@ -695,8 +695,6 @@ const message = {
|
||||
accountModelsRequired: 'Configure at least one model',
|
||||
accountModelsDuplicate: 'Duplicate models exist in the catalog',
|
||||
modelPool: 'Model Pool',
|
||||
modelPoolHelper:
|
||||
'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',
|
||||
modelInputTypes: 'Input Types',
|
||||
reasoning: 'Reasoning Model',
|
||||
token: 'トークン',
|
||||
|
||||
@@ -687,8 +687,6 @@ const message = {
|
||||
accountModelsRequired: 'Configure at least one model',
|
||||
accountModelsDuplicate: 'Duplicate models exist in the catalog',
|
||||
modelPool: 'Model Pool',
|
||||
modelPoolHelper:
|
||||
'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',
|
||||
modelInputTypes: 'Input Types',
|
||||
reasoning: 'Reasoning Model',
|
||||
token: '토큰',
|
||||
|
||||
@@ -702,8 +702,6 @@ const message = {
|
||||
accountModelsRequired: 'Configure at least one model',
|
||||
accountModelsDuplicate: 'Duplicate models exist in the catalog',
|
||||
modelPool: 'Model Pool',
|
||||
modelPoolHelper:
|
||||
'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',
|
||||
modelInputTypes: 'Input Types',
|
||||
reasoning: 'Reasoning Model',
|
||||
token: 'Token',
|
||||
|
||||
@@ -697,8 +697,6 @@ const message = {
|
||||
accountModelsRequired: 'Configure at least one model',
|
||||
accountModelsDuplicate: 'Duplicate models exist in the catalog',
|
||||
modelPool: 'Model Pool',
|
||||
modelPoolHelper:
|
||||
'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',
|
||||
modelInputTypes: 'Input Types',
|
||||
reasoning: 'Reasoning Model',
|
||||
token: 'Token',
|
||||
|
||||
@@ -694,8 +694,6 @@ const message = {
|
||||
accountModelsRequired: 'Configure at least one model',
|
||||
accountModelsDuplicate: 'Duplicate models exist in the catalog',
|
||||
modelPool: 'Model Pool',
|
||||
modelPoolHelper:
|
||||
'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',
|
||||
modelInputTypes: 'Input Types',
|
||||
reasoning: 'Reasoning Model',
|
||||
token: 'Токен',
|
||||
|
||||
@@ -698,8 +698,6 @@ const message = {
|
||||
accountModelsRequired: 'Configure at least one model',
|
||||
accountModelsDuplicate: 'Duplicate models exist in the catalog',
|
||||
modelPool: 'Model Pool',
|
||||
modelPoolHelper:
|
||||
'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',
|
||||
modelInputTypes: 'Input Types',
|
||||
reasoning: 'Reasoning Model',
|
||||
token: 'Token',
|
||||
|
||||
@@ -662,7 +662,6 @@ const message = {
|
||||
accountModelsRequired: '請至少配置一個模型',
|
||||
accountModelsDuplicate: '模型池中存在重複模型,請檢查後重試',
|
||||
modelPool: '模型池',
|
||||
modelPoolHelper: '在此維護該模型帳號的模型池,建立智能體與 OpenClaw 模型切換都會使用這些模型',
|
||||
modelInputTypes: '輸入類型',
|
||||
reasoning: '推理模型',
|
||||
token: 'Token',
|
||||
|
||||
@@ -661,7 +661,6 @@ const message = {
|
||||
accountModelsRequired: '请至少配置一个模型',
|
||||
accountModelsDuplicate: '模型池中存在重复模型,请检查后重试',
|
||||
modelPool: '模型池',
|
||||
modelPoolHelper: '在这里维护该模型账号的模型池,智能体创建和 OpenClaw 模型切换都会使用这些模型',
|
||||
modelInputTypes: '输入类型',
|
||||
reasoning: '推理模型',
|
||||
token: 'Token',
|
||||
|
||||
@@ -115,7 +115,7 @@ const headerTitle = computed(() =>
|
||||
|
||||
const showInitialModel = computed(() => !form.id && initialModelProviders.includes(form.provider));
|
||||
const apiTypeOptions = computed(() => {
|
||||
if (form.provider === 'minimax') {
|
||||
if (form.provider === 'minimax' || form.provider === 'xiaomi') {
|
||||
return ['anthropic-messages'];
|
||||
}
|
||||
if (form.provider === 'custom' || form.provider === 'vllm') {
|
||||
@@ -176,7 +176,7 @@ const resetInitialModel = () => {
|
||||
};
|
||||
|
||||
const normalizeProviderAPIType = (provider: string, apiType?: string) => {
|
||||
if (provider === 'minimax') {
|
||||
if (provider === 'minimax' || provider === 'xiaomi') {
|
||||
return 'anthropic-messages';
|
||||
}
|
||||
if (provider === 'ollama') {
|
||||
|
||||
@@ -2,13 +2,6 @@
|
||||
<DrawerPro v-model="open" :header="headerTitle" size="large" @close="handleClose">
|
||||
<template #content>
|
||||
<div v-loading="loading">
|
||||
<el-alert
|
||||
:title="$t('aiTools.agents.modelPoolHelper')"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-4"
|
||||
/>
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" @click="openCreate">
|
||||
{{ $t('commons.button.add') }}
|
||||
@@ -45,21 +38,7 @@
|
||||
<DrawerPro v-model="editorOpen" :header="editorTitle" size="normal" @close="handleEditorClose">
|
||||
<el-form ref="formRef" :model="form" label-position="top" :rules="rules" v-loading="saving">
|
||||
<el-form-item :label="$t('aiTools.model.model')" prop="id">
|
||||
<el-select
|
||||
v-model="form.id"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
:reserve-keyword="false"
|
||||
@change="handleModelIDChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="model in providerModelOptions"
|
||||
:key="model.id"
|
||||
:label="model.name"
|
||||
:value="model.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input v-model="form.id" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input v-model="form.name" />
|
||||
@@ -99,14 +78,12 @@ import {
|
||||
createAgentAccountModel,
|
||||
deleteAgentAccountModel,
|
||||
getAgentAccountModels,
|
||||
getAgentProviders,
|
||||
updateAgentAccountModel,
|
||||
} from '@/api/modules/ai';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import i18n from '@/lang';
|
||||
import { MsgError } from '@/utils/message';
|
||||
import { getAgentProviderDisplayName } from '@/utils/agent';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
|
||||
const emit = defineEmits(['updated']);
|
||||
@@ -116,9 +93,7 @@ const editorOpen = ref(false);
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const formRef = ref<FormInstance>();
|
||||
const providerModels = ref<Record<string, AI.ProviderModelInfo[]>>({});
|
||||
const items = ref<AI.AgentAccountModel[]>([]);
|
||||
const { isIntl } = useGlobalStore();
|
||||
|
||||
const account = reactive({
|
||||
id: 0,
|
||||
@@ -145,8 +120,6 @@ const editorTitle = computed(() =>
|
||||
form.recordId ? i18n.global.t('commons.button.edit') : i18n.global.t('commons.button.add'),
|
||||
);
|
||||
|
||||
const providerModelOptions = computed(() => providerModels.value[account.provider] || []);
|
||||
|
||||
const rules = reactive({
|
||||
id: [Rules.requiredInput],
|
||||
name: [Rules.requiredInput],
|
||||
@@ -173,20 +146,6 @@ const buildModelItem = (model?: Partial<AI.AgentAccountModel>): AI.AgentAccountM
|
||||
input: Array.from(new Set((model?.input || []).filter((value) => value === 'text' || value === 'image'))),
|
||||
});
|
||||
|
||||
const loadProviders = async () => {
|
||||
if (Object.keys(providerModels.value).length > 0) {
|
||||
return;
|
||||
}
|
||||
const res = await getAgentProviders();
|
||||
const data = res.data || [];
|
||||
const blockedProviders = new Set(['ark-coding-plan', 'bailian-coding-plan']);
|
||||
const filteredData = isIntl.value ? data.filter((item) => !blockedProviders.has(item.provider)) : data;
|
||||
providerModels.value = filteredData.reduce((acc, item) => {
|
||||
acc[item.provider] = item.models || [];
|
||||
return acc;
|
||||
}, {} as Record<string, AI.ProviderModelInfo[]>);
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
if (!account.id) {
|
||||
items.value = [];
|
||||
@@ -213,15 +172,6 @@ const resetForm = () => {
|
||||
|
||||
const openCreate = () => {
|
||||
resetForm();
|
||||
const firstModel = providerModelOptions.value[0];
|
||||
if (firstModel) {
|
||||
form.id = firstModel.id;
|
||||
form.name = firstModel.name || firstModel.id;
|
||||
form.contextWindow = firstModel.contextWindow || 128000;
|
||||
form.maxTokens = firstModel.maxTokens || 8192;
|
||||
form.reasoning = firstModel.reasoning;
|
||||
form.input = firstModel.input?.length ? [...firstModel.input] : ['text'];
|
||||
}
|
||||
editorOpen.value = true;
|
||||
};
|
||||
|
||||
@@ -230,21 +180,6 @@ const openEdit = (row: AI.AgentAccountModel) => {
|
||||
editorOpen.value = true;
|
||||
};
|
||||
|
||||
const handleModelIDChange = (value: string) => {
|
||||
form.id = String(value || '').trim();
|
||||
const matched = providerModelOptions.value.find((item) => item.id === form.id);
|
||||
if (!matched) {
|
||||
return;
|
||||
}
|
||||
if (!form.name) {
|
||||
form.name = matched.name || matched.id;
|
||||
}
|
||||
form.contextWindow = matched.contextWindow || form.contextWindow || 128000;
|
||||
form.maxTokens = matched.maxTokens || form.maxTokens || 8192;
|
||||
form.reasoning = matched.reasoning;
|
||||
form.input = matched.input?.length ? [...matched.input] : ['text'];
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
if (!account.id || !formRef.value) {
|
||||
return;
|
||||
@@ -319,7 +254,6 @@ const openDrawer = async (row: AI.AgentAccountItem) => {
|
||||
account.name = row.name;
|
||||
account.provider = row.provider;
|
||||
open.value = true;
|
||||
await loadProviders();
|
||||
await search();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user