107 lines
3.5 KiB
Python
107 lines
3.5 KiB
Python
from __future__ import annotations
|
|
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import opensquilla.tool_boundary as tool_boundary
|
|
from opensquilla.engine.types import ToolCall
|
|
from opensquilla.tools.dispatch import build_tool_handler
|
|
from opensquilla.tools.registry import get_default_registry
|
|
from opensquilla.tools.types import CallerKind, ToolContext
|
|
|
|
REMOVED_TOOL_NAMES = {"generate_image", "spawn_subagent", "send_message"}
|
|
CANONICAL_TOOL_NAMES = {
|
|
"image_generate",
|
|
"sessions_spawn",
|
|
"sessions_send",
|
|
"web_search",
|
|
"web_discover",
|
|
}
|
|
OWNER_ONLY_TOOL_NAMES = {"http_request", "git_commit"}
|
|
|
|
|
|
def test_tool_call_boundary_has_canonical_and_stable_exports() -> None:
|
|
from opensquilla.engine import ToolHandler as EngineToolHandler
|
|
from opensquilla.engine.types import ToolResult as EngineToolResult
|
|
from opensquilla.tools.boundary import ToolCall as ToolsToolCall
|
|
|
|
assert tool_boundary.ToolCall is ToolCall
|
|
assert tool_boundary.ToolResult is EngineToolResult
|
|
assert ToolsToolCall is ToolCall
|
|
assert EngineToolHandler is tool_boundary.AgentToolHandler
|
|
|
|
|
|
def test_engine_types_import_does_not_register_builtin_tools() -> None:
|
|
script = (
|
|
"import sys; "
|
|
"import opensquilla.engine.types; "
|
|
"assert 'opensquilla.tools.builtin' not in sys.modules, "
|
|
"sorted(k for k in sys.modules if k.startswith('opensquilla.tools'))"
|
|
)
|
|
|
|
result = subprocess.run(
|
|
[sys.executable, "-c", script],
|
|
text=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
check=False,
|
|
)
|
|
|
|
assert result.returncode == 0, result.stdout
|
|
|
|
|
|
def test_default_registry_public_surface_uses_canonical_tool_names() -> None:
|
|
import opensquilla.tools.builtin # noqa: F401
|
|
|
|
registry = get_default_registry()
|
|
owner_names = {
|
|
tool.name
|
|
for tool in registry.to_tool_definitions(
|
|
ToolContext(is_owner=True, caller_kind=CallerKind.AGENT)
|
|
)
|
|
}
|
|
channel_names = {
|
|
tool.name
|
|
for tool in registry.to_tool_definitions(
|
|
ToolContext(is_owner=False, caller_kind=CallerKind.CHANNEL)
|
|
)
|
|
}
|
|
|
|
assert REMOVED_TOOL_NAMES.isdisjoint(owner_names)
|
|
assert REMOVED_TOOL_NAMES.isdisjoint(channel_names)
|
|
assert CANONICAL_TOOL_NAMES <= owner_names
|
|
assert "research_search" not in owner_names
|
|
assert "research_search" not in channel_names
|
|
assert "web_search" in channel_names
|
|
assert "web_discover" in channel_names
|
|
assert OWNER_ONLY_TOOL_NAMES <= owner_names
|
|
assert OWNER_ONLY_TOOL_NAMES.isdisjoint(channel_names)
|
|
|
|
|
|
async def test_removed_tools_are_not_dispatchable_by_name() -> None:
|
|
import opensquilla.tools.builtin # noqa: F401
|
|
|
|
handler = build_tool_handler(
|
|
get_default_registry(),
|
|
ToolContext(is_owner=True, caller_kind=CallerKind.AGENT),
|
|
)
|
|
|
|
for name in REMOVED_TOOL_NAMES:
|
|
result = await handler(ToolCall(tool_use_id=f"tc-{name}", tool_name=name, arguments={}))
|
|
assert result.is_error is True
|
|
assert '"error_class": "ToolNotFound"' in result.content
|
|
|
|
|
|
def test_web_ui_tool_icon_map_avoids_removed_wrapper_tools() -> None:
|
|
source = Path("src/opensquilla/gateway/static/js/views/chat.js").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
start = source.index("const _TOOL_EMOJI = {")
|
|
end = source.index(" function _toolEmoji", start)
|
|
tool_display_map = source[start:end]
|
|
|
|
for name in REMOVED_TOOL_NAMES:
|
|
assert name not in tool_display_map
|
|
assert "http_request" in tool_display_map
|