@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,5 +1,5 @@
1
1
  import { ImageAdapter } from './adapter.js';
2
- import { ImageGenerationResult } from '../../types.js';
2
+ import { ImageGenerationResult, StreamChunk } from '../../types.js';
3
3
  /** The adapter kind this activity handles */
4
4
  export declare const kind: "image";
5
5
  /**
@@ -18,8 +18,9 @@ export type ImageSizeForModel<TAdapter, TModel extends string> = TAdapter extend
18
18
  * The model is extracted from the adapter's model property.
19
19
  *
20
20
  * @template TAdapter - The image adapter type
21
+ * @template TStream - Whether to stream the output
21
22
  */
22
- export type ImageActivityOptions<TAdapter extends ImageAdapter<string, any, any, any>> = {
23
+ export type ImageActivityOptions<TAdapter extends ImageAdapter<string, any, any, any>, TStream extends boolean = false> = {
23
24
  /** The image adapter to use (must be created with a model) */
24
25
  adapter: TAdapter & {
25
26
  kind: typeof kind;
@@ -30,13 +31,25 @@ export type ImageActivityOptions<TAdapter extends ImageAdapter<string, any, any,
30
31
  numberOfImages?: number;
31
32
  /** Image size in WIDTHxHEIGHT format (e.g., "1024x1024") */
32
33
  size?: ImageSizeForModel<TAdapter, TAdapter['model']>;
34
+ /**
35
+ * Whether to stream the image generation result.
36
+ * When true, returns an AsyncIterable<StreamChunk> for streaming transport.
37
+ * When false or not provided, returns a Promise<ImageGenerationResult>.
38
+ *
39
+ * @default false
40
+ */
41
+ stream?: TStream;
33
42
  } & ({} extends ImageProviderOptionsForModel<TAdapter, TAdapter['model']> ? {
34
43
  /** Provider-specific options for image generation */ modelOptions?: ImageProviderOptionsForModel<TAdapter, TAdapter['model']>;
35
44
  } : {
36
45
  /** Provider-specific options for image generation */ modelOptions: ImageProviderOptionsForModel<TAdapter, TAdapter['model']>;
37
46
  });
38
- /** Result type for the image activity */
39
- export type ImageActivityResult = Promise<ImageGenerationResult>;
47
+ /**
48
+ * Result type for the image activity.
49
+ * - If stream is true: AsyncIterable<StreamChunk>
50
+ * - Otherwise: Promise<ImageGenerationResult>
51
+ */
52
+ export type ImageActivityResult<TStream extends boolean = false> = TStream extends true ? AsyncIterable<StreamChunk> : Promise<ImageGenerationResult>;
40
53
  /**
41
54
  * Image activity - generates images from text prompts.
42
55
  *
@@ -82,10 +95,10 @@ export type ImageActivityResult = Promise<ImageGenerationResult>;
82
95
  * })
83
96
  * ```
84
97
  */
85
- export declare function generateImage<TAdapter extends ImageAdapter<string, any, any, any>>(options: ImageActivityOptions<TAdapter>): ImageActivityResult;
98
+ export declare function generateImage<TAdapter extends ImageAdapter<string, any, any, any>, TStream extends boolean = false>(options: ImageActivityOptions<TAdapter, TStream>): ImageActivityResult<TStream>;
86
99
  /**
87
100
  * Create typed options for the generateImage() function without executing.
88
101
  */
89
- export declare function createImageOptions<TAdapter extends ImageAdapter<string, any, any, any>>(options: ImageActivityOptions<TAdapter>): ImageActivityOptions<TAdapter>;
102
+ export declare function createImageOptions<TAdapter extends ImageAdapter<string, any, any, any>, TStream extends boolean = false>(options: ImageActivityOptions<TAdapter, TStream>): ImageActivityOptions<TAdapter, TStream>;
90
103
  export type { ImageAdapter, ImageAdapterConfig, AnyImageAdapter, } from './adapter.js';
91
104
  export { BaseImageAdapter } from './adapter.js';
@@ -1,10 +1,19 @@
1
1
  import { aiEventClient } from "../../event-client.js";
2
+ import { streamGenerationResult } from "../stream-generation-result.js";
2
3
  const kind = "image";
3
4
  function createId(prefix) {
4
5
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
5
6
  }
6
- async function generateImage(options) {
7
- const { adapter, ...rest } = options;
7
+ function generateImage(options) {
8
+ if (options.stream) {
9
+ return streamGenerationResult(
10
+ () => runGenerateImage(options)
11
+ );
12
+ }
13
+ return runGenerateImage(options);
14
+ }
15
+ async function runGenerateImage(options) {
16
+ const { adapter, stream: _stream, ...rest } = options;
8
17
  const model = adapter.model;
9
18
  const requestId = createId("image");
10
19
  const startTime = Date.now();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/activities/generateImage/index.ts"],"sourcesContent":["/**\n * Image Activity\n *\n * Generates images from text prompts.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '../../event-client.js'\nimport type { ImageAdapter } from './adapter'\nimport type { ImageGenerationResult } from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'image' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/**\n * Extract model-specific provider options from an ImageAdapter via ~types.\n * If the model has specific options defined in ModelProviderOptions (and not just via index signature),\n * use those; otherwise fall back to base provider options.\n */\nexport type ImageProviderOptionsForModel<TAdapter, TModel extends string> =\n TAdapter extends ImageAdapter<any, infer BaseOptions, infer ModelOptions, any>\n ? string extends keyof ModelOptions\n ? // ModelOptions is Record<string, unknown> or has index signature - use BaseOptions\n BaseOptions\n : // ModelOptions has explicit keys - check if TModel is one of them\n TModel extends keyof ModelOptions\n ? ModelOptions[TModel]\n : BaseOptions\n : object\n\n/**\n * Extract model-specific size options from an ImageAdapter via ~types.\n * If the model has specific sizes defined, use those; otherwise fall back to string.\n */\nexport type ImageSizeForModel<TAdapter, TModel extends string> =\n TAdapter extends ImageAdapter<any, any, any, infer SizeByName>\n ? string extends keyof SizeByName\n ? // SizeByName has index signature - fall back to string\n string\n : // SizeByName has explicit keys - check if TModel is one of them\n TModel extends keyof SizeByName\n ? SizeByName[TModel]\n : string\n : string\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the image activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The image adapter type\n */\nexport type ImageActivityOptions<\n TAdapter extends ImageAdapter<string, any, any, any>,\n> = {\n /** The image adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\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?: ImageSizeForModel<TAdapter, TAdapter['model']>\n} & ({} extends ImageProviderOptionsForModel<TAdapter, TAdapter['model']>\n ? {\n /** Provider-specific options for image generation */ modelOptions?: ImageProviderOptionsForModel<\n TAdapter,\n TAdapter['model']\n >\n }\n : {\n /** Provider-specific options for image generation */ modelOptions: ImageProviderOptionsForModel<\n TAdapter,\n TAdapter['model']\n >\n })\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/** Result type for the image activity */\nexport type ImageActivityResult = Promise<ImageGenerationResult>\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 * Image activity - generates images from text prompts.\n *\n * Uses AI image generation models to create images based on natural language descriptions.\n *\n * @example Generate a single image\n * ```ts\n * import { generateImage } from '@tanstack/ai'\n * import { openaiImage } from '@tanstack/ai-openai'\n *\n * const result = await generateImage({\n * adapter: openaiImage('dall-e-3'),\n * prompt: 'A serene mountain landscape at sunset'\n * })\n *\n * console.log(result.images[0].url)\n * ```\n *\n * @example Generate multiple images\n * ```ts\n * const result = await generateImage({\n * adapter: openaiImage('dall-e-2'),\n * prompt: 'A cute robot mascot',\n * numberOfImages: 4,\n * size: '512x512'\n * })\n *\n * result.images.forEach((image, i) => {\n * console.log(`Image ${i + 1}: ${image.url}`)\n * })\n * ```\n *\n * @example With provider-specific options\n * ```ts\n * const result = await generateImage({\n * adapter: openaiImage('dall-e-3'),\n * prompt: 'A professional headshot photo',\n * size: '1024x1024',\n * modelOptions: {\n * quality: 'hd',\n * style: 'natural'\n * }\n * })\n * ```\n */\nexport async function generateImage<\n TAdapter extends ImageAdapter<string, any, any, any>,\n>(options: ImageActivityOptions<TAdapter>): ImageActivityResult {\n const { adapter, ...rest } = options\n const model = adapter.model\n const requestId = createId('image')\n const startTime = Date.now()\n\n aiEventClient.emit('image:request:started', {\n requestId,\n provider: adapter.name,\n model,\n prompt: rest.prompt,\n numberOfImages: rest.numberOfImages,\n size: rest.size,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: startTime,\n })\n\n return adapter.generateImages({ ...rest, model }).then((result) => {\n const duration = Date.now() - startTime\n\n aiEventClient.emit('image:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n images: result.images.map((image) => ({\n url: image.url,\n b64Json: image.b64Json,\n })),\n duration,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: Date.now(),\n })\n\n if (result.usage) {\n aiEventClient.emit('image:usage', {\n requestId,\n model,\n usage: result.usage,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: Date.now(),\n })\n }\n\n return result\n })\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the generateImage() function without executing.\n */\nexport function createImageOptions<\n TAdapter extends ImageAdapter<string, any, any, any>,\n>(options: ImageActivityOptions<TAdapter>): ImageActivityOptions<TAdapter> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n ImageAdapter,\n ImageAdapterConfig,\n AnyImageAdapter,\n} from './adapter'\nexport { BaseImageAdapter } from './adapter'\n"],"names":[],"mappings":";AAgBO,MAAM,OAAO;AA+EpB,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAmDA,eAAsB,cAEpB,SAA8D;AAC9D,QAAM,EAAE,SAAS,GAAG,KAAA,IAAS;AAC7B,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,OAAO;AAClC,QAAM,YAAY,KAAK,IAAA;AAEvB,gBAAc,KAAK,yBAAyB;AAAA,IAC1C;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,QAAQ,KAAK;AAAA,IACb,gBAAgB,KAAK;AAAA,IACrB,MAAM,KAAK;AAAA,IACX,cAAc,KAAK;AAAA,IACnB,WAAW;AAAA,EAAA,CACZ;AAED,SAAO,QAAQ,eAAe,EAAE,GAAG,MAAM,OAAO,EAAE,KAAK,CAAC,WAAW;AACjE,UAAM,WAAW,KAAK,IAAA,IAAQ;AAE9B,kBAAc,KAAK,2BAA2B;AAAA,MAC5C;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB;AAAA,MACA,QAAQ,OAAO,OAAO,IAAI,CAAC,WAAW;AAAA,QACpC,KAAK,MAAM;AAAA,QACX,SAAS,MAAM;AAAA,MAAA,EACf;AAAA,MACF;AAAA,MACA,cAAc,KAAK;AAAA,MACnB,WAAW,KAAK,IAAA;AAAA,IAAI,CACrB;AAED,QAAI,OAAO,OAAO;AAChB,oBAAc,KAAK,eAAe;AAAA,QAChC;AAAA,QACA;AAAA,QACA,OAAO,OAAO;AAAA,QACd,cAAc,KAAK;AAAA,QACnB,WAAW,KAAK,IAAA;AAAA,MAAI,CACrB;AAAA,IACH;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AASO,SAAS,mBAEd,SAAyE;AACzE,SAAO;AACT;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/activities/generateImage/index.ts"],"sourcesContent":["/**\n * Image Activity\n *\n * Generates images from text prompts.\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 { ImageAdapter } from './adapter'\nimport type { ImageGenerationResult, StreamChunk } from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'image' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/**\n * Extract model-specific provider options from an ImageAdapter via ~types.\n * If the model has specific options defined in ModelProviderOptions (and not just via index signature),\n * use those; otherwise fall back to base provider options.\n */\nexport type ImageProviderOptionsForModel<TAdapter, TModel extends string> =\n TAdapter extends ImageAdapter<any, infer BaseOptions, infer ModelOptions, any>\n ? string extends keyof ModelOptions\n ? // ModelOptions is Record<string, unknown> or has index signature - use BaseOptions\n BaseOptions\n : // ModelOptions has explicit keys - check if TModel is one of them\n TModel extends keyof ModelOptions\n ? ModelOptions[TModel]\n : BaseOptions\n : object\n\n/**\n * Extract model-specific size options from an ImageAdapter via ~types.\n * If the model has specific sizes defined, use those; otherwise fall back to string.\n */\nexport type ImageSizeForModel<TAdapter, TModel extends string> =\n TAdapter extends ImageAdapter<any, any, any, infer SizeByName>\n ? string extends keyof SizeByName\n ? // SizeByName has index signature - fall back to string\n string\n : // SizeByName has explicit keys - check if TModel is one of them\n TModel extends keyof SizeByName\n ? SizeByName[TModel]\n : string\n : string\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the image activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The image adapter type\n * @template TStream - Whether to stream the output\n */\nexport type ImageActivityOptions<\n TAdapter extends ImageAdapter<string, any, any, any>,\n TStream extends boolean = false,\n> = {\n /** The image adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\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?: ImageSizeForModel<TAdapter, TAdapter['model']>\n /**\n * Whether to stream the image generation result.\n * When true, returns an AsyncIterable<StreamChunk> for streaming transport.\n * When false or not provided, returns a Promise<ImageGenerationResult>.\n *\n * @default false\n */\n stream?: TStream\n} & ({} extends ImageProviderOptionsForModel<TAdapter, TAdapter['model']>\n ? {\n /** Provider-specific options for image generation */ modelOptions?: ImageProviderOptionsForModel<\n TAdapter,\n TAdapter['model']\n >\n }\n : {\n /** Provider-specific options for image generation */ modelOptions: ImageProviderOptionsForModel<\n TAdapter,\n TAdapter['model']\n >\n })\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the image activity.\n * - If stream is true: AsyncIterable<StreamChunk>\n * - Otherwise: Promise<ImageGenerationResult>\n */\nexport type ImageActivityResult<TStream extends boolean = false> =\n TStream extends true\n ? AsyncIterable<StreamChunk>\n : Promise<ImageGenerationResult>\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 * Image activity - generates images from text prompts.\n *\n * Uses AI image generation models to create images based on natural language descriptions.\n *\n * @example Generate a single image\n * ```ts\n * import { generateImage } from '@tanstack/ai'\n * import { openaiImage } from '@tanstack/ai-openai'\n *\n * const result = await generateImage({\n * adapter: openaiImage('dall-e-3'),\n * prompt: 'A serene mountain landscape at sunset'\n * })\n *\n * console.log(result.images[0].url)\n * ```\n *\n * @example Generate multiple images\n * ```ts\n * const result = await generateImage({\n * adapter: openaiImage('dall-e-2'),\n * prompt: 'A cute robot mascot',\n * numberOfImages: 4,\n * size: '512x512'\n * })\n *\n * result.images.forEach((image, i) => {\n * console.log(`Image ${i + 1}: ${image.url}`)\n * })\n * ```\n *\n * @example With provider-specific options\n * ```ts\n * const result = await generateImage({\n * adapter: openaiImage('dall-e-3'),\n * prompt: 'A professional headshot photo',\n * size: '1024x1024',\n * modelOptions: {\n * quality: 'hd',\n * style: 'natural'\n * }\n * })\n * ```\n */\nexport function generateImage<\n TAdapter extends ImageAdapter<string, any, any, any>,\n TStream extends boolean = false,\n>(\n options: ImageActivityOptions<TAdapter, TStream>,\n): ImageActivityResult<TStream> {\n if (options.stream) {\n return streamGenerationResult(() =>\n runGenerateImage(options),\n ) as ImageActivityResult<TStream>\n }\n\n return runGenerateImage(options) as ImageActivityResult<TStream>\n}\n\n/**\n * Internal implementation of image generation (always non-streaming).\n * Contains all devtools event emission logic.\n */\nasync function runGenerateImage<\n TAdapter extends ImageAdapter<string, any, any, any>,\n>(\n options: ImageActivityOptions<TAdapter, boolean>,\n): Promise<ImageGenerationResult> {\n const { adapter, stream: _stream, ...rest } = options\n const model = adapter.model\n const requestId = createId('image')\n const startTime = Date.now()\n\n aiEventClient.emit('image:request:started', {\n requestId,\n provider: adapter.name,\n model,\n prompt: rest.prompt,\n numberOfImages: rest.numberOfImages,\n size: rest.size,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: startTime,\n })\n\n return adapter.generateImages({ ...rest, model }).then((result) => {\n const duration = Date.now() - startTime\n\n aiEventClient.emit('image:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n images: result.images.map((image) => ({\n url: image.url,\n b64Json: image.b64Json,\n })),\n duration,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: Date.now(),\n })\n\n if (result.usage) {\n aiEventClient.emit('image:usage', {\n requestId,\n model,\n usage: result.usage,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: Date.now(),\n })\n }\n\n return result\n })\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the generateImage() function without executing.\n */\nexport function createImageOptions<\n TAdapter extends ImageAdapter<string, any, any, any>,\n TStream extends boolean = false,\n>(\n options: ImageActivityOptions<TAdapter, TStream>,\n): ImageActivityOptions<TAdapter, TStream> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n ImageAdapter,\n ImageAdapterConfig,\n AnyImageAdapter,\n} from './adapter'\nexport { BaseImageAdapter } from './adapter'\n"],"names":[],"mappings":";;AAiBO,MAAM,OAAO;AAgGpB,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAmDO,SAAS,cAId,SAC8B;AAC9B,MAAI,QAAQ,QAAQ;AAClB,WAAO;AAAA,MAAuB,MAC5B,iBAAiB,OAAO;AAAA,IAAA;AAAA,EAE5B;AAEA,SAAO,iBAAiB,OAAO;AACjC;AAMA,eAAe,iBAGb,SACgC;AAChC,QAAM,EAAE,SAAS,QAAQ,SAAS,GAAG,SAAS;AAC9C,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,OAAO;AAClC,QAAM,YAAY,KAAK,IAAA;AAEvB,gBAAc,KAAK,yBAAyB;AAAA,IAC1C;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,QAAQ,KAAK;AAAA,IACb,gBAAgB,KAAK;AAAA,IACrB,MAAM,KAAK;AAAA,IACX,cAAc,KAAK;AAAA,IACnB,WAAW;AAAA,EAAA,CACZ;AAED,SAAO,QAAQ,eAAe,EAAE,GAAG,MAAM,OAAO,EAAE,KAAK,CAAC,WAAW;AACjE,UAAM,WAAW,KAAK,IAAA,IAAQ;AAE9B,kBAAc,KAAK,2BAA2B;AAAA,MAC5C;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB;AAAA,MACA,QAAQ,OAAO,OAAO,IAAI,CAAC,WAAW;AAAA,QACpC,KAAK,MAAM;AAAA,QACX,SAAS,MAAM;AAAA,MAAA,EACf;AAAA,MACF;AAAA,MACA,cAAc,KAAK;AAAA,MACnB,WAAW,KAAK,IAAA;AAAA,IAAI,CACrB;AAED,QAAI,OAAO,OAAO;AAChB,oBAAc,KAAK,eAAe;AAAA,QAChC;AAAA,QACA;AAAA,QACA,OAAO,OAAO;AAAA,QACd,cAAc,KAAK;AAAA,QACnB,WAAW,KAAK,IAAA;AAAA,MAAI,CACrB;AAAA,IACH;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AASO,SAAS,mBAId,SACyC;AACzC,SAAO;AACT;"}
@@ -1,5 +1,5 @@
1
1
  import { TTSAdapter } from './adapter.js';
2
- import { TTSResult } from '../../types.js';
2
+ import { StreamChunk, TTSResult } from '../../types.js';
3
3
  /** The adapter kind this activity handles */
4
4
  export declare const kind: "tts";
5
5
  /**
@@ -11,8 +11,9 @@ export type TTSProviderOptions<TAdapter> = TAdapter extends TTSAdapter<any, any>
11
11
  * The model is extracted from the adapter's model property.
12
12
  *
13
13
  * @template TAdapter - The TTS adapter type
14
+ * @template TStream - Whether to stream the output
14
15
  */
15
- export interface TTSActivityOptions<TAdapter extends TTSAdapter<string, object>> {
16
+ export interface TTSActivityOptions<TAdapter extends TTSAdapter<string, object>, TStream extends boolean = false> {
16
17
  /** The TTS adapter to use (must be created with a model) */
17
18
  adapter: TAdapter & {
18
19
  kind: typeof kind;
@@ -27,9 +28,21 @@ export interface TTSActivityOptions<TAdapter extends TTSAdapter<string, object>>
27
28
  speed?: number;
28
29
  /** Provider-specific options for TTS generation */
29
30
  modelOptions?: TTSProviderOptions<TAdapter>;
31
+ /**
32
+ * Whether to stream the generation result.
33
+ * When true, returns an AsyncIterable<StreamChunk> for streaming transport.
34
+ * When false or not provided, returns a Promise<TTSResult>.
35
+ *
36
+ * @default false
37
+ */
38
+ stream?: TStream;
30
39
  }
31
- /** Result type for the TTS activity */
32
- export type TTSActivityResult = Promise<TTSResult>;
40
+ /**
41
+ * Result type for the TTS activity.
42
+ * - If stream is true: AsyncIterable<StreamChunk>
43
+ * - Otherwise: Promise<TTSResult>
44
+ */
45
+ export type TTSActivityResult<TStream extends boolean = false> = TStream extends true ? AsyncIterable<StreamChunk> : Promise<TTSResult>;
33
46
  /**
34
47
  * TTS activity - generates speech from text.
35
48
  *
@@ -60,10 +73,10 @@ export type TTSActivityResult = Promise<TTSResult>;
60
73
  * })
61
74
  * ```
62
75
  */
63
- export declare function generateSpeech<TAdapter extends TTSAdapter<string, object>>(options: TTSActivityOptions<TAdapter>): TTSActivityResult;
76
+ export declare function generateSpeech<TAdapter extends TTSAdapter<string, object>, TStream extends boolean = false>(options: TTSActivityOptions<TAdapter, TStream>): TTSActivityResult<TStream>;
64
77
  /**
65
78
  * Create typed options for the generateSpeech() function without executing.
66
79
  */
67
- export declare function createSpeechOptions<TAdapter extends TTSAdapter<string, object>>(options: TTSActivityOptions<TAdapter>): TTSActivityOptions<TAdapter>;
80
+ export declare function createSpeechOptions<TAdapter extends TTSAdapter<string, object>, TStream extends boolean = false>(options: TTSActivityOptions<TAdapter, TStream>): TTSActivityOptions<TAdapter, TStream>;
68
81
  export type { TTSAdapter, TTSAdapterConfig, AnyTTSAdapter } from './adapter.js';
69
82
  export { BaseTTSAdapter } from './adapter.js';
@@ -1,10 +1,19 @@
1
1
  import { aiEventClient } from "../../event-client.js";
2
+ import { streamGenerationResult } from "../stream-generation-result.js";
2
3
  const kind = "tts";
3
4
  function createId(prefix) {
4
5
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
5
6
  }
6
- async function generateSpeech(options) {
7
- const { adapter, ...rest } = options;
7
+ function generateSpeech(options) {
8
+ if (options.stream) {
9
+ return streamGenerationResult(
10
+ () => runGenerateSpeech(options)
11
+ );
12
+ }
13
+ return runGenerateSpeech(options);
14
+ }
15
+ async function runGenerateSpeech(options) {
16
+ const { adapter, stream: _stream, ...rest } = options;
8
17
  const model = adapter.model;
9
18
  const requestId = createId("speech");
10
19
  const startTime = Date.now();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/activities/generateSpeech/index.ts"],"sourcesContent":["/**\n * TTS Activity\n *\n * Generates speech audio from text using text-to-speech models.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '../../event-client.js'\nimport type { TTSAdapter } from './adapter'\nimport type { TTSResult } from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'tts' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/**\n * Extract provider options from a TTSAdapter via ~types.\n */\nexport type TTSProviderOptions<TAdapter> =\n TAdapter extends TTSAdapter<any, any>\n ? TAdapter['~types']['providerOptions']\n : object\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the TTS activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The TTS adapter type\n */\nexport interface TTSActivityOptions<\n TAdapter extends TTSAdapter<string, object>,\n> {\n /** The TTS adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\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 /** Provider-specific options for TTS generation */\n modelOptions?: TTSProviderOptions<TAdapter>\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/** Result type for the TTS activity */\nexport type TTSActivityResult = Promise<TTSResult>\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 * TTS activity - generates speech from text.\n *\n * Uses AI text-to-speech models to create audio from natural language text.\n *\n * @example Generate speech from text\n * ```ts\n * import { generateSpeech } from '@tanstack/ai'\n * import { openaiTTS } from '@tanstack/ai-openai'\n *\n * const result = await generateSpeech({\n * adapter: openaiTTS('tts-1-hd'),\n * text: 'Hello, welcome to TanStack AI!',\n * voice: 'nova'\n * })\n *\n * console.log(result.audio) // base64-encoded audio\n * ```\n *\n * @example With format and speed options\n * ```ts\n * const result = await generateSpeech({\n * adapter: openaiTTS('tts-1'),\n * text: 'This is slower speech.',\n * voice: 'alloy',\n * format: 'wav',\n * speed: 0.8\n * })\n * ```\n */\nexport async function generateSpeech<\n TAdapter extends TTSAdapter<string, object>,\n>(options: TTSActivityOptions<TAdapter>): TTSActivityResult {\n const { adapter, ...rest } = options\n const model = adapter.model\n const requestId = createId('speech')\n const startTime = Date.now()\n\n aiEventClient.emit('speech:request:started', {\n requestId,\n provider: adapter.name,\n model,\n text: rest.text,\n voice: rest.voice,\n format: rest.format,\n speed: rest.speed,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: startTime,\n })\n\n return adapter.generateSpeech({ ...rest, model }).then((result) => {\n const duration = Date.now() - startTime\n\n aiEventClient.emit('speech:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n audio: result.audio,\n format: result.format,\n audioDuration: result.duration,\n contentType: result.contentType,\n duration,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: Date.now(),\n })\n\n return result\n })\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the generateSpeech() function without executing.\n */\nexport function createSpeechOptions<\n TAdapter extends TTSAdapter<string, object>,\n>(options: TTSActivityOptions<TAdapter>): TTSActivityOptions<TAdapter> {\n return options\n}\n\n// Re-export adapter types\nexport type { TTSAdapter, TTSAdapterConfig, AnyTTSAdapter } from './adapter'\nexport { BaseTTSAdapter } from './adapter'\n"],"names":[],"mappings":";AAgBO,MAAM,OAAO;AAgDpB,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAoCA,eAAsB,eAEpB,SAA0D;AAC1D,QAAM,EAAE,SAAS,GAAG,KAAA,IAAS;AAC7B,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,QAAQ;AACnC,QAAM,YAAY,KAAK,IAAA;AAEvB,gBAAc,KAAK,0BAA0B;AAAA,IAC3C;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,MAAM,KAAK;AAAA,IACX,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,OAAO,KAAK;AAAA,IACZ,cAAc,KAAK;AAAA,IACnB,WAAW;AAAA,EAAA,CACZ;AAED,SAAO,QAAQ,eAAe,EAAE,GAAG,MAAM,OAAO,EAAE,KAAK,CAAC,WAAW;AACjE,UAAM,WAAW,KAAK,IAAA,IAAQ;AAE9B,kBAAc,KAAK,4BAA4B;AAAA,MAC7C;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,eAAe,OAAO;AAAA,MACtB,aAAa,OAAO;AAAA,MACpB;AAAA,MACA,cAAc,KAAK;AAAA,MACnB,WAAW,KAAK,IAAA;AAAA,IAAI,CACrB;AAED,WAAO;AAAA,EACT,CAAC;AACH;AASO,SAAS,oBAEd,SAAqE;AACrE,SAAO;AACT;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/activities/generateSpeech/index.ts"],"sourcesContent":["/**\n * TTS Activity\n *\n * Generates speech audio from text using text-to-speech models.\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 { TTSAdapter } from './adapter'\nimport type { StreamChunk, TTSResult } from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'tts' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/**\n * Extract provider options from a TTSAdapter via ~types.\n */\nexport type TTSProviderOptions<TAdapter> =\n TAdapter extends TTSAdapter<any, any>\n ? TAdapter['~types']['providerOptions']\n : object\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the TTS activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The TTS adapter type\n * @template TStream - Whether to stream the output\n */\nexport interface TTSActivityOptions<\n TAdapter extends TTSAdapter<string, object>,\n TStream extends boolean = false,\n> {\n /** The TTS adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\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 /** Provider-specific options for TTS generation */\n modelOptions?: TTSProviderOptions<TAdapter>\n /**\n * Whether to stream the generation result.\n * When true, returns an AsyncIterable<StreamChunk> for streaming transport.\n * When false or not provided, returns a Promise<TTSResult>.\n *\n * @default false\n */\n stream?: TStream\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the TTS activity.\n * - If stream is true: AsyncIterable<StreamChunk>\n * - Otherwise: Promise<TTSResult>\n */\nexport type TTSActivityResult<TStream extends boolean = false> =\n TStream extends true ? AsyncIterable<StreamChunk> : Promise<TTSResult>\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 * TTS activity - generates speech from text.\n *\n * Uses AI text-to-speech models to create audio from natural language text.\n *\n * @example Generate speech from text\n * ```ts\n * import { generateSpeech } from '@tanstack/ai'\n * import { openaiTTS } from '@tanstack/ai-openai'\n *\n * const result = await generateSpeech({\n * adapter: openaiTTS('tts-1-hd'),\n * text: 'Hello, welcome to TanStack AI!',\n * voice: 'nova'\n * })\n *\n * console.log(result.audio) // base64-encoded audio\n * ```\n *\n * @example With format and speed options\n * ```ts\n * const result = await generateSpeech({\n * adapter: openaiTTS('tts-1'),\n * text: 'This is slower speech.',\n * voice: 'alloy',\n * format: 'wav',\n * speed: 0.8\n * })\n * ```\n */\nexport function generateSpeech<\n TAdapter extends TTSAdapter<string, object>,\n TStream extends boolean = false,\n>(options: TTSActivityOptions<TAdapter, TStream>): TTSActivityResult<TStream> {\n if (options.stream) {\n return streamGenerationResult(() =>\n runGenerateSpeech(options),\n ) as TTSActivityResult<TStream>\n }\n return runGenerateSpeech(options) as TTSActivityResult<TStream>\n}\n\n/**\n * Run the core TTS generation logic (non-streaming).\n */\nasync function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(\n options: TTSActivityOptions<TAdapter, boolean>,\n): Promise<TTSResult> {\n const { adapter, stream: _stream, ...rest } = options\n const model = adapter.model\n const requestId = createId('speech')\n const startTime = Date.now()\n\n aiEventClient.emit('speech:request:started', {\n requestId,\n provider: adapter.name,\n model,\n text: rest.text,\n voice: rest.voice,\n format: rest.format,\n speed: rest.speed,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: startTime,\n })\n\n return adapter.generateSpeech({ ...rest, model }).then((result) => {\n const duration = Date.now() - startTime\n\n aiEventClient.emit('speech:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n audio: result.audio,\n format: result.format,\n audioDuration: result.duration,\n contentType: result.contentType,\n duration,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: Date.now(),\n })\n\n return result\n })\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the generateSpeech() function without executing.\n */\nexport function createSpeechOptions<\n TAdapter extends TTSAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: TTSActivityOptions<TAdapter, TStream>,\n): TTSActivityOptions<TAdapter, TStream> {\n return options\n}\n\n// Re-export adapter types\nexport type { TTSAdapter, TTSAdapterConfig, AnyTTSAdapter } from './adapter'\nexport { BaseTTSAdapter } from './adapter'\n"],"names":[],"mappings":";;AAiBO,MAAM,OAAO;AA+DpB,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAoCO,SAAS,eAGd,SAA4E;AAC5E,MAAI,QAAQ,QAAQ;AAClB,WAAO;AAAA,MAAuB,MAC5B,kBAAkB,OAAO;AAAA,IAAA;AAAA,EAE7B;AACA,SAAO,kBAAkB,OAAO;AAClC;AAKA,eAAe,kBACb,SACoB;AACpB,QAAM,EAAE,SAAS,QAAQ,SAAS,GAAG,SAAS;AAC9C,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,QAAQ;AACnC,QAAM,YAAY,KAAK,IAAA;AAEvB,gBAAc,KAAK,0BAA0B;AAAA,IAC3C;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,MAAM,KAAK;AAAA,IACX,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,OAAO,KAAK;AAAA,IACZ,cAAc,KAAK;AAAA,IACnB,WAAW;AAAA,EAAA,CACZ;AAED,SAAO,QAAQ,eAAe,EAAE,GAAG,MAAM,OAAO,EAAE,KAAK,CAAC,WAAW;AACjE,UAAM,WAAW,KAAK,IAAA,IAAQ;AAE9B,kBAAc,KAAK,4BAA4B;AAAA,MAC7C;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,eAAe,OAAO;AAAA,MACtB,aAAa,OAAO;AAAA,MACpB;AAAA,MACA,cAAc,KAAK;AAAA,MACnB,WAAW,KAAK,IAAA;AAAA,IAAI,CACrB;AAED,WAAO;AAAA,EACT,CAAC;AACH;AASO,SAAS,oBAId,SACuC;AACvC,SAAO;AACT;"}
@@ -1,5 +1,5 @@
1
1
  import { TranscriptionAdapter } from './adapter.js';
2
- import { TranscriptionResult } from '../../types.js';
2
+ import { StreamChunk, TranscriptionResult } from '../../types.js';
3
3
  /** The adapter kind this activity handles */
4
4
  export declare const kind: "transcription";
5
5
  /**
@@ -11,8 +11,9 @@ export type TranscriptionProviderOptions<TAdapter> = TAdapter extends Transcript
11
11
  * The model is extracted from the adapter's model property.
12
12
  *
13
13
  * @template TAdapter - The transcription adapter type
14
+ * @template TStream - Whether to stream the output
14
15
  */
15
- export interface TranscriptionActivityOptions<TAdapter extends TranscriptionAdapter<string, object>> {
16
+ export interface TranscriptionActivityOptions<TAdapter extends TranscriptionAdapter<string, object>, TStream extends boolean = false> {
16
17
  /** The transcription adapter to use (must be created with a model) */
17
18
  adapter: TAdapter & {
18
19
  kind: typeof kind;
@@ -27,9 +28,21 @@ export interface TranscriptionActivityOptions<TAdapter extends TranscriptionAdap
27
28
  responseFormat?: 'json' | 'text' | 'srt' | 'verbose_json' | 'vtt';
28
29
  /** Provider-specific options for transcription */
29
30
  modelOptions?: TranscriptionProviderOptions<TAdapter>;
31
+ /**
32
+ * Whether to stream the transcription result.
33
+ * When true, returns an AsyncIterable<StreamChunk> for streaming transport.
34
+ * When false or not provided, returns a Promise<TranscriptionResult>.
35
+ *
36
+ * @default false
37
+ */
38
+ stream?: TStream;
30
39
  }
31
- /** Result type for the transcription activity */
32
- export type TranscriptionActivityResult = Promise<TranscriptionResult>;
40
+ /**
41
+ * Result type for the transcription activity.
42
+ * - If stream is true: AsyncIterable<StreamChunk>
43
+ * - Otherwise: Promise<TranscriptionResult>
44
+ */
45
+ export type TranscriptionActivityResult<TStream extends boolean = false> = TStream extends true ? AsyncIterable<StreamChunk> : Promise<TranscriptionResult>;
33
46
  /**
34
47
  * Transcription activity - converts audio to text.
35
48
  *
@@ -61,11 +74,22 @@ export type TranscriptionActivityResult = Promise<TranscriptionResult>;
61
74
  * console.log(`[${segment.start}s - ${segment.end}s]: ${segment.text}`)
62
75
  * })
63
76
  * ```
77
+ *
78
+ * @example Streaming transcription result
79
+ * ```ts
80
+ * for await (const chunk of generateTranscription({
81
+ * adapter: openaiTranscription('whisper-1'),
82
+ * audio: audioFile,
83
+ * stream: true
84
+ * })) {
85
+ * console.log(chunk)
86
+ * }
87
+ * ```
64
88
  */
65
- export declare function generateTranscription<TAdapter extends TranscriptionAdapter<string, object>>(options: TranscriptionActivityOptions<TAdapter>): TranscriptionActivityResult;
89
+ export declare function generateTranscription<TAdapter extends TranscriptionAdapter<string, object>, TStream extends boolean = false>(options: TranscriptionActivityOptions<TAdapter, TStream>): TranscriptionActivityResult<TStream>;
66
90
  /**
67
91
  * Create typed options for the generateTranscription() function without executing.
68
92
  */
69
- export declare function createTranscriptionOptions<TAdapter extends TranscriptionAdapter<string, object>>(options: TranscriptionActivityOptions<TAdapter>): TranscriptionActivityOptions<TAdapter>;
93
+ export declare function createTranscriptionOptions<TAdapter extends TranscriptionAdapter<string, object>, TStream extends boolean = false>(options: TranscriptionActivityOptions<TAdapter, TStream>): TranscriptionActivityOptions<TAdapter, TStream>;
70
94
  export type { TranscriptionAdapter, TranscriptionAdapterConfig, AnyTranscriptionAdapter, } from './adapter.js';
71
95
  export { BaseTranscriptionAdapter } from './adapter.js';
@@ -1,10 +1,21 @@
1
1
  import { aiEventClient } from "../../event-client.js";
2
+ import { streamGenerationResult } from "../stream-generation-result.js";
2
3
  const kind = "transcription";
3
4
  function createId(prefix) {
4
5
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
5
6
  }
6
- async function generateTranscription(options) {
7
- const { adapter, ...rest } = options;
7
+ function generateTranscription(options) {
8
+ if (options.stream) {
9
+ return streamGenerationResult(
10
+ () => runGenerateTranscription(options)
11
+ );
12
+ }
13
+ return runGenerateTranscription(
14
+ options
15
+ );
16
+ }
17
+ async function runGenerateTranscription(options) {
18
+ const { adapter, stream: _stream, ...rest } = options;
8
19
  const model = adapter.model;
9
20
  const requestId = createId("transcription");
10
21
  const startTime = Date.now();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/activities/generateTranscription/index.ts"],"sourcesContent":["/**\n * Transcription Activity\n *\n * Transcribes audio to text using speech-to-text models.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '../../event-client.js'\nimport type { TranscriptionAdapter } from './adapter'\nimport type { TranscriptionResult } from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'transcription' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/**\n * Extract provider options from a TranscriptionAdapter via ~types.\n */\nexport type TranscriptionProviderOptions<TAdapter> =\n TAdapter extends TranscriptionAdapter<any, any>\n ? TAdapter['~types']['providerOptions']\n : object\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the transcription activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The transcription adapter type\n */\nexport interface TranscriptionActivityOptions<\n TAdapter extends TranscriptionAdapter<string, object>,\n> {\n /** The transcription adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\n /** The audio data to transcribe - can be base64 string, File, Blob, or Buffer */\n audio: string | File | Blob | ArrayBuffer\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 /** Provider-specific options for transcription */\n modelOptions?: TranscriptionProviderOptions<TAdapter>\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/** Result type for the transcription activity */\nexport type TranscriptionActivityResult = Promise<TranscriptionResult>\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 * Transcription activity - converts audio to text.\n *\n * Uses AI speech-to-text models to transcribe audio content.\n *\n * @example Transcribe an audio file\n * ```ts\n * import { generateTranscription } from '@tanstack/ai'\n * import { openaiTranscription } from '@tanstack/ai-openai'\n *\n * const result = await generateTranscription({\n * adapter: openaiTranscription('whisper-1'),\n * audio: audioFile, // File, Blob, or base64 string\n * language: 'en'\n * })\n *\n * console.log(result.text)\n * ```\n *\n * @example With verbose output for timestamps\n * ```ts\n * const result = await generateTranscription({\n * adapter: openaiTranscription('whisper-1'),\n * audio: audioFile,\n * responseFormat: 'verbose_json'\n * })\n *\n * result.segments?.forEach(segment => {\n * console.log(`[${segment.start}s - ${segment.end}s]: ${segment.text}`)\n * })\n * ```\n */\nexport async function generateTranscription<\n TAdapter extends TranscriptionAdapter<string, object>,\n>(\n options: TranscriptionActivityOptions<TAdapter>,\n): TranscriptionActivityResult {\n const { adapter, ...rest } = options\n const model = adapter.model\n const requestId = createId('transcription')\n const startTime = Date.now()\n\n aiEventClient.emit('transcription:request:started', {\n requestId,\n provider: adapter.name,\n model,\n language: rest.language,\n prompt: rest.prompt,\n responseFormat: rest.responseFormat,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: startTime,\n })\n\n const result = await adapter.transcribe({ ...rest, model })\n const duration = Date.now() - startTime\n\n aiEventClient.emit('transcription:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n text: result.text,\n language: result.language,\n duration,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: Date.now(),\n })\n\n return result\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the generateTranscription() function without executing.\n */\nexport function createTranscriptionOptions<\n TAdapter extends TranscriptionAdapter<string, object>,\n>(\n options: TranscriptionActivityOptions<TAdapter>,\n): TranscriptionActivityOptions<TAdapter> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n TranscriptionAdapter,\n TranscriptionAdapterConfig,\n AnyTranscriptionAdapter,\n} from './adapter'\nexport { BaseTranscriptionAdapter } from './adapter'\n"],"names":[],"mappings":";AAgBO,MAAM,OAAO;AAgDpB,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAsCA,eAAsB,sBAGpB,SAC6B;AAC7B,QAAM,EAAE,SAAS,GAAG,KAAA,IAAS;AAC7B,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,eAAe;AAC1C,QAAM,YAAY,KAAK,IAAA;AAEvB,gBAAc,KAAK,iCAAiC;AAAA,IAClD;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,UAAU,KAAK;AAAA,IACf,QAAQ,KAAK;AAAA,IACb,gBAAgB,KAAK;AAAA,IACrB,cAAc,KAAK;AAAA,IACnB,WAAW;AAAA,EAAA,CACZ;AAED,QAAM,SAAS,MAAM,QAAQ,WAAW,EAAE,GAAG,MAAM,OAAO;AAC1D,QAAM,WAAW,KAAK,IAAA,IAAQ;AAE9B,gBAAc,KAAK,mCAAmC;AAAA,IACpD;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,MAAM,OAAO;AAAA,IACb,UAAU,OAAO;AAAA,IACjB;AAAA,IACA,cAAc,KAAK;AAAA,IACnB,WAAW,KAAK,IAAA;AAAA,EAAI,CACrB;AAED,SAAO;AACT;AASO,SAAS,2BAGd,SACwC;AACxC,SAAO;AACT;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/activities/generateTranscription/index.ts"],"sourcesContent":["/**\n * Transcription Activity\n *\n * Transcribes audio to text using speech-to-text models.\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 { TranscriptionAdapter } from './adapter'\nimport type { StreamChunk, TranscriptionResult } from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'transcription' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/**\n * Extract provider options from a TranscriptionAdapter via ~types.\n */\nexport type TranscriptionProviderOptions<TAdapter> =\n TAdapter extends TranscriptionAdapter<any, any>\n ? TAdapter['~types']['providerOptions']\n : object\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the transcription activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The transcription adapter type\n * @template TStream - Whether to stream the output\n */\nexport interface TranscriptionActivityOptions<\n TAdapter extends TranscriptionAdapter<string, object>,\n TStream extends boolean = false,\n> {\n /** The transcription adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\n /** The audio data to transcribe - can be base64 string, File, Blob, or Buffer */\n audio: string | File | Blob | ArrayBuffer\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 /** Provider-specific options for transcription */\n modelOptions?: TranscriptionProviderOptions<TAdapter>\n /**\n * Whether to stream the transcription result.\n * When true, returns an AsyncIterable<StreamChunk> for streaming transport.\n * When false or not provided, returns a Promise<TranscriptionResult>.\n *\n * @default false\n */\n stream?: TStream\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the transcription activity.\n * - If stream is true: AsyncIterable<StreamChunk>\n * - Otherwise: Promise<TranscriptionResult>\n */\nexport type TranscriptionActivityResult<TStream extends boolean = false> =\n TStream extends true\n ? AsyncIterable<StreamChunk>\n : Promise<TranscriptionResult>\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 * Transcription activity - converts audio to text.\n *\n * Uses AI speech-to-text models to transcribe audio content.\n *\n * @example Transcribe an audio file\n * ```ts\n * import { generateTranscription } from '@tanstack/ai'\n * import { openaiTranscription } from '@tanstack/ai-openai'\n *\n * const result = await generateTranscription({\n * adapter: openaiTranscription('whisper-1'),\n * audio: audioFile, // File, Blob, or base64 string\n * language: 'en'\n * })\n *\n * console.log(result.text)\n * ```\n *\n * @example With verbose output for timestamps\n * ```ts\n * const result = await generateTranscription({\n * adapter: openaiTranscription('whisper-1'),\n * audio: audioFile,\n * responseFormat: 'verbose_json'\n * })\n *\n * result.segments?.forEach(segment => {\n * console.log(`[${segment.start}s - ${segment.end}s]: ${segment.text}`)\n * })\n * ```\n *\n * @example Streaming transcription result\n * ```ts\n * for await (const chunk of generateTranscription({\n * adapter: openaiTranscription('whisper-1'),\n * audio: audioFile,\n * stream: true\n * })) {\n * console.log(chunk)\n * }\n * ```\n */\nexport function generateTranscription<\n TAdapter extends TranscriptionAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: TranscriptionActivityOptions<TAdapter, TStream>,\n): TranscriptionActivityResult<TStream> {\n if (options.stream) {\n return streamGenerationResult(() =>\n runGenerateTranscription(options),\n ) as TranscriptionActivityResult<TStream>\n }\n\n return runGenerateTranscription(\n options,\n ) as TranscriptionActivityResult<TStream>\n}\n\n/**\n * Run non-streaming transcription\n */\nasync function runGenerateTranscription<\n TAdapter extends TranscriptionAdapter<string, object>,\n>(\n options: TranscriptionActivityOptions<TAdapter, boolean>,\n): Promise<TranscriptionResult> {\n const { adapter, stream: _stream, ...rest } = options\n const model = adapter.model\n const requestId = createId('transcription')\n const startTime = Date.now()\n\n aiEventClient.emit('transcription:request:started', {\n requestId,\n provider: adapter.name,\n model,\n language: rest.language,\n prompt: rest.prompt,\n responseFormat: rest.responseFormat,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: startTime,\n })\n\n const result = await adapter.transcribe({ ...rest, model })\n const duration = Date.now() - startTime\n\n aiEventClient.emit('transcription:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n text: result.text,\n language: result.language,\n duration,\n modelOptions: rest.modelOptions as Record<string, unknown> | undefined,\n timestamp: Date.now(),\n })\n\n return result\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the generateTranscription() function without executing.\n */\nexport function createTranscriptionOptions<\n TAdapter extends TranscriptionAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: TranscriptionActivityOptions<TAdapter, TStream>,\n): TranscriptionActivityOptions<TAdapter, TStream> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n TranscriptionAdapter,\n TranscriptionAdapterConfig,\n AnyTranscriptionAdapter,\n} from './adapter'\nexport { BaseTranscriptionAdapter } from './adapter'\n"],"names":[],"mappings":";;AAiBO,MAAM,OAAO;AAiEpB,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAiDO,SAAS,sBAId,SACsC;AACtC,MAAI,QAAQ,QAAQ;AAClB,WAAO;AAAA,MAAuB,MAC5B,yBAAyB,OAAO;AAAA,IAAA;AAAA,EAEpC;AAEA,SAAO;AAAA,IACL;AAAA,EAAA;AAEJ;AAKA,eAAe,yBAGb,SAC8B;AAC9B,QAAM,EAAE,SAAS,QAAQ,SAAS,GAAG,SAAS;AAC9C,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,eAAe;AAC1C,QAAM,YAAY,KAAK,IAAA;AAEvB,gBAAc,KAAK,iCAAiC;AAAA,IAClD;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,UAAU,KAAK;AAAA,IACf,QAAQ,KAAK;AAAA,IACb,gBAAgB,KAAK;AAAA,IACrB,cAAc,KAAK;AAAA,IACnB,WAAW;AAAA,EAAA,CACZ;AAED,QAAM,SAAS,MAAM,QAAQ,WAAW,EAAE,GAAG,MAAM,OAAO;AAC1D,QAAM,WAAW,KAAK,IAAA,IAAQ;AAE9B,gBAAc,KAAK,mCAAmC;AAAA,IACpD;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,MAAM,OAAO;AAAA,IACb,UAAU,OAAO;AAAA,IACjB;AAAA,IACA,cAAc,KAAK;AAAA,IACnB,WAAW,KAAK,IAAA;AAAA,EAAI,CACrB;AAED,SAAO;AACT;AASO,SAAS,2BAId,SACiD;AACjD,SAAO;AACT;"}
@@ -1,5 +1,5 @@
1
1
  import { VideoAdapter } from './adapter.js';
2
- import { VideoJobResult, VideoStatusResult, VideoUrlResult } from '../../types.js';
2
+ import { StreamChunk, VideoJobResult, VideoStatusResult, VideoUrlResult } from '../../types.js';
3
3
  /** The adapter kind this activity handles */
4
4
  export declare const kind: "video";
5
5
  /**
@@ -24,9 +24,12 @@ interface VideoActivityBaseOptions<TAdapter extends VideoAdapter<string, any, an
24
24
  * Options for creating a new video generation job.
25
25
  * The model is extracted from the adapter's model property.
26
26
  *
27
+ * @template TAdapter - The video adapter type
28
+ * @template TStream - Whether to stream the output
29
+ *
27
30
  * @experimental Video generation is an experimental feature and may change.
28
31
  */
29
- export type VideoCreateOptions<TAdapter extends VideoAdapter<string, any, any, any>> = VideoActivityBaseOptions<TAdapter> & {
32
+ export type VideoCreateOptions<TAdapter extends VideoAdapter<string, any, any, any>, TStream extends boolean = false> = VideoActivityBaseOptions<TAdapter> & {
30
33
  /** Request type - create a new job (default if not specified) */
31
34
  request?: 'create';
32
35
  /** Text description of the desired video */
@@ -35,6 +38,21 @@ export type VideoCreateOptions<TAdapter extends VideoAdapter<string, any, any, a
35
38
  size?: VideoSizeForAdapter<TAdapter>;
36
39
  /** Video duration in seconds */
37
40
  duration?: number;
41
+ /**
42
+ * Whether to stream the video generation lifecycle.
43
+ * When true, returns an AsyncIterable<StreamChunk> that handles the full
44
+ * job lifecycle: create job, poll for status, yield updates, and yield final result.
45
+ * When false or not provided, returns a Promise<VideoJobResult>.
46
+ *
47
+ * @default false
48
+ */
49
+ stream?: TStream;
50
+ /** Polling interval in milliseconds (stream mode only). @default 2000 */
51
+ pollingInterval?: number;
52
+ /** Maximum time to wait before timing out in milliseconds (stream mode only). @default 600000 */
53
+ maxDuration?: number;
54
+ /** Custom run ID (stream mode only) */
55
+ runId?: string;
38
56
  } & ({} extends VideoProviderOptions<TAdapter> ? {
39
57
  /** Provider-specific options for video generation */ modelOptions?: VideoProviderOptions<TAdapter>;
40
58
  } : {
@@ -68,19 +86,24 @@ export interface VideoUrlOptions<TAdapter extends VideoAdapter<string, any, any,
68
86
  *
69
87
  * @experimental Video generation is an experimental feature and may change.
70
88
  */
71
- export type VideoActivityOptions<TAdapter extends VideoAdapter<string, any, any, any>, TRequest extends 'create' | 'status' | 'url' = 'create'> = TRequest extends 'status' ? VideoStatusOptions<TAdapter> : TRequest extends 'url' ? VideoUrlOptions<TAdapter> : VideoCreateOptions<TAdapter>;
89
+ export type VideoActivityOptions<TAdapter extends VideoAdapter<string, any, any, any>, TRequest extends 'create' | 'status' | 'url' = 'create', TStream extends boolean = false> = TRequest extends 'status' ? VideoStatusOptions<TAdapter> : TRequest extends 'url' ? VideoUrlOptions<TAdapter> : VideoCreateOptions<TAdapter, TStream>;
72
90
  /**
73
- * Result type for the video activity, based on request type.
91
+ * Result type for the video activity, based on request type and streaming.
92
+ * - If stream is true (create request): AsyncIterable<StreamChunk>
93
+ * - Otherwise: Promise<VideoJobResult | VideoStatusResult | VideoUrlResult>
74
94
  *
75
95
  * @experimental Video generation is an experimental feature and may change.
76
96
  */
77
- export type VideoActivityResult<TRequest extends 'create' | 'status' | 'url' = 'create'> = TRequest extends 'status' ? Promise<VideoStatusResult> : TRequest extends 'url' ? Promise<VideoUrlResult> : Promise<VideoJobResult>;
97
+ export type VideoActivityResult<TRequest extends 'create' | 'status' | 'url' = 'create', TStream extends boolean = false> = TRequest extends 'status' ? Promise<VideoStatusResult> : TRequest extends 'url' ? Promise<VideoUrlResult> : TStream extends true ? AsyncIterable<StreamChunk> : Promise<VideoJobResult>;
78
98
  /**
79
99
  * Generate video - creates a video generation job from a text prompt.
80
100
  *
81
101
  * Uses AI video generation models to create videos based on natural language descriptions.
82
102
  * Unlike image generation, video generation is asynchronous and requires polling for completion.
83
103
  *
104
+ * When `stream: true` is passed, handles the full job lifecycle automatically:
105
+ * create job → poll for status → stream updates → yield final result.
106
+ *
84
107
  * @experimental Video generation is an experimental feature and may change.
85
108
  *
86
109
  * @example Create a video generation job
@@ -96,8 +119,23 @@ export type VideoActivityResult<TRequest extends 'create' | 'status' | 'url' = '
96
119
  *
97
120
  * console.log('Job started:', jobId)
98
121
  * ```
122
+ *
123
+ * @example Stream the full video generation lifecycle
124
+ * ```ts
125
+ * import { generateVideo, toServerSentEventsResponse } from '@tanstack/ai'
126
+ * import { openaiVideo } from '@tanstack/ai-openai'
127
+ *
128
+ * const stream = generateVideo({
129
+ * adapter: openaiVideo('sora-2'),
130
+ * prompt: 'A cat chasing a dog in a sunny park',
131
+ * stream: true,
132
+ * pollingInterval: 3000,
133
+ * })
134
+ *
135
+ * return toServerSentEventsResponse(stream)
136
+ * ```
99
137
  */
100
- export declare function generateVideo<TAdapter extends VideoAdapter<string, any, any, any>>(options: VideoCreateOptions<TAdapter>): Promise<VideoJobResult>;
138
+ export declare function generateVideo<TAdapter extends VideoAdapter<string, any, any, any>, TStream extends boolean = false>(options: VideoCreateOptions<TAdapter, TStream>): VideoActivityResult<'create', TStream>;
101
139
  /**
102
140
  * Get video job status - returns the current status, progress, and URL if available.
103
141
  *
@@ -137,6 +175,6 @@ export declare function getVideoJobStatus<TAdapter extends VideoAdapter<string,
137
175
  /**
138
176
  * Create typed options for the generateVideo() function without executing.
139
177
  */
140
- export declare function createVideoOptions<TAdapter extends VideoAdapter<string, any, any, any>>(options: VideoCreateOptions<TAdapter>): VideoCreateOptions<TAdapter>;
178
+ export declare function createVideoOptions<TAdapter extends VideoAdapter<string, any, any, any>, TStream extends boolean = false>(options: VideoCreateOptions<TAdapter, TStream>): VideoCreateOptions<TAdapter, TStream>;
141
179
  export type { VideoAdapter, VideoAdapterConfig, AnyVideoAdapter, } from './adapter.js';
142
180
  export { BaseVideoAdapter } from './adapter.js';
@@ -3,7 +3,15 @@ const kind = "video";
3
3
  function createId(prefix) {
4
4
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
5
5
  }
6
- async function generateVideo(options) {
6
+ function generateVideo(options) {
7
+ if (options.stream) {
8
+ return runStreamingVideoGeneration(
9
+ options
10
+ );
11
+ }
12
+ return runCreateVideoJob(options);
13
+ }
14
+ async function runCreateVideoJob(options) {
7
15
  const { adapter, prompt, size, duration, modelOptions } = options;
8
16
  const model = adapter.model;
9
17
  return adapter.createVideoJob({
@@ -14,6 +22,87 @@ async function generateVideo(options) {
14
22
  modelOptions
15
23
  });
16
24
  }
25
+ function sleep(ms) {
26
+ return new Promise((resolve) => setTimeout(resolve, ms));
27
+ }
28
+ async function* runStreamingVideoGeneration(options) {
29
+ const { adapter, prompt, size, duration, modelOptions } = options;
30
+ const model = adapter.model;
31
+ const runId = options.runId ?? createId("run");
32
+ const pollingInterval = options.pollingInterval ?? 2e3;
33
+ const maxDuration = options.maxDuration ?? 6e5;
34
+ yield {
35
+ type: "RUN_STARTED",
36
+ runId,
37
+ timestamp: Date.now()
38
+ };
39
+ try {
40
+ const jobResult = await adapter.createVideoJob({
41
+ model,
42
+ prompt,
43
+ size,
44
+ duration,
45
+ modelOptions
46
+ });
47
+ yield {
48
+ type: "CUSTOM",
49
+ name: "video:job:created",
50
+ value: { jobId: jobResult.jobId },
51
+ timestamp: Date.now()
52
+ };
53
+ const startTime = Date.now();
54
+ while (Date.now() - startTime < maxDuration) {
55
+ await sleep(pollingInterval);
56
+ const statusResult = await adapter.getVideoStatus(jobResult.jobId);
57
+ yield {
58
+ type: "CUSTOM",
59
+ name: "video:status",
60
+ value: {
61
+ jobId: jobResult.jobId,
62
+ status: statusResult.status,
63
+ progress: statusResult.progress,
64
+ error: statusResult.error
65
+ },
66
+ timestamp: Date.now()
67
+ };
68
+ if (statusResult.status === "completed") {
69
+ const urlResult = await adapter.getVideoUrl(jobResult.jobId);
70
+ yield {
71
+ type: "CUSTOM",
72
+ name: "generation:result",
73
+ value: {
74
+ jobId: jobResult.jobId,
75
+ status: "completed",
76
+ url: urlResult.url,
77
+ expiresAt: urlResult.expiresAt
78
+ },
79
+ timestamp: Date.now()
80
+ };
81
+ yield {
82
+ type: "RUN_FINISHED",
83
+ runId,
84
+ finishReason: "stop",
85
+ timestamp: Date.now()
86
+ };
87
+ return;
88
+ }
89
+ if (statusResult.status === "failed") {
90
+ throw new Error(statusResult.error || "Video generation failed");
91
+ }
92
+ }
93
+ throw new Error("Video generation timed out");
94
+ } catch (error) {
95
+ yield {
96
+ type: "RUN_ERROR",
97
+ runId,
98
+ error: {
99
+ message: error.message || "Video generation failed",
100
+ code: error.code
101
+ },
102
+ timestamp: Date.now()
103
+ };
104
+ }
105
+ }
17
106
  async function getVideoJobStatus(options) {
18
107
  const { adapter, jobId } = options;
19
108
  const requestId = createId("video-status");