import type { XChatWithToolsOptions, XClientOptions, XStreamEvent, XToolCall, } from "../core/types"; import { stream } from "./stream"; import { buildAssistantMessage, buildRequestBase, createToolLoopConfig, executeToolCalls, throwIfMaxRoundsExceeded, } from "./tool-loop-runner"; export const streamWithTools = async function* ( options: XClientOptions, input: XChatWithToolsOptions, executors: Record Promise | unknown>, ): AsyncGenerator { const config = createToolLoopConfig(input, options); const requestBase = buildRequestBase(input, true); let rounds = 0; let currentMessages = [...input.messages]; while (rounds < config.maxRounds) { rounds += 1; const toolCalls: XToolCall[] = []; let assistantText = ""; let assistantReasoning = ""; for await (const event of stream(options, { ...requestBase, messages: currentMessages, })) { if (event.type === "text.delta") { assistantText += event.text; } if (event.type === "reasoning.delta") { assistantReasoning += event.text; } if (event.type === "tool_call.done") { toolCalls.push(event.toolCall); } yield event; } if (toolCalls.length === 0) { return; } const { toolMessages } = await executeToolCalls(config.provider, toolCalls, executors, config.strategy); const assistantMessage = buildAssistantMessage(assistantText, toolCalls, assistantReasoning); currentMessages = [...currentMessages, assistantMessage, ...toolMessages]; } throwIfMaxRoundsExceeded(config); };