Repository navigation
feat(ai): stub non-core tool schemas + tool_search to reduce context window usage - #1301
Conversation
…ontext window usage
Sending all 37 full Zod schemas every request cost ~7–18k tokens per turn. Non-core tools
now use passthrough stub schemas; the AI calls tool_search("keyword") to get full parameter
schemas on demand before using extended tools (same pattern as Claude Code's own ToolSearch).
- New stub-tools.ts: wraps non-core tools with z.object({}).passthrough() inputSchema;
real safeParse validation runs inside execute so invalid args return a helpful error
- New tool-search-tool.ts: introspects the live registry via z.toJSONSchema; supports
select:name1,name2 and keyword queries
- chat/route.ts: uses pageSpaceToolsStubbed + injects tool_search referencing full registry
- system-prompt.ts: adds TOOL_DISCOVERY_PROMPT teaching the 8 core tools and tool_search usage
- Tests: 23 unit tests across stub-tools, tool-search-tool, and ai-tools
- Prompt evals: riteway ai .sudo files in ai-evals/tool-discovery/ for behavioral testing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughImplements a tool discovery system where AI models use core tools directly and dynamically discover non-core tool schemas via tool_search. Core tools (list_drives, read_page, etc.) have unmodified schemas; non-core tools are stubbed to accept any input, validate requests, and redirect to tool_search on mismatch. The chat route injects tool_search backed by the full tool registry alongside the stubbed tools. ChangesTool Discovery & Schema Resolution
Sequence DiagramsequenceDiagram
participant Model as AI Model
participant StubTools as Stubbed Tool Set
participant ToolSearch as tool_search
participant FullTools as Full Tool Registry
participant CoreTool as Core Tool (e.g., list_drives)
participant NonCoreTool as Non-core Tool (e.g., create_calendar_event)
Model->>StubTools: Call list_drives with valid args
StubTools->>CoreTool: Execute directly (no stub)
CoreTool-->>Model: Return workspace list
Model->>StubTools: Call create_calendar_event with incomplete args
StubTools->>StubTools: Validate args against original schema
StubTools-->>Model: Error: use tool_search first
Model->>ToolSearch: Query tool_search("calendar")
ToolSearch->>FullTools: Look up matching tools
FullTools-->>ToolSearch: Return calendar tool with full schema
ToolSearch-->>Model: Return create_calendar_event schema
Model->>StubTools: Call create_calendar_event with valid args from schema
StubTools->>NonCoreTool: Execute with validated args
NonCoreTool-->>Model: Return event confirmation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/src/app/api/ai/chat/route.ts (1)
527-532:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate logging to reflect stubbed tools usage.
The debug log on line 528 references
pageSpaceTools.length, but the code now usespageSpaceToolsStubbed(line 515). While the key counts are identical, the log message should reflect the actual tool set in use for accurate debugging context.📊 Proposed fix
- loggers.ai.debug('AI Page Chat API: Tools built from baseline + runtime toggles', { - totalTools: Object.keys(pageSpaceTools).length, + loggers.ai.debug('AI Page Chat API: Tools built from stubbed baseline + runtime toggles', { + totalTools: Object.keys(pageSpaceToolsStubbed).length, filteredTools: Object.keys(filteredTools).length, isReadOnly: readOnlyMode, webSearchEnabled: webSearchMode🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/api/ai/chat/route.ts` around lines 527 - 532, The debug message in loggers.ai.debug currently reports counts using pageSpaceTools but the runtime uses pageSpaceToolsStubbed; update the logging call (loggers.ai.debug in this file) to use Object.keys(pageSpaceToolsStubbed).length for totalTools so the log reflects the actual tool set in use alongside filteredTools, readOnlyMode and webSearchMode.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@apps/web/src/app/api/ai/chat/route.ts`:
- Around line 527-532: The debug message in loggers.ai.debug currently reports
counts using pageSpaceTools but the runtime uses pageSpaceToolsStubbed; update
the logging call (loggers.ai.debug in this file) to use
Object.keys(pageSpaceToolsStubbed).length for totalTools so the log reflects the
actual tool set in use alongside filteredTools, readOnlyMode and webSearchMode.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: edd8f665-958d-401c-9db4-462a202e80fe
📒 Files selected for processing (11)
apps/web/ai-evals/tool-discovery/core-tools-no-search.sudoapps/web/ai-evals/tool-discovery/non-core-requires-search.sudoapps/web/ai-evals/tool-discovery/spec.mdcapps/web/src/app/api/ai/chat/route.tsapps/web/src/lib/ai/core/__tests__/ai-tools.test.tsapps/web/src/lib/ai/core/__tests__/stub-tools.test.tsapps/web/src/lib/ai/core/ai-tools.tsapps/web/src/lib/ai/core/stub-tools.tsapps/web/src/lib/ai/core/system-prompt.tsapps/web/src/lib/ai/tools/__tests__/tool-search-tool.test.tsapps/web/src/lib/ai/tools/tool-search-tool.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31a79e2595
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // return complete parameter schemas on demand. | ||
| filteredTools = { | ||
| ...filteredTools, | ||
| tool_search: createToolSearchTool(pageSpaceTools), |
There was a problem hiding this comment.
Restrict tool_search to currently enabled tools
tool_search is wired against pageSpaceTools (the full registry), not the runtime-filtered toolset, so in read-only mode or when web search is disabled it still advertises schemas for tools that were intentionally removed from filteredTools. In those sessions the model can discover (and then attempt) unavailable tools like web_search/write tools, which causes unavailable-tool execution errors instead of a normal response path. Build tool_search from the same filtered availability set (with full schemas for only those allowed tools) to avoid exposing tools that cannot be executed in this request.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in f57a557. tool_search now receives enabledFullSchemaTools — the intersection of filteredTools keys and pageSpaceTools full schemas. In read-only mode, write tools are absent from filteredTools so they don't appear in tool_search results. Same for web_search when web search is disabled.
…lSearchTool in chat route tests The two chat route tests mock @/lib/ai/core but didn't include pageSpaceToolsStubbed (newly exported) or mock the new @/lib/ai/tools/tool-search-tool import. This caused all lifecycle spy assertions to fail with 0 calls in CI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tool_search was wired to pageSpaceTools (full registry), meaning in read-only mode or with web search disabled it could advertise unavailable tools. Now builds enabledFullSchemaTools by intersecting the filtered key set with pageSpaceTools, so tool_search only returns schemas for tools that can actually execute in this request. Also fixes the debug log to count pageSpaceToolsStubbed instead of pageSpaceTools. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t in route Aligns tool-search-tool.ts with the AI SDK's ToolSet type (Record<string, CoreTool>) instead of the looser Record<string, Tool>. Adds explicit as ToolSet cast on Object.fromEntries in route.ts since TypeScript cannot infer the tuple value type. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…window usage (#1301) * feat(ai): stub non-core tool schemas + tool_search meta-tool to cut context window usage Sending all 37 full Zod schemas every request cost ~7–18k tokens per turn. Non-core tools now use passthrough stub schemas; the AI calls tool_search("keyword") to get full parameter schemas on demand before using extended tools (same pattern as Claude Code's own ToolSearch). - New stub-tools.ts: wraps non-core tools with z.object({}).passthrough() inputSchema; real safeParse validation runs inside execute so invalid args return a helpful error - New tool-search-tool.ts: introspects the live registry via z.toJSONSchema; supports select:name1,name2 and keyword queries - chat/route.ts: uses pageSpaceToolsStubbed + injects tool_search referencing full registry - system-prompt.ts: adds TOOL_DISCOVERY_PROMPT teaching the 8 core tools and tool_search usage - Tests: 23 unit tests across stub-tools, tool-search-tool, and ai-tools - Prompt evals: riteway ai .sudo files in ai-evals/tool-discovery/ for behavioral testing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tests): add missing mocks for pageSpaceToolsStubbed and createToolSearchTool in chat route tests The two chat route tests mock @/lib/ai/core but didn't include pageSpaceToolsStubbed (newly exported) or mock the new @/lib/ai/tools/tool-search-tool import. This caused all lifecycle spy assertions to fail with 0 calls in CI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ai): scope tool_search to enabled tools + fix stale log count tool_search was wired to pageSpaceTools (full registry), meaning in read-only mode or with web search disabled it could advertise unavailable tools. Now builds enabledFullSchemaTools by intersecting the filtered key set with pageSpaceTools, so tool_search only returns schemas for tools that can actually execute in this request. Also fixes the debug log to count pageSpaceToolsStubbed instead of pageSpaceTools. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(ai): use ToolSet type in createToolSearchTool + explicit cast in route Aligns tool-search-tool.ts with the AI SDK's ToolSet type (Record<string, CoreTool>) instead of the looser Record<string, Tool>. Adds explicit as ToolSet cast on Object.fromEntries in route.ts since TypeScript cannot infer the tuple value type. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Changes
New files:
Modified files:
Test plan
🤖 Generated with Claude Code