mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-17 01:49:15 +08:00
123 lines
4.9 KiB
Python
123 lines
4.9 KiB
Python
import traceback
|
|
|
|
from quart import jsonify, request
|
|
|
|
from astrbot.core import logger
|
|
from astrbot.core.agent.handoff import HandoffTool
|
|
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
|
|
|
from .route import Response, Route, RouteContext
|
|
|
|
|
|
class SubAgentRoute(Route):
|
|
def __init__(
|
|
self,
|
|
context: RouteContext,
|
|
core_lifecycle: AstrBotCoreLifecycle,
|
|
) -> None:
|
|
super().__init__(context)
|
|
self.core_lifecycle = core_lifecycle
|
|
# NOTE: dict cannot hold duplicate keys; use list form to register multiple
|
|
# methods for the same path.
|
|
self.routes = [
|
|
("/subagent/config", ("GET", self.get_config)),
|
|
("/subagent/config", ("POST", self.update_config)),
|
|
("/subagent/available-tools", ("GET", self.get_available_tools)),
|
|
]
|
|
self.register_routes()
|
|
|
|
async def get_config(self):
|
|
try:
|
|
cfg = self.core_lifecycle.astrbot_config
|
|
data = cfg.get("subagent_orchestrator")
|
|
|
|
# First-time access: return a sane default instead of erroring.
|
|
if not isinstance(data, dict):
|
|
data = {
|
|
"main_enable": False,
|
|
"remove_main_duplicate_tools": False,
|
|
"agents": [],
|
|
}
|
|
|
|
# Backward compatibility: older config used `enable`.
|
|
if (
|
|
isinstance(data, dict)
|
|
and "main_enable" not in data
|
|
and "enable" in data
|
|
):
|
|
data["main_enable"] = bool(data.get("enable", False))
|
|
|
|
# Ensure required keys exist.
|
|
data.setdefault("main_enable", False)
|
|
data.setdefault("remove_main_duplicate_tools", False)
|
|
data.setdefault("agents", [])
|
|
|
|
# Backward/forward compatibility: ensure each agent contains provider_id.
|
|
# None means follow global/default provider settings.
|
|
agents_list = data.get("agents")
|
|
if isinstance(agents_list, list):
|
|
for a in agents_list:
|
|
if isinstance(a, dict):
|
|
a.setdefault("provider_id", None)
|
|
a.setdefault("persona_id", None)
|
|
return jsonify(Response().ok(data=data).to_json())
|
|
except Exception as e:
|
|
logger.error(traceback.format_exc())
|
|
return jsonify(Response().error(f"获取 subagent 配置失败: {e!s}").to_json())
|
|
|
|
async def update_config(self):
|
|
try:
|
|
data = await request.json
|
|
if not isinstance(data, dict):
|
|
return jsonify(Response().error("配置必须为 JSON 对象").to_json())
|
|
|
|
cfg = self.core_lifecycle.astrbot_config
|
|
cfg["subagent_orchestrator"] = data
|
|
|
|
# Persist to cmd_config.json
|
|
# AstrBotConfigManager does not expose a `save()` method; persist via AstrBotConfig.
|
|
cfg.save_config()
|
|
|
|
# Reload dynamic handoff tools if orchestrator exists
|
|
orch = getattr(self.core_lifecycle, "subagent_orchestrator", None)
|
|
if orch is not None:
|
|
await orch.reload_from_config(data)
|
|
|
|
return jsonify(Response().ok(message="保存成功").to_json())
|
|
except Exception as e:
|
|
logger.error(traceback.format_exc())
|
|
return jsonify(Response().error(f"保存 subagent 配置失败: {e!s}").to_json())
|
|
|
|
async def get_available_tools(self):
|
|
"""Return all registered tools (name/description/parameters/active/origin).
|
|
|
|
UI can use this to build a multi-select list for subagent tool assignment.
|
|
"""
|
|
try:
|
|
prov_mgr = self.core_lifecycle.provider_manager
|
|
if prov_mgr is None:
|
|
return Response().error("Provider manager not available").to_json()
|
|
tool_mgr = prov_mgr.llm_tools
|
|
tools_dict = []
|
|
for tool in tool_mgr.func_list:
|
|
# Prevent recursive routing: subagents should not be able to select
|
|
# the handoff (transfer_to_*) tools as their own mounted tools.
|
|
if isinstance(tool, HandoffTool):
|
|
continue
|
|
tool_handler_module_path = getattr(tool, "handler_module_path", None)
|
|
if tool_handler_module_path == "core.subagent_orchestrator":
|
|
continue
|
|
tools_dict.append(
|
|
{
|
|
"name": tool.name,
|
|
"description": tool.description,
|
|
"parameters": tool.parameters,
|
|
"active": tool.active,
|
|
"handler_module_path": tool_handler_module_path,
|
|
},
|
|
)
|
|
return jsonify(Response().ok(data=tools_dict).to_json())
|
|
except Exception as e:
|
|
logger.error(traceback.format_exc())
|
|
return jsonify(Response().error(f"获取可用工具失败: {e!s}").to_json())
|