From 0074c73a2759f652af65875fab62718dd0c99f9b Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Wed, 31 Dec 2025 12:16:32 +0800 Subject: [PATCH] feat: enhance configuration editor with template schema support and UI improvements - Added support for template schemas in the configuration editor, allowing users to define and manage additional parameters like temperature, top_p, and max_tokens. - Improved UI components in ProviderModelsPanel and ObjectEditor for better user interaction, including new configuration buttons and enhanced input handling. - Updated localization files to include new configuration options. --- astrbot/core/config/default.py | 27 +- .../provider/ProviderModelsPanel.vue | 15 +- .../components/shared/ConfigItemRenderer.vue | 1 + .../src/components/shared/ObjectEditor.vue | 240 ++++++++++++++++-- .../i18n/locales/en-US/features/provider.json | 3 +- .../i18n/locales/zh-CN/features/provider.json | 3 +- 6 files changed, 269 insertions(+), 20 deletions(-) diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 7a89cc387..7c0d0a839 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -1451,7 +1451,32 @@ class ChatProviderTemplate(TypedDict): "description": "自定义请求体参数", "type": "dict", "items": {}, - "hint": "此处添加的键值对将被合并到发送给 API 的 extra_body 中。值可以是字符串、数字或布尔值。", + "hint": "用于在请求时添加额外的参数,如 temperature、top_p、max_tokens 等。", + "template_schema": { + "temperature": { + "name": "Temperature", + "description": "温度参数", + "hint": "控制输出的随机性,范围通常为 0-2。值越高越随机。", + "type": "float", + "default": 0.6, + "slider": {"min": 0, "max": 2, "step": 0.1}, + }, + "top_p": { + "name": "Top-p", + "description": "Top-p 采样", + "hint": "核采样参数,范围通常为 0-1。控制模型考虑的概率质量。", + "type": "float", + "default": 1.0, + "slider": {"min": 0, "max": 1, "step": 0.01}, + }, + "max_tokens": { + "name": "Max Tokens", + "description": "最大令牌数", + "hint": "生成的最大令牌数。", + "type": "int", + "default": 8192, + }, + }, }, "provider": { "type": "string", diff --git a/dashboard/src/components/provider/ProviderModelsPanel.vue b/dashboard/src/components/provider/ProviderModelsPanel.vue index cdd1349ed..a27e63fdf 100644 --- a/dashboard/src/components/provider/ProviderModelsPanel.vue +++ b/dashboard/src/components/provider/ProviderModelsPanel.vue @@ -82,7 +82,7 @@ {{ tm('availability.test') }} diff --git a/dashboard/src/components/shared/ConfigItemRenderer.vue b/dashboard/src/components/shared/ConfigItemRenderer.vue index 964e9798e..91e6a2a4e 100644 --- a/dashboard/src/components/shared/ConfigItemRenderer.vue +++ b/dashboard/src/components/shared/ConfigItemRenderer.vue @@ -188,6 +188,7 @@ diff --git a/dashboard/src/components/shared/ObjectEditor.vue b/dashboard/src/components/shared/ObjectEditor.vue index a2f549210..ee6dc84bf 100644 --- a/dashboard/src/components/shared/ObjectEditor.vue +++ b/dashboard/src/components/shared/ObjectEditor.vue @@ -26,8 +26,9 @@ -
-
+ +
+
- +
+ + +
mdi-delete @@ -89,7 +104,79 @@
-
+ + +
+ +
预设
+
+ + +
+ {{ template.name || template.description || templateKey }} + {{ template.hint }} +
+
+ + +
+ + +
+ +
+ + + mdi-close + + +
+
+
+ +
mdi-code-json

暂无参数

@@ -142,6 +229,10 @@ const props = defineProps({ type: Object, required: true }, + itemMeta: { + type: Object, + default: null + }, buttonText: { type: String, default: '修改' @@ -164,11 +255,25 @@ const originalKeyValuePairs = ref([]) const newKey = ref('') const newValueType = ref('string') +// Template schema support +const templateSchema = computed(() => { + return props.itemMeta?.template_schema || {} +}) + +const hasTemplateSchema = computed(() => { + return Object.keys(templateSchema.value).length > 0 +}) + // 计算要显示的键名 const displayKeys = computed(() => { return Object.keys(props.modelValue).slice(0, props.maxDisplayItems) }) +// 分离模板字段和普通字段 +const nonTemplatePairs = computed(() => { + return localKeyValuePairs.value.filter(pair => !templateSchema.value[pair.key]) +}) + // 监听 modelValue 变化,主要用于初始化 watch(() => props.modelValue, (newValue) => { // This watch is primarily for initialization or external changes @@ -180,10 +285,24 @@ function initializeLocalKeyValuePairs() { for (const [key, value] of Object.entries(props.modelValue)) { let _type = (typeof value) === 'object' ? 'json':(typeof value) let _value = _type === 'json'?JSON.stringify(value):value + + // Check if this key has a template schema + const template = templateSchema.value[key] + if (template) { + // Use template type if available + _type = template.type || _type + // Use template default if value is missing + if (_value === undefined || _value === null) { + _value = template.default !== undefined ? template.default : _value + } + } + localKeyValuePairs.value.push({ key: key, value: _value, - type: _type + type: _type, + slider: template?.slider, + template: template }) } } @@ -239,8 +358,11 @@ function updateJSON(index, newValue) { } } -function removeKeyValuePair(index) { - localKeyValuePairs.value.splice(index, 1) +function removeKeyValuePairByKey(key) { + const index = localKeyValuePairs.value.findIndex(pair => pair.key === key) + if (index >= 0) { + localKeyValuePairs.value.splice(index, 1) + } } function updateKey(index, newKey) { @@ -258,10 +380,83 @@ function updateKey(index, newKey) { return } + // 检查新键名是否有模板 + const template = templateSchema.value[newKey] + if (template) { + // 更新类型和默认值 + localKeyValuePairs.value[index].type = template.type || localKeyValuePairs.value[index].type + if (localKeyValuePairs.value[index].value === undefined || localKeyValuePairs.value[index].value === null || localKeyValuePairs.value[index].value === '') { + localKeyValuePairs.value[index].value = template.default !== undefined ? template.default : localKeyValuePairs.value[index].value + } + localKeyValuePairs.value[index].slider = template.slider + localKeyValuePairs.value[index].template = template + } else { + // 清除模板信息 + localKeyValuePairs.value[index].slider = undefined + localKeyValuePairs.value[index].template = undefined + } + // 更新本地副本 localKeyValuePairs.value[index].key = newKey } +function isTemplateKeyAdded(templateKey) { + return localKeyValuePairs.value.some(pair => pair.key === templateKey) +} + +function getTemplateValue(templateKey) { + const pair = localKeyValuePairs.value.find(pair => pair.key === templateKey) + if (pair) { + return pair.value + } + const template = templateSchema.value[templateKey] + return template?.default !== undefined ? template.default : getDefaultValueForType(template?.type || 'string') +} + +function updateTemplateValue(templateKey, newValue) { + const existingIndex = localKeyValuePairs.value.findIndex(pair => pair.key === templateKey) + const template = templateSchema.value[templateKey] + + if (existingIndex >= 0) { + // 更新现有值 + localKeyValuePairs.value[existingIndex].value = newValue + } else { + // 添加新字段 + let valueType = template?.type || 'string' + localKeyValuePairs.value.push({ + key: templateKey, + value: newValue, + type: valueType, + slider: template?.slider, + template: template + }) + } +} + +function removeTemplateKey(templateKey) { + const index = localKeyValuePairs.value.findIndex(pair => pair.key === templateKey) + if (index >= 0) { + localKeyValuePairs.value.splice(index, 1) + } +} + +function getDefaultValueForType(type) { + switch (type) { + case 'int': + case 'float': + case 'number': + return 0 + case 'bool': + case 'boolean': + return false + case 'json': + return "{}" + case 'string': + default: + return "" + } +} + function confirmDialog() { const updatedValue = {} for (const pair of localKeyValuePairs.value) { @@ -269,12 +464,17 @@ function confirmDialog() { let convertedValue = pair.value // 根据声明的类型进行转换 switch (pair.type) { + case 'int': + convertedValue = parseInt(pair.value) || 0 + break + case 'float': case 'number': // 尝试转换为数字,如果失败则保持原值(或设为默认值0) convertedValue = Number(pair.value) // 可选:检查是否为有效数字,无效则设为0或报错 // if (isNaN(convertedValue)) convertedValue = 0; break + case 'bool': case 'boolean': // 布尔值通常由 v-switch 正确处理,但为保险起见可以显式转换 // 注意:在 JavaScript 中,只有严格的 false, 0, "", null, undefined, NaN 会被转换为 false @@ -307,4 +507,12 @@ function cancelDialog() { .key-value-pair { width: 100%; } + +.template-field { + transition: opacity 0.2s; +} + +.template-field-inactive { + opacity: 0.8; +} \ No newline at end of file diff --git a/dashboard/src/i18n/locales/en-US/features/provider.json b/dashboard/src/i18n/locales/en-US/features/provider.json index d50112e5a..5f4a7987c 100644 --- a/dashboard/src/i18n/locales/en-US/features/provider.json +++ b/dashboard/src/i18n/locales/en-US/features/provider.json @@ -129,6 +129,7 @@ "manualDialogPreviewLabel": "Display ID (auto generated)", "manualDialogPreviewHint": "Generated as sourceId/modelId", "manualModelRequired": "Please enter a model ID", - "manualModelExists": "Model already exists" + "manualModelExists": "Model already exists", + "configure": "Configure" } } \ No newline at end of file diff --git a/dashboard/src/i18n/locales/zh-CN/features/provider.json b/dashboard/src/i18n/locales/zh-CN/features/provider.json index de2c0c7fc..84b9d6c58 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/provider.json +++ b/dashboard/src/i18n/locales/zh-CN/features/provider.json @@ -130,6 +130,7 @@ "manualDialogPreviewLabel": "显示 ID(自动生成)", "manualDialogPreviewHint": "生成规则:源ID/模型ID", "manualModelRequired": "请输入模型 ID", - "manualModelExists": "该模型已存在" + "manualModelExists": "该模型已存在", + "configure": "配置" } } \ No newline at end of file