Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fc42db40ce | |||
| e413a002c1 | |||
| 6437d759a3 | |||
| c758b2d888 | |||
| 510290fe0e | |||
| c61d62edb6 | |||
| 45bce6fe76 | |||
| f156adddf8 | |||
| b5a4b80c36 | |||
| 792fb69d6d |
@@ -1 +1 @@
|
||||
__version__ = "4.10.3"
|
||||
__version__ = "4.10.6"
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Any, TypedDict
|
||||
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
||||
|
||||
VERSION = "4.10.3"
|
||||
VERSION = "4.10.6"
|
||||
DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
|
||||
|
||||
WEBHOOK_SUPPORTED_PLATFORMS = [
|
||||
@@ -1451,7 +1451,32 @@ CONFIG_METADATA_2 = {
|
||||
"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",
|
||||
|
||||
@@ -149,8 +149,16 @@ class RecursiveCharacterChunker(BaseChunker):
|
||||
分割后的文本块列表
|
||||
|
||||
"""
|
||||
chunk_size = chunk_size or self.chunk_size
|
||||
overlap = overlap or self.chunk_overlap
|
||||
if chunk_size is None:
|
||||
chunk_size = self.chunk_size
|
||||
if overlap is None:
|
||||
overlap = self.chunk_overlap
|
||||
if chunk_size <= 0:
|
||||
raise ValueError("chunk_size must be greater than 0")
|
||||
if overlap < 0:
|
||||
raise ValueError("chunk_overlap must be non-negative")
|
||||
if overlap >= chunk_size:
|
||||
raise ValueError("chunk_overlap must be less than chunk_size")
|
||||
result = []
|
||||
for i in range(0, len(text), chunk_size - overlap):
|
||||
end = min(i + chunk_size, len(text))
|
||||
|
||||
@@ -378,7 +378,8 @@ class ProviderOpenAIOfficial(Provider):
|
||||
new_content.append(part)
|
||||
message["content"] = new_content
|
||||
# reasoning key is "reasoning_content"
|
||||
message["reasoning_content"] = reasoning_content
|
||||
if reasoning_content:
|
||||
message["reasoning_content"] = reasoning_content
|
||||
|
||||
async def _handle_api_error(
|
||||
self,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import fnmatch
|
||||
|
||||
from astrbot.core.utils.shared_preferences import SharedPreferences
|
||||
|
||||
|
||||
@@ -30,7 +32,7 @@ class UmopConfigRouter:
|
||||
if len(p1_ls) != 3 or len(p2_ls) != 3:
|
||||
return False # 非法格式
|
||||
|
||||
return all(p == "" or p == "*" or p == t for p, t in zip(p1_ls, p2_ls))
|
||||
return all(p == "" or fnmatch.fnmatchcase(t, p) for p, t in zip(p1_ls, p2_ls))
|
||||
|
||||
def get_conf_id_for_umop(self, umo: str) -> str | None:
|
||||
"""根据 UMO 获取对应的配置文件 ID
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
## What's Changed
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复钉钉适配器中"回复消息 At 发送人"功能失效的问题
|
||||
- 修复 Xinference STT 在部分情况下无法使用的问题
|
||||
- 修复"会话隔离"功能在非默认配置下无法生效的问题
|
||||
- 修复部分 LLM 中转商因 token 使用情况不符合 OpenAI 标准接口规范导致请求报错的问题
|
||||
- 修复 Deepseek 模型开启思考模式后工具调用报错的问题
|
||||
- 修复部分操作系统环境下 pip 安装依赖时出现 `UnicodeDecodeError` 错误的问题
|
||||
|
||||
### 优化
|
||||
|
||||
- 全面优化对思考型模型的支持(如 Anthropic Extended Thinking、Deepseek 思考模式),完整回传 thinking 内容,提升模型推理性能
|
||||
- 优化 WebUI 记忆侧边栏中"更多功能"和"平台日志"模块的展开状态记忆
|
||||
- 为 MiniMax TTS 新增 "auto" 音色情绪选项,支持模型根据文本内容自动选择情绪
|
||||
- 优化备份功能,支持大文件分片下载
|
||||
- 为 WebSocket 连接添加 max_size 参数,以处理更大的消息并防止接收来自 Satori 平台的大负载时连接断开
|
||||
- 优化插件安装流程,通过文件安装插件时,若插件已加载则先终止再重新加载,避免重复加载
|
||||
- 知识库支持将 overlap 参数设置为 0
|
||||
|
||||
### 新增
|
||||
|
||||
- 为 `dict` 类型的 Schema 新增 JSON value 和 template schema 功能。详见 [dict-类型的-schema](https://docs.astrbot.app/dev/star/guides/plugin-config.html#dict-%E7%B1%BB%E5%9E%8B%E7%9A%84-schema)。
|
||||
- 新增 `template_list` 类型的 Schema,支持渲染指定 template 下的列表。详见 [template-list-类型的-schema](https://docs.astrbot.app/dev/star/guides/plugin-config.html#template-list-%E7%B1%BB%E5%9E%8B%E7%9A%84-schema)。
|
||||
@@ -0,0 +1,5 @@
|
||||
## What's Changed
|
||||
|
||||
hotfix of v4.10.4
|
||||
|
||||
fix: 部分配置项的输入框不显示,如飞书机器人配置的部分配置项。(#4268)
|
||||
@@ -0,0 +1,11 @@
|
||||
## What's Changed
|
||||
|
||||
hotfix of v4.10.4
|
||||
|
||||
fix:
|
||||
|
||||
1. ‼️ 部分情况下使用 OpenAI 接口报错与 reasoning_content 有关的问题;
|
||||
|
||||
feat:
|
||||
|
||||
1. WebUI 已安装插件页支持记忆视图类型(列表/卡片),列表视图显示插件的人类友好名称和 logo。
|
||||
@@ -82,7 +82,7 @@
|
||||
{{ tm('availability.test') }}
|
||||
<template #activator="{ props }">
|
||||
<v-btn
|
||||
icon="mdi-wrench"
|
||||
icon="mdi-connection"
|
||||
size="small"
|
||||
variant="text"
|
||||
:disabled="!entry.provider.enable"
|
||||
@@ -93,6 +93,19 @@
|
||||
</template>
|
||||
</v-tooltip>
|
||||
|
||||
<v-tooltip location="top" max-width="300">
|
||||
{{ tm('models.configure') }}
|
||||
<template #activator="{ props }">
|
||||
<v-btn
|
||||
icon="mdi-cog"
|
||||
size="small"
|
||||
variant="text"
|
||||
v-bind="props"
|
||||
@click.stop="emit('open-provider-edit', entry.provider)"
|
||||
></v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
|
||||
<v-btn icon="mdi-delete" size="small" variant="text" color="error" @click.stop="emit('delete-provider', entry.provider)"></v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -203,9 +203,8 @@ function hasVisibleItemsAfter(items, currentIndex) {
|
||||
|
||||
<v-col cols="12" sm="6" class="config-input">
|
||||
<ConfigItemRenderer
|
||||
v-if="metadata[metadataKey].items[key]"
|
||||
v-model="iterable[key]"
|
||||
:item-meta="metadata[metadataKey].items[key]"
|
||||
:item-meta="metadata[metadataKey].items[key] || null"
|
||||
:loading="loadingEmbeddingDim"
|
||||
:show-fullscreen-btn="!!metadata[metadataKey].items[key]?.editor_mode"
|
||||
@get-embedding-dim="getEmbeddingDimensions(iterable)"
|
||||
|
||||
@@ -219,7 +219,7 @@ function getSpecialSubtype(value) {
|
||||
<ConfigItemRenderer
|
||||
v-else
|
||||
v-model="createSelectorModel(itemKey).value"
|
||||
:item-meta="itemMeta"
|
||||
:item-meta="itemMeta || null"
|
||||
:show-fullscreen-btn="!!itemMeta?.editor_mode"
|
||||
@open-fullscreen="openEditorDialog(itemKey, iterable, itemMeta?.editor_theme, itemMeta?.editor_language)"
|
||||
/>
|
||||
|
||||
@@ -188,6 +188,7 @@
|
||||
<ObjectEditor
|
||||
v-else-if="itemMeta?.type === 'dict'"
|
||||
:model-value="modelValue"
|
||||
:item-meta="itemMeta"
|
||||
@update:model-value="emitUpdate"
|
||||
class="config-field"
|
||||
/>
|
||||
@@ -222,7 +223,7 @@ const props = defineProps({
|
||||
},
|
||||
itemMeta: {
|
||||
type: Object,
|
||||
required: true
|
||||
default: null
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<v-btn size="small" color="primary" variant="tonal" @click="openDialog">
|
||||
{{ preferSingleItem ? '添加更多' : (buttonText || t('core.common.list.modifyButton')) }}
|
||||
{{ preferSingleItem ? t('core.common.list.addMore') : (buttonText || t('core.common.list.modifyButton')) }}
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -48,6 +48,14 @@
|
||||
:placeholder="t('core.common.list.inputPlaceholder')"
|
||||
class="flex-grow-1">
|
||||
</v-text-field>
|
||||
<v-btn
|
||||
@click="addItem"
|
||||
variant="tonal"
|
||||
color="primary"
|
||||
size="small"
|
||||
:disabled="!newItem.trim()">
|
||||
{{ t('core.common.list.addButton') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@click="showBatchImport = true"
|
||||
variant="tonal"
|
||||
@@ -318,4 +326,4 @@ function cancelBatchImport() {
|
||||
.v-chip {
|
||||
margin: 2px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -26,8 +26,9 @@
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pa-4" style="max-height: 400px; overflow-y: auto;">
|
||||
<div v-if="localKeyValuePairs.length > 0">
|
||||
<div v-for="(pair, index) in localKeyValuePairs" :key="index" class="key-value-pair">
|
||||
<!-- Regular key-value pairs (non-template) -->
|
||||
<div v-if="nonTemplatePairs.length > 0">
|
||||
<div v-for="(pair, index) in nonTemplatePairs" :key="index" class="key-value-pair">
|
||||
<v-row no-gutters align="center" class="mb-2">
|
||||
<v-col cols="4">
|
||||
<v-text-field
|
||||
@@ -48,15 +49,29 @@
|
||||
hide-details
|
||||
placeholder="字符串值"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
v-else-if="pair.type === 'number'"
|
||||
v-model.number="pair.value"
|
||||
type="number"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
placeholder="数值"
|
||||
></v-text-field>
|
||||
<div v-else-if="pair.type === 'number' || pair.type === 'float' || pair.type === 'int'" class="d-flex align-center gap-2 flex-grow-1">
|
||||
<v-slider
|
||||
v-if="pair.slider"
|
||||
:model-value="Number(pair.value) || 0"
|
||||
@update:model-value="pair.value = $event"
|
||||
:min="pair.slider.min"
|
||||
:max="pair.slider.max"
|
||||
:step="pair.slider.step"
|
||||
color="primary"
|
||||
density="compact"
|
||||
hide-details
|
||||
class="flex-grow-1"
|
||||
></v-slider>
|
||||
<v-text-field
|
||||
v-model.number="pair.value"
|
||||
type="number"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
placeholder="数值"
|
||||
:style="pair.slider ? 'max-width: 120px;' : ''"
|
||||
></v-text-field>
|
||||
</div>
|
||||
<v-switch
|
||||
v-else-if="pair.type === 'boolean'"
|
||||
v-model="pair.value"
|
||||
@@ -81,7 +96,7 @@
|
||||
variant="text"
|
||||
size="small"
|
||||
color="error"
|
||||
@click="removeKeyValuePair(index)"
|
||||
@click="removeKeyValuePairByKey(pair.key)"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
@@ -89,7 +104,79 @@
|
||||
</v-row>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center py-8">
|
||||
|
||||
<!-- Template schema fields -->
|
||||
<div v-if="hasTemplateSchema" class="mt-4">
|
||||
<v-divider class="mb-3"></v-divider>
|
||||
<div class="text-caption text-grey mb-2">预设</div>
|
||||
<div v-for="(template, templateKey) in templateSchema" :key="templateKey" class="template-field" :class="{ 'template-field-inactive': !isTemplateKeyAdded(templateKey) }">
|
||||
<v-row no-gutters align="center" class="mb-2">
|
||||
<v-col cols="4">
|
||||
<div class="d-flex flex-column">
|
||||
<span class="text-caption font-weight-medium">{{ template.name || template.description || templateKey }}</span>
|
||||
<span v-if="template.hint" class="text-caption text-grey" style="font-size: 0.7rem;">{{ template.hint }}</span>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="7" class="pl-2 d-flex align-center justify-end">
|
||||
<v-text-field
|
||||
v-if="template.type === 'string'"
|
||||
:model-value="getTemplateValue(templateKey)"
|
||||
@update:model-value="updateTemplateValue(templateKey, $event)"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
placeholder="字符串值"
|
||||
></v-text-field>
|
||||
<div v-else-if="template.type === 'number' || template.type === 'float' || template.type === 'int'" class="d-flex align-center ga-4 flex-grow-1">
|
||||
<v-slider
|
||||
v-if="template.slider"
|
||||
:model-value="Number(getTemplateValue(templateKey)) || 0"
|
||||
@update:model-value="updateTemplateValue(templateKey, $event)"
|
||||
:min="template.slider.min"
|
||||
:max="template.slider.max"
|
||||
:step="template.slider.step"
|
||||
color="primary"
|
||||
density="compact"
|
||||
hide-details
|
||||
class="flex-grow-1"
|
||||
></v-slider>
|
||||
<v-text-field
|
||||
:model-value="getTemplateValue(templateKey)"
|
||||
@update:model-value="updateTemplateValue(templateKey, $event)"
|
||||
type="number"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
placeholder="数值"
|
||||
:style="template.slider ? 'max-width: 120px;' : ''"
|
||||
></v-text-field>
|
||||
</div>
|
||||
<v-switch
|
||||
v-else-if="template.type === 'boolean' || template.type === 'bool'"
|
||||
:model-value="getTemplateValue(templateKey)"
|
||||
@update:model-value="updateTemplateValue(templateKey, $event)"
|
||||
density="compact"
|
||||
hide-details
|
||||
color="primary"
|
||||
></v-switch>
|
||||
</v-col>
|
||||
<v-col cols="1" class="pl-2">
|
||||
<v-btn
|
||||
v-if="isTemplateKeyAdded(templateKey)"
|
||||
icon
|
||||
variant="text"
|
||||
size="small"
|
||||
color="error"
|
||||
@click="removeTemplateKey(templateKey)"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="localKeyValuePairs.length === 0 && !hasTemplateSchema" class="text-center py-8">
|
||||
<v-icon size="64" color="grey-lighten-1">mdi-code-json</v-icon>
|
||||
<p class="text-grey mt-4">暂无参数</p>
|
||||
</div>
|
||||
@@ -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;
|
||||
}
|
||||
</style>
|
||||
@@ -74,6 +74,7 @@
|
||||
"list": {
|
||||
"addItemPlaceholder": "Add new item, press Enter to confirm",
|
||||
"addButton": "Add",
|
||||
"addMore": "Add More",
|
||||
"batchImport": "Batch Import",
|
||||
"batchImportTitle": "Batch Import",
|
||||
"batchImportLabel": "One item per line",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,7 @@
|
||||
"list": {
|
||||
"addItemPlaceholder": "添加新项,按回车确认添加",
|
||||
"addButton": "添加",
|
||||
"addMore": "添加更多",
|
||||
"batchImport": "批量导入",
|
||||
"batchImportTitle": "批量导入",
|
||||
"batchImportLabel": "每行一个项目",
|
||||
@@ -94,4 +95,4 @@
|
||||
"copy": "复制",
|
||||
"noData": "暂无数据"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
"manualDialogPreviewLabel": "显示 ID(自动生成)",
|
||||
"manualDialogPreviewHint": "生成规则:源ID/模型ID",
|
||||
"manualModelRequired": "请输入模型 ID",
|
||||
"manualModelExists": "该模型已存在"
|
||||
"manualModelExists": "该模型已存在",
|
||||
"configure": "配置"
|
||||
}
|
||||
}
|
||||
@@ -78,13 +78,19 @@ const readmeDialog = reactive({
|
||||
});
|
||||
|
||||
// 新增变量支持列表视图
|
||||
const isListView = ref(false);
|
||||
// 从 localStorage 恢复显示模式,默认为 false(卡片视图)
|
||||
const getInitialListViewMode = () => {
|
||||
if (typeof window !== 'undefined' && window.localStorage) {
|
||||
return localStorage.getItem('pluginListViewMode') === 'true';
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const isListView = ref(getInitialListViewMode());
|
||||
const pluginSearch = ref("");
|
||||
const loading_ = ref(false);
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(1);
|
||||
const itemsPerPage = ref(6); // 每页显示6个卡片 (2行 x 3列,避免滚动)
|
||||
|
||||
// 危险插件确认对话框
|
||||
const dangerConfirmDialog = ref(false);
|
||||
@@ -113,7 +119,6 @@ const uploadTab = ref('file');
|
||||
const showPluginFullName = ref(false);
|
||||
const marketSearch = ref("");
|
||||
const debouncedMarketSearch = ref("");
|
||||
const filterKeys = ['name', 'desc', 'author'];
|
||||
const refreshingMarket = ref(false);
|
||||
const sortBy = ref('default'); // default, stars, author, updated
|
||||
const sortOrder = ref('desc'); // desc (降序) or asc (升序)
|
||||
@@ -162,18 +167,6 @@ const pluginHeaders = computed(() => [
|
||||
]);
|
||||
|
||||
|
||||
// 插件市场表头
|
||||
const pluginMarketHeaders = computed(() => [
|
||||
{ title: tm('table.headers.name'), key: 'name', maxWidth: '200px' },
|
||||
{ title: tm('table.headers.description'), key: 'desc', maxWidth: '250px' },
|
||||
{ title: tm('table.headers.author'), key: 'author', maxWidth: '90px' },
|
||||
{ title: tm('table.headers.stars'), key: 'stars', maxWidth: '80px' },
|
||||
{ title: tm('table.headers.lastUpdate'), key: 'updated_at', maxWidth: '100px' },
|
||||
{ title: tm('table.headers.tags'), key: 'tags', maxWidth: '100px' },
|
||||
{ title: tm('table.headers.actions'), key: 'actions', sortable: false }
|
||||
]);
|
||||
|
||||
|
||||
// 过滤要显示的插件
|
||||
const filteredExtensions = computed(() => {
|
||||
const data = Array.isArray(extension_data?.data) ? extension_data.data : [];
|
||||
@@ -197,9 +190,6 @@ const filteredPlugins = computed(() => {
|
||||
});
|
||||
});
|
||||
|
||||
const pinnedPlugins = computed(() => {
|
||||
return pluginMarketData.value.filter(plugin => plugin?.pinned);
|
||||
});
|
||||
|
||||
// 过滤后的插件市场数据(带搜索)
|
||||
const filteredMarketPlugins = computed(() => {
|
||||
@@ -552,14 +542,6 @@ const viewReadme = (plugin) => {
|
||||
readmeDialog.show = true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const open = (link) => {
|
||||
if (link) {
|
||||
window.open(link, '_blank');
|
||||
}
|
||||
};
|
||||
|
||||
// 为表格视图创建一个处理安装插件的函数
|
||||
const handleInstallPlugin = async (plugin) => {
|
||||
if (plugin.tags && plugin.tags.includes('danger')) {
|
||||
@@ -918,6 +900,13 @@ watch(marketSearch, (newVal) => {
|
||||
}, 300); // 300ms 防抖延迟
|
||||
});
|
||||
|
||||
// 监听显示模式变化并保存到 localStorage
|
||||
watch(isListView, (newVal) => {
|
||||
if (typeof window !== 'undefined' && window.localStorage) {
|
||||
localStorage.setItem('pluginListViewMode', String(newVal));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1037,8 +1026,21 @@ watch(marketSearch, (newVal) => {
|
||||
|
||||
<template v-slot:item.name="{ item }">
|
||||
<div class="d-flex align-center py-2">
|
||||
<div v-if="item.logo" class="mr-3" style="flex-shrink: 0;">
|
||||
<img :src="item.logo" :alt="item.name"
|
||||
style="height: 40px; width: 40px; border-radius: 8px; object-fit: cover;" />
|
||||
</div>
|
||||
<div v-else class="mr-3" style="flex-shrink: 0;">
|
||||
<img :src="defaultPluginIcon" :alt="item.name"
|
||||
style="height: 40px; width: 40px; border-radius: 8px; object-fit: cover;" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-subtitle-1 font-weight-medium">{{ item.name }}</div>
|
||||
<div class="text-subtitle-1 font-weight-medium">
|
||||
{{ item.display_name && item.display_name.length ? item.display_name : item.name }}
|
||||
</div>
|
||||
<div v-if="item.display_name && item.display_name.length" class="text-caption text-medium-emphasis mt-1">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div v-if="item.reserved" class="d-flex align-center mt-1">
|
||||
<v-chip color="primary" size="x-small" class="font-weight-medium">{{ tm('status.system')
|
||||
}}</v-chip>
|
||||
@@ -1048,7 +1050,7 @@ watch(marketSearch, (newVal) => {
|
||||
</template>
|
||||
|
||||
<template v-slot:item.desc="{ item }">
|
||||
<div class="text-body-2 text-medium-emphasis">{{ item.desc }}</div>
|
||||
<div class="text-body-2 text-medium-emphasis mt-2 mb-2" style="display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis;">{{ item.desc }}</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.version="{ item }">
|
||||
@@ -1084,7 +1086,7 @@ watch(marketSearch, (newVal) => {
|
||||
<v-tooltip activator="parent" location="top">{{ tm('tooltips.disable') }}</v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<v-btn icon size="small" color="info" @click="reloadPlugin(item.name)">
|
||||
<v-btn icon size="small" @click="reloadPlugin(item.name)">
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
<v-tooltip activator="parent" location="top">{{ tm('tooltips.reload') }}</v-tooltip>
|
||||
</v-btn>
|
||||
@@ -1104,7 +1106,7 @@ watch(marketSearch, (newVal) => {
|
||||
<v-tooltip activator="parent" location="top">{{ tm('tooltips.viewDocs') }}</v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<v-btn icon size="small" color="warning" @click="updateExtension(item.name)"
|
||||
<v-btn icon size="small" @click="updateExtension(item.name)"
|
||||
:v-show="item.has_update">
|
||||
<v-icon>mdi-update</v-icon>
|
||||
<v-tooltip activator="parent" location="top">{{ tm('tooltips.update') }}</v-tooltip>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "AstrBot"
|
||||
version = "4.10.3"
|
||||
version = "4.10.6"
|
||||
description = "Easy-to-use multi-platform LLM chatbot and development framework"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
Reference in New Issue
Block a user