Skip to content

feat: load deferred tools on demand via tool.describe - #344

Merged
frostming merged 1 commit into
mainfrom
feat/progressive-tool-loading
Oct 7, 2026
Merged

frostming merged 1 commit into
mainfrom
feat/progressive-tool-loading

Conversation

@frostming

Copy link
Copy Markdown
Collaborator

What changed and why

Progressive tool loading: tools are either loaded directly or deferred (loaded on demand). This replaces the tool_sources / known_tools / tool_providers mechanism merged in #339 with a single deferred flag on Tool.

  • Tool.deferred (also on @tool and Tool.from_callable) marks a tool as loaded on demand.
  • The system prompt lists all allowed deferred tools by model-facing name in a <deferred_tools> block.
  • New builtin tool.describe(names) returns tool definitions and appends a tool.loaded tape event with the newly loaded runtime names. It is exposed only when an allowed deferred tool exists.
  • On every model request the agent reads all tool.loaded events from the tape and appends those tools, in load order, after the direct tools.
  • allowed_tools filters direct and deferred tools alike.
  • Code mode sees every allowed tool: tool preparation now happens in _run_once_stream, with code mode first. Non-preserved deferred tools go into the stub without loading, and deferral applies only to the remaining model-facing tools.
  • before_tool_call recovery checks the model-facing tools of the current request (plus code tools) and tells the model to call tool_describe when it calls an unloaded deferred tool.

Impacted modules

  • src/bub/tools.py: Tool.deferred; removed ToolProvider
  • src/bub/builtin/agent.py: removed tool_sources, known_tools, tool_providers, REQUEST_TOOLS_STATE_KEY; added _allowed_tools and _prepare_deferred_tools
  • src/bub/builtin/tools.py: tool.describe, loaded_tool_names, render_deferred_tools_prompt
  • src/bub/builtin/hook_impl.py: unknown-tool recovery scoped to the request toolset
  • Docs: build/tools.mdx (en and zh-cn)

Breaking: plugins that use Agent.tool_providers, Agent.tool_sources or Agent.known_tools need to register tools in Agent.tools with deferred=True instead.

Verification

  • uv run ruff check src tests: passed
  • uv run mypy src: passed
  • uv run pytest -q: 598 passed, 1 skipped
  • tests/test_tool_providers.py is replaced by tests/test_tool_loading.py, which covers loading and persistence across turns, allowed_tools filtering, hiding tool_describe when there are no deferred tools, guidance for unloaded tools, and code-mode exposure.

🤖 Generated with Claude Code

Replace the tool_sources/known_tools/tool_providers mechanism from #339 with
a deferred flag on Tool. Deferred tools are listed by name in the system
prompt and loaded through tool.describe, which records a tool.loaded tape
event; each request appends loaded tools after the direct tools. Code mode
sees every allowed tool, deferred ones included.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bub 71f6bf7 Commit Preview URL

Branch Preview URL
Oct 07 2026, 11:15 AM

@frostming
frostming merged commit b4a61bf into main Oct 7, 2026
6 checks passed
@frostming
frostming deleted the feat/progressive-tool-loading branch October 7, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant