mirror of
https://github.com/AstrBotDevs/AstrBot.git
synced 2026-10-29 17:24:47 +08:00
feat: filesystem grep, read, write, edit file and workspace support (#7402)
* feat: filesystem grep, read, edit file * feat: add file write tool and enhance file read functionality * feat: enhance tool prompt formatting and add escaped text decoding for file editing * feat: remove redundant safe path tests from security restrictions * feat: implement file read tool with support for text and image files, including validation for large files * feat: add file read utilities and integrate with filesystem tools * refactor: move computer tools to builtin tools registry * refactor: remove unused plugin_context parameter from _apply_sandbox_tools * feat: supports to display enabled builtin tools in configs * feat: add tooltip for disabled builtin tools and update localization strings * feat: add workspace extra prompt handling in message processing * feat: add ripgrep installation to Dockerfile * perf: shell executed in workspace dir in local env * feat: enhance file reading capabilities to support PDF and DOCX parsing, including workspace storage for long documents * feat: update converted text notice to suggest using grep for large files * feat: implement handling for large tool results with overflow file writing and read tool integration * fix: test * feat: enhance onboarding steps to include computer access configuration and related help information * feat: add support for additional temporary path in restricted environment checks * feat: update computer access hints and add detailed configuration instructions
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useModuleI18n } from '@/i18n/composables';
|
||||
import type { ToolItem } from '../types';
|
||||
import type { BuiltinToolConfigTag, ToolConfigCondition, ToolItem } from '../types';
|
||||
|
||||
const { tm: tmTool } = useModuleI18n('features/tooluse');
|
||||
|
||||
@@ -15,7 +15,7 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const toolHeaders = computed(() => [
|
||||
{ title: tmTool('functionTools.title'), key: 'name', minWidth: '240px' },
|
||||
{ title: tmTool('functionTools.title'), key: 'name', minWidth: '320px' },
|
||||
{ title: tmTool('functionTools.description'), key: 'description' },
|
||||
{ title: tmTool('functionTools.table.origin'), key: 'origin', sortable: false, width: '120px' },
|
||||
{ title: tmTool('functionTools.table.originName'), key: 'origin_name', sortable: false, width: '160px' },
|
||||
@@ -23,6 +23,52 @@ const toolHeaders = computed(() => [
|
||||
]);
|
||||
|
||||
const parameterEntries = (tool: ToolItem) => Object.entries(tool.parameters?.properties || {});
|
||||
|
||||
const formatConfigValue = (value: unknown) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => String(item)).join(', ');
|
||||
}
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? 'true' : 'false';
|
||||
}
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-';
|
||||
}
|
||||
return String(value);
|
||||
};
|
||||
|
||||
const formatCondition = (condition: ToolConfigCondition) => {
|
||||
if (condition.message) {
|
||||
return condition.message;
|
||||
}
|
||||
|
||||
switch (condition.operator) {
|
||||
case 'truthy':
|
||||
return tmTool('functionTools.configTags.conditions.truthy', {
|
||||
key: condition.key
|
||||
});
|
||||
case 'equals':
|
||||
return tmTool('functionTools.configTags.conditions.equals', {
|
||||
key: condition.key,
|
||||
expected: formatConfigValue(condition.expected)
|
||||
});
|
||||
case 'in':
|
||||
return tmTool('functionTools.configTags.conditions.in', {
|
||||
key: condition.key,
|
||||
expected: formatConfigValue(condition.expected)
|
||||
});
|
||||
default:
|
||||
return tmTool('functionTools.configTags.conditions.fallback', {
|
||||
key: condition.key,
|
||||
actual: formatConfigValue(condition.actual)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const enabledConfigTags = (tool: ToolItem): BuiltinToolConfigTag[] => {
|
||||
if (tool.origin !== 'builtin') return [];
|
||||
return (tool.builtin_config_tags || []).filter(tag => tag.enabled);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -38,7 +84,39 @@ const parameterEntries = (tool: ToolItem) => Object.entries(tool.parameters?.pro
|
||||
>
|
||||
<template #item.name="{ item }">
|
||||
<div class="py-2">
|
||||
<div class="tool-name text-body-2 font-weight-medium">{{ item.name }}</div>
|
||||
<div class="d-flex flex-wrap align-center ga-1">
|
||||
<div class="tool-name text-body-2 font-weight-medium">{{ item.name }}</div>
|
||||
<v-tooltip
|
||||
v-for="tag in enabledConfigTags(item)"
|
||||
:key="`${item.name}-${tag.conf_id}`"
|
||||
location="top"
|
||||
>
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
<v-chip
|
||||
v-bind="tooltipProps"
|
||||
size="x-small"
|
||||
variant="tonal"
|
||||
color="secondary"
|
||||
class="text-caption font-weight-medium"
|
||||
>
|
||||
{{ tag.conf_name }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<div class="tool-config-tooltip">
|
||||
<div class="text-body-2 font-weight-medium mb-2">
|
||||
{{ tmTool('functionTools.configTags.tooltipTitle', { config: tag.conf_name }) }}
|
||||
</div>
|
||||
<div
|
||||
v-for="(condition, index) in tag.matched_conditions"
|
||||
:key="`${tag.conf_id}-${index}-${condition.key}`"
|
||||
class="text-body-2 text-medium-emphasis mb-1"
|
||||
>
|
||||
{{ formatCondition(condition) }}
|
||||
</div>
|
||||
</div>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -135,4 +213,16 @@ const parameterEntries = (tool: ToolItem) => Object.entries(tool.parameters?.pro
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.tool-config-tooltip {
|
||||
max-width: 360px;
|
||||
padding: 4px 0;
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
|
||||
.tool-config-tooltip :deep(.text-body-2),
|
||||
.tool-config-tooltip :deep(.text-medium-emphasis),
|
||||
.tool-config-tooltip :deep(.font-weight-medium) {
|
||||
color: inherit !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -89,6 +89,23 @@ export interface ToolParameter {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface ToolConfigCondition {
|
||||
key: string;
|
||||
operator: 'truthy' | 'equals' | 'in' | 'custom' | string;
|
||||
expected?: unknown;
|
||||
actual?: unknown;
|
||||
matched: boolean;
|
||||
message?: string | null;
|
||||
}
|
||||
|
||||
export interface BuiltinToolConfigTag {
|
||||
conf_id: string;
|
||||
conf_name: string;
|
||||
enabled: boolean;
|
||||
matched_conditions: ToolConfigCondition[];
|
||||
failed_conditions: ToolConfigCondition[];
|
||||
}
|
||||
|
||||
/** MCP/函数工具对象 */
|
||||
export interface ToolItem {
|
||||
name: string;
|
||||
@@ -100,4 +117,6 @@ export interface ToolItem {
|
||||
};
|
||||
origin?: string;
|
||||
origin_name?: string;
|
||||
builtin_config_statuses?: BuiltinToolConfigTag[];
|
||||
builtin_config_tags?: BuiltinToolConfigTag[];
|
||||
}
|
||||
|
||||
@@ -90,31 +90,52 @@
|
||||
<div v-if="filteredTools.length > 0" class="tools-selection">
|
||||
<v-virtual-scroll :items="filteredTools" height="300" item-height="72">
|
||||
<template v-slot:default="{ item }">
|
||||
<v-list-item :key="item.name" density="comfortable"
|
||||
@click="toggleTool(item.name)">
|
||||
<template v-slot:prepend>
|
||||
<v-checkbox-btn :model-value="isToolSelected(item.name)"
|
||||
@click.stop="toggleTool(item.name)" />
|
||||
<v-tooltip
|
||||
:disabled="!isBuiltinTool(item)"
|
||||
location="top"
|
||||
>
|
||||
<template v-slot:activator="{ props: tooltipProps }">
|
||||
<div v-bind="tooltipProps">
|
||||
<v-list-item
|
||||
:key="item.name"
|
||||
density="comfortable"
|
||||
:disabled="isBuiltinTool(item)"
|
||||
@click="toggleTool(item.name)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-checkbox-btn
|
||||
v-if="!isBuiltinTool(item)"
|
||||
:model-value="isToolSelected(item.name)"
|
||||
@click.stop="toggleTool(item.name)"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="builtin-tool-checkbox-placeholder"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-list-item-title>
|
||||
{{ item.name }}
|
||||
|
||||
<v-chip v-if="item.origin" size="x-small" color="info" class="mr-2"
|
||||
variant="tonal">
|
||||
{{ item.origin }}
|
||||
</v-chip>
|
||||
<v-chip v-if="item.origin_name" size="x-small" color="info"
|
||||
variant="outlined">
|
||||
{{ item.origin_name }}
|
||||
</v-chip>
|
||||
|
||||
</v-list-item-title>
|
||||
|
||||
<v-list-item-subtitle v-if="item.description">
|
||||
{{ truncateText(item.description, 100) }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<v-list-item-title>
|
||||
{{ item.name }}
|
||||
|
||||
<v-chip v-if="item.origin" size="x-small" color="info" class="mr-2"
|
||||
variant="tonal">
|
||||
{{ item.origin }}
|
||||
</v-chip>
|
||||
<v-chip v-if="item.origin_name" size="x-small" color="info"
|
||||
variant="outlined">
|
||||
{{ item.origin_name }}
|
||||
</v-chip>
|
||||
|
||||
</v-list-item-title>
|
||||
|
||||
<v-list-item-subtitle v-if="item.description">
|
||||
{{ truncateText(item.description, 100) }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<span>{{ tm('form.builtinToolDisabledHint') }}</span>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</div>
|
||||
@@ -155,11 +176,26 @@
|
||||
</h4>
|
||||
<div v-if="Array.isArray(personaForm.tools) && personaForm.tools.length > 0"
|
||||
class="d-flex flex-wrap ga-1" style="max-height: 100px; overflow-y: auto;">
|
||||
<v-chip v-for="toolName in personaForm.tools" :key="toolName" size="small"
|
||||
color="primary" variant="tonal" closable
|
||||
@click:close="removeTool(toolName)">
|
||||
{{ toolName }}
|
||||
</v-chip>
|
||||
<v-tooltip
|
||||
v-for="toolName in personaForm.tools"
|
||||
:key="toolName"
|
||||
:disabled="!isBuiltinToolName(toolName)"
|
||||
location="top"
|
||||
>
|
||||
<template v-slot:activator="{ props: tooltipProps }">
|
||||
<v-chip
|
||||
v-bind="tooltipProps"
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
:closable="!isBuiltinToolName(toolName)"
|
||||
@click:close="removeTool(toolName)"
|
||||
>
|
||||
{{ toolName }}
|
||||
</v-chip>
|
||||
</template>
|
||||
<span>{{ tm('form.builtinToolDisabledHint') }}</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
<div v-else class="text-body-2 text-medium-emphasis">
|
||||
{{ tm('form.noToolsSelected') }}
|
||||
@@ -712,6 +748,9 @@ export default {
|
||||
},
|
||||
|
||||
toggleTool(toolName) {
|
||||
if (this.isBuiltinToolName(toolName)) {
|
||||
return;
|
||||
}
|
||||
// 如果当前是全选状态,需要先转换为具体的工具列表
|
||||
if (this.personaForm.tools === null) {
|
||||
// 如果是全选状态,点击某个工具表示要取消选择该工具
|
||||
@@ -735,6 +774,9 @@ export default {
|
||||
},
|
||||
|
||||
removeTool(toolName) {
|
||||
if (this.isBuiltinToolName(toolName)) {
|
||||
return;
|
||||
}
|
||||
// 如果当前是全选状态,需要先转换为具体的工具列表
|
||||
if (this.personaForm.tools === null) {
|
||||
// 创建一个包含所有工具的数组,然后移除指定工具
|
||||
@@ -784,6 +826,14 @@ export default {
|
||||
return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
|
||||
},
|
||||
|
||||
isBuiltinTool(tool) {
|
||||
return tool?.origin === 'builtin' || tool?.readonly === true;
|
||||
},
|
||||
|
||||
isBuiltinToolName(toolName) {
|
||||
return this.availableTools.some(tool => tool.name === toolName && this.isBuiltinTool(tool));
|
||||
},
|
||||
|
||||
getDialogRules(index) {
|
||||
const dialogType = index % 2 === 0 ? this.tm('form.userMessage') : this.tm('form.assistantMessage');
|
||||
return [
|
||||
@@ -859,6 +909,12 @@ export default {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.builtin-tool-checkbox-placeholder {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex: 0 0 40px;
|
||||
}
|
||||
|
||||
.skills-selection {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -117,7 +117,9 @@ const defaultPersonaData = {
|
||||
|
||||
const normalizedTools = computed(() => (Array.isArray(personaData.value?.tools) ? personaData.value.tools : []))
|
||||
const normalizedSkills = computed(() => (Array.isArray(personaData.value?.skills) ? personaData.value.skills : []))
|
||||
const allToolsCount = computed(() => Object.keys(toolMetaMap.value).length)
|
||||
const allToolsCount = computed(() =>
|
||||
Object.values(toolMetaMap.value).filter((tool) => tool.origin !== 'builtin').length
|
||||
)
|
||||
const allSkillsCount = computed(() => availableSkills.value.length)
|
||||
const resolvedTools = computed(() =>
|
||||
normalizedTools.value.map((toolName) => {
|
||||
|
||||
@@ -152,11 +152,11 @@
|
||||
},
|
||||
"agent_computer_use": {
|
||||
"description": "Agent Computer Use",
|
||||
"hint": "Allows the AstrBot to access and use your computer or an sandbox environment to perform more complex tasks. See [Sandbox Mode](https://docs.astrbot.app/use/astrbot-agent-sandbox.html), [Skills](https://docs.astrbot.app/use/skills.html)",
|
||||
"hint": "Allows the AstrBot to access and use your computer or an sandbox environment to perform more complex tasks. See: [Computer Use](https://docs.astrbot.app/use/computer.html).",
|
||||
"provider_settings": {
|
||||
"computer_use_runtime": {
|
||||
"description": "Computer Use Runtime",
|
||||
"hint": "sandbox means running in a sandbox environment, local means running in a local environment, none means disabling Computer Use. If skills are uploaded, choosing none will cause them to not be usable by the Agent."
|
||||
"hint": "Environment allowed for Agent usage. `local` means the local machine environment, `sandbox` means the sandbox environment, and `none` means no environment is allowed."
|
||||
},
|
||||
"computer_use_require_admin": {
|
||||
"description": "Require AstrBot Admin Permission",
|
||||
@@ -1641,4 +1641,4 @@
|
||||
"helpMiddle": "or",
|
||||
"helpSuffix": "."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"mcpServersQuickSelect": "MCP Servers Quick Select",
|
||||
"searchTools": "Search Tools",
|
||||
"selectedTools": "Selected Tools",
|
||||
"builtinToolDisabledHint": "Builtin tools cannot be enabled or disabled here yet. Please enable or disable the corresponding config items in the config file.",
|
||||
"noToolsAvailable": "No tools available",
|
||||
"noToolsFound": "No matching tools found",
|
||||
"loadingTools": "Loading tools...",
|
||||
|
||||
@@ -47,6 +47,15 @@
|
||||
"originName": "Origin Name",
|
||||
"readonly": "Read-only",
|
||||
"actions": "Actions"
|
||||
},
|
||||
"configTags": {
|
||||
"tooltipTitle": "This tool is enabled in config file {config} because:",
|
||||
"conditions": {
|
||||
"truthy": "{key} is enabled",
|
||||
"equals": "{key} = {expected}",
|
||||
"in": "{key} matched {expected}",
|
||||
"fallback": "{key} is currently {actual}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
|
||||
@@ -12,11 +12,21 @@
|
||||
"onboard": {
|
||||
"title": "Quick Onboarding",
|
||||
"subtitle": "Complete initialization directly on the welcome page.",
|
||||
"step1Title": "Configure Platform Bot",
|
||||
"step1Desc": "Connect AstrBot to IM platforms like QQ, Lark, Slack, Telegram, etc.",
|
||||
"step2Title": "Configure AI Model",
|
||||
"step2Desc": "Configure AI models for AstrBot.",
|
||||
"step1Title": "Configure AI Model",
|
||||
"step1Desc": "Configure AI models for AstrBot.",
|
||||
"step2Title": "Configure Platform Bot",
|
||||
"step2Desc": "Connect AstrBot to IM platforms like QQ, Lark, Slack, Telegram, etc.",
|
||||
"step3Title": "Allow Agent to Use the Computer",
|
||||
"step3Desc": "Set whether the Agent can access and use this computer.",
|
||||
"step3HelpTitle": "Access Details",
|
||||
"step3HelpItem1": "The Agent can access and use the workspace directory, which is isolated for each user, as well as Skills to handle more complex tasks.",
|
||||
"step3HelpItem2": "For AstrBot administrators, the Agent is additionally allowed to execute shell commands, run Python code, and access all local directories on this machine.",
|
||||
"step3HelpClose": "Close",
|
||||
"step3SelectLabel": "Computer Access",
|
||||
"step3Allow": "Allow",
|
||||
"step3Deny": "Disallow",
|
||||
"configure": "Configure",
|
||||
"save": "Save",
|
||||
"skip": "Skip",
|
||||
"pending": "Pending",
|
||||
"completed": "Completed",
|
||||
@@ -24,7 +34,11 @@
|
||||
"platformLoadFailed": "Failed to load platform configuration",
|
||||
"providerLoadFailed": "Failed to load provider configuration",
|
||||
"providerUpdateFailed": "Failed to update default chat provider in config file \"default\"",
|
||||
"providerDefaultUpdated": "Default chat provider in config file \"default\" has been set to {id}"
|
||||
"providerDefaultUpdated": "Default chat provider in config file \"default\" has been set to {id}",
|
||||
"computerAccessUpdateFailed": "Failed to update Agent computer access",
|
||||
"computerAccessAllowed": "Agent is now allowed to access and use the computer",
|
||||
"computerAccessDenied": "Agent is no longer allowed to access and use the computer",
|
||||
"step3HelpItem3": "For more detailed control, configure the Computer Access options under Settings -> General -> Computer Access."
|
||||
},
|
||||
"resources": {
|
||||
"title": "Resources",
|
||||
|
||||
@@ -152,11 +152,11 @@
|
||||
},
|
||||
"agent_computer_use": {
|
||||
"description": "Использование компьютера (Agent Computer Use)",
|
||||
"hint": "Позволяет AstrBot получать доступ к вашему компьютеру или песочнице для выполнения сложных задач. См. [Режим песочницы](https://docs.astrbot.app/use/astrbot-agent-sandbox.html), [Навыки](https://docs.astrbot.app/use/skills.html)",
|
||||
"hint": "Позволяет AstrBot получать доступ к вашему компьютеру или песочнице для выполнения сложных задач. Подробнее: [Использование компьютера](https://docs.astrbot.app/use/computer.html).",
|
||||
"provider_settings": {
|
||||
"computer_use_runtime": {
|
||||
"description": "Среда выполнения (Runtime)",
|
||||
"hint": "'sandbox' означает запуск в изолированной среде, 'local' — локально на вашем ПК, 'none' — отключено. Если навыки загружены, выбор 'none' сделает их недоступными для агента."
|
||||
"hint": "Среда, к которой разрешён доступ Agent: `local` — локальная среда компьютера, `sandbox` — изолированная песочница, `none` — запретить доступ к любым средам."
|
||||
},
|
||||
"computer_use_require_admin": {
|
||||
"description": "Требовать права администратора AstrBot",
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"mcpServersQuickSelect": "Быстрый выбор MCP серверов",
|
||||
"searchTools": "Поиск инструментов",
|
||||
"selectedTools": "Выбранные инструменты",
|
||||
"builtinToolDisabledHint": "Встроенные инструменты пока нельзя включать или выключать здесь. Измените соответствующие параметры в файле конфигурации.",
|
||||
"noToolsAvailable": "Нет доступных инструментов",
|
||||
"noToolsFound": "Инструменты не найдены",
|
||||
"loadingTools": "Загрузка инструментов...",
|
||||
@@ -143,4 +144,4 @@
|
||||
"success": "Объект перемещен",
|
||||
"error": "Ошибка перемещения"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,15 @@
|
||||
"originName": "Имя источника",
|
||||
"readonly": "Только чтение",
|
||||
"actions": "Действия"
|
||||
},
|
||||
"configTags": {
|
||||
"tooltipTitle": "Этот инструмент включен в файле конфигурации {config}, потому что:",
|
||||
"conditions": {
|
||||
"truthy": "{key} включен",
|
||||
"equals": "{key} = {expected}",
|
||||
"in": "{key} соответствует {expected}",
|
||||
"fallback": "Текущее значение {key}: {actual}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
|
||||
@@ -1,37 +1,51 @@
|
||||
{
|
||||
"greeting": {
|
||||
"morning": "Доброе утро, добро пожаловать в AstrBot",
|
||||
"afternoon": "Добрый день, добро пожаловать в AstrBot",
|
||||
"evening": "Добрый вечер, добро пожаловать в AstrBot",
|
||||
"newYear": "С Новым Годом!"
|
||||
},
|
||||
"subtitle": "Сначала пройдите базовое руководство. Настройку платформ и провайдеров моделей можно завершить позже.",
|
||||
"announcement": {
|
||||
"title": "Объявление"
|
||||
},
|
||||
"onboard": {
|
||||
"title": "Быстрый старт",
|
||||
"subtitle": "Вы можете выполнить первичную настройку прямо здесь.",
|
||||
"step1Title": "Настройка платформ",
|
||||
"step1Desc": "Подключите AstrBot к QQ, Lark, WeChat, Telegram и другим мессенджерам.",
|
||||
"step2Title": "Настройка AI моделей",
|
||||
"step2Desc": "Выберите и настройте AI провайдеров для AstrBot.",
|
||||
"configure": "Настроить",
|
||||
"skip": "Пропустить",
|
||||
"pending": "Ожидает",
|
||||
"completed": "Готово",
|
||||
"skipped": "Пропущено",
|
||||
"platformLoadFailed": "Ошибка загрузки конфигурации платформ",
|
||||
"providerLoadFailed": "Ошибка загрузки конфигурации провайдеров",
|
||||
"providerUpdateFailed": "Ошибка обновления провайдера по умолчанию в файле default",
|
||||
"providerDefaultUpdated": "Провайдер {id} установлен по умолчанию в файле default"
|
||||
},
|
||||
"resources": {
|
||||
"title": "Ресурсы",
|
||||
"githubDesc": "Поставьте нам звезду на GitHub!",
|
||||
"docsTitle": "Документация",
|
||||
"docsDesc": "Официальная документация AstrBot.",
|
||||
"afdianTitle": "Afdian",
|
||||
"afdianDesc": "Поддержите команду AstrBot через Afdian."
|
||||
}
|
||||
}
|
||||
{
|
||||
"greeting": {
|
||||
"morning": "Доброе утро, добро пожаловать в AstrBot",
|
||||
"afternoon": "Добрый день, добро пожаловать в AstrBot",
|
||||
"evening": "Добрый вечер, добро пожаловать в AstrBot",
|
||||
"newYear": "С Новым Годом!"
|
||||
},
|
||||
"subtitle": "Сначала пройдите базовое руководство. Настройку платформ и провайдеров моделей можно завершить позже.",
|
||||
"announcement": {
|
||||
"title": "Объявление"
|
||||
},
|
||||
"onboard": {
|
||||
"title": "Быстрый старт",
|
||||
"subtitle": "Вы можете выполнить первичную настройку прямо здесь.",
|
||||
"step1Title": "Настройка AI моделей",
|
||||
"step1Desc": "Выберите и настройте AI провайдеров для AstrBot.",
|
||||
"step2Title": "Настройка платформ",
|
||||
"step2Desc": "Подключите AstrBot к QQ, Lark, WeChat, Telegram и другим мессенджерам.",
|
||||
"step3Title": "Разрешить Agent использовать компьютер",
|
||||
"step3Desc": "Укажите, может ли Agent получать доступ к этому компьютеру и использовать его.",
|
||||
"step3HelpTitle": "Сведения о доступе",
|
||||
"step3HelpItem1": "Agent сможет получать доступ к каталогу рабочего пространства и использовать его, при этом рабочие каталоги разных пользователей изолированы друг от друга, а также использовать Skills для выполнения более сложных задач.",
|
||||
"step3HelpItem2": "Для администраторов AstrBot Agent дополнительно сможет выполнять shell-команды, запускать Python-код и получать доступ ко всем локальным каталогам на этом компьютере.",
|
||||
"step3HelpClose": "Закрыть",
|
||||
"step3SelectLabel": "Доступ к компьютеру",
|
||||
"step3Allow": "Разрешить",
|
||||
"step3Deny": "Запретить",
|
||||
"configure": "Настроить",
|
||||
"save": "Сохранить",
|
||||
"skip": "Пропустить",
|
||||
"pending": "Ожидает",
|
||||
"completed": "Готово",
|
||||
"skipped": "Пропущено",
|
||||
"platformLoadFailed": "Ошибка загрузки конфигурации платформ",
|
||||
"providerLoadFailed": "Ошибка загрузки конфигурации провайдеров",
|
||||
"providerUpdateFailed": "Ошибка обновления провайдера по умолчанию в файле default",
|
||||
"providerDefaultUpdated": "Провайдер {id} установлен по умолчанию в файле default",
|
||||
"computerAccessUpdateFailed": "Не удалось обновить доступ Agent к компьютеру",
|
||||
"computerAccessAllowed": "Agent теперь может получать доступ к компьютеру и использовать его",
|
||||
"computerAccessDenied": "Agent больше не может получать доступ к компьютеру и использовать его",
|
||||
"step3HelpItem3": "Для более тонкой настройки перейдите в «Конфигурация → Основные настройки → Доступ к компьютеру»."
|
||||
},
|
||||
"resources": {
|
||||
"title": "Ресурсы",
|
||||
"githubDesc": "Поставьте нам звезду на GitHub!",
|
||||
"docsTitle": "Документация",
|
||||
"docsDesc": "Официальная документация AstrBot.",
|
||||
"afdianTitle": "Afdian",
|
||||
"afdianDesc": "Поддержите команду AstrBot через Afdian."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,11 +154,11 @@
|
||||
},
|
||||
"agent_computer_use": {
|
||||
"description": "使用电脑能力",
|
||||
"hint": "让 AstrBot 访问和使用你的电脑或者隔离的沙盒环境,以执行更复杂的任务。详见: [沙盒模式](https://docs.astrbot.app/use/astrbot-agent-sandbox.html), [Skills](https://docs.astrbot.app/use/skills.html)。",
|
||||
"hint": "让 AstrBot 访问和使用本机环境或者隔离的沙盒环境,以执行更复杂的任务。详见: [电脑使用](https://docs.astrbot.app/use/computer.html)。",
|
||||
"provider_settings": {
|
||||
"computer_use_runtime": {
|
||||
"description": "运行环境",
|
||||
"hint": "sandbox 代表在沙箱环境中运行, local 代表在本地环境中运行, none 代表不启用。如果上传了 skills,选择 none 会导致其无法被 Agent 正常使用。"
|
||||
"hint": "允许 Agent 访问的环境。local 为本机环境,sandbox 为沙箱环境,none 为不允许任何环境。"
|
||||
},
|
||||
"computer_use_require_admin": {
|
||||
"description": "需要 AstrBot 管理员权限",
|
||||
@@ -1119,22 +1119,22 @@
|
||||
"hint": "仅在使用 qwen3-rerank 模型时生效。建议使用英文撰写。"
|
||||
},
|
||||
"nvidia_rerank_api_base": {
|
||||
"description": "API Base URL"
|
||||
"description": "API Base URL"
|
||||
},
|
||||
"nvidia_rerank_api_key": {
|
||||
"description": "API Key"
|
||||
"description": "API Key"
|
||||
},
|
||||
"nvidia_rerank_model": {
|
||||
"description": "重排序模型名称",
|
||||
"hint": "请参照NVIDIA Docs中模型名称填写。"
|
||||
"description": "重排序模型名称",
|
||||
"hint": "请参照NVIDIA Docs中模型名称填写。"
|
||||
},
|
||||
"nvidia_rerank_model_endpoint": {
|
||||
"description": "自定义模型端点",
|
||||
"hint": "自定义URL末尾端点,默认为 /reranking"
|
||||
"description": "自定义模型端点",
|
||||
"hint": "自定义URL末尾端点,默认为 /reranking"
|
||||
},
|
||||
"nvidia_rerank_truncate": {
|
||||
"description": "文本截断策略",
|
||||
"hint": "当输入文本过长时,是否截断输入以适应模型的最大上下文长度。"
|
||||
"description": "文本截断策略",
|
||||
"hint": "当输入文本过长时,是否截断输入以适应模型的最大上下文长度。"
|
||||
},
|
||||
"launch_model_if_not_running": {
|
||||
"description": "模型未运行时自动启动",
|
||||
@@ -1643,4 +1643,4 @@
|
||||
"helpMiddle": "或",
|
||||
"helpSuffix": "。"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@
|
||||
"mcpServersQuickSelect": "MCP 服务器快速选择",
|
||||
"searchTools": "搜索工具",
|
||||
"selectedTools": "已选择的工具",
|
||||
"builtinToolDisabledHint": "暂不支持在这里启用和停用内置工具,请在配置文件中启用和停用工具对应的配置项。",
|
||||
"noToolsAvailable": "暂无可用工具",
|
||||
"noToolsFound": "未找到匹配的工具",
|
||||
"loadingTools": "正在加载工具...",
|
||||
|
||||
@@ -47,6 +47,15 @@
|
||||
"originName": "来源名称",
|
||||
"readonly": "只读",
|
||||
"actions": "操作"
|
||||
},
|
||||
"configTags": {
|
||||
"tooltipTitle": "该工具在配置文件 {config} 中启用,因为:",
|
||||
"conditions": {
|
||||
"truthy": "启用了 {key}",
|
||||
"equals": "{key} = {expected}",
|
||||
"in": "{key} 命中了 {expected}",
|
||||
"fallback": "{key} 当前值为 {actual}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
|
||||
@@ -12,11 +12,21 @@
|
||||
"onboard": {
|
||||
"title": "快速引导",
|
||||
"subtitle": "欢迎页可直接完成初始化。",
|
||||
"step1Title": "配置平台机器人",
|
||||
"step1Desc": "将 AstrBot 连接到 QQ、飞书、企业微信、Telegram 等 IM 平台。",
|
||||
"step2Title": "配置 AI 模型",
|
||||
"step2Desc": "为 AstrBot 配置 AI 模型。",
|
||||
"step1Title": "配置 AI 模型",
|
||||
"step1Desc": "为 AstrBot 配置 AI 模型。",
|
||||
"step2Title": "配置平台机器人",
|
||||
"step2Desc": "将 AstrBot 连接到 QQ、飞书、企业微信、Telegram 等 IM 平台。",
|
||||
"step3Title": "允许 Agent 使用电脑",
|
||||
"step3Desc": "设置 Agent 是否可以访问和使用当前电脑。",
|
||||
"step3HelpTitle": "允许 Agent 使用电脑",
|
||||
"step3HelpItem1": "Agent 将可以访问和使用工作区目录(每个用户的工作区目录相互独立)以及 Skills 来执行更多复杂任务。",
|
||||
"step3HelpItem2": "对于 AstrBot 管理员,会额外允许 Agent 执行 Shell 命令、Python 代码,以及访问本机的所有目录。",
|
||||
"step3HelpClose": "关闭",
|
||||
"step3SelectLabel": "电脑访问权限",
|
||||
"step3Allow": "允许",
|
||||
"step3Deny": "不允许",
|
||||
"configure": "去配置",
|
||||
"save": "保存",
|
||||
"skip": "跳过",
|
||||
"pending": "待处理",
|
||||
"completed": "已完成",
|
||||
@@ -24,7 +34,11 @@
|
||||
"platformLoadFailed": "加载平台配置失败",
|
||||
"providerLoadFailed": "加载提供商配置失败",
|
||||
"providerUpdateFailed": "更新 default 配置文件默认对话提供商失败",
|
||||
"providerDefaultUpdated": "已将 default 配置文件的默认对话提供商设置为 {id}"
|
||||
"providerDefaultUpdated": "已将 default 配置文件的默认对话提供商设置为 {id}",
|
||||
"computerAccessUpdateFailed": "更新 Agent 电脑访问权限失败",
|
||||
"computerAccessAllowed": "已允许 Agent 访问和使用电脑",
|
||||
"computerAccessDenied": "已禁止 Agent 访问和使用电脑",
|
||||
"step3HelpItem3": "如需更多细化权限与能力配置,可前往 配置文件 -> 普通配置 -> 使用电脑能力 继续设置。"
|
||||
},
|
||||
"resources": {
|
||||
"title": "相关资源",
|
||||
|
||||
@@ -20,11 +20,28 @@
|
||||
</div>
|
||||
|
||||
<v-timeline align="start" side="end" density="compact" class="welcome-timeline" truncate-line="both">
|
||||
<v-timeline-item :dot-color="platformStepState === 'completed' ? 'success' : 'primary'"
|
||||
:icon="platformStepState === 'completed' ? 'mdi-check' : 'mdi-numeric-1'" fill-dot size="small">
|
||||
<v-timeline-item :dot-color="providerStepState === 'completed' ? 'success' : 'primary'"
|
||||
icon="mdi-numeric-1" fill-dot size="small">
|
||||
<div class="pl-2">
|
||||
<div class="text-h6 font-weight-bold mb-1">{{ tm('onboard.step1Title') }}</div>
|
||||
<p class="text-body-2 text-medium-emphasis mb-3">{{ tm('onboard.step1Desc') }}</p>
|
||||
<div class="d-flex align-center">
|
||||
<v-btn color="primary" variant="flat" rounded="pill" class="px-6" @click="openProviderDialog">
|
||||
{{ tm('onboard.configure') }}
|
||||
</v-btn>
|
||||
<div v-if="providerStepState === 'completed'"
|
||||
class="text-success d-flex align-center text-body-2 font-weight-medium ml-3">
|
||||
{{ tm('onboard.completed') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-timeline-item>
|
||||
|
||||
<v-timeline-item :dot-color="platformStepState === 'completed' ? 'success' : 'primary'"
|
||||
icon="mdi-numeric-2" fill-dot size="small">
|
||||
<div class="pl-2">
|
||||
<div class="text-h6 font-weight-bold mb-1">{{ tm('onboard.step2Title') }}</div>
|
||||
<p class="text-body-2 text-medium-emphasis mb-3">{{ tm('onboard.step2Desc') }}</p>
|
||||
<div class="d-flex align-center">
|
||||
<v-btn color="primary" variant="flat" rounded="pill" class="px-6" :loading="loadingPlatformDialog"
|
||||
@click="openPlatformDialog">
|
||||
@@ -38,22 +55,37 @@
|
||||
</div>
|
||||
</v-timeline-item>
|
||||
|
||||
<v-timeline-item :dot-color="providerStepState === 'completed' ? 'success' : 'primary'"
|
||||
:icon="providerStepState === 'completed' ? 'mdi-check' : 'mdi-numeric-2'" fill-dot size="small">
|
||||
<v-timeline-item :dot-color="computerAccessStepState === 'completed' ? 'success' : 'primary'"
|
||||
icon="mdi-numeric-3" fill-dot size="small">
|
||||
<div class="pl-2">
|
||||
<div class="text-h6 font-weight-bold mb-1"
|
||||
:class="{ 'text-medium-emphasis': platformStepState !== 'completed' }">{{ tm('onboard.step2Title')
|
||||
}}
|
||||
</div>
|
||||
<p class="text-body-2 text-medium-emphasis mb-3">{{ tm('onboard.step2Desc') }}</p>
|
||||
<div class="d-flex align-center">
|
||||
<v-btn color="primary" variant="flat" rounded="pill" class="px-6" @click="openProviderDialog">
|
||||
{{ tm('onboard.configure') }}
|
||||
<div class="d-flex align-center mb-1">
|
||||
<div class="text-h6 font-weight-bold">{{ tm('onboard.step3Title') }}</div>
|
||||
<v-btn
|
||||
icon
|
||||
variant="text"
|
||||
density="comfortable"
|
||||
size="small"
|
||||
class="ml-1"
|
||||
@click="showComputerAccessHelpDialog = true"
|
||||
>
|
||||
<span class="text-body-2 font-weight-bold">?</span>
|
||||
</v-btn>
|
||||
<div v-if="providerStepState === 'completed'"
|
||||
class="text-success d-flex align-center text-body-2 font-weight-medium ml-3">
|
||||
{{ tm('onboard.completed') }}
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-body-2 text-medium-emphasis mb-3">{{ tm('onboard.step3Desc') }}</p>
|
||||
<div class="d-flex flex-wrap align-center ga-3">
|
||||
<v-select
|
||||
v-model="computerAccessRuntime"
|
||||
:items="computerAccessOptions"
|
||||
item-title="title"
|
||||
item-value="value"
|
||||
:label="tm('onboard.step3SelectLabel')"
|
||||
:loading="savingComputerAccess"
|
||||
:disabled="savingComputerAccess"
|
||||
hide-details
|
||||
density="comfortable"
|
||||
variant="outlined"
|
||||
class="computer-access-select"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</v-timeline-item>
|
||||
@@ -136,6 +168,26 @@
|
||||
<AddNewPlatform v-model:show="showAddPlatformDialog" :metadata="platformMetadata" :config_data="platformConfigData"
|
||||
@refresh-config="loadPlatformConfigBase" />
|
||||
<ProviderConfigDialog v-model="showProviderDialog" />
|
||||
<v-dialog v-model="showComputerAccessHelpDialog" max-width="640">
|
||||
<v-card>
|
||||
<v-card-title class="text-h3 font-weight-bold pa-4">
|
||||
{{ tm('onboard.step3HelpTitle') }}
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<ol class="computer-access-help-list">
|
||||
<li>{{ tm('onboard.step3HelpItem1') }}</li>
|
||||
<li>{{ tm('onboard.step3HelpItem2') }}</li>
|
||||
<li>{{ tm('onboard.step3HelpItem3') }}</li>
|
||||
</ol>
|
||||
</v-card-text>
|
||||
<v-card-actions class="px-6 pb-4">
|
||||
<v-spacer />
|
||||
<v-btn color="primary" variant="text" @click="showComputerAccessHelpDialog = false">
|
||||
{{ tm('onboard.step3HelpClose') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -151,6 +203,7 @@ import 'markstream-vue/index.css';
|
||||
import 'highlight.js/styles/github.css';
|
||||
|
||||
type StepState = 'pending' | 'completed' | 'skipped';
|
||||
type ComputerAccessRuntime = 'local' | 'none';
|
||||
|
||||
const { tm } = useModuleI18n('features/welcome');
|
||||
const { locale } = useI18n();
|
||||
@@ -158,6 +211,7 @@ const { success: showSuccess, error: showError } = useToast();
|
||||
|
||||
const showAddPlatformDialog = ref(false);
|
||||
const showProviderDialog = ref(false);
|
||||
const showComputerAccessHelpDialog = ref(false);
|
||||
const loadingPlatformDialog = ref(false);
|
||||
|
||||
const platformMetadata = ref<Record<string, any>>({});
|
||||
@@ -167,6 +221,10 @@ const providerCountBeforeOpen = ref(0);
|
||||
|
||||
const platformStepState = ref<StepState>('pending');
|
||||
const providerStepState = ref<StepState>('pending');
|
||||
const computerAccessStepState = ref<StepState>('pending');
|
||||
const computerAccessRuntime = ref<ComputerAccessRuntime>('none');
|
||||
const savedComputerAccessRuntime = ref<ComputerAccessRuntime>('none');
|
||||
const savingComputerAccess = ref(false);
|
||||
const welcomeAnnouncementRaw = ref<unknown>(null);
|
||||
|
||||
function resolveWelcomeAnnouncement(raw: unknown, currentLocale: string) {
|
||||
@@ -276,6 +334,11 @@ async function loadPlatformConfigBase() {
|
||||
platformConfigData.value = res.data.data.config || {};
|
||||
}
|
||||
|
||||
async function fetchDefaultConfig() {
|
||||
const res = await axios.get('/api/config/abconf', { params: { id: 'default' } });
|
||||
return res.data?.data?.config || {};
|
||||
}
|
||||
|
||||
function getChatProvidersFromTemplatePayload(payload: any) {
|
||||
const providers = payload?.providers || [];
|
||||
const sources = payload?.provider_sources || [];
|
||||
@@ -315,8 +378,7 @@ async function syncDefaultConfigProviderIfNeeded() {
|
||||
const targetProviderId = pickDefaultProviderId(providers);
|
||||
if (!targetProviderId) return;
|
||||
|
||||
const configRes = await axios.get('/api/config/abconf', { params: { id: 'default' } });
|
||||
const configData = configRes.data?.data?.config || {};
|
||||
const configData = await fetchDefaultConfig();
|
||||
if (!configData.provider_settings) {
|
||||
configData.provider_settings = {};
|
||||
}
|
||||
@@ -336,6 +398,62 @@ async function syncDefaultConfigProviderIfNeeded() {
|
||||
showSuccess(tm('onboard.providerDefaultUpdated', { id: targetProviderId }));
|
||||
}
|
||||
|
||||
function normalizeComputerAccessRuntime(runtime: unknown): ComputerAccessRuntime {
|
||||
return runtime === 'local' || runtime === 'sandbox' ? 'local' : 'none';
|
||||
}
|
||||
|
||||
function syncComputerAccessRuntime(configData: any) {
|
||||
const providerSettings = configData?.provider_settings || {};
|
||||
const currentRuntime = providerSettings?.computer_use_runtime;
|
||||
const normalizedRuntime = normalizeComputerAccessRuntime(currentRuntime);
|
||||
|
||||
computerAccessRuntime.value = normalizedRuntime;
|
||||
savedComputerAccessRuntime.value = normalizedRuntime;
|
||||
computerAccessStepState.value =
|
||||
currentRuntime === 'local' || currentRuntime === 'none' || currentRuntime === 'sandbox'
|
||||
? 'completed'
|
||||
: 'pending';
|
||||
}
|
||||
|
||||
const computerAccessOptions = computed(() => [
|
||||
{ title: tm('onboard.step3Allow'), value: 'local' },
|
||||
{ title: tm('onboard.step3Deny'), value: 'none' }
|
||||
]);
|
||||
|
||||
async function saveComputerAccessRuntime() {
|
||||
savingComputerAccess.value = true;
|
||||
try {
|
||||
const configData = await fetchDefaultConfig();
|
||||
if (!configData.provider_settings) {
|
||||
configData.provider_settings = {};
|
||||
}
|
||||
|
||||
configData.provider_settings.computer_use_runtime = computerAccessRuntime.value;
|
||||
|
||||
const updateRes = await axios.post('/api/config/astrbot/update', {
|
||||
conf_id: 'default',
|
||||
config: configData
|
||||
});
|
||||
if (updateRes.data.status !== 'ok') {
|
||||
throw new Error(updateRes.data.message || tm('onboard.computerAccessUpdateFailed'));
|
||||
}
|
||||
|
||||
savedComputerAccessRuntime.value = computerAccessRuntime.value;
|
||||
computerAccessStepState.value = 'completed';
|
||||
showSuccess(
|
||||
tm(
|
||||
computerAccessRuntime.value === 'local'
|
||||
? 'onboard.computerAccessAllowed'
|
||||
: 'onboard.computerAccessDenied'
|
||||
)
|
||||
);
|
||||
} catch (err: any) {
|
||||
showError(err?.response?.data?.message || err?.message || tm('onboard.computerAccessUpdateFailed'));
|
||||
} finally {
|
||||
savingComputerAccess.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadWelcomeAnnouncement() {
|
||||
try {
|
||||
const res = await axios.get('https://cloud.astrbot.app/api/v1/announcement');
|
||||
@@ -366,6 +484,13 @@ onMounted(async () => {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
try {
|
||||
const defaultConfig = await fetchDefaultConfig();
|
||||
syncComputerAccessRuntime(defaultConfig);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
async function openPlatformDialog() {
|
||||
@@ -416,6 +541,18 @@ watch(showProviderDialog, async (visible, wasVisible) => {
|
||||
showError(err?.response?.data?.message || err?.message || tm('onboard.providerUpdateFailed'));
|
||||
}
|
||||
});
|
||||
|
||||
watch(computerAccessRuntime, async (value, oldValue) => {
|
||||
if (value === oldValue) return;
|
||||
if (value === savedComputerAccessRuntime.value) return;
|
||||
if (savingComputerAccess.value) return;
|
||||
|
||||
try {
|
||||
await saveComputerAccessRuntime();
|
||||
} catch {
|
||||
computerAccessRuntime.value = savedComputerAccessRuntime.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -430,4 +567,14 @@ watch(showProviderDialog, async (visible, wasVisible) => {
|
||||
.welcome-announcement-markdown {
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.computer-access-select {
|
||||
max-width: 240px;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.computer-access-help-list {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user