@tanstack/ai 0.6.2 → 0.6.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/activities/generateVideo/index.ts"],"sourcesContent":["/**\n * Video Activity (Experimental)\n *\n * Generates videos from text prompts using a jobs/polling architecture.\n * This is a self-contained module with implementation, types, and JSDoc.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\n\nimport { aiEventClient } from '../../event-client.js'\nimport type { VideoAdapter } from './adapter'\nimport type {\n VideoJobResult,\n VideoStatusResult,\n VideoUrlResult,\n} from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'video' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/**\n * Extract provider options from a VideoAdapter via ~types.\n */\nexport type VideoProviderOptions<TAdapter> =\n TAdapter extends VideoAdapter<any, any, any, any>\n ? TAdapter['~types']['providerOptions']\n : object\n\n/**\n * Extract the size type for a VideoAdapter's model via ~types.\n */\nexport type VideoSizeForAdapter<TAdapter> =\n TAdapter extends VideoAdapter<infer TModel, any, any, infer TSizeMap>\n ? TModel extends keyof TSizeMap\n ? TSizeMap[TModel]\n : string\n : string\n\n// ===========================\n// Activity Options Types\n\nfunction createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n// ===========================\n\n/**\n * Base options shared by all video activity operations.\n * The model is extracted from the adapter's model property.\n */\ninterface VideoActivityBaseOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n> {\n /** The video adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\n}\n\n/**\n * Options for creating a new video generation job.\n * The model is extracted from the adapter's model property.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type VideoCreateOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n> = VideoActivityBaseOptions<TAdapter> & {\n /** Request type - create a new job (default if not specified) */\n request?: 'create'\n /** Text description of the desired video */\n prompt: string\n /** Video size — format depends on the provider (e.g., \"16:9\", \"1280x720\") */\n size?: VideoSizeForAdapter<TAdapter>\n /** Video duration in seconds */\n duration?: number\n} & ({} extends VideoProviderOptions<TAdapter>\n ? {\n /** Provider-specific options for video generation */ modelOptions?: VideoProviderOptions<TAdapter>\n }\n : {\n /** Provider-specific options for video generation */ modelOptions: VideoProviderOptions<TAdapter>\n })\n\n/**\n * Options for polling the status of a video generation job.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport interface VideoStatusOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n> extends VideoActivityBaseOptions<TAdapter> {\n /** Request type - get job status */\n request: 'status'\n /** The job ID to check status for */\n jobId: string\n}\n\n/**\n * Options for getting the URL of a completed video.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport interface VideoUrlOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n> extends VideoActivityBaseOptions<TAdapter> {\n /** Request type - get video URL */\n request: 'url'\n /** The job ID to get URL for */\n jobId: string\n}\n\n/**\n * Union type for all video activity options.\n * Discriminated by the `request` field.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type VideoActivityOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n TRequest extends 'create' | 'status' | 'url' = 'create',\n> = TRequest extends 'status'\n ? VideoStatusOptions<TAdapter>\n : TRequest extends 'url'\n ? VideoUrlOptions<TAdapter>\n : VideoCreateOptions<TAdapter>\n\n// ===========================\n// Activity Result Types\n// ===========================\n\n/**\n * Result type for the video activity, based on request type.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type VideoActivityResult<\n TRequest extends 'create' | 'status' | 'url' = 'create',\n> = TRequest extends 'status'\n ? Promise<VideoStatusResult>\n : TRequest extends 'url'\n ? Promise<VideoUrlResult>\n : Promise<VideoJobResult>\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * Generate video - creates a video generation job from a text prompt.\n *\n * Uses AI video generation models to create videos based on natural language descriptions.\n * Unlike image generation, video generation is asynchronous and requires polling for completion.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @example Create a video generation job\n * ```ts\n * import { generateVideo } from '@tanstack/ai'\n * import { openaiVideo } from '@tanstack/ai-openai'\n *\n * // Start a video generation job\n * const { jobId } = await generateVideo({\n * adapter: openaiVideo('sora-2'),\n * prompt: 'A cat chasing a dog in a sunny park'\n * })\n *\n * console.log('Job started:', jobId)\n * ```\n */\nexport async function generateVideo<\n TAdapter extends VideoAdapter<string, any, any, any>,\n>(options: VideoCreateOptions<TAdapter>): Promise<VideoJobResult> {\n const { adapter, prompt, size, duration, modelOptions } = options\n const model = adapter.model\n\n return adapter.createVideoJob({\n model,\n prompt,\n size,\n duration,\n modelOptions,\n })\n}\n\n/**\n * Get video job status - returns the current status, progress, and URL if available.\n *\n * This function combines status checking and URL retrieval. If the job is completed,\n * it will automatically fetch and include the video URL.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @example Check job status\n * ```ts\n * import { getVideoJobStatus } from '@tanstack/ai'\n * import { openaiVideo } from '@tanstack/ai-openai'\n *\n * const result = await getVideoJobStatus({\n * adapter: openaiVideo('sora-2'),\n * jobId: 'job-123'\n * })\n *\n * console.log('Status:', result.status)\n * console.log('Progress:', result.progress)\n * if (result.url) {\n * console.log('Video URL:', result.url)\n * }\n * ```\n */\nexport async function getVideoJobStatus<\n TAdapter extends VideoAdapter<string, any, any, any>,\n>(options: {\n adapter: TAdapter & { kind: typeof kind }\n jobId: string\n}): Promise<{\n status: 'pending' | 'processing' | 'completed' | 'failed'\n progress?: number\n url?: string\n error?: string\n}> {\n const { adapter, jobId } = options\n const requestId = createId('video-status')\n const startTime = Date.now()\n\n aiEventClient.emit('video:request:started', {\n requestId,\n provider: adapter.name,\n model: adapter.model,\n requestType: 'status',\n jobId,\n timestamp: startTime,\n })\n\n // Get status first\n const statusResult = await adapter.getVideoStatus(jobId)\n\n // If completed, also get the URL\n if (statusResult.status === 'completed') {\n try {\n const urlResult = await adapter.getVideoUrl(jobId)\n aiEventClient.emit('video:request:completed', {\n requestId,\n provider: adapter.name,\n model: adapter.model,\n requestType: 'status',\n jobId,\n status: statusResult.status,\n progress: statusResult.progress,\n url: urlResult.url,\n duration: Date.now() - startTime,\n timestamp: Date.now(),\n })\n return {\n status: statusResult.status,\n progress: statusResult.progress,\n url: urlResult.url,\n }\n } catch (error) {\n aiEventClient.emit('video:request:completed', {\n requestId,\n provider: adapter.name,\n model: adapter.model,\n requestType: 'status',\n jobId,\n status: statusResult.status,\n progress: statusResult.progress,\n error:\n error instanceof Error ? error.message : 'Failed to get video URL',\n duration: Date.now() - startTime,\n timestamp: Date.now(),\n })\n // If URL fetch fails, still return status\n return {\n status: statusResult.status,\n progress: statusResult.progress,\n error:\n error instanceof Error ? error.message : 'Failed to get video URL',\n }\n }\n }\n\n aiEventClient.emit('video:request:completed', {\n requestId,\n provider: adapter.name,\n model: adapter.model,\n requestType: 'status',\n jobId,\n status: statusResult.status,\n progress: statusResult.progress,\n error: statusResult.error,\n duration: Date.now() - startTime,\n timestamp: Date.now(),\n })\n\n // Return status for non-completed jobs\n return {\n status: statusResult.status,\n progress: statusResult.progress,\n error: statusResult.error,\n }\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the generateVideo() function without executing.\n */\nexport function createVideoOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n>(options: VideoCreateOptions<TAdapter>): VideoCreateOptions<TAdapter> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n VideoAdapter,\n VideoAdapterConfig,\n AnyVideoAdapter,\n} from './adapter'\nexport { BaseVideoAdapter } from './adapter'\n"],"names":[],"mappings":";AAsBO,MAAM,OAAO;AA2BpB,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AA6HA,eAAsB,cAEpB,SAAgE;AAChE,QAAM,EAAE,SAAS,QAAQ,MAAM,UAAU,iBAAiB;AAC1D,QAAM,QAAQ,QAAQ;AAEtB,SAAO,QAAQ,eAAe;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AACH;AA2BA,eAAsB,kBAEpB,SAQC;AACD,QAAM,EAAE,SAAS,MAAA,IAAU;AAC3B,QAAM,YAAY,SAAS,cAAc;AACzC,QAAM,YAAY,KAAK,IAAA;AAEvB,gBAAc,KAAK,yBAAyB;AAAA,IAC1C;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB,OAAO,QAAQ;AAAA,IACf,aAAa;AAAA,IACb;AAAA,IACA,WAAW;AAAA,EAAA,CACZ;AAGD,QAAM,eAAe,MAAM,QAAQ,eAAe,KAAK;AAGvD,MAAI,aAAa,WAAW,aAAa;AACvC,QAAI;AACF,YAAM,YAAY,MAAM,QAAQ,YAAY,KAAK;AACjD,oBAAc,KAAK,2BAA2B;AAAA,QAC5C;AAAA,QACA,UAAU,QAAQ;AAAA,QAClB,OAAO,QAAQ;AAAA,QACf,aAAa;AAAA,QACb;AAAA,QACA,QAAQ,aAAa;AAAA,QACrB,UAAU,aAAa;AAAA,QACvB,KAAK,UAAU;AAAA,QACf,UAAU,KAAK,IAAA,IAAQ;AAAA,QACvB,WAAW,KAAK,IAAA;AAAA,MAAI,CACrB;AACD,aAAO;AAAA,QACL,QAAQ,aAAa;AAAA,QACrB,UAAU,aAAa;AAAA,QACvB,KAAK,UAAU;AAAA,MAAA;AAAA,IAEnB,SAAS,OAAO;AACd,oBAAc,KAAK,2BAA2B;AAAA,QAC5C;AAAA,QACA,UAAU,QAAQ;AAAA,QAClB,OAAO,QAAQ;AAAA,QACf,aAAa;AAAA,QACb;AAAA,QACA,QAAQ,aAAa;AAAA,QACrB,UAAU,aAAa;AAAA,QACvB,OACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAC3C,UAAU,KAAK,IAAA,IAAQ;AAAA,QACvB,WAAW,KAAK,IAAA;AAAA,MAAI,CACrB;AAED,aAAO;AAAA,QACL,QAAQ,aAAa;AAAA,QACrB,UAAU,aAAa;AAAA,QACvB,OACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAAA;AAAA,IAE/C;AAAA,EACF;AAEA,gBAAc,KAAK,2BAA2B;AAAA,IAC5C;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB,OAAO,QAAQ;AAAA,IACf,aAAa;AAAA,IACb;AAAA,IACA,QAAQ,aAAa;AAAA,IACrB,UAAU,aAAa;AAAA,IACvB,OAAO,aAAa;AAAA,IACpB,UAAU,KAAK,IAAA,IAAQ;AAAA,IACvB,WAAW,KAAK,IAAA;AAAA,EAAI,CACrB;AAGD,SAAO;AAAA,IACL,QAAQ,aAAa;AAAA,IACrB,UAAU,aAAa;AAAA,IACvB,OAAO,aAAa;AAAA,EAAA;AAExB;AASO,SAAS,mBAEd,SAAqE;AACrE,SAAO;AACT;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/activities/generateVideo/index.ts"],"sourcesContent":["/**\n * Video Activity (Experimental)\n *\n * Generates videos from text prompts using a jobs/polling architecture.\n * This is a self-contained module with implementation, types, and JSDoc.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\n\nimport { aiEventClient } from '../../event-client.js'\nimport type { VideoAdapter } from './adapter'\nimport type {\n StreamChunk,\n VideoJobResult,\n VideoStatusResult,\n VideoUrlResult,\n} from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'video' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/**\n * Extract provider options from a VideoAdapter via ~types.\n */\nexport type VideoProviderOptions<TAdapter> =\n TAdapter extends VideoAdapter<any, any, any, any>\n ? TAdapter['~types']['providerOptions']\n : object\n\n/**\n * Extract the size type for a VideoAdapter's model via ~types.\n */\nexport type VideoSizeForAdapter<TAdapter> =\n TAdapter extends VideoAdapter<infer TModel, any, any, infer TSizeMap>\n ? TModel extends keyof TSizeMap\n ? TSizeMap[TModel]\n : string\n : string\n\n// ===========================\n// Activity Options Types\n\nfunction createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n// ===========================\n\n/**\n * Base options shared by all video activity operations.\n * The model is extracted from the adapter's model property.\n */\ninterface VideoActivityBaseOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n> {\n /** The video adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\n}\n\n/**\n * Options for creating a new video generation job.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The video adapter type\n * @template TStream - Whether to stream the output\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type VideoCreateOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n TStream extends boolean = false,\n> = VideoActivityBaseOptions<TAdapter> & {\n /** Request type - create a new job (default if not specified) */\n request?: 'create'\n /** Text description of the desired video */\n prompt: string\n /** Video size — format depends on the provider (e.g., \"16:9\", \"1280x720\") */\n size?: VideoSizeForAdapter<TAdapter>\n /** Video duration in seconds */\n duration?: number\n /**\n * Whether to stream the video generation lifecycle.\n * When true, returns an AsyncIterable<StreamChunk> that handles the full\n * job lifecycle: create job, poll for status, yield updates, and yield final result.\n * When false or not provided, returns a Promise<VideoJobResult>.\n *\n * @default false\n */\n stream?: TStream\n /** Polling interval in milliseconds (stream mode only). @default 2000 */\n pollingInterval?: number\n /** Maximum time to wait before timing out in milliseconds (stream mode only). @default 600000 */\n maxDuration?: number\n /** Custom run ID (stream mode only) */\n runId?: string\n} & ({} extends VideoProviderOptions<TAdapter>\n ? {\n /** Provider-specific options for video generation */ modelOptions?: VideoProviderOptions<TAdapter>\n }\n : {\n /** Provider-specific options for video generation */ modelOptions: VideoProviderOptions<TAdapter>\n })\n\n/**\n * Options for polling the status of a video generation job.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport interface VideoStatusOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n> extends VideoActivityBaseOptions<TAdapter> {\n /** Request type - get job status */\n request: 'status'\n /** The job ID to check status for */\n jobId: string\n}\n\n/**\n * Options for getting the URL of a completed video.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport interface VideoUrlOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n> extends VideoActivityBaseOptions<TAdapter> {\n /** Request type - get video URL */\n request: 'url'\n /** The job ID to get URL for */\n jobId: string\n}\n\n/**\n * Union type for all video activity options.\n * Discriminated by the `request` field.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type VideoActivityOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n TRequest extends 'create' | 'status' | 'url' = 'create',\n TStream extends boolean = false,\n> = TRequest extends 'status'\n ? VideoStatusOptions<TAdapter>\n : TRequest extends 'url'\n ? VideoUrlOptions<TAdapter>\n : VideoCreateOptions<TAdapter, TStream>\n\n// ===========================\n// Activity Result Types\n// ===========================\n\n/**\n * Result type for the video activity, based on request type and streaming.\n * - If stream is true (create request): AsyncIterable<StreamChunk>\n * - Otherwise: Promise<VideoJobResult | VideoStatusResult | VideoUrlResult>\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type VideoActivityResult<\n TRequest extends 'create' | 'status' | 'url' = 'create',\n TStream extends boolean = false,\n> = TRequest extends 'status'\n ? Promise<VideoStatusResult>\n : TRequest extends 'url'\n ? Promise<VideoUrlResult>\n : TStream extends true\n ? AsyncIterable<StreamChunk>\n : Promise<VideoJobResult>\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * Generate video - creates a video generation job from a text prompt.\n *\n * Uses AI video generation models to create videos based on natural language descriptions.\n * Unlike image generation, video generation is asynchronous and requires polling for completion.\n *\n * When `stream: true` is passed, handles the full job lifecycle automatically:\n * create job → poll for status → stream updates → yield final result.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @example Create a video generation job\n * ```ts\n * import { generateVideo } from '@tanstack/ai'\n * import { openaiVideo } from '@tanstack/ai-openai'\n *\n * // Start a video generation job\n * const { jobId } = await generateVideo({\n * adapter: openaiVideo('sora-2'),\n * prompt: 'A cat chasing a dog in a sunny park'\n * })\n *\n * console.log('Job started:', jobId)\n * ```\n *\n * @example Stream the full video generation lifecycle\n * ```ts\n * import { generateVideo, toServerSentEventsResponse } from '@tanstack/ai'\n * import { openaiVideo } from '@tanstack/ai-openai'\n *\n * const stream = generateVideo({\n * adapter: openaiVideo('sora-2'),\n * prompt: 'A cat chasing a dog in a sunny park',\n * stream: true,\n * pollingInterval: 3000,\n * })\n *\n * return toServerSentEventsResponse(stream)\n * ```\n */\nexport function generateVideo<\n TAdapter extends VideoAdapter<string, any, any, any>,\n TStream extends boolean = false,\n>(\n options: VideoCreateOptions<TAdapter, TStream>,\n): VideoActivityResult<'create', TStream> {\n if (options.stream) {\n return runStreamingVideoGeneration(\n options as VideoCreateOptions<TAdapter, true>,\n ) as VideoActivityResult<'create', TStream>\n }\n\n return runCreateVideoJob(options) as VideoActivityResult<'create', TStream>\n}\n\n/**\n * Internal implementation of non-streaming video job creation.\n */\nasync function runCreateVideoJob<\n TAdapter extends VideoAdapter<string, any, any, any>,\n>(options: VideoCreateOptions<TAdapter, boolean>): Promise<VideoJobResult> {\n const { adapter, prompt, size, duration, modelOptions } = options\n const model = adapter.model\n\n return adapter.createVideoJob({\n model,\n prompt,\n size,\n duration,\n modelOptions,\n })\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Internal streaming implementation for video generation.\n * Handles the full job lifecycle: create job → poll for status → stream updates → yield final result.\n */\nasync function* runStreamingVideoGeneration<\n TAdapter extends VideoAdapter<string, any, any, any>,\n>(options: VideoCreateOptions<TAdapter, true>): AsyncIterable<StreamChunk> {\n const { adapter, prompt, size, duration, modelOptions } = options\n const model = adapter.model\n const runId = options.runId ?? createId('run')\n const pollingInterval = options.pollingInterval ?? 2000\n const maxDuration = options.maxDuration ?? 600_000\n\n yield {\n type: 'RUN_STARTED',\n runId,\n timestamp: Date.now(),\n }\n\n try {\n // Create the video generation job\n const jobResult = await adapter.createVideoJob({\n model,\n prompt,\n size,\n duration,\n modelOptions,\n })\n\n yield {\n type: 'CUSTOM',\n name: 'video:job:created',\n value: { jobId: jobResult.jobId },\n timestamp: Date.now(),\n }\n\n // Poll for completion\n const startTime = Date.now()\n while (Date.now() - startTime < maxDuration) {\n await sleep(pollingInterval)\n\n const statusResult = await adapter.getVideoStatus(jobResult.jobId)\n\n yield {\n type: 'CUSTOM',\n name: 'video:status',\n value: {\n jobId: jobResult.jobId,\n status: statusResult.status,\n progress: statusResult.progress,\n error: statusResult.error,\n },\n timestamp: Date.now(),\n }\n\n if (statusResult.status === 'completed') {\n const urlResult = await adapter.getVideoUrl(jobResult.jobId)\n\n yield {\n type: 'CUSTOM',\n name: 'generation:result',\n value: {\n jobId: jobResult.jobId,\n status: 'completed',\n url: urlResult.url,\n expiresAt: urlResult.expiresAt,\n },\n timestamp: Date.now(),\n }\n\n yield {\n type: 'RUN_FINISHED',\n runId,\n finishReason: 'stop',\n timestamp: Date.now(),\n }\n return\n }\n\n if (statusResult.status === 'failed') {\n throw new Error(statusResult.error || 'Video generation failed')\n }\n }\n\n throw new Error('Video generation timed out')\n } catch (error: any) {\n yield {\n type: 'RUN_ERROR',\n runId,\n error: {\n message: error.message || 'Video generation failed',\n code: error.code,\n },\n timestamp: Date.now(),\n }\n }\n}\n\n/**\n * Get video job status - returns the current status, progress, and URL if available.\n *\n * This function combines status checking and URL retrieval. If the job is completed,\n * it will automatically fetch and include the video URL.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @example Check job status\n * ```ts\n * import { getVideoJobStatus } from '@tanstack/ai'\n * import { openaiVideo } from '@tanstack/ai-openai'\n *\n * const result = await getVideoJobStatus({\n * adapter: openaiVideo('sora-2'),\n * jobId: 'job-123'\n * })\n *\n * console.log('Status:', result.status)\n * console.log('Progress:', result.progress)\n * if (result.url) {\n * console.log('Video URL:', result.url)\n * }\n * ```\n */\nexport async function getVideoJobStatus<\n TAdapter extends VideoAdapter<string, any, any, any>,\n>(options: {\n adapter: TAdapter & { kind: typeof kind }\n jobId: string\n}): Promise<{\n status: 'pending' | 'processing' | 'completed' | 'failed'\n progress?: number\n url?: string\n error?: string\n}> {\n const { adapter, jobId } = options\n const requestId = createId('video-status')\n const startTime = Date.now()\n\n aiEventClient.emit('video:request:started', {\n requestId,\n provider: adapter.name,\n model: adapter.model,\n requestType: 'status',\n jobId,\n timestamp: startTime,\n })\n\n // Get status first\n const statusResult = await adapter.getVideoStatus(jobId)\n\n // If completed, also get the URL\n if (statusResult.status === 'completed') {\n try {\n const urlResult = await adapter.getVideoUrl(jobId)\n aiEventClient.emit('video:request:completed', {\n requestId,\n provider: adapter.name,\n model: adapter.model,\n requestType: 'status',\n jobId,\n status: statusResult.status,\n progress: statusResult.progress,\n url: urlResult.url,\n duration: Date.now() - startTime,\n timestamp: Date.now(),\n })\n return {\n status: statusResult.status,\n progress: statusResult.progress,\n url: urlResult.url,\n }\n } catch (error) {\n aiEventClient.emit('video:request:completed', {\n requestId,\n provider: adapter.name,\n model: adapter.model,\n requestType: 'status',\n jobId,\n status: statusResult.status,\n progress: statusResult.progress,\n error:\n error instanceof Error ? error.message : 'Failed to get video URL',\n duration: Date.now() - startTime,\n timestamp: Date.now(),\n })\n // If URL fetch fails, still return status\n return {\n status: statusResult.status,\n progress: statusResult.progress,\n error:\n error instanceof Error ? error.message : 'Failed to get video URL',\n }\n }\n }\n\n aiEventClient.emit('video:request:completed', {\n requestId,\n provider: adapter.name,\n model: adapter.model,\n requestType: 'status',\n jobId,\n status: statusResult.status,\n progress: statusResult.progress,\n error: statusResult.error,\n duration: Date.now() - startTime,\n timestamp: Date.now(),\n })\n\n // Return status for non-completed jobs\n return {\n status: statusResult.status,\n progress: statusResult.progress,\n error: statusResult.error,\n }\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the generateVideo() function without executing.\n */\nexport function createVideoOptions<\n TAdapter extends VideoAdapter<string, any, any, any>,\n TStream extends boolean = false,\n>(\n options: VideoCreateOptions<TAdapter, TStream>,\n): VideoCreateOptions<TAdapter, TStream> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n VideoAdapter,\n VideoAdapterConfig,\n AnyVideoAdapter,\n} from './adapter'\nexport { BaseVideoAdapter } from './adapter'\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { StreamChunk } from '../types.js';
2
+ /**
3
+ * Wrap a one-shot generation result as a StreamChunk async iterable.
4
+ *
5
+ * This allows non-streaming activities (image, speech, transcription, summarize)
6
+ * to be sent over the same streaming transport as chat.
7
+ *
8
+ * @param generator - An async function that performs the generation and returns the result
9
+ * @param options - Optional configuration (runId)
10
+ * @returns An AsyncIterable of StreamChunks with RUN_STARTED, CUSTOM(generation:result), and RUN_FINISHED events
11
+ */
12
+ export declare function streamGenerationResult<TResult>(generator: () => Promise<TResult>, options?: {
13
+ runId?: string;
14
+ }): AsyncIterable<StreamChunk>;
@@ -0,0 +1,40 @@
1
+ function createId(prefix) {
2
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
3
+ }
4
+ async function* streamGenerationResult(generator, options) {
5
+ const runId = createId("run");
6
+ yield {
7
+ type: "RUN_STARTED",
8
+ runId,
9
+ timestamp: Date.now()
10
+ };
11
+ try {
12
+ const result = await generator();
13
+ yield {
14
+ type: "CUSTOM",
15
+ name: "generation:result",
16
+ value: result,
17
+ timestamp: Date.now()
18
+ };
19
+ yield {
20
+ type: "RUN_FINISHED",
21
+ runId,
22
+ finishReason: "stop",
23
+ timestamp: Date.now()
24
+ };
25
+ } catch (error) {
26
+ yield {
27
+ type: "RUN_ERROR",
28
+ runId,
29
+ error: {
30
+ message: error.message || "Generation failed",
31
+ code: error.code
32
+ },
33
+ timestamp: Date.now()
34
+ };
35
+ }
36
+ }
37
+ export {
38
+ streamGenerationResult
39
+ };
40
+ //# sourceMappingURL=stream-generation-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-generation-result.js","sources":["../../../src/activities/stream-generation-result.ts"],"sourcesContent":["/**\n * Internal helper for wrapping one-shot generation results as StreamChunk\n * async iterables. NOT exported from the package — used only by activity\n * implementations to support `stream: true`.\n */\n\nimport type { StreamChunk } from '../types'\n\nfunction createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n/**\n * Wrap a one-shot generation result as a StreamChunk async iterable.\n *\n * This allows non-streaming activities (image, speech, transcription, summarize)\n * to be sent over the same streaming transport as chat.\n *\n * @param generator - An async function that performs the generation and returns the result\n * @param options - Optional configuration (runId)\n * @returns An AsyncIterable of StreamChunks with RUN_STARTED, CUSTOM(generation:result), and RUN_FINISHED events\n */\nexport async function* streamGenerationResult<TResult>(\n generator: () => Promise<TResult>,\n options?: { runId?: string },\n): AsyncIterable<StreamChunk> {\n const runId = options?.runId ?? createId('run')\n\n yield {\n type: 'RUN_STARTED',\n runId,\n timestamp: Date.now(),\n }\n\n try {\n const result = await generator()\n\n yield {\n type: 'CUSTOM',\n name: 'generation:result',\n value: result as unknown,\n timestamp: Date.now(),\n }\n\n yield {\n type: 'RUN_FINISHED',\n runId,\n finishReason: 'stop',\n timestamp: Date.now(),\n }\n } catch (error: any) {\n yield {\n type: 'RUN_ERROR',\n runId,\n error: {\n message: error.message || 'Generation failed',\n code: error.code,\n },\n timestamp: Date.now(),\n }\n }\n}\n"],"names":[],"mappings":"AAQA,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAYA,gBAAuB,uBACrB,WACA,SAC4B;AAC5B,QAAM,QAA0B,SAAS,KAAK;AAE9C,QAAM;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA,WAAW,KAAK,IAAA;AAAA,EAAI;AAGtB,MAAI;AACF,UAAM,SAAS,MAAM,UAAA;AAErB,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,WAAW,KAAK,IAAA;AAAA,IAAI;AAGtB,UAAM;AAAA,MACJ,MAAM;AAAA,MACN;AAAA,MACA,cAAc;AAAA,MACd,WAAW,KAAK,IAAA;AAAA,IAAI;AAAA,EAExB,SAAS,OAAY;AACnB,UAAM;AAAA,MACJ,MAAM;AAAA,MACN;AAAA,MACA,OAAO;AAAA,QACL,SAAS,MAAM,WAAW;AAAA,QAC1B,MAAM,MAAM;AAAA,MAAA;AAAA,MAEd,WAAW,KAAK,IAAA;AAAA,IAAI;AAAA,EAExB;AACF;"}
@@ -1,4 +1,5 @@
1
1
  import { aiEventClient } from "../../event-client.js";
2
+ import { streamGenerationResult } from "../stream-generation-result.js";
2
3
  const kind = "summarize";
3
4
  function createId(prefix) {
4
5
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
@@ -62,23 +63,7 @@ async function* runStreamingSummarize(options) {
62
63
  yield* adapter.summarizeStream(summarizeOptions);
63
64
  return;
64
65
  }
65
- const result = await adapter.summarize(summarizeOptions);
66
- yield {
67
- type: "TEXT_MESSAGE_CONTENT",
68
- messageId: result.id,
69
- model: result.model,
70
- timestamp: Date.now(),
71
- delta: result.summary,
72
- content: result.summary
73
- };
74
- yield {
75
- type: "RUN_FINISHED",
76
- runId: result.id,
77
- model: result.model,
78
- timestamp: Date.now(),
79
- finishReason: "stop",
80
- usage: result.usage
81
- };
66
+ yield* streamGenerationResult(() => adapter.summarize(summarizeOptions));
82
67
  }
83
68
  function createSummarizeOptions(options) {
84
69
  return options;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/activities/summarize/index.ts"],"sourcesContent":["/**\n * Summarize Activity\n *\n * Generates summaries from text input.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '../../event-client.js'\nimport type { SummarizeAdapter } from './adapter'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n} from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'summarize' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/** Extract provider options from a SummarizeAdapter via ~types */\nexport type SummarizeProviderOptions<TAdapter> =\n TAdapter extends SummarizeAdapter<any, any>\n ? TAdapter['~types']['providerOptions']\n : object\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the summarize activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The summarize adapter type\n * @template TStream - Whether to stream the output\n */\nexport interface SummarizeActivityOptions<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n> {\n /** The summarize adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\n /** The text to summarize */\n text: string\n /** Maximum length of the summary (in words or characters, provider-dependent) */\n maxLength?: number\n /** Style of summary to generate */\n style?: 'bullet-points' | 'paragraph' | 'concise'\n /** Topics or aspects to focus on in the summary */\n focus?: Array<string>\n /** Provider-specific options */\n modelOptions?: SummarizeProviderOptions<TAdapter>\n /**\n * Whether to stream the summarization result.\n * When true, returns an AsyncIterable<StreamChunk> for streaming output.\n * When false or not provided, returns a Promise<SummarizationResult>.\n *\n * @default false\n */\n stream?: TStream\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the summarize activity.\n * - If stream is true: AsyncIterable<StreamChunk>\n * - Otherwise: Promise<SummarizationResult>\n */\nexport type SummarizeActivityResult<TStream extends boolean> =\n TStream extends true\n ? AsyncIterable<StreamChunk>\n : Promise<SummarizationResult>\n\n// ===========================\n// Helper Functions\n// ===========================\n\nfunction createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * Summarize activity - generates summaries from text.\n *\n * Supports both streaming and non-streaming modes.\n *\n * @example Basic summarization\n * ```ts\n * import { summarize } from '@tanstack/ai'\n * import { openaiSummarize } from '@tanstack/ai-openai'\n *\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...'\n * })\n *\n * console.log(result.summary)\n * ```\n *\n * @example Summarization with style\n * ```ts\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...',\n * style: 'bullet-points',\n * maxLength: 100\n * })\n * ```\n *\n * @example Focused summarization\n * ```ts\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long technical document...',\n * focus: ['key findings', 'methodology']\n * })\n * ```\n *\n * @example Streaming summarization\n * ```ts\n * for await (const chunk of summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...',\n * stream: true\n * })) {\n * if (chunk.type === 'content') {\n * process.stdout.write(chunk.delta)\n * }\n * }\n * ```\n */\nexport function summarize<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: SummarizeActivityOptions<TAdapter, TStream>,\n): SummarizeActivityResult<TStream> {\n const { stream } = options\n\n if (stream) {\n return runStreamingSummarize(\n options as unknown as SummarizeActivityOptions<\n SummarizeAdapter<string, object>,\n true\n >,\n ) as SummarizeActivityResult<TStream>\n }\n\n return runSummarize(\n options as unknown as SummarizeActivityOptions<\n SummarizeAdapter<string, object>,\n false\n >,\n ) as SummarizeActivityResult<TStream>\n}\n\n/**\n * Run non-streaming summarization\n */\nasync function runSummarize(\n options: SummarizeActivityOptions<SummarizeAdapter<string, object>, false>,\n): Promise<SummarizationResult> {\n const { adapter, text, maxLength, style, focus } = options\n const model = adapter.model\n const requestId = createId('summarize')\n const inputLength = text.length\n const startTime = Date.now()\n\n aiEventClient.emit('summarize:request:started', {\n requestId,\n provider: adapter.name,\n model,\n inputLength,\n timestamp: startTime,\n })\n\n const summarizeOptions: SummarizationOptions = {\n model,\n text,\n maxLength,\n style,\n focus,\n }\n\n const result = await adapter.summarize(summarizeOptions)\n\n const duration = Date.now() - startTime\n const outputLength = result.summary.length\n\n aiEventClient.emit('summarize:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n inputLength,\n outputLength,\n duration,\n timestamp: Date.now(),\n })\n\n return result\n}\n\n/**\n * Run streaming summarization\n * Uses the adapter's native streaming if available, otherwise falls back\n * to non-streaming and yields the result as a single chunk.\n */\nasync function* runStreamingSummarize(\n options: SummarizeActivityOptions<SummarizeAdapter<string, object>, true>,\n): AsyncIterable<StreamChunk> {\n const { adapter, text, maxLength, style, focus } = options\n const model = adapter.model\n\n const summarizeOptions: SummarizationOptions = {\n model,\n text,\n maxLength,\n style,\n focus,\n }\n\n // Use real streaming if the adapter supports it\n if (adapter.summarizeStream) {\n yield* adapter.summarizeStream(summarizeOptions)\n return\n }\n\n // Fall back to non-streaming and yield as a single chunk\n const result = await adapter.summarize(summarizeOptions)\n\n // Yield TEXT_MESSAGE_CONTENT event with the summary\n yield {\n type: 'TEXT_MESSAGE_CONTENT',\n messageId: result.id,\n model: result.model,\n timestamp: Date.now(),\n delta: result.summary,\n content: result.summary,\n }\n\n // Yield RUN_FINISHED event\n yield {\n type: 'RUN_FINISHED',\n runId: result.id,\n model: result.model,\n timestamp: Date.now(),\n finishReason: 'stop',\n usage: result.usage,\n }\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the summarize() function without executing.\n */\nexport function createSummarizeOptions<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: SummarizeActivityOptions<TAdapter, TStream>,\n): SummarizeActivityOptions<TAdapter, TStream> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n SummarizeAdapter,\n SummarizeAdapterConfig,\n AnySummarizeAdapter,\n} from './adapter'\nexport { BaseSummarizeAdapter } from './adapter'\n"],"names":[],"mappings":";AAoBO,MAAM,OAAO;AAmEpB,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAwDO,SAAS,UAId,SACkC;AAClC,QAAM,EAAE,WAAW;AAEnB,MAAI,QAAQ;AACV,WAAO;AAAA,MACL;AAAA,IAAA;AAAA,EAKJ;AAEA,SAAO;AAAA,IACL;AAAA,EAAA;AAKJ;AAKA,eAAe,aACb,SAC8B;AAC9B,QAAM,EAAE,SAAS,MAAM,WAAW,OAAO,UAAU;AACnD,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,WAAW;AACtC,QAAM,cAAc,KAAK;AACzB,QAAM,YAAY,KAAK,IAAA;AAEvB,gBAAc,KAAK,6BAA6B;AAAA,IAC9C;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA;AAAA,IACA,WAAW;AAAA,EAAA,CACZ;AAED,QAAM,mBAAyC;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAGF,QAAM,SAAS,MAAM,QAAQ,UAAU,gBAAgB;AAEvD,QAAM,WAAW,KAAK,IAAA,IAAQ;AAC9B,QAAM,eAAe,OAAO,QAAQ;AAEpC,gBAAc,KAAK,+BAA+B;AAAA,IAChD;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,KAAK,IAAA;AAAA,EAAI,CACrB;AAED,SAAO;AACT;AAOA,gBAAgB,sBACd,SAC4B;AAC5B,QAAM,EAAE,SAAS,MAAM,WAAW,OAAO,UAAU;AACnD,QAAM,QAAQ,QAAQ;AAEtB,QAAM,mBAAyC;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAIF,MAAI,QAAQ,iBAAiB;AAC3B,WAAO,QAAQ,gBAAgB,gBAAgB;AAC/C;AAAA,EACF;AAGA,QAAM,SAAS,MAAM,QAAQ,UAAU,gBAAgB;AAGvD,QAAM;AAAA,IACJ,MAAM;AAAA,IACN,WAAW,OAAO;AAAA,IAClB,OAAO,OAAO;AAAA,IACd,WAAW,KAAK,IAAA;AAAA,IAChB,OAAO,OAAO;AAAA,IACd,SAAS,OAAO;AAAA,EAAA;AAIlB,QAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO,OAAO;AAAA,IACd,OAAO,OAAO;AAAA,IACd,WAAW,KAAK,IAAA;AAAA,IAChB,cAAc;AAAA,IACd,OAAO,OAAO;AAAA,EAAA;AAElB;AASO,SAAS,uBAId,SAC6C;AAC7C,SAAO;AACT;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/activities/summarize/index.ts"],"sourcesContent":["/**\n * Summarize Activity\n *\n * Generates summaries from text input.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '../../event-client.js'\nimport { streamGenerationResult } from '../stream-generation-result.js'\nimport type { SummarizeAdapter } from './adapter'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n} from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'summarize' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/** Extract provider options from a SummarizeAdapter via ~types */\nexport type SummarizeProviderOptions<TAdapter> =\n TAdapter extends SummarizeAdapter<any, any>\n ? TAdapter['~types']['providerOptions']\n : object\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the summarize activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The summarize adapter type\n * @template TStream - Whether to stream the output\n */\nexport interface SummarizeActivityOptions<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n> {\n /** The summarize adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\n /** The text to summarize */\n text: string\n /** Maximum length of the summary (in words or characters, provider-dependent) */\n maxLength?: number\n /** Style of summary to generate */\n style?: 'bullet-points' | 'paragraph' | 'concise'\n /** Topics or aspects to focus on in the summary */\n focus?: Array<string>\n /** Provider-specific options */\n modelOptions?: SummarizeProviderOptions<TAdapter>\n /**\n * Whether to stream the summarization result.\n * When true, returns an AsyncIterable<StreamChunk> for streaming output.\n * When false or not provided, returns a Promise<SummarizationResult>.\n *\n * @default false\n */\n stream?: TStream\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the summarize activity.\n * - If stream is true: AsyncIterable<StreamChunk>\n * - Otherwise: Promise<SummarizationResult>\n */\nexport type SummarizeActivityResult<TStream extends boolean> =\n TStream extends true\n ? AsyncIterable<StreamChunk>\n : Promise<SummarizationResult>\n\n// ===========================\n// Helper Functions\n// ===========================\n\nfunction createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * Summarize activity - generates summaries from text.\n *\n * Supports both streaming and non-streaming modes.\n *\n * @example Basic summarization\n * ```ts\n * import { summarize } from '@tanstack/ai'\n * import { openaiSummarize } from '@tanstack/ai-openai'\n *\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...'\n * })\n *\n * console.log(result.summary)\n * ```\n *\n * @example Summarization with style\n * ```ts\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...',\n * style: 'bullet-points',\n * maxLength: 100\n * })\n * ```\n *\n * @example Focused summarization\n * ```ts\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long technical document...',\n * focus: ['key findings', 'methodology']\n * })\n * ```\n *\n * @example Streaming summarization\n * ```ts\n * for await (const chunk of summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...',\n * stream: true\n * })) {\n * if (chunk.type === 'content') {\n * process.stdout.write(chunk.delta)\n * }\n * }\n * ```\n */\nexport function summarize<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: SummarizeActivityOptions<TAdapter, TStream>,\n): SummarizeActivityResult<TStream> {\n const { stream } = options\n\n if (stream) {\n return runStreamingSummarize(\n options as unknown as SummarizeActivityOptions<\n SummarizeAdapter<string, object>,\n true\n >,\n ) as SummarizeActivityResult<TStream>\n }\n\n return runSummarize(\n options as unknown as SummarizeActivityOptions<\n SummarizeAdapter<string, object>,\n false\n >,\n ) as SummarizeActivityResult<TStream>\n}\n\n/**\n * Run non-streaming summarization\n */\nasync function runSummarize(\n options: SummarizeActivityOptions<SummarizeAdapter<string, object>, false>,\n): Promise<SummarizationResult> {\n const { adapter, text, maxLength, style, focus } = options\n const model = adapter.model\n const requestId = createId('summarize')\n const inputLength = text.length\n const startTime = Date.now()\n\n aiEventClient.emit('summarize:request:started', {\n requestId,\n provider: adapter.name,\n model,\n inputLength,\n timestamp: startTime,\n })\n\n const summarizeOptions: SummarizationOptions = {\n model,\n text,\n maxLength,\n style,\n focus,\n }\n\n const result = await adapter.summarize(summarizeOptions)\n\n const duration = Date.now() - startTime\n const outputLength = result.summary.length\n\n aiEventClient.emit('summarize:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n inputLength,\n outputLength,\n duration,\n timestamp: Date.now(),\n })\n\n return result\n}\n\n/**\n * Run streaming summarization\n * Uses the adapter's native streaming if available, otherwise falls back\n * to non-streaming and yields the result as a single chunk.\n */\nasync function* runStreamingSummarize(\n options: SummarizeActivityOptions<SummarizeAdapter<string, object>, true>,\n): AsyncIterable<StreamChunk> {\n const { adapter, text, maxLength, style, focus } = options\n const model = adapter.model\n\n const summarizeOptions: SummarizationOptions = {\n model,\n text,\n maxLength,\n style,\n focus,\n }\n\n // Use real streaming if the adapter supports it\n if (adapter.summarizeStream) {\n yield* adapter.summarizeStream(summarizeOptions)\n return\n }\n\n // Fall back to non-streaming — wrap result with streamGenerationResult\n yield* streamGenerationResult(() => adapter.summarize(summarizeOptions))\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the summarize() function without executing.\n */\nexport function createSummarizeOptions<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: SummarizeActivityOptions<TAdapter, TStream>,\n): SummarizeActivityOptions<TAdapter, TStream> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n SummarizeAdapter,\n SummarizeAdapterConfig,\n AnySummarizeAdapter,\n} from './adapter'\nexport { BaseSummarizeAdapter } from './adapter'\n"],"names":[],"mappings":";;AAqBO,MAAM,OAAO;AAmEpB,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAwDO,SAAS,UAId,SACkC;AAClC,QAAM,EAAE,WAAW;AAEnB,MAAI,QAAQ;AACV,WAAO;AAAA,MACL;AAAA,IAAA;AAAA,EAKJ;AAEA,SAAO;AAAA,IACL;AAAA,EAAA;AAKJ;AAKA,eAAe,aACb,SAC8B;AAC9B,QAAM,EAAE,SAAS,MAAM,WAAW,OAAO,UAAU;AACnD,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,WAAW;AACtC,QAAM,cAAc,KAAK;AACzB,QAAM,YAAY,KAAK,IAAA;AAEvB,gBAAc,KAAK,6BAA6B;AAAA,IAC9C;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA;AAAA,IACA,WAAW;AAAA,EAAA,CACZ;AAED,QAAM,mBAAyC;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAGF,QAAM,SAAS,MAAM,QAAQ,UAAU,gBAAgB;AAEvD,QAAM,WAAW,KAAK,IAAA,IAAQ;AAC9B,QAAM,eAAe,OAAO,QAAQ;AAEpC,gBAAc,KAAK,+BAA+B;AAAA,IAChD;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,KAAK,IAAA;AAAA,EAAI,CACrB;AAED,SAAO;AACT;AAOA,gBAAgB,sBACd,SAC4B;AAC5B,QAAM,EAAE,SAAS,MAAM,WAAW,OAAO,UAAU;AACnD,QAAM,QAAQ,QAAQ;AAEtB,QAAM,mBAAyC;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAIF,MAAI,QAAQ,iBAAiB;AAC3B,WAAO,QAAQ,gBAAgB,gBAAgB;AAC/C;AAAA,EACF;AAGA,SAAO,uBAAuB,MAAM,QAAQ,UAAU,gBAAgB,CAAC;AACzE;AASO,SAAS,uBAId,SAC6C;AAC7C,SAAO;AACT;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Core TanStack AI library - Open source AI SDK",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -6,8 +6,9 @@
6
6
  */
7
7
 
8
8
  import { aiEventClient } from '../../event-client.js'
9
+ import { streamGenerationResult } from '../stream-generation-result.js'
9
10
  import type { ImageAdapter } from './adapter'
10
- import type { ImageGenerationResult } from '../../types'
11
+ import type { ImageGenerationResult, StreamChunk } from '../../types'
11
12
 
12
13
  // ===========================
13
14
  // Activity Kind
@@ -60,9 +61,11 @@ export type ImageSizeForModel<TAdapter, TModel extends string> =
60
61
  * The model is extracted from the adapter's model property.
61
62
  *
62
63
  * @template TAdapter - The image adapter type
64
+ * @template TStream - Whether to stream the output
63
65
  */
64
66
  export type ImageActivityOptions<
65
67
  TAdapter extends ImageAdapter<string, any, any, any>,
68
+ TStream extends boolean = false,
66
69
  > = {
67
70
  /** The image adapter to use (must be created with a model) */
68
71
  adapter: TAdapter & { kind: typeof kind }
@@ -72,6 +75,14 @@ export type ImageActivityOptions<
72
75
  numberOfImages?: number
73
76
  /** Image size in WIDTHxHEIGHT format (e.g., "1024x1024") */
74
77
  size?: ImageSizeForModel<TAdapter, TAdapter['model']>
78
+ /**
79
+ * Whether to stream the image generation result.
80
+ * When true, returns an AsyncIterable<StreamChunk> for streaming transport.
81
+ * When false or not provided, returns a Promise<ImageGenerationResult>.
82
+ *
83
+ * @default false
84
+ */
85
+ stream?: TStream
75
86
  } & ({} extends ImageProviderOptionsForModel<TAdapter, TAdapter['model']>
76
87
  ? {
77
88
  /** Provider-specific options for image generation */ modelOptions?: ImageProviderOptionsForModel<
@@ -90,8 +101,15 @@ export type ImageActivityOptions<
90
101
  // Activity Result Type
91
102
  // ===========================
92
103
 
93
- /** Result type for the image activity */
94
- export type ImageActivityResult = Promise<ImageGenerationResult>
104
+ /**
105
+ * Result type for the image activity.
106
+ * - If stream is true: AsyncIterable<StreamChunk>
107
+ * - Otherwise: Promise<ImageGenerationResult>
108
+ */
109
+ export type ImageActivityResult<TStream extends boolean = false> =
110
+ TStream extends true
111
+ ? AsyncIterable<StreamChunk>
112
+ : Promise<ImageGenerationResult>
95
113
 
96
114
  function createId(prefix: string): string {
97
115
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
@@ -146,10 +164,31 @@ function createId(prefix: string): string {
146
164
  * })
147
165
  * ```
148
166
  */
149
- export async function generateImage<
167
+ export function generateImage<
168
+ TAdapter extends ImageAdapter<string, any, any, any>,
169
+ TStream extends boolean = false,
170
+ >(
171
+ options: ImageActivityOptions<TAdapter, TStream>,
172
+ ): ImageActivityResult<TStream> {
173
+ if (options.stream) {
174
+ return streamGenerationResult(() =>
175
+ runGenerateImage(options),
176
+ ) as ImageActivityResult<TStream>
177
+ }
178
+
179
+ return runGenerateImage(options) as ImageActivityResult<TStream>
180
+ }
181
+
182
+ /**
183
+ * Internal implementation of image generation (always non-streaming).
184
+ * Contains all devtools event emission logic.
185
+ */
186
+ async function runGenerateImage<
150
187
  TAdapter extends ImageAdapter<string, any, any, any>,
151
- >(options: ImageActivityOptions<TAdapter>): ImageActivityResult {
152
- const { adapter, ...rest } = options
188
+ >(
189
+ options: ImageActivityOptions<TAdapter, boolean>,
190
+ ): Promise<ImageGenerationResult> {
191
+ const { adapter, stream: _stream, ...rest } = options
153
192
  const model = adapter.model
154
193
  const requestId = createId('image')
155
194
  const startTime = Date.now()
@@ -204,7 +243,10 @@ export async function generateImage<
204
243
  */
205
244
  export function createImageOptions<
206
245
  TAdapter extends ImageAdapter<string, any, any, any>,
207
- >(options: ImageActivityOptions<TAdapter>): ImageActivityOptions<TAdapter> {
246
+ TStream extends boolean = false,
247
+ >(
248
+ options: ImageActivityOptions<TAdapter, TStream>,
249
+ ): ImageActivityOptions<TAdapter, TStream> {
208
250
  return options
209
251
  }
210
252
 
@@ -6,8 +6,9 @@
6
6
  */
7
7
 
8
8
  import { aiEventClient } from '../../event-client.js'
9
+ import { streamGenerationResult } from '../stream-generation-result.js'
9
10
  import type { TTSAdapter } from './adapter'
10
- import type { TTSResult } from '../../types'
11
+ import type { StreamChunk, TTSResult } from '../../types'
11
12
 
12
13
  // ===========================
13
14
  // Activity Kind
@@ -37,9 +38,11 @@ export type TTSProviderOptions<TAdapter> =
37
38
  * The model is extracted from the adapter's model property.
38
39
  *
39
40
  * @template TAdapter - The TTS adapter type
41
+ * @template TStream - Whether to stream the output
40
42
  */
41
43
  export interface TTSActivityOptions<
42
44
  TAdapter extends TTSAdapter<string, object>,
45
+ TStream extends boolean = false,
43
46
  > {
44
47
  /** The TTS adapter to use (must be created with a model) */
45
48
  adapter: TAdapter & { kind: typeof kind }
@@ -53,14 +56,27 @@ export interface TTSActivityOptions<
53
56
  speed?: number
54
57
  /** Provider-specific options for TTS generation */
55
58
  modelOptions?: TTSProviderOptions<TAdapter>
59
+ /**
60
+ * Whether to stream the generation result.
61
+ * When true, returns an AsyncIterable<StreamChunk> for streaming transport.
62
+ * When false or not provided, returns a Promise<TTSResult>.
63
+ *
64
+ * @default false
65
+ */
66
+ stream?: TStream
56
67
  }
57
68
 
58
69
  // ===========================
59
70
  // Activity Result Type
60
71
  // ===========================
61
72
 
62
- /** Result type for the TTS activity */
63
- export type TTSActivityResult = Promise<TTSResult>
73
+ /**
74
+ * Result type for the TTS activity.
75
+ * - If stream is true: AsyncIterable<StreamChunk>
76
+ * - Otherwise: Promise<TTSResult>
77
+ */
78
+ export type TTSActivityResult<TStream extends boolean = false> =
79
+ TStream extends true ? AsyncIterable<StreamChunk> : Promise<TTSResult>
64
80
 
65
81
  function createId(prefix: string): string {
66
82
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
@@ -100,10 +116,25 @@ function createId(prefix: string): string {
100
116
  * })
101
117
  * ```
102
118
  */
103
- export async function generateSpeech<
119
+ export function generateSpeech<
104
120
  TAdapter extends TTSAdapter<string, object>,
105
- >(options: TTSActivityOptions<TAdapter>): TTSActivityResult {
106
- const { adapter, ...rest } = options
121
+ TStream extends boolean = false,
122
+ >(options: TTSActivityOptions<TAdapter, TStream>): TTSActivityResult<TStream> {
123
+ if (options.stream) {
124
+ return streamGenerationResult(() =>
125
+ runGenerateSpeech(options),
126
+ ) as TTSActivityResult<TStream>
127
+ }
128
+ return runGenerateSpeech(options) as TTSActivityResult<TStream>
129
+ }
130
+
131
+ /**
132
+ * Run the core TTS generation logic (non-streaming).
133
+ */
134
+ async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
135
+ options: TTSActivityOptions<TAdapter, boolean>,
136
+ ): Promise<TTSResult> {
137
+ const { adapter, stream: _stream, ...rest } = options
107
138
  const model = adapter.model
108
139
  const requestId = createId('speech')
109
140
  const startTime = Date.now()
@@ -149,7 +180,10 @@ export async function generateSpeech<
149
180
  */
150
181
  export function createSpeechOptions<
151
182
  TAdapter extends TTSAdapter<string, object>,
152
- >(options: TTSActivityOptions<TAdapter>): TTSActivityOptions<TAdapter> {
183
+ TStream extends boolean = false,
184
+ >(
185
+ options: TTSActivityOptions<TAdapter, TStream>,
186
+ ): TTSActivityOptions<TAdapter, TStream> {
153
187
  return options
154
188
  }
155
189
 
@@ -6,8 +6,9 @@
6
6
  */
7
7
 
8
8
  import { aiEventClient } from '../../event-client.js'
9
+ import { streamGenerationResult } from '../stream-generation-result.js'
9
10
  import type { TranscriptionAdapter } from './adapter'
10
- import type { TranscriptionResult } from '../../types'
11
+ import type { StreamChunk, TranscriptionResult } from '../../types'
11
12
 
12
13
  // ===========================
13
14
  // Activity Kind
@@ -37,9 +38,11 @@ export type TranscriptionProviderOptions<TAdapter> =
37
38
  * The model is extracted from the adapter's model property.
38
39
  *
39
40
  * @template TAdapter - The transcription adapter type
41
+ * @template TStream - Whether to stream the output
40
42
  */
41
43
  export interface TranscriptionActivityOptions<
42
44
  TAdapter extends TranscriptionAdapter<string, object>,
45
+ TStream extends boolean = false,
43
46
  > {
44
47
  /** The transcription adapter to use (must be created with a model) */
45
48
  adapter: TAdapter & { kind: typeof kind }
@@ -53,14 +56,29 @@ export interface TranscriptionActivityOptions<
53
56
  responseFormat?: 'json' | 'text' | 'srt' | 'verbose_json' | 'vtt'
54
57
  /** Provider-specific options for transcription */
55
58
  modelOptions?: TranscriptionProviderOptions<TAdapter>
59
+ /**
60
+ * Whether to stream the transcription result.
61
+ * When true, returns an AsyncIterable<StreamChunk> for streaming transport.
62
+ * When false or not provided, returns a Promise<TranscriptionResult>.
63
+ *
64
+ * @default false
65
+ */
66
+ stream?: TStream
56
67
  }
57
68
 
58
69
  // ===========================
59
70
  // Activity Result Type
60
71
  // ===========================
61
72
 
62
- /** Result type for the transcription activity */
63
- export type TranscriptionActivityResult = Promise<TranscriptionResult>
73
+ /**
74
+ * Result type for the transcription activity.
75
+ * - If stream is true: AsyncIterable<StreamChunk>
76
+ * - Otherwise: Promise<TranscriptionResult>
77
+ */
78
+ export type TranscriptionActivityResult<TStream extends boolean = false> =
79
+ TStream extends true
80
+ ? AsyncIterable<StreamChunk>
81
+ : Promise<TranscriptionResult>
64
82
 
65
83
  function createId(prefix: string): string {
66
84
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
@@ -101,13 +119,44 @@ function createId(prefix: string): string {
101
119
  * console.log(`[${segment.start}s - ${segment.end}s]: ${segment.text}`)
102
120
  * })
103
121
  * ```
122
+ *
123
+ * @example Streaming transcription result
124
+ * ```ts
125
+ * for await (const chunk of generateTranscription({
126
+ * adapter: openaiTranscription('whisper-1'),
127
+ * audio: audioFile,
128
+ * stream: true
129
+ * })) {
130
+ * console.log(chunk)
131
+ * }
132
+ * ```
133
+ */
134
+ export function generateTranscription<
135
+ TAdapter extends TranscriptionAdapter<string, object>,
136
+ TStream extends boolean = false,
137
+ >(
138
+ options: TranscriptionActivityOptions<TAdapter, TStream>,
139
+ ): TranscriptionActivityResult<TStream> {
140
+ if (options.stream) {
141
+ return streamGenerationResult(() =>
142
+ runGenerateTranscription(options),
143
+ ) as TranscriptionActivityResult<TStream>
144
+ }
145
+
146
+ return runGenerateTranscription(
147
+ options,
148
+ ) as TranscriptionActivityResult<TStream>
149
+ }
150
+
151
+ /**
152
+ * Run non-streaming transcription
104
153
  */
105
- export async function generateTranscription<
154
+ async function runGenerateTranscription<
106
155
  TAdapter extends TranscriptionAdapter<string, object>,
107
156
  >(
108
- options: TranscriptionActivityOptions<TAdapter>,
109
- ): TranscriptionActivityResult {
110
- const { adapter, ...rest } = options
157
+ options: TranscriptionActivityOptions<TAdapter, boolean>,
158
+ ): Promise<TranscriptionResult> {
159
+ const { adapter, stream: _stream, ...rest } = options
111
160
  const model = adapter.model
112
161
  const requestId = createId('transcription')
113
162
  const startTime = Date.now()
@@ -149,9 +198,10 @@ export async function generateTranscription<
149
198
  */
150
199
  export function createTranscriptionOptions<
151
200
  TAdapter extends TranscriptionAdapter<string, object>,
201
+ TStream extends boolean = false,
152
202
  >(
153
- options: TranscriptionActivityOptions<TAdapter>,
154
- ): TranscriptionActivityOptions<TAdapter> {
203
+ options: TranscriptionActivityOptions<TAdapter, TStream>,
204
+ ): TranscriptionActivityOptions<TAdapter, TStream> {
155
205
  return options
156
206
  }
157
207