@tanstack/ai-client 0.7.1 → 0.7.4
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 +6 -6
- package/dist/esm/chat-client.d.ts +62 -3
- package/dist/esm/chat-client.js +264 -54
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/connection-adapters.d.ts +28 -12
- package/dist/esm/connection-adapters.js +93 -0
- package/dist/esm/connection-adapters.js.map +1 -1
- package/dist/esm/generation-client.d.ts +5 -5
- package/dist/esm/generation-client.js +1 -1
- package/dist/esm/generation-client.js.map +1 -1
- package/dist/esm/generation-types.d.ts +5 -5
- package/dist/esm/generation-types.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/types.d.ts +23 -2
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/video-generation-client.d.ts +5 -5
- package/dist/esm/video-generation-client.js +1 -1
- package/dist/esm/video-generation-client.js.map +1 -1
- package/package.json +3 -3
- package/src/chat-client.ts +355 -86
- package/src/connection-adapters.ts +147 -12
- package/src/generation-client.ts +7 -7
- package/src/generation-types.ts +5 -5
- package/src/index.ts +3 -0
- package/src/types.ts +31 -2
- package/src/video-generation-client.ts +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generation-types.js","sources":["../../src/generation-types.ts"],"sourcesContent":["import type { StreamChunk } from '@tanstack/ai'\nimport type {
|
|
1
|
+
{"version":3,"file":"generation-types.js","sources":["../../src/generation-types.ts"],"sourcesContent":["import type { StreamChunk } from '@tanstack/ai'\nimport type { ConnectConnectionAdapter } from './connection-adapters'\n\n// ===========================\n// Inference Utilities\n// ===========================\n\n/**\n * Infers the output type from an `onResult` callback's return type.\n *\n * - If the callback returns a concrete type (excluding null/void/undefined), uses that type.\n * - If the callback only returns null/void/undefined, or is not provided, falls back to TResult.\n *\n * @template TResult - The raw result type from the generation\n * @template TFn - The onResult callback type (or undefined if not provided)\n */\nexport type InferGenerationOutput<TResult, TFn> = TFn extends (\n result: any,\n) => infer R\n ? [Exclude<R, null | void | undefined>] extends [never]\n ? TResult\n : Exclude<R, null | void | undefined>\n : TResult\n\n// ===========================\n// State\n// ===========================\n\n/**\n * State machine for generation clients.\n * Simpler than ChatClientState since generation is a single request/response cycle.\n */\nexport type GenerationClientState = 'idle' | 'generating' | 'success' | 'error'\n\n// ===========================\n// Event Constants\n// ===========================\n\n/**\n * Well-known CUSTOM event names used by generation clients.\n * These events are emitted by the server-side streaming helpers\n * and consumed by the client-side GenerationClient.\n */\nexport const GENERATION_EVENTS = {\n /** The generation result payload */\n RESULT: 'generation:result',\n /** Progress update (0-100) with optional message */\n PROGRESS: 'generation:progress',\n /** Video job created with jobId */\n VIDEO_JOB_CREATED: 'video:job:created',\n /** Video job status update */\n VIDEO_STATUS: 'video:status',\n} as const\n\n// ===========================\n// Transport Types\n// ===========================\n\n/**\n * Options passed to a fetcher function by the generation client.\n */\nexport interface GenerationFetcherOptions {\n /** AbortSignal that is triggered when the user calls `stop()` */\n signal: AbortSignal\n}\n\n/**\n * A direct async function that performs a generation request.\n *\n * Can return the result directly, or return a `Response` with an SSE body\n * (e.g., from a TanStack Start server function using `toServerSentEventsResponse()`).\n * When a `Response` is returned, the client will parse it as an SSE stream.\n *\n * @template TInput - The input type for the generation request\n * @template TResult - The result type returned by the generation\n */\nexport type GenerationFetcher<TInput, TResult> = (\n input: TInput,\n options?: GenerationFetcherOptions,\n) => Promise<TResult | Response>\n\n/**\n * Transport configuration for generation clients.\n * Supports either a connect-based streaming adapter or a direct fetcher function.\n */\nexport type GenerationTransport<TInput, TResult> =\n | { connection: ConnectConnectionAdapter; fetcher?: never }\n | { fetcher: GenerationFetcher<TInput, TResult>; connection?: never }\n\n// ===========================\n// Client Options\n// ===========================\n\n/**\n * Options for the GenerationClient.\n *\n * @template TInput - The input type for the generation request (used by consuming code)\n * @template TResult - The result type returned by the generation\n * @template TOutput - The output type after optional transform (defaults to TResult)\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport interface GenerationClientOptions<_TInput, TResult, TOutput = TResult> {\n /** Unique identifier for this generation client instance */\n id?: string\n\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n\n /**\n * Callback when a result is received. Can optionally return a transformed value\n * that replaces the stored result.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: TResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n\n // Framework state callbacks (set by hooks, not users)\n /** @internal Called when result changes */\n onResultChange?: (result: TOutput | null) => void\n /** @internal Called when loading state changes */\n onLoadingChange?: (isLoading: boolean) => void\n /** @internal Called when error state changes */\n onErrorChange?: (error: Error | undefined) => void\n /** @internal Called when generation status changes */\n onStatusChange?: (status: GenerationClientState) => void\n}\n\n// ===========================\n// Video-Specific Options\n// ===========================\n\n/**\n * Video status information returned during job polling.\n */\nexport interface VideoStatusInfo {\n /** Job identifier */\n jobId: string\n /** Current status of the video generation job */\n status: 'pending' | 'processing' | 'completed' | 'failed'\n /** Progress percentage (0-100), if available */\n progress?: number\n /** URL to the generated video (when completed) */\n url?: string\n /** Error message if status is 'failed' */\n error?: string\n}\n\n/**\n * Composite result for video generation (job completion).\n */\nexport interface VideoGenerateResult {\n /** Job identifier */\n jobId: string\n /** Final status */\n status: 'completed'\n /** URL to the generated video */\n url: string\n /** When the URL expires, if applicable */\n expiresAt?: Date\n}\n\n/**\n * Options for the VideoGenerationClient.\n */\nexport interface VideoGenerationClientOptions<\n TOutput = VideoGenerateResult,\n> extends GenerationClientOptions<\n VideoGenerateInput,\n VideoGenerateResult,\n TOutput\n> {\n /** Callback when a video job is created */\n onJobCreated?: (jobId: string) => void\n /** Callback on each status update */\n onStatusUpdate?: (status: VideoStatusInfo) => void\n\n // Framework state callbacks\n /** @internal Called when jobId changes */\n onJobIdChange?: (jobId: string | null) => void\n /** @internal Called when video status changes */\n onVideoStatusChange?: (status: VideoStatusInfo | null) => void\n}\n\n// ===========================\n// Input Types\n// ===========================\n\n/**\n * Input for image generation.\n */\nexport interface ImageGenerateInput {\n /** Text description of the desired image(s) */\n prompt: string\n /** Number of images to generate (default: 1) */\n numberOfImages?: number\n /** Image size in WIDTHxHEIGHT format (e.g., \"1024x1024\") */\n size?: string\n /** Model-specific options */\n modelOptions?: Record<string, any>\n}\n\n/**\n * Input for text-to-speech generation.\n */\nexport interface SpeechGenerateInput {\n /** The text to convert to speech */\n text: string\n /** The voice to use for generation */\n voice?: string\n /** The output audio format */\n format?: 'mp3' | 'opus' | 'aac' | 'flac' | 'wav' | 'pcm'\n /** The speed of the generated audio (0.25 to 4.0) */\n speed?: number\n /** Model-specific options */\n modelOptions?: Record<string, any>\n}\n\n/**\n * Input for audio transcription.\n */\nexport interface TranscriptionGenerateInput {\n /** The audio data to transcribe - can be base64 string, File, or Blob */\n audio: string | File | Blob\n /** The language of the audio in ISO-639-1 format (e.g., 'en') */\n language?: string\n /** An optional prompt to guide the transcription */\n prompt?: string\n /** The format of the transcription output */\n responseFormat?: 'json' | 'text' | 'srt' | 'verbose_json' | 'vtt'\n /** Model-specific options */\n modelOptions?: Record<string, any>\n}\n\n/**\n * Input for text summarization.\n */\nexport interface SummarizeGenerateInput {\n /** The text to summarize */\n text: string\n /** Maximum length of the summary */\n maxLength?: number\n /** Style of the summary */\n style?: 'bullet-points' | 'paragraph' | 'concise'\n /** Topics to focus on */\n focus?: Array<string>\n /** Model-specific options */\n modelOptions?: Record<string, any>\n}\n\n/**\n * Input for video generation.\n */\nexport interface VideoGenerateInput {\n /** Text description of the desired video */\n prompt: string\n /** Video size — format depends on provider (e.g., \"16:9\", \"1280x720\") */\n size?: string\n /** Video duration in seconds */\n duration?: number\n /** Model-specific options */\n modelOptions?: Record<string, any>\n}\n"],"names":[],"mappings":"AA2CO,MAAM,oBAAoB;AAAA;AAAA,EAE/B,QAAQ;AAAA;AAAA,EAER,UAAU;AAAA;AAAA,EAEV,mBAAmB;AAAA;AAAA,EAEnB,cAAc;AAChB;"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -2,13 +2,13 @@ export { ChatClient } from './chat-client.js';
|
|
|
2
2
|
export { RealtimeClient } from './realtime-client.js';
|
|
3
3
|
export { GenerationClient } from './generation-client.js';
|
|
4
4
|
export { VideoGenerationClient } from './video-generation-client.js';
|
|
5
|
-
export type { UIMessage, MessagePart, TextPart, ToolCallPart, ToolResultPart, ThinkingPart, ChatClientOptions, ChatRequestBody, InferChatMessages, ChatClientState, MultimodalContent, } from './types.js';
|
|
5
|
+
export type { UIMessage, MessagePart, TextPart, ToolCallPart, ToolResultPart, ThinkingPart, ChatClientOptions, ChatRequestBody, InferChatMessages, ChatClientState, ConnectionStatus, MultimodalContent, } from './types.js';
|
|
6
6
|
export type { InferGenerationOutput, GenerationClientState, GenerationClientOptions, GenerationFetcher, GenerationFetcherOptions, GenerationTransport, VideoGenerationClientOptions, VideoStatusInfo, VideoGenerateResult, ImageGenerateInput, SpeechGenerateInput, TranscriptionGenerateInput, SummarizeGenerateInput, VideoGenerateInput, } from './generation-types.js';
|
|
7
7
|
export { GENERATION_EVENTS } from './generation-types.js';
|
|
8
8
|
export { clientTools, createChatClientOptions } from './types.js';
|
|
9
9
|
export type { ExtractToolNames, ExtractToolInput, ExtractToolOutput, } from './tool-types.js';
|
|
10
10
|
export type { AnyClientTool } from '@tanstack/ai';
|
|
11
11
|
export type { RealtimeAdapter, RealtimeConnection, RealtimeClientOptions, RealtimeClientState, RealtimeStateChangeCallback, } from './realtime-types.js';
|
|
12
|
-
export { fetchServerSentEvents, fetchHttpStream, stream, rpcStream, type ConnectionAdapter, type FetchConnectionOptions, } from './connection-adapters.js';
|
|
12
|
+
export { fetchServerSentEvents, fetchHttpStream, stream, rpcStream, type ConnectConnectionAdapter, type ConnectionAdapter, type FetchConnectionOptions, type SubscribeConnectionAdapter, } from './connection-adapters.js';
|
|
13
13
|
export { uiMessageToModelMessages, modelMessageToUIMessage, modelMessagesToUIMessages, convertMessagesToModelMessages, normalizeToUIMessage, generateMessageId, } from '@tanstack/ai';
|
|
14
14
|
export { StreamProcessor, ImmediateStrategy, PunctuationStrategy, BatchStrategy, WordBoundaryStrategy, CompositeStrategy, parsePartialJSON, PartialJSONParser, defaultJSONParser, type ChunkStrategy, type StreamProcessorOptions, type StreamProcessorEvents, type InternalToolCallState, type ToolCallState, type ToolResultState, type JSONParser, type ChunkRecording, type ProcessorResult, type ProcessorState, } from '@tanstack/ai';
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ export type ToolResultState = 'streaming' | 'complete' | 'error';
|
|
|
12
12
|
* ChatClient state - track the lifecycle of a chat
|
|
13
13
|
*/
|
|
14
14
|
export type ChatClientState = 'ready' | 'submitted' | 'streaming' | 'error';
|
|
15
|
+
/**
|
|
16
|
+
* Connection lifecycle state for the subscription loop.
|
|
17
|
+
*/
|
|
18
|
+
export type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'error';
|
|
15
19
|
/**
|
|
16
20
|
* Multimodal content input for sending messages with rich media.
|
|
17
21
|
* Allows sending text, images, audio, video, and documents to the LLM.
|
|
@@ -125,8 +129,9 @@ export interface UIMessage<TTools extends ReadonlyArray<AnyClientTool> = any> {
|
|
|
125
129
|
}
|
|
126
130
|
export interface ChatClientOptions<TTools extends ReadonlyArray<AnyClientTool> = any> {
|
|
127
131
|
/**
|
|
128
|
-
* Connection adapter for streaming
|
|
129
|
-
*
|
|
132
|
+
* Connection adapter for streaming.
|
|
133
|
+
* Supports mutually exclusive modes: request-response via `connect()`, or
|
|
134
|
+
* subscribe/send mode via `subscribe()` + `send()`.
|
|
130
135
|
*/
|
|
131
136
|
connection: ConnectionAdapter;
|
|
132
137
|
/**
|
|
@@ -174,6 +179,22 @@ export interface ChatClientOptions<TTools extends ReadonlyArray<AnyClientTool> =
|
|
|
174
179
|
* Callback when chat status changes
|
|
175
180
|
*/
|
|
176
181
|
onStatusChange?: (status: ChatClientState) => void;
|
|
182
|
+
/**
|
|
183
|
+
* Callback when subscription lifecycle changes.
|
|
184
|
+
* This is independent from request lifecycle (`isLoading`, `status`).
|
|
185
|
+
*/
|
|
186
|
+
onSubscriptionChange?: (isSubscribed: boolean) => void;
|
|
187
|
+
/**
|
|
188
|
+
* Callback when connection lifecycle changes.
|
|
189
|
+
*/
|
|
190
|
+
onConnectionStatusChange?: (status: ConnectionStatus) => void;
|
|
191
|
+
/**
|
|
192
|
+
* Callback when session generation activity changes.
|
|
193
|
+
* Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).
|
|
194
|
+
* Unlike `onLoadingChange` (request-local), this reflects shared generation
|
|
195
|
+
* activity visible to all subscribers (e.g. across tabs/devices).
|
|
196
|
+
*/
|
|
197
|
+
onSessionGeneratingChange?: (isGenerating: boolean) => void;
|
|
177
198
|
/**
|
|
178
199
|
* Callback when a custom event is received from a server-side tool.
|
|
179
200
|
* Custom events are emitted by tools using `context.emitCustomEvent()` during execution.
|
package/dist/esm/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sources":["../../src/types.ts"],"sourcesContent":["import type {\n AnyClientTool,\n AudioPart,\n ChunkStrategy,\n ContentPart,\n DocumentPart,\n ImagePart,\n InferToolInput,\n InferToolOutput,\n ModelMessage,\n StreamChunk,\n VideoPart,\n} from '@tanstack/ai'\nimport type { ConnectionAdapter } from './connection-adapters'\n\n/**\n * Tool call states - track the lifecycle of a tool call\n */\nexport type ToolCallState =\n | 'awaiting-input' // Received start but no arguments yet\n | 'input-streaming' // Partial arguments received\n | 'input-complete' // All arguments received\n | 'approval-requested' // Waiting for user approval\n | 'approval-responded' // User has approved/denied\n\n/**\n * Tool result states - track the lifecycle of a tool result\n */\nexport type ToolResultState =\n | 'streaming' // Placeholder for future streamed output\n | 'complete' // Result is complete\n | 'error' // Error occurred\n\n/**\n * ChatClient state - track the lifecycle of a chat\n */\nexport type ChatClientState = 'ready' | 'submitted' | 'streaming' | 'error'\n\n/**\n * Multimodal content input for sending messages with rich media.\n * Allows sending text, images, audio, video, and documents to the LLM.\n *\n * @example\n * ```ts\n * // Send an image with a question\n * client.sendMessage({\n * content: [\n * { type: 'text', content: 'What is in this image?' },\n * { type: 'image', source: { type: 'url', value: 'https://example.com/photo.jpg' } }\n * ],\n * id: 'custom-message-id' // optional\n * })\n * ```\n */\nexport interface MultimodalContent {\n /**\n * The content of the message.\n * Can be a simple string or an array of content parts for multimodal messages.\n */\n content: string | Array<ContentPart>\n /**\n * Optional custom ID for the message.\n * If not provided, a unique ID will be generated.\n */\n id?: string\n}\n\n/**\n * Message parts - building blocks of UIMessage\n */\nexport interface TextPart {\n type: 'text'\n content: string\n}\n\n/**\n * Helper type that creates a tool-call part for a specific tool.\n * This is a conditional type to enable proper distribution over union types,\n * creating a discriminated union where `name` is the discriminant.\n */\ntype ToolCallPartForTool<T> = T extends AnyClientTool\n ? {\n type: 'tool-call'\n id: string\n name: T['name']\n arguments: string // JSON string (may be incomplete)\n /** Parsed tool input (typed from inputSchema) */\n input?: InferToolInput<T>\n state: ToolCallState\n /** Approval metadata if tool requires user approval */\n approval?: {\n id: string // Unique approval ID\n needsApproval: boolean // Always true if present\n approved?: boolean // User's decision (undefined until responded)\n }\n /** Tool execution output (for client tools or after approval) */\n output?: InferToolOutput<T>\n }\n : never\n\n/**\n * Fallback tool-call part type when tools are not typed\n */\ntype UntypedToolCallPart = {\n type: 'tool-call'\n id: string\n name: string\n arguments: string\n input?: any\n state: ToolCallState\n approval?: {\n id: string\n needsApproval: boolean\n approved?: boolean\n }\n output?: any\n}\n\n/**\n * Tool call part that creates a proper discriminated union.\n * When TTools is typed, checking `part.name === 'toolName'` will narrow\n * `part.output` to the correct type for that tool.\n *\n * The discriminant is `name`, so code like:\n * ```ts\n * if (part.name === 'recommendGuitar') {\n * // part.output is now typed to the recommendGuitar tool's output\n * }\n * ```\n */\nexport type ToolCallPart<TTools extends ReadonlyArray<AnyClientTool> = any> =\n // Check if we have a concrete tools array (not 'any' or 'never')\n [TTools] extends [never]\n ? UntypedToolCallPart\n : unknown extends TTools\n ? UntypedToolCallPart\n : TTools extends ReadonlyArray<infer Tool>\n ? Tool extends AnyClientTool\n ? ToolCallPartForTool<Tool>\n : UntypedToolCallPart\n : UntypedToolCallPart\n\nexport interface ToolResultPart {\n type: 'tool-result'\n toolCallId: string\n content: string\n state: ToolResultState\n error?: string // Error message if state is \"error\"\n}\n\nexport interface ThinkingPart {\n type: 'thinking'\n content: string\n}\n\nexport type MessagePart<TTools extends ReadonlyArray<AnyClientTool> = any> =\n | TextPart\n | ImagePart\n | AudioPart\n | VideoPart\n | DocumentPart\n | ToolCallPart<TTools>\n | ToolResultPart\n | ThinkingPart\n\n/**\n * UIMessage - Domain-specific message format optimized for building chat UIs\n * Contains parts that can be text, tool calls, or tool results\n */\nexport interface UIMessage<TTools extends ReadonlyArray<AnyClientTool> = any> {\n id: string\n role: 'system' | 'user' | 'assistant'\n parts: Array<MessagePart<TTools>>\n createdAt?: Date\n}\n\nexport interface ChatClientOptions<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n> {\n /**\n * Connection adapter for streaming\n * Use fetchServerSentEvents(), fetchHttpStream(), or stream() to create adapters\n */\n connection: ConnectionAdapter\n\n /**\n * Initial messages to populate the chat\n */\n initialMessages?: Array<UIMessage<TTools>>\n\n /**\n * Unique identifier for this chat instance\n * Used for managing multiple chats\n */\n id?: string\n\n /**\n * Additional body parameters to send\n */\n body?: Record<string, any>\n\n /**\n * Callback when a response is received\n */\n onResponse?: (response?: Response) => void | Promise<void>\n\n /**\n * Callback when a stream chunk is received\n */\n onChunk?: (chunk: StreamChunk) => void\n\n /**\n * Callback when the response is finished\n */\n onFinish?: (message: UIMessage<TTools>) => void\n\n /**\n * Callback when an error occurs\n */\n onError?: (error: Error) => void\n\n /**\n * Callback when messages change\n */\n onMessagesChange?: (messages: Array<UIMessage<TTools>>) => void\n\n /**\n * Callback when loading state changes\n */\n onLoadingChange?: (isLoading: boolean) => void\n\n /**\n * Callback when error state changes\n */\n onErrorChange?: (error: Error | undefined) => void\n\n /**\n * Callback when chat status changes\n */\n onStatusChange?: (status: ChatClientState) => void\n\n /**\n * Callback when a custom event is received from a server-side tool.\n * Custom events are emitted by tools using `context.emitCustomEvent()` during execution.\n *\n * @param eventType - The name of the custom event\n * @param data - The event payload data\n * @param context - Additional context including the toolCallId that emitted the event\n */\n onCustomEvent?: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n\n /**\n * Client-side tools with execution logic\n * When provided, tools with execute functions will be called automatically\n */\n tools?: TTools\n\n /**\n * Stream processing options (optional)\n * Configure chunking strategy\n */\n streamProcessor?: {\n /**\n * Strategy for when to emit text updates\n * Defaults to ImmediateStrategy (every chunk)\n */\n chunkStrategy?: ChunkStrategy\n }\n}\n\nexport interface ChatRequestBody {\n messages: Array<ModelMessage>\n data?: Record<string, any>\n}\n\n/**\n * Create a typed array of client tools with proper type inference.\n * This eliminates the need for `as const` when defining tool arrays.\n *\n * @example\n * ```ts\n * const tools = clientTools(\n * myTool1.client(() => result1),\n * myTool2.client(() => result2),\n * )\n *\n * // tools is now properly typed as a tuple with literal tool names\n * // This enables type narrowing when checking part.name === 'toolName'\n * ```\n */\nexport function clientTools<const T extends Array<AnyClientTool>>(\n ...tools: T\n): T {\n return tools\n}\n\n/**\n * Helper to create typed chat client options\n * Use this to get proper type inference for messages\n *\n * @example\n * ```ts\n * const tools = clientTools(myTool1, myTool2)\n *\n * const chatOptions = createChatClientOptions({\n * connection: fetchServerSentEvents('/api/chat'),\n * tools,\n * })\n *\n * type MyMessages = InferChatMessages<typeof chatOptions>\n * ```\n */\nexport function createChatClientOptions<\n const TTools extends ReadonlyArray<AnyClientTool>,\n>(options: ChatClientOptions<TTools>): ChatClientOptions<TTools> {\n return options\n}\n\n/**\n * Extract the message type from chat options\n *\n * @example\n * ```ts\n * const chatOptions = createChatClientOptions({\n * connection: fetchServerSentEvents('/api/chat'),\n * tools: [myTool1, myTool2],\n * })\n *\n * type MyMessages = InferChatMessages<typeof chatOptions>\n * // MyMessages is now Array<UIMessage<[typeof myTool1, typeof myTool2]>>\n * ```\n */\nexport type InferChatMessages<T> =\n T extends ChatClientOptions<infer TTools> ? Array<UIMessage<TTools>> : never\n"],"names":[],"mappings":"AAsSO,SAAS,eACX,OACA;AACH,SAAO;AACT;AAkBO,SAAS,wBAEd,SAA+D;AAC/D,SAAO;AACT;"}
|
|
1
|
+
{"version":3,"file":"types.js","sources":["../../src/types.ts"],"sourcesContent":["import type {\n AnyClientTool,\n AudioPart,\n ChunkStrategy,\n ContentPart,\n DocumentPart,\n ImagePart,\n InferToolInput,\n InferToolOutput,\n ModelMessage,\n StreamChunk,\n VideoPart,\n} from '@tanstack/ai'\nimport type { ConnectionAdapter } from './connection-adapters'\n\n/**\n * Tool call states - track the lifecycle of a tool call\n */\nexport type ToolCallState =\n | 'awaiting-input' // Received start but no arguments yet\n | 'input-streaming' // Partial arguments received\n | 'input-complete' // All arguments received\n | 'approval-requested' // Waiting for user approval\n | 'approval-responded' // User has approved/denied\n\n/**\n * Tool result states - track the lifecycle of a tool result\n */\nexport type ToolResultState =\n | 'streaming' // Placeholder for future streamed output\n | 'complete' // Result is complete\n | 'error' // Error occurred\n\n/**\n * ChatClient state - track the lifecycle of a chat\n */\nexport type ChatClientState = 'ready' | 'submitted' | 'streaming' | 'error'\n\n/**\n * Connection lifecycle state for the subscription loop.\n */\nexport type ConnectionStatus =\n | 'disconnected'\n | 'connecting'\n | 'connected'\n | 'error'\n\n/**\n * Multimodal content input for sending messages with rich media.\n * Allows sending text, images, audio, video, and documents to the LLM.\n *\n * @example\n * ```ts\n * // Send an image with a question\n * client.sendMessage({\n * content: [\n * { type: 'text', content: 'What is in this image?' },\n * { type: 'image', source: { type: 'url', value: 'https://example.com/photo.jpg' } }\n * ],\n * id: 'custom-message-id' // optional\n * })\n * ```\n */\nexport interface MultimodalContent {\n /**\n * The content of the message.\n * Can be a simple string or an array of content parts for multimodal messages.\n */\n content: string | Array<ContentPart>\n /**\n * Optional custom ID for the message.\n * If not provided, a unique ID will be generated.\n */\n id?: string\n}\n\n/**\n * Message parts - building blocks of UIMessage\n */\nexport interface TextPart {\n type: 'text'\n content: string\n}\n\n/**\n * Helper type that creates a tool-call part for a specific tool.\n * This is a conditional type to enable proper distribution over union types,\n * creating a discriminated union where `name` is the discriminant.\n */\ntype ToolCallPartForTool<T> = T extends AnyClientTool\n ? {\n type: 'tool-call'\n id: string\n name: T['name']\n arguments: string // JSON string (may be incomplete)\n /** Parsed tool input (typed from inputSchema) */\n input?: InferToolInput<T>\n state: ToolCallState\n /** Approval metadata if tool requires user approval */\n approval?: {\n id: string // Unique approval ID\n needsApproval: boolean // Always true if present\n approved?: boolean // User's decision (undefined until responded)\n }\n /** Tool execution output (for client tools or after approval) */\n output?: InferToolOutput<T>\n }\n : never\n\n/**\n * Fallback tool-call part type when tools are not typed\n */\ntype UntypedToolCallPart = {\n type: 'tool-call'\n id: string\n name: string\n arguments: string\n input?: any\n state: ToolCallState\n approval?: {\n id: string\n needsApproval: boolean\n approved?: boolean\n }\n output?: any\n}\n\n/**\n * Tool call part that creates a proper discriminated union.\n * When TTools is typed, checking `part.name === 'toolName'` will narrow\n * `part.output` to the correct type for that tool.\n *\n * The discriminant is `name`, so code like:\n * ```ts\n * if (part.name === 'recommendGuitar') {\n * // part.output is now typed to the recommendGuitar tool's output\n * }\n * ```\n */\nexport type ToolCallPart<TTools extends ReadonlyArray<AnyClientTool> = any> =\n // Check if we have a concrete tools array (not 'any' or 'never')\n [TTools] extends [never]\n ? UntypedToolCallPart\n : unknown extends TTools\n ? UntypedToolCallPart\n : TTools extends ReadonlyArray<infer Tool>\n ? Tool extends AnyClientTool\n ? ToolCallPartForTool<Tool>\n : UntypedToolCallPart\n : UntypedToolCallPart\n\nexport interface ToolResultPart {\n type: 'tool-result'\n toolCallId: string\n content: string\n state: ToolResultState\n error?: string // Error message if state is \"error\"\n}\n\nexport interface ThinkingPart {\n type: 'thinking'\n content: string\n}\n\nexport type MessagePart<TTools extends ReadonlyArray<AnyClientTool> = any> =\n | TextPart\n | ImagePart\n | AudioPart\n | VideoPart\n | DocumentPart\n | ToolCallPart<TTools>\n | ToolResultPart\n | ThinkingPart\n\n/**\n * UIMessage - Domain-specific message format optimized for building chat UIs\n * Contains parts that can be text, tool calls, or tool results\n */\nexport interface UIMessage<TTools extends ReadonlyArray<AnyClientTool> = any> {\n id: string\n role: 'system' | 'user' | 'assistant'\n parts: Array<MessagePart<TTools>>\n createdAt?: Date\n}\n\nexport interface ChatClientOptions<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n> {\n /**\n * Connection adapter for streaming.\n * Supports mutually exclusive modes: request-response via `connect()`, or\n * subscribe/send mode via `subscribe()` + `send()`.\n */\n connection: ConnectionAdapter\n\n /**\n * Initial messages to populate the chat\n */\n initialMessages?: Array<UIMessage<TTools>>\n\n /**\n * Unique identifier for this chat instance\n * Used for managing multiple chats\n */\n id?: string\n\n /**\n * Additional body parameters to send\n */\n body?: Record<string, any>\n\n /**\n * Callback when a response is received\n */\n onResponse?: (response?: Response) => void | Promise<void>\n\n /**\n * Callback when a stream chunk is received\n */\n onChunk?: (chunk: StreamChunk) => void\n\n /**\n * Callback when the response is finished\n */\n onFinish?: (message: UIMessage<TTools>) => void\n\n /**\n * Callback when an error occurs\n */\n onError?: (error: Error) => void\n\n /**\n * Callback when messages change\n */\n onMessagesChange?: (messages: Array<UIMessage<TTools>>) => void\n\n /**\n * Callback when loading state changes\n */\n onLoadingChange?: (isLoading: boolean) => void\n\n /**\n * Callback when error state changes\n */\n onErrorChange?: (error: Error | undefined) => void\n\n /**\n * Callback when chat status changes\n */\n onStatusChange?: (status: ChatClientState) => void\n\n /**\n * Callback when subscription lifecycle changes.\n * This is independent from request lifecycle (`isLoading`, `status`).\n */\n onSubscriptionChange?: (isSubscribed: boolean) => void\n\n /**\n * Callback when connection lifecycle changes.\n */\n onConnectionStatusChange?: (status: ConnectionStatus) => void\n\n /**\n * Callback when session generation activity changes.\n * Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).\n * Unlike `onLoadingChange` (request-local), this reflects shared generation\n * activity visible to all subscribers (e.g. across tabs/devices).\n */\n onSessionGeneratingChange?: (isGenerating: boolean) => void\n\n /**\n * Callback when a custom event is received from a server-side tool.\n * Custom events are emitted by tools using `context.emitCustomEvent()` during execution.\n *\n * @param eventType - The name of the custom event\n * @param data - The event payload data\n * @param context - Additional context including the toolCallId that emitted the event\n */\n onCustomEvent?: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n\n /**\n * Client-side tools with execution logic\n * When provided, tools with execute functions will be called automatically\n */\n tools?: TTools\n\n /**\n * Stream processing options (optional)\n * Configure chunking strategy\n */\n streamProcessor?: {\n /**\n * Strategy for when to emit text updates\n * Defaults to ImmediateStrategy (every chunk)\n */\n chunkStrategy?: ChunkStrategy\n }\n}\n\nexport interface ChatRequestBody {\n messages: Array<ModelMessage>\n data?: Record<string, any>\n}\n\n/**\n * Create a typed array of client tools with proper type inference.\n * This eliminates the need for `as const` when defining tool arrays.\n *\n * @example\n * ```ts\n * const tools = clientTools(\n * myTool1.client(() => result1),\n * myTool2.client(() => result2),\n * )\n *\n * // tools is now properly typed as a tuple with literal tool names\n * // This enables type narrowing when checking part.name === 'toolName'\n * ```\n */\nexport function clientTools<const T extends Array<AnyClientTool>>(\n ...tools: T\n): T {\n return tools\n}\n\n/**\n * Helper to create typed chat client options\n * Use this to get proper type inference for messages\n *\n * @example\n * ```ts\n * const tools = clientTools(myTool1, myTool2)\n *\n * const chatOptions = createChatClientOptions({\n * connection: fetchServerSentEvents('/api/chat'),\n * tools,\n * })\n *\n * type MyMessages = InferChatMessages<typeof chatOptions>\n * ```\n */\nexport function createChatClientOptions<\n const TTools extends ReadonlyArray<AnyClientTool>,\n>(options: ChatClientOptions<TTools>): ChatClientOptions<TTools> {\n return options\n}\n\n/**\n * Extract the message type from chat options\n *\n * @example\n * ```ts\n * const chatOptions = createChatClientOptions({\n * connection: fetchServerSentEvents('/api/chat'),\n * tools: [myTool1, myTool2],\n * })\n *\n * type MyMessages = InferChatMessages<typeof chatOptions>\n * // MyMessages is now Array<UIMessage<[typeof myTool1, typeof myTool2]>>\n * ```\n */\nexport type InferChatMessages<T> =\n T extends ChatClientOptions<infer TTools> ? Array<UIMessage<TTools>> : never\n"],"names":[],"mappings":"AAmUO,SAAS,eACX,OACA;AACH,SAAO;AACT;AAkBO,SAAS,wBAEd,SAA+D;AAC/D,SAAO;AACT;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ConnectConnectionAdapter } from './connection-adapters.js';
|
|
2
2
|
import { GenerationClientState, GenerationFetcher, VideoGenerateInput, VideoGenerateResult, VideoGenerationClientOptions, VideoStatusInfo } from './generation-types.js';
|
|
3
3
|
/**
|
|
4
4
|
* A specialized client for job-based video generation.
|
|
@@ -7,14 +7,14 @@ import { GenerationClientState, GenerationFetcher, VideoGenerateInput, VideoGene
|
|
|
7
7
|
* until completion. This client handles the full lifecycle.
|
|
8
8
|
*
|
|
9
9
|
* Supports two transport modes:
|
|
10
|
-
* - **
|
|
10
|
+
* - **ConnectConnectionAdapter** — Server handles the polling loop internally and
|
|
11
11
|
* streams status updates via CUSTOM events.
|
|
12
12
|
* - **Fetcher** — Direct async function that returns a completed
|
|
13
13
|
* `VideoGenerateResult`.
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* ```typescript
|
|
17
|
-
* // With
|
|
17
|
+
* // With streaming connection adapter (server-driven polling)
|
|
18
18
|
* const client = new VideoGenerationClient({
|
|
19
19
|
* connection: fetchServerSentEvents('/api/generate/video'),
|
|
20
20
|
* onResultChange: setResult,
|
|
@@ -48,7 +48,7 @@ export declare class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
48
48
|
private abortController;
|
|
49
49
|
private callbacksRef;
|
|
50
50
|
constructor(options: VideoGenerationClientOptions<TOutput> & ({
|
|
51
|
-
connection:
|
|
51
|
+
connection: ConnectConnectionAdapter;
|
|
52
52
|
fetcher?: never;
|
|
53
53
|
} | {
|
|
54
54
|
fetcher: GenerationFetcher<VideoGenerateInput, VideoGenerateResult>;
|
|
@@ -64,7 +64,7 @@ export declare class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
64
64
|
*/
|
|
65
65
|
private generateWithFetcher;
|
|
66
66
|
/**
|
|
67
|
-
* Process a stream of AG-UI events from the
|
|
67
|
+
* Process a stream of AG-UI events from the streaming connection adapter.
|
|
68
68
|
* The server handles the polling loop and streams status updates.
|
|
69
69
|
*/
|
|
70
70
|
private processStream;
|
|
@@ -79,7 +79,7 @@ class VideoGenerationClient {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
* Process a stream of AG-UI events from the
|
|
82
|
+
* Process a stream of AG-UI events from the streaming connection adapter.
|
|
83
83
|
* The server handles the polling loop and streams status updates.
|
|
84
84
|
*/
|
|
85
85
|
async processStream(source) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-generation-client.js","sources":["../../src/video-generation-client.ts"],"sourcesContent":["import { GENERATION_EVENTS } from './generation-types'\nimport { parseSSEResponse } from './sse-parser'\nimport type { StreamChunk } from '@tanstack/ai'\nimport type { ConnectionAdapter } from './connection-adapters'\nimport type {\n GenerationClientState,\n GenerationFetcher,\n VideoGenerateInput,\n VideoGenerateResult,\n VideoGenerationClientOptions,\n VideoStatusInfo,\n} from './generation-types'\n\n/**\n * Callbacks stored in a ref so hooks can update them without recreating the client.\n */\ninterface VideoCallbacks<TOutput> {\n onResult?: (result: VideoGenerateResult) => TOutput | null | void\n onError?: (error: Error) => void\n onProgress?: (progress: number, message?: string) => void\n onChunk?: (chunk: StreamChunk) => void\n onJobCreated?: (jobId: string) => void\n onStatusUpdate?: (status: VideoStatusInfo) => void\n onResultChange?: (result: TOutput | null) => void\n onLoadingChange?: (isLoading: boolean) => void\n onErrorChange?: (error: Error | undefined) => void\n onStatusChange?: (status: GenerationClientState) => void\n onJobIdChange?: (jobId: string | null) => void\n onVideoStatusChange?: (status: VideoStatusInfo | null) => void\n}\n\n/**\n * A specialized client for job-based video generation.\n *\n * Video generation is asynchronous: a job is created, then polled for status\n * until completion. This client handles the full lifecycle.\n *\n * Supports two transport modes:\n * - **ConnectionAdapter** — Server handles the polling loop internally and\n * streams status updates via CUSTOM events.\n * - **Fetcher** — Direct async function that returns a completed\n * `VideoGenerateResult`.\n *\n * @example\n * ```typescript\n * // With ConnectionAdapter (server-driven polling)\n * const client = new VideoGenerationClient({\n * connection: fetchServerSentEvents('/api/generate/video'),\n * onResultChange: setResult,\n * onVideoStatusChange: setVideoStatus,\n * })\n *\n * // With fetcher (direct result)\n * const client = new VideoGenerationClient({\n * fetcher: async (input) => {\n * const res = await fetch('/api/video/generate', {\n * method: 'POST',\n * body: JSON.stringify(input),\n * })\n * return res.json() // { jobId, status: 'completed', url, expiresAt }\n * },\n * })\n *\n * await client.generate({ prompt: 'A flying car over a city' })\n * ```\n */\nexport class VideoGenerationClient<TOutput = VideoGenerateResult> {\n private connection: ConnectionAdapter | undefined\n private fetcher:\n | GenerationFetcher<VideoGenerateInput, VideoGenerateResult>\n | undefined\n private body: Record<string, any>\n\n private result: TOutput | null = null\n private jobId: string | null = null\n private videoStatus: VideoStatusInfo | null = null\n private isLoading = false\n private error: Error | undefined = undefined\n private status: GenerationClientState = 'idle'\n private abortController: AbortController | null = null\n private callbacksRef: VideoCallbacks<TOutput>\n\n constructor(\n options: VideoGenerationClientOptions<TOutput> &\n (\n | { connection: ConnectionAdapter; fetcher?: never }\n | {\n fetcher: GenerationFetcher<VideoGenerateInput, VideoGenerateResult>\n connection?: never\n }\n ),\n ) {\n this.connection = options.connection\n this.fetcher = options.fetcher\n this.body = options.body ?? {}\n\n this.callbacksRef = {\n onResult: options.onResult,\n onError: options.onError,\n onProgress: options.onProgress,\n onChunk: options.onChunk,\n onJobCreated: options.onJobCreated,\n onStatusUpdate: options.onStatusUpdate,\n onResultChange: options.onResultChange,\n onLoadingChange: options.onLoadingChange,\n onErrorChange: options.onErrorChange,\n onStatusChange: options.onStatusChange,\n onJobIdChange: options.onJobIdChange,\n onVideoStatusChange: options.onVideoStatusChange,\n }\n }\n\n /**\n * Trigger video generation.\n * Only one generation can be in-flight at a time.\n */\n async generate(input: VideoGenerateInput): Promise<void> {\n if (this.isLoading) return\n\n this.setIsLoading(true)\n this.setStatus('generating')\n this.setError(undefined)\n this.setJobId(null)\n this.setVideoStatus(null)\n\n const abortController = new AbortController()\n this.abortController = abortController\n const { signal } = abortController\n\n try {\n if (this.fetcher) {\n await this.generateWithFetcher(input, signal)\n } else if (this.connection) {\n const mergedData = { ...this.body, ...input }\n const stream = this.connection.connect([], mergedData, signal)\n await this.processStream(stream)\n } else {\n throw new Error(\n 'VideoGenerationClient requires either a connection or fetcher option',\n )\n }\n } catch (err: any) {\n if (signal.aborted) return\n const error = err instanceof Error ? err : new Error(String(err))\n this.setError(error)\n this.setStatus('error')\n this.callbacksRef.onError?.(error)\n } finally {\n this.abortController = null\n this.setIsLoading(false)\n }\n }\n\n /**\n * Direct fetcher mode: call fetcher and set result.\n */\n private async generateWithFetcher(\n input: VideoGenerateInput,\n signal: AbortSignal,\n ): Promise<void> {\n if (!this.fetcher) return\n\n // Fetcher returns a completed result directly, or a Response with SSE body\n const result = await this.fetcher(input, { signal })\n if (signal.aborted) return\n\n if (result instanceof Response) {\n // Server function returned SSE Response — parse stream\n await this.processStream(parseSSEResponse(result, signal))\n } else {\n this.setResult(result)\n this.setStatus('success')\n }\n }\n\n /**\n * Process a stream of AG-UI events from the ConnectionAdapter.\n * The server handles the polling loop and streams status updates.\n */\n private async processStream(\n source: AsyncIterable<StreamChunk>,\n ): Promise<void> {\n for await (const chunk of source) {\n if (this.abortController?.signal.aborted) break\n\n this.callbacksRef.onChunk?.(chunk)\n\n switch (chunk.type) {\n case 'CUSTOM': {\n if (chunk.name === GENERATION_EVENTS.VIDEO_JOB_CREATED) {\n const { jobId } = chunk.value as { jobId: string }\n this.setJobId(jobId)\n this.callbacksRef.onJobCreated?.(jobId)\n } else if (chunk.name === GENERATION_EVENTS.VIDEO_STATUS) {\n const statusInfo = chunk.value as VideoStatusInfo\n this.setVideoStatus(statusInfo)\n this.callbacksRef.onStatusUpdate?.(statusInfo)\n if (statusInfo.progress !== undefined) {\n this.callbacksRef.onProgress?.(statusInfo.progress)\n }\n } else if (chunk.name === GENERATION_EVENTS.RESULT) {\n this.setResult(chunk.value as VideoGenerateResult)\n } else if (chunk.name === GENERATION_EVENTS.PROGRESS) {\n const { progress, message } = chunk.value as {\n progress: number\n message?: string\n }\n this.callbacksRef.onProgress?.(progress, message)\n }\n break\n }\n case 'RUN_FINISHED': {\n this.setStatus('success')\n break\n }\n case 'RUN_ERROR': {\n throw new Error(chunk.error.message)\n }\n }\n }\n }\n\n /**\n * Abort any in-flight generation or polling.\n */\n stop(): void {\n if (this.abortController) {\n this.abortController.abort()\n this.abortController = null\n }\n this.setIsLoading(false)\n if (this.status === 'generating') {\n this.setStatus('idle')\n }\n }\n\n /**\n * Clear all state and return to idle.\n */\n reset(): void {\n this.stop()\n this.setResult(null)\n this.setJobId(null)\n this.setVideoStatus(null)\n this.setError(undefined)\n this.setStatus('idle')\n }\n\n /**\n * Update options without recreating the client.\n */\n updateOptions(\n options: Partial<\n Pick<\n VideoGenerationClientOptions<TOutput>,\n | 'body'\n | 'onResult'\n | 'onError'\n | 'onProgress'\n | 'onChunk'\n | 'onJobCreated'\n | 'onStatusUpdate'\n >\n >,\n ): void {\n if (options.body !== undefined) {\n this.body = options.body ?? {}\n }\n if (options.onResult !== undefined) {\n this.callbacksRef.onResult = options.onResult\n }\n if (options.onError !== undefined) {\n this.callbacksRef.onError = options.onError\n }\n if (options.onProgress !== undefined) {\n this.callbacksRef.onProgress = options.onProgress\n }\n if (options.onChunk !== undefined) {\n this.callbacksRef.onChunk = options.onChunk\n }\n if (options.onJobCreated !== undefined) {\n this.callbacksRef.onJobCreated = options.onJobCreated\n }\n if (options.onStatusUpdate !== undefined) {\n this.callbacksRef.onStatusUpdate = options.onStatusUpdate\n }\n }\n\n // ===========================\n // Getters\n // ===========================\n\n getResult(): TOutput | null {\n return this.result\n }\n\n getJobId(): string | null {\n return this.jobId\n }\n\n getVideoStatus(): VideoStatusInfo | null {\n return this.videoStatus\n }\n\n getIsLoading(): boolean {\n return this.isLoading\n }\n\n getError(): Error | undefined {\n return this.error\n }\n\n getStatus(): GenerationClientState {\n return this.status\n }\n\n // ===========================\n // Private state setters\n // ===========================\n\n private setResult(rawResult: VideoGenerateResult | null): void {\n if (rawResult === null) {\n this.result = null\n this.callbacksRef.onResultChange?.(null)\n return\n }\n\n if (this.callbacksRef.onResult) {\n const transformed = this.callbacksRef.onResult(rawResult)\n if (transformed === null) {\n // null return → keep previous result unchanged\n return\n }\n if (transformed !== undefined) {\n // Non-null, non-undefined → use transformed value\n this.result = transformed\n this.callbacksRef.onResultChange?.(this.result)\n return\n }\n }\n\n // No onResult callback, or callback returned void → use raw value\n this.result = rawResult as unknown as TOutput\n this.callbacksRef.onResultChange?.(this.result)\n }\n\n private setJobId(jobId: string | null): void {\n this.jobId = jobId\n this.callbacksRef.onJobIdChange?.(jobId)\n }\n\n private setVideoStatus(status: VideoStatusInfo | null): void {\n this.videoStatus = status\n this.callbacksRef.onVideoStatusChange?.(status)\n }\n\n private setIsLoading(isLoading: boolean): void {\n this.isLoading = isLoading\n this.callbacksRef.onLoadingChange?.(isLoading)\n }\n\n private setError(error: Error | undefined): void {\n this.error = error\n this.callbacksRef.onErrorChange?.(error)\n }\n\n private setStatus(status: GenerationClientState): void {\n this.status = status\n this.callbacksRef.onStatusChange?.(status)\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"video-generation-client.js","sources":["../../src/video-generation-client.ts"],"sourcesContent":["import { GENERATION_EVENTS } from './generation-types'\nimport { parseSSEResponse } from './sse-parser'\nimport type { StreamChunk } from '@tanstack/ai'\nimport type { ConnectConnectionAdapter } from './connection-adapters'\nimport type {\n GenerationClientState,\n GenerationFetcher,\n VideoGenerateInput,\n VideoGenerateResult,\n VideoGenerationClientOptions,\n VideoStatusInfo,\n} from './generation-types'\n\n/**\n * Callbacks stored in a ref so hooks can update them without recreating the client.\n */\ninterface VideoCallbacks<TOutput> {\n onResult?: (result: VideoGenerateResult) => TOutput | null | void\n onError?: (error: Error) => void\n onProgress?: (progress: number, message?: string) => void\n onChunk?: (chunk: StreamChunk) => void\n onJobCreated?: (jobId: string) => void\n onStatusUpdate?: (status: VideoStatusInfo) => void\n onResultChange?: (result: TOutput | null) => void\n onLoadingChange?: (isLoading: boolean) => void\n onErrorChange?: (error: Error | undefined) => void\n onStatusChange?: (status: GenerationClientState) => void\n onJobIdChange?: (jobId: string | null) => void\n onVideoStatusChange?: (status: VideoStatusInfo | null) => void\n}\n\n/**\n * A specialized client for job-based video generation.\n *\n * Video generation is asynchronous: a job is created, then polled for status\n * until completion. This client handles the full lifecycle.\n *\n * Supports two transport modes:\n * - **ConnectConnectionAdapter** — Server handles the polling loop internally and\n * streams status updates via CUSTOM events.\n * - **Fetcher** — Direct async function that returns a completed\n * `VideoGenerateResult`.\n *\n * @example\n * ```typescript\n * // With streaming connection adapter (server-driven polling)\n * const client = new VideoGenerationClient({\n * connection: fetchServerSentEvents('/api/generate/video'),\n * onResultChange: setResult,\n * onVideoStatusChange: setVideoStatus,\n * })\n *\n * // With fetcher (direct result)\n * const client = new VideoGenerationClient({\n * fetcher: async (input) => {\n * const res = await fetch('/api/video/generate', {\n * method: 'POST',\n * body: JSON.stringify(input),\n * })\n * return res.json() // { jobId, status: 'completed', url, expiresAt }\n * },\n * })\n *\n * await client.generate({ prompt: 'A flying car over a city' })\n * ```\n */\nexport class VideoGenerationClient<TOutput = VideoGenerateResult> {\n private connection: ConnectConnectionAdapter | undefined\n private fetcher:\n | GenerationFetcher<VideoGenerateInput, VideoGenerateResult>\n | undefined\n private body: Record<string, any>\n\n private result: TOutput | null = null\n private jobId: string | null = null\n private videoStatus: VideoStatusInfo | null = null\n private isLoading = false\n private error: Error | undefined = undefined\n private status: GenerationClientState = 'idle'\n private abortController: AbortController | null = null\n private callbacksRef: VideoCallbacks<TOutput>\n\n constructor(\n options: VideoGenerationClientOptions<TOutput> &\n (\n | { connection: ConnectConnectionAdapter; fetcher?: never }\n | {\n fetcher: GenerationFetcher<VideoGenerateInput, VideoGenerateResult>\n connection?: never\n }\n ),\n ) {\n this.connection = options.connection\n this.fetcher = options.fetcher\n this.body = options.body ?? {}\n\n this.callbacksRef = {\n onResult: options.onResult,\n onError: options.onError,\n onProgress: options.onProgress,\n onChunk: options.onChunk,\n onJobCreated: options.onJobCreated,\n onStatusUpdate: options.onStatusUpdate,\n onResultChange: options.onResultChange,\n onLoadingChange: options.onLoadingChange,\n onErrorChange: options.onErrorChange,\n onStatusChange: options.onStatusChange,\n onJobIdChange: options.onJobIdChange,\n onVideoStatusChange: options.onVideoStatusChange,\n }\n }\n\n /**\n * Trigger video generation.\n * Only one generation can be in-flight at a time.\n */\n async generate(input: VideoGenerateInput): Promise<void> {\n if (this.isLoading) return\n\n this.setIsLoading(true)\n this.setStatus('generating')\n this.setError(undefined)\n this.setJobId(null)\n this.setVideoStatus(null)\n\n const abortController = new AbortController()\n this.abortController = abortController\n const { signal } = abortController\n\n try {\n if (this.fetcher) {\n await this.generateWithFetcher(input, signal)\n } else if (this.connection) {\n const mergedData = { ...this.body, ...input }\n const stream = this.connection.connect([], mergedData, signal)\n await this.processStream(stream)\n } else {\n throw new Error(\n 'VideoGenerationClient requires either a connection or fetcher option',\n )\n }\n } catch (err: any) {\n if (signal.aborted) return\n const error = err instanceof Error ? err : new Error(String(err))\n this.setError(error)\n this.setStatus('error')\n this.callbacksRef.onError?.(error)\n } finally {\n this.abortController = null\n this.setIsLoading(false)\n }\n }\n\n /**\n * Direct fetcher mode: call fetcher and set result.\n */\n private async generateWithFetcher(\n input: VideoGenerateInput,\n signal: AbortSignal,\n ): Promise<void> {\n if (!this.fetcher) return\n\n // Fetcher returns a completed result directly, or a Response with SSE body\n const result = await this.fetcher(input, { signal })\n if (signal.aborted) return\n\n if (result instanceof Response) {\n // Server function returned SSE Response — parse stream\n await this.processStream(parseSSEResponse(result, signal))\n } else {\n this.setResult(result)\n this.setStatus('success')\n }\n }\n\n /**\n * Process a stream of AG-UI events from the streaming connection adapter.\n * The server handles the polling loop and streams status updates.\n */\n private async processStream(\n source: AsyncIterable<StreamChunk>,\n ): Promise<void> {\n for await (const chunk of source) {\n if (this.abortController?.signal.aborted) break\n\n this.callbacksRef.onChunk?.(chunk)\n\n switch (chunk.type) {\n case 'CUSTOM': {\n if (chunk.name === GENERATION_EVENTS.VIDEO_JOB_CREATED) {\n const { jobId } = chunk.value as { jobId: string }\n this.setJobId(jobId)\n this.callbacksRef.onJobCreated?.(jobId)\n } else if (chunk.name === GENERATION_EVENTS.VIDEO_STATUS) {\n const statusInfo = chunk.value as VideoStatusInfo\n this.setVideoStatus(statusInfo)\n this.callbacksRef.onStatusUpdate?.(statusInfo)\n if (statusInfo.progress !== undefined) {\n this.callbacksRef.onProgress?.(statusInfo.progress)\n }\n } else if (chunk.name === GENERATION_EVENTS.RESULT) {\n this.setResult(chunk.value as VideoGenerateResult)\n } else if (chunk.name === GENERATION_EVENTS.PROGRESS) {\n const { progress, message } = chunk.value as {\n progress: number\n message?: string\n }\n this.callbacksRef.onProgress?.(progress, message)\n }\n break\n }\n case 'RUN_FINISHED': {\n this.setStatus('success')\n break\n }\n case 'RUN_ERROR': {\n throw new Error(chunk.error.message)\n }\n }\n }\n }\n\n /**\n * Abort any in-flight generation or polling.\n */\n stop(): void {\n if (this.abortController) {\n this.abortController.abort()\n this.abortController = null\n }\n this.setIsLoading(false)\n if (this.status === 'generating') {\n this.setStatus('idle')\n }\n }\n\n /**\n * Clear all state and return to idle.\n */\n reset(): void {\n this.stop()\n this.setResult(null)\n this.setJobId(null)\n this.setVideoStatus(null)\n this.setError(undefined)\n this.setStatus('idle')\n }\n\n /**\n * Update options without recreating the client.\n */\n updateOptions(\n options: Partial<\n Pick<\n VideoGenerationClientOptions<TOutput>,\n | 'body'\n | 'onResult'\n | 'onError'\n | 'onProgress'\n | 'onChunk'\n | 'onJobCreated'\n | 'onStatusUpdate'\n >\n >,\n ): void {\n if (options.body !== undefined) {\n this.body = options.body ?? {}\n }\n if (options.onResult !== undefined) {\n this.callbacksRef.onResult = options.onResult\n }\n if (options.onError !== undefined) {\n this.callbacksRef.onError = options.onError\n }\n if (options.onProgress !== undefined) {\n this.callbacksRef.onProgress = options.onProgress\n }\n if (options.onChunk !== undefined) {\n this.callbacksRef.onChunk = options.onChunk\n }\n if (options.onJobCreated !== undefined) {\n this.callbacksRef.onJobCreated = options.onJobCreated\n }\n if (options.onStatusUpdate !== undefined) {\n this.callbacksRef.onStatusUpdate = options.onStatusUpdate\n }\n }\n\n // ===========================\n // Getters\n // ===========================\n\n getResult(): TOutput | null {\n return this.result\n }\n\n getJobId(): string | null {\n return this.jobId\n }\n\n getVideoStatus(): VideoStatusInfo | null {\n return this.videoStatus\n }\n\n getIsLoading(): boolean {\n return this.isLoading\n }\n\n getError(): Error | undefined {\n return this.error\n }\n\n getStatus(): GenerationClientState {\n return this.status\n }\n\n // ===========================\n // Private state setters\n // ===========================\n\n private setResult(rawResult: VideoGenerateResult | null): void {\n if (rawResult === null) {\n this.result = null\n this.callbacksRef.onResultChange?.(null)\n return\n }\n\n if (this.callbacksRef.onResult) {\n const transformed = this.callbacksRef.onResult(rawResult)\n if (transformed === null) {\n // null return → keep previous result unchanged\n return\n }\n if (transformed !== undefined) {\n // Non-null, non-undefined → use transformed value\n this.result = transformed\n this.callbacksRef.onResultChange?.(this.result)\n return\n }\n }\n\n // No onResult callback, or callback returned void → use raw value\n this.result = rawResult as unknown as TOutput\n this.callbacksRef.onResultChange?.(this.result)\n }\n\n private setJobId(jobId: string | null): void {\n this.jobId = jobId\n this.callbacksRef.onJobIdChange?.(jobId)\n }\n\n private setVideoStatus(status: VideoStatusInfo | null): void {\n this.videoStatus = status\n this.callbacksRef.onVideoStatusChange?.(status)\n }\n\n private setIsLoading(isLoading: boolean): void {\n this.isLoading = isLoading\n this.callbacksRef.onLoadingChange?.(isLoading)\n }\n\n private setError(error: Error | undefined): void {\n this.error = error\n this.callbacksRef.onErrorChange?.(error)\n }\n\n private setStatus(status: GenerationClientState): void {\n this.status = status\n this.callbacksRef.onStatusChange?.(status)\n }\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-client",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "Framework-agnostic headless client for TanStack AI",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"src"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@tanstack/ai": "0.
|
|
35
|
-
"@tanstack/ai-event-client": "0.1.
|
|
34
|
+
"@tanstack/ai": "0.9.1",
|
|
35
|
+
"@tanstack/ai-event-client": "0.1.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@vitest/coverage-v8": "4.0.14",
|