@tanstack/ai 0.23.1 → 0.25.0
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/dist/esm/activities/chat/adapter.d.ts +3 -1
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/index.d.ts +33 -9
- package/dist/esm/activities/chat/index.js +19 -9
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +2 -1
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +14 -14
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +21 -21
- package/dist/esm/activities/chat/runtime-context-types.d.ts +43 -0
- package/dist/esm/activities/chat/stream/message-updaters.d.ts +2 -2
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +1 -0
- package/dist/esm/activities/chat/stream/processor.js +35 -12
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +15 -5
- package/dist/esm/activities/chat/tools/tool-calls.js +59 -19
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +12 -8
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/error-payload.d.ts +26 -0
- package/dist/esm/activities/error-payload.js +12 -1
- package/dist/esm/activities/error-payload.js.map +1 -1
- package/dist/esm/activities/generateAudio/index.js +9 -0
- package/dist/esm/activities/generateAudio/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.js +9 -0
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +1 -1
- package/dist/esm/adapter-internals.js +3 -2
- package/dist/esm/client.d.ts +1 -1
- package/dist/esm/client.js +3 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +9 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tool-registry.d.ts +7 -7
- package/dist/esm/tool-registry.js +1 -1
- package/dist/esm/tool-registry.js.map +1 -1
- package/dist/esm/types.d.ts +56 -59
- package/dist/esm/utilities/ag-ui-wire.js +1 -1
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/chat-params.d.ts +8 -3
- package/dist/esm/utilities/chat-params.js +6 -2
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/dist/esm/utilities/tool-result.d.ts +21 -0
- package/dist/esm/utilities/tool-result.js +37 -0
- package/dist/esm/utilities/tool-result.js.map +1 -0
- package/dist/esm/utilities/usage.d.ts +31 -0
- package/dist/esm/utilities/usage.js +11 -0
- package/dist/esm/utilities/usage.js.map +1 -0
- package/package.json +2 -2
- package/src/activities/chat/adapter.ts +3 -0
- package/src/activities/chat/index.ts +219 -47
- package/src/activities/chat/messages.ts +2 -1
- package/src/activities/chat/middleware/compose.ts +23 -17
- package/src/activities/chat/middleware/types.ts +21 -21
- package/src/activities/chat/runtime-context-types.ts +68 -0
- package/src/activities/chat/stream/message-updaters.ts +2 -1
- package/src/activities/chat/stream/processor.ts +48 -8
- package/src/activities/chat/tools/tool-calls.ts +138 -43
- package/src/activities/chat/tools/tool-definition.ts +25 -31
- package/src/activities/error-payload.ts +44 -0
- package/src/activities/generateAudio/index.ts +10 -0
- package/src/activities/generateSpeech/index.ts +10 -0
- package/src/adapter-internals.ts +4 -1
- package/src/client.ts +5 -1
- package/src/index.ts +10 -0
- package/src/tool-registry.ts +16 -14
- package/src/types.ts +118 -79
- package/src/utilities/ag-ui-wire.ts +4 -1
- package/src/utilities/chat-params.ts +22 -7
- package/src/utilities/tool-result.ts +60 -0
- package/src/utilities/usage.ts +41 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeToolResult } from "../../utilities/tool-result.js";
|
|
1
2
|
function isContentPart(part) {
|
|
2
3
|
return part.type === "text" || part.type === "image" || part.type === "audio" || part.type === "video" || part.type === "document";
|
|
3
4
|
}
|
|
@@ -168,7 +169,7 @@ function buildAssistantMessages(uiMessage) {
|
|
|
168
169
|
if (part.output !== void 0 && !emittedToolResultIds.has(part.id)) {
|
|
169
170
|
messageList.push({
|
|
170
171
|
role: "tool",
|
|
171
|
-
content:
|
|
172
|
+
content: normalizeToolResult(part.output),
|
|
172
173
|
toolCallId: part.id
|
|
173
174
|
});
|
|
174
175
|
emittedToolResultIds.add(part.id);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sources":["../../../../src/activities/chat/messages.ts"],"sourcesContent":["import type {\n ContentPart,\n MessagePart,\n ModelMessage,\n TextPart,\n ToolCallPart,\n UIMessage,\n} from '../../types'\n// ===========================\n// Message Converters\n// ===========================\n\n/**\n * Check if a MessagePart is a content part (text, image, audio, video, document)\n * that maps directly to a ModelMessage ContentPart.\n */\nfunction isContentPart(part: MessagePart): part is ContentPart {\n return (\n part.type === 'text' ||\n part.type === 'image' ||\n part.type === 'audio' ||\n part.type === 'video' ||\n part.type === 'document'\n )\n}\n\nfunction safeJsonStringify(value: unknown): string {\n try {\n return JSON.stringify(value)\n } catch {\n return ''\n }\n}\n\nfunction parseToolResultContent(content: string): unknown {\n try {\n return JSON.parse(content)\n } catch {\n return content\n }\n}\n\n/**\n * Collapse an array of ContentParts into the most compact ModelMessage content:\n * - Empty array → null\n * - All text parts → joined string (or null if empty)\n * - Mixed content → ContentPart array as-is\n */\nfunction collapseContentParts(\n parts: Array<ContentPart>,\n): string | null | Array<ContentPart> {\n if (parts.length === 0) return null\n\n const allText = parts.every((p) => p.type === 'text')\n if (allText) {\n const joined = parts.map((p) => p.content).join('')\n return joined || null\n }\n\n return parts\n}\n\n/**\n * Extract text content from ModelMessage content (string, null, or ContentPart array).\n * Used when only the text portion is needed (e.g., tool result content).\n */\nfunction getTextContent(content: string | null | Array<ContentPart>): string {\n if (content === null) return ''\n if (typeof content === 'string') return content\n return content\n .filter((part): part is TextPart => part.type === 'text')\n .map((part) => part.content)\n .join('')\n}\n\n/**\n * Convert UIMessages or ModelMessages to ModelMessages\n */\nexport function convertMessagesToModelMessages(\n messages: Array<UIMessage | ModelMessage>,\n): Array<ModelMessage> {\n // Pre-pass: collect toolCallIds already represented in anchor UIMessage parts.\n // Fan-out tool messages whose toolCallId matches an anchored ToolResultPart\n // are AG-UI duplicates and must be dropped to avoid double-feeding the LLM.\n const anchoredToolCallIds = new Set<string>()\n for (const msg of messages) {\n if ('parts' in msg) {\n for (const part of msg.parts) {\n if (part.type === 'tool-result') {\n anchoredToolCallIds.add(part.toolCallId)\n }\n }\n }\n }\n\n const modelMessages: Array<ModelMessage> = []\n for (const msg of messages) {\n if ('parts' in msg) {\n // UIMessage anchor — existing fan-out path\n modelMessages.push(...uiMessageToModelMessages(msg))\n continue\n }\n\n const role = (msg as { role: string }).role\n\n // AG-UI tool fan-out duplicate — drop if anchor already covers it\n if (\n role === 'tool' &&\n msg.toolCallId &&\n anchoredToolCallIds.has(msg.toolCallId)\n ) {\n continue\n }\n\n // AG-UI reasoning and activity — no ModelMessage equivalent today\n if (role === 'reasoning' || role === 'activity') {\n continue\n }\n\n // AG-UI developer — collapse to system\n if (role === 'developer') {\n modelMessages.push({\n role: 'system' as ModelMessage['role'],\n content: (msg as { content: string }).content,\n })\n continue\n }\n\n // Already a ModelMessage (user, assistant, system, tool with no anchor) — pass through\n modelMessages.push(msg)\n }\n return modelMessages\n}\n\n/**\n * Convert a UIMessage to ModelMessage(s)\n *\n * Walks the parts array IN ORDER to preserve the interleaving of text,\n * tool calls, and tool results. This is critical for multi-round tool\n * flows where the model generates text, calls a tool, gets the result,\n * then generates more text and calls another tool.\n *\n * The output preserves the sequential structure:\n * text1 → toolCall1 → toolResult1 → text2 → toolCall2 → toolResult2\n * becomes:\n * assistant: {content: \"text1\", toolCalls: [toolCall1]}\n * tool: toolResult1\n * assistant: {content: \"text2\", toolCalls: [toolCall2]}\n * tool: toolResult2\n *\n * @param uiMessage - The UIMessage to convert\n * @returns An array of ModelMessages preserving part ordering\n */\nexport function uiMessageToModelMessages(\n uiMessage: UIMessage,\n): Array<ModelMessage> {\n // Skip system messages - they're handled via systemPrompts, not ModelMessages\n if (uiMessage.role === 'system') {\n return []\n }\n\n // For non-assistant messages (user), use the simpler path since they\n // don't have tool calls or tool results to interleave\n if (uiMessage.role !== 'assistant') {\n return [buildUserOrToolMessage(uiMessage)]\n }\n\n // For assistant messages, walk parts in order to preserve interleaving\n return buildAssistantMessages(uiMessage)\n}\n\n/**\n * Build a single ModelMessage for user messages (simple path).\n * Preserves ordering of text and multimodal content parts.\n */\nfunction buildUserOrToolMessage(uiMessage: UIMessage): ModelMessage {\n const contentParts: Array<ContentPart> = []\n for (const part of uiMessage.parts) {\n if (isContentPart(part)) {\n contentParts.push(part)\n }\n }\n\n return {\n role: uiMessage.role as 'user' | 'assistant' | 'tool',\n content: collapseContentParts(contentParts),\n }\n}\n\n// Accumulator for building an assistant segment (content + tool calls)\ninterface AssistantSegment {\n contentParts: Array<ContentPart>\n toolCalls: Array<{\n id: string\n type: 'function'\n function: { name: string; arguments: string }\n /** Provider-specific metadata that round-trips with the tool call.\n * Untyped at this framework layer; adapters narrow it via their\n * `TToolCallMetadata` generic. */\n metadata?: unknown\n }>\n}\n\nfunction createSegment(): AssistantSegment {\n return { contentParts: [], toolCalls: [] }\n}\n\nfunction isToolCallIncluded(part: ToolCallPart): boolean {\n return (\n part.state === 'input-complete' ||\n part.state === 'complete' ||\n part.state === 'approval-responded' ||\n part.output !== undefined\n )\n}\n\n/**\n * Build ModelMessages for an assistant UIMessage, preserving the\n * sequential interleaving of text, tool calls, and tool results.\n *\n * Walks parts in order. Text and tool-call parts accumulate into the\n * current \"segment\". When a tool-result part is encountered, the\n * current segment is flushed as an assistant message, then the tool\n * result is emitted as a tool message.\n */\nfunction buildAssistantMessages(uiMessage: UIMessage): Array<ModelMessage> {\n const messageList: Array<ModelMessage> = []\n let current = createSegment()\n let pendingThinking: Array<{ content: string; signature?: string }> = []\n\n // Track emitted tool result IDs to avoid duplicates.\n // A tool call can have BOTH an explicit tool-result part AND an output\n // field on the tool-call part. We only want one per tool call ID.\n const emittedToolResultIds = new Set<string>()\n\n function flushSegment(): void {\n const content = collapseContentParts(current.contentParts)\n const hasContent = content !== null\n const hasToolCalls = current.toolCalls.length > 0\n\n if (hasContent || hasToolCalls) {\n messageList.push({\n role: 'assistant',\n content,\n ...(hasToolCalls && { toolCalls: current.toolCalls }),\n ...(pendingThinking.length > 0 && { thinking: pendingThinking }),\n })\n pendingThinking = []\n }\n current = createSegment()\n }\n\n for (const part of uiMessage.parts) {\n switch (part.type) {\n case 'text':\n case 'image':\n case 'audio':\n case 'video':\n case 'document':\n current.contentParts.push(part)\n break\n\n case 'tool-call':\n if (isToolCallIncluded(part)) {\n current.toolCalls.push({\n id: part.id,\n type: 'function' as const,\n function: {\n name: part.name,\n arguments: part.arguments,\n },\n ...(part.metadata !== undefined && { metadata: part.metadata }),\n })\n }\n break\n\n case 'tool-result':\n // Flush the current assistant segment before emitting the tool result\n flushSegment()\n\n // Emit the tool result\n if (\n (part.state === 'complete' || part.state === 'error') &&\n !emittedToolResultIds.has(part.toolCallId)\n ) {\n messageList.push({\n role: 'tool',\n content: part.content,\n toolCallId: part.toolCallId,\n })\n emittedToolResultIds.add(part.toolCallId)\n }\n break\n\n case 'thinking':\n if (part.content) {\n pendingThinking.push({\n content: part.content,\n ...(part.signature && { signature: part.signature }),\n })\n }\n break\n\n case 'structured-output':\n // Only emit completed structured responses into history. Streaming or\n // errored buffers would push malformed JSON into the next LLM turn's\n // assistant content. `raw` is the source of truth; `data` is the\n // defensive fallback for terminal-only completes that didn't ship raw.\n if (part.status === 'complete') {\n const serialized =\n part.raw !== ''\n ? part.raw\n : part.data !== undefined\n ? safeJsonStringify(part.data)\n : ''\n if (serialized !== '') {\n current.contentParts.push({ type: 'text', content: serialized })\n }\n }\n break\n\n default:\n break\n }\n }\n\n // Flush any remaining accumulated content\n flushSegment()\n\n // Emit tool results from client tool-call parts with output or approval,\n // but only if not already covered by an explicit tool-result part above.\n // These are appended at the end since they don't have explicit tool-result\n // parts in the parts array to trigger inline emission.\n for (const part of uiMessage.parts) {\n if (part.type !== 'tool-call') continue\n\n // Output takes priority — if the tool has already produced a result,\n // emit the concrete output regardless of approval metadata.\n if (part.output !== undefined && !emittedToolResultIds.has(part.id)) {\n messageList.push({\n role: 'tool',\n content: JSON.stringify(part.output),\n toolCallId: part.id,\n })\n emittedToolResultIds.add(part.id)\n }\n\n // Approval response without output — emit approval status for iteration tracking\n if (\n part.output === undefined &&\n part.state === 'approval-responded' &&\n part.approval?.approved !== undefined &&\n !emittedToolResultIds.has(part.id)\n ) {\n const approved = part.approval.approved\n messageList.push({\n role: 'tool',\n content: JSON.stringify({\n approved,\n ...(approved && { pendingExecution: true }),\n message: approved\n ? 'User approved this action'\n : 'User denied this action',\n }),\n toolCallId: part.id,\n })\n emittedToolResultIds.add(part.id)\n }\n }\n\n // If no messages were produced (e.g., empty parts), emit a minimal assistant message\n if (messageList.length === 0) {\n messageList.push({\n role: 'assistant',\n content: null,\n })\n }\n\n return messageList\n}\n\n/**\n * Convert a ModelMessage to UIMessage\n *\n * This conversion creates a parts-based structure:\n * - content field → TextPart\n * - toolCalls array → ToolCallPart[]\n * - role=\"tool\" messages should be converted separately and merged\n *\n * @param modelMessage - The ModelMessage to convert\n * @param id - Optional ID for the UIMessage (generated if not provided)\n * @returns A UIMessage with parts\n */\nexport function modelMessageToUIMessage(\n modelMessage: ModelMessage,\n id?: string,\n): UIMessage {\n const parts: Array<MessagePart> = []\n\n if (modelMessage.role === 'assistant' && modelMessage.thinking?.length) {\n for (const thinking of modelMessage.thinking) {\n if (!thinking.content) continue\n parts.push({\n type: 'thinking',\n content: thinking.content,\n ...(thinking.signature && { signature: thinking.signature }),\n })\n }\n }\n\n // Handle tool results (when role is \"tool\") - only produce tool-result part,\n // not a text part (the content IS the tool result, not display text)\n if (modelMessage.role === 'tool' && modelMessage.toolCallId) {\n parts.push({\n type: 'tool-result',\n toolCallId: modelMessage.toolCallId,\n content: getTextContent(modelMessage.content),\n state: 'complete',\n })\n } else if (Array.isArray(modelMessage.content)) {\n // Multimodal content - preserve all content parts as MessageParts\n for (const part of modelMessage.content) {\n parts.push(part)\n }\n } else {\n // String or null content\n const textContent = getTextContent(modelMessage.content)\n if (textContent) {\n parts.push({\n type: 'text',\n content: textContent,\n })\n }\n }\n\n // Handle tool calls\n if (modelMessage.toolCalls && modelMessage.toolCalls.length > 0) {\n for (const toolCall of modelMessage.toolCalls) {\n parts.push({\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.function.name,\n arguments: toolCall.function.arguments,\n state: 'input-complete', // Model messages have complete arguments\n ...(toolCall.metadata !== undefined && { metadata: toolCall.metadata }),\n })\n }\n }\n\n return {\n id: id || generateMessageId(),\n role: modelMessage.role === 'tool' ? 'assistant' : modelMessage.role,\n parts,\n }\n}\n\n/**\n * Convert an array of ModelMessages to UIMessages\n *\n * This handles merging tool result messages with their corresponding assistant messages\n *\n * @param modelMessages - Array of ModelMessages to convert\n * @returns Array of UIMessages\n */\nexport function modelMessagesToUIMessages(\n modelMessages: Array<ModelMessage>,\n): Array<UIMessage> {\n const uiMessages: Array<UIMessage> = []\n let currentAssistantMessage: UIMessage | null = null\n\n for (const msg of modelMessages) {\n if (msg.role === 'tool') {\n // Tool result - merge into the last assistant message if possible\n if (\n msg.toolCallId !== undefined &&\n currentAssistantMessage &&\n currentAssistantMessage.role === 'assistant'\n ) {\n const content = getTextContent(msg.content)\n const toolCallPart = currentAssistantMessage.parts.find(\n (part): part is ToolCallPart =>\n part.type === 'tool-call' && part.id === msg.toolCallId,\n )\n\n if (toolCallPart) {\n toolCallPart.output = parseToolResultContent(content)\n toolCallPart.state = 'complete'\n }\n\n currentAssistantMessage.parts.push({\n type: 'tool-result',\n toolCallId: msg.toolCallId,\n content,\n state: 'complete',\n })\n } else {\n // No assistant message to merge into, create a standalone one\n const toolResultUIMessage = modelMessageToUIMessage(msg)\n uiMessages.push(toolResultUIMessage)\n }\n } else {\n // Regular message\n const uiMessage = modelMessageToUIMessage(msg)\n uiMessages.push(uiMessage)\n\n // Track assistant messages for potential tool result merging\n if (msg.role === 'assistant') {\n currentAssistantMessage = uiMessage\n } else {\n currentAssistantMessage = null\n }\n }\n }\n\n return uiMessages\n}\n\n/**\n * Normalize a message (UIMessage or ModelMessage) to a UIMessage\n * Ensures the message has an ID and createdAt timestamp\n *\n * @param message - Either a UIMessage or ModelMessage\n * @param generateId - Function to generate a message ID if needed\n * @returns A UIMessage with guaranteed id and createdAt\n */\nexport function normalizeToUIMessage(\n message: UIMessage | ModelMessage,\n generateId: () => string,\n): UIMessage {\n if ('parts' in message) {\n // Already a UIMessage\n return {\n ...message,\n id: message.id || generateId(),\n createdAt: message.createdAt || new Date(),\n }\n } else {\n // ModelMessage - convert to UIMessage\n return {\n ...modelMessageToUIMessage(message, generateId()),\n createdAt: new Date(),\n }\n }\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `msg-${Date.now()}-${Math.random().toString(36).substring(7)}`\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"messages.js","sources":["../../../../src/activities/chat/messages.ts"],"sourcesContent":["import { normalizeToolResult } from '../../utilities/tool-result'\nimport type {\n ContentPart,\n MessagePart,\n ModelMessage,\n TextPart,\n ToolCallPart,\n UIMessage,\n} from '../../types'\n// ===========================\n// Message Converters\n// ===========================\n\n/**\n * Check if a MessagePart is a content part (text, image, audio, video, document)\n * that maps directly to a ModelMessage ContentPart.\n */\nfunction isContentPart(part: MessagePart): part is ContentPart {\n return (\n part.type === 'text' ||\n part.type === 'image' ||\n part.type === 'audio' ||\n part.type === 'video' ||\n part.type === 'document'\n )\n}\n\nfunction safeJsonStringify(value: unknown): string {\n try {\n return JSON.stringify(value)\n } catch {\n return ''\n }\n}\n\nfunction parseToolResultContent(content: string): unknown {\n try {\n return JSON.parse(content)\n } catch {\n return content\n }\n}\n\n/**\n * Collapse an array of ContentParts into the most compact ModelMessage content:\n * - Empty array → null\n * - All text parts → joined string (or null if empty)\n * - Mixed content → ContentPart array as-is\n */\nfunction collapseContentParts(\n parts: Array<ContentPart>,\n): string | null | Array<ContentPart> {\n if (parts.length === 0) return null\n\n const allText = parts.every((p) => p.type === 'text')\n if (allText) {\n const joined = parts.map((p) => p.content).join('')\n return joined || null\n }\n\n return parts\n}\n\n/**\n * Extract text content from ModelMessage content (string, null, or ContentPart array).\n * Used when only the text portion is needed (e.g., tool result content).\n */\nfunction getTextContent(content: string | null | Array<ContentPart>): string {\n if (content === null) return ''\n if (typeof content === 'string') return content\n return content\n .filter((part): part is TextPart => part.type === 'text')\n .map((part) => part.content)\n .join('')\n}\n\n/**\n * Convert UIMessages or ModelMessages to ModelMessages\n */\nexport function convertMessagesToModelMessages(\n messages: Array<UIMessage | ModelMessage>,\n): Array<ModelMessage> {\n // Pre-pass: collect toolCallIds already represented in anchor UIMessage parts.\n // Fan-out tool messages whose toolCallId matches an anchored ToolResultPart\n // are AG-UI duplicates and must be dropped to avoid double-feeding the LLM.\n const anchoredToolCallIds = new Set<string>()\n for (const msg of messages) {\n if ('parts' in msg) {\n for (const part of msg.parts) {\n if (part.type === 'tool-result') {\n anchoredToolCallIds.add(part.toolCallId)\n }\n }\n }\n }\n\n const modelMessages: Array<ModelMessage> = []\n for (const msg of messages) {\n if ('parts' in msg) {\n // UIMessage anchor — existing fan-out path\n modelMessages.push(...uiMessageToModelMessages(msg))\n continue\n }\n\n const role = (msg as { role: string }).role\n\n // AG-UI tool fan-out duplicate — drop if anchor already covers it\n if (\n role === 'tool' &&\n msg.toolCallId &&\n anchoredToolCallIds.has(msg.toolCallId)\n ) {\n continue\n }\n\n // AG-UI reasoning and activity — no ModelMessage equivalent today\n if (role === 'reasoning' || role === 'activity') {\n continue\n }\n\n // AG-UI developer — collapse to system\n if (role === 'developer') {\n modelMessages.push({\n role: 'system' as ModelMessage['role'],\n content: (msg as { content: string }).content,\n })\n continue\n }\n\n // Already a ModelMessage (user, assistant, system, tool with no anchor) — pass through\n modelMessages.push(msg)\n }\n return modelMessages\n}\n\n/**\n * Convert a UIMessage to ModelMessage(s)\n *\n * Walks the parts array IN ORDER to preserve the interleaving of text,\n * tool calls, and tool results. This is critical for multi-round tool\n * flows where the model generates text, calls a tool, gets the result,\n * then generates more text and calls another tool.\n *\n * The output preserves the sequential structure:\n * text1 → toolCall1 → toolResult1 → text2 → toolCall2 → toolResult2\n * becomes:\n * assistant: {content: \"text1\", toolCalls: [toolCall1]}\n * tool: toolResult1\n * assistant: {content: \"text2\", toolCalls: [toolCall2]}\n * tool: toolResult2\n *\n * @param uiMessage - The UIMessage to convert\n * @returns An array of ModelMessages preserving part ordering\n */\nexport function uiMessageToModelMessages(\n uiMessage: UIMessage,\n): Array<ModelMessage> {\n // Skip system messages - they're handled via systemPrompts, not ModelMessages\n if (uiMessage.role === 'system') {\n return []\n }\n\n // For non-assistant messages (user), use the simpler path since they\n // don't have tool calls or tool results to interleave\n if (uiMessage.role !== 'assistant') {\n return [buildUserOrToolMessage(uiMessage)]\n }\n\n // For assistant messages, walk parts in order to preserve interleaving\n return buildAssistantMessages(uiMessage)\n}\n\n/**\n * Build a single ModelMessage for user messages (simple path).\n * Preserves ordering of text and multimodal content parts.\n */\nfunction buildUserOrToolMessage(uiMessage: UIMessage): ModelMessage {\n const contentParts: Array<ContentPart> = []\n for (const part of uiMessage.parts) {\n if (isContentPart(part)) {\n contentParts.push(part)\n }\n }\n\n return {\n role: uiMessage.role as 'user' | 'assistant' | 'tool',\n content: collapseContentParts(contentParts),\n }\n}\n\n// Accumulator for building an assistant segment (content + tool calls)\ninterface AssistantSegment {\n contentParts: Array<ContentPart>\n toolCalls: Array<{\n id: string\n type: 'function'\n function: { name: string; arguments: string }\n /** Provider-specific metadata that round-trips with the tool call.\n * Untyped at this framework layer; adapters narrow it via their\n * `TToolCallMetadata` generic. */\n metadata?: unknown\n }>\n}\n\nfunction createSegment(): AssistantSegment {\n return { contentParts: [], toolCalls: [] }\n}\n\nfunction isToolCallIncluded(part: ToolCallPart): boolean {\n return (\n part.state === 'input-complete' ||\n part.state === 'complete' ||\n part.state === 'approval-responded' ||\n part.output !== undefined\n )\n}\n\n/**\n * Build ModelMessages for an assistant UIMessage, preserving the\n * sequential interleaving of text, tool calls, and tool results.\n *\n * Walks parts in order. Text and tool-call parts accumulate into the\n * current \"segment\". When a tool-result part is encountered, the\n * current segment is flushed as an assistant message, then the tool\n * result is emitted as a tool message.\n */\nfunction buildAssistantMessages(uiMessage: UIMessage): Array<ModelMessage> {\n const messageList: Array<ModelMessage> = []\n let current = createSegment()\n let pendingThinking: Array<{ content: string; signature?: string }> = []\n\n // Track emitted tool result IDs to avoid duplicates.\n // A tool call can have BOTH an explicit tool-result part AND an output\n // field on the tool-call part. We only want one per tool call ID.\n const emittedToolResultIds = new Set<string>()\n\n function flushSegment(): void {\n const content = collapseContentParts(current.contentParts)\n const hasContent = content !== null\n const hasToolCalls = current.toolCalls.length > 0\n\n if (hasContent || hasToolCalls) {\n messageList.push({\n role: 'assistant',\n content,\n ...(hasToolCalls && { toolCalls: current.toolCalls }),\n ...(pendingThinking.length > 0 && { thinking: pendingThinking }),\n })\n pendingThinking = []\n }\n current = createSegment()\n }\n\n for (const part of uiMessage.parts) {\n switch (part.type) {\n case 'text':\n case 'image':\n case 'audio':\n case 'video':\n case 'document':\n current.contentParts.push(part)\n break\n\n case 'tool-call':\n if (isToolCallIncluded(part)) {\n current.toolCalls.push({\n id: part.id,\n type: 'function' as const,\n function: {\n name: part.name,\n arguments: part.arguments,\n },\n ...(part.metadata !== undefined && { metadata: part.metadata }),\n })\n }\n break\n\n case 'tool-result':\n // Flush the current assistant segment before emitting the tool result\n flushSegment()\n\n // Emit the tool result\n if (\n (part.state === 'complete' || part.state === 'error') &&\n !emittedToolResultIds.has(part.toolCallId)\n ) {\n messageList.push({\n role: 'tool',\n content: part.content,\n toolCallId: part.toolCallId,\n })\n emittedToolResultIds.add(part.toolCallId)\n }\n break\n\n case 'thinking':\n if (part.content) {\n pendingThinking.push({\n content: part.content,\n ...(part.signature && { signature: part.signature }),\n })\n }\n break\n\n case 'structured-output':\n // Only emit completed structured responses into history. Streaming or\n // errored buffers would push malformed JSON into the next LLM turn's\n // assistant content. `raw` is the source of truth; `data` is the\n // defensive fallback for terminal-only completes that didn't ship raw.\n if (part.status === 'complete') {\n const serialized =\n part.raw !== ''\n ? part.raw\n : part.data !== undefined\n ? safeJsonStringify(part.data)\n : ''\n if (serialized !== '') {\n current.contentParts.push({ type: 'text', content: serialized })\n }\n }\n break\n\n default:\n break\n }\n }\n\n // Flush any remaining accumulated content\n flushSegment()\n\n // Emit tool results from client tool-call parts with output or approval,\n // but only if not already covered by an explicit tool-result part above.\n // These are appended at the end since they don't have explicit tool-result\n // parts in the parts array to trigger inline emission.\n for (const part of uiMessage.parts) {\n if (part.type !== 'tool-call') continue\n\n // Output takes priority — if the tool has already produced a result,\n // emit the concrete output regardless of approval metadata.\n if (part.output !== undefined && !emittedToolResultIds.has(part.id)) {\n messageList.push({\n role: 'tool',\n content: normalizeToolResult(part.output),\n toolCallId: part.id,\n })\n emittedToolResultIds.add(part.id)\n }\n\n // Approval response without output — emit approval status for iteration tracking\n if (\n part.output === undefined &&\n part.state === 'approval-responded' &&\n part.approval?.approved !== undefined &&\n !emittedToolResultIds.has(part.id)\n ) {\n const approved = part.approval.approved\n messageList.push({\n role: 'tool',\n content: JSON.stringify({\n approved,\n ...(approved && { pendingExecution: true }),\n message: approved\n ? 'User approved this action'\n : 'User denied this action',\n }),\n toolCallId: part.id,\n })\n emittedToolResultIds.add(part.id)\n }\n }\n\n // If no messages were produced (e.g., empty parts), emit a minimal assistant message\n if (messageList.length === 0) {\n messageList.push({\n role: 'assistant',\n content: null,\n })\n }\n\n return messageList\n}\n\n/**\n * Convert a ModelMessage to UIMessage\n *\n * This conversion creates a parts-based structure:\n * - content field → TextPart\n * - toolCalls array → ToolCallPart[]\n * - role=\"tool\" messages should be converted separately and merged\n *\n * @param modelMessage - The ModelMessage to convert\n * @param id - Optional ID for the UIMessage (generated if not provided)\n * @returns A UIMessage with parts\n */\nexport function modelMessageToUIMessage(\n modelMessage: ModelMessage,\n id?: string,\n): UIMessage {\n const parts: Array<MessagePart> = []\n\n if (modelMessage.role === 'assistant' && modelMessage.thinking?.length) {\n for (const thinking of modelMessage.thinking) {\n if (!thinking.content) continue\n parts.push({\n type: 'thinking',\n content: thinking.content,\n ...(thinking.signature && { signature: thinking.signature }),\n })\n }\n }\n\n // Handle tool results (when role is \"tool\") - only produce tool-result part,\n // not a text part (the content IS the tool result, not display text)\n if (modelMessage.role === 'tool' && modelMessage.toolCallId) {\n parts.push({\n type: 'tool-result',\n toolCallId: modelMessage.toolCallId,\n content: getTextContent(modelMessage.content),\n state: 'complete',\n })\n } else if (Array.isArray(modelMessage.content)) {\n // Multimodal content - preserve all content parts as MessageParts\n for (const part of modelMessage.content) {\n parts.push(part)\n }\n } else {\n // String or null content\n const textContent = getTextContent(modelMessage.content)\n if (textContent) {\n parts.push({\n type: 'text',\n content: textContent,\n })\n }\n }\n\n // Handle tool calls\n if (modelMessage.toolCalls && modelMessage.toolCalls.length > 0) {\n for (const toolCall of modelMessage.toolCalls) {\n parts.push({\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.function.name,\n arguments: toolCall.function.arguments,\n state: 'input-complete', // Model messages have complete arguments\n ...(toolCall.metadata !== undefined && { metadata: toolCall.metadata }),\n })\n }\n }\n\n return {\n id: id || generateMessageId(),\n role: modelMessage.role === 'tool' ? 'assistant' : modelMessage.role,\n parts,\n }\n}\n\n/**\n * Convert an array of ModelMessages to UIMessages\n *\n * This handles merging tool result messages with their corresponding assistant messages\n *\n * @param modelMessages - Array of ModelMessages to convert\n * @returns Array of UIMessages\n */\nexport function modelMessagesToUIMessages(\n modelMessages: Array<ModelMessage>,\n): Array<UIMessage> {\n const uiMessages: Array<UIMessage> = []\n let currentAssistantMessage: UIMessage | null = null\n\n for (const msg of modelMessages) {\n if (msg.role === 'tool') {\n // Tool result - merge into the last assistant message if possible\n if (\n msg.toolCallId !== undefined &&\n currentAssistantMessage &&\n currentAssistantMessage.role === 'assistant'\n ) {\n const content = getTextContent(msg.content)\n const toolCallPart = currentAssistantMessage.parts.find(\n (part): part is ToolCallPart =>\n part.type === 'tool-call' && part.id === msg.toolCallId,\n )\n\n if (toolCallPart) {\n toolCallPart.output = parseToolResultContent(content)\n toolCallPart.state = 'complete'\n }\n\n currentAssistantMessage.parts.push({\n type: 'tool-result',\n toolCallId: msg.toolCallId,\n content,\n state: 'complete',\n })\n } else {\n // No assistant message to merge into, create a standalone one\n const toolResultUIMessage = modelMessageToUIMessage(msg)\n uiMessages.push(toolResultUIMessage)\n }\n } else {\n // Regular message\n const uiMessage = modelMessageToUIMessage(msg)\n uiMessages.push(uiMessage)\n\n // Track assistant messages for potential tool result merging\n if (msg.role === 'assistant') {\n currentAssistantMessage = uiMessage\n } else {\n currentAssistantMessage = null\n }\n }\n }\n\n return uiMessages\n}\n\n/**\n * Normalize a message (UIMessage or ModelMessage) to a UIMessage\n * Ensures the message has an ID and createdAt timestamp\n *\n * @param message - Either a UIMessage or ModelMessage\n * @param generateId - Function to generate a message ID if needed\n * @returns A UIMessage with guaranteed id and createdAt\n */\nexport function normalizeToUIMessage(\n message: UIMessage | ModelMessage,\n generateId: () => string,\n): UIMessage {\n if ('parts' in message) {\n // Already a UIMessage\n return {\n ...message,\n id: message.id || generateId(),\n createdAt: message.createdAt || new Date(),\n }\n } else {\n // ModelMessage - convert to UIMessage\n return {\n ...modelMessageToUIMessage(message, generateId()),\n createdAt: new Date(),\n }\n }\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `msg-${Date.now()}-${Math.random().toString(36).substring(7)}`\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -5,17 +5,17 @@ import { AbortInfo, AfterToolCallInfo, BeforeToolCallDecision, ChatMiddleware, C
|
|
|
5
5
|
* Internal middleware runner that manages composed execution of middleware hooks.
|
|
6
6
|
* Created once per chat() invocation.
|
|
7
7
|
*/
|
|
8
|
-
export declare class MiddlewareRunner {
|
|
8
|
+
export declare class MiddlewareRunner<TContext = unknown> {
|
|
9
9
|
private readonly middlewares;
|
|
10
10
|
private readonly logger;
|
|
11
|
-
constructor(middlewares: ReadonlyArray<ChatMiddleware
|
|
11
|
+
constructor(middlewares: ReadonlyArray<ChatMiddleware<TContext>>, logger: InternalLogger);
|
|
12
12
|
get hasMiddleware(): boolean;
|
|
13
13
|
/**
|
|
14
14
|
* Pipe config through all middleware onConfig hooks in order.
|
|
15
15
|
* Each middleware receives the merged config from previous middleware.
|
|
16
16
|
* Partial returns are shallow-merged with the current config.
|
|
17
17
|
*/
|
|
18
|
-
runOnConfig(ctx: ChatMiddlewareContext
|
|
18
|
+
runOnConfig(ctx: ChatMiddlewareContext<TContext>, config: ChatMiddlewareConfig): Promise<ChatMiddlewareConfig>;
|
|
19
19
|
/**
|
|
20
20
|
* Pipe config through all middleware onStructuredOutputConfig hooks in order.
|
|
21
21
|
* Each middleware receives the merged config from previous middleware.
|
|
@@ -24,11 +24,11 @@ export declare class MiddlewareRunner {
|
|
|
24
24
|
* Called once at the structured-output boundary, before runOnConfig at the
|
|
25
25
|
* same boundary (which receives a ChatMiddlewareConfig view, no outputSchema).
|
|
26
26
|
*/
|
|
27
|
-
runOnStructuredOutputConfig(ctx: ChatMiddlewareContext
|
|
27
|
+
runOnStructuredOutputConfig(ctx: ChatMiddlewareContext<TContext>, config: StructuredOutputMiddlewareConfig): Promise<StructuredOutputMiddlewareConfig>;
|
|
28
28
|
/**
|
|
29
29
|
* Call onStart on all middleware in order.
|
|
30
30
|
*/
|
|
31
|
-
runOnStart(ctx: ChatMiddlewareContext): Promise<void>;
|
|
31
|
+
runOnStart(ctx: ChatMiddlewareContext<TContext>): Promise<void>;
|
|
32
32
|
/**
|
|
33
33
|
* Pipe a single chunk through all middleware onChunk hooks in order.
|
|
34
34
|
* Returns the resulting chunks (0..N) to yield to the consumer.
|
|
@@ -38,40 +38,40 @@ export declare class MiddlewareRunner {
|
|
|
38
38
|
* - chunk[]: expand to multiple chunks
|
|
39
39
|
* - null: drop the chunk entirely
|
|
40
40
|
*/
|
|
41
|
-
runOnChunk(ctx: ChatMiddlewareContext
|
|
41
|
+
runOnChunk(ctx: ChatMiddlewareContext<TContext>, chunk: StreamChunk): Promise<Array<StreamChunk>>;
|
|
42
42
|
/**
|
|
43
43
|
* Run onBeforeToolCall through middleware in order.
|
|
44
44
|
* Returns the first non-void decision, or undefined to continue normally.
|
|
45
45
|
*/
|
|
46
|
-
runOnBeforeToolCall(ctx: ChatMiddlewareContext
|
|
46
|
+
runOnBeforeToolCall(ctx: ChatMiddlewareContext<TContext>, hookCtx: ToolCallHookContext): Promise<BeforeToolCallDecision>;
|
|
47
47
|
/**
|
|
48
48
|
* Run onAfterToolCall on all middleware in order.
|
|
49
49
|
*/
|
|
50
|
-
runOnAfterToolCall(ctx: ChatMiddlewareContext
|
|
50
|
+
runOnAfterToolCall(ctx: ChatMiddlewareContext<TContext>, info: AfterToolCallInfo): Promise<void>;
|
|
51
51
|
/**
|
|
52
52
|
* Run onUsage on all middleware in order.
|
|
53
53
|
*/
|
|
54
|
-
runOnUsage(ctx: ChatMiddlewareContext
|
|
54
|
+
runOnUsage(ctx: ChatMiddlewareContext<TContext>, usage: UsageInfo): Promise<void>;
|
|
55
55
|
/**
|
|
56
56
|
* Run onFinish on all middleware in order.
|
|
57
57
|
*/
|
|
58
|
-
runOnFinish(ctx: ChatMiddlewareContext
|
|
58
|
+
runOnFinish(ctx: ChatMiddlewareContext<TContext>, info: FinishInfo): Promise<void>;
|
|
59
59
|
/**
|
|
60
60
|
* Run onAbort on all middleware in order.
|
|
61
61
|
*/
|
|
62
|
-
runOnAbort(ctx: ChatMiddlewareContext
|
|
62
|
+
runOnAbort(ctx: ChatMiddlewareContext<TContext>, info: AbortInfo): Promise<void>;
|
|
63
63
|
/**
|
|
64
64
|
* Run onError on all middleware in order.
|
|
65
65
|
*/
|
|
66
|
-
runOnError(ctx: ChatMiddlewareContext
|
|
66
|
+
runOnError(ctx: ChatMiddlewareContext<TContext>, info: ErrorInfo): Promise<void>;
|
|
67
67
|
/**
|
|
68
68
|
* Run onIteration on all middleware in order.
|
|
69
69
|
* Called at the start of each agent loop iteration.
|
|
70
70
|
*/
|
|
71
|
-
runOnIteration(ctx: ChatMiddlewareContext
|
|
71
|
+
runOnIteration(ctx: ChatMiddlewareContext<TContext>, info: IterationInfo): Promise<void>;
|
|
72
72
|
/**
|
|
73
73
|
* Run onToolPhaseComplete on all middleware in order.
|
|
74
74
|
* Called after all tool calls in an iteration have been processed.
|
|
75
75
|
*/
|
|
76
|
-
runOnToolPhaseComplete(ctx: ChatMiddlewareContext
|
|
76
|
+
runOnToolPhaseComplete(ctx: ChatMiddlewareContext<TContext>, info: ToolPhaseCompleteInfo): Promise<void>;
|
|
77
77
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.js","sources":["../../../../../src/activities/chat/middleware/compose.ts"],"sourcesContent":["import { aiEventClient } from '@tanstack/ai-event-client'\nimport type { StreamChunk } from '../../../types'\nimport type { InternalLogger } from '../../../logger/internal-logger'\nimport type {\n AbortInfo,\n AfterToolCallInfo,\n BeforeToolCallDecision,\n ChatMiddleware,\n ChatMiddlewareConfig,\n ChatMiddlewareContext,\n ErrorInfo,\n FinishInfo,\n IterationInfo,\n StructuredOutputMiddlewareConfig,\n ToolCallHookContext,\n ToolPhaseCompleteInfo,\n UsageInfo,\n} from './types'\n\n/** Check if a middleware should be skipped for instrumentation events. */\nfunction shouldSkipInstrumentation(mw: ChatMiddleware): boolean {\n return mw.name === 'devtools' || mw.name === 'strip-to-spec'\n}\n\n/** Build the base context for middleware instrumentation events. */\nfunction instrumentCtx(ctx: ChatMiddlewareContext) {\n return {\n requestId: ctx.requestId,\n streamId: ctx.streamId,\n clientId: ctx.threadId,\n timestamp: Date.now(),\n }\n}\n\n/**\n * Internal middleware runner that manages composed execution of middleware hooks.\n * Created once per chat() invocation.\n */\nexport class MiddlewareRunner {\n private readonly middlewares: ReadonlyArray<ChatMiddleware>\n private readonly logger: InternalLogger\n\n constructor(\n middlewares: ReadonlyArray<ChatMiddleware>,\n logger: InternalLogger,\n ) {\n this.middlewares = middlewares\n this.logger = logger\n }\n\n get hasMiddleware(): boolean {\n return this.middlewares.length > 0\n }\n\n /**\n * Pipe config through all middleware onConfig hooks in order.\n * Each middleware receives the merged config from previous middleware.\n * Partial returns are shallow-merged with the current config.\n */\n async runOnConfig(\n ctx: ChatMiddlewareContext,\n config: ChatMiddlewareConfig,\n ): Promise<ChatMiddlewareConfig> {\n let current = config\n for (const mw of this.middlewares) {\n if (mw.onConfig) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n const result = await mw.onConfig(ctx, current)\n const hasTransform = result !== undefined && result !== null\n if (hasTransform) {\n current = { ...current, ...result }\n if (!skip) {\n this.logger.config(\n `middleware=${mw.name ?? 'unnamed'} keys=${Object.keys(result).join(',')}`,\n {\n middleware: mw.name ?? 'unnamed',\n changes: result,\n },\n )\n }\n }\n if (!skip) {\n const base = instrumentCtx(ctx)\n aiEventClient.emit('middleware:hook:executed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n hookName: 'onConfig',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform,\n })\n if (hasTransform) {\n aiEventClient.emit('middleware:config:transformed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n iteration: ctx.iteration,\n changes: result,\n })\n }\n }\n }\n }\n return current\n }\n\n /**\n * Pipe config through all middleware onStructuredOutputConfig hooks in order.\n * Each middleware receives the merged config from previous middleware.\n * Partial returns are shallow-merged with the current config.\n *\n * Called once at the structured-output boundary, before runOnConfig at the\n * same boundary (which receives a ChatMiddlewareConfig view, no outputSchema).\n */\n async runOnStructuredOutputConfig(\n ctx: ChatMiddlewareContext,\n config: StructuredOutputMiddlewareConfig,\n ): Promise<StructuredOutputMiddlewareConfig> {\n let current = config\n for (const mw of this.middlewares) {\n if (mw.onStructuredOutputConfig) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n const result = await mw.onStructuredOutputConfig(ctx, current)\n const hasTransform = result !== undefined && result !== null\n if (hasTransform) {\n current = { ...current, ...result }\n if (!skip) {\n this.logger.config(\n `middleware=${mw.name ?? 'unnamed'} keys=${Object.keys(result).join(',')}`,\n {\n middleware: mw.name ?? 'unnamed',\n changes: result,\n },\n )\n }\n }\n if (!skip) {\n const base = instrumentCtx(ctx)\n aiEventClient.emit('middleware:hook:executed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n hookName: 'onStructuredOutputConfig',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform,\n })\n if (hasTransform) {\n aiEventClient.emit('middleware:config:transformed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n iteration: ctx.iteration,\n // `result` is `Partial<StructuredOutputMiddlewareConfig>` —\n // Object.fromEntries(Object.entries(result)) yields the\n // structural `Record<string, unknown>` the event emitter wants\n // without an `as` cast.\n changes: Object.fromEntries(Object.entries(result)),\n })\n }\n }\n }\n }\n return current\n }\n\n /**\n * Call onStart on all middleware in order.\n */\n async runOnStart(ctx: ChatMiddlewareContext): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onStart) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onStart(ctx)\n if (!skip) {\n this.logger.middleware(\n `hook=onStart middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onStart' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onStart',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Pipe a single chunk through all middleware onChunk hooks in order.\n * Returns the resulting chunks (0..N) to yield to the consumer.\n *\n * - void: pass through unchanged\n * - chunk: replace with this chunk\n * - chunk[]: expand to multiple chunks\n * - null: drop the chunk entirely\n */\n async runOnChunk(\n ctx: ChatMiddlewareContext,\n chunk: StreamChunk,\n ): Promise<Array<StreamChunk>> {\n let chunks: Array<StreamChunk> = [chunk]\n\n for (const mw of this.middlewares) {\n if (!mw.onChunk) continue\n const skip = shouldSkipInstrumentation(mw)\n\n const nextChunks: Array<StreamChunk> = []\n for (const c of chunks) {\n // Cast: @ag-ui/core Zod passthrough types prevent direct `.type` access\n const chunkType = c.type\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onChunk', in: c },\n )\n }\n const result = await mw.onChunk(ctx, c)\n if (result === null) {\n // Drop this chunk\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=<dropped>`,\n {\n middleware: mw.name ?? 'unnamed',\n hook: 'onChunk',\n dropped: true,\n },\n )\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: 0,\n wasDropped: true,\n })\n }\n continue\n } else if (result === undefined) {\n // Pass through — no instrumentation for pass-throughs\n nextChunks.push(c)\n } else if (Array.isArray(result)) {\n // Expand\n nextChunks.push(...result)\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=[${result.map((r: StreamChunk) => r.type).join(',')}]`,\n {\n middleware: mw.name ?? 'unnamed',\n hook: 'onChunk',\n in: c,\n out: result,\n },\n )\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: result.length,\n wasDropped: false,\n })\n }\n } else {\n // Replace\n nextChunks.push(result)\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=${result.type}`,\n {\n middleware: mw.name ?? 'unnamed',\n hook: 'onChunk',\n in: c,\n out: result,\n },\n )\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: 1,\n wasDropped: false,\n })\n }\n }\n }\n chunks = nextChunks\n }\n\n return chunks\n }\n\n /**\n * Run onBeforeToolCall through middleware in order.\n * Returns the first non-void decision, or undefined to continue normally.\n */\n async runOnBeforeToolCall(\n ctx: ChatMiddlewareContext,\n hookCtx: ToolCallHookContext,\n ): Promise<BeforeToolCallDecision> {\n for (const mw of this.middlewares) {\n if (mw.onBeforeToolCall) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n const decision = await mw.onBeforeToolCall(ctx, hookCtx)\n const hasTransform = decision !== undefined && decision !== null\n if (!skip) {\n this.logger.middleware(\n `hook=onBeforeToolCall middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onBeforeToolCall' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onBeforeToolCall',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform,\n })\n }\n if (hasTransform) {\n return decision\n }\n }\n }\n return undefined\n }\n\n /**\n * Run onAfterToolCall on all middleware in order.\n */\n async runOnAfterToolCall(\n ctx: ChatMiddlewareContext,\n info: AfterToolCallInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onAfterToolCall) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onAfterToolCall(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onAfterToolCall middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onAfterToolCall' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onAfterToolCall',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onUsage on all middleware in order.\n */\n async runOnUsage(\n ctx: ChatMiddlewareContext,\n usage: UsageInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onUsage) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onUsage(ctx, usage)\n if (!skip) {\n this.logger.middleware(\n `hook=onUsage middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onUsage' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onUsage',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onFinish on all middleware in order.\n */\n async runOnFinish(\n ctx: ChatMiddlewareContext,\n info: FinishInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onFinish) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onFinish(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onFinish middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onFinish' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onFinish',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onAbort on all middleware in order.\n */\n async runOnAbort(ctx: ChatMiddlewareContext, info: AbortInfo): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onAbort) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onAbort(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onAbort middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onAbort' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onAbort',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onError on all middleware in order.\n */\n async runOnError(ctx: ChatMiddlewareContext, info: ErrorInfo): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onError) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onError(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onError middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onError' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onError',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onIteration on all middleware in order.\n * Called at the start of each agent loop iteration.\n */\n async runOnIteration(\n ctx: ChatMiddlewareContext,\n info: IterationInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onIteration) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onIteration(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onIteration middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onIteration' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onIteration',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onToolPhaseComplete on all middleware in order.\n * Called after all tool calls in an iteration have been processed.\n */\n async runOnToolPhaseComplete(\n ctx: ChatMiddlewareContext,\n info: ToolPhaseCompleteInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onToolPhaseComplete) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onToolPhaseComplete(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onToolPhaseComplete middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onToolPhaseComplete' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onToolPhaseComplete',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"compose.js","sources":["../../../../../src/activities/chat/middleware/compose.ts"],"sourcesContent":["import { aiEventClient } from '@tanstack/ai-event-client'\nimport type { StreamChunk } from '../../../types'\nimport type { InternalLogger } from '../../../logger/internal-logger'\nimport type {\n AbortInfo,\n AfterToolCallInfo,\n BeforeToolCallDecision,\n ChatMiddleware,\n ChatMiddlewareConfig,\n ChatMiddlewareContext,\n ErrorInfo,\n FinishInfo,\n IterationInfo,\n StructuredOutputMiddlewareConfig,\n ToolCallHookContext,\n ToolPhaseCompleteInfo,\n UsageInfo,\n} from './types'\n\n/** Check if a middleware should be skipped for instrumentation events. */\nfunction shouldSkipInstrumentation(mw: ChatMiddleware<any>): boolean {\n return mw.name === 'devtools' || mw.name === 'strip-to-spec'\n}\n\n/** Build the base context for middleware instrumentation events. */\nfunction instrumentCtx(ctx: ChatMiddlewareContext<any>) {\n return {\n requestId: ctx.requestId,\n streamId: ctx.streamId,\n clientId: ctx.threadId,\n timestamp: Date.now(),\n }\n}\n\n/**\n * Internal middleware runner that manages composed execution of middleware hooks.\n * Created once per chat() invocation.\n */\nexport class MiddlewareRunner<TContext = unknown> {\n private readonly middlewares: ReadonlyArray<ChatMiddleware<TContext>>\n private readonly logger: InternalLogger\n\n constructor(\n middlewares: ReadonlyArray<ChatMiddleware<TContext>>,\n logger: InternalLogger,\n ) {\n this.middlewares = middlewares\n this.logger = logger\n }\n\n get hasMiddleware(): boolean {\n return this.middlewares.length > 0\n }\n\n /**\n * Pipe config through all middleware onConfig hooks in order.\n * Each middleware receives the merged config from previous middleware.\n * Partial returns are shallow-merged with the current config.\n */\n async runOnConfig(\n ctx: ChatMiddlewareContext<TContext>,\n config: ChatMiddlewareConfig,\n ): Promise<ChatMiddlewareConfig> {\n let current = config\n for (const mw of this.middlewares) {\n if (mw.onConfig) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n const result = await mw.onConfig(ctx, current)\n const hasTransform = result !== undefined && result !== null\n if (hasTransform) {\n current = { ...current, ...result }\n if (!skip) {\n this.logger.config(\n `middleware=${mw.name ?? 'unnamed'} keys=${Object.keys(result).join(',')}`,\n {\n middleware: mw.name ?? 'unnamed',\n changes: result,\n },\n )\n }\n }\n if (!skip) {\n const base = instrumentCtx(ctx)\n aiEventClient.emit('middleware:hook:executed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n hookName: 'onConfig',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform,\n })\n if (hasTransform) {\n aiEventClient.emit('middleware:config:transformed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n iteration: ctx.iteration,\n changes: result,\n })\n }\n }\n }\n }\n return current\n }\n\n /**\n * Pipe config through all middleware onStructuredOutputConfig hooks in order.\n * Each middleware receives the merged config from previous middleware.\n * Partial returns are shallow-merged with the current config.\n *\n * Called once at the structured-output boundary, before runOnConfig at the\n * same boundary (which receives a ChatMiddlewareConfig view, no outputSchema).\n */\n async runOnStructuredOutputConfig(\n ctx: ChatMiddlewareContext<TContext>,\n config: StructuredOutputMiddlewareConfig,\n ): Promise<StructuredOutputMiddlewareConfig> {\n let current = config\n for (const mw of this.middlewares) {\n if (mw.onStructuredOutputConfig) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n const result = await mw.onStructuredOutputConfig(ctx, current)\n const hasTransform = result !== undefined && result !== null\n if (hasTransform) {\n current = { ...current, ...result }\n if (!skip) {\n this.logger.config(\n `middleware=${mw.name ?? 'unnamed'} keys=${Object.keys(result).join(',')}`,\n {\n middleware: mw.name ?? 'unnamed',\n changes: result,\n },\n )\n }\n }\n if (!skip) {\n const base = instrumentCtx(ctx)\n aiEventClient.emit('middleware:hook:executed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n hookName: 'onStructuredOutputConfig',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform,\n })\n if (hasTransform) {\n aiEventClient.emit('middleware:config:transformed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n iteration: ctx.iteration,\n // `result` is `Partial<StructuredOutputMiddlewareConfig>` —\n // Object.fromEntries(Object.entries(result)) yields the\n // structural `Record<string, unknown>` the event emitter wants\n // without an `as` cast.\n changes: Object.fromEntries(Object.entries(result)),\n })\n }\n }\n }\n }\n return current\n }\n\n /**\n * Call onStart on all middleware in order.\n */\n async runOnStart(ctx: ChatMiddlewareContext<TContext>): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onStart) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onStart(ctx)\n if (!skip) {\n this.logger.middleware(\n `hook=onStart middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onStart' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onStart',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Pipe a single chunk through all middleware onChunk hooks in order.\n * Returns the resulting chunks (0..N) to yield to the consumer.\n *\n * - void: pass through unchanged\n * - chunk: replace with this chunk\n * - chunk[]: expand to multiple chunks\n * - null: drop the chunk entirely\n */\n async runOnChunk(\n ctx: ChatMiddlewareContext<TContext>,\n chunk: StreamChunk,\n ): Promise<Array<StreamChunk>> {\n let chunks: Array<StreamChunk> = [chunk]\n\n for (const mw of this.middlewares) {\n if (!mw.onChunk) continue\n const skip = shouldSkipInstrumentation(mw)\n\n const nextChunks: Array<StreamChunk> = []\n for (const c of chunks) {\n // Cast: @ag-ui/core Zod passthrough types prevent direct `.type` access\n const chunkType = c.type\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onChunk', in: c },\n )\n }\n const result = await mw.onChunk(ctx, c)\n if (result === null) {\n // Drop this chunk\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=<dropped>`,\n {\n middleware: mw.name ?? 'unnamed',\n hook: 'onChunk',\n dropped: true,\n },\n )\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: 0,\n wasDropped: true,\n })\n }\n continue\n } else if (result === undefined) {\n // Pass through — no instrumentation for pass-throughs\n nextChunks.push(c)\n } else if (Array.isArray(result)) {\n // Expand\n nextChunks.push(...result)\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=[${result.map((r: StreamChunk) => r.type).join(',')}]`,\n {\n middleware: mw.name ?? 'unnamed',\n hook: 'onChunk',\n in: c,\n out: result,\n },\n )\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: result.length,\n wasDropped: false,\n })\n }\n } else {\n // Replace\n nextChunks.push(result)\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=${result.type}`,\n {\n middleware: mw.name ?? 'unnamed',\n hook: 'onChunk',\n in: c,\n out: result,\n },\n )\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: 1,\n wasDropped: false,\n })\n }\n }\n }\n chunks = nextChunks\n }\n\n return chunks\n }\n\n /**\n * Run onBeforeToolCall through middleware in order.\n * Returns the first non-void decision, or undefined to continue normally.\n */\n async runOnBeforeToolCall(\n ctx: ChatMiddlewareContext<TContext>,\n hookCtx: ToolCallHookContext,\n ): Promise<BeforeToolCallDecision> {\n for (const mw of this.middlewares) {\n if (mw.onBeforeToolCall) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n const decision = await mw.onBeforeToolCall(ctx, hookCtx)\n const hasTransform = decision !== undefined && decision !== null\n if (!skip) {\n this.logger.middleware(\n `hook=onBeforeToolCall middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onBeforeToolCall' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onBeforeToolCall',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform,\n })\n }\n if (hasTransform) {\n return decision\n }\n }\n }\n return undefined\n }\n\n /**\n * Run onAfterToolCall on all middleware in order.\n */\n async runOnAfterToolCall(\n ctx: ChatMiddlewareContext<TContext>,\n info: AfterToolCallInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onAfterToolCall) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onAfterToolCall(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onAfterToolCall middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onAfterToolCall' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onAfterToolCall',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onUsage on all middleware in order.\n */\n async runOnUsage(\n ctx: ChatMiddlewareContext<TContext>,\n usage: UsageInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onUsage) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onUsage(ctx, usage)\n if (!skip) {\n this.logger.middleware(\n `hook=onUsage middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onUsage' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onUsage',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onFinish on all middleware in order.\n */\n async runOnFinish(\n ctx: ChatMiddlewareContext<TContext>,\n info: FinishInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onFinish) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onFinish(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onFinish middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onFinish' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onFinish',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onAbort on all middleware in order.\n */\n async runOnAbort(\n ctx: ChatMiddlewareContext<TContext>,\n info: AbortInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onAbort) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onAbort(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onAbort middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onAbort' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onAbort',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onError on all middleware in order.\n */\n async runOnError(\n ctx: ChatMiddlewareContext<TContext>,\n info: ErrorInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onError) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onError(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onError middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onError' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onError',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onIteration on all middleware in order.\n * Called at the start of each agent loop iteration.\n */\n async runOnIteration(\n ctx: ChatMiddlewareContext<TContext>,\n info: IterationInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onIteration) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onIteration(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onIteration middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onIteration' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onIteration',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onToolPhaseComplete on all middleware in order.\n * Called after all tool calls in an iteration have been processed.\n */\n async runOnToolPhaseComplete(\n ctx: ChatMiddlewareContext<TContext>,\n info: ToolPhaseCompleteInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onToolPhaseComplete) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onToolPhaseComplete(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onToolPhaseComplete middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onToolPhaseComplete' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onToolPhaseComplete',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JSONSchema, ModelMessage, StreamChunk, Tool, ToolCall
|
|
1
|
+
import { JSONSchema, ModelMessage, StreamChunk, TokenUsage, Tool, ToolCall } from '../../../types.js';
|
|
2
2
|
import { SystemPrompt } from '../../../system-prompts.js';
|
|
3
3
|
/**
|
|
4
4
|
* Phase of the chat middleware lifecycle.
|
|
@@ -15,7 +15,7 @@ export type ChatMiddlewarePhase = 'init' | 'beforeModel' | 'modelStream' | 'befo
|
|
|
15
15
|
* Stable context object passed to all middleware hooks.
|
|
16
16
|
* Created once per chat() invocation and shared across all hooks.
|
|
17
17
|
*/
|
|
18
|
-
export interface ChatMiddlewareContext {
|
|
18
|
+
export interface ChatMiddlewareContext<TContext = unknown> {
|
|
19
19
|
/** Unique identifier for this chat request */
|
|
20
20
|
requestId: string;
|
|
21
21
|
/** Unique identifier for this stream */
|
|
@@ -45,8 +45,8 @@ export interface ChatMiddlewareContext {
|
|
|
45
45
|
signal?: AbortSignal;
|
|
46
46
|
/** Abort the chat run with a reason */
|
|
47
47
|
abort: (reason?: string) => void;
|
|
48
|
-
/**
|
|
49
|
-
context:
|
|
48
|
+
/** Runtime context provided by chat() options */
|
|
49
|
+
context: TContext;
|
|
50
50
|
/**
|
|
51
51
|
* Defer a non-blocking side-effect promise.
|
|
52
52
|
* Deferred promises do not block streaming and are awaited
|
|
@@ -204,11 +204,11 @@ export interface ToolPhaseCompleteInfo {
|
|
|
204
204
|
* Token usage statistics passed to the onUsage hook.
|
|
205
205
|
* Extracted from the RUN_FINISHED chunk when usage data is present.
|
|
206
206
|
*
|
|
207
|
-
* Includes optional provider-reported `cost`/`costDetails` (see {@link
|
|
208
|
-
* Kept as an interface extending `
|
|
207
|
+
* Includes optional provider-reported `cost`/`costDetails` (see {@link TokenUsage}).
|
|
208
|
+
* Kept as an interface extending `TokenUsage` to preserve declaration merging for
|
|
209
209
|
* this publicly exported type.
|
|
210
210
|
*/
|
|
211
|
-
export interface UsageInfo extends
|
|
211
|
+
export interface UsageInfo extends TokenUsage {
|
|
212
212
|
}
|
|
213
213
|
/**
|
|
214
214
|
* Information passed to onFinish.
|
|
@@ -221,7 +221,7 @@ export interface FinishInfo {
|
|
|
221
221
|
/** Final accumulated text content */
|
|
222
222
|
content: string;
|
|
223
223
|
/** Final usage totals, if available (optionally including provider-reported cost) */
|
|
224
|
-
usage?:
|
|
224
|
+
usage?: TokenUsage | undefined;
|
|
225
225
|
}
|
|
226
226
|
/**
|
|
227
227
|
* Information passed to onAbort.
|
|
@@ -270,7 +270,7 @@ export interface ErrorInfo {
|
|
|
270
270
|
* }
|
|
271
271
|
* ```
|
|
272
272
|
*/
|
|
273
|
-
export interface ChatMiddleware {
|
|
273
|
+
export interface ChatMiddleware<TContext = unknown> {
|
|
274
274
|
/** Optional name for debugging and identification */
|
|
275
275
|
name?: string;
|
|
276
276
|
/**
|
|
@@ -280,7 +280,7 @@ export interface ChatMiddleware {
|
|
|
280
280
|
* Return a partial config to merge with the current config, or void to pass through.
|
|
281
281
|
* Only the fields you return are overwritten — everything else is preserved.
|
|
282
282
|
*/
|
|
283
|
-
onConfig?: (ctx: ChatMiddlewareContext
|
|
283
|
+
onConfig?: (ctx: ChatMiddlewareContext<TContext>, config: ChatMiddlewareConfig) => void | null | Partial<ChatMiddlewareConfig> | Promise<void | null | Partial<ChatMiddlewareConfig>>;
|
|
284
284
|
/**
|
|
285
285
|
* Called at the start of the final structured-output call (when the chat
|
|
286
286
|
* was invoked with outputSchema). Pipes through middleware in order, like
|
|
@@ -296,55 +296,55 @@ export interface ChatMiddleware {
|
|
|
296
296
|
* to every adapter call; use this hook when you need to transform the
|
|
297
297
|
* outputSchema or apply structured-output-specific behavior.
|
|
298
298
|
*/
|
|
299
|
-
onStructuredOutputConfig?: (ctx: ChatMiddlewareContext
|
|
299
|
+
onStructuredOutputConfig?: (ctx: ChatMiddlewareContext<TContext>, config: StructuredOutputMiddlewareConfig) => void | null | Partial<StructuredOutputMiddlewareConfig> | Promise<void | null | Partial<StructuredOutputMiddlewareConfig>>;
|
|
300
300
|
/**
|
|
301
301
|
* Called when the chat run starts (after initial onConfig).
|
|
302
302
|
*/
|
|
303
|
-
onStart?: (ctx: ChatMiddlewareContext) => void | Promise<void>;
|
|
303
|
+
onStart?: (ctx: ChatMiddlewareContext<TContext>) => void | Promise<void>;
|
|
304
304
|
/**
|
|
305
305
|
* Called at the start of each agent loop iteration, after a new assistant message ID
|
|
306
306
|
* is created. Use this to observe iteration boundaries.
|
|
307
307
|
*/
|
|
308
|
-
onIteration?: (ctx: ChatMiddlewareContext
|
|
308
|
+
onIteration?: (ctx: ChatMiddlewareContext<TContext>, info: IterationInfo) => void | Promise<void>;
|
|
309
309
|
/**
|
|
310
310
|
* Called for every chunk yielded by chat().
|
|
311
311
|
* Can observe, transform, expand, or drop chunks.
|
|
312
312
|
*
|
|
313
313
|
* @returns void (pass through), chunk (replace), chunk[] (expand), null (drop)
|
|
314
314
|
*/
|
|
315
|
-
onChunk?: (ctx: ChatMiddlewareContext
|
|
315
|
+
onChunk?: (ctx: ChatMiddlewareContext<TContext>, chunk: StreamChunk) => void | StreamChunk | Array<StreamChunk> | null | Promise<void | StreamChunk | Array<StreamChunk> | null>;
|
|
316
316
|
/**
|
|
317
317
|
* Called before a tool is executed.
|
|
318
318
|
* Can observe, transform args, skip execution, or abort the run.
|
|
319
319
|
*/
|
|
320
|
-
onBeforeToolCall?: (ctx: ChatMiddlewareContext
|
|
320
|
+
onBeforeToolCall?: (ctx: ChatMiddlewareContext<TContext>, hookCtx: ToolCallHookContext) => BeforeToolCallDecision | Promise<BeforeToolCallDecision>;
|
|
321
321
|
/**
|
|
322
322
|
* Called after a tool execution completes (success or failure).
|
|
323
323
|
*/
|
|
324
|
-
onAfterToolCall?: (ctx: ChatMiddlewareContext
|
|
324
|
+
onAfterToolCall?: (ctx: ChatMiddlewareContext<TContext>, info: AfterToolCallInfo) => void | Promise<void>;
|
|
325
325
|
/**
|
|
326
326
|
* Called after all tool calls in an iteration have been processed.
|
|
327
327
|
* Provides aggregate data about tool execution results, approvals, and client tools.
|
|
328
328
|
*/
|
|
329
|
-
onToolPhaseComplete?: (ctx: ChatMiddlewareContext
|
|
329
|
+
onToolPhaseComplete?: (ctx: ChatMiddlewareContext<TContext>, info: ToolPhaseCompleteInfo) => void | Promise<void>;
|
|
330
330
|
/**
|
|
331
331
|
* Called when usage data is available from a RUN_FINISHED chunk.
|
|
332
332
|
* Called once per model iteration that reports usage.
|
|
333
333
|
*/
|
|
334
|
-
onUsage?: (ctx: ChatMiddlewareContext
|
|
334
|
+
onUsage?: (ctx: ChatMiddlewareContext<TContext>, usage: UsageInfo) => void | Promise<void>;
|
|
335
335
|
/**
|
|
336
336
|
* Called when the chat run completes normally.
|
|
337
337
|
* Exactly one of onFinish/onAbort/onError will be called per run.
|
|
338
338
|
*/
|
|
339
|
-
onFinish?: (ctx: ChatMiddlewareContext
|
|
339
|
+
onFinish?: (ctx: ChatMiddlewareContext<TContext>, info: FinishInfo) => void | Promise<void>;
|
|
340
340
|
/**
|
|
341
341
|
* Called when the chat run is aborted.
|
|
342
342
|
* Exactly one of onFinish/onAbort/onError will be called per run.
|
|
343
343
|
*/
|
|
344
|
-
onAbort?: (ctx: ChatMiddlewareContext
|
|
344
|
+
onAbort?: (ctx: ChatMiddlewareContext<TContext>, info: AbortInfo) => void | Promise<void>;
|
|
345
345
|
/**
|
|
346
346
|
* Called when the chat run encounters an unhandled error.
|
|
347
347
|
* Exactly one of onFinish/onAbort/onError will be called per run.
|
|
348
348
|
*/
|
|
349
|
-
onError?: (ctx: ChatMiddlewareContext
|
|
349
|
+
onError?: (ctx: ChatMiddlewareContext<TContext>, info: ErrorInfo) => void | Promise<void>;
|
|
350
350
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ChatMiddleware } from './middleware/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shared type-level helpers for inferring the runtime `context` requirement
|
|
4
|
+
* from typed tools and middleware.
|
|
5
|
+
*
|
|
6
|
+
* These primitives are consumed by both the chat activity options
|
|
7
|
+
* (`./index.ts`, which merges tool + middleware requirements) and the tool
|
|
8
|
+
* execution layer (`./tools/tool-calls.ts`, which only sees tools). They live
|
|
9
|
+
* here so the two call sites share one definition instead of maintaining
|
|
10
|
+
* divergent copies.
|
|
11
|
+
*/
|
|
12
|
+
/** True only when `T` is exactly `unknown`. */
|
|
13
|
+
type IsUnknown<T> = unknown extends T ? [T] extends [unknown] ? true : false : false;
|
|
14
|
+
/**
|
|
15
|
+
* Drops an `unknown` context requirement to `never` so that untyped tools and
|
|
16
|
+
* middleware (which default `TContext` to `unknown`) contribute no requirement
|
|
17
|
+
* to the merged context.
|
|
18
|
+
*/
|
|
19
|
+
type KnownContext<T> = IsUnknown<T> extends true ? never : T;
|
|
20
|
+
/**
|
|
21
|
+
* Merge two inferred context requirements, treating `never` as "no
|
|
22
|
+
* requirement". Using this instead of a raw intersection keeps a `never`
|
|
23
|
+
* (untyped) contributor from collapsing the whole merge to `never`.
|
|
24
|
+
*/
|
|
25
|
+
export type MergeContext<TLeft, TRight> = [TLeft] extends [never] ? TRight : [TRight] extends [never] ? TLeft : TLeft & TRight;
|
|
26
|
+
/** Collapse a union of context requirements into their intersection. */
|
|
27
|
+
export type UnionToIntersection<T> = [T] extends [never] ? never : (T extends unknown ? (value: T) => void : never) extends (value: infer TIntersection) => void ? TIntersection : never;
|
|
28
|
+
/** Strip `undefined` from a context requirement. */
|
|
29
|
+
export type DefinedContext<T> = Exclude<T, undefined>;
|
|
30
|
+
/**
|
|
31
|
+
* Extract the `context` requirement declared by a tool execute function's
|
|
32
|
+
* second argument, dropping `unknown` (untyped) contexts to `never`.
|
|
33
|
+
*/
|
|
34
|
+
type ContextFromExecute<T> = T extends (...args: any) => any ? NonNullable<Parameters<T>[1]> extends {
|
|
35
|
+
context: infer TUserContext;
|
|
36
|
+
} ? KnownContext<TUserContext> : never : never;
|
|
37
|
+
/** Extract the context requirement declared by a single tool. */
|
|
38
|
+
export type ContextFromTool<T> = T extends {
|
|
39
|
+
execute?: infer TExecute;
|
|
40
|
+
} ? ContextFromExecute<TExecute> : never;
|
|
41
|
+
/** Extract the context requirement declared by a single middleware. */
|
|
42
|
+
export type ContextFromMiddleware<T> = T extends ChatMiddleware<infer TContext> ? KnownContext<TContext> : never;
|
|
43
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UIMessage } from '../../../types.js';
|
|
1
|
+
import { ContentPart, UIMessage } from '../../../types.js';
|
|
2
2
|
import { ToolCallState, ToolResultState } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Update or add a text part to a message.
|
|
@@ -20,7 +20,7 @@ export declare function updateToolCallPart(messages: Array<UIMessage>, messageId
|
|
|
20
20
|
/**
|
|
21
21
|
* Update or add a tool result part to a message.
|
|
22
22
|
*/
|
|
23
|
-
export declare function updateToolResultPart(messages: Array<UIMessage>, messageId: string, toolCallId: string, content: string
|
|
23
|
+
export declare function updateToolResultPart(messages: Array<UIMessage>, messageId: string, toolCallId: string, content: string | Array<ContentPart>, state: ToolResultState, error?: string): Array<UIMessage>;
|
|
24
24
|
/**
|
|
25
25
|
* Update a tool call part with approval request metadata.
|
|
26
26
|
*/
|