| 212 | * by the existing query pipeline. |
| 213 | */ |
| 214 | export async function* queryModelOpenAI( |
| 215 | messages: Message[], |
| 216 | systemPrompt: SystemPrompt, |
| 217 | tools: Tools, |
| 218 | signal: AbortSignal, |
| 219 | options: Options, |
| 220 | ): AsyncGenerator< |
| 221 | StreamEvent | AssistantMessage | SystemAPIErrorMessage, |
| 222 | void |
| 223 | > { |
| 224 | try { |
| 225 | // 1. Resolve model name |
| 226 | const openaiModel = resolveOpenAIModel(options.model) |
| 227 | |
| 228 | // 2. Normalize messages using shared preprocessing |
| 229 | const messagesForAPI = normalizeMessagesForAPI(messages, tools) |
| 230 | |
| 231 | // 3. Check if tool search is enabled (similar to Anthropic path) |
| 232 | const useSearchExtraTools = await isSearchExtraToolsEnabled( |
| 233 | options.model, |
| 234 | tools, |
| 235 | options.getToolPermissionContext || |
| 236 | (async () => getEmptyToolPermissionContext()), |
| 237 | options.agents || [], |
| 238 | options.querySource, |
| 239 | ) |
| 240 | |
| 241 | // 4. Build deferred tools set (similar to Anthropic path) |
| 242 | const deferredToolNames = new Set<string>() |
| 243 | if (useSearchExtraTools) { |
| 244 | for (const t of tools) { |
| 245 | if (isDeferredTool(t)) deferredToolNames.add(t.name) |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | // 5. Filter tools (similar to Anthropic path) |
| 250 | // Never include deferred tools in the API tools array — they are invoked |
| 251 | // via ExecuteExtraTool which looks them up from the global tool registry |
| 252 | // at runtime. Keeping the tools array stable preserves the prompt cache. |
| 253 | let filteredTools = tools |
| 254 | if (useSearchExtraTools && deferredToolNames.size > 0) { |
| 255 | filteredTools = tools.filter(tool => { |
| 256 | // Always include non-deferred tools |
| 257 | if (!deferredToolNames.has(tool.name)) return true |
| 258 | // Always include SearchExtraToolsTool (so it can discover more tools) |
| 259 | if (toolMatchesName(tool, SEARCH_EXTRA_TOOLS_TOOL_NAME)) return true |
| 260 | // All other deferred tools are excluded — use ExecuteExtraTool instead |
| 261 | return false |
| 262 | }) |
| 263 | } |
| 264 | |
| 265 | // 6. Build tool schemas with deferLoading flag |
| 266 | const toolSchemas = await Promise.all( |
| 267 | filteredTools.map(tool => |
| 268 | toolToAPISchema(tool, { |
| 269 | getToolPermissionContext: options.getToolPermissionContext, |
| 270 | tools, |
| 271 | agents: options.agents, |