Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions tests/tools/test_tool_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,66 @@ def test_classify_keeps_unknown_in_visible(self):
assert deferrable == []


# ---------------------------------------------------------------------------
# Omnio product tools — always loaded, never behind the bridge
# ---------------------------------------------------------------------------


class TestOmnioAlwaysLoad:
"""Omnio's own plugin tools are named in the agent's operating docs and
skills; a deferred one makes the model substitute a weaker built-in."""

OMNIO_TOOLS = [
"web_read", "web_map", "request_user_input",
"render_component", "store-credential", "emit_client_event",
]

@staticmethod
def _register(name, toolset):
from tools.registry import registry

def _handler(args, task_id=None, **kw):
return json.dumps({"ok": True, "tool": name})

registry.register(
name=name,
handler=_handler,
schema=_td(name, f"desc for {name}")["function"],
toolset=toolset,
)

def test_omnio_tools_never_defer(self):
from tools.tool_search import is_deferrable_tool_name
for name in self.OMNIO_TOOLS:
# Registered as ordinary plugin tools — without the always-load
# set they would classify as deferrable.
self._register(name, "omnio")
assert not is_deferrable_tool_name(name), (
f"Omnio product tool '{name}' must NEVER be deferrable"
)

def test_omnio_tools_stay_visible_in_classify(self):
from tools.tool_search import classify_tools
for name in self.OMNIO_TOOLS:
self._register(name, "omnio")
defs = [_td(n, f"desc for {n}") for n in self.OMNIO_TOOLS]
visible, deferrable = classify_tools(defs)
assert {(td["function"]["name"]) for td in visible} == set(self.OMNIO_TOOLS)
assert deferrable == []

def test_other_plugin_and_mcp_tools_still_defer(self):
"""The always-load set is a named allowlist, not a blanket opt-out."""
from tools.tool_search import classify_tools, is_deferrable_tool_name
self._register("omnio_unlisted_helper", "omnio")
self._register("mcp_omnio_ctl_list", "mcp-omnio-ctl")
for name in ("omnio_unlisted_helper", "mcp_omnio_ctl_list"):
assert is_deferrable_tool_name(name)
defs = [_td("omnio_unlisted_helper"), _td("mcp_omnio_ctl_list")]
visible, deferrable = classify_tools(defs)
assert visible == []
assert len(deferrable) == 2


# ---------------------------------------------------------------------------
# Token estimation + threshold gate
# ---------------------------------------------------------------------------
Expand Down
25 changes: 23 additions & 2 deletions tools/tool_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,27 @@ def load_config() -> ToolSearchConfig:
# Tool classification
# ---------------------------------------------------------------------------

# Omnio's own plugin tools. These are the product's surface, not third-party
# capability: the agent's operating docs and skills reference them BY NAME, so
# a deferred one reads to the model as "not available" and it silently
# substitutes a weaker built-in (observed live: web_read deferred, web_extract
# used instead). The catalog is six tools, so deferral saves nothing measurable
# while costing a blind tool_call round-trip on every use.
#
# Kept as a separate constant rather than appended to _HERMES_CORE_TOOLS
# because that list is also the ``tools:`` payload for the hermes-cli,
# hermes-cron and hermes-telegram toolsets — adding Omnio names there would
# make those toolsets advertise tools that don't exist outside our sprites.
# Leaving the upstream literal untouched also keeps upstream syncs conflict-free.
_OMNIO_ALWAYS_LOAD_TOOLS = frozenset({
"web_read",
"web_map",
"request_user_input",
"render_component",
"store-credential",
"emit_client_event",
})


def _core_tool_names() -> frozenset[str]:
"""Return the set of tool names that must NEVER be deferred.
Expand All @@ -194,9 +215,9 @@ def _core_tool_names() -> frozenset[str]:
"""
try:
from toolsets import _HERMES_CORE_TOOLS
return frozenset(_HERMES_CORE_TOOLS)
return frozenset(_HERMES_CORE_TOOLS) | _OMNIO_ALWAYS_LOAD_TOOLS
except Exception:
return frozenset()
return _OMNIO_ALWAYS_LOAD_TOOLS


def is_deferrable_tool_name(name: str) -> bool:
Expand Down
Loading