From da2b0d2fa064d237432bfa26e079ef04e90f17a4 Mon Sep 17 00:00:00 2001 From: YUZHEthefool <2804776511@qq.com> Date: Tue, 15 Sep 2026 17:15:07 +0800 Subject: [PATCH 1/2] feat(btw): move the dual-loop config into AI capabilities The BTW dual-loop group is not a plugin setting, yet its WebUI metadata lived under plugin_group.metadata.btw, so the plugin page showed it and the AI capabilities tab never did. Host the group on ai_group.metadata.btw and list it with the other capabilities, keeping the root-level btw config shape and the absolute btw.* item paths unchanged. The plugin group keeps only its selector. Label btw.work_loop.computer_use_runtime so the inherit option no longer renders as raw text. Move the bilingual config-metadata entries from plugin_group.btw to ai_group.btw and update the docs navigation paths. Fixes #183 AI-Generated: true Generated-At: 2026-09-15T09:14:29Z --- astrbot/core/config/default.py | 3 +- .../src/components/config/AiConfigPanel.vue | 1 + .../en-US/features/config-metadata.json | 103 +++++++++--------- .../zh-CN/features/config-metadata.json | 103 +++++++++--------- .../tests/aiConfigCapabilities.vitest.ts | 90 +++++++++++++++ docs/en/dev/astrbot-config.md | 2 +- docs/zh/dev/astrbot-config.md | 2 +- tests/unit/test_config_metadata_i18n.py | 21 ++-- 8 files changed, 213 insertions(+), 112 deletions(-) create mode 100644 dashboard/tests/aiConfigCapabilities.vitest.ts diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index ae84cd0bba..a458c52d86 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -4736,7 +4736,7 @@ }, } -CONFIG_METADATA_3["plugin_group"]["metadata"]["btw"] = { +CONFIG_METADATA_3["ai_group"]["metadata"]["btw"] = { "description": "BTW 双循环", "type": "object", "items": { @@ -4769,6 +4769,7 @@ "description": "工作循环 Computer Use 运行时", "type": "string", "options": ["inherit", "none", "local", "sandbox"], + "labels": ["继承", "无", "本地", "沙箱"], "hint": "inherit 沿用当前 Computer Use 配置。对话循环始终禁用电脑和文件工具;工作循环仍须满足已有角色、路径和沙箱授权规则。", "condition": {"btw.enabled": True}, }, diff --git a/dashboard/src/components/config/AiConfigPanel.vue b/dashboard/src/components/config/AiConfigPanel.vue index 612b3f9f02..a962e03979 100644 --- a/dashboard/src/components/config/AiConfigPanel.vue +++ b/dashboard/src/components/config/AiConfigPanel.vue @@ -368,6 +368,7 @@ const localTabGroups = computed(() => { 'websearch', 'agent_computer_use', 'proactive_capability', + 'btw', ] .filter((key) => props.metadata?.[key]) .map((key) => ({ diff --git a/dashboard/src/i18n/locales/en-US/features/config-metadata.json b/dashboard/src/i18n/locales/en-US/features/config-metadata.json index d3c058df1b..a4b9c662f1 100644 --- a/dashboard/src/i18n/locales/en-US/features/config-metadata.json +++ b/dashboard/src/i18n/locales/en-US/features/config-metadata.json @@ -409,6 +409,58 @@ } } }, + "btw": { + "description": "BTW dual loops", + "btw": { + "enabled": { + "description": "Enable BTW dual loops", + "hint": "Experimental and disabled by default. Ordinary admitted AI requests use the conversation entry over the existing Agent." + }, + "conversation_loop": { + "provider_id": { + "description": "Conversation loop model", + "hint": "Leave empty to keep the current session model selection. When set, this model takes priority." + } + }, + "work_loop": { + "enabled": { + "description": "Enable work loop", + "hint": "Disabled by default. Allow explicit work execution." + }, + "provider_id": { + "description": "Work loop model", + "hint": "Leave empty to keep the current session model selection. When set, this model takes priority." + }, + "computer_use_runtime": { + "description": "Work loop Computer Use runtime", + "hint": "inherit uses the existing Computer Use setting. The conversation loop has no computer or file tools. Work still follows existing role, path, and sandbox authorization rules.", + "labels": ["Inherit", "None", "Local", "Sandbox"] + }, + "max_concurrent": { + "description": "Concurrent work execution", + "hint": "Active execution limit, default 2. This is not a waiting-queue length limit." + } + }, + "work_session": { + "max_age_seconds": { + "description": "Terminal work retention (seconds)", + "hint": "Keep completed, failed or cancelled records for 3600 seconds by default." + } + }, + "plugin_routes": { + "description": "Plugin tool loop assignments", + "hint": "Plugin LLM tools default to the work loop; explicitly assign an enabled plugin to the conversation loop or both when needed." + }, + "mcp_routes": { + "description": "MCP server loop assignments", + "hint": "MCP tools default to the work loop; explicitly assign an enabled server to the conversation loop or both when needed." + }, + "skill_routes": { + "description": "Skill loop assignments", + "hint": "Skills default to both loops; explicitly restrict an enabled Skill to one loop when needed. Workspace Skills remain work-only with the local runtime." + } + } + }, "truncate_and_compress": { "hint": "How AstrBot manages working memory.", "description": "Context Management Strategy", @@ -1202,57 +1254,6 @@ "description": "Available Plugins", "hint": "All non-disabled plugins are enabled by default. If a plugin is disabled on the plugins page, selections here will not take effect." } - }, - "btw": { - "description": "BTW dual loops", - "btw": { - "enabled": { - "description": "Enable BTW dual loops", - "hint": "Experimental and disabled by default. Ordinary admitted AI requests use the conversation entry over the existing Agent." - }, - "work_loop": { - "enabled": { - "description": "Enable work loop", - "hint": "Disabled by default. Allow explicit work execution." - }, - "max_concurrent": { - "description": "Concurrent work execution", - "hint": "Active execution limit, default 2. This is not a waiting-queue length limit." - }, - "provider_id": { - "description": "Work loop model", - "hint": "Leave empty to keep the current session model selection. When set, this model takes priority." - }, - "computer_use_runtime": { - "description": "Work loop Computer Use runtime", - "hint": "inherit uses the existing Computer Use setting. The conversation loop has no computer or file tools. Work still follows existing role, path, and sandbox authorization rules." - } - }, - "work_session": { - "max_age_seconds": { - "description": "Terminal work retention (seconds)", - "hint": "Keep completed, failed or cancelled records for 3600 seconds by default." - } - }, - "conversation_loop": { - "provider_id": { - "description": "Conversation loop model", - "hint": "Leave empty to keep the current session model selection. When set, this model takes priority." - } - }, - "plugin_routes": { - "description": "Plugin tool loop assignments", - "hint": "Plugin LLM tools default to the work loop; explicitly assign an enabled plugin to the conversation loop or both when needed." - }, - "mcp_routes": { - "description": "MCP server loop assignments", - "hint": "MCP tools default to the work loop; explicitly assign an enabled server to the conversation loop or both when needed." - }, - "skill_routes": { - "description": "Skill loop assignments", - "hint": "Skills default to both loops; explicitly restrict an enabled Skill to one loop when needed. Workspace Skills remain work-only with the local runtime." - } - } } }, "ext_group": { diff --git a/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json b/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json index 7668b1dc35..4edb877fe8 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json +++ b/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json @@ -403,6 +403,58 @@ } } }, + "btw": { + "description": "BTW 双循环", + "btw": { + "enabled": { + "description": "启用 BTW 双循环", + "hint": "实验功能,默认关闭。普通且已通过准入的 AI 请求经对话入口使用现有 Agent。" + }, + "conversation_loop": { + "provider_id": { + "description": "对话循环模型", + "hint": "留空时沿用当前会话的模型选择。配置后优先使用此模型。" + } + }, + "work_loop": { + "enabled": { + "description": "启用工作循环", + "hint": "默认关闭;允许显式工作请求使用工作执行器。" + }, + "provider_id": { + "description": "工作循环模型", + "hint": "留空时沿用当前会话的模型选择。配置后优先使用此模型。" + }, + "computer_use_runtime": { + "description": "工作循环 Computer Use 运行时", + "hint": "inherit 沿用当前 Computer Use 配置。对话循环始终禁用电脑和文件工具;工作循环仍须满足已有角色、路径和沙箱授权规则。", + "labels": ["继承", "无", "本地", "沙箱"] + }, + "max_concurrent": { + "description": "工作任务执行并发", + "hint": "同时执行的工作任务数,默认 2。此值不是等待队列的长度限制。" + } + }, + "work_session": { + "max_age_seconds": { + "description": "终态工作会话保留秒数", + "hint": "已完成、失败或取消的工作会话保留时间,默认 3600 秒。" + } + }, + "plugin_routes": { + "description": "插件工具循环分配", + "hint": "插件 LLM 工具默认仅在工作循环可用;可为每个已启用插件显式改为对话循环或两者。" + }, + "mcp_routes": { + "description": "MCP 服务器循环分配", + "hint": "MCP 工具默认仅在工作循环可用;可为每个已启用服务器显式改为对话循环或两者。" + }, + "skill_routes": { + "description": "Skills 循环分配", + "hint": "Skill 默认注入两个循环;可为每个已启用 Skill 显式限制到单一循环。 工作区 Skill 仅在本地工作循环可用。" + } + } + }, "truncate_and_compress": { "hint": "AstrBot 如何管理工作记忆。", "description": "上下文管理策略", @@ -1196,57 +1248,6 @@ "description": "可用插件", "hint": "默认启用全部未被禁用的插件。若插件在插件页面被禁用,则此处的选择不会生效。" } - }, - "btw": { - "description": "BTW 双循环", - "btw": { - "enabled": { - "description": "启用 BTW 双循环", - "hint": "实验功能,默认关闭。普通且已通过准入的 AI 请求经对话入口使用现有 Agent。" - }, - "work_loop": { - "enabled": { - "description": "启用工作循环", - "hint": "默认关闭;允许显式工作请求使用工作执行器。" - }, - "max_concurrent": { - "description": "工作任务执行并发", - "hint": "同时执行的工作任务数,默认 2。此值不是等待队列的长度限制。" - }, - "provider_id": { - "description": "工作循环模型", - "hint": "留空时沿用当前会话的模型选择。配置后优先使用此模型。" - }, - "computer_use_runtime": { - "description": "工作循环 Computer Use 运行时", - "hint": "inherit 沿用当前 Computer Use 配置。对话循环始终禁用电脑和文件工具;工作循环仍须满足已有角色、路径和沙箱授权规则。" - } - }, - "work_session": { - "max_age_seconds": { - "description": "终态工作会话保留秒数", - "hint": "已完成、失败或取消的工作会话保留时间,默认 3600 秒。" - } - }, - "conversation_loop": { - "provider_id": { - "description": "对话循环模型", - "hint": "留空时沿用当前会话的模型选择。配置后优先使用此模型。" - } - }, - "plugin_routes": { - "description": "插件工具循环分配", - "hint": "插件 LLM 工具默认仅在工作循环可用;可为每个已启用插件显式改为对话循环或两者。" - }, - "mcp_routes": { - "description": "MCP 服务器循环分配", - "hint": "MCP 工具默认仅在工作循环可用;可为每个已启用服务器显式改为对话循环或两者。" - }, - "skill_routes": { - "description": "Skills 循环分配", - "hint": "Skill 默认注入两个循环;可为每个已启用 Skill 显式限制到单一循环。 工作区 Skill 仅在本地工作循环可用。" - } - } } }, "ext_group": { diff --git a/dashboard/tests/aiConfigCapabilities.vitest.ts b/dashboard/tests/aiConfigCapabilities.vitest.ts new file mode 100644 index 0000000000..c035bdfab3 --- /dev/null +++ b/dashboard/tests/aiConfigCapabilities.vitest.ts @@ -0,0 +1,90 @@ +import { describe, expect, it, vi } from 'vitest'; +import AiConfigPanel from '@/components/config/AiConfigPanel.vue'; +import { initI18n } from '@/i18n/composables'; +import { mountWithVuetify } from './utils/mountWithVuetify'; + +vi.mock('@/utils/monacoLoader', () => ({})); + +vi.mock('@guolao/vue-monaco-editor', () => ({ + VueMonacoEditor: { + name: 'VueMonacoEditor', + template: '
', + }, +})); + +function buildMetadata(includeBtw: boolean) { + const metadata: Record