@tanstack/ai 0.0.3 → 0.2.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/README.md +26 -0
- package/dist/esm/activities/chat/adapter.d.ts +100 -0
- package/dist/esm/activities/chat/adapter.js +14 -0
- package/dist/esm/activities/chat/adapter.js.map +1 -0
- package/dist/esm/{utilities → activities/chat}/agent-loop-strategies.d.ts +4 -4
- package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -0
- package/dist/esm/activities/chat/index.d.ts +166 -0
- package/dist/esm/{core/chat.js → activities/chat/index.js} +131 -33
- package/dist/esm/activities/chat/index.js.map +1 -0
- package/dist/esm/{message-converters.d.ts → activities/chat/messages.d.ts} +1 -1
- package/dist/esm/{message-converters.js → activities/chat/messages.js} +7 -7
- package/dist/esm/activities/chat/messages.js.map +1 -0
- package/dist/esm/activities/chat/stream/json-parser.js.map +1 -0
- package/dist/esm/{stream → activities/chat/stream}/message-updaters.d.ts +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -0
- package/dist/esm/{stream → activities/chat/stream}/processor.d.ts +1 -1
- package/dist/esm/{stream → activities/chat/stream}/processor.js +1 -1
- package/dist/esm/activities/chat/stream/processor.js.map +1 -0
- package/dist/esm/activities/chat/stream/strategies.js.map +1 -0
- package/dist/esm/{stream → activities/chat/stream}/types.d.ts +2 -9
- package/dist/esm/activities/chat/tools/schema-converter.d.ts +116 -0
- package/dist/esm/activities/chat/tools/schema-converter.js +115 -0
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -0
- package/dist/esm/{tools → activities/chat/tools}/tool-calls.d.ts +1 -1
- package/dist/esm/{tools → activities/chat/tools}/tool-calls.js +23 -30
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -0
- package/dist/esm/{tools → activities/chat/tools}/tool-definition.d.ts +22 -18
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -0
- package/dist/esm/activities/generateImage/adapter.d.ts +68 -0
- package/dist/esm/activities/generateImage/adapter.js +14 -0
- package/dist/esm/activities/generateImage/adapter.js.map +1 -0
- package/dist/esm/activities/generateImage/index.d.ts +89 -0
- package/dist/esm/activities/generateImage/index.js +15 -0
- package/dist/esm/activities/generateImage/index.js.map +1 -0
- package/dist/esm/activities/generateSpeech/adapter.d.ts +62 -0
- package/dist/esm/activities/generateSpeech/adapter.js +14 -0
- package/dist/esm/activities/generateSpeech/adapter.js.map +1 -0
- package/dist/esm/activities/generateSpeech/index.d.ts +69 -0
- package/dist/esm/activities/generateSpeech/index.js +15 -0
- package/dist/esm/activities/generateSpeech/index.js.map +1 -0
- package/dist/esm/activities/generateTranscription/adapter.d.ts +62 -0
- package/dist/esm/activities/generateTranscription/adapter.js +14 -0
- package/dist/esm/activities/generateTranscription/adapter.js.map +1 -0
- package/dist/esm/activities/generateTranscription/index.d.ts +71 -0
- package/dist/esm/activities/generateTranscription/index.js +15 -0
- package/dist/esm/activities/generateTranscription/index.js.map +1 -0
- package/dist/esm/activities/generateVideo/adapter.d.ts +80 -0
- package/dist/esm/activities/generateVideo/adapter.js +14 -0
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -0
- package/dist/esm/activities/generateVideo/index.d.ts +136 -0
- package/dist/esm/activities/generateVideo/index.js +47 -0
- package/dist/esm/activities/generateVideo/index.js.map +1 -0
- package/dist/esm/activities/index.d.ts +22 -0
- package/dist/esm/activities/index.js +34 -0
- package/dist/esm/activities/index.js.map +1 -0
- package/dist/esm/activities/summarize/adapter.d.ts +74 -0
- package/dist/esm/activities/summarize/adapter.js +14 -0
- package/dist/esm/activities/summarize/adapter.js.map +1 -0
- package/dist/esm/activities/summarize/index.d.ts +100 -0
- package/dist/esm/activities/summarize/index.js +90 -0
- package/dist/esm/activities/summarize/index.js.map +1 -0
- package/dist/esm/event-client.d.ts +4 -18
- package/dist/esm/event-client.js.map +1 -1
- package/dist/esm/index.d.ts +16 -14
- package/dist/esm/index.js +29 -20
- package/dist/esm/stream-to-response.d.ts +95 -0
- package/dist/esm/stream-to-response.js +118 -0
- package/dist/esm/stream-to-response.js.map +1 -0
- package/dist/esm/types.d.ts +370 -133
- package/package.json +7 -6
- package/src/activities/chat/adapter.ts +150 -0
- package/src/{utilities → activities/chat}/agent-loop-strategies.ts +4 -4
- package/src/{core/chat.ts → activities/chat/index.ts} +435 -79
- package/src/{message-converters.ts → activities/chat/messages.ts} +10 -13
- package/src/{stream → activities/chat/stream}/message-updaters.ts +1 -1
- package/src/{stream → activities/chat/stream}/processor.ts +2 -5
- package/src/{stream → activities/chat/stream}/types.ts +8 -18
- package/src/activities/chat/tools/schema-converter.ts +332 -0
- package/src/{tools → activities/chat/tools}/tool-calls.ts +63 -44
- package/src/{tools → activities/chat/tools}/tool-definition.ts +51 -38
- package/src/activities/generateImage/adapter.ts +104 -0
- package/src/activities/generateImage/index.ts +162 -0
- package/src/activities/generateSpeech/adapter.ts +87 -0
- package/src/activities/generateSpeech/index.ts +122 -0
- package/src/activities/generateTranscription/adapter.ts +89 -0
- package/src/activities/generateTranscription/index.ts +132 -0
- package/src/activities/generateVideo/adapter.ts +116 -0
- package/src/activities/generateVideo/index.ts +261 -0
- package/src/activities/index.ts +164 -0
- package/src/activities/summarize/adapter.ts +107 -0
- package/src/activities/summarize/index.ts +287 -0
- package/src/event-client.ts +5 -21
- package/src/index.ts +60 -15
- package/src/stream-to-response.ts +237 -0
- package/src/types.ts +429 -284
- package/dist/esm/base-adapter.d.ts +0 -36
- package/dist/esm/base-adapter.js +0 -12
- package/dist/esm/base-adapter.js.map +0 -1
- package/dist/esm/core/chat-common-options.d.ts +0 -52
- package/dist/esm/core/chat.d.ts +0 -30
- package/dist/esm/core/chat.js.map +0 -1
- package/dist/esm/core/embedding.d.ts +0 -8
- package/dist/esm/core/embedding.js +0 -33
- package/dist/esm/core/embedding.js.map +0 -1
- package/dist/esm/core/summarize.d.ts +0 -9
- package/dist/esm/core/summarize.js +0 -36
- package/dist/esm/core/summarize.js.map +0 -1
- package/dist/esm/message-converters.js.map +0 -1
- package/dist/esm/stream/json-parser.js.map +0 -1
- package/dist/esm/stream/message-updaters.js.map +0 -1
- package/dist/esm/stream/processor.js.map +0 -1
- package/dist/esm/stream/strategies.js.map +0 -1
- package/dist/esm/tools/tool-calls.js.map +0 -1
- package/dist/esm/tools/tool-definition.js.map +0 -1
- package/dist/esm/tools/zod-converter.d.ts +0 -30
- package/dist/esm/tools/zod-converter.js +0 -36
- package/dist/esm/tools/zod-converter.js.map +0 -1
- package/dist/esm/utilities/agent-loop-strategies.js.map +0 -1
- package/dist/esm/utilities/chat-options.d.ts +0 -6
- package/dist/esm/utilities/chat-options.js +0 -7
- package/dist/esm/utilities/chat-options.js.map +0 -1
- package/dist/esm/utilities/messages.d.ts +0 -30
- package/dist/esm/utilities/messages.js +0 -7
- package/dist/esm/utilities/messages.js.map +0 -1
- package/dist/esm/utilities/stream-to-response.d.ts +0 -48
- package/dist/esm/utilities/stream-to-response.js +0 -62
- package/dist/esm/utilities/stream-to-response.js.map +0 -1
- package/src/base-adapter.ts +0 -86
- package/src/core/chat-common-options.ts +0 -55
- package/src/core/embedding.ts +0 -54
- package/src/core/summarize.ts +0 -56
- package/src/tools/zod-converter.ts +0 -85
- package/src/utilities/chat-options.ts +0 -35
- package/src/utilities/messages.ts +0 -63
- package/src/utilities/stream-to-response.ts +0 -116
- /package/dist/esm/{utilities → activities/chat}/agent-loop-strategies.js +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/index.d.ts +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/json-parser.d.ts +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/json-parser.js +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/message-updaters.js +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/strategies.d.ts +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/strategies.js +0 -0
- /package/dist/esm/{tools → activities/chat/tools}/tool-definition.js +0 -0
- /package/src/{stream → activities/chat/stream}/index.ts +0 -0
- /package/src/{stream → activities/chat/stream}/json-parser.ts +0 -0
- /package/src/{stream → activities/chat/stream}/strategies.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/activities/chat/index.ts"],"sourcesContent":["/**\n * Text Activity\n *\n * Handles agentic text generation, one-shot text generation, and agentic structured output.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '../../event-client.js'\nimport { streamToText } from '../../stream-to-response.js'\nimport { ToolCallManager, executeToolCalls } from './tools/tool-calls'\nimport {\n convertSchemaToJsonSchema,\n isStandardSchema,\n parseWithStandardSchema,\n} from './tools/schema-converter'\nimport { maxIterations as maxIterationsStrategy } from './agent-loop-strategies'\nimport type {\n ApprovalRequest,\n ClientToolRequest,\n ToolResult,\n} from './tools/tool-calls'\nimport type { AnyTextAdapter } from './adapter'\nimport type {\n AgentLoopStrategy,\n ConstrainedModelMessage,\n DoneStreamChunk,\n InferSchemaType,\n ModelMessage,\n SchemaInput,\n StreamChunk,\n TextOptions,\n Tool,\n ToolCall,\n} from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'text' as const\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the text activity.\n * Types are extracted directly from the adapter (which has pre-resolved generics).\n *\n * @template TAdapter - The text adapter type (created by a provider function)\n * @template TSchema - Optional Standard Schema for structured output\n * @template TStream - Whether to stream the output (default: true)\n */\nexport interface TextActivityOptions<\n TAdapter extends AnyTextAdapter,\n TSchema extends SchemaInput | undefined,\n TStream extends boolean,\n> {\n /** The text adapter to use (created by a provider function like openaiText('gpt-4o')) */\n adapter: TAdapter\n /** Conversation messages - content types are constrained by the adapter's input modalities and metadata */\n messages?: Array<\n ConstrainedModelMessage<{\n inputModalities: TAdapter['~types']['inputModalities']\n messageMetadataByModality: TAdapter['~types']['messageMetadataByModality']\n }>\n >\n /** System prompts to prepend to the conversation */\n systemPrompts?: TextOptions['systemPrompts']\n /** Tools for function calling (auto-executed when called) */\n tools?: TextOptions['tools']\n /** Controls the randomness of the output. Higher values make output more random. Range: [0.0, 2.0] */\n temperature?: TextOptions['temperature']\n /** Nucleus sampling parameter. The model considers tokens with topP probability mass. */\n topP?: TextOptions['topP']\n /** The maximum number of tokens to generate in the response. */\n maxTokens?: TextOptions['maxTokens']\n /** Additional metadata to attach to the request. */\n metadata?: TextOptions['metadata']\n /** Model-specific provider options (type comes from adapter) */\n modelOptions?: TAdapter['~types']['providerOptions']\n /** AbortController for cancellation */\n abortController?: TextOptions['abortController']\n /** Strategy for controlling the agent loop */\n agentLoopStrategy?: TextOptions['agentLoopStrategy']\n /** Unique conversation identifier for tracking */\n conversationId?: TextOptions['conversationId']\n /**\n * Optional Standard Schema for structured output.\n * When provided, the activity will:\n * 1. Run the full agentic loop (executing tools as needed)\n * 2. Once complete, return a Promise with the parsed output matching the schema\n *\n * Supports any Standard Schema compliant library (Zod v4+, ArkType, Valibot, etc.)\n *\n * @example\n * ```ts\n * const result = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Generate a person' }],\n * outputSchema: z.object({ name: z.string(), age: z.number() })\n * })\n * // result is { name: string, age: number }\n * ```\n */\n outputSchema?: TSchema\n /**\n * Whether to stream the text result.\n * When true (default), returns an AsyncIterable<StreamChunk> for streaming output.\n * When false, returns a Promise<string> with the collected text content.\n *\n * Note: If outputSchema is provided, this option is ignored and the result\n * is always a Promise<InferSchemaType<TSchema>>.\n *\n * @default true\n *\n * @example Non-streaming text\n * ```ts\n * const text = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Hello!' }],\n * stream: false\n * })\n * // text is a string with the full response\n * ```\n */\n stream?: TStream\n}\n\n// ===========================\n// Chat Options Helper\n// ===========================\n\n/**\n * Create typed options for the chat() function without executing.\n * This is useful for pre-defining configurations with full type inference.\n *\n * @example\n * ```ts\n * const chatOptions = createChatOptions({\n * adapter: anthropicText('claude-sonnet-4-5'),\n * })\n *\n * const stream = chat({ ...chatOptions, messages })\n * ```\n */\nexport function createChatOptions<\n TAdapter extends AnyTextAdapter,\n TSchema extends SchemaInput | undefined = undefined,\n TStream extends boolean = true,\n>(\n options: TextActivityOptions<TAdapter, TSchema, TStream>,\n): TextActivityOptions<TAdapter, TSchema, TStream> {\n return options\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the text activity.\n * - If outputSchema is provided: Promise<InferSchemaType<TSchema>>\n * - If stream is false: Promise<string>\n * - Otherwise (stream is true, default): AsyncIterable<StreamChunk>\n */\nexport type TextActivityResult<\n TSchema extends SchemaInput | undefined,\n TStream extends boolean = true,\n> = TSchema extends SchemaInput\n ? Promise<InferSchemaType<TSchema>>\n : TStream extends false\n ? Promise<string>\n : AsyncIterable<StreamChunk>\n\n// ===========================\n// ChatEngine Implementation\n// ===========================\n\ninterface TextEngineConfig<\n TAdapter extends AnyTextAdapter,\n TParams extends TextOptions<any, any> = TextOptions<any>,\n> {\n adapter: TAdapter\n systemPrompts?: Array<string>\n params: TParams\n}\n\ntype ToolPhaseResult = 'continue' | 'stop' | 'wait'\ntype CyclePhase = 'processText' | 'executeToolCalls'\n\nclass TextEngine<\n TAdapter extends AnyTextAdapter,\n TParams extends TextOptions<any, any> = TextOptions<any>,\n> {\n private readonly adapter: TAdapter\n private readonly params: TParams\n private readonly systemPrompts: Array<string>\n private readonly tools: ReadonlyArray<Tool>\n private readonly loopStrategy: AgentLoopStrategy\n private readonly toolCallManager: ToolCallManager\n private readonly initialMessageCount: number\n private readonly requestId: string\n private readonly streamId: string\n private readonly effectiveRequest?: Request | RequestInit\n private readonly effectiveSignal?: AbortSignal\n\n private messages: Array<ModelMessage>\n private iterationCount = 0\n private lastFinishReason: string | null = null\n private streamStartTime = 0\n private totalChunkCount = 0\n private currentMessageId: string | null = null\n private accumulatedContent = ''\n private doneChunk: DoneStreamChunk | null = null\n private shouldEmitStreamEnd = true\n private earlyTermination = false\n private toolPhase: ToolPhaseResult = 'continue'\n private cyclePhase: CyclePhase = 'processText'\n\n constructor(config: TextEngineConfig<TAdapter, TParams>) {\n this.adapter = config.adapter\n this.params = config.params\n this.systemPrompts = config.params.systemPrompts || []\n this.tools = config.params.tools || []\n this.loopStrategy =\n config.params.agentLoopStrategy || maxIterationsStrategy(5)\n this.toolCallManager = new ToolCallManager(this.tools)\n this.initialMessageCount = config.params.messages.length\n this.messages = config.params.messages\n this.requestId = this.createId('chat')\n this.streamId = this.createId('stream')\n this.effectiveRequest = config.params.abortController\n ? { signal: config.params.abortController.signal }\n : undefined\n this.effectiveSignal = config.params.abortController?.signal\n }\n\n /** Get the accumulated content after the chat loop completes */\n getAccumulatedContent(): string {\n return this.accumulatedContent\n }\n\n /** Get the final messages array after the chat loop completes */\n getMessages(): Array<ModelMessage> {\n return this.messages\n }\n\n async *run(): AsyncGenerator<StreamChunk> {\n this.beforeRun()\n\n try {\n const pendingPhase = yield* this.checkForPendingToolCalls()\n if (pendingPhase === 'wait') {\n return\n }\n\n do {\n if (this.earlyTermination || this.isAborted()) {\n return\n }\n\n this.beginCycle()\n\n if (this.cyclePhase === 'processText') {\n yield* this.streamModelResponse()\n } else {\n yield* this.processToolCalls()\n }\n\n this.endCycle()\n } while (this.shouldContinue())\n } finally {\n this.afterRun()\n }\n }\n\n private beforeRun(): void {\n this.streamStartTime = Date.now()\n const {\n model,\n tools,\n temperature,\n topP,\n maxTokens,\n metadata,\n modelOptions,\n conversationId,\n } = this.params\n\n // Gather flattened options into an object for event emission\n const options: Record<string, unknown> = {}\n if (temperature !== undefined) options.temperature = temperature\n if (topP !== undefined) options.topP = topP\n if (maxTokens !== undefined) options.maxTokens = maxTokens\n if (metadata !== undefined) options.metadata = metadata\n\n aiEventClient.emit('text:started', {\n requestId: this.requestId,\n streamId: this.streamId,\n model: model,\n provider: this.adapter.name,\n messageCount: this.initialMessageCount,\n hasTools: !!tools && tools.length > 0,\n streaming: true,\n timestamp: Date.now(),\n clientId: conversationId,\n toolNames: tools?.map((t) => t.name),\n options: Object.keys(options).length > 0 ? options : undefined,\n modelOptions: modelOptions as Record<string, unknown> | undefined,\n })\n\n aiEventClient.emit('stream:started', {\n streamId: this.streamId,\n model,\n provider: this.adapter.name,\n timestamp: Date.now(),\n })\n }\n\n private afterRun(): void {\n if (!this.shouldEmitStreamEnd) {\n return\n }\n\n const now = Date.now()\n\n // Emit text:completed with final state\n aiEventClient.emit('text:completed', {\n requestId: this.requestId,\n streamId: this.streamId,\n model: this.params.model,\n content: this.accumulatedContent,\n messageId: this.currentMessageId || undefined,\n finishReason: this.lastFinishReason || undefined,\n usage: this.doneChunk?.usage,\n timestamp: now,\n })\n\n aiEventClient.emit('stream:ended', {\n requestId: this.requestId,\n streamId: this.streamId,\n totalChunks: this.totalChunkCount,\n duration: now - this.streamStartTime,\n timestamp: now,\n })\n }\n\n private beginCycle(): void {\n if (this.cyclePhase === 'processText') {\n this.beginIteration()\n }\n }\n\n private endCycle(): void {\n if (this.cyclePhase === 'processText') {\n this.cyclePhase = 'executeToolCalls'\n return\n }\n\n this.cyclePhase = 'processText'\n this.iterationCount++\n }\n\n private beginIteration(): void {\n this.currentMessageId = this.createId('msg')\n this.accumulatedContent = ''\n this.doneChunk = null\n }\n\n private async *streamModelResponse(): AsyncGenerator<StreamChunk> {\n const { temperature, topP, maxTokens, metadata, modelOptions } = this.params\n const tools = this.params.tools\n\n // Convert tool schemas to JSON Schema before passing to adapter\n const toolsWithJsonSchemas = tools?.map((tool) => ({\n ...tool,\n inputSchema: tool.inputSchema\n ? convertSchemaToJsonSchema(tool.inputSchema)\n : undefined,\n outputSchema: tool.outputSchema\n ? convertSchemaToJsonSchema(tool.outputSchema)\n : undefined,\n }))\n\n for await (const chunk of this.adapter.chatStream({\n model: this.params.model,\n messages: this.messages,\n tools: toolsWithJsonSchemas,\n temperature,\n topP,\n maxTokens,\n metadata,\n request: this.effectiveRequest,\n modelOptions,\n systemPrompts: this.systemPrompts,\n })) {\n if (this.isAborted()) {\n break\n }\n\n this.totalChunkCount++\n\n yield chunk\n this.handleStreamChunk(chunk)\n\n if (this.earlyTermination) {\n break\n }\n }\n }\n\n private handleStreamChunk(chunk: StreamChunk): void {\n switch (chunk.type) {\n case 'content':\n this.handleContentChunk(chunk)\n break\n case 'tool_call':\n this.handleToolCallChunk(chunk)\n break\n case 'tool_result':\n this.handleToolResultChunk(chunk)\n break\n case 'done':\n this.handleDoneChunk(chunk)\n break\n case 'error':\n this.handleErrorChunk(chunk)\n break\n case 'thinking':\n this.handleThinkingChunk(chunk)\n break\n default:\n break\n }\n }\n\n private handleContentChunk(chunk: Extract<StreamChunk, { type: 'content' }>) {\n this.accumulatedContent = chunk.content\n aiEventClient.emit('stream:chunk:content', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n content: chunk.content,\n delta: chunk.delta,\n timestamp: Date.now(),\n })\n }\n\n private handleToolCallChunk(\n chunk: Extract<StreamChunk, { type: 'tool_call' }>,\n ): void {\n this.toolCallManager.addToolCallChunk(chunk)\n aiEventClient.emit('stream:chunk:tool-call', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n toolCallId: chunk.toolCall.id,\n toolName: chunk.toolCall.function.name,\n index: chunk.index,\n arguments: chunk.toolCall.function.arguments,\n timestamp: Date.now(),\n })\n }\n\n private handleToolResultChunk(\n chunk: Extract<StreamChunk, { type: 'tool_result' }>,\n ): void {\n aiEventClient.emit('stream:chunk:tool-result', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n toolCallId: chunk.toolCallId,\n result: chunk.content,\n timestamp: Date.now(),\n })\n }\n\n private handleDoneChunk(chunk: DoneStreamChunk): void {\n // Don't overwrite a tool_calls finishReason with a stop finishReason\n // This can happen when adapters send multiple done chunks\n if (\n this.doneChunk?.finishReason === 'tool_calls' &&\n chunk.finishReason === 'stop'\n ) {\n // Still emit the event and update lastFinishReason, but don't overwrite doneChunk\n this.lastFinishReason = chunk.finishReason\n aiEventClient.emit('stream:chunk:done', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n finishReason: chunk.finishReason,\n usage: chunk.usage,\n timestamp: Date.now(),\n })\n\n if (chunk.usage) {\n aiEventClient.emit('usage:tokens', {\n requestId: this.requestId,\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n model: this.params.model,\n usage: chunk.usage,\n timestamp: Date.now(),\n })\n }\n return\n }\n\n this.doneChunk = chunk\n this.lastFinishReason = chunk.finishReason\n aiEventClient.emit('stream:chunk:done', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n finishReason: chunk.finishReason,\n usage: chunk.usage,\n timestamp: Date.now(),\n })\n\n if (chunk.usage) {\n aiEventClient.emit('usage:tokens', {\n requestId: this.requestId,\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n model: this.params.model,\n usage: chunk.usage,\n timestamp: Date.now(),\n })\n }\n }\n\n private handleErrorChunk(\n chunk: Extract<StreamChunk, { type: 'error' }>,\n ): void {\n aiEventClient.emit('stream:chunk:error', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n error: chunk.error.message,\n timestamp: Date.now(),\n })\n this.earlyTermination = true\n this.shouldEmitStreamEnd = false\n }\n\n private handleThinkingChunk(\n chunk: Extract<StreamChunk, { type: 'thinking' }>,\n ): void {\n aiEventClient.emit('stream:chunk:thinking', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n content: chunk.content,\n delta: chunk.delta,\n timestamp: Date.now(),\n })\n }\n\n private async *checkForPendingToolCalls(): AsyncGenerator<\n StreamChunk,\n ToolPhaseResult,\n void\n > {\n const pendingToolCalls = this.getPendingToolCallsFromMessages()\n if (pendingToolCalls.length === 0) {\n return 'continue'\n }\n\n const doneChunk = this.createSyntheticDoneChunk()\n\n aiEventClient.emit('text:iteration', {\n requestId: this.requestId,\n streamId: this.streamId,\n iterationNumber: this.iterationCount + 1,\n messageCount: this.messages.length,\n toolCallCount: pendingToolCalls.length,\n timestamp: Date.now(),\n })\n\n const { approvals, clientToolResults } = this.collectClientState()\n\n const executionResult = await executeToolCalls(\n pendingToolCalls,\n this.tools,\n approvals,\n clientToolResults,\n )\n\n if (\n executionResult.needsApproval.length > 0 ||\n executionResult.needsClientExecution.length > 0\n ) {\n for (const chunk of this.emitApprovalRequests(\n executionResult.needsApproval,\n doneChunk,\n )) {\n yield chunk\n }\n\n for (const chunk of this.emitClientToolInputs(\n executionResult.needsClientExecution,\n doneChunk,\n )) {\n yield chunk\n }\n\n this.shouldEmitStreamEnd = false\n return 'wait'\n }\n\n const toolResultChunks = this.emitToolResults(\n executionResult.results,\n doneChunk,\n )\n\n for (const chunk of toolResultChunks) {\n yield chunk\n }\n\n return 'continue'\n }\n\n private async *processToolCalls(): AsyncGenerator<StreamChunk, void, void> {\n if (!this.shouldExecuteToolPhase()) {\n this.setToolPhase('stop')\n return\n }\n\n const toolCalls = this.toolCallManager.getToolCalls()\n const doneChunk = this.doneChunk\n\n if (!doneChunk || toolCalls.length === 0) {\n this.setToolPhase('stop')\n return\n }\n\n aiEventClient.emit('text:iteration', {\n requestId: this.requestId,\n streamId: this.streamId,\n iterationNumber: this.iterationCount + 1,\n messageCount: this.messages.length,\n toolCallCount: toolCalls.length,\n timestamp: Date.now(),\n })\n\n this.addAssistantToolCallMessage(toolCalls)\n\n const { approvals, clientToolResults } = this.collectClientState()\n\n const executionResult = await executeToolCalls(\n toolCalls,\n this.tools,\n approvals,\n clientToolResults,\n )\n\n if (\n executionResult.needsApproval.length > 0 ||\n executionResult.needsClientExecution.length > 0\n ) {\n for (const chunk of this.emitApprovalRequests(\n executionResult.needsApproval,\n doneChunk,\n )) {\n yield chunk\n }\n\n for (const chunk of this.emitClientToolInputs(\n executionResult.needsClientExecution,\n doneChunk,\n )) {\n yield chunk\n }\n\n this.setToolPhase('wait')\n return\n }\n\n const toolResultChunks = this.emitToolResults(\n executionResult.results,\n doneChunk,\n )\n\n for (const chunk of toolResultChunks) {\n yield chunk\n }\n\n this.toolCallManager.clear()\n\n this.setToolPhase('continue')\n }\n\n private shouldExecuteToolPhase(): boolean {\n return (\n this.doneChunk?.finishReason === 'tool_calls' &&\n this.tools.length > 0 &&\n this.toolCallManager.hasToolCalls()\n )\n }\n\n private addAssistantToolCallMessage(toolCalls: Array<ToolCall>): void {\n this.messages = [\n ...this.messages,\n {\n role: 'assistant',\n content: this.accumulatedContent || null,\n toolCalls,\n },\n ]\n }\n\n private collectClientState(): {\n approvals: Map<string, boolean>\n clientToolResults: Map<string, any>\n } {\n const approvals = new Map<string, boolean>()\n const clientToolResults = new Map<string, any>()\n\n for (const message of this.messages) {\n // todo remove any and fix this\n if (message.role === 'assistant' && (message as any).parts) {\n const parts = (message as any).parts\n for (const part of parts) {\n if (\n part.type === 'tool-call' &&\n part.state === 'approval-responded' &&\n part.approval\n ) {\n approvals.set(part.approval.id, part.approval.approved)\n }\n\n if (\n part.type === 'tool-call' &&\n part.output !== undefined &&\n !part.approval\n ) {\n clientToolResults.set(part.id, part.output)\n }\n }\n }\n }\n\n return { approvals, clientToolResults }\n }\n\n private emitApprovalRequests(\n approvals: Array<ApprovalRequest>,\n doneChunk: DoneStreamChunk,\n ): Array<StreamChunk> {\n const chunks: Array<StreamChunk> = []\n\n for (const approval of approvals) {\n aiEventClient.emit('stream:approval-requested', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n toolCallId: approval.toolCallId,\n toolName: approval.toolName,\n input: approval.input,\n approvalId: approval.approvalId,\n timestamp: Date.now(),\n })\n\n chunks.push({\n type: 'approval-requested',\n id: doneChunk.id,\n model: doneChunk.model,\n timestamp: Date.now(),\n toolCallId: approval.toolCallId,\n toolName: approval.toolName,\n input: approval.input,\n approval: {\n id: approval.approvalId,\n needsApproval: true,\n },\n })\n }\n\n return chunks\n }\n\n private emitClientToolInputs(\n clientRequests: Array<ClientToolRequest>,\n doneChunk: DoneStreamChunk,\n ): Array<StreamChunk> {\n const chunks: Array<StreamChunk> = []\n\n for (const clientTool of clientRequests) {\n aiEventClient.emit('stream:tool-input-available', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n toolCallId: clientTool.toolCallId,\n toolName: clientTool.toolName,\n input: clientTool.input,\n timestamp: Date.now(),\n })\n\n chunks.push({\n type: 'tool-input-available',\n id: doneChunk.id,\n model: doneChunk.model,\n timestamp: Date.now(),\n toolCallId: clientTool.toolCallId,\n toolName: clientTool.toolName,\n input: clientTool.input,\n })\n }\n\n return chunks\n }\n\n private emitToolResults(\n results: Array<ToolResult>,\n doneChunk: DoneStreamChunk,\n ): Array<StreamChunk> {\n const chunks: Array<StreamChunk> = []\n\n for (const result of results) {\n aiEventClient.emit('tool:call-completed', {\n requestId: this.requestId,\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n toolCallId: result.toolCallId,\n toolName: result.toolName,\n result: result.result,\n duration: result.duration ?? 0,\n timestamp: Date.now(),\n })\n\n const content = JSON.stringify(result.result)\n const chunk: Extract<StreamChunk, { type: 'tool_result' }> = {\n type: 'tool_result',\n id: doneChunk.id,\n model: doneChunk.model,\n timestamp: Date.now(),\n toolCallId: result.toolCallId,\n content,\n }\n\n chunks.push(chunk)\n\n this.messages = [\n ...this.messages,\n {\n role: 'tool',\n content,\n toolCallId: result.toolCallId,\n },\n ]\n }\n\n return chunks\n }\n\n private getPendingToolCallsFromMessages(): Array<ToolCall> {\n const completedToolIds = new Set(\n this.messages\n .filter((message) => message.role === 'tool' && message.toolCallId)\n .map((message) => message.toolCallId!), // toolCallId exists due to filter\n )\n\n const pending: Array<ToolCall> = []\n\n for (const message of this.messages) {\n if (message.role === 'assistant' && message.toolCalls) {\n for (const toolCall of message.toolCalls) {\n if (!completedToolIds.has(toolCall.id)) {\n pending.push(toolCall)\n }\n }\n }\n }\n\n return pending\n }\n\n private createSyntheticDoneChunk(): DoneStreamChunk {\n return {\n type: 'done',\n id: this.createId('pending'),\n model: this.params.model,\n timestamp: Date.now(),\n finishReason: 'tool_calls',\n }\n }\n\n private shouldContinue(): boolean {\n if (this.cyclePhase === 'executeToolCalls') {\n return true\n }\n\n return (\n this.loopStrategy({\n iterationCount: this.iterationCount,\n messages: this.messages,\n finishReason: this.lastFinishReason,\n }) && this.toolPhase === 'continue'\n )\n }\n\n private isAborted(): boolean {\n return !!this.effectiveSignal?.aborted\n }\n\n private setToolPhase(phase: ToolPhaseResult): void {\n this.toolPhase = phase\n if (phase === 'wait') {\n this.shouldEmitStreamEnd = false\n }\n }\n\n private createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n }\n}\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * Text activity - handles agentic text generation, one-shot text generation, and agentic structured output.\n *\n * This activity supports four modes:\n * 1. **Streaming agentic text**: Stream responses with automatic tool execution\n * 2. **Streaming one-shot text**: Simple streaming request/response without tools\n * 3. **Non-streaming text**: Returns collected text as a string (stream: false)\n * 4. **Agentic structured output**: Run tools, then return structured data\n *\n * @example Full agentic text (streaming with tools)\n * ```ts\n * import { chat } from '@tanstack/ai'\n * import { openaiText } from '@tanstack/ai-openai'\n *\n * for await (const chunk of chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'What is the weather?' }],\n * tools: [weatherTool]\n * })) {\n * if (chunk.type === 'content') {\n * console.log(chunk.delta)\n * }\n * }\n * ```\n *\n * @example One-shot text (streaming without tools)\n * ```ts\n * for await (const chunk of chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Hello!' }]\n * })) {\n * console.log(chunk)\n * }\n * ```\n *\n * @example Non-streaming text (stream: false)\n * ```ts\n * const text = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Hello!' }],\n * stream: false\n * })\n * // text is a string with the full response\n * ```\n *\n * @example Agentic structured output (tools + structured response)\n * ```ts\n * import { z } from 'zod'\n *\n * const result = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Research and summarize the topic' }],\n * tools: [researchTool, analyzeTool],\n * outputSchema: z.object({\n * summary: z.string(),\n * keyPoints: z.array(z.string())\n * })\n * })\n * // result is { summary: string, keyPoints: string[] }\n * ```\n */\nexport function chat<\n TAdapter extends AnyTextAdapter,\n TSchema extends SchemaInput | undefined = undefined,\n TStream extends boolean = true,\n>(\n options: TextActivityOptions<TAdapter, TSchema, TStream>,\n): TextActivityResult<TSchema, TStream> {\n const { outputSchema, stream } = options\n\n // If outputSchema is provided, run agentic structured output\n if (outputSchema) {\n return runAgenticStructuredOutput(\n options as unknown as TextActivityOptions<\n AnyTextAdapter,\n SchemaInput,\n boolean\n >,\n ) as TextActivityResult<TSchema, TStream>\n }\n\n // If stream is explicitly false, run non-streaming text\n if (stream === false) {\n return runNonStreamingText(\n options as unknown as TextActivityOptions<\n AnyTextAdapter,\n undefined,\n false\n >,\n ) as TextActivityResult<TSchema, TStream>\n }\n\n // Otherwise, run streaming text (default)\n return runStreamingText(\n options as unknown as TextActivityOptions<AnyTextAdapter, undefined, true>,\n ) as TextActivityResult<TSchema, TStream>\n}\n\n/**\n * Run streaming text (agentic or one-shot depending on tools)\n */\nasync function* runStreamingText(\n options: TextActivityOptions<AnyTextAdapter, undefined, true>,\n): AsyncIterable<StreamChunk> {\n const { adapter, ...textOptions } = options\n const model = adapter.model\n\n const engine = new TextEngine({\n adapter,\n params: { ...textOptions, model } as TextOptions<\n Record<string, any>,\n Record<string, any>\n >,\n })\n\n for await (const chunk of engine.run()) {\n yield chunk\n }\n}\n\n/**\n * Run non-streaming text - collects all content and returns as a string.\n * Runs the full agentic loop (if tools are provided) but returns collected text.\n */\nfunction runNonStreamingText(\n options: TextActivityOptions<AnyTextAdapter, undefined, false>,\n): Promise<string> {\n // Run the streaming text and collect all text using streamToText\n const stream = runStreamingText(\n options as unknown as TextActivityOptions<AnyTextAdapter, undefined, true>,\n )\n\n return streamToText(stream)\n}\n\n/**\n * Run agentic structured output:\n * 1. Execute the full agentic loop (with tools)\n * 2. Once complete, call adapter.structuredOutput with the conversation context\n * 3. Validate and return the structured result\n */\nasync function runAgenticStructuredOutput<TSchema extends SchemaInput>(\n options: TextActivityOptions<AnyTextAdapter, TSchema, boolean>,\n): Promise<InferSchemaType<TSchema>> {\n const { adapter, outputSchema, ...textOptions } = options\n const model = adapter.model\n\n if (!outputSchema) {\n throw new Error('outputSchema is required for structured output')\n }\n\n // Create the engine and run the agentic loop\n const engine = new TextEngine({\n adapter,\n params: { ...textOptions, model } as TextOptions<\n Record<string, unknown>,\n Record<string, unknown>\n >,\n })\n\n // Consume the stream to run the agentic loop\n for await (const _chunk of engine.run()) {\n // Just consume the stream to execute the agentic loop\n }\n\n // Get the final messages from the engine (includes tool results)\n const finalMessages = engine.getMessages()\n\n // Build text options for structured output, excluding tools since\n // the agentic loop is complete and we only need the final response\n const {\n tools: _tools,\n agentLoopStrategy: _als,\n ...structuredTextOptions\n } = textOptions\n\n // Convert the schema to JSON Schema before passing to the adapter\n const jsonSchema = convertSchemaToJsonSchema(outputSchema)\n if (!jsonSchema) {\n throw new Error('Failed to convert output schema to JSON Schema')\n }\n\n // Call the adapter's structured output method with the conversation context\n // The adapter receives JSON Schema and can apply vendor-specific patches\n const result = await adapter.structuredOutput({\n chatOptions: {\n ...structuredTextOptions,\n model,\n messages: finalMessages,\n },\n outputSchema: jsonSchema,\n })\n\n // Validate the result against the schema if it's a Standard Schema\n if (isStandardSchema(outputSchema)) {\n return parseWithStandardSchema<InferSchemaType<TSchema>>(\n outputSchema,\n result.data,\n )\n }\n\n // For plain JSON Schema, return the data as-is\n return result.data as InferSchemaType<TSchema>\n}\n\n// Re-export adapter types\nexport type {\n TextAdapter,\n TextAdapterConfig,\n StructuredOutputOptions,\n StructuredOutputResult,\n} from './adapter'\nexport { BaseTextAdapter } from './adapter'\n"],"names":["maxIterationsStrategy"],"mappings":"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"}
|
|
@@ -19,9 +19,9 @@ function convertMessagesToModelMessages(messages) {
|
|
|
19
19
|
return modelMessages;
|
|
20
20
|
}
|
|
21
21
|
function uiMessageToModelMessages(uiMessage) {
|
|
22
|
-
const
|
|
22
|
+
const messageList = [];
|
|
23
23
|
if (uiMessage.role === "system") {
|
|
24
|
-
return
|
|
24
|
+
return messageList;
|
|
25
25
|
}
|
|
26
26
|
const textParts = [];
|
|
27
27
|
const toolCallParts = [];
|
|
@@ -48,13 +48,13 @@ function uiMessageToModelMessages(uiMessage) {
|
|
|
48
48
|
}
|
|
49
49
|
})) : void 0;
|
|
50
50
|
if (uiMessage.role !== "assistant" || content || !toolCalls) {
|
|
51
|
-
|
|
51
|
+
messageList.push({
|
|
52
52
|
role: uiMessage.role,
|
|
53
53
|
content,
|
|
54
54
|
...toolCalls && toolCalls.length > 0 && { toolCalls }
|
|
55
55
|
});
|
|
56
56
|
} else if (toolCalls.length > 0) {
|
|
57
|
-
|
|
57
|
+
messageList.push({
|
|
58
58
|
role: "assistant",
|
|
59
59
|
content,
|
|
60
60
|
toolCalls
|
|
@@ -62,14 +62,14 @@ function uiMessageToModelMessages(uiMessage) {
|
|
|
62
62
|
}
|
|
63
63
|
for (const toolResultPart of toolResultParts) {
|
|
64
64
|
if (toolResultPart.state === "complete" || toolResultPart.state === "error") {
|
|
65
|
-
|
|
65
|
+
messageList.push({
|
|
66
66
|
role: "tool",
|
|
67
67
|
content: toolResultPart.content,
|
|
68
68
|
toolCallId: toolResultPart.toolCallId
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
return
|
|
72
|
+
return messageList;
|
|
73
73
|
}
|
|
74
74
|
function modelMessageToUIMessage(modelMessage, id) {
|
|
75
75
|
const parts = [];
|
|
@@ -159,4 +159,4 @@ export {
|
|
|
159
159
|
normalizeToUIMessage,
|
|
160
160
|
uiMessageToModelMessages
|
|
161
161
|
};
|
|
162
|
-
//# sourceMappingURL=
|
|
162
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +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 ToolResultPart,\n UIMessage,\n} from '../../types'\n// ===========================\n// Message Converters\n// ===========================\n\n/**\n * Helper to extract text content from string or ContentPart array\n * For multimodal content, this extracts only the text parts\n */\nfunction getTextContent(content: string | null | Array<ContentPart>): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // Extract text from ContentPart array\n return content\n .filter((part) => 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 const modelMessages: Array<ModelMessage> = []\n for (const msg of messages) {\n if ('parts' in msg) {\n // UIMessage - convert to ModelMessages\n modelMessages.push(...uiMessageToModelMessages(msg))\n } else {\n // Already ModelMessage\n modelMessages.push(msg)\n }\n }\n return modelMessages\n}\n\n/**\n * Convert a UIMessage to ModelMessage(s)\n *\n * This conversion handles the parts-based structure:\n * - Text parts → content field\n * - ToolCall parts → toolCalls array\n * - ToolResult parts → separate role=\"tool\" messages\n *\n * @param uiMessage - The UIMessage to convert\n * @returns An array of ModelMessages (may be multiple if tool results are present)\n */\nexport function uiMessageToModelMessages(\n uiMessage: UIMessage,\n): Array<ModelMessage> {\n const messageList: Array<ModelMessage> = []\n\n // Skip system messages - they're handled via systemPrompts, not ModelMessages\n if (uiMessage.role === 'system') {\n return messageList\n }\n\n // Separate parts by type\n // Note: thinking parts are UI-only and not included in ModelMessages\n const textParts: Array<TextPart> = []\n const toolCallParts: Array<ToolCallPart> = []\n const toolResultParts: Array<ToolResultPart> = []\n\n for (const part of uiMessage.parts) {\n if (part.type === 'text') {\n textParts.push(part)\n } else if (part.type === 'tool-call') {\n toolCallParts.push(part)\n } else if (part.type === 'tool-result') {\n toolResultParts.push(part)\n }\n // thinking parts are skipped - they're UI-only\n }\n\n // Build the main message (user or assistant)\n const content = textParts.map((p) => p.content).join('') || null\n const toolCalls =\n toolCallParts.length > 0\n ? toolCallParts\n .filter(\n (p) =>\n p.state === 'input-complete' ||\n p.state === 'approval-responded' ||\n p.output !== undefined, // Include if has output (client tool result)\n )\n .map((p) => ({\n id: p.id,\n type: 'function' as const,\n function: {\n name: p.name,\n arguments: p.arguments,\n },\n }))\n : undefined\n\n // Create the main message\n if (uiMessage.role !== 'assistant' || content || !toolCalls) {\n messageList.push({\n role: uiMessage.role,\n content,\n ...(toolCalls && toolCalls.length > 0 && { toolCalls }),\n })\n } else if (toolCalls.length > 0) {\n // Assistant message with only tool calls\n messageList.push({\n role: 'assistant',\n content,\n toolCalls,\n })\n }\n\n // Add tool result messages (only completed ones)\n for (const toolResultPart of toolResultParts) {\n if (\n toolResultPart.state === 'complete' ||\n toolResultPart.state === 'error'\n ) {\n messageList.push({\n role: 'tool',\n content: toolResultPart.content,\n toolCallId: toolResultPart.toolCallId,\n })\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 // Handle content (convert multimodal content to text for UI)\n const textContent = getTextContent(modelMessage.content)\n if (textContent) {\n parts.push({\n type: 'text',\n content: textContent,\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 })\n }\n }\n\n // Handle tool results (when role is \"tool\")\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 }\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 currentAssistantMessage &&\n currentAssistantMessage.role === 'assistant'\n ) {\n currentAssistantMessage.parts.push({\n type: 'tool-result',\n toolCallId: msg.toolCallId!,\n content: getTextContent(msg.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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-parser.js","sources":["../../../../../src/activities/chat/stream/json-parser.ts"],"sourcesContent":["/**\n * JSON Parser for partial/incomplete JSON strings\n *\n * Used during streaming to parse tool call arguments that may be incomplete.\n */\n\nimport { parse as parsePartialJSONLib } from 'partial-json'\n\n/**\n * JSON Parser interface - allows for custom parser implementations\n */\nexport interface JSONParser {\n /**\n * Parse a JSON string (may be incomplete/partial)\n * @param jsonString - The JSON string to parse\n * @returns The parsed object, or undefined if parsing fails\n */\n parse: (jsonString: string) => any\n}\n\n/**\n * Partial JSON Parser implementation using the partial-json library\n * This parser can handle incomplete JSON strings during streaming\n */\nexport class PartialJSONParser implements JSONParser {\n /**\n * Parse a potentially incomplete JSON string\n * @param jsonString - The JSON string to parse (may be incomplete)\n * @returns The parsed object, or undefined if parsing fails\n */\n parse(jsonString: string): any {\n if (!jsonString || jsonString.trim() === '') {\n return undefined\n }\n\n try {\n return parsePartialJSONLib(jsonString)\n } catch {\n // If partial parsing fails, return undefined\n // This is expected during early streaming when we have very little data\n return undefined\n }\n }\n}\n\n/**\n * Default parser instance\n */\nexport const defaultJSONParser = new PartialJSONParser()\n\n/**\n * Parse partial JSON string (convenience function)\n * @param jsonString - The JSON string to parse (may be incomplete)\n * @returns The parsed object, or undefined if parsing fails\n */\nexport function parsePartialJSON(jsonString: string): any {\n return defaultJSONParser.parse(jsonString)\n}\n"],"names":["parsePartialJSONLib"],"mappings":";AAwBO,MAAM,kBAAwC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMnD,MAAM,YAAyB;AAC7B,QAAI,CAAC,cAAc,WAAW,KAAA,MAAW,IAAI;AAC3C,aAAO;AAAA,IACT;AAEA,QAAI;AACF,aAAOA,MAAoB,UAAU;AAAA,IACvC,QAAQ;AAGN,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAKO,MAAM,oBAAoB,IAAI,kBAAA;AAO9B,SAAS,iBAAiB,YAAyB;AACxD,SAAO,kBAAkB,MAAM,UAAU;AAC3C;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-updaters.js","sources":["../../../../../src/activities/chat/stream/message-updaters.ts"],"sourcesContent":["/**\n * Message Updaters (Internal)\n *\n * Internal helper functions for updating UIMessage parts.\n * These are used by StreamProcessor to manage the message array.\n */\n\nimport type {\n ThinkingPart,\n ToolCallPart,\n ToolResultPart,\n UIMessage,\n} from '../../../types'\nimport type { ToolCallState, ToolResultState } from './types'\n\n/**\n * Update or add a text part to a message.\n *\n * If the last part is a text part, update it (continuing the same text segment).\n * Otherwise, create a new text part (starting a new text segment after tool calls).\n */\nexport function updateTextPart(\n messages: Array<UIMessage>,\n messageId: string,\n content: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const lastPart = parts.length > 0 ? parts[parts.length - 1] : null\n\n if (lastPart && lastPart.type === 'text') {\n // Update the last text part (continuing same text segment)\n parts[parts.length - 1] = { type: 'text', content }\n } else {\n // Create new text part (starting new text segment after tool calls/results)\n parts.push({ type: 'text', content })\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a tool call part to a message.\n */\nexport function updateToolCallPart(\n messages: Array<UIMessage>,\n messageId: string,\n toolCall: {\n id: string\n name: string\n arguments: string\n state: ToolCallState\n },\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n // Find by ID, not index!\n const existingPartIndex = parts.findIndex(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCall.id,\n )\n\n const toolCallPart: ToolCallPart = {\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.name,\n arguments: toolCall.arguments,\n state: toolCall.state,\n }\n\n if (existingPartIndex >= 0) {\n // Update existing tool call\n parts[existingPartIndex] = toolCallPart\n } else {\n // Add new tool call at the end (preserve natural streaming order)\n parts.push(toolCallPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a tool result part to a message.\n */\nexport function updateToolResultPart(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n content: string,\n state: ToolResultState,\n error?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const resultPartIndex = parts.findIndex(\n (p): p is ToolResultPart =>\n p.type === 'tool-result' && p.toolCallId === toolCallId,\n )\n\n const toolResultPart: ToolResultPart = {\n type: 'tool-result',\n toolCallId,\n content,\n state,\n ...(error && { error }),\n }\n\n if (resultPartIndex >= 0) {\n parts[resultPartIndex] = toolResultPart\n } else {\n parts.push(toolResultPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with approval request metadata.\n */\nexport function updateToolCallApproval(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n approvalId: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n toolCallPart.state = 'approval-requested'\n toolCallPart.approval = {\n id: approvalId,\n needsApproval: true,\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part's state (e.g., to \"input-complete\").\n */\nexport function updateToolCallState(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n state: ToolCallState,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n toolCallPart.state = state\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with output.\n * Searches all messages to find the tool call by ID.\n */\nexport function updateToolCallWithOutput(\n messages: Array<UIMessage>,\n toolCallId: string,\n output: any,\n state?: ToolCallState,\n errorText?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n toolCallPart.output = errorText ? { error: errorText } : output\n if (state) {\n toolCallPart.state = state\n } else {\n toolCallPart.state = 'input-complete'\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with approval response.\n * Searches all messages to find the tool call by approval ID.\n */\nexport function updateToolCallApprovalResponse(\n messages: Array<UIMessage>,\n approvalId: string,\n approved: boolean,\n): Array<UIMessage> {\n return messages.map((msg) => {\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart =>\n p.type === 'tool-call' && p.approval?.id === approvalId,\n )\n\n if (toolCallPart && toolCallPart.approval) {\n toolCallPart.approval.approved = approved\n toolCallPart.state = 'approval-responded'\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a thinking part to a message.\n */\nexport function updateThinkingPart(\n messages: Array<UIMessage>,\n messageId: string,\n content: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const thinkingPartIndex = parts.findIndex((p) => p.type === 'thinking')\n\n const thinkingPart: ThinkingPart = {\n type: 'thinking',\n content,\n }\n\n if (thinkingPartIndex >= 0) {\n // Update existing thinking part\n parts[thinkingPartIndex] = thinkingPart\n } else {\n // Add new thinking part at the end (preserve natural streaming order)\n parts.push(thinkingPart)\n }\n\n return { ...msg, parts }\n })\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChunkRecording, ChunkStrategy, ProcessorResult, ProcessorState, ToolCallState, ToolResultState } from './types.js';
|
|
2
|
-
import { ModelMessage, StreamChunk, ToolCall, UIMessage } from '
|
|
2
|
+
import { ModelMessage, StreamChunk, ToolCall, UIMessage } from '../../../types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Events emitted by the StreamProcessor
|
|
5
5
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { generateMessageId, uiMessageToModelMessages } from "../
|
|
1
|
+
import { generateMessageId, uiMessageToModelMessages } from "../messages.js";
|
|
2
2
|
import { defaultJSONParser } from "./json-parser.js";
|
|
3
3
|
import { updateToolCallWithOutput, updateToolResultPart, updateToolCallApprovalResponse, updateToolCallPart, updateThinkingPart, updateToolCallApproval, updateTextPart } from "./message-updaters.js";
|
|
4
4
|
import { ImmediateStrategy } from "./strategies.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processor.js","sources":["../../../../../src/activities/chat/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 { generateMessageId, uiMessageToModelMessages } from '../messages.js'\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 @@
|
|
|
1
|
+
{"version":3,"file":"strategies.js","sources":["../../../../../src/activities/chat/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;"}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import { StreamChunk, ToolCall } from '
|
|
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';
|
|
1
|
+
import { StreamChunk, ToolCall, ToolCallState, ToolResultState } from '../../../types.js';
|
|
2
|
+
export type { ToolCallState, ToolResultState };
|
|
10
3
|
/**
|
|
11
4
|
* Internal state for a tool call being tracked
|
|
12
5
|
*/
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
import { JSONSchema, SchemaInput } from '../../../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Check if a value is a Standard JSON Schema compliant schema.
|
|
5
|
+
* Standard JSON Schema compliant libraries (Zod v4+, ArkType, Valibot with toStandardJsonSchema, etc.)
|
|
6
|
+
* implement the '~standard' property with jsonSchema converter methods.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isStandardJSONSchema(schema: unknown): schema is StandardJSONSchemaV1;
|
|
9
|
+
/**
|
|
10
|
+
* Check if a value is a Standard Schema compliant schema (for validation).
|
|
11
|
+
* Standard Schema compliant libraries implement the '~standard' property with a validate function.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isStandardSchema(schema: unknown): schema is StandardSchemaV1;
|
|
14
|
+
/**
|
|
15
|
+
* Options for schema conversion
|
|
16
|
+
*/
|
|
17
|
+
export interface ConvertSchemaOptions {
|
|
18
|
+
/**
|
|
19
|
+
* When true, transforms the schema to be compatible with OpenAI's structured output requirements:
|
|
20
|
+
* - All properties are added to the `required` array
|
|
21
|
+
* - Optional fields get null added to their type union
|
|
22
|
+
* - additionalProperties is set to false for all objects
|
|
23
|
+
*
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
forStructuredOutput?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Converts a Standard JSON Schema compliant schema or plain JSONSchema to JSON Schema format
|
|
30
|
+
* compatible with LLM providers.
|
|
31
|
+
*
|
|
32
|
+
* Supports any schema library that implements the Standard JSON Schema spec (v1):
|
|
33
|
+
* - Zod v4+ (natively supports StandardJSONSchemaV1)
|
|
34
|
+
* - ArkType (natively supports StandardJSONSchemaV1)
|
|
35
|
+
* - Valibot (via `toStandardJsonSchema()` from `@valibot/to-json-schema`)
|
|
36
|
+
*
|
|
37
|
+
* If the input is already a plain JSONSchema object, it is returned as-is.
|
|
38
|
+
*
|
|
39
|
+
* @param schema - Standard JSON Schema compliant schema or plain JSONSchema object to convert
|
|
40
|
+
* @param options - Conversion options
|
|
41
|
+
* @returns JSON Schema object that can be sent to LLM providers
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* // Using Zod v4+ (natively supports Standard JSON Schema)
|
|
46
|
+
* import * as z from 'zod';
|
|
47
|
+
*
|
|
48
|
+
* const zodSchema = z.object({
|
|
49
|
+
* location: z.string().describe('City name'),
|
|
50
|
+
* unit: z.enum(['celsius', 'fahrenheit']).optional()
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* const jsonSchema = convertSchemaToJsonSchema(zodSchema);
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // Using ArkType (natively supports Standard JSON Schema)
|
|
57
|
+
* import { type } from 'arktype';
|
|
58
|
+
*
|
|
59
|
+
* const arkSchema = type({
|
|
60
|
+
* location: 'string',
|
|
61
|
+
* unit: "'celsius' | 'fahrenheit'"
|
|
62
|
+
* });
|
|
63
|
+
*
|
|
64
|
+
* const jsonSchema = convertSchemaToJsonSchema(arkSchema);
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* // Using Valibot (via toStandardJsonSchema)
|
|
68
|
+
* import * as v from 'valibot';
|
|
69
|
+
* import { toStandardJsonSchema } from '@valibot/to-json-schema';
|
|
70
|
+
*
|
|
71
|
+
* const valibotSchema = toStandardJsonSchema(v.object({
|
|
72
|
+
* location: v.string(),
|
|
73
|
+
* unit: v.optional(v.picklist(['celsius', 'fahrenheit']))
|
|
74
|
+
* }));
|
|
75
|
+
*
|
|
76
|
+
* const jsonSchema = convertSchemaToJsonSchema(valibotSchema);
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // Using JSONSchema directly (passes through unchanged)
|
|
80
|
+
* const rawSchema = {
|
|
81
|
+
* type: 'object',
|
|
82
|
+
* properties: { location: { type: 'string' } },
|
|
83
|
+
* required: ['location']
|
|
84
|
+
* };
|
|
85
|
+
* const result = convertSchemaToJsonSchema(rawSchema);
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare function convertSchemaToJsonSchema(schema: SchemaInput | undefined, options?: ConvertSchemaOptions): JSONSchema | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* Validates data against a Standard Schema compliant schema.
|
|
91
|
+
*
|
|
92
|
+
* @param schema - Standard Schema compliant schema
|
|
93
|
+
* @param data - Data to validate
|
|
94
|
+
* @returns Validation result with success status, data or issues
|
|
95
|
+
*/
|
|
96
|
+
export declare function validateWithStandardSchema<T>(schema: unknown, data: unknown): Promise<{
|
|
97
|
+
success: true;
|
|
98
|
+
data: T;
|
|
99
|
+
} | {
|
|
100
|
+
success: false;
|
|
101
|
+
issues: Array<{
|
|
102
|
+
message: string;
|
|
103
|
+
path?: Array<string>;
|
|
104
|
+
}>;
|
|
105
|
+
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Synchronously validates data against a Standard Schema compliant schema.
|
|
108
|
+
* Note: Some Standard Schema implementations may only support async validation.
|
|
109
|
+
* In those cases, this function will throw.
|
|
110
|
+
*
|
|
111
|
+
* @param schema - Standard Schema compliant schema
|
|
112
|
+
* @param data - Data to validate
|
|
113
|
+
* @returns Parsed/validated data
|
|
114
|
+
* @throws Error if validation fails or if the schema only supports async validation
|
|
115
|
+
*/
|
|
116
|
+
export declare function parseWithStandardSchema<T>(schema: unknown, data: unknown): T;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
function isStandardJSONSchema(schema) {
|
|
2
|
+
return typeof schema === "object" && schema !== null && "~standard" in schema && typeof schema["~standard"] === "object" && schema["~standard"].version === 1 && typeof schema["~standard"].jsonSchema === "object" && typeof schema["~standard"].jsonSchema.input === "function";
|
|
3
|
+
}
|
|
4
|
+
function isStandardSchema(schema) {
|
|
5
|
+
return typeof schema === "object" && schema !== null && "~standard" in schema && typeof schema["~standard"] === "object" && schema !== null && schema["~standard"] !== null && "version" in schema["~standard"] && schema["~standard"].version === 1 && "validate" in schema["~standard"] && typeof schema["~standard"].validate === "function";
|
|
6
|
+
}
|
|
7
|
+
function makeStructuredOutputCompatible(schema, originalRequired = []) {
|
|
8
|
+
const result = { ...schema };
|
|
9
|
+
if (result.type === "object" && result.properties) {
|
|
10
|
+
const properties = { ...result.properties };
|
|
11
|
+
const allPropertyNames = Object.keys(properties);
|
|
12
|
+
for (const propName of allPropertyNames) {
|
|
13
|
+
const prop = properties[propName];
|
|
14
|
+
const wasOptional = !originalRequired.includes(propName);
|
|
15
|
+
if (prop.type === "object" && prop.properties) {
|
|
16
|
+
properties[propName] = makeStructuredOutputCompatible(
|
|
17
|
+
prop,
|
|
18
|
+
prop.required || []
|
|
19
|
+
);
|
|
20
|
+
} else if (prop.type === "array" && prop.items) {
|
|
21
|
+
properties[propName] = {
|
|
22
|
+
...prop,
|
|
23
|
+
items: makeStructuredOutputCompatible(
|
|
24
|
+
prop.items,
|
|
25
|
+
prop.items.required || []
|
|
26
|
+
)
|
|
27
|
+
};
|
|
28
|
+
} else if (wasOptional) {
|
|
29
|
+
if (prop.type && !Array.isArray(prop.type)) {
|
|
30
|
+
properties[propName] = {
|
|
31
|
+
...prop,
|
|
32
|
+
type: [prop.type, "null"]
|
|
33
|
+
};
|
|
34
|
+
} else if (Array.isArray(prop.type) && !prop.type.includes("null")) {
|
|
35
|
+
properties[propName] = {
|
|
36
|
+
...prop,
|
|
37
|
+
type: [...prop.type, "null"]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
result.properties = properties;
|
|
43
|
+
result.required = allPropertyNames;
|
|
44
|
+
result.additionalProperties = false;
|
|
45
|
+
}
|
|
46
|
+
if (result.type === "array" && result.items) {
|
|
47
|
+
result.items = makeStructuredOutputCompatible(
|
|
48
|
+
result.items,
|
|
49
|
+
result.items.required || []
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
function convertSchemaToJsonSchema(schema, options = {}) {
|
|
55
|
+
if (!schema) return void 0;
|
|
56
|
+
const { forStructuredOutput = false } = options;
|
|
57
|
+
if (isStandardJSONSchema(schema)) {
|
|
58
|
+
const jsonSchema = schema["~standard"].jsonSchema.input({
|
|
59
|
+
target: "draft-07"
|
|
60
|
+
});
|
|
61
|
+
let result = jsonSchema;
|
|
62
|
+
if (typeof result === "object" && "$schema" in result) {
|
|
63
|
+
const { $schema, ...rest } = result;
|
|
64
|
+
result = rest;
|
|
65
|
+
}
|
|
66
|
+
if (typeof result === "object") {
|
|
67
|
+
if ("properties" in result && !result.type) {
|
|
68
|
+
result.type = "object";
|
|
69
|
+
}
|
|
70
|
+
if (result.type === "object" && !("properties" in result)) {
|
|
71
|
+
result.properties = {};
|
|
72
|
+
}
|
|
73
|
+
if (result.type === "object" && !("required" in result)) {
|
|
74
|
+
result.required = [];
|
|
75
|
+
}
|
|
76
|
+
if (forStructuredOutput) {
|
|
77
|
+
result = makeStructuredOutputCompatible(
|
|
78
|
+
result,
|
|
79
|
+
result.required || []
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
if (forStructuredOutput && typeof schema === "object") {
|
|
86
|
+
return makeStructuredOutputCompatible(
|
|
87
|
+
schema,
|
|
88
|
+
schema.required || []
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return schema;
|
|
92
|
+
}
|
|
93
|
+
function parseWithStandardSchema(schema, data) {
|
|
94
|
+
if (!isStandardSchema(schema)) {
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
const result = schema["~standard"].validate(data);
|
|
98
|
+
if (result instanceof Promise) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
"Schema validation returned a Promise. Use validateWithStandardSchema for async validation."
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
if (!result.issues) {
|
|
104
|
+
return result.value;
|
|
105
|
+
}
|
|
106
|
+
const errorMessages = result.issues.map((issue) => issue.message || "Validation failed").join(", ");
|
|
107
|
+
throw new Error(`Validation failed: ${errorMessages}`);
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
convertSchemaToJsonSchema,
|
|
111
|
+
isStandardJSONSchema,
|
|
112
|
+
isStandardSchema,
|
|
113
|
+
parseWithStandardSchema
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=schema-converter.js.map
|