feat: OpenClaw supports configuring model Max Tokens (#13818)

This commit is contained in:
CityFun
2026-09-15 18:36:24 +08:00
committed by GitHub
parent 89bd32b6d4
commit 6b20ff0b13
18 changed files with 359 additions and 14 deletions

View File

@@ -162,16 +162,25 @@ type AgentWebsiteBindReq struct {
}
type AgentModelConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
AccountID uint `json:"accountId" validate:"required"`
Model string `json:"model" validate:"required"`
Fallbacks []string `json:"fallbacks"`
AgentID uint `json:"agentId" validate:"required"`
AccountID uint `json:"accountId" validate:"required"`
Model string `json:"model" validate:"required"`
Fallbacks []string `json:"fallbacks"`
Metadata []AgentModelMetadata `json:"metadata" validate:"dive"`
}
type AgentModelConfig struct {
AccountID uint `json:"accountId"`
Model string `json:"model"`
Fallbacks []string `json:"fallbacks"`
AccountID uint `json:"accountId"`
Model string `json:"model"`
Fallbacks []string `json:"fallbacks"`
Metadata []AgentModelMetadata `json:"metadata"`
}
type AgentModelMetadata struct {
Model string `json:"model" validate:"required"`
InputMode string `json:"inputMode" validate:"required,oneof=auto text image"`
ContextWindow int `json:"contextWindow" validate:"min=0"`
MaxTokens int `json:"maxTokens" validate:"min=0"`
}
type AgentHermesChatSessionItem struct {

View File

@@ -936,6 +936,7 @@ func (a AgentService) GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig,
AccountID: agent.AccountID,
Model: model,
Fallbacks: extractOpenclawFallbackModelIDs(conf, account, models, model),
Metadata: extractOpenclawModelMetadata(conf, account, models),
}, nil
}
@@ -967,7 +968,7 @@ func (a AgentService) UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error
if agent.AgentType != constant.AppOpenclaw {
return fmt.Errorf("%s does not support", agent.AgentType)
}
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, req.Fallbacks); err != nil {
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, req.Fallbacks, req.Metadata); err != nil {
return err
}
}
@@ -1684,7 +1685,7 @@ func (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error {
return err
}
fallbacks := extractOpenclawFallbackModelIDs(conf, account, accountModels, selectedAccountModel.ID)
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, fallbacks); err != nil {
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, fallbacks, nil); err != nil {
return err
}
case constant.AppHermesAgent:

View File

@@ -1354,6 +1354,10 @@ func installOpenclawPlugin(mgr *cmd.CommandHelper, containerName, spec, pluginID
} else if slices.Contains(options, "--dangerously-force-unsafe-install") {
args = append(args, "--dangerously-force-unsafe-install")
}
// Source confirmation does not grant the selected channel plugin's capabilities.
if slices.Contains(options, "--accept-capabilities") {
args = append(args, "--accept-capabilities")
}
return mgr.Run("docker", args...)
}

View File

@@ -10,6 +10,7 @@ import (
"net/url"
"path"
"regexp"
"slices"
"strconv"
"strings"
"time"
@@ -737,9 +738,11 @@ type modelProvider struct {
}
type modelEntry struct {
ID string `json:"id"`
Name string `json:"name"`
Input []string `json:"input,omitempty"`
ID string `json:"id"`
Name string `json:"name"`
Input []string `json:"input,omitempty"`
ContextWindow int `json:"contextWindow,omitempty"`
MaxTokens int `json:"maxTokens,omitempty"`
}
func requiresOpenclawProviderModels(provider string) bool {
@@ -767,7 +770,7 @@ type browserConfig struct {
DefaultProfile string `json:"defaultProfile"`
}
func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName, token string, allowedOrigins []string, fallbacks []string) error {
func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName, token string, allowedOrigins []string, fallbacks []string, metadata []dto.AgentModelMetadata) error {
if strings.TrimSpace(confDir) == "" {
return fmt.Errorf("config dir is required")
}
@@ -852,6 +855,7 @@ func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName,
}
conf = initial
} else {
preserveOpenclawModelMetadata(conf, cfg.Models)
if err := applyOpenclawModelsConfig(conf, cfg.Models); err != nil {
return err
}
@@ -906,6 +910,9 @@ func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName,
if allowedOrigins != nil {
setSecurityConfig(conf, dto.AgentSecurityConfig{AllowedOrigins: allowedOrigins})
}
if err := applyOpenclawModelMetadata(conf, account, metadata); err != nil {
return err
}
if err := writeOpenclawConfigRaw(configPath, conf); err != nil {
return err
}
@@ -920,6 +927,144 @@ func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName,
return writeAgentEnvMap(path.Join(confDir, ".env"), envMap, order)
}
func readOpenclawModelsConfig(conf map[string]interface{}) *modelsConfig {
raw, ok := conf["models"]
if !ok {
return nil
}
payload, err := json.Marshal(raw)
if err != nil {
return nil
}
var models modelsConfig
if err := json.Unmarshal(payload, &models); err != nil {
return nil
}
return &models
}
func preserveOpenclawModelMetadata(conf map[string]interface{}, next *modelsConfig) {
current := readOpenclawModelsConfig(conf)
if current == nil || next == nil {
return
}
for providerID, nextProvider := range next.Providers {
currentProvider, ok := current.Providers[providerID]
if !ok {
continue
}
byID := make(map[string]modelEntry, len(currentProvider.Models))
for _, entry := range currentProvider.Models {
byID[entry.ID] = entry
}
for index := range nextProvider.Models {
currentEntry, ok := byID[nextProvider.Models[index].ID]
if !ok {
continue
}
nextProvider.Models[index].Input = currentEntry.Input
nextProvider.Models[index].ContextWindow = currentEntry.ContextWindow
nextProvider.Models[index].MaxTokens = currentEntry.MaxTokens
}
next.Providers[providerID] = nextProvider
}
}
func extractOpenclawModelMetadata(conf map[string]interface{}, account *model.AgentAccount, accountModels []dto.AgentAccountModel) []dto.AgentModelMetadata {
result := make([]dto.AgentModelMetadata, 0, len(accountModels))
configured := readOpenclawModelsConfig(conf)
for _, item := range accountModels {
_, inferred, providerID, _, err := buildOpenclawAccountModelConfig(account, item)
if err != nil {
continue
}
metadata := dto.AgentModelMetadata{Model: item.ID, InputMode: "auto"}
if configured != nil {
for _, entry := range configured.Providers[providerID].Models {
if entry.ID != inferred.ID {
continue
}
metadata.ContextWindow = entry.ContextWindow
metadata.MaxTokens = entry.MaxTokens
if len(entry.Input) > 0 && !slices.Equal(entry.Input, inferred.Input) {
if slices.Contains(entry.Input, "image") {
metadata.InputMode = "image"
} else {
metadata.InputMode = "text"
}
}
break
}
}
result = append(result, metadata)
}
return result
}
func applyOpenclawModelMetadata(conf map[string]interface{}, account *model.AgentAccount, requested []dto.AgentModelMetadata) error {
if len(requested) == 0 {
return nil
}
configured := readOpenclawModelsConfig(conf)
if configured == nil {
return fmt.Errorf("model metadata is not supported for provider %s", account.Provider)
}
accountModels, err := loadAgentAccountModels(account)
if err != nil {
return err
}
available := make(map[string]dto.AgentAccountModel, len(accountModels))
for _, item := range accountModels {
available[item.ID] = item
}
seen := make(map[string]struct{}, len(requested))
for _, metadata := range requested {
item, ok := available[metadata.Model]
if !ok {
return buserr.New("ErrAgentModelNotInAccount")
}
if _, ok := seen[metadata.Model]; ok {
return fmt.Errorf("duplicate model metadata: %s", metadata.Model)
}
seen[metadata.Model] = struct{}{}
_, inferred, providerID, _, err := buildOpenclawAccountModelConfig(account, item)
if err != nil {
return err
}
provider := configured.Providers[providerID]
found := false
for index := range provider.Models {
if provider.Models[index].ID != inferred.ID {
continue
}
found = true
provider.Models[index].ContextWindow = metadata.ContextWindow
provider.Models[index].MaxTokens = metadata.MaxTokens
switch metadata.InputMode {
case "auto":
provider.Models[index].Input = inferred.Input
case "text":
provider.Models[index].Input = []string{"text"}
case "image":
provider.Models[index].Input = []string{"text", "image"}
default:
return fmt.Errorf("unsupported model input mode: %s", metadata.InputMode)
}
break
}
if !found {
return buserr.New("ErrAgentModelNotInAccount")
}
configured.Providers[providerID] = provider
}
modelsMap, err := structToMap(configured)
if err != nil {
return err
}
conf["models"] = modelsMap
return nil
}
func resolveOpenclawFallbackModels(account *model.AgentAccount, primaryModel string, fallbackIDs []string) ([]string, error) {
accountModels, err := loadAgentAccountModels(account)
if err != nil {
@@ -1041,7 +1186,7 @@ func prepareOpenclawInstallFiles(appInstall *model.AppInstall, account *model.Ag
return fmt.Errorf("app install is required")
}
confDir := path.Join(appInstall.GetPath(), "data", "conf")
if err := writeOpenclawConfig(confDir, account, modelName, token, allowedOrigins, nil); err != nil {
if err := writeOpenclawConfig(confDir, account, modelName, token, allowedOrigins, nil, nil); err != nil {
return err
}
dataDir := path.Join(appInstall.GetPath(), "data")

View File

@@ -426,12 +426,21 @@ export namespace AI {
accountId: number;
model: string;
fallbacks: string[];
metadata: AgentModelMetadata[];
}
export interface AgentModelConfig {
accountId: number;
model: string;
fallbacks: string[];
metadata: AgentModelMetadata[];
}
export interface AgentModelMetadata {
model: string;
inputMode: 'auto' | 'text' | 'image';
contextWindow: number;
maxTokens: number;
}
export interface AgentHermesChatSessionItem {

View File

@@ -758,6 +758,14 @@ const message = {
addFallbackModel: 'Add Fallback Model',
fallbackModelsEmpty: 'No fallback models configured',
modelInputTypes: 'Input Types',
modelMetadata: 'Model Capabilities',
modelMetadataHelper:
'These settings apply only to this OpenClaw instance. Leave values empty to use OpenClaw or provider defaults.',
inputModeAuto: 'Auto detect',
inputModeText: 'Text only',
inputModeImage: 'Text and images',
contextWindow: 'Context Window',
useModelDefault: 'Use model default',
reasoning: 'Reasoning Model',
verified: 'Verified',
verifySkipped: 'No verification',

View File

@@ -764,6 +764,14 @@ const message = {
addFallbackModel: 'Agregar modelo de respaldo',
fallbackModelsEmpty: 'No hay modelos de respaldo configurados',
modelInputTypes: 'Tipos de entrada',
modelMetadata: 'Capacidades del modelo',
modelMetadataHelper:
'Estos ajustes solo se aplican a esta instancia de OpenClaw. Déjelos vacíos para usar los valores predeterminados de OpenClaw o del proveedor.',
inputModeAuto: 'Detectar automáticamente',
inputModeText: 'Solo texto',
inputModeImage: 'Texto e imágenes',
contextWindow: 'Ventana de contexto',
useModelDefault: 'Usar valor predeterminado del modelo',
reasoning: 'Modelo de razonamiento',
verified: 'Verificado',
verifySkipped: 'Sin verificación',

View File

@@ -755,6 +755,14 @@ const message = {
addFallbackModel: 'افزودن مدل جایگزین',
fallbackModelsEmpty: 'هیچ مدل جایگزینی پیکربندی نشده است',
modelInputTypes: 'انواع ورودی',
modelMetadata: 'قابلیت‌های مدل',
modelMetadataHelper:
'این تنظیمات فقط برای نمونه فعلی OpenClaw اعمال می‌شوند. برای استفاده از پیش‌فرض OpenClaw یا ارائه‌دهنده، فیلدها را خالی بگذارید.',
inputModeAuto: 'تشخیص خودکار',
inputModeText: 'فقط متن',
inputModeImage: 'متن و تصویر',
contextWindow: 'پنجره زمینه',
useModelDefault: 'استفاده از پیش‌فرض مدل',
reasoning: 'مدل استدلالی',
verified: 'تأیید شده',
verifySkipped: 'بدون تأیید',

View File

@@ -759,6 +759,14 @@ const message = {
addFallbackModel: 'フォールバックモデルを追加',
fallbackModelsEmpty: 'フォールバックモデルは未設定です',
modelInputTypes: '入力タイプ',
modelMetadata: 'モデル機能設定',
modelMetadataHelper:
'この設定は現在の OpenClaw にのみ適用されます。空欄の場合は OpenClaw またはプロバイダーの既定値を使用します。',
inputModeAuto: '自動判定',
inputModeText: 'テキストのみ',
inputModeImage: 'テキストと画像',
contextWindow: 'コンテキストウィンドウ',
useModelDefault: 'モデルの既定値を使用',
reasoning: '推論モデル',
verified: '検証済み',
verifySkipped: '検証なし',

View File

@@ -749,6 +749,14 @@ const message = {
addFallbackModel: '폴백 모델 추가',
fallbackModelsEmpty: '구성된 폴백 모델이 없습니다',
modelInputTypes: '입력 유형',
modelMetadata: '모델 기능 설정',
modelMetadataHelper:
'이 설정은 현재 OpenClaw에만 적용됩니다. 비워 두면 OpenClaw 또는 공급자의 기본값을 사용합니다.',
inputModeAuto: '자동 감지',
inputModeText: '텍스트만',
inputModeImage: '텍스트 및 이미지',
contextWindow: '컨텍스트 창',
useModelDefault: '모델 기본값 사용',
reasoning: '추론 모델',
verified: '검증됨',
verifySkipped: '검증 안 함',

View File

@@ -750,6 +750,14 @@ const message = {
addFallbackModel: 'ເພີ່ມໂມເດວສຳຮອງ',
fallbackModelsEmpty: 'ຍັງບໍ່ໄດ້ຕັ້ງຄ່າໂມເດວສຳຮອງ',
modelInputTypes: 'ປະເພດຂໍ້ມູນຂາເຂົ້າ',
modelMetadata: 'ຄວາມສາມາດຂອງໂມເດວ',
modelMetadataHelper:
'ການຕັ້ງຄ່ານີ້ໃຊ້ກັບ OpenClaw ປັດຈຸບັນເທົ່ານັ້ນ. ປ່ອຍຫວ່າງເພື່ອໃຊ້ຄ່າເລີ່ມຕົ້ນຂອງ OpenClaw ຫຼືຜູ້ໃຫ້ບໍລິການ.',
inputModeAuto: 'ກວດຫາອັດຕະໂນມັດ',
inputModeText: 'ຂໍ້ຄວາມເທົ່ານັ້ນ',
inputModeImage: 'ຂໍ້ຄວາມແລະຮູບພາບ',
contextWindow: 'ຂອບເຂດບໍລິບົດ',
useModelDefault: 'ໃຊ້ຄ່າເລີ່ມຕົ້ນຂອງໂມເດວ',
reasoning: 'ໂມເດວການໃຊ້ເຫດຜົນ',
verified: 'ກວດສອບແລ້ວ',
verifySkipped: 'ບໍ່ມີການກວດສອບ',

View File

@@ -767,6 +767,14 @@ const message = {
addFallbackModel: 'Tambah model fallback',
fallbackModelsEmpty: 'Tiada model fallback dikonfigurasikan',
modelInputTypes: 'Jenis input',
modelMetadata: 'Keupayaan model',
modelMetadataHelper:
'Tetapan ini hanya digunakan pada tika OpenClaw semasa. Biarkan kosong untuk menggunakan nilai lalai OpenClaw atau penyedia.',
inputModeAuto: 'Kesan secara automatik',
inputModeText: 'Teks sahaja',
inputModeImage: 'Teks dan imej',
contextWindow: 'Tetingkap konteks',
useModelDefault: 'Gunakan lalai model',
reasoning: 'Model penaakulan',
verified: 'Disahkan',
verifySkipped: 'Tanpa pengesahan',

View File

@@ -761,6 +761,14 @@ const message = {
addFallbackModel: 'Adicionar modelo de fallback',
fallbackModelsEmpty: 'Nenhum modelo de fallback configurado',
modelInputTypes: 'Tipos de entrada',
modelMetadata: 'Recursos do modelo',
modelMetadataHelper:
'Estas configurações se aplicam somente a esta instância do OpenClaw. Deixe em branco para usar os padrões do OpenClaw ou do provedor.',
inputModeAuto: 'Detectar automaticamente',
inputModeText: 'Somente texto',
inputModeImage: 'Texto e imagens',
contextWindow: 'Janela de contexto',
useModelDefault: 'Usar padrão do modelo',
reasoning: 'Modelo de raciocínio',
verified: 'Verificado',
verifySkipped: 'Sem verificação',

View File

@@ -756,6 +756,14 @@ const message = {
addFallbackModel: 'Добавить резервную модель',
fallbackModelsEmpty: 'Резервные модели не настроены',
modelInputTypes: 'Типы ввода',
modelMetadata: 'Возможности модели',
modelMetadataHelper:
'Эти настройки применяются только к текущему экземпляру OpenClaw. Оставьте поля пустыми, чтобы использовать значения OpenClaw или поставщика.',
inputModeAuto: 'Определять автоматически',
inputModeText: 'Только текст',
inputModeImage: 'Текст и изображения',
contextWindow: 'Контекстное окно',
useModelDefault: 'Использовать значение модели',
reasoning: 'Модель рассуждений',
verified: 'Проверено',
verifySkipped: 'Без проверки',

View File

@@ -762,6 +762,14 @@ const message = {
addFallbackModel: 'Yedek model ekle',
fallbackModelsEmpty: 'Yapılandırılmış yedek model yok',
modelInputTypes: 'Girdi türleri',
modelMetadata: 'Model yetenekleri',
modelMetadataHelper:
'Bu ayarlar yalnızca mevcut OpenClaw örneğine uygulanır. OpenClaw veya sağlayıcı varsayılanlarını kullanmak için boş bırakın.',
inputModeAuto: 'Otomatik algıla',
inputModeText: 'Yalnızca metin',
inputModeImage: 'Metin ve görseller',
contextWindow: 'Bağlam penceresi',
useModelDefault: 'Model varsayılanını kullan',
reasoning: 'Akıl yürütme modeli',
verified: 'Doğrulandı',
verifySkipped: 'Doğrulama yok',

View File

@@ -729,6 +729,13 @@ const message = {
addFallbackModel: '新增備用模型',
fallbackModelsEmpty: '尚未設定備用模型',
modelInputTypes: '輸入類型',
modelMetadata: '模型能力設定',
modelMetadataHelper: '設定僅套用於目前的 OpenClaw。留空時使用 OpenClaw 或模型供應商的預設值。',
inputModeAuto: '自動識別',
inputModeText: '僅文字',
inputModeImage: '文字和圖片',
contextWindow: '上下文視窗',
useModelDefault: '使用模型預設值',
reasoning: '推理模型',
verified: '驗證狀態',
verifySkipped: '略過驗證',

View File

@@ -746,6 +746,13 @@ const message = {
addFallbackModel: '新增备用模型',
fallbackModelsEmpty: '尚未配置备用模型',
modelInputTypes: '输入类型',
modelMetadata: '模型能力配置',
modelMetadataHelper: '配置仅作用于当前 OpenClaw。留空时使用 OpenClaw 或模型供应商的默认值。',
inputModeAuto: '自动识别',
inputModeText: '仅文本',
inputModeImage: '文本和图片',
contextWindow: '上下文窗口',
useModelDefault: '使用模型默认值',
reasoning: '推理模型',
verified: '验证状态',
verifySkipped: '不验证',

View File

@@ -55,6 +55,46 @@
</el-table>
</div>
</el-form-item>
<template v-if="showMetadata">
<el-divider content-position="left">{{ t('aiTools.agents.modelMetadata') }}</el-divider>
<span class="input-help metadata-help">{{ t('aiTools.agents.modelMetadataHelper') }}</span>
<el-table :data="metadataRows" size="small">
<el-table-column :label="t('aiTools.model.model')" min-width="180">
<template #default="{ row }">
{{ modelOptions.find((item) => item.id === row.model)?.name || row.model }}
</template>
</el-table-column>
<el-table-column :label="t('aiTools.agents.modelInputTypes')" min-width="160">
<template #default="{ row }">
<el-select v-model="row.inputMode">
<el-option :label="t('aiTools.agents.inputModeAuto')" value="auto" />
<el-option :label="t('aiTools.agents.inputModeText')" value="text" />
<el-option :label="t('aiTools.agents.inputModeImage')" value="image" />
</el-select>
</template>
</el-table-column>
<el-table-column :label="t('aiTools.agents.contextWindow')" min-width="150">
<template #default="{ row }">
<el-input-number
v-model="row.contextWindow"
:min="1"
:controls="false"
:placeholder="t('aiTools.agents.useModelDefault')"
/>
</template>
</el-table-column>
<el-table-column label="Max Tokens" min-width="150">
<template #default="{ row }">
<el-input-number
v-model="row.maxTokens"
:min="1"
:controls="false"
:placeholder="t('aiTools.agents.useModelDefault')"
/>
</template>
</el-table-column>
</el-table>
</template>
<el-form-item>
<el-button v-permission type="primary" :loading="saving" @click="saveModel">
{{ t('commons.button.save') }}
@@ -88,6 +128,16 @@ const agentType = ref<AI.AgentType>('openclaw');
const accountOptions = ref<AI.AgentAccountItem[]>([]);
const modelOptions = ref<AI.AgentAccountModel[]>([]);
const showFallbacks = computed(() => agentType.value === 'openclaw');
const metadataRows = ref<
Array<
Omit<AI.AgentModelMetadata, 'contextWindow' | 'maxTokens'> & {
contextWindow?: number;
maxTokens?: number;
}
>
>([]);
const selectedAccount = computed(() => accountOptions.value.find((item) => item.id === form.accountId));
const showMetadata = computed(() => agentType.value === 'openclaw' && selectedAccount.value?.provider !== 'gemini');
interface AgentModelLoadParams {
agentId: number;
@@ -145,11 +195,25 @@ const syncFallbacks = (fallbacks?: string[]) => {
}
};
const syncMetadata = (metadata: AI.AgentModelMetadata[] = []) => {
const configured = new Map(metadata.map((item) => [item.model, item]));
metadataRows.value = modelOptions.value.map((model) => {
const item = configured.get(model.id);
return {
model: model.id,
inputMode: item?.inputMode || 'auto',
contextWindow: item?.contextWindow || undefined,
maxTokens: item?.maxTokens || undefined,
};
});
};
const handleAccountChange = () => {
setModelOptionsByAccount(form.accountId);
ensureSelectedModel();
form.fallbacks = [];
fallbackCandidate.value = '';
syncMetadata();
};
const handleModelChange = () => {
@@ -199,6 +263,7 @@ const load = async (params: AgentModelLoadParams) => {
form.model = '';
form.fallbacks = [];
modelOptions.value = [];
metadataRows.value = [];
return;
}
const config = configRes.data;
@@ -208,6 +273,7 @@ const load = async (params: AgentModelLoadParams) => {
setModelOptionsByAccount(currentAccount.id);
ensureSelectedModel(config.model);
syncFallbacks(showFallbacks.value ? config.fallbacks || [] : []);
syncMetadata(showMetadata.value ? config.metadata || [] : []);
} finally {
loading.value = false;
}
@@ -225,6 +291,14 @@ const saveModel = async () => {
accountId: form.accountId,
model: form.model,
fallbacks: showFallbacks.value ? form.fallbacks : [],
metadata: showMetadata.value
? metadataRows.value.map((item) => ({
model: item.model,
inputMode: item.inputMode,
contextWindow: item.contextWindow || 0,
maxTokens: item.maxTokens || 0,
}))
: [],
});
MsgSuccess(t('aiTools.agents.switchModelSuccess'));
emit('updated');
@@ -257,4 +331,13 @@ defineExpose({
padding: 12px 0;
color: var(--el-text-color-secondary);
}
.metadata-help {
display: block;
margin: -8px 0 12px;
}
:deep(.el-input-number) {
width: 100%;
}
</style>