Skip to content

fix(agent): defer verbose core tool schemas - #58838

Open
konsisumer wants to merge 1 commit into
NousResearch:mainfrom
konsisumer:fix/defer-verbose-core-tool-schemas
Open

fix(agent): defer verbose core tool schemas#58838
konsisumer wants to merge 1 commit into
NousResearch:mainfrom
konsisumer:fix/defer-verbose-core-tool-schemas

Conversation

@konsisumer

Copy link
Copy Markdown
Contributor

Extends Tool Search so default sessions stop sending verbose built-in tool manuals on every request while keeping the core direct-tool substrate available.

Shared root cause

  • The shared prompt bloat comes from model_tools.get_tool_definitions() always returning full schemas for every default core tool, while tools.tool_search only deferred MCP/plugin tools. A default hermes-cli assembly in this worktree measured about 14,746 estimated schema tokens before assembly.
  • This change reuses the existing Tool Search bridge for selected verbose built-ins (terminal, memory, skill_manage, browser_*, execute_code, delegate_task, session_search, etc.) while keeping foundational tools such as read_file, write_file, search_files, web_search, web_extract, process, and todo direct.
  • tools.tool_search.auto_token_threshold caps the auto gate so large-context models still benefit once deferrable schemas exceed the fixed token ceiling; defer_core_tools: false restores the previous MCP/plugin-only behavior.

How this fixes each issue

How to test

  • /opt/homebrew/bin/timeout -k 30 480 sh -c 'pytest tests/tools/test_tool_search.py -q' sh passed: 44 passed.
  • /opt/homebrew/bin/timeout -k 30 480 sh -c 'pytest tests/tools/test_tool_search.py tests/test_get_tool_definitions_cache_isolation.py tests/test_model_tools.py -q' sh passed: 81 passed.
  • Full suite command /opt/homebrew/bin/timeout -k 30 480 sh -c 'pytest tests/ -q -x --timeout=60 "$@"' sh was attempted and aborted during collection because this environment lacks FastAPI and lazy dependency installation is blocked by externally managed Python (ModuleNotFoundError: No module named 'fastapi', then PEP 668 pip failure).
  • Changed Python files passed ruff check.
  • python -m py_compile passed for changed Python files.
  • python scripts/check-windows-footguns.py agent/agent_init.py agent/agent_runtime_helpers.py agent/memory_manager.py agent/tool_executor.py model_tools.py tools/tool_search.py tests/tools/test_tool_search.py passed.

What platforms tested on

  • macOS on darwin-arm64 (local)

This coordinated PR bundles a fix that spans several issues. Happy to split it back into focused per-issue PRs if you'd prefer to review them separately.

Refs #13983
Refs #57044

@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/tools Tool registry, model_tools, toolsets P3 Low — cosmetic, nice to have labels Jul 5, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Comment (not blocking)

New feature/feature gate: defers verbose core tool schemas to reduce per-call overhead. The diff touches 9 files across the tool dispatch and MOA relay layers. New allowlist-based deferral logic with comprehensive test coverage.

What looks good:

  • Well-tested (new tests added for deferral conditions, thresholds, and gating)
  • Clear allowlist vs. denylist distinction for which tools defer
  • Respects the "core tool schema is sent on every call" cost concern from AGENTS.md

Note: This is a behavioral change to the tool dispatch surface. The tests appear thorough, but as a non-trivial change to what the agent sees per call, a human reviewer should confirm the deferral logic correctly handles all edge cases before merge.

@konsisumer

Copy link
Copy Markdown
Contributor Author

Rebased onto current origin/main and tightened the new tool-search auto-deferral path so skill tools and enabled plugin tools stay direct. This preserves the intended core-schema reduction while restoring the expected prompt-size and plugin visibility behavior after the rebase.

Local verification:

  • pytest tests/hermes_cli/test_prompt_size.py tests/hermes_cli/test_plugins.py tests/tools/test_tool_search.py -q --timeout=60
  • python scripts/check-windows-footguns.py --diff origin/main
  • git diff --check

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for targeting the measured default-schema cost. The premise remains present on current main: tools/tool_search.py:5-11 and model_tools.py:537-563 explicitly preserve core schemas.

Problems

  • agent/agent_init.py:1072-1076 records the full scope in available_tool_names, but the unchanged validator in agent/conversation_loop.py:4567-4575 rejects names outside valid_tool_names. Deferred names are absent from that visible set, so a direct deferred call is rejected rather than bridged.
  • tools/tool_search.py:662-667 keeps plugins direct, while tools/tool_search.py:741-742 still builds the bridge catalog from all deferrable tools. This exposes plugins twice and permits bridge calls despite the stated direct-only behavior.
  • website/docs/user-guide/features/tool-search.md:82-86 mis-indents the new YAML keys; it also names skill_manage as deferrable although it is absent from DEFAULT_DEFERRABLE_CORE_TOOLS at tools/tool_search.py:65-103.

Suggested changes

  • Route exact, in-scope deferred direct calls through tool_call before validation, with sequential and concurrent regressions.
  • Keep the bridge catalog consistent with the hidden subset, then correct the documentation.

Automated hermes-sweeper review.

Comment thread agent/agent_init.py
quiet_mode=True,
skip_tool_search_assembly=True,
)
agent.available_tool_names = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

valid_tool_names remains the model-facing set, while conversation_loop.py rejects any direct function name outside it before execution. A deferred terminal direct call therefore cannot reach the bridge. Please route exact in-scope deferred names to tool_call before that validator and cover it with an end-to-end execution-path regression.

Comment thread tools/tool_search.py
limit = max(1, min(config.max_search_limit, _safe_int(raw_limit, config.search_default_limit)))

_, deferrable = classify_tools(current_tool_defs)
_, deferrable = classify_tools(current_tool_defs, config=config)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catalog still contains plugin tools because classify_tools() marks them deferrable, even though assembly retains them directly at lines 662-667. Filter dispatch catalogs to the same hidden subset so direct plugins are not duplicated as bridge targets.

threshold_pct: 10 # percentage of context — only used in auto mode
search_default_limit: 5
max_search_limit: 20
threshold_pct: 10 # percentage of context — capped by auto_token_threshold

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These keys need four-space indentation under tools.tool_search; as written they are siblings of tool_search under tools and will not configure the fields described below.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-massive Sweeper blast radius: massive — everyone, every turn (invariant surface) labels Jul 15, 2026
@konsisumer

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review. The exact direct-call bridge must rewrite both the tool name and arguments before agent/conversation_loop.py validates and persists the assistant tool-call turn; the existing executor paths run too late to do that safely.

May I widen this PR's scope to include that pre-validation normalizer and its run-loop regressions? With that approval, I will also apply the in-scope bridge-catalog and documentation corrections from this review in the same update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/tools Tool registry, model_tools, toolsets P3 Low — cosmetic, nice to have sweeper:blast-massive Sweeper blast radius: massive — everyone, every turn (invariant surface) sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants