@tanstack/ai 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/esm/base-adapter.d.ts +35 -0
- package/dist/esm/base-adapter.js +12 -0
- package/dist/esm/base-adapter.js.map +1 -0
- package/dist/esm/core/chat-common-options.d.ts +52 -0
- package/dist/esm/core/chat.d.ts +30 -0
- package/dist/esm/core/chat.js +533 -0
- package/dist/esm/core/chat.js.map +1 -0
- package/dist/esm/core/embedding.d.ts +8 -0
- package/dist/esm/core/embedding.js +33 -0
- package/dist/esm/core/embedding.js.map +1 -0
- package/dist/esm/core/summarize.d.ts +9 -0
- package/dist/esm/core/summarize.js +36 -0
- package/dist/esm/core/summarize.js.map +1 -0
- package/dist/esm/event-client.d.ts +311 -0
- package/dist/esm/event-client.js +62 -0
- package/dist/esm/event-client.js.map +1 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.js +50 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/message-converters.d.ts +52 -0
- package/dist/esm/message-converters.js +162 -0
- package/dist/esm/message-converters.js.map +1 -0
- package/dist/esm/stream/index.d.ts +11 -0
- package/dist/esm/stream/json-parser.d.ts +38 -0
- package/dist/esm/stream/json-parser.js +28 -0
- package/dist/esm/stream/json-parser.js.map +1 -0
- package/dist/esm/stream/message-updaters.d.ts +44 -0
- package/dist/esm/stream/message-updaters.js +141 -0
- package/dist/esm/stream/message-updaters.js.map +1 -0
- package/dist/esm/stream/processor.d.ts +242 -0
- package/dist/esm/stream/processor.js +693 -0
- package/dist/esm/stream/processor.js.map +1 -0
- package/dist/esm/stream/strategies.d.ts +43 -0
- package/dist/esm/stream/strategies.js +54 -0
- package/dist/esm/stream/strategies.js.map +1 -0
- package/dist/esm/stream/types.d.ts +71 -0
- package/dist/esm/tools/tool-calls.d.ts +112 -0
- package/dist/esm/tools/tool-calls.js +302 -0
- package/dist/esm/tools/tool-calls.js.map +1 -0
- package/dist/esm/tools/tool-definition.d.ts +125 -0
- package/dist/esm/tools/tool-definition.js +25 -0
- package/dist/esm/tools/tool-definition.js.map +1 -0
- package/dist/esm/tools/zod-converter.d.ts +30 -0
- package/dist/esm/tools/zod-converter.js +36 -0
- package/dist/esm/tools/zod-converter.js.map +1 -0
- package/dist/esm/types.d.ts +619 -0
- package/dist/esm/utilities/agent-loop-strategies.d.ts +59 -0
- package/dist/esm/utilities/agent-loop-strategies.js +23 -0
- package/dist/esm/utilities/agent-loop-strategies.js.map +1 -0
- package/dist/esm/utilities/chat-options.d.ts +6 -0
- package/dist/esm/utilities/chat-options.js +7 -0
- package/dist/esm/utilities/chat-options.js.map +1 -0
- package/dist/esm/utilities/messages.d.ts +30 -0
- package/dist/esm/utilities/messages.js +7 -0
- package/dist/esm/utilities/messages.js.map +1 -0
- package/dist/esm/utilities/stream-to-response.d.ts +48 -0
- package/dist/esm/utilities/stream-to-response.js +62 -0
- package/dist/esm/utilities/stream-to-response.js.map +1 -0
- package/package.json +65 -0
- package/src/base-adapter.ts +85 -0
- package/src/core/chat-common-options.ts +55 -0
- package/src/core/chat.ts +771 -0
- package/src/core/embedding.ts +54 -0
- package/src/core/summarize.ts +56 -0
- package/src/event-client.ts +389 -0
- package/src/index.ts +68 -0
- package/src/message-converters.ts +285 -0
- package/src/stream/index.ts +41 -0
- package/src/stream/json-parser.ts +58 -0
- package/src/stream/message-updaters.ts +275 -0
- package/src/stream/processor.ts +1092 -0
- package/src/stream/strategies.ts +78 -0
- package/src/stream/types.ts +94 -0
- package/src/tools/tool-calls.ts +471 -0
- package/src/tools/tool-definition.ts +206 -0
- package/src/tools/zod-converter.ts +85 -0
- package/src/types.ts +872 -0
- package/src/utilities/agent-loop-strategies.ts +85 -0
- package/src/utilities/chat-options.ts +35 -0
- package/src/utilities/messages.ts +63 -0
- package/src/utilities/stream-to-response.ts +116 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processor.js","sources":["../../../src/stream/processor.ts"],"sourcesContent":["/**\n * Unified Stream Processor\n *\n * Core stream processing engine that manages the full UIMessage[] conversation.\n * Single source of truth for message state.\n *\n * Handles:\n * - Full conversation management (UIMessage[])\n * - Text content accumulation with configurable chunking strategies\n * - Parallel tool calls with lifecycle state tracking\n * - Tool results and approval flows\n * - Thinking/reasoning content\n * - Recording/replay for testing\n * - Event-driven architecture for UI updates\n */\nimport {\n generateMessageId,\n uiMessageToModelMessages,\n} from '../message-converters'\nimport { defaultJSONParser } from './json-parser'\nimport {\n updateTextPart,\n updateThinkingPart,\n updateToolCallApproval,\n updateToolCallApprovalResponse,\n updateToolCallPart,\n updateToolCallWithOutput,\n updateToolResultPart,\n} from './message-updaters'\nimport { ImmediateStrategy } from './strategies'\nimport type {\n ChunkRecording,\n ChunkStrategy,\n InternalToolCallState,\n ProcessorResult,\n ProcessorState,\n ToolCallState,\n ToolResultState,\n} from './types'\nimport type {\n ModelMessage,\n StreamChunk,\n ToolCall,\n ToolCallPart,\n UIMessage,\n} from '../types'\n\n/**\n * Events emitted by the StreamProcessor\n */\nexport interface StreamProcessorEvents {\n // State events - full array on any change\n onMessagesChange?: (messages: Array<UIMessage>) => void\n\n // Lifecycle events\n onStreamStart?: () => void\n onStreamEnd?: (message: UIMessage) => void\n onError?: (error: Error) => void\n\n // Interaction events - client must handle these\n onToolCall?: (args: {\n toolCallId: string\n toolName: string\n input: any\n }) => void\n onApprovalRequest?: (args: {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n }) => void\n\n // Granular events for UI optimization (character-by-character, state tracking)\n onTextUpdate?: (messageId: string, content: string) => void\n onToolCallStateChange?: (\n messageId: string,\n toolCallId: string,\n state: ToolCallState,\n args: string,\n ) => void\n onThinkingUpdate?: (messageId: string, content: string) => void\n}\n\n/**\n * Legacy handlers for backward compatibility\n * These are the old callback-style handlers\n */\nexport interface StreamProcessorHandlers {\n onTextUpdate?: (content: string) => void\n onThinkingUpdate?: (content: string) => void\n\n // Tool call lifecycle handlers\n onToolCallStart?: (index: number, id: string, name: string) => void\n onToolCallDelta?: (index: number, args: string) => void\n onToolCallComplete?: (\n index: number,\n id: string,\n name: string,\n args: string,\n ) => void\n onToolCallStateChange?: (\n index: number,\n id: string,\n name: string,\n state: ToolCallState,\n args: string,\n parsedArgs?: any,\n ) => void\n\n // Tool result handlers\n onToolResultStateChange?: (\n toolCallId: string,\n content: string,\n state: ToolResultState,\n error?: string,\n ) => void\n\n // Approval/client tool handlers\n onApprovalRequested?: (\n toolCallId: string,\n toolName: string,\n input: any,\n approvalId: string,\n ) => void\n onToolInputAvailable?: (\n toolCallId: string,\n toolName: string,\n input: any,\n ) => void\n\n // Stream lifecycle\n onStreamEnd?: (content: string, toolCalls?: Array<ToolCall>) => void\n onError?: (error: { message: string; code?: string }) => void\n}\n\n/**\n * Options for StreamProcessor\n */\nexport interface StreamProcessorOptions {\n chunkStrategy?: ChunkStrategy\n /** New event-driven handlers */\n events?: StreamProcessorEvents\n /** Legacy callback handlers (for backward compatibility) */\n handlers?: StreamProcessorHandlers\n jsonParser?: {\n parse: (jsonString: string) => any\n }\n /** Enable recording for replay testing */\n recording?: boolean\n /** Initial messages to populate the processor */\n initialMessages?: Array<UIMessage>\n}\n\n/**\n * StreamProcessor - State machine for processing AI response streams\n *\n * Manages the full UIMessage[] conversation and emits events on changes.\n *\n * State tracking:\n * - Full message array\n * - Current assistant message being streamed\n * - Text content accumulation\n * - Multiple parallel tool calls\n * - Tool call completion detection\n *\n * Tool call completion is detected when:\n * 1. A new tool call starts at a different index\n * 2. Text content arrives\n * 3. Stream ends\n */\nexport class StreamProcessor {\n private chunkStrategy: ChunkStrategy\n private events: StreamProcessorEvents\n private handlers: StreamProcessorHandlers\n private jsonParser: { parse: (jsonString: string) => any }\n private recordingEnabled: boolean\n\n // Message state\n private messages: Array<UIMessage> = []\n private currentAssistantMessageId: string | null = null\n\n // Stream state for current assistant message\n // Total accumulated text across all segments (for the final result)\n private totalTextContent = ''\n // Current segment's text content (for onTextUpdate callbacks)\n private currentSegmentText = ''\n private lastEmittedText = ''\n private thinkingContent = ''\n private toolCalls: Map<string, InternalToolCallState> = new Map()\n private toolCallOrder: Array<string> = []\n private finishReason: string | null = null\n private isDone = false\n // Track if we've had tool calls since the last text segment started\n private hasToolCallsSinceTextStart = false\n\n // Recording\n private recording: ChunkRecording | null = null\n private recordingStartTime = 0\n\n constructor(options: StreamProcessorOptions = {}) {\n this.chunkStrategy = options.chunkStrategy || new ImmediateStrategy()\n this.events = options.events || {}\n this.handlers = options.handlers || {}\n this.jsonParser = options.jsonParser || defaultJSONParser\n this.recordingEnabled = options.recording ?? false\n\n // Initialize with provided messages\n if (options.initialMessages) {\n this.messages = [...options.initialMessages]\n }\n }\n\n // ============================================\n // Message Management Methods\n // ============================================\n\n /**\n * Set the messages array (e.g., from persisted state)\n */\n setMessages(messages: Array<UIMessage>): void {\n this.messages = [...messages]\n this.emitMessagesChange()\n }\n\n /**\n * Add a user message to the conversation\n */\n addUserMessage(content: string): UIMessage {\n const userMessage: UIMessage = {\n id: generateMessageId(),\n role: 'user',\n parts: [{ type: 'text', content }],\n createdAt: new Date(),\n }\n\n this.messages = [...this.messages, userMessage]\n this.emitMessagesChange()\n\n return userMessage\n }\n\n /**\n * Start streaming a new assistant message\n * Returns the message ID\n */\n startAssistantMessage(): string {\n // Reset stream state for new message\n this.resetStreamState()\n\n const assistantMessage: UIMessage = {\n id: generateMessageId(),\n role: 'assistant',\n parts: [],\n createdAt: new Date(),\n }\n\n this.currentAssistantMessageId = assistantMessage.id\n this.messages = [...this.messages, assistantMessage]\n\n // Emit events\n this.events.onStreamStart?.()\n this.emitMessagesChange()\n\n return assistantMessage.id\n }\n\n /**\n * Add a tool result (called by client after handling onToolCall)\n */\n addToolResult(toolCallId: string, output: any, error?: string): void {\n // Find the message containing this tool call\n const messageWithToolCall = this.messages.find((msg) =>\n msg.parts.some(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n ),\n )\n\n if (!messageWithToolCall) {\n console.warn(\n `[StreamProcessor] Could not find message with tool call ${toolCallId}`,\n )\n return\n }\n\n // Step 1: Update the tool-call part's output field (for UI rendering)\n let updatedMessages = updateToolCallWithOutput(\n this.messages,\n toolCallId,\n output,\n error ? 'input-complete' : undefined,\n error,\n )\n\n // Step 2: Create a tool-result part (for LLM conversation history)\n const content = typeof output === 'string' ? output : JSON.stringify(output)\n const toolResultState: ToolResultState = error ? 'error' : 'complete'\n\n updatedMessages = updateToolResultPart(\n updatedMessages,\n messageWithToolCall.id,\n toolCallId,\n content,\n toolResultState,\n error,\n )\n\n this.messages = updatedMessages\n this.emitMessagesChange()\n }\n\n /**\n * Add an approval response (called by client after handling onApprovalRequest)\n */\n addToolApprovalResponse(approvalId: string, approved: boolean): void {\n this.messages = updateToolCallApprovalResponse(\n this.messages,\n approvalId,\n approved,\n )\n this.emitMessagesChange()\n }\n\n /**\n * Get the conversation as ModelMessages (for sending to LLM)\n */\n toModelMessages(): Array<ModelMessage> {\n const modelMessages: Array<ModelMessage> = []\n for (const msg of this.messages) {\n modelMessages.push(...uiMessageToModelMessages(msg))\n }\n return modelMessages\n }\n\n /**\n * Get current messages\n */\n getMessages(): Array<UIMessage> {\n return this.messages\n }\n\n /**\n * Check if all tool calls in the last assistant message are complete\n * Useful for auto-continue logic\n */\n areAllToolsComplete(): boolean {\n const lastAssistant = this.messages.findLast(\n (m: UIMessage) => m.role === 'assistant',\n )\n\n if (!lastAssistant) return true\n\n const toolParts = lastAssistant.parts.filter(\n (p): p is ToolCallPart => p.type === 'tool-call',\n )\n\n if (toolParts.length === 0) return true\n\n // All tool calls must be in a terminal state\n return toolParts.every(\n (part) =>\n part.state === 'approval-responded' ||\n (part.output !== undefined && !part.approval),\n )\n }\n\n /**\n * Remove messages after a certain index (for reload/retry)\n */\n removeMessagesAfter(index: number): void {\n this.messages = this.messages.slice(0, index + 1)\n this.emitMessagesChange()\n }\n\n /**\n * Clear all messages\n */\n clearMessages(): void {\n this.messages = []\n this.currentAssistantMessageId = null\n this.emitMessagesChange()\n }\n\n // ============================================\n // Stream Processing Methods\n // ============================================\n\n /**\n * Process a stream and emit events through handlers\n */\n async process(stream: AsyncIterable<any>): Promise<ProcessorResult> {\n // Reset stream state (but keep messages)\n this.resetStreamState()\n\n // Start recording if enabled\n if (this.recordingEnabled) {\n this.startRecording()\n }\n\n // Process each chunk\n for await (const chunk of stream) {\n this.processChunk(chunk)\n }\n\n // Stream ended - finalize everything\n this.finalizeStream()\n\n // Finalize recording\n if (this.recording) {\n this.recording.result = this.getResult()\n }\n\n return this.getResult()\n }\n\n /**\n * Process a single chunk from the stream\n */\n processChunk(chunk: StreamChunk): void {\n // Record chunk if enabled\n if (this.recording) {\n this.recording.chunks.push({\n chunk,\n timestamp: Date.now(),\n index: this.recording.chunks.length,\n })\n }\n\n switch (chunk.type) {\n case 'content':\n this.handleContentChunk(chunk)\n break\n\n case 'tool_call':\n this.handleToolCallChunk(chunk)\n break\n\n case 'tool_result':\n this.handleToolResultChunk(chunk)\n break\n\n case 'done':\n this.handleDoneChunk(chunk)\n break\n\n case 'error':\n this.handleErrorChunk(chunk)\n break\n\n case 'thinking':\n this.handleThinkingChunk(chunk)\n break\n\n case 'approval-requested':\n this.handleApprovalRequestedChunk(chunk)\n break\n\n case 'tool-input-available':\n this.handleToolInputAvailableChunk(chunk)\n break\n\n default:\n // Unknown chunk type - ignore\n break\n }\n }\n\n /**\n * Handle a content chunk\n */\n private handleContentChunk(\n chunk: Extract<StreamChunk, { type: 'content' }>,\n ): void {\n // Content arriving means all current tool calls are complete\n this.completeAllToolCalls()\n\n const previousSegment = this.currentSegmentText\n\n // Detect if this is a NEW text segment (after tool calls) vs continuation\n const isNewSegment =\n this.hasToolCallsSinceTextStart &&\n previousSegment.length > 0 &&\n this.isNewTextSegment(chunk, previousSegment)\n\n if (isNewSegment) {\n // Emit any accumulated text before starting new segment\n if (previousSegment !== this.lastEmittedText) {\n this.emitTextUpdate()\n }\n // Reset SEGMENT text accumulation for the new text segment after tool calls\n this.currentSegmentText = ''\n this.lastEmittedText = ''\n this.hasToolCallsSinceTextStart = false\n }\n\n const currentText = this.currentSegmentText\n let nextText = currentText\n\n // Prefer delta over content - delta is the incremental change\n if (chunk.delta !== '') {\n nextText = currentText + chunk.delta\n } else if (chunk.content !== '') {\n // Fallback: use content if delta is not provided\n if (chunk.content.startsWith(currentText)) {\n nextText = chunk.content\n } else if (currentText.startsWith(chunk.content)) {\n nextText = currentText\n } else {\n nextText = currentText + chunk.content\n }\n }\n\n // Calculate the delta for totalTextContent\n const textDelta = nextText.slice(currentText.length)\n this.currentSegmentText = nextText\n this.totalTextContent += textDelta\n\n // Use delta for chunk strategy if available\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const chunkPortion = chunk.delta ?? chunk.content ?? ''\n const shouldEmit = this.chunkStrategy.shouldEmit(\n chunkPortion,\n this.currentSegmentText,\n )\n if (shouldEmit && this.currentSegmentText !== this.lastEmittedText) {\n this.emitTextUpdate()\n }\n }\n\n /**\n * Handle a tool call chunk\n */\n private handleToolCallChunk(\n chunk: Extract<StreamChunk, { type: 'tool_call' }>,\n ): void {\n // Mark that we've seen tool calls since the last text segment\n this.hasToolCallsSinceTextStart = true\n\n const toolCallId = chunk.toolCall.id\n const existingToolCall = this.toolCalls.get(toolCallId)\n\n if (!existingToolCall) {\n // New tool call starting\n const initialState: ToolCallState = chunk.toolCall.function.arguments\n ? 'input-streaming'\n : 'awaiting-input'\n\n const newToolCall: InternalToolCallState = {\n id: chunk.toolCall.id,\n name: chunk.toolCall.function.name,\n arguments: chunk.toolCall.function.arguments || '',\n state: initialState,\n parsedArguments: undefined,\n index: chunk.index,\n }\n\n // Try to parse the arguments\n if (chunk.toolCall.function.arguments) {\n newToolCall.parsedArguments = this.jsonParser.parse(\n chunk.toolCall.function.arguments,\n )\n }\n\n this.toolCalls.set(toolCallId, newToolCall)\n this.toolCallOrder.push(toolCallId)\n\n // Get actual index for this tool call (based on order)\n const actualIndex = this.toolCallOrder.indexOf(toolCallId)\n\n // Emit legacy lifecycle event\n this.handlers.onToolCallStart?.(\n actualIndex,\n chunk.toolCall.id,\n chunk.toolCall.function.name,\n )\n\n // Emit legacy state change event\n this.handlers.onToolCallStateChange?.(\n actualIndex,\n chunk.toolCall.id,\n chunk.toolCall.function.name,\n initialState,\n chunk.toolCall.function.arguments || '',\n newToolCall.parsedArguments,\n )\n\n // Emit initial delta\n if (chunk.toolCall.function.arguments) {\n this.handlers.onToolCallDelta?.(\n actualIndex,\n chunk.toolCall.function.arguments,\n )\n }\n\n // Update UIMessage\n if (this.currentAssistantMessageId) {\n this.messages = updateToolCallPart(\n this.messages,\n this.currentAssistantMessageId,\n {\n id: chunk.toolCall.id,\n name: chunk.toolCall.function.name,\n arguments: chunk.toolCall.function.arguments || '',\n state: initialState,\n },\n )\n this.emitMessagesChange()\n\n // Emit new granular event\n this.events.onToolCallStateChange?.(\n this.currentAssistantMessageId,\n chunk.toolCall.id,\n initialState,\n chunk.toolCall.function.arguments || '',\n )\n }\n } else {\n // Continuing existing tool call\n const wasAwaitingInput = existingToolCall.state === 'awaiting-input'\n\n existingToolCall.arguments += chunk.toolCall.function.arguments || ''\n\n // Update state\n if (wasAwaitingInput && chunk.toolCall.function.arguments) {\n existingToolCall.state = 'input-streaming'\n }\n\n // Try to parse the updated arguments\n existingToolCall.parsedArguments = this.jsonParser.parse(\n existingToolCall.arguments,\n )\n\n // Get actual index for this tool call\n const actualIndex = this.toolCallOrder.indexOf(toolCallId)\n\n // Emit legacy state change event\n this.handlers.onToolCallStateChange?.(\n actualIndex,\n existingToolCall.id,\n existingToolCall.name,\n existingToolCall.state,\n existingToolCall.arguments,\n existingToolCall.parsedArguments,\n )\n\n // Emit delta\n if (chunk.toolCall.function.arguments) {\n this.handlers.onToolCallDelta?.(\n actualIndex,\n chunk.toolCall.function.arguments,\n )\n }\n\n // Update UIMessage\n if (this.currentAssistantMessageId) {\n this.messages = updateToolCallPart(\n this.messages,\n this.currentAssistantMessageId,\n {\n id: existingToolCall.id,\n name: existingToolCall.name,\n arguments: existingToolCall.arguments,\n state: existingToolCall.state,\n },\n )\n this.emitMessagesChange()\n\n // Emit new granular event\n this.events.onToolCallStateChange?.(\n this.currentAssistantMessageId,\n existingToolCall.id,\n existingToolCall.state,\n existingToolCall.arguments,\n )\n }\n }\n }\n\n /**\n * Handle a tool result chunk\n */\n private handleToolResultChunk(\n chunk: Extract<StreamChunk, { type: 'tool_result' }>,\n ): void {\n const state: ToolResultState = 'complete'\n\n // Emit legacy handler\n this.handlers.onToolResultStateChange?.(\n chunk.toolCallId,\n chunk.content,\n state,\n )\n\n // Update UIMessage if we have a current assistant message\n if (this.currentAssistantMessageId) {\n this.messages = updateToolResultPart(\n this.messages,\n this.currentAssistantMessageId,\n chunk.toolCallId,\n chunk.content,\n state,\n )\n this.emitMessagesChange()\n }\n }\n\n /**\n * Handle a done chunk\n */\n private handleDoneChunk(chunk: Extract<StreamChunk, { type: 'done' }>): void {\n this.finishReason = chunk.finishReason\n this.isDone = true\n this.completeAllToolCalls()\n }\n\n /**\n * Handle an error chunk\n */\n private handleErrorChunk(\n chunk: Extract<StreamChunk, { type: 'error' }>,\n ): void {\n // Emit legacy handler\n this.handlers.onError?.(chunk.error)\n\n // Emit new event\n this.events.onError?.(new Error(chunk.error.message))\n }\n\n /**\n * Handle a thinking chunk\n */\n private handleThinkingChunk(\n chunk: Extract<StreamChunk, { type: 'thinking' }>,\n ): void {\n const previous = this.thinkingContent\n let nextThinking = previous\n\n // Prefer delta over content\n if (chunk.delta !== '') {\n nextThinking = previous + chunk.delta\n } else if (chunk.content !== '') {\n if (chunk.content.startsWith(previous)) {\n nextThinking = chunk.content\n } else if (previous.startsWith(chunk.content)) {\n nextThinking = previous\n } else {\n nextThinking = previous + chunk.content\n }\n }\n\n this.thinkingContent = nextThinking\n\n // Emit legacy handler\n this.handlers.onThinkingUpdate?.(this.thinkingContent)\n\n // Update UIMessage\n if (this.currentAssistantMessageId) {\n this.messages = updateThinkingPart(\n this.messages,\n this.currentAssistantMessageId,\n this.thinkingContent,\n )\n this.emitMessagesChange()\n\n // Emit new granular event\n this.events.onThinkingUpdate?.(\n this.currentAssistantMessageId,\n this.thinkingContent,\n )\n }\n }\n\n /**\n * Handle an approval-requested chunk\n */\n private handleApprovalRequestedChunk(\n chunk: Extract<StreamChunk, { type: 'approval-requested' }>,\n ): void {\n // Emit legacy handler\n this.handlers.onApprovalRequested?.(\n chunk.toolCallId,\n chunk.toolName,\n chunk.input,\n chunk.approval.id,\n )\n\n // Update UIMessage with approval metadata\n if (this.currentAssistantMessageId) {\n this.messages = updateToolCallApproval(\n this.messages,\n this.currentAssistantMessageId,\n chunk.toolCallId,\n chunk.approval.id,\n )\n this.emitMessagesChange()\n }\n\n // Emit new event\n this.events.onApprovalRequest?.({\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n input: chunk.input,\n approvalId: chunk.approval.id,\n })\n }\n\n /**\n * Handle a tool-input-available chunk\n */\n private handleToolInputAvailableChunk(\n chunk: Extract<StreamChunk, { type: 'tool-input-available' }>,\n ): void {\n // Emit legacy handler\n this.handlers.onToolInputAvailable?.(\n chunk.toolCallId,\n chunk.toolName,\n chunk.input,\n )\n\n // Emit new event\n this.events.onToolCall?.({\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n input: chunk.input,\n })\n }\n\n /**\n * Detect if an incoming content chunk represents a NEW text segment\n */\n private isNewTextSegment(\n chunk: Extract<StreamChunk, { type: 'content' }>,\n previous: string,\n ): boolean {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (chunk.delta !== undefined && chunk.content !== undefined) {\n if (chunk.content.length < previous.length) {\n return true\n }\n if (\n !chunk.content.startsWith(previous) &&\n !previous.startsWith(chunk.content)\n ) {\n return true\n }\n }\n return false\n }\n\n /**\n * Complete all tool calls\n */\n private completeAllToolCalls(): void {\n this.toolCalls.forEach((toolCall, id) => {\n if (toolCall.state !== 'input-complete') {\n const index = this.toolCallOrder.indexOf(id)\n this.completeToolCall(index, toolCall)\n }\n })\n }\n\n /**\n * Mark a tool call as complete and emit event\n */\n private completeToolCall(\n index: number,\n toolCall: InternalToolCallState,\n ): void {\n toolCall.state = 'input-complete'\n\n // Try final parse\n toolCall.parsedArguments = this.jsonParser.parse(toolCall.arguments)\n\n // Emit legacy state change event\n this.handlers.onToolCallStateChange?.(\n index,\n toolCall.id,\n toolCall.name,\n 'input-complete',\n toolCall.arguments,\n toolCall.parsedArguments,\n )\n\n // Emit legacy complete event\n this.handlers.onToolCallComplete?.(\n index,\n toolCall.id,\n toolCall.name,\n toolCall.arguments,\n )\n\n // Update UIMessage\n if (this.currentAssistantMessageId) {\n this.messages = updateToolCallPart(\n this.messages,\n this.currentAssistantMessageId,\n {\n id: toolCall.id,\n name: toolCall.name,\n arguments: toolCall.arguments,\n state: 'input-complete',\n },\n )\n this.emitMessagesChange()\n\n // Emit new granular event\n this.events.onToolCallStateChange?.(\n this.currentAssistantMessageId,\n toolCall.id,\n 'input-complete',\n toolCall.arguments,\n )\n }\n }\n\n /**\n * Emit pending text update\n */\n private emitTextUpdate(): void {\n this.lastEmittedText = this.currentSegmentText\n\n // Emit legacy handler\n this.handlers.onTextUpdate?.(this.currentSegmentText)\n\n // Update UIMessage\n if (this.currentAssistantMessageId) {\n this.messages = updateTextPart(\n this.messages,\n this.currentAssistantMessageId,\n this.currentSegmentText,\n )\n this.emitMessagesChange()\n\n // Emit new granular event\n this.events.onTextUpdate?.(\n this.currentAssistantMessageId,\n this.currentSegmentText,\n )\n }\n }\n\n /**\n * Emit messages change event\n */\n private emitMessagesChange(): void {\n this.events.onMessagesChange?.([...this.messages])\n }\n\n /**\n * Finalize the stream - complete all pending operations\n */\n finalizeStream(): void {\n // Complete any remaining tool calls\n this.completeAllToolCalls()\n\n // Emit any pending text if not already emitted\n if (this.currentSegmentText !== this.lastEmittedText) {\n this.emitTextUpdate()\n }\n\n // Emit legacy stream end with total accumulated content\n const toolCalls = this.getCompletedToolCalls()\n this.handlers.onStreamEnd?.(\n this.totalTextContent,\n toolCalls.length > 0 ? toolCalls : undefined,\n )\n\n // Emit new stream end event\n if (this.currentAssistantMessageId) {\n const assistantMessage = this.messages.find(\n (m) => m.id === this.currentAssistantMessageId,\n )\n if (assistantMessage) {\n this.events.onStreamEnd?.(assistantMessage)\n }\n }\n }\n\n /**\n * Get completed tool calls in API format\n */\n private getCompletedToolCalls(): Array<ToolCall> {\n return Array.from(this.toolCalls.values())\n .filter((tc) => tc.state === 'input-complete')\n .map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.name,\n arguments: tc.arguments,\n },\n }))\n }\n\n /**\n * Get current result\n */\n private getResult(): ProcessorResult {\n const toolCalls = this.getCompletedToolCalls()\n return {\n content: this.totalTextContent,\n thinking: this.thinkingContent || undefined,\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n finishReason: this.finishReason,\n }\n }\n\n /**\n * Get current processor state (legacy)\n */\n getState(): ProcessorState {\n return {\n content: this.totalTextContent,\n thinking: this.thinkingContent,\n toolCalls: new Map(this.toolCalls),\n toolCallOrder: [...this.toolCallOrder],\n finishReason: this.finishReason,\n done: this.isDone,\n }\n }\n\n /**\n * Start recording chunks\n */\n startRecording(): void {\n this.recordingEnabled = true\n this.recordingStartTime = Date.now()\n this.recording = {\n version: '1.0',\n timestamp: this.recordingStartTime,\n chunks: [],\n }\n }\n\n /**\n * Get the current recording\n */\n getRecording(): ChunkRecording | null {\n return this.recording\n }\n\n /**\n * Reset stream state (but keep messages)\n */\n private resetStreamState(): void {\n this.totalTextContent = ''\n this.currentSegmentText = ''\n this.lastEmittedText = ''\n this.thinkingContent = ''\n this.toolCalls.clear()\n this.toolCallOrder = []\n this.finishReason = null\n this.isDone = false\n this.hasToolCallsSinceTextStart = false\n this.chunkStrategy.reset?.()\n }\n\n /**\n * Full reset (including messages)\n */\n reset(): void {\n this.resetStreamState()\n this.messages = []\n this.currentAssistantMessageId = null\n }\n\n /**\n * Replay a recording through the processor\n */\n static async replay(\n recording: ChunkRecording,\n options?: StreamProcessorOptions,\n ): Promise<ProcessorResult> {\n const processor = new StreamProcessor(options)\n return processor.process(createReplayStream(recording))\n }\n}\n\n/**\n * Create an async iterable from a recording\n */\nexport function createReplayStream(\n recording: ChunkRecording,\n): AsyncIterable<StreamChunk> {\n return {\n // eslint-disable-next-line @typescript-eslint/require-await\n async *[Symbol.asyncIterator]() {\n for (const { chunk } of recording.chunks) {\n yield chunk\n }\n },\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ChunkStrategy } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Immediate Strategy - emit on every chunk (default behavior)
|
|
4
|
+
*/
|
|
5
|
+
export declare class ImmediateStrategy implements ChunkStrategy {
|
|
6
|
+
shouldEmit(_chunk: string, _accumulated: string): boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Punctuation Strategy - emit when chunk contains punctuation
|
|
10
|
+
* Useful for natural text flow in UI
|
|
11
|
+
*/
|
|
12
|
+
export declare class PunctuationStrategy implements ChunkStrategy {
|
|
13
|
+
private punctuation;
|
|
14
|
+
shouldEmit(chunk: string, _accumulated: string): boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Batch Strategy - emit every N chunks
|
|
18
|
+
* Useful for reducing UI update frequency
|
|
19
|
+
*/
|
|
20
|
+
export declare class BatchStrategy implements ChunkStrategy {
|
|
21
|
+
private batchSize;
|
|
22
|
+
private chunkCount;
|
|
23
|
+
constructor(batchSize?: number);
|
|
24
|
+
shouldEmit(_chunk: string, _accumulated: string): boolean;
|
|
25
|
+
reset(): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Word Boundary Strategy - emit at word boundaries
|
|
29
|
+
* Prevents cutting words in half
|
|
30
|
+
*/
|
|
31
|
+
export declare class WordBoundaryStrategy implements ChunkStrategy {
|
|
32
|
+
shouldEmit(chunk: string, _accumulated: string): boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Composite Strategy - combine multiple strategies (OR logic)
|
|
36
|
+
* Emits if ANY strategy says to emit
|
|
37
|
+
*/
|
|
38
|
+
export declare class CompositeStrategy implements ChunkStrategy {
|
|
39
|
+
private strategies;
|
|
40
|
+
constructor(strategies: Array<ChunkStrategy>);
|
|
41
|
+
shouldEmit(chunk: string, accumulated: string): boolean;
|
|
42
|
+
reset(): void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
class ImmediateStrategy {
|
|
2
|
+
shouldEmit(_chunk, _accumulated) {
|
|
3
|
+
return true;
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
class PunctuationStrategy {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.punctuation = /[.,!?;:\n]/;
|
|
9
|
+
}
|
|
10
|
+
shouldEmit(chunk, _accumulated) {
|
|
11
|
+
return this.punctuation.test(chunk);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
class BatchStrategy {
|
|
15
|
+
constructor(batchSize = 5) {
|
|
16
|
+
this.batchSize = batchSize;
|
|
17
|
+
this.chunkCount = 0;
|
|
18
|
+
}
|
|
19
|
+
shouldEmit(_chunk, _accumulated) {
|
|
20
|
+
this.chunkCount++;
|
|
21
|
+
if (this.chunkCount >= this.batchSize) {
|
|
22
|
+
this.chunkCount = 0;
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
reset() {
|
|
28
|
+
this.chunkCount = 0;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
class WordBoundaryStrategy {
|
|
32
|
+
shouldEmit(chunk, _accumulated) {
|
|
33
|
+
return /\s$/.test(chunk);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class CompositeStrategy {
|
|
37
|
+
constructor(strategies) {
|
|
38
|
+
this.strategies = strategies;
|
|
39
|
+
}
|
|
40
|
+
shouldEmit(chunk, accumulated) {
|
|
41
|
+
return this.strategies.some((s) => s.shouldEmit(chunk, accumulated));
|
|
42
|
+
}
|
|
43
|
+
reset() {
|
|
44
|
+
this.strategies.forEach((s) => s.reset?.());
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
BatchStrategy,
|
|
49
|
+
CompositeStrategy,
|
|
50
|
+
ImmediateStrategy,
|
|
51
|
+
PunctuationStrategy,
|
|
52
|
+
WordBoundaryStrategy
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=strategies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategies.js","sources":["../../../src/stream/strategies.ts"],"sourcesContent":["/**\n * Built-in Chunk Strategies\n *\n * Strategies for controlling when text updates are emitted to the UI\n */\n\nimport type { ChunkStrategy } from './types'\n\n/**\n * Immediate Strategy - emit on every chunk (default behavior)\n */\nexport class ImmediateStrategy implements ChunkStrategy {\n shouldEmit(_chunk: string, _accumulated: string): boolean {\n return true\n }\n}\n\n/**\n * Punctuation Strategy - emit when chunk contains punctuation\n * Useful for natural text flow in UI\n */\nexport class PunctuationStrategy implements ChunkStrategy {\n private punctuation = /[.,!?;:\\n]/\n\n shouldEmit(chunk: string, _accumulated: string): boolean {\n return this.punctuation.test(chunk)\n }\n}\n\n/**\n * Batch Strategy - emit every N chunks\n * Useful for reducing UI update frequency\n */\nexport class BatchStrategy implements ChunkStrategy {\n private chunkCount = 0\n\n constructor(private batchSize: number = 5) {}\n\n shouldEmit(_chunk: string, _accumulated: string): boolean {\n this.chunkCount++\n if (this.chunkCount >= this.batchSize) {\n this.chunkCount = 0\n return true\n }\n return false\n }\n\n reset(): void {\n this.chunkCount = 0\n }\n}\n\n/**\n * Word Boundary Strategy - emit at word boundaries\n * Prevents cutting words in half\n */\nexport class WordBoundaryStrategy implements ChunkStrategy {\n shouldEmit(chunk: string, _accumulated: string): boolean {\n // Emit if chunk ends with whitespace\n return /\\s$/.test(chunk)\n }\n}\n\n/**\n * Composite Strategy - combine multiple strategies (OR logic)\n * Emits if ANY strategy says to emit\n */\nexport class CompositeStrategy implements ChunkStrategy {\n constructor(private strategies: Array<ChunkStrategy>) {}\n\n shouldEmit(chunk: string, accumulated: string): boolean {\n return this.strategies.some((s) => s.shouldEmit(chunk, accumulated))\n }\n\n reset(): void {\n this.strategies.forEach((s) => s.reset?.())\n }\n}\n"],"names":[],"mappings":"AAWO,MAAM,kBAA2C;AAAA,EACtD,WAAW,QAAgB,cAA+B;AACxD,WAAO;AAAA,EACT;AACF;AAMO,MAAM,oBAA6C;AAAA,EAAnD,cAAA;AACL,SAAQ,cAAc;AAAA,EAAA;AAAA,EAEtB,WAAW,OAAe,cAA+B;AACvD,WAAO,KAAK,YAAY,KAAK,KAAK;AAAA,EACpC;AACF;AAMO,MAAM,cAAuC;AAAA,EAGlD,YAAoB,YAAoB,GAAG;AAAvB,SAAA,YAAA;AAFpB,SAAQ,aAAa;AAAA,EAEuB;AAAA,EAE5C,WAAW,QAAgB,cAA+B;AACxD,SAAK;AACL,QAAI,KAAK,cAAc,KAAK,WAAW;AACrC,WAAK,aAAa;AAClB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;AAMO,MAAM,qBAA8C;AAAA,EACzD,WAAW,OAAe,cAA+B;AAEvD,WAAO,MAAM,KAAK,KAAK;AAAA,EACzB;AACF;AAMO,MAAM,kBAA2C;AAAA,EACtD,YAAoB,YAAkC;AAAlC,SAAA,aAAA;AAAA,EAAmC;AAAA,EAEvD,WAAW,OAAe,aAA8B;AACtD,WAAO,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,WAAW,OAAO,WAAW,CAAC;AAAA,EACrE;AAAA,EAEA,QAAc;AACZ,SAAK,WAAW,QAAQ,CAAC,MAAM,EAAE,SAAS;AAAA,EAC5C;AACF;"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { StreamChunk, ToolCall } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Tool call states - track the lifecycle of a tool call
|
|
4
|
+
*/
|
|
5
|
+
export type ToolCallState = 'awaiting-input' | 'input-streaming' | 'input-complete' | 'approval-requested' | 'approval-responded';
|
|
6
|
+
/**
|
|
7
|
+
* Tool result states - track the lifecycle of a tool result
|
|
8
|
+
*/
|
|
9
|
+
export type ToolResultState = 'streaming' | 'complete' | 'error';
|
|
10
|
+
/**
|
|
11
|
+
* Internal state for a tool call being tracked
|
|
12
|
+
*/
|
|
13
|
+
export interface InternalToolCallState {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
arguments: string;
|
|
17
|
+
state: ToolCallState;
|
|
18
|
+
parsedArguments?: any;
|
|
19
|
+
index: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Strategy for determining when to emit text updates
|
|
23
|
+
*/
|
|
24
|
+
export interface ChunkStrategy {
|
|
25
|
+
/**
|
|
26
|
+
* Called for each text chunk received
|
|
27
|
+
* @param chunk - The new chunk of text (delta)
|
|
28
|
+
* @param accumulated - All text accumulated so far
|
|
29
|
+
* @returns true if an update should be emitted now
|
|
30
|
+
*/
|
|
31
|
+
shouldEmit: (chunk: string, accumulated: string) => boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Optional: Reset strategy state (called when streaming starts)
|
|
34
|
+
*/
|
|
35
|
+
reset?: () => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Result from processing a stream
|
|
39
|
+
*/
|
|
40
|
+
export interface ProcessorResult {
|
|
41
|
+
content: string;
|
|
42
|
+
thinking?: string;
|
|
43
|
+
toolCalls?: Array<ToolCall>;
|
|
44
|
+
finishReason?: string | null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Current state of the processor
|
|
48
|
+
*/
|
|
49
|
+
export interface ProcessorState {
|
|
50
|
+
content: string;
|
|
51
|
+
thinking: string;
|
|
52
|
+
toolCalls: Map<string, InternalToolCallState>;
|
|
53
|
+
toolCallOrder: Array<string>;
|
|
54
|
+
finishReason: string | null;
|
|
55
|
+
done: boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Recording format for replay testing
|
|
59
|
+
*/
|
|
60
|
+
export interface ChunkRecording {
|
|
61
|
+
version: '1.0';
|
|
62
|
+
timestamp: number;
|
|
63
|
+
model?: string;
|
|
64
|
+
provider?: string;
|
|
65
|
+
chunks: Array<{
|
|
66
|
+
chunk: StreamChunk;
|
|
67
|
+
timestamp: number;
|
|
68
|
+
index: number;
|
|
69
|
+
}>;
|
|
70
|
+
result?: ProcessorResult;
|
|
71
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { DoneStreamChunk, ModelMessage, Tool, ToolCall, ToolResultStreamChunk } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* - Accumulates streaming tool call chunks (ID, name, arguments)
|
|
7
|
+
* - Validates tool calls (filters out incomplete ones)
|
|
8
|
+
* - Executes tool `execute` functions with parsed arguments
|
|
9
|
+
* - Emits `tool_result` chunks for client visibility
|
|
10
|
+
* - Returns tool result messages for conversation history
|
|
11
|
+
*
|
|
12
|
+
* This class is used internally by the AI.chat() method to handle the automatic
|
|
13
|
+
* tool execution loop. It can also be used independently for custom tool execution logic.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const manager = new ToolCallManager(tools);
|
|
18
|
+
*
|
|
19
|
+
* // During streaming, accumulate tool calls
|
|
20
|
+
* for await (const chunk of stream) {
|
|
21
|
+
* if (chunk.type === "tool_call") {
|
|
22
|
+
* manager.addToolCallChunk(chunk);
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* // After stream completes, execute tools
|
|
27
|
+
* if (manager.hasToolCalls()) {
|
|
28
|
+
* const toolResults = yield* manager.executeTools(doneChunk);
|
|
29
|
+
* messages = [...messages, ...toolResults];
|
|
30
|
+
* manager.clear();
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare class ToolCallManager {
|
|
35
|
+
private toolCallsMap;
|
|
36
|
+
private tools;
|
|
37
|
+
constructor(tools: ReadonlyArray<Tool>);
|
|
38
|
+
/**
|
|
39
|
+
* Add a tool call chunk to the accumulator
|
|
40
|
+
* Handles streaming tool calls by accumulating arguments
|
|
41
|
+
*/
|
|
42
|
+
addToolCallChunk(chunk: {
|
|
43
|
+
toolCall: {
|
|
44
|
+
id: string;
|
|
45
|
+
type: 'function';
|
|
46
|
+
function: {
|
|
47
|
+
name: string;
|
|
48
|
+
arguments: string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
index: number;
|
|
52
|
+
}): void;
|
|
53
|
+
/**
|
|
54
|
+
* Check if there are any complete tool calls to execute
|
|
55
|
+
*/
|
|
56
|
+
hasToolCalls(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Get all complete tool calls (filtered for valid ID and name)
|
|
59
|
+
*/
|
|
60
|
+
getToolCalls(): Array<ToolCall>;
|
|
61
|
+
/**
|
|
62
|
+
* Execute all tool calls and return tool result messages
|
|
63
|
+
* Also yields tool_result chunks for streaming
|
|
64
|
+
*/
|
|
65
|
+
executeTools(doneChunk: DoneStreamChunk): AsyncGenerator<ToolResultStreamChunk, Array<ModelMessage>, void>;
|
|
66
|
+
/**
|
|
67
|
+
* Clear the tool calls map for the next iteration
|
|
68
|
+
*/
|
|
69
|
+
clear(): void;
|
|
70
|
+
}
|
|
71
|
+
export interface ToolResult {
|
|
72
|
+
toolCallId: string;
|
|
73
|
+
toolName: string;
|
|
74
|
+
result: any;
|
|
75
|
+
state?: 'output-available' | 'output-error';
|
|
76
|
+
/** Duration of tool execution in milliseconds (only for server-executed tools) */
|
|
77
|
+
duration?: number;
|
|
78
|
+
}
|
|
79
|
+
export interface ApprovalRequest {
|
|
80
|
+
toolCallId: string;
|
|
81
|
+
toolName: string;
|
|
82
|
+
input: any;
|
|
83
|
+
approvalId: string;
|
|
84
|
+
}
|
|
85
|
+
export interface ClientToolRequest {
|
|
86
|
+
toolCallId: string;
|
|
87
|
+
toolName: string;
|
|
88
|
+
input: any;
|
|
89
|
+
}
|
|
90
|
+
interface ExecuteToolCallsResult {
|
|
91
|
+
/** Tool results ready to send to LLM */
|
|
92
|
+
results: Array<ToolResult>;
|
|
93
|
+
/** Tools that need user approval before execution */
|
|
94
|
+
needsApproval: Array<ApprovalRequest>;
|
|
95
|
+
/** Tools that need client-side execution */
|
|
96
|
+
needsClientExecution: Array<ClientToolRequest>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Execute tool calls based on their configuration
|
|
100
|
+
*
|
|
101
|
+
* Handles three cases:
|
|
102
|
+
* 1. Client tools (no execute) - request client to execute
|
|
103
|
+
* 2. Server tools with approval - check approval before executing
|
|
104
|
+
* 3. Normal server tools - execute immediately
|
|
105
|
+
*
|
|
106
|
+
* @param toolCalls - Tool calls from the LLM
|
|
107
|
+
* @param tools - Available tools with their configurations
|
|
108
|
+
* @param approvals - Map of approval decisions (approval.id -> approved boolean)
|
|
109
|
+
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
110
|
+
*/
|
|
111
|
+
export declare function executeToolCalls(toolCalls: Array<ToolCall>, tools: ReadonlyArray<Tool>, approvals?: Map<string, boolean>, clientResults?: Map<string, any>): Promise<ExecuteToolCallsResult>;
|
|
112
|
+
export {};
|