Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 800c9d8344 | |||
| f132964436 | |||
| 42e84afd89 | |||
| a7ed6b8c76 | |||
| ee43b98ce6 | |||
| 681b4747a6 | |||
| a6da4ebe5e | |||
| e35a604b30 | |||
| 19651d24bb | |||
| dba08edd0d | |||
| dc06bc943a |
@@ -19,7 +19,6 @@ from astrbot.core.astr_agent_hooks import MAIN_AGENT_HOOKS
|
||||
from astrbot.core.astr_agent_run_util import AgentRunner
|
||||
from astrbot.core.astr_agent_tool_exec import FunctionToolExecutor
|
||||
from astrbot.core.astr_main_agent_resources import (
|
||||
CHATUI_EXTRA_PROMPT,
|
||||
CHATUI_SPECIAL_DEFAULT_PERSONA_PROMPT,
|
||||
EXECUTE_SHELL_TOOL,
|
||||
FILE_DOWNLOAD_TOOL,
|
||||
@@ -259,6 +258,8 @@ async def _ensure_persona_and_skills(
|
||||
return
|
||||
|
||||
# get persona ID
|
||||
|
||||
# 1. from session service config - highest priority
|
||||
persona_id = (
|
||||
await sp.get_async(
|
||||
scope="umo",
|
||||
@@ -269,14 +270,15 @@ async def _ensure_persona_and_skills(
|
||||
).get("persona_id")
|
||||
|
||||
if not persona_id:
|
||||
persona_id = req.conversation.persona_id or cfg.get("default_personality")
|
||||
if persona_id is None or persona_id != "[%None]":
|
||||
default_persona = plugin_context.persona_manager.selected_default_persona_v3
|
||||
if default_persona:
|
||||
persona_id = default_persona["name"]
|
||||
if event.get_platform_name() == "webchat":
|
||||
persona_id = "_chatui_default_"
|
||||
req.system_prompt += CHATUI_SPECIAL_DEFAULT_PERSONA_PROMPT
|
||||
# 2. from conversation setting - second priority
|
||||
persona_id = req.conversation.persona_id
|
||||
|
||||
if persona_id == "[%None]":
|
||||
# explicitly set to no persona
|
||||
pass
|
||||
elif persona_id is None:
|
||||
# 3. from config default persona setting - last priority
|
||||
persona_id = cfg.get("default_personality")
|
||||
|
||||
persona = next(
|
||||
builtins.filter(
|
||||
@@ -291,6 +293,11 @@ async def _ensure_persona_and_skills(
|
||||
req.system_prompt += f"\n# Persona Instructions\n\n{prompt}\n"
|
||||
if begin_dialogs := copy.deepcopy(persona.get("_begin_dialogs_processed")):
|
||||
req.contexts[:0] = begin_dialogs
|
||||
else:
|
||||
# special handling for webchat persona
|
||||
if event.get_platform_name() == "webchat" and persona_id != "[%None]":
|
||||
persona_id = "_chatui_default_"
|
||||
req.system_prompt += CHATUI_SPECIAL_DEFAULT_PERSONA_PROMPT
|
||||
|
||||
# Inject skills prompt
|
||||
skills_cfg = cfg.get("skills", {})
|
||||
@@ -931,7 +938,6 @@ async def build_main_agent(
|
||||
|
||||
if event.get_platform_name() == "webchat":
|
||||
asyncio.create_task(_handle_webchat(event, req, provider))
|
||||
req.system_prompt += f"\n{CHATUI_EXTRA_PROMPT}\n"
|
||||
|
||||
if req.func_tool and req.func_tool.tools:
|
||||
tool_prompt = (
|
||||
|
||||
@@ -78,9 +78,6 @@ CHATUI_SPECIAL_DEFAULT_PERSONA_PROMPT = (
|
||||
"You listen more than you speak, respect uncertainty, avoid forcing quick conclusions or grand narratives, "
|
||||
"and prefer clear, restrained language over unnecessary emotional embellishment. At your core, you value "
|
||||
"empathy, clarity, autonomy, and meaning, favoring steady, sustainable progress over judgment or dramatic leaps."
|
||||
)
|
||||
|
||||
CHATUI_EXTRA_PROMPT = (
|
||||
'When you answered, you need to add a follow up question / summarization but do not add "Follow up" words. '
|
||||
"Such as, user asked you to generate codes, you can add: Do you need me to run these codes for you?"
|
||||
)
|
||||
|
||||
@@ -114,6 +114,9 @@ DEFAULT_CONFIG = {
|
||||
"provider": "moonshotai",
|
||||
"moonshotai_api_key": "",
|
||||
},
|
||||
"proactive_capability": {
|
||||
"add_cron_tools": True,
|
||||
},
|
||||
"sandbox": {
|
||||
"enable": False,
|
||||
"booter": "shipyard",
|
||||
@@ -2232,6 +2235,14 @@ CONFIG_METADATA_2 = {
|
||||
},
|
||||
},
|
||||
},
|
||||
"proactive_capability": {
|
||||
"type": "object",
|
||||
"items": {
|
||||
"add_cron_tools": {
|
||||
"type": "bool",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"provider_stt_settings": {
|
||||
@@ -2684,6 +2695,7 @@ CONFIG_METADATA_3 = {
|
||||
"skills": {
|
||||
"description": "Skills",
|
||||
"type": "object",
|
||||
"hint": "",
|
||||
"items": {
|
||||
"provider_settings.skills.runtime": {
|
||||
"description": "Skill Runtime",
|
||||
@@ -2698,7 +2710,24 @@ CONFIG_METADATA_3 = {
|
||||
"provider_settings.enable": True,
|
||||
},
|
||||
},
|
||||
"proactive_capability": {
|
||||
"description": "主动型 Agent",
|
||||
"hint": "https://docs.astrbot.app/use/proactive-agent.html",
|
||||
"type": "object",
|
||||
"items": {
|
||||
"provider_settings.proactive_capability.add_cron_tools": {
|
||||
"description": "启用",
|
||||
"type": "bool",
|
||||
"hint": "启用后,将会传递给 Agent 相关工具来实现主动型 Agent。你可以告诉 AstrBot 未来某个时间要做的事情,它将被定时触发然后执行任务。",
|
||||
},
|
||||
},
|
||||
"condition": {
|
||||
"provider_settings.agent_runner_type": "local",
|
||||
"provider_settings.enable": True,
|
||||
},
|
||||
},
|
||||
"truncate_and_compress": {
|
||||
"hint": "",
|
||||
"description": "上下文管理策略",
|
||||
"type": "object",
|
||||
"items": {
|
||||
|
||||
@@ -94,6 +94,10 @@ class InternalAgentSubStage(Stage):
|
||||
|
||||
self.sandbox_cfg = settings.get("sandbox", {})
|
||||
|
||||
# Proactive capability configuration
|
||||
proactive_cfg = settings.get("proactive_capability", {})
|
||||
self.add_cron_tools = proactive_cfg.get("add_cron_tools", True)
|
||||
|
||||
self.conv_manager = ctx.plugin_manager.context.conversation_manager
|
||||
|
||||
self.main_agent_cfg = MainAgentBuildConfig(
|
||||
@@ -113,6 +117,7 @@ class InternalAgentSubStage(Stage):
|
||||
llm_safety_mode=self.llm_safety_mode,
|
||||
safety_mode_strategy=self.safety_mode_strategy,
|
||||
sandbox_cfg=self.sandbox_cfg,
|
||||
add_cron_tools=self.add_cron_tools,
|
||||
provider_settings=settings,
|
||||
subagent_orchestrator=conf.get("subagent_orchestrator", {}),
|
||||
timezone=self.ctx.plugin_manager.context.get_config().get("timezone"),
|
||||
|
||||
@@ -4,7 +4,6 @@ import traceback
|
||||
from quart import request
|
||||
|
||||
from astrbot.core import DEMO_MODE, logger
|
||||
from astrbot.core.computer.computer_client import get_booter
|
||||
from astrbot.core.skills.skill_manager import SkillManager
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_temp_path
|
||||
|
||||
@@ -60,41 +59,9 @@ class SkillsRoute(Route):
|
||||
temp_path = os.path.join(temp_dir, filename)
|
||||
await file.save(temp_path)
|
||||
|
||||
cfg = self.core_lifecycle.astrbot_config.get("provider_settings", {}).get(
|
||||
"skills", {}
|
||||
)
|
||||
runtime = cfg.get("runtime", "local")
|
||||
if runtime == "sandbox":
|
||||
sandbox_enabled = (
|
||||
self.core_lifecycle.astrbot_config.get("provider_settings", {})
|
||||
.get("sandbox", {})
|
||||
.get("enable", False)
|
||||
)
|
||||
if not sandbox_enabled:
|
||||
return (
|
||||
Response()
|
||||
.error(
|
||||
"Sandbox is not enabled. Please enable sandbox before using sandbox runtime."
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
skill_mgr = SkillManager()
|
||||
skill_name = skill_mgr.install_skill_from_zip(temp_path, overwrite=True)
|
||||
|
||||
if runtime == "sandbox":
|
||||
sb = await get_booter(self.core_lifecycle.star_context, "skills-upload")
|
||||
remote_root = "/home/shared/skills"
|
||||
remote_zip = f"{remote_root}/{skill_name}.zip"
|
||||
await sb.shell.exec(f"mkdir -p {remote_root}")
|
||||
upload_result = await sb.upload_file(temp_path, remote_zip)
|
||||
if not upload_result.get("success", False):
|
||||
return (
|
||||
Response().error("Failed to upload skill to sandbox").__dict__
|
||||
)
|
||||
await sb.shell.exec(
|
||||
f"unzip -o {remote_zip} -d {remote_root} && rm -f {remote_zip}"
|
||||
)
|
||||
|
||||
return (
|
||||
Response()
|
||||
.ok({"name": skill_name}, "Skill uploaded successfully.")
|
||||
|
||||
@@ -130,19 +130,25 @@ class ToolsRoute(Route):
|
||||
server_data = await request.json
|
||||
|
||||
name = server_data.get("name", "")
|
||||
old_name = server_data.get("oldName") or name
|
||||
|
||||
if not name:
|
||||
return Response().error("服务器名称不能为空").__dict__
|
||||
|
||||
config = self.tool_mgr.load_mcp_config()
|
||||
|
||||
if name not in config["mcpServers"]:
|
||||
return Response().error(f"服务器 {name} 不存在").__dict__
|
||||
if old_name not in config["mcpServers"]:
|
||||
return Response().error(f"服务器 {old_name} 不存在").__dict__
|
||||
|
||||
is_rename = name != old_name
|
||||
|
||||
if name in config["mcpServers"] and is_rename:
|
||||
return Response().error(f"服务器 {name} 已存在").__dict__
|
||||
|
||||
# 获取活动状态
|
||||
active = server_data.get(
|
||||
"active",
|
||||
config["mcpServers"][name].get("active", True),
|
||||
config["mcpServers"][old_name].get("active", True),
|
||||
)
|
||||
|
||||
# 创建新的配置对象
|
||||
@@ -153,7 +159,13 @@ class ToolsRoute(Route):
|
||||
|
||||
# 复制所有配置字段
|
||||
for key, value in server_data.items():
|
||||
if key not in ["name", "active", "tools", "errlogs"]: # 排除特殊字段
|
||||
if key not in [
|
||||
"name",
|
||||
"active",
|
||||
"tools",
|
||||
"errlogs",
|
||||
"oldName",
|
||||
]: # 排除特殊字段
|
||||
if key == "mcpServers":
|
||||
key_0 = list(server_data["mcpServers"].keys())[
|
||||
0
|
||||
@@ -165,29 +177,42 @@ class ToolsRoute(Route):
|
||||
|
||||
# 如果只更新活动状态,保留原始配置
|
||||
if only_update_active:
|
||||
for key, value in config["mcpServers"][name].items():
|
||||
for key, value in config["mcpServers"][old_name].items():
|
||||
if key != "active": # 除了active之外的所有字段都保留
|
||||
server_config[key] = value
|
||||
|
||||
config["mcpServers"][name] = server_config
|
||||
# config["mcpServers"][name] = server_config
|
||||
if is_rename:
|
||||
config["mcpServers"].pop(old_name)
|
||||
config["mcpServers"][name] = server_config
|
||||
else:
|
||||
config["mcpServers"][name] = server_config
|
||||
|
||||
if self.tool_mgr.save_mcp_config(config):
|
||||
# 处理MCP客户端状态变化
|
||||
if active:
|
||||
if name in self.tool_mgr.mcp_client_dict or not only_update_active:
|
||||
if (
|
||||
old_name in self.tool_mgr.mcp_client_dict
|
||||
or not only_update_active
|
||||
or is_rename
|
||||
):
|
||||
try:
|
||||
await self.tool_mgr.disable_mcp_server(name, timeout=10)
|
||||
await self.tool_mgr.disable_mcp_server(old_name, timeout=10)
|
||||
except TimeoutError as e:
|
||||
return (
|
||||
Response()
|
||||
.error(f"启用前停用 MCP 服务器时 {name} 超时: {e!s}")
|
||||
.error(
|
||||
f"启用前停用 MCP 服务器时 {old_name} 超时: {e!s}"
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(traceback.format_exc())
|
||||
return (
|
||||
Response()
|
||||
.error(f"启用前停用 MCP 服务器时 {name} 失败: {e!s}")
|
||||
.error(
|
||||
f"启用前停用 MCP 服务器时 {old_name} 失败: {e!s}"
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
try:
|
||||
@@ -208,18 +233,20 @@ class ToolsRoute(Route):
|
||||
.__dict__
|
||||
)
|
||||
# 如果要停用服务器
|
||||
elif name in self.tool_mgr.mcp_client_dict:
|
||||
elif old_name in self.tool_mgr.mcp_client_dict:
|
||||
try:
|
||||
await self.tool_mgr.disable_mcp_server(name, timeout=10)
|
||||
await self.tool_mgr.disable_mcp_server(old_name, timeout=10)
|
||||
except TimeoutError:
|
||||
return (
|
||||
Response().error(f"停用 MCP 服务器 {name} 超时。").__dict__
|
||||
Response()
|
||||
.error(f"停用 MCP 服务器 {old_name} 超时。")
|
||||
.__dict__
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(traceback.format_exc())
|
||||
return (
|
||||
Response()
|
||||
.error(f"停用 MCP 服务器 {name} 失败: {e!s}")
|
||||
.error(f"停用 MCP 服务器 {old_name} 失败: {e!s}")
|
||||
.__dict__
|
||||
)
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
<!-- Reasoning Block (Collapsible) - 放在最前面 -->
|
||||
<ReasoningBlock v-if="msg.content.reasoning && msg.content.reasoning.trim()"
|
||||
:reasoning="msg.content.reasoning" :is-dark="isDark"
|
||||
class="mt-2"
|
||||
:initial-expanded="isReasoningExpanded(index)" />
|
||||
|
||||
<MessagePartsRenderer :parts="msg.content.message" :is-dark="isDark"
|
||||
@@ -1203,37 +1204,6 @@ export default {
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.embedded-images {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.embedded-image {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.bot-embedded-image {
|
||||
max-width: 55%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.embedded-audio {
|
||||
width: 300px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.embedded-audio .audio-player {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
/* 文件附件样式 */
|
||||
.file-attachments,
|
||||
.embedded-files {
|
||||
|
||||
@@ -331,4 +331,86 @@ const getRenderParts = (messageParts) => {
|
||||
.tool-call-chevron.rotated {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
|
||||
.embedded-images {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.embedded-image {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.bot-embedded-image {
|
||||
max-width: 55%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.embedded-audio {
|
||||
width: 300px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.embedded-audio .audio-player {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
/* 文件附件样式 */
|
||||
.file-attachments,
|
||||
.embedded-files {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.file-attachment,
|
||||
.embedded-file {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
/* 文件附件样式 */
|
||||
.file-attachments,
|
||||
.embedded-files {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.file-attachment,
|
||||
.embedded-file {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 12px;
|
||||
background-color: rgba(var(--v-theme-primary), 0.08);
|
||||
border: 1px solid rgba(var(--v-theme-primary), 0.2);
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
transition: all 0.2s ease;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.file-link-download {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
<template>
|
||||
<div class="mb-3 mt-1.5 border border-gray-200 dark:border-gray-700 rounded-2xl overflow-hidden w-fit"
|
||||
:class="{ 'dark:bg-purple-900/8': isDark, 'bg-purple-50/50': !isDark }">
|
||||
<div class="inline-flex items-center px-2 py-2 cursor-pointer select-none rounded-2xl transition-colors hover:bg-purple-50/80 dark:hover:bg-purple-900/15"
|
||||
@click="toggleExpanded">
|
||||
<v-icon size="small" class="mr-1.5 text-purple-600 dark:text-purple-400 transition-transform"
|
||||
:class="{ 'rotate-90': isExpanded }">
|
||||
<div class="reasoning-block" :class="{ 'reasoning-block--dark': isDark }">
|
||||
<div class="reasoning-header" @click="toggleExpanded">
|
||||
<v-icon size="small" class="reasoning-icon" :class="{ 'rotate-90': isExpanded }">
|
||||
mdi-chevron-right
|
||||
</v-icon>
|
||||
<span class="text-sm font-medium text-purple-600 dark:text-purple-400 tracking-wide">
|
||||
<span class="reasoning-title">
|
||||
{{ tm('reasoning.thinking') }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="isExpanded" class="px-3 border-t border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 animate-fade-in italic">
|
||||
<MarkdownRender :content="reasoning" class="reasoning-text markdown-content text-sm leading-relaxed"
|
||||
<div v-if="isExpanded" class="reasoning-content animate-fade-in">
|
||||
<MarkdownRender :content="reasoning" class="reasoning-text markdown-content"
|
||||
:typewriter="false" :is-dark="isDark" :style="isDark ? { opacity: '0.85' } : {}" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,6 +44,63 @@ const toggleExpanded = () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
/* Reasoning 区块样式 */
|
||||
.reasoning-container {
|
||||
margin-bottom: 12px;
|
||||
margin-top: 6px;
|
||||
border: 1px solid var(--v-theme-border);
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.reasoning-header {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 8px 8px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background-color 0.2s ease;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.reasoning-header:hover {
|
||||
background-color: rgba(103, 58, 183, 0.08);
|
||||
}
|
||||
|
||||
.reasoning-header.is-dark:hover {
|
||||
background-color: rgba(103, 58, 183, 0.15);
|
||||
}
|
||||
|
||||
.reasoning-icon {
|
||||
margin-right: 6px;
|
||||
color: var(--v-theme-secondary);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.reasoning-label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--v-theme-secondary);
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.reasoning-content {
|
||||
padding: 0px 12px;
|
||||
border-top: 1px solid var(--v-theme-border);
|
||||
color: gray;
|
||||
animation: fadeIn 0.2s ease-in-out;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.reasoning-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--v-theme-secondaryText);
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.2s ease-in-out;
|
||||
}
|
||||
@@ -65,9 +119,4 @@ const toggleExpanded = () => {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.reasoning-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--v-theme-secondaryText);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -81,10 +81,10 @@
|
||||
</v-container>
|
||||
|
||||
<!-- 添加/编辑 MCP 服务器对话框 -->
|
||||
<v-dialog v-model="showMcpServerDialog" max-width="750px" persistent>
|
||||
<v-dialog v-model="showMcpServerDialog" max-width="750px">
|
||||
<v-card>
|
||||
<v-card-title class="bg-primary text-white py-3">
|
||||
<v-icon color="white" class="me-2">{{ isEditMode ? 'mdi-pencil' : 'mdi-plus' }}</v-icon>
|
||||
<v-card-title class="pa-4 pl-6">
|
||||
<v-icon class="me-2">{{ isEditMode ? 'mdi-pencil' : 'mdi-plus' }}</v-icon>
|
||||
<span>{{ isEditMode ? tm('dialogs.addServer.editTitle') : tm('dialogs.addServer.title') }}</span>
|
||||
</v-card-title>
|
||||
|
||||
@@ -251,6 +251,7 @@ export default {
|
||||
active: true,
|
||||
tools: []
|
||||
},
|
||||
originalServerName: '',
|
||||
save_message_snack: false,
|
||||
save_message: '',
|
||||
save_message_success: 'success'
|
||||
@@ -359,6 +360,9 @@ export default {
|
||||
active: this.currentServer.active,
|
||||
...configObj
|
||||
};
|
||||
if (this.isEditMode && this.originalServerName) {
|
||||
serverData.oldName = this.originalServerName;
|
||||
}
|
||||
const endpoint = this.isEditMode ? '/api/tools/mcp/update' : '/api/tools/mcp/add';
|
||||
axios.post(endpoint, serverData)
|
||||
.then(response => {
|
||||
@@ -402,6 +406,7 @@ export default {
|
||||
active: server.active,
|
||||
tools: server.tools || []
|
||||
};
|
||||
this.originalServerName = server.name;
|
||||
this.serverConfigJson = JSON.stringify(configCopy, null, 2);
|
||||
this.isEditMode = true;
|
||||
this.showMcpServerDialog = true;
|
||||
@@ -461,6 +466,7 @@ export default {
|
||||
this.serverConfigJson = '';
|
||||
this.jsonError = null;
|
||||
this.isEditMode = false;
|
||||
this.originalServerName = '';
|
||||
},
|
||||
showSuccess(message) {
|
||||
this.save_message = message;
|
||||
|
||||
@@ -42,10 +42,10 @@
|
||||
|
||||
<v-dialog v-model="uploadDialog" max-width="520px" persistent>
|
||||
<v-card>
|
||||
<v-card-title>{{ tm('skills.uploadDialogTitle') }}</v-card-title>
|
||||
<v-card-title class="text-h3 pa-4 pb-0 pl-6">{{ tm('skills.uploadDialogTitle') }}</v-card-title>
|
||||
<v-card-text>
|
||||
<small class="text-grey">{{ tm('skills.uploadHint') }}</small>
|
||||
<v-file-input v-model="uploadFile" accept=".zip" :label="tm('skills.selectFile')" prepend-icon="mdi-file-zip"
|
||||
<v-file-input v-model="uploadFile" accept=".zip" :label="tm('skills.selectFile')" prepend-icon="mdi-folder-zip-outline"
|
||||
variant="outlined" class="mt-4" :multiple="false" />
|
||||
</v-card-text>
|
||||
<v-card-actions class="d-flex justify-end">
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
}
|
||||
},
|
||||
"skills": {
|
||||
"hint": "https://docs.astrbot.app/use/skills.html",
|
||||
"description": "Skills",
|
||||
"provider_settings": {
|
||||
"skills": {
|
||||
@@ -175,7 +176,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"proactive_capability": {
|
||||
"description": "Proactive Agent",
|
||||
"hint": "https://docs.astrbot.app/en/use/proactive-agent.html",
|
||||
"provider_settings": {
|
||||
"proactive_capability": {
|
||||
"add_cron_tools": {
|
||||
"description": "Enable",
|
||||
"hint": "When enabled, related tools will be passed to the Agent to implement proactive Agent capabilities. You can tell AstrBot what to do at a future time, and it will be triggered on schedule to execute the task, and report the result back to you."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"truncate_and_compress": {
|
||||
"hint": "https://docs.astrbot.app/en/use/context-compress.html",
|
||||
"description": "Context Management Strategy",
|
||||
"provider_settings": {
|
||||
"max_context_length": {
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
}
|
||||
},
|
||||
"skills": {
|
||||
"hint": "https://docs.astrbot.app/en/use/skills.html",
|
||||
"description": "Skills",
|
||||
"provider_settings": {
|
||||
"skills": {
|
||||
@@ -175,7 +176,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"proactive_capability": {
|
||||
"description": "主动型 Agent",
|
||||
"hint": "https://docs.astrbot.app/use/proactive-agent.html",
|
||||
"provider_settings": {
|
||||
"proactive_capability": {
|
||||
"add_cron_tools": {
|
||||
"description": "启用",
|
||||
"hint": "启用后,将会传递给 Agent 相关工具来实现主动型 Agent。你可以告诉 AstrBot 未来某个时间要做的事情,它将被定时触发然后执行任务,然后将结果发送给你。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"truncate_and_compress": {
|
||||
"hint": "https://docs.astrbot.app/use/context-compress.html",
|
||||
"description": "上下文管理策略",
|
||||
"provider_settings": {
|
||||
"max_context_length": {
|
||||
|
||||
@@ -28,17 +28,13 @@
|
||||
|
||||
<v-alert v-if="!jobs.length && !loading" type="info" variant="tonal">{{ tm('table.empty') }}</v-alert>
|
||||
|
||||
<v-data-table
|
||||
:items="jobs"
|
||||
:headers="headers"
|
||||
:loading="loading"
|
||||
item-key="job_id"
|
||||
density="comfortable"
|
||||
class="elevation-0"
|
||||
>
|
||||
<v-data-table :items="jobs" :headers="headers" :loading="loading" item-key="job_id" density="comfortable"
|
||||
class="elevation-0">
|
||||
<template #item.name="{ item }">
|
||||
<div class="font-weight-medium">{{ item.name }}</div>
|
||||
<div class="text-caption text-medium-emphasis">{{ item.description }}</div>
|
||||
<div class="py-4">
|
||||
<div class="font-weight-medium">{{ item.name }}</div>
|
||||
<div class="text-caption text-medium-emphasis">{{ item.description }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #item.type="{ item }">
|
||||
<v-chip size="small" :color="item.run_once ? 'orange' : 'primary'" variant="tonal">
|
||||
@@ -57,15 +53,10 @@
|
||||
<template #item.note="{ item }">{{ item.note || tm('table.notAvailable') }}</template>
|
||||
<template #item.actions="{ item }">
|
||||
<div class="d-flex" style="gap: 8px;">
|
||||
<v-switch
|
||||
v-model="item.enabled"
|
||||
inset
|
||||
density="compact"
|
||||
hide-details
|
||||
color="primary"
|
||||
@change="toggleJob(item)"
|
||||
/>
|
||||
<v-btn size="small" variant="text" color="primary" @click="deleteJob(item)">{{ tm('actions.delete') }}</v-btn>
|
||||
<v-switch v-model="item.enabled" inset density="compact" hide-details color="primary"
|
||||
@change="toggleJob(item)" />
|
||||
<v-btn size="small" variant="text" color="primary" @click="deleteJob(item)">{{ tm('actions.delete')
|
||||
}}</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</v-data-table>
|
||||
@@ -83,39 +74,19 @@
|
||||
<v-switch v-model="newJob.run_once" :label="tm('form.runOnce')" inset color="primary" hide-details />
|
||||
<v-text-field v-model="newJob.name" :label="tm('form.name')" variant="outlined" density="comfortable" />
|
||||
<v-text-field v-model="newJob.note" :label="tm('form.note')" variant="outlined" density="comfortable" />
|
||||
<v-text-field
|
||||
v-if="!newJob.run_once"
|
||||
v-model="newJob.cron_expression"
|
||||
:label="tm('form.cron')"
|
||||
:placeholder="tm('form.cronPlaceholder')"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
/>
|
||||
<v-text-field
|
||||
v-else
|
||||
v-model="newJob.run_at"
|
||||
:label="tm('form.runAt')"
|
||||
type="datetime-local"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="newJob.session"
|
||||
:label="tm('form.session')"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="newJob.timezone"
|
||||
:label="tm('form.timezone')"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
/>
|
||||
<v-text-field v-if="!newJob.run_once" v-model="newJob.cron_expression" :label="tm('form.cron')"
|
||||
:placeholder="tm('form.cronPlaceholder')" variant="outlined" density="comfortable" />
|
||||
<v-text-field v-else v-model="newJob.run_at" :label="tm('form.runAt')" type="datetime-local"
|
||||
variant="outlined" density="comfortable" />
|
||||
<v-text-field v-model="newJob.session" :label="tm('form.session')" variant="outlined" density="comfortable" />
|
||||
<v-text-field v-model="newJob.timezone" :label="tm('form.timezone')" variant="outlined"
|
||||
density="comfortable" />
|
||||
<v-switch v-model="newJob.enabled" :label="tm('form.enabled')" inset color="primary" hide-details />
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-end">
|
||||
<v-btn variant="text" @click="createDialog = false">{{ tm('actions.cancel') }}</v-btn>
|
||||
<v-btn variant="tonal" color="primary" :loading="creating" @click="createJob">{{ tm('actions.submit') }}</v-btn>
|
||||
<v-btn variant="tonal" color="primary" :loading="creating" @click="createJob">{{ tm('actions.submit')
|
||||
}}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
@@ -2433,7 +2433,9 @@ watch(isListView, (newVal) => {
|
||||
></v-progress-linear>
|
||||
</div>
|
||||
|
||||
<div class="v-card-title text-h5">{{ tm("dialogs.install.title") }}</div>
|
||||
<v-card-title class="text-h3 pa-4 pb-0 pl-6">
|
||||
{{ tm("dialogs.install.title") }}
|
||||
</v-card-title>
|
||||
|
||||
<div class="v-card-text">
|
||||
<v-tabs v-model="uploadTab" color="primary">
|
||||
|
||||
Reference in New Issue
Block a user