@tanstack/ai 0.11.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/activities/chat/adapter.d.ts +12 -4
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/index.d.ts +23 -3
- package/dist/esm/activities/chat/index.js +78 -17
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +3 -1
- package/dist/esm/activities/chat/middleware/compose.js +79 -1
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/generateImage/index.d.ts +7 -0
- package/dist/esm/activities/generateImage/index.js +19 -3
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +7 -0
- package/dist/esm/activities/generateSpeech/index.js +21 -3
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +7 -0
- package/dist/esm/activities/generateTranscription/index.js +32 -13
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +7 -0
- package/dist/esm/activities/generateVideo/index.js +49 -7
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/summarize/index.d.ts +7 -0
- package/dist/esm/activities/summarize/index.js +54 -19
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +3 -0
- package/dist/esm/adapter-internals.js +7 -0
- package/dist/esm/adapter-internals.js.map +1 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logger/console-logger.d.ts +11 -0
- package/dist/esm/logger/console-logger.js +27 -0
- package/dist/esm/logger/console-logger.js.map +1 -0
- package/dist/esm/logger/internal-logger.d.ts +33 -0
- package/dist/esm/logger/internal-logger.js +69 -0
- package/dist/esm/logger/internal-logger.js.map +1 -0
- package/dist/esm/logger/resolve.d.ts +14 -0
- package/dist/esm/logger/resolve.js +54 -0
- package/dist/esm/logger/resolve.js.map +1 -0
- package/dist/esm/logger/types.d.ts +75 -0
- package/dist/esm/tools/provider-tool.d.ts +21 -0
- package/dist/esm/types.d.ts +34 -0
- package/package.json +6 -2
- package/skills/ai-core/SKILL.md +5 -3
- package/skills/ai-core/debug-logging/SKILL.md +263 -0
- package/src/activities/chat/adapter.ts +14 -3
- package/src/activities/chat/index.ts +119 -24
- package/src/activities/chat/middleware/compose.ts +84 -1
- package/src/activities/generateImage/index.ts +29 -3
- package/src/activities/generateSpeech/index.ts +35 -3
- package/src/activities/generateTranscription/index.ts +45 -13
- package/src/activities/generateVideo/index.ts +65 -6
- package/src/activities/summarize/index.ts +66 -20
- package/src/adapter-internals.ts +7 -0
- package/src/index.ts +12 -0
- package/src/logger/console-logger.ts +49 -0
- package/src/logger/internal-logger.ts +107 -0
- package/src/logger/resolve.ts +72 -0
- package/src/logger/types.ts +78 -0
- package/src/tools/provider-tool.ts +25 -0
- package/src/types.ts +36 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.js","sources":["../../../../../src/activities/chat/middleware/compose.ts"],"sourcesContent":["import { aiEventClient } from '@tanstack/ai-event-client'\nimport type { StreamChunk } from '../../../types'\nimport type {\n AbortInfo,\n AfterToolCallInfo,\n BeforeToolCallDecision,\n ChatMiddleware,\n ChatMiddlewareConfig,\n ChatMiddlewareContext,\n ErrorInfo,\n FinishInfo,\n IterationInfo,\n ToolCallHookContext,\n ToolPhaseCompleteInfo,\n UsageInfo,\n} from './types'\n\n/** Check if a middleware should be skipped for instrumentation events. */\nfunction shouldSkipInstrumentation(mw: ChatMiddleware): boolean {\n return mw.name === 'devtools' || mw.name === 'strip-to-spec'\n}\n\n/** Build the base context for middleware instrumentation events. */\nfunction instrumentCtx(ctx: ChatMiddlewareContext) {\n return {\n requestId: ctx.requestId,\n streamId: ctx.streamId,\n clientId: ctx.conversationId,\n timestamp: Date.now(),\n }\n}\n\n/**\n * Internal middleware runner that manages composed execution of middleware hooks.\n * Created once per chat() invocation.\n */\nexport class MiddlewareRunner {\n private readonly middlewares: ReadonlyArray<ChatMiddleware>\n\n constructor(middlewares: ReadonlyArray<ChatMiddleware>) {\n this.middlewares = middlewares\n }\n\n get hasMiddleware(): boolean {\n return this.middlewares.length > 0\n }\n\n /**\n * Pipe config through all middleware onConfig hooks in order.\n * Each middleware receives the merged config from previous middleware.\n * Partial returns are shallow-merged with the current config.\n */\n async runOnConfig(\n ctx: ChatMiddlewareContext,\n config: ChatMiddlewareConfig,\n ): Promise<ChatMiddlewareConfig> {\n let current = config\n for (const mw of this.middlewares) {\n if (mw.onConfig) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n const result = await mw.onConfig(ctx, current)\n const hasTransform = result !== undefined && result !== null\n if (hasTransform) {\n current = { ...current, ...result }\n }\n if (!skip) {\n const base = instrumentCtx(ctx)\n aiEventClient.emit('middleware:hook:executed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n hookName: 'onConfig',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform,\n })\n if (hasTransform) {\n aiEventClient.emit('middleware:config:transformed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n iteration: ctx.iteration,\n changes: result as Record<string, unknown>,\n })\n }\n }\n }\n }\n return current\n }\n\n /**\n * Call onStart on all middleware in order.\n */\n async runOnStart(ctx: ChatMiddlewareContext): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onStart) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onStart(ctx)\n if (!skip) {\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onStart',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Pipe a single chunk through all middleware onChunk hooks in order.\n * Returns the resulting chunks (0..N) to yield to the consumer.\n *\n * - void: pass through unchanged\n * - chunk: replace with this chunk\n * - chunk[]: expand to multiple chunks\n * - null: drop the chunk entirely\n */\n async runOnChunk(\n ctx: ChatMiddlewareContext,\n chunk: StreamChunk,\n ): Promise<Array<StreamChunk>> {\n let chunks: Array<StreamChunk> = [chunk]\n\n for (const mw of this.middlewares) {\n if (!mw.onChunk) continue\n const skip = shouldSkipInstrumentation(mw)\n\n const nextChunks: Array<StreamChunk> = []\n for (const c of chunks) {\n // Cast: @ag-ui/core Zod passthrough types prevent direct `.type` access\n const chunkType = (c as StreamChunk & { type: string }).type\n const result = await mw.onChunk(ctx, c)\n if (result === null) {\n // Drop this chunk\n if (!skip) {\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: 0,\n wasDropped: true,\n })\n }\n continue\n } else if (result === undefined) {\n // Pass through — no instrumentation for pass-throughs\n nextChunks.push(c)\n } else if (Array.isArray(result)) {\n // Expand\n nextChunks.push(...result)\n if (!skip) {\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: result.length,\n wasDropped: false,\n })\n }\n } else {\n // Replace\n nextChunks.push(result)\n if (!skip) {\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: 1,\n wasDropped: false,\n })\n }\n }\n }\n chunks = nextChunks\n }\n\n return chunks\n }\n\n /**\n * Run onBeforeToolCall through middleware in order.\n * Returns the first non-void decision, or undefined to continue normally.\n */\n async runOnBeforeToolCall(\n ctx: ChatMiddlewareContext,\n hookCtx: ToolCallHookContext,\n ): Promise<BeforeToolCallDecision> {\n for (const mw of this.middlewares) {\n if (mw.onBeforeToolCall) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n const decision = await mw.onBeforeToolCall(ctx, hookCtx)\n const hasTransform = decision !== undefined && decision !== null\n if (!skip) {\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onBeforeToolCall',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform,\n })\n }\n if (hasTransform) {\n return decision\n }\n }\n }\n return undefined\n }\n\n /**\n * Run onAfterToolCall on all middleware in order.\n */\n async runOnAfterToolCall(\n ctx: ChatMiddlewareContext,\n info: AfterToolCallInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onAfterToolCall) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onAfterToolCall(ctx, info)\n if (!skip) {\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onAfterToolCall',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onUsage on all middleware in order.\n */\n async runOnUsage(\n ctx: ChatMiddlewareContext,\n usage: UsageInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onUsage) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onUsage(ctx, usage)\n if (!skip) {\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onUsage',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onFinish on all middleware in order.\n */\n async runOnFinish(\n ctx: ChatMiddlewareContext,\n info: FinishInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onFinish) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onFinish(ctx, info)\n if (!skip) {\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onFinish',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onAbort on all middleware in order.\n */\n async runOnAbort(ctx: ChatMiddlewareContext, info: AbortInfo): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onAbort) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onAbort(ctx, info)\n if (!skip) {\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onAbort',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onError on all middleware in order.\n */\n async runOnError(ctx: ChatMiddlewareContext, info: ErrorInfo): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onError) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onError(ctx, info)\n if (!skip) {\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onError',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onIteration on all middleware in order.\n * Called at the start of each agent loop iteration.\n */\n async runOnIteration(\n ctx: ChatMiddlewareContext,\n info: IterationInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onIteration) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onIteration(ctx, info)\n if (!skip) {\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onIteration',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onToolPhaseComplete on all middleware in order.\n * Called after all tool calls in an iteration have been processed.\n */\n async runOnToolPhaseComplete(\n ctx: ChatMiddlewareContext,\n info: ToolPhaseCompleteInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onToolPhaseComplete) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onToolPhaseComplete(ctx, info)\n if (!skip) {\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onToolPhaseComplete',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"compose.js","sources":["../../../../../src/activities/chat/middleware/compose.ts"],"sourcesContent":["import { aiEventClient } from '@tanstack/ai-event-client'\nimport type { StreamChunk } from '../../../types'\nimport type { InternalLogger } from '../../../logger/internal-logger'\nimport type {\n AbortInfo,\n AfterToolCallInfo,\n BeforeToolCallDecision,\n ChatMiddleware,\n ChatMiddlewareConfig,\n ChatMiddlewareContext,\n ErrorInfo,\n FinishInfo,\n IterationInfo,\n ToolCallHookContext,\n ToolPhaseCompleteInfo,\n UsageInfo,\n} from './types'\n\n/** Check if a middleware should be skipped for instrumentation events. */\nfunction shouldSkipInstrumentation(mw: ChatMiddleware): boolean {\n return mw.name === 'devtools' || mw.name === 'strip-to-spec'\n}\n\n/** Build the base context for middleware instrumentation events. */\nfunction instrumentCtx(ctx: ChatMiddlewareContext) {\n return {\n requestId: ctx.requestId,\n streamId: ctx.streamId,\n clientId: ctx.conversationId,\n timestamp: Date.now(),\n }\n}\n\n/**\n * Internal middleware runner that manages composed execution of middleware hooks.\n * Created once per chat() invocation.\n */\nexport class MiddlewareRunner {\n private readonly middlewares: ReadonlyArray<ChatMiddleware>\n private readonly logger: InternalLogger\n\n constructor(\n middlewares: ReadonlyArray<ChatMiddleware>,\n logger: InternalLogger,\n ) {\n this.middlewares = middlewares\n this.logger = logger\n }\n\n get hasMiddleware(): boolean {\n return this.middlewares.length > 0\n }\n\n /**\n * Pipe config through all middleware onConfig hooks in order.\n * Each middleware receives the merged config from previous middleware.\n * Partial returns are shallow-merged with the current config.\n */\n async runOnConfig(\n ctx: ChatMiddlewareContext,\n config: ChatMiddlewareConfig,\n ): Promise<ChatMiddlewareConfig> {\n let current = config\n for (const mw of this.middlewares) {\n if (mw.onConfig) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n const result = await mw.onConfig(ctx, current)\n const hasTransform = result !== undefined && result !== null\n if (hasTransform) {\n current = { ...current, ...result }\n if (!skip) {\n this.logger.config(\n `middleware=${mw.name ?? 'unnamed'} keys=${Object.keys(result as object).join(',')}`,\n {\n middleware: mw.name ?? 'unnamed',\n changes: result,\n },\n )\n }\n }\n if (!skip) {\n const base = instrumentCtx(ctx)\n aiEventClient.emit('middleware:hook:executed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n hookName: 'onConfig',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform,\n })\n if (hasTransform) {\n aiEventClient.emit('middleware:config:transformed', {\n ...base,\n middlewareName: mw.name || 'unnamed',\n iteration: ctx.iteration,\n changes: result as Record<string, unknown>,\n })\n }\n }\n }\n }\n return current\n }\n\n /**\n * Call onStart on all middleware in order.\n */\n async runOnStart(ctx: ChatMiddlewareContext): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onStart) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onStart(ctx)\n if (!skip) {\n this.logger.middleware(\n `hook=onStart middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onStart' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onStart',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Pipe a single chunk through all middleware onChunk hooks in order.\n * Returns the resulting chunks (0..N) to yield to the consumer.\n *\n * - void: pass through unchanged\n * - chunk: replace with this chunk\n * - chunk[]: expand to multiple chunks\n * - null: drop the chunk entirely\n */\n async runOnChunk(\n ctx: ChatMiddlewareContext,\n chunk: StreamChunk,\n ): Promise<Array<StreamChunk>> {\n let chunks: Array<StreamChunk> = [chunk]\n\n for (const mw of this.middlewares) {\n if (!mw.onChunk) continue\n const skip = shouldSkipInstrumentation(mw)\n\n const nextChunks: Array<StreamChunk> = []\n for (const c of chunks) {\n // Cast: @ag-ui/core Zod passthrough types prevent direct `.type` access\n const chunkType = (c as StreamChunk & { type: string }).type\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onChunk', in: c },\n )\n }\n const result = await mw.onChunk(ctx, c)\n if (result === null) {\n // Drop this chunk\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=<dropped>`,\n {\n middleware: mw.name ?? 'unnamed',\n hook: 'onChunk',\n dropped: true,\n },\n )\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: 0,\n wasDropped: true,\n })\n }\n continue\n } else if (result === undefined) {\n // Pass through — no instrumentation for pass-throughs\n nextChunks.push(c)\n } else if (Array.isArray(result)) {\n // Expand\n nextChunks.push(...result)\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=[${result.map((r: StreamChunk) => (r as StreamChunk & { type: string }).type).join(',')}]`,\n {\n middleware: mw.name ?? 'unnamed',\n hook: 'onChunk',\n in: c,\n out: result,\n },\n )\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: result.length,\n wasDropped: false,\n })\n }\n } else {\n // Replace\n nextChunks.push(result)\n if (!skip) {\n this.logger.middleware(\n `hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=${(result as StreamChunk & { type: string }).type}`,\n {\n middleware: mw.name ?? 'unnamed',\n hook: 'onChunk',\n in: c,\n out: result,\n },\n )\n aiEventClient.emit('middleware:chunk:transformed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n originalChunkType: chunkType,\n resultCount: 1,\n wasDropped: false,\n })\n }\n }\n }\n chunks = nextChunks\n }\n\n return chunks\n }\n\n /**\n * Run onBeforeToolCall through middleware in order.\n * Returns the first non-void decision, or undefined to continue normally.\n */\n async runOnBeforeToolCall(\n ctx: ChatMiddlewareContext,\n hookCtx: ToolCallHookContext,\n ): Promise<BeforeToolCallDecision> {\n for (const mw of this.middlewares) {\n if (mw.onBeforeToolCall) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n const decision = await mw.onBeforeToolCall(ctx, hookCtx)\n const hasTransform = decision !== undefined && decision !== null\n if (!skip) {\n this.logger.middleware(\n `hook=onBeforeToolCall middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onBeforeToolCall' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onBeforeToolCall',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform,\n })\n }\n if (hasTransform) {\n return decision\n }\n }\n }\n return undefined\n }\n\n /**\n * Run onAfterToolCall on all middleware in order.\n */\n async runOnAfterToolCall(\n ctx: ChatMiddlewareContext,\n info: AfterToolCallInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onAfterToolCall) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onAfterToolCall(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onAfterToolCall middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onAfterToolCall' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onAfterToolCall',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onUsage on all middleware in order.\n */\n async runOnUsage(\n ctx: ChatMiddlewareContext,\n usage: UsageInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onUsage) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onUsage(ctx, usage)\n if (!skip) {\n this.logger.middleware(\n `hook=onUsage middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onUsage' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onUsage',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onFinish on all middleware in order.\n */\n async runOnFinish(\n ctx: ChatMiddlewareContext,\n info: FinishInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onFinish) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onFinish(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onFinish middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onFinish' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onFinish',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onAbort on all middleware in order.\n */\n async runOnAbort(ctx: ChatMiddlewareContext, info: AbortInfo): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onAbort) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onAbort(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onAbort middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onAbort' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onAbort',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onError on all middleware in order.\n */\n async runOnError(ctx: ChatMiddlewareContext, info: ErrorInfo): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onError) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onError(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onError middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onError' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onError',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onIteration on all middleware in order.\n * Called at the start of each agent loop iteration.\n */\n async runOnIteration(\n ctx: ChatMiddlewareContext,\n info: IterationInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onIteration) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onIteration(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onIteration middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onIteration' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onIteration',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n\n /**\n * Run onToolPhaseComplete on all middleware in order.\n * Called after all tool calls in an iteration have been processed.\n */\n async runOnToolPhaseComplete(\n ctx: ChatMiddlewareContext,\n info: ToolPhaseCompleteInfo,\n ): Promise<void> {\n for (const mw of this.middlewares) {\n if (mw.onToolPhaseComplete) {\n const skip = shouldSkipInstrumentation(mw)\n const start = Date.now()\n await mw.onToolPhaseComplete(ctx, info)\n if (!skip) {\n this.logger.middleware(\n `hook=onToolPhaseComplete middleware=${mw.name ?? 'unnamed'}`,\n { middleware: mw.name ?? 'unnamed', hook: 'onToolPhaseComplete' },\n )\n aiEventClient.emit('middleware:hook:executed', {\n ...instrumentCtx(ctx),\n middlewareName: mw.name || 'unnamed',\n hookName: 'onToolPhaseComplete',\n iteration: ctx.iteration,\n duration: Date.now() - start,\n hasTransform: false,\n })\n }\n }\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DebugOption } from '../../logger/types.js';
|
|
1
2
|
import { ImageAdapter } from './adapter.js';
|
|
2
3
|
import { ImageGenerationResult, StreamChunk } from '../../types.js';
|
|
3
4
|
/** The adapter kind this activity handles */
|
|
@@ -39,6 +40,12 @@ export type ImageActivityOptions<TAdapter extends ImageAdapter<string, any, any,
|
|
|
39
40
|
* @default false
|
|
40
41
|
*/
|
|
41
42
|
stream?: TStream;
|
|
43
|
+
/**
|
|
44
|
+
* Enable debug logging. Pass `true` to enable all categories, `false` to
|
|
45
|
+
* silence everything including errors, or a `DebugConfig` object for granular
|
|
46
|
+
* control and/or a custom `Logger`.
|
|
47
|
+
*/
|
|
48
|
+
debug?: DebugOption;
|
|
42
49
|
} & ({} extends ImageProviderOptionsForModel<TAdapter, TAdapter['model']> ? {
|
|
43
50
|
/** Provider-specific options for image generation */ modelOptions?: ImageProviderOptionsForModel<TAdapter, TAdapter['model']>;
|
|
44
51
|
} : {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { aiEventClient } from "@tanstack/ai-event-client";
|
|
2
2
|
import { streamGenerationResult } from "../stream-generation-result.js";
|
|
3
|
+
import { resolveDebugOption } from "../../logger/resolve.js";
|
|
3
4
|
const kind = "image";
|
|
4
5
|
function createId(prefix) {
|
|
5
6
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -13,10 +14,11 @@ function generateImage(options) {
|
|
|
13
14
|
return runGenerateImage(options);
|
|
14
15
|
}
|
|
15
16
|
async function runGenerateImage(options) {
|
|
16
|
-
const { adapter, stream: _stream, ...rest } = options;
|
|
17
|
+
const { adapter, stream: _stream, debug: _debug, ...rest } = options;
|
|
17
18
|
const model = adapter.model;
|
|
18
19
|
const requestId = createId("image");
|
|
19
20
|
const startTime = Date.now();
|
|
21
|
+
const logger = resolveDebugOption(options.debug);
|
|
20
22
|
aiEventClient.emit("image:request:started", {
|
|
21
23
|
requestId,
|
|
22
24
|
provider: adapter.name,
|
|
@@ -27,7 +29,12 @@ async function runGenerateImage(options) {
|
|
|
27
29
|
modelOptions: rest.modelOptions,
|
|
28
30
|
timestamp: startTime
|
|
29
31
|
});
|
|
30
|
-
|
|
32
|
+
logger.request(`activity=generateImage provider=${adapter.name}`, {
|
|
33
|
+
provider: adapter.name,
|
|
34
|
+
model
|
|
35
|
+
});
|
|
36
|
+
try {
|
|
37
|
+
const result = await adapter.generateImages({ ...rest, model, logger });
|
|
31
38
|
const duration = Date.now() - startTime;
|
|
32
39
|
aiEventClient.emit("image:request:completed", {
|
|
33
40
|
requestId,
|
|
@@ -50,8 +57,17 @@ async function runGenerateImage(options) {
|
|
|
50
57
|
timestamp: Date.now()
|
|
51
58
|
});
|
|
52
59
|
}
|
|
60
|
+
logger.output(`activity=generateImage count=${result.images.length}`, {
|
|
61
|
+
count: result.images.length
|
|
62
|
+
});
|
|
53
63
|
return result;
|
|
54
|
-
})
|
|
64
|
+
} catch (error) {
|
|
65
|
+
logger.errors("generateImage activity failed", {
|
|
66
|
+
error,
|
|
67
|
+
source: "generateImage"
|
|
68
|
+
});
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
55
71
|
}
|
|
56
72
|
function createImageOptions(options) {
|
|
57
73
|
return options;
|
|
@@ -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 '@tanstack/ai-event-client'\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
|
+
{"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 '@tanstack/ai-event-client'\nimport { streamGenerationResult } from '../stream-generation-result.js'\nimport { resolveDebugOption } from '../../logger/resolve'\nimport type { InternalLogger } from '../../logger/internal-logger'\nimport type { DebugOption } from '../../logger/types'\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 /**\n * Enable debug logging. Pass `true` to enable all categories, `false` to\n * silence everything including errors, or a `DebugConfig` object for granular\n * control and/or a custom `Logger`.\n */\n debug?: DebugOption\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, debug: _debug, ...rest } = options\n const model = adapter.model\n const requestId = createId('image')\n const startTime = Date.now()\n const logger: InternalLogger = resolveDebugOption(options.debug)\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 logger.request(`activity=generateImage provider=${adapter.name}`, {\n provider: adapter.name,\n model,\n })\n\n try {\n const result = await adapter.generateImages({ ...rest, model, logger })\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 logger.output(`activity=generateImage count=${result.images.length}`, {\n count: result.images.length,\n })\n\n return result\n } catch (error) {\n logger.errors('generateImage activity failed', {\n error,\n source: 'generateImage',\n })\n throw error\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":";;;AAoBO,MAAM,OAAO;AAsGpB,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,OAAO,QAAQ,GAAG,SAAS;AAC7D,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,OAAO;AAClC,QAAM,YAAY,KAAK,IAAA;AACvB,QAAM,SAAyB,mBAAmB,QAAQ,KAAK;AAE/D,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,mCAAmC,QAAQ,IAAI,IAAI;AAAA,IAChE,UAAU,QAAQ;AAAA,IAClB;AAAA,EAAA,CACD;AAED,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ,eAAe,EAAE,GAAG,MAAM,OAAO,QAAQ;AACtE,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,OAAO,gCAAgC,OAAO,OAAO,MAAM,IAAI;AAAA,MACpE,OAAO,OAAO,OAAO;AAAA,IAAA,CACtB;AAED,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO,OAAO,iCAAiC;AAAA,MAC7C;AAAA,MACA,QAAQ;AAAA,IAAA,CACT;AACD,UAAM;AAAA,EACR;AACF;AASO,SAAS,mBAId,SACyC;AACzC,SAAO;AACT;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DebugOption } from '../../logger/types.js';
|
|
1
2
|
import { TTSAdapter } from './adapter.js';
|
|
2
3
|
import { StreamChunk, TTSResult } from '../../types.js';
|
|
3
4
|
/** The adapter kind this activity handles */
|
|
@@ -36,6 +37,12 @@ export interface TTSActivityOptions<TAdapter extends TTSAdapter<string, object>,
|
|
|
36
37
|
* @default false
|
|
37
38
|
*/
|
|
38
39
|
stream?: TStream;
|
|
40
|
+
/**
|
|
41
|
+
* Enable debug logging. Pass `true` to enable all categories, `false` to
|
|
42
|
+
* silence everything including errors, or a `DebugConfig` object for granular
|
|
43
|
+
* control and/or a custom `Logger`.
|
|
44
|
+
*/
|
|
45
|
+
debug?: DebugOption;
|
|
39
46
|
}
|
|
40
47
|
/**
|
|
41
48
|
* Result type for the TTS activity.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { aiEventClient } from "@tanstack/ai-event-client";
|
|
2
2
|
import { streamGenerationResult } from "../stream-generation-result.js";
|
|
3
|
+
import { resolveDebugOption } from "../../logger/resolve.js";
|
|
3
4
|
const kind = "tts";
|
|
4
5
|
function createId(prefix) {
|
|
5
6
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -13,10 +14,12 @@ function generateSpeech(options) {
|
|
|
13
14
|
return runGenerateSpeech(options);
|
|
14
15
|
}
|
|
15
16
|
async function runGenerateSpeech(options) {
|
|
16
|
-
const { adapter, stream: _stream, ...rest } = options;
|
|
17
|
+
const { adapter, stream: _stream, debug: _debug, ...rest } = options;
|
|
17
18
|
const model = adapter.model;
|
|
18
19
|
const requestId = createId("speech");
|
|
19
20
|
const startTime = Date.now();
|
|
21
|
+
const logger = resolveDebugOption(options.debug);
|
|
22
|
+
const providerName = adapter.provider ?? adapter.name ?? "unknown";
|
|
20
23
|
aiEventClient.emit("speech:request:started", {
|
|
21
24
|
requestId,
|
|
22
25
|
provider: adapter.name,
|
|
@@ -28,7 +31,12 @@ async function runGenerateSpeech(options) {
|
|
|
28
31
|
modelOptions: rest.modelOptions,
|
|
29
32
|
timestamp: startTime
|
|
30
33
|
});
|
|
31
|
-
|
|
34
|
+
logger.request(`activity=generateSpeech provider=${providerName}`, {
|
|
35
|
+
provider: providerName,
|
|
36
|
+
model
|
|
37
|
+
});
|
|
38
|
+
try {
|
|
39
|
+
const result = await adapter.generateSpeech({ ...rest, model, logger });
|
|
32
40
|
const duration = Date.now() - startTime;
|
|
33
41
|
aiEventClient.emit("speech:request:completed", {
|
|
34
42
|
requestId,
|
|
@@ -42,8 +50,18 @@ async function runGenerateSpeech(options) {
|
|
|
42
50
|
modelOptions: rest.modelOptions,
|
|
43
51
|
timestamp: Date.now()
|
|
44
52
|
});
|
|
53
|
+
logger.output(`activity=generateSpeech bytes=${result.audio.length}`, {
|
|
54
|
+
bytes: result.audio.length,
|
|
55
|
+
contentType: result.contentType
|
|
56
|
+
});
|
|
45
57
|
return result;
|
|
46
|
-
})
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.errors("generateSpeech activity failed", {
|
|
60
|
+
error,
|
|
61
|
+
source: "generateSpeech"
|
|
62
|
+
});
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
47
65
|
}
|
|
48
66
|
function createSpeechOptions(options) {
|
|
49
67
|
return options;
|
|
@@ -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 '@tanstack/ai-event-client'\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
|
|
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 '@tanstack/ai-event-client'\nimport { streamGenerationResult } from '../stream-generation-result.js'\nimport { resolveDebugOption } from '../../logger/resolve'\nimport type { InternalLogger } from '../../logger/internal-logger'\nimport type { DebugOption } from '../../logger/types'\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 * Enable debug logging. Pass `true` to enable all categories, `false` to\n * silence everything including errors, or a `DebugConfig` object for granular\n * control and/or a custom `Logger`.\n */\n debug?: DebugOption\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, debug: _debug, ...rest } = options\n const model = adapter.model\n const requestId = createId('speech')\n const startTime = Date.now()\n const logger: InternalLogger = resolveDebugOption(options.debug)\n const providerName =\n (adapter as { name?: string; provider?: string }).provider ??\n (adapter as { name?: string }).name ??\n 'unknown'\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 logger.request(`activity=generateSpeech provider=${providerName}`, {\n provider: providerName,\n model,\n })\n\n try {\n const result = await adapter.generateSpeech({ ...rest, model, logger })\n\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 logger.output(`activity=generateSpeech bytes=${result.audio.length}`, {\n bytes: result.audio.length,\n contentType: result.contentType,\n })\n\n return result\n } catch (error) {\n logger.errors('generateSpeech activity failed', {\n error,\n source: 'generateSpeech',\n })\n throw error\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":";;;AAoBO,MAAM,OAAO;AAqEpB,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,OAAO,QAAQ,GAAG,SAAS;AAC7D,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,QAAQ;AACnC,QAAM,YAAY,KAAK,IAAA;AACvB,QAAM,SAAyB,mBAAmB,QAAQ,KAAK;AAC/D,QAAM,eACH,QAAiD,YACjD,QAA8B,QAC/B;AAEF,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,oCAAoC,YAAY,IAAI;AAAA,IACjE,UAAU;AAAA,IACV;AAAA,EAAA,CACD;AAED,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ,eAAe,EAAE,GAAG,MAAM,OAAO,QAAQ;AAEtE,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,OAAO,iCAAiC,OAAO,MAAM,MAAM,IAAI;AAAA,MACpE,OAAO,OAAO,MAAM;AAAA,MACpB,aAAa,OAAO;AAAA,IAAA,CACrB;AAED,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO,OAAO,kCAAkC;AAAA,MAC9C;AAAA,MACA,QAAQ;AAAA,IAAA,CACT;AACD,UAAM;AAAA,EACR;AACF;AASO,SAAS,oBAId,SACuC;AACvC,SAAO;AACT;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DebugOption } from '../../logger/types.js';
|
|
1
2
|
import { TranscriptionAdapter } from './adapter.js';
|
|
2
3
|
import { StreamChunk, TranscriptionResult } from '../../types.js';
|
|
3
4
|
/** The adapter kind this activity handles */
|
|
@@ -36,6 +37,12 @@ export interface TranscriptionActivityOptions<TAdapter extends TranscriptionAdap
|
|
|
36
37
|
* @default false
|
|
37
38
|
*/
|
|
38
39
|
stream?: TStream;
|
|
40
|
+
/**
|
|
41
|
+
* Enable debug logging. Pass `true` to enable all categories, `false` to
|
|
42
|
+
* silence everything including errors, or a `DebugConfig` object for granular
|
|
43
|
+
* control and/or a custom `Logger`.
|
|
44
|
+
*/
|
|
45
|
+
debug?: DebugOption;
|
|
39
46
|
}
|
|
40
47
|
/**
|
|
41
48
|
* Result type for the transcription activity.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { aiEventClient } from "@tanstack/ai-event-client";
|
|
2
2
|
import { streamGenerationResult } from "../stream-generation-result.js";
|
|
3
|
+
import { resolveDebugOption } from "../../logger/resolve.js";
|
|
3
4
|
const kind = "transcription";
|
|
4
5
|
function createId(prefix) {
|
|
5
6
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -15,10 +16,12 @@ function generateTranscription(options) {
|
|
|
15
16
|
);
|
|
16
17
|
}
|
|
17
18
|
async function runGenerateTranscription(options) {
|
|
18
|
-
const { adapter, stream: _stream, ...rest } = options;
|
|
19
|
+
const { adapter, stream: _stream, debug: _debug, ...rest } = options;
|
|
19
20
|
const model = adapter.model;
|
|
20
21
|
const requestId = createId("transcription");
|
|
21
22
|
const startTime = Date.now();
|
|
23
|
+
const logger = resolveDebugOption(options.debug);
|
|
24
|
+
const providerName = adapter.provider ?? adapter.name ?? "unknown";
|
|
22
25
|
aiEventClient.emit("transcription:request:started", {
|
|
23
26
|
requestId,
|
|
24
27
|
provider: adapter.name,
|
|
@@ -29,19 +32,35 @@ async function runGenerateTranscription(options) {
|
|
|
29
32
|
modelOptions: rest.modelOptions,
|
|
30
33
|
timestamp: startTime
|
|
31
34
|
});
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
requestId,
|
|
36
|
-
provider: adapter.name,
|
|
37
|
-
model,
|
|
38
|
-
text: result.text,
|
|
39
|
-
language: result.language,
|
|
40
|
-
duration,
|
|
41
|
-
modelOptions: rest.modelOptions,
|
|
42
|
-
timestamp: Date.now()
|
|
35
|
+
logger.request(`activity=generateTranscription provider=${providerName}`, {
|
|
36
|
+
provider: providerName,
|
|
37
|
+
model
|
|
43
38
|
});
|
|
44
|
-
|
|
39
|
+
try {
|
|
40
|
+
const result = await adapter.transcribe({ ...rest, model, logger });
|
|
41
|
+
const duration = Date.now() - startTime;
|
|
42
|
+
aiEventClient.emit("transcription:request:completed", {
|
|
43
|
+
requestId,
|
|
44
|
+
provider: adapter.name,
|
|
45
|
+
model,
|
|
46
|
+
text: result.text,
|
|
47
|
+
language: result.language,
|
|
48
|
+
duration,
|
|
49
|
+
modelOptions: rest.modelOptions,
|
|
50
|
+
timestamp: Date.now()
|
|
51
|
+
});
|
|
52
|
+
logger.output(
|
|
53
|
+
`activity=generateTranscription length=${result.text.length}`,
|
|
54
|
+
{ hasText: !!result.text }
|
|
55
|
+
);
|
|
56
|
+
return result;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
logger.errors("generateTranscription activity failed", {
|
|
59
|
+
error,
|
|
60
|
+
source: "generateTranscription"
|
|
61
|
+
});
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
45
64
|
}
|
|
46
65
|
function createTranscriptionOptions(options) {
|
|
47
66
|
return options;
|
|
@@ -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 '@tanstack/ai-event-client'\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
|
|
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 '@tanstack/ai-event-client'\nimport { streamGenerationResult } from '../stream-generation-result.js'\nimport { resolveDebugOption } from '../../logger/resolve'\nimport type { InternalLogger } from '../../logger/internal-logger'\nimport type { DebugOption } from '../../logger/types'\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 * Enable debug logging. Pass `true` to enable all categories, `false` to\n * silence everything including errors, or a `DebugConfig` object for granular\n * control and/or a custom `Logger`.\n */\n debug?: DebugOption\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, debug: _debug, ...rest } = options\n const model = adapter.model\n const requestId = createId('transcription')\n const startTime = Date.now()\n const logger: InternalLogger = resolveDebugOption(options.debug)\n const providerName =\n (adapter as { name?: string; provider?: string }).provider ??\n (adapter as { name?: string }).name ??\n 'unknown'\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 logger.request(`activity=generateTranscription provider=${providerName}`, {\n provider: providerName,\n model,\n })\n\n try {\n const result = await adapter.transcribe({ ...rest, model, logger })\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 logger.output(\n `activity=generateTranscription length=${result.text.length}`,\n { hasText: !!result.text },\n )\n\n return result\n } catch (error) {\n logger.errors('generateTranscription activity failed', {\n error,\n source: 'generateTranscription',\n })\n throw error\n }\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":";;;AAoBO,MAAM,OAAO;AAuEpB,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,OAAO,QAAQ,GAAG,SAAS;AAC7D,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,SAAS,eAAe;AAC1C,QAAM,YAAY,KAAK,IAAA;AACvB,QAAM,SAAyB,mBAAmB,QAAQ,KAAK;AAC/D,QAAM,eACH,QAAiD,YACjD,QAA8B,QAC/B;AAEF,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,SAAO,QAAQ,2CAA2C,YAAY,IAAI;AAAA,IACxE,UAAU;AAAA,IACV;AAAA,EAAA,CACD;AAED,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ,WAAW,EAAE,GAAG,MAAM,OAAO,QAAQ;AAClE,UAAM,WAAW,KAAK,IAAA,IAAQ;AAE9B,kBAAc,KAAK,mCAAmC;AAAA,MACpD;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB;AAAA,MACA,MAAM,OAAO;AAAA,MACb,UAAU,OAAO;AAAA,MACjB;AAAA,MACA,cAAc,KAAK;AAAA,MACnB,WAAW,KAAK,IAAA;AAAA,IAAI,CACrB;AAED,WAAO;AAAA,MACL,yCAAyC,OAAO,KAAK,MAAM;AAAA,MAC3D,EAAE,SAAS,CAAC,CAAC,OAAO,KAAA;AAAA,IAAK;AAG3B,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO,OAAO,yCAAyC;AAAA,MACrD;AAAA,MACA,QAAQ;AAAA,IAAA,CACT;AACD,UAAM;AAAA,EACR;AACF;AASO,SAAS,2BAId,SACiD;AACjD,SAAO;AACT;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DebugOption } from '../../logger/types.js';
|
|
1
2
|
import { VideoAdapter } from './adapter.js';
|
|
2
3
|
import { StreamChunk, VideoJobResult, VideoStatusResult, VideoUrlResult } from '../../types.js';
|
|
3
4
|
/** The adapter kind this activity handles */
|
|
@@ -53,6 +54,12 @@ export type VideoCreateOptions<TAdapter extends VideoAdapter<string, any, any, a
|
|
|
53
54
|
maxDuration?: number;
|
|
54
55
|
/** Custom run ID (stream mode only) */
|
|
55
56
|
runId?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Enable debug logging. Pass `true` to enable all categories, `false` to
|
|
59
|
+
* silence everything including errors, or a `DebugConfig` object for granular
|
|
60
|
+
* control and/or a custom `Logger`.
|
|
61
|
+
*/
|
|
62
|
+
debug?: DebugOption;
|
|
56
63
|
} & ({} extends VideoProviderOptions<TAdapter> ? {
|
|
57
64
|
/** Provider-specific options for video generation */ modelOptions?: VideoProviderOptions<TAdapter>;
|
|
58
65
|
} : {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { aiEventClient } from "@tanstack/ai-event-client";
|
|
2
|
+
import { resolveDebugOption } from "../../logger/resolve.js";
|
|
2
3
|
const kind = "video";
|
|
3
4
|
function createId(prefix) {
|
|
4
5
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -14,13 +15,33 @@ function generateVideo(options) {
|
|
|
14
15
|
async function runCreateVideoJob(options) {
|
|
15
16
|
const { adapter, prompt, size, duration, modelOptions } = options;
|
|
16
17
|
const model = adapter.model;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
modelOptions
|
|
18
|
+
const logger = resolveDebugOption(options.debug);
|
|
19
|
+
const providerName = adapter.provider ?? adapter.name ?? "unknown";
|
|
20
|
+
logger.request(`activity=generateVideo provider=${providerName}`, {
|
|
21
|
+
provider: providerName,
|
|
22
|
+
model
|
|
23
23
|
});
|
|
24
|
+
try {
|
|
25
|
+
const result = await adapter.createVideoJob({
|
|
26
|
+
model,
|
|
27
|
+
prompt,
|
|
28
|
+
size,
|
|
29
|
+
duration,
|
|
30
|
+
modelOptions,
|
|
31
|
+
logger
|
|
32
|
+
});
|
|
33
|
+
logger.output(`activity=generateVideo jobId=${result.jobId}`, {
|
|
34
|
+
jobId: result.jobId,
|
|
35
|
+
model: result.model
|
|
36
|
+
});
|
|
37
|
+
return result;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
logger.errors("generateVideo activity failed", {
|
|
40
|
+
error,
|
|
41
|
+
source: "generateVideo"
|
|
42
|
+
});
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
24
45
|
}
|
|
25
46
|
function sleep(ms) {
|
|
26
47
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -31,6 +52,8 @@ async function* runStreamingVideoGeneration(options) {
|
|
|
31
52
|
const runId = options.runId ?? createId("run");
|
|
32
53
|
const pollingInterval = options.pollingInterval ?? 2e3;
|
|
33
54
|
const maxDuration = options.maxDuration ?? 6e5;
|
|
55
|
+
const logger = resolveDebugOption(options.debug);
|
|
56
|
+
const providerName = adapter.provider ?? adapter.name ?? "unknown";
|
|
34
57
|
const threadId = createId("thread");
|
|
35
58
|
yield {
|
|
36
59
|
type: "RUN_STARTED",
|
|
@@ -38,13 +61,21 @@ async function* runStreamingVideoGeneration(options) {
|
|
|
38
61
|
threadId,
|
|
39
62
|
timestamp: Date.now()
|
|
40
63
|
};
|
|
64
|
+
logger.request(
|
|
65
|
+
`activity=generateVideo provider=${providerName} stream=true`,
|
|
66
|
+
{
|
|
67
|
+
provider: providerName,
|
|
68
|
+
model
|
|
69
|
+
}
|
|
70
|
+
);
|
|
41
71
|
try {
|
|
42
72
|
const jobResult = await adapter.createVideoJob({
|
|
43
73
|
model,
|
|
44
74
|
prompt,
|
|
45
75
|
size,
|
|
46
76
|
duration,
|
|
47
|
-
modelOptions
|
|
77
|
+
modelOptions,
|
|
78
|
+
logger
|
|
48
79
|
});
|
|
49
80
|
yield {
|
|
50
81
|
type: "CUSTOM",
|
|
@@ -69,6 +100,13 @@ async function* runStreamingVideoGeneration(options) {
|
|
|
69
100
|
};
|
|
70
101
|
if (statusResult.status === "completed") {
|
|
71
102
|
const urlResult = await adapter.getVideoUrl(jobResult.jobId);
|
|
103
|
+
logger.output(
|
|
104
|
+
`activity=generateVideo jobId=${jobResult.jobId} status=completed`,
|
|
105
|
+
{
|
|
106
|
+
jobId: jobResult.jobId,
|
|
107
|
+
url: urlResult.url
|
|
108
|
+
}
|
|
109
|
+
);
|
|
72
110
|
yield {
|
|
73
111
|
type: "CUSTOM",
|
|
74
112
|
name: "generation:result",
|
|
@@ -95,6 +133,10 @@ async function* runStreamingVideoGeneration(options) {
|
|
|
95
133
|
}
|
|
96
134
|
throw new Error("Video generation timed out");
|
|
97
135
|
} catch (error) {
|
|
136
|
+
logger.errors("generateVideo activity failed", {
|
|
137
|
+
error,
|
|
138
|
+
source: "generateVideo"
|
|
139
|
+
});
|
|
98
140
|
yield {
|
|
99
141
|
type: "RUN_ERROR",
|
|
100
142
|
runId,
|