205 lines
6.9 KiB
Python
205 lines
6.9 KiB
Python
import json
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
from astrbot.api import FunctionTool
|
|
from astrbot.core.agent.run_context import ContextWrapper
|
|
from astrbot.core.agent.tool import ToolExecResult
|
|
from astrbot.core.astr_agent_context import AstrAgentContext
|
|
|
|
from ..computer_client import get_booter
|
|
|
|
|
|
def _to_json(data: Any) -> str:
|
|
return json.dumps(data, ensure_ascii=False, default=str)
|
|
|
|
|
|
def _ensure_admin(context: ContextWrapper[AstrAgentContext]) -> str | None:
|
|
if context.context.event.role != "admin":
|
|
return (
|
|
"error: Permission denied. Browser and skill lifecycle tools are only allowed "
|
|
"for admin users."
|
|
)
|
|
return None
|
|
|
|
|
|
async def _get_browser_component(context: ContextWrapper[AstrAgentContext]) -> Any:
|
|
booter = await get_booter(
|
|
context.context.context,
|
|
context.context.event.unified_msg_origin,
|
|
)
|
|
browser = getattr(booter, "browser", None)
|
|
if browser is None:
|
|
raise RuntimeError(
|
|
"Current sandbox booter does not support browser capability. "
|
|
"Please switch to shipyard_neo."
|
|
)
|
|
return browser
|
|
|
|
|
|
@dataclass
|
|
class BrowserExecTool(FunctionTool):
|
|
name: str = "astrbot_execute_browser"
|
|
description: str = "Execute one browser automation command in the sandbox."
|
|
parameters: dict = field(
|
|
default_factory=lambda: {
|
|
"type": "object",
|
|
"properties": {
|
|
"cmd": {"type": "string", "description": "Browser command to execute."},
|
|
"timeout": {"type": "integer", "default": 30},
|
|
"description": {
|
|
"type": "string",
|
|
"description": "Optional execution description.",
|
|
},
|
|
"tags": {"type": "string", "description": "Optional tags."},
|
|
"learn": {
|
|
"type": "boolean",
|
|
"description": "Whether to mark execution as learn evidence.",
|
|
"default": False,
|
|
},
|
|
"include_trace": {
|
|
"type": "boolean",
|
|
"description": "Whether to include trace_ref in response.",
|
|
"default": False,
|
|
},
|
|
},
|
|
"required": ["cmd"],
|
|
}
|
|
)
|
|
|
|
async def call(
|
|
self,
|
|
context: ContextWrapper[AstrAgentContext],
|
|
cmd: str,
|
|
timeout: int = 30,
|
|
description: str | None = None,
|
|
tags: str | None = None,
|
|
learn: bool = False,
|
|
include_trace: bool = False,
|
|
) -> ToolExecResult:
|
|
if err := _ensure_admin(context):
|
|
return err
|
|
try:
|
|
browser = await _get_browser_component(context)
|
|
result = await browser.exec(
|
|
cmd=cmd,
|
|
timeout=timeout,
|
|
description=description,
|
|
tags=tags,
|
|
learn=learn,
|
|
include_trace=include_trace,
|
|
)
|
|
return _to_json(result)
|
|
except Exception as e:
|
|
return f"Error executing browser command: {str(e)}"
|
|
|
|
|
|
@dataclass
|
|
class BrowserBatchExecTool(FunctionTool):
|
|
name: str = "astrbot_execute_browser_batch"
|
|
description: str = "Execute a browser command batch in the sandbox."
|
|
parameters: dict = field(
|
|
default_factory=lambda: {
|
|
"type": "object",
|
|
"properties": {
|
|
"commands": {
|
|
"type": "array",
|
|
"items": {"type": "string"},
|
|
"description": "Ordered browser commands.",
|
|
},
|
|
"timeout": {"type": "integer", "default": 60},
|
|
"stop_on_error": {"type": "boolean", "default": True},
|
|
"description": {
|
|
"type": "string",
|
|
"description": "Optional execution description.",
|
|
},
|
|
"tags": {"type": "string", "description": "Optional tags."},
|
|
"learn": {
|
|
"type": "boolean",
|
|
"description": "Whether to mark execution as learn evidence.",
|
|
"default": False,
|
|
},
|
|
"include_trace": {
|
|
"type": "boolean",
|
|
"description": "Whether to include trace_ref in response.",
|
|
"default": False,
|
|
},
|
|
},
|
|
"required": ["commands"],
|
|
}
|
|
)
|
|
|
|
async def call(
|
|
self,
|
|
context: ContextWrapper[AstrAgentContext],
|
|
commands: list[str],
|
|
timeout: int = 60,
|
|
stop_on_error: bool = True,
|
|
description: str | None = None,
|
|
tags: str | None = None,
|
|
learn: bool = False,
|
|
include_trace: bool = False,
|
|
) -> ToolExecResult:
|
|
if err := _ensure_admin(context):
|
|
return err
|
|
try:
|
|
browser = await _get_browser_component(context)
|
|
result = await browser.exec_batch(
|
|
commands=commands,
|
|
timeout=timeout,
|
|
stop_on_error=stop_on_error,
|
|
description=description,
|
|
tags=tags,
|
|
learn=learn,
|
|
include_trace=include_trace,
|
|
)
|
|
return _to_json(result)
|
|
except Exception as e:
|
|
return f"Error executing browser batch command: {str(e)}"
|
|
|
|
|
|
@dataclass
|
|
class RunBrowserSkillTool(FunctionTool):
|
|
name: str = "astrbot_run_browser_skill"
|
|
description: str = "Run a released browser skill in the sandbox by skill_key."
|
|
parameters: dict = field(
|
|
default_factory=lambda: {
|
|
"type": "object",
|
|
"properties": {
|
|
"skill_key": {"type": "string"},
|
|
"timeout": {"type": "integer", "default": 60},
|
|
"stop_on_error": {"type": "boolean", "default": True},
|
|
"include_trace": {"type": "boolean", "default": False},
|
|
"description": {"type": "string"},
|
|
"tags": {"type": "string"},
|
|
},
|
|
"required": ["skill_key"],
|
|
}
|
|
)
|
|
|
|
async def call(
|
|
self,
|
|
context: ContextWrapper[AstrAgentContext],
|
|
skill_key: str,
|
|
timeout: int = 60,
|
|
stop_on_error: bool = True,
|
|
include_trace: bool = False,
|
|
description: str | None = None,
|
|
tags: str | None = None,
|
|
) -> ToolExecResult:
|
|
if err := _ensure_admin(context):
|
|
return err
|
|
try:
|
|
browser = await _get_browser_component(context)
|
|
result = await browser.run_skill(
|
|
skill_key=skill_key,
|
|
timeout=timeout,
|
|
stop_on_error=stop_on_error,
|
|
include_trace=include_trace,
|
|
description=description,
|
|
tags=tags,
|
|
)
|
|
return _to_json(result)
|
|
except Exception as e:
|
|
return f"Error running browser skill: {str(e)}"
|