11import type { BetaToolUnion } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
22import type { SystemPrompt } from '../../../utils/systemPromptType.js'
3- import type { Message , StreamEvent , SystemAPIErrorMessage , AssistantMessage } from '../../../types/message.js'
3+ import type {
4+ Message ,
5+ StreamEvent ,
6+ SystemAPIErrorMessage ,
7+ AssistantMessage ,
8+ } from '../../../types/message.js'
49import type { Tools } from '../../../Tool.js'
510import { getOpenAIClient } from './client.js'
611import { anthropicMessagesToOpenAI } from './convertMessages.js'
7- import { anthropicToolsToOpenAI , anthropicToolChoiceToOpenAI } from './convertTools.js'
12+ import {
13+ anthropicToolsToOpenAI ,
14+ anthropicToolChoiceToOpenAI ,
15+ } from './convertTools.js'
816import { adaptOpenAIStreamToAnthropic } from './streamAdapter.js'
917import { resolveOpenAIModel } from './modelMapping.js'
1018import { normalizeMessagesForAPI } from '../../../utils/messages.js'
1119import { toolToAPISchema } from '../../../utils/api.js'
12- import { getEmptyToolPermissionContext } from '../../../Tool.js'
20+ import {
21+ getEmptyToolPermissionContext ,
22+ toolMatchesName ,
23+ } from '../../../Tool.js'
1324import { logForDebugging } from '../../../utils/debug.js'
1425import { addToTotalSessionCost } from '../../../cost-tracker.js'
1526import { calculateUSDCost } from '../../../utils/modelCost.js'
@@ -19,6 +30,14 @@ import {
1930 createAssistantAPIErrorMessage ,
2031 normalizeContentFromAPI ,
2132} from '../../../utils/messages.js'
33+ import {
34+ isToolSearchEnabled ,
35+ extractDiscoveredToolNames ,
36+ } from '../../../utils/toolSearch.js'
37+ import {
38+ isDeferredTool ,
39+ TOOL_SEARCH_TOOL_NAME ,
40+ } from '../../../tools/ToolSearchTool/prompt.js'
2241
2342/**
2443 * OpenAI-compatible query path. Converts Anthropic-format messages/tools to
@@ -43,41 +62,97 @@ export async function* queryModelOpenAI(
4362 // 2. Normalize messages using shared preprocessing
4463 const messagesForAPI = normalizeMessagesForAPI ( messages , tools )
4564
46- // 3. Build tool schemas
65+ // 3. Check if tool search is enabled (similar to Anthropic path)
66+ const useToolSearch = await isToolSearchEnabled (
67+ options . model ,
68+ tools ,
69+ options . getToolPermissionContext ||
70+ ( async ( ) => getEmptyToolPermissionContext ( ) ) ,
71+ options . agents || [ ] ,
72+ options . querySource ,
73+ )
74+
75+ // 4. Build deferred tools set (similar to Anthropic path)
76+ const deferredToolNames = new Set < string > ( )
77+ if ( useToolSearch ) {
78+ for ( const t of tools ) {
79+ if ( isDeferredTool ( t ) ) deferredToolNames . add ( t . name )
80+ }
81+ }
82+
83+ // 5. Filter tools (similar to Anthropic path)
84+ let filteredTools = tools
85+ if ( useToolSearch && deferredToolNames . size > 0 ) {
86+ const discoveredToolNames = extractDiscoveredToolNames ( messages )
87+
88+ filteredTools = tools . filter ( tool => {
89+ // Always include non-deferred tools
90+ if ( ! deferredToolNames . has ( tool . name ) ) return true
91+ // Always include ToolSearchTool (so it can discover more tools)
92+ if ( toolMatchesName ( tool , TOOL_SEARCH_TOOL_NAME ) ) return true
93+ // Only include deferred tools that have been discovered
94+ return discoveredToolNames . has ( tool . name )
95+ } )
96+ }
97+
98+ // 6. Build tool schemas with deferLoading flag
4799 const toolSchemas = await Promise . all (
48- tools . map ( tool =>
100+ filteredTools . map ( tool =>
49101 toolToAPISchema ( tool , {
50102 getToolPermissionContext : options . getToolPermissionContext ,
51103 tools,
52104 agents : options . agents ,
53105 allowedAgentTypes : options . allowedAgentTypes ,
54106 model : options . model ,
107+ deferLoading : useToolSearch && deferredToolNames . has ( tool . name ) ,
55108 } ) ,
56109 ) ,
57110 )
58- // Filter out non-standard tools (server tools like advisor)
111+
112+ // 7. Filter out non-standard tools (server tools like advisor)
59113 const standardTools = toolSchemas . filter (
60114 ( t ) : t is BetaToolUnion & { type : string } => {
61115 const anyT = t as Record < string , unknown >
62- return anyT . type !== 'advisor_20260301' && anyT . type !== 'computer_20250124'
116+ return (
117+ anyT . type !== 'advisor_20260301' && anyT . type !== 'computer_20250124'
118+ )
63119 } ,
64120 )
65121
66- // 4. Convert messages and tools to OpenAI format
67- const openaiMessages = anthropicMessagesToOpenAI ( messagesForAPI , systemPrompt )
122+ // 8. Convert messages and tools to OpenAI format
123+ const openaiMessages = anthropicMessagesToOpenAI (
124+ messagesForAPI ,
125+ systemPrompt ,
126+ )
68127 const openaiTools = anthropicToolsToOpenAI ( standardTools )
69128 const openaiToolChoice = anthropicToolChoiceToOpenAI ( options . toolChoice )
70129
71- // 5. Get client and make streaming request
130+ // 9. Log tool filtering details
131+ if ( useToolSearch ) {
132+ const includedDeferredTools = filteredTools . filter ( t =>
133+ deferredToolNames . has ( t . name ) ,
134+ ) . length
135+ logForDebugging (
136+ `[OpenAI] Tool search enabled: ${ includedDeferredTools } /${ deferredToolNames . size } deferred tools included, total tools=${ openaiTools . length } ` ,
137+ )
138+ } else {
139+ logForDebugging (
140+ `[OpenAI] Tool search disabled, total tools=${ openaiTools . length } ` ,
141+ )
142+ }
143+
144+ // 10. Get client and make streaming request
72145 const client = getOpenAIClient ( {
73146 maxRetries : 0 ,
74147 fetchOverride : options . fetchOverride ,
75148 source : options . querySource ,
76149 } )
77150
78- logForDebugging ( `[OpenAI] Calling model=${ openaiModel } , messages=${ openaiMessages . length } , tools=${ openaiTools . length } ` )
151+ logForDebugging (
152+ `[OpenAI] Calling model=${ openaiModel } , messages=${ openaiMessages . length } , tools=${ openaiTools . length } ` ,
153+ )
79154
80- // 6 . Call OpenAI API with streaming
155+ // 11 . Call OpenAI API with streaming
81156 const stream = await client . chat . completions . create (
82157 {
83158 model : openaiModel ,
@@ -103,7 +178,7 @@ export async function* queryModelOpenAI(
103178
104179 // Accumulate content blocks and usage, same as the Anthropic path in claude.ts
105180 const contentBlocks : Record < number , any > = { }
106- let partialMessage : any = undefined
181+ let partialMessage : any
107182 let usage = {
108183 input_tokens : 0 ,
109184 output_tokens : 0 ,
@@ -121,7 +196,7 @@ export async function* queryModelOpenAI(
121196 if ( ( event as any ) . message ?. usage ) {
122197 usage = {
123198 ...usage ,
124- ...( ( event as any ) . message . usage ) ,
199+ ...( event as any ) . message . usage ,
125200 }
126201 }
127202 break
@@ -164,11 +239,7 @@ export async function* queryModelOpenAI(
164239 const m : AssistantMessage = {
165240 message : {
166241 ...partialMessage ,
167- content : normalizeContentFromAPI (
168- [ block ] ,
169- tools ,
170- options . agentId ,
171- ) ,
242+ content : normalizeContentFromAPI ( [ block ] , tools , options . agentId ) ,
172243 } ,
173244 requestId : undefined ,
174245 type : 'assistant' ,
@@ -192,7 +263,10 @@ export async function* queryModelOpenAI(
192263 }
193264
194265 // Track cost and token usage (matching the Anthropic path in claude.ts)
195- if ( event . type === 'message_stop' && usage . input_tokens + usage . output_tokens > 0 ) {
266+ if (
267+ event . type === 'message_stop' &&
268+ usage . input_tokens + usage . output_tokens > 0
269+ ) {
196270 const costUSD = calculateUSDCost ( openaiModel , usage as any )
197271 addToTotalSessionCost ( costUSD , usage as any , options . model )
198272 }
0 commit comments