@tanstack/ai 0.6.2 → 0.8.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/index.d.ts +20 -0
- package/dist/esm/activities/chat/index.js +248 -213
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +66 -0
- package/dist/esm/activities/chat/middleware/compose.js +327 -0
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -0
- package/dist/esm/activities/chat/middleware/index.d.ts +2 -0
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.d.ts +89 -0
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +76 -0
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -0
- package/dist/esm/activities/chat/middleware/types.d.ts +307 -0
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +16 -1
- package/dist/esm/activities/chat/tools/tool-calls.js +148 -64
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/generateImage/index.d.ts +19 -6
- package/dist/esm/activities/generateImage/index.js +12 -3
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +19 -6
- package/dist/esm/activities/generateSpeech/index.js +12 -3
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +30 -6
- package/dist/esm/activities/generateTranscription/index.js +14 -3
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +45 -7
- package/dist/esm/activities/generateVideo/index.js +91 -2
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/stream-generation-result.d.ts +14 -0
- package/dist/esm/activities/stream-generation-result.js +40 -0
- package/dist/esm/activities/stream-generation-result.js.map +1 -0
- package/dist/esm/activities/summarize/index.js +3 -18
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/middlewares/content-guard.d.ts +77 -0
- package/dist/esm/middlewares/content-guard.js +155 -0
- package/dist/esm/middlewares/content-guard.js.map +1 -0
- package/dist/esm/middlewares/index.d.ts +2 -0
- package/dist/esm/middlewares/index.js +7 -0
- package/dist/esm/middlewares/index.js.map +1 -0
- package/dist/esm/middlewares/tool-cache.d.ts +1 -0
- package/dist/esm/realtime/index.d.ts +30 -0
- package/dist/esm/realtime/index.js +8 -0
- package/dist/esm/realtime/index.js.map +1 -0
- package/dist/esm/realtime/types.d.ts +234 -0
- package/package.json +6 -6
- package/src/activities/chat/index.ts +322 -256
- package/src/activities/chat/middleware/compose.ts +392 -0
- package/src/activities/chat/middleware/index.ts +17 -0
- package/src/activities/chat/middleware/tool-cache-middleware.ts +189 -0
- package/src/activities/chat/middleware/types.ts +419 -0
- package/src/activities/chat/tools/tool-calls.ts +225 -87
- package/src/activities/generateImage/index.ts +50 -8
- package/src/activities/generateSpeech/index.ts +42 -8
- package/src/activities/generateTranscription/index.ts +60 -10
- package/src/activities/generateVideo/index.ts +174 -7
- package/src/activities/stream-generation-result.ts +62 -0
- package/src/activities/summarize/index.ts +4 -23
- package/src/index.ts +41 -2
- package/src/middlewares/content-guard.ts +285 -0
- package/src/middlewares/index.ts +13 -0
- package/src/middlewares/tool-cache.ts +6 -0
- package/src/realtime/index.ts +38 -0
- package/src/realtime/types.ts +294 -0
- package/dist/esm/event-client.d.ts +0 -394
- package/dist/esm/event-client.js +0 -13
- package/dist/esm/event-client.js.map +0 -1
- package/src/event-client.ts +0 -497
|
@@ -10,6 +10,41 @@ import type {
|
|
|
10
10
|
ToolCallStartEvent,
|
|
11
11
|
ToolExecutionContext,
|
|
12
12
|
} from '../../../types'
|
|
13
|
+
import type {
|
|
14
|
+
AfterToolCallInfo,
|
|
15
|
+
BeforeToolCallDecision,
|
|
16
|
+
} from '../middleware/types'
|
|
17
|
+
|
|
18
|
+
function safeJsonParse(value: string): unknown {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(value)
|
|
21
|
+
} catch {
|
|
22
|
+
return value
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Optional middleware hooks for tool execution.
|
|
28
|
+
* When provided, these callbacks are invoked before/after each tool execution.
|
|
29
|
+
*/
|
|
30
|
+
export interface ToolExecutionMiddlewareHooks {
|
|
31
|
+
onBeforeToolCall?: (
|
|
32
|
+
toolCall: ToolCall,
|
|
33
|
+
tool: Tool | undefined,
|
|
34
|
+
args: unknown,
|
|
35
|
+
) => Promise<BeforeToolCallDecision>
|
|
36
|
+
onAfterToolCall?: (info: AfterToolCallInfo) => Promise<void>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Error thrown when middleware decides to abort the chat run during tool execution.
|
|
41
|
+
*/
|
|
42
|
+
export class MiddlewareAbortError extends Error {
|
|
43
|
+
constructor(reason: string) {
|
|
44
|
+
super(reason)
|
|
45
|
+
this.name = 'MiddlewareAbortError'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
13
48
|
|
|
14
49
|
/**
|
|
15
50
|
* Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.
|
|
@@ -289,6 +324,152 @@ async function* executeWithEventPolling<T>(
|
|
|
289
324
|
return state.result
|
|
290
325
|
}
|
|
291
326
|
|
|
327
|
+
/**
|
|
328
|
+
* Apply a middleware onBeforeToolCall decision.
|
|
329
|
+
* Returns the (possibly transformed) input if execution should proceed,
|
|
330
|
+
* or undefined if the tool call was skipped (result already pushed).
|
|
331
|
+
* Throws MiddlewareAbortError if the decision is 'abort'.
|
|
332
|
+
*/
|
|
333
|
+
async function applyBeforeToolCallDecision(
|
|
334
|
+
toolCall: ToolCall,
|
|
335
|
+
tool: Tool,
|
|
336
|
+
input: unknown,
|
|
337
|
+
toolName: string,
|
|
338
|
+
middlewareHooks: ToolExecutionMiddlewareHooks,
|
|
339
|
+
results: Array<ToolResult>,
|
|
340
|
+
): Promise<{ proceed: true; input: unknown } | { proceed: false }> {
|
|
341
|
+
if (!middlewareHooks.onBeforeToolCall) {
|
|
342
|
+
return { proceed: true, input }
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const decision = await middlewareHooks.onBeforeToolCall(toolCall, tool, input)
|
|
346
|
+
if (!decision) {
|
|
347
|
+
return { proceed: true, input }
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (decision.type === 'abort') {
|
|
351
|
+
throw new MiddlewareAbortError(decision.reason || 'Aborted by middleware')
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (decision.type === 'skip') {
|
|
355
|
+
const skipResult = decision.result
|
|
356
|
+
results.push({
|
|
357
|
+
toolCallId: toolCall.id,
|
|
358
|
+
toolName,
|
|
359
|
+
result:
|
|
360
|
+
typeof skipResult === 'string'
|
|
361
|
+
? safeJsonParse(skipResult)
|
|
362
|
+
: skipResult || null,
|
|
363
|
+
duration: 0,
|
|
364
|
+
})
|
|
365
|
+
if (middlewareHooks.onAfterToolCall) {
|
|
366
|
+
await middlewareHooks.onAfterToolCall({
|
|
367
|
+
toolCall,
|
|
368
|
+
tool,
|
|
369
|
+
toolName,
|
|
370
|
+
toolCallId: toolCall.id,
|
|
371
|
+
ok: true,
|
|
372
|
+
duration: 0,
|
|
373
|
+
result: skipResult,
|
|
374
|
+
})
|
|
375
|
+
}
|
|
376
|
+
return { proceed: false }
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return { proceed: true, input: decision.args }
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Execute a server-side tool with event polling, output validation, and middleware hooks.
|
|
384
|
+
* Yields CustomEvent chunks during execution and pushes the result to the results array.
|
|
385
|
+
*/
|
|
386
|
+
async function* executeServerTool(
|
|
387
|
+
toolCall: ToolCall,
|
|
388
|
+
tool: Tool,
|
|
389
|
+
toolName: string,
|
|
390
|
+
input: unknown,
|
|
391
|
+
context: ToolExecutionContext,
|
|
392
|
+
pendingEvents: Array<CustomEvent>,
|
|
393
|
+
results: Array<ToolResult>,
|
|
394
|
+
middlewareHooks?: ToolExecutionMiddlewareHooks,
|
|
395
|
+
): AsyncGenerator<CustomEvent, void, void> {
|
|
396
|
+
const startTime = Date.now()
|
|
397
|
+
try {
|
|
398
|
+
const executionPromise = Promise.resolve(tool.execute!(input, context))
|
|
399
|
+
let result = yield* executeWithEventPolling(executionPromise, pendingEvents)
|
|
400
|
+
const duration = Date.now() - startTime
|
|
401
|
+
|
|
402
|
+
// Flush remaining events
|
|
403
|
+
while (pendingEvents.length > 0) {
|
|
404
|
+
yield pendingEvents.shift()!
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Validate output against outputSchema if provided
|
|
408
|
+
if (
|
|
409
|
+
tool.outputSchema &&
|
|
410
|
+
isStandardSchema(tool.outputSchema) &&
|
|
411
|
+
result !== undefined &&
|
|
412
|
+
result !== null
|
|
413
|
+
) {
|
|
414
|
+
result = parseWithStandardSchema(tool.outputSchema, result)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const finalResult =
|
|
418
|
+
typeof result === 'string' ? safeJsonParse(result) : result || null
|
|
419
|
+
|
|
420
|
+
results.push({
|
|
421
|
+
toolCallId: toolCall.id,
|
|
422
|
+
toolName,
|
|
423
|
+
result: finalResult,
|
|
424
|
+
duration,
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
if (middlewareHooks?.onAfterToolCall) {
|
|
428
|
+
await middlewareHooks.onAfterToolCall({
|
|
429
|
+
toolCall,
|
|
430
|
+
tool,
|
|
431
|
+
toolName,
|
|
432
|
+
toolCallId: toolCall.id,
|
|
433
|
+
ok: true,
|
|
434
|
+
duration,
|
|
435
|
+
result: finalResult,
|
|
436
|
+
})
|
|
437
|
+
}
|
|
438
|
+
} catch (error: unknown) {
|
|
439
|
+
const duration = Date.now() - startTime
|
|
440
|
+
|
|
441
|
+
// Flush remaining events
|
|
442
|
+
while (pendingEvents.length > 0) {
|
|
443
|
+
yield pendingEvents.shift()!
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (error instanceof MiddlewareAbortError) {
|
|
447
|
+
throw error
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const message = error instanceof Error ? error.message : 'Unknown error'
|
|
451
|
+
results.push({
|
|
452
|
+
toolCallId: toolCall.id,
|
|
453
|
+
toolName,
|
|
454
|
+
result: { error: message },
|
|
455
|
+
state: 'output-error',
|
|
456
|
+
duration,
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
if (middlewareHooks?.onAfterToolCall) {
|
|
460
|
+
await middlewareHooks.onAfterToolCall({
|
|
461
|
+
toolCall,
|
|
462
|
+
tool,
|
|
463
|
+
toolName,
|
|
464
|
+
toolCallId: toolCall.id,
|
|
465
|
+
ok: false,
|
|
466
|
+
duration,
|
|
467
|
+
error,
|
|
468
|
+
})
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
292
473
|
/**
|
|
293
474
|
* Execute tool calls based on their configuration.
|
|
294
475
|
* Yields CustomEvent chunks during tool execution for real-time progress updates.
|
|
@@ -313,6 +494,7 @@ export async function* executeToolCalls(
|
|
|
313
494
|
eventName: string,
|
|
314
495
|
value: Record<string, any>,
|
|
315
496
|
) => CustomEvent,
|
|
497
|
+
middlewareHooks?: ToolExecutionMiddlewareHooks,
|
|
316
498
|
): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void> {
|
|
317
499
|
const results: Array<ToolResult> = []
|
|
318
500
|
const needsApproval: Array<ApprovalRequest> = []
|
|
@@ -402,13 +584,6 @@ export async function* executeToolCalls(
|
|
|
402
584
|
},
|
|
403
585
|
}
|
|
404
586
|
|
|
405
|
-
// Helper to flush any pending events
|
|
406
|
-
function* flushEvents(): Generator<CustomEvent> {
|
|
407
|
-
while (pendingEvents.length > 0) {
|
|
408
|
-
yield pendingEvents.shift()!
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
587
|
// CASE 1: Client-side tool (no execute function)
|
|
413
588
|
if (!tool.execute) {
|
|
414
589
|
// Check if tool needs approval
|
|
@@ -482,51 +657,30 @@ export async function* executeToolCalls(
|
|
|
482
657
|
const approved = approvals.get(approvalId)
|
|
483
658
|
|
|
484
659
|
if (approved) {
|
|
485
|
-
//
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
tool
|
|
490
|
-
|
|
491
|
-
let result = yield* executeWithEventPolling(
|
|
492
|
-
executionPromise,
|
|
493
|
-
pendingEvents,
|
|
494
|
-
)
|
|
495
|
-
const duration = Date.now() - startTime
|
|
496
|
-
yield* flushEvents()
|
|
497
|
-
|
|
498
|
-
// Validate output against outputSchema if provided (for Standard Schema compliant schemas)
|
|
499
|
-
if (
|
|
500
|
-
tool.outputSchema &&
|
|
501
|
-
isStandardSchema(tool.outputSchema) &&
|
|
502
|
-
result !== undefined &&
|
|
503
|
-
result !== null
|
|
504
|
-
) {
|
|
505
|
-
result = parseWithStandardSchema(tool.outputSchema, result)
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
results.push({
|
|
509
|
-
toolCallId: toolCall.id,
|
|
510
|
-
toolName,
|
|
511
|
-
result:
|
|
512
|
-
typeof result === 'string'
|
|
513
|
-
? JSON.parse(result)
|
|
514
|
-
: result || null,
|
|
515
|
-
duration,
|
|
516
|
-
})
|
|
517
|
-
} catch (error: unknown) {
|
|
518
|
-
const duration = Date.now() - startTime
|
|
519
|
-
yield* flushEvents()
|
|
520
|
-
const message =
|
|
521
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
522
|
-
results.push({
|
|
523
|
-
toolCallId: toolCall.id,
|
|
660
|
+
// Apply middleware before-hook for approved tools
|
|
661
|
+
if (middlewareHooks) {
|
|
662
|
+
const decision = await applyBeforeToolCallDecision(
|
|
663
|
+
toolCall,
|
|
664
|
+
tool,
|
|
665
|
+
input,
|
|
524
666
|
toolName,
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
667
|
+
middlewareHooks,
|
|
668
|
+
results,
|
|
669
|
+
)
|
|
670
|
+
if (!decision.proceed) continue
|
|
671
|
+
input = decision.input
|
|
529
672
|
}
|
|
673
|
+
|
|
674
|
+
yield* executeServerTool(
|
|
675
|
+
toolCall,
|
|
676
|
+
tool,
|
|
677
|
+
toolName,
|
|
678
|
+
input,
|
|
679
|
+
context,
|
|
680
|
+
pendingEvents,
|
|
681
|
+
results,
|
|
682
|
+
middlewareHooks,
|
|
683
|
+
)
|
|
530
684
|
} else {
|
|
531
685
|
// User declined
|
|
532
686
|
results.push({
|
|
@@ -549,45 +703,29 @@ export async function* executeToolCalls(
|
|
|
549
703
|
}
|
|
550
704
|
|
|
551
705
|
// CASE 3: Normal server tool - execute immediately
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
pendingEvents,
|
|
558
|
-
)
|
|
559
|
-
const duration = Date.now() - startTime
|
|
560
|
-
yield* flushEvents()
|
|
561
|
-
|
|
562
|
-
// Validate output against outputSchema if provided (for Standard Schema compliant schemas)
|
|
563
|
-
if (
|
|
564
|
-
tool.outputSchema &&
|
|
565
|
-
isStandardSchema(tool.outputSchema) &&
|
|
566
|
-
result !== undefined &&
|
|
567
|
-
result !== null
|
|
568
|
-
) {
|
|
569
|
-
result = parseWithStandardSchema(tool.outputSchema, result)
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
results.push({
|
|
573
|
-
toolCallId: toolCall.id,
|
|
574
|
-
toolName,
|
|
575
|
-
result:
|
|
576
|
-
typeof result === 'string' ? JSON.parse(result) : result || null,
|
|
577
|
-
duration,
|
|
578
|
-
})
|
|
579
|
-
} catch (error: unknown) {
|
|
580
|
-
const duration = Date.now() - startTime
|
|
581
|
-
yield* flushEvents()
|
|
582
|
-
const message = error instanceof Error ? error.message : 'Unknown error'
|
|
583
|
-
results.push({
|
|
584
|
-
toolCallId: toolCall.id,
|
|
706
|
+
if (middlewareHooks) {
|
|
707
|
+
const decision = await applyBeforeToolCallDecision(
|
|
708
|
+
toolCall,
|
|
709
|
+
tool,
|
|
710
|
+
input,
|
|
585
711
|
toolName,
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
712
|
+
middlewareHooks,
|
|
713
|
+
results,
|
|
714
|
+
)
|
|
715
|
+
if (!decision.proceed) continue
|
|
716
|
+
input = decision.input
|
|
590
717
|
}
|
|
718
|
+
|
|
719
|
+
yield* executeServerTool(
|
|
720
|
+
toolCall,
|
|
721
|
+
tool,
|
|
722
|
+
toolName,
|
|
723
|
+
input,
|
|
724
|
+
context,
|
|
725
|
+
pendingEvents,
|
|
726
|
+
results,
|
|
727
|
+
middlewareHooks,
|
|
728
|
+
)
|
|
591
729
|
}
|
|
592
730
|
|
|
593
731
|
return { results, needsApproval, needsClientExecution }
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* This is a self-contained module with implementation, types, and JSDoc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { aiEventClient } from '
|
|
8
|
+
import { aiEventClient } from '@tanstack/ai-event-client'
|
|
9
|
+
import { streamGenerationResult } from '../stream-generation-result.js'
|
|
9
10
|
import type { ImageAdapter } from './adapter'
|
|
10
|
-
import type { ImageGenerationResult } from '../../types'
|
|
11
|
+
import type { ImageGenerationResult, StreamChunk } from '../../types'
|
|
11
12
|
|
|
12
13
|
// ===========================
|
|
13
14
|
// Activity Kind
|
|
@@ -60,9 +61,11 @@ export type ImageSizeForModel<TAdapter, TModel extends string> =
|
|
|
60
61
|
* The model is extracted from the adapter's model property.
|
|
61
62
|
*
|
|
62
63
|
* @template TAdapter - The image adapter type
|
|
64
|
+
* @template TStream - Whether to stream the output
|
|
63
65
|
*/
|
|
64
66
|
export type ImageActivityOptions<
|
|
65
67
|
TAdapter extends ImageAdapter<string, any, any, any>,
|
|
68
|
+
TStream extends boolean = false,
|
|
66
69
|
> = {
|
|
67
70
|
/** The image adapter to use (must be created with a model) */
|
|
68
71
|
adapter: TAdapter & { kind: typeof kind }
|
|
@@ -72,6 +75,14 @@ export type ImageActivityOptions<
|
|
|
72
75
|
numberOfImages?: number
|
|
73
76
|
/** Image size in WIDTHxHEIGHT format (e.g., "1024x1024") */
|
|
74
77
|
size?: ImageSizeForModel<TAdapter, TAdapter['model']>
|
|
78
|
+
/**
|
|
79
|
+
* Whether to stream the image generation result.
|
|
80
|
+
* When true, returns an AsyncIterable<StreamChunk> for streaming transport.
|
|
81
|
+
* When false or not provided, returns a Promise<ImageGenerationResult>.
|
|
82
|
+
*
|
|
83
|
+
* @default false
|
|
84
|
+
*/
|
|
85
|
+
stream?: TStream
|
|
75
86
|
} & ({} extends ImageProviderOptionsForModel<TAdapter, TAdapter['model']>
|
|
76
87
|
? {
|
|
77
88
|
/** Provider-specific options for image generation */ modelOptions?: ImageProviderOptionsForModel<
|
|
@@ -90,8 +101,15 @@ export type ImageActivityOptions<
|
|
|
90
101
|
// Activity Result Type
|
|
91
102
|
// ===========================
|
|
92
103
|
|
|
93
|
-
/**
|
|
94
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Result type for the image activity.
|
|
106
|
+
* - If stream is true: AsyncIterable<StreamChunk>
|
|
107
|
+
* - Otherwise: Promise<ImageGenerationResult>
|
|
108
|
+
*/
|
|
109
|
+
export type ImageActivityResult<TStream extends boolean = false> =
|
|
110
|
+
TStream extends true
|
|
111
|
+
? AsyncIterable<StreamChunk>
|
|
112
|
+
: Promise<ImageGenerationResult>
|
|
95
113
|
|
|
96
114
|
function createId(prefix: string): string {
|
|
97
115
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
@@ -146,10 +164,31 @@ function createId(prefix: string): string {
|
|
|
146
164
|
* })
|
|
147
165
|
* ```
|
|
148
166
|
*/
|
|
149
|
-
export
|
|
167
|
+
export function generateImage<
|
|
168
|
+
TAdapter extends ImageAdapter<string, any, any, any>,
|
|
169
|
+
TStream extends boolean = false,
|
|
170
|
+
>(
|
|
171
|
+
options: ImageActivityOptions<TAdapter, TStream>,
|
|
172
|
+
): ImageActivityResult<TStream> {
|
|
173
|
+
if (options.stream) {
|
|
174
|
+
return streamGenerationResult(() =>
|
|
175
|
+
runGenerateImage(options),
|
|
176
|
+
) as ImageActivityResult<TStream>
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return runGenerateImage(options) as ImageActivityResult<TStream>
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Internal implementation of image generation (always non-streaming).
|
|
184
|
+
* Contains all devtools event emission logic.
|
|
185
|
+
*/
|
|
186
|
+
async function runGenerateImage<
|
|
150
187
|
TAdapter extends ImageAdapter<string, any, any, any>,
|
|
151
|
-
>(
|
|
152
|
-
|
|
188
|
+
>(
|
|
189
|
+
options: ImageActivityOptions<TAdapter, boolean>,
|
|
190
|
+
): Promise<ImageGenerationResult> {
|
|
191
|
+
const { adapter, stream: _stream, ...rest } = options
|
|
153
192
|
const model = adapter.model
|
|
154
193
|
const requestId = createId('image')
|
|
155
194
|
const startTime = Date.now()
|
|
@@ -204,7 +243,10 @@ export async function generateImage<
|
|
|
204
243
|
*/
|
|
205
244
|
export function createImageOptions<
|
|
206
245
|
TAdapter extends ImageAdapter<string, any, any, any>,
|
|
207
|
-
|
|
246
|
+
TStream extends boolean = false,
|
|
247
|
+
>(
|
|
248
|
+
options: ImageActivityOptions<TAdapter, TStream>,
|
|
249
|
+
): ImageActivityOptions<TAdapter, TStream> {
|
|
208
250
|
return options
|
|
209
251
|
}
|
|
210
252
|
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* This is a self-contained module with implementation, types, and JSDoc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { aiEventClient } from '
|
|
8
|
+
import { aiEventClient } from '@tanstack/ai-event-client'
|
|
9
|
+
import { streamGenerationResult } from '../stream-generation-result.js'
|
|
9
10
|
import type { TTSAdapter } from './adapter'
|
|
10
|
-
import type { TTSResult } from '../../types'
|
|
11
|
+
import type { StreamChunk, TTSResult } from '../../types'
|
|
11
12
|
|
|
12
13
|
// ===========================
|
|
13
14
|
// Activity Kind
|
|
@@ -37,9 +38,11 @@ export type TTSProviderOptions<TAdapter> =
|
|
|
37
38
|
* The model is extracted from the adapter's model property.
|
|
38
39
|
*
|
|
39
40
|
* @template TAdapter - The TTS adapter type
|
|
41
|
+
* @template TStream - Whether to stream the output
|
|
40
42
|
*/
|
|
41
43
|
export interface TTSActivityOptions<
|
|
42
44
|
TAdapter extends TTSAdapter<string, object>,
|
|
45
|
+
TStream extends boolean = false,
|
|
43
46
|
> {
|
|
44
47
|
/** The TTS adapter to use (must be created with a model) */
|
|
45
48
|
adapter: TAdapter & { kind: typeof kind }
|
|
@@ -53,14 +56,27 @@ export interface TTSActivityOptions<
|
|
|
53
56
|
speed?: number
|
|
54
57
|
/** Provider-specific options for TTS generation */
|
|
55
58
|
modelOptions?: TTSProviderOptions<TAdapter>
|
|
59
|
+
/**
|
|
60
|
+
* Whether to stream the generation result.
|
|
61
|
+
* When true, returns an AsyncIterable<StreamChunk> for streaming transport.
|
|
62
|
+
* When false or not provided, returns a Promise<TTSResult>.
|
|
63
|
+
*
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
stream?: TStream
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
// ===========================
|
|
59
70
|
// Activity Result Type
|
|
60
71
|
// ===========================
|
|
61
72
|
|
|
62
|
-
/**
|
|
63
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Result type for the TTS activity.
|
|
75
|
+
* - If stream is true: AsyncIterable<StreamChunk>
|
|
76
|
+
* - Otherwise: Promise<TTSResult>
|
|
77
|
+
*/
|
|
78
|
+
export type TTSActivityResult<TStream extends boolean = false> =
|
|
79
|
+
TStream extends true ? AsyncIterable<StreamChunk> : Promise<TTSResult>
|
|
64
80
|
|
|
65
81
|
function createId(prefix: string): string {
|
|
66
82
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
@@ -100,10 +116,25 @@ function createId(prefix: string): string {
|
|
|
100
116
|
* })
|
|
101
117
|
* ```
|
|
102
118
|
*/
|
|
103
|
-
export
|
|
119
|
+
export function generateSpeech<
|
|
104
120
|
TAdapter extends TTSAdapter<string, object>,
|
|
105
|
-
|
|
106
|
-
|
|
121
|
+
TStream extends boolean = false,
|
|
122
|
+
>(options: TTSActivityOptions<TAdapter, TStream>): TTSActivityResult<TStream> {
|
|
123
|
+
if (options.stream) {
|
|
124
|
+
return streamGenerationResult(() =>
|
|
125
|
+
runGenerateSpeech(options),
|
|
126
|
+
) as TTSActivityResult<TStream>
|
|
127
|
+
}
|
|
128
|
+
return runGenerateSpeech(options) as TTSActivityResult<TStream>
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Run the core TTS generation logic (non-streaming).
|
|
133
|
+
*/
|
|
134
|
+
async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
|
|
135
|
+
options: TTSActivityOptions<TAdapter, boolean>,
|
|
136
|
+
): Promise<TTSResult> {
|
|
137
|
+
const { adapter, stream: _stream, ...rest } = options
|
|
107
138
|
const model = adapter.model
|
|
108
139
|
const requestId = createId('speech')
|
|
109
140
|
const startTime = Date.now()
|
|
@@ -149,7 +180,10 @@ export async function generateSpeech<
|
|
|
149
180
|
*/
|
|
150
181
|
export function createSpeechOptions<
|
|
151
182
|
TAdapter extends TTSAdapter<string, object>,
|
|
152
|
-
|
|
183
|
+
TStream extends boolean = false,
|
|
184
|
+
>(
|
|
185
|
+
options: TTSActivityOptions<TAdapter, TStream>,
|
|
186
|
+
): TTSActivityOptions<TAdapter, TStream> {
|
|
153
187
|
return options
|
|
154
188
|
}
|
|
155
189
|
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* This is a self-contained module with implementation, types, and JSDoc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { aiEventClient } from '
|
|
8
|
+
import { aiEventClient } from '@tanstack/ai-event-client'
|
|
9
|
+
import { streamGenerationResult } from '../stream-generation-result.js'
|
|
9
10
|
import type { TranscriptionAdapter } from './adapter'
|
|
10
|
-
import type { TranscriptionResult } from '../../types'
|
|
11
|
+
import type { StreamChunk, TranscriptionResult } from '../../types'
|
|
11
12
|
|
|
12
13
|
// ===========================
|
|
13
14
|
// Activity Kind
|
|
@@ -37,9 +38,11 @@ export type TranscriptionProviderOptions<TAdapter> =
|
|
|
37
38
|
* The model is extracted from the adapter's model property.
|
|
38
39
|
*
|
|
39
40
|
* @template TAdapter - The transcription adapter type
|
|
41
|
+
* @template TStream - Whether to stream the output
|
|
40
42
|
*/
|
|
41
43
|
export interface TranscriptionActivityOptions<
|
|
42
44
|
TAdapter extends TranscriptionAdapter<string, object>,
|
|
45
|
+
TStream extends boolean = false,
|
|
43
46
|
> {
|
|
44
47
|
/** The transcription adapter to use (must be created with a model) */
|
|
45
48
|
adapter: TAdapter & { kind: typeof kind }
|
|
@@ -53,14 +56,29 @@ export interface TranscriptionActivityOptions<
|
|
|
53
56
|
responseFormat?: 'json' | 'text' | 'srt' | 'verbose_json' | 'vtt'
|
|
54
57
|
/** Provider-specific options for transcription */
|
|
55
58
|
modelOptions?: TranscriptionProviderOptions<TAdapter>
|
|
59
|
+
/**
|
|
60
|
+
* Whether to stream the transcription result.
|
|
61
|
+
* When true, returns an AsyncIterable<StreamChunk> for streaming transport.
|
|
62
|
+
* When false or not provided, returns a Promise<TranscriptionResult>.
|
|
63
|
+
*
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
stream?: TStream
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
// ===========================
|
|
59
70
|
// Activity Result Type
|
|
60
71
|
// ===========================
|
|
61
72
|
|
|
62
|
-
/**
|
|
63
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Result type for the transcription activity.
|
|
75
|
+
* - If stream is true: AsyncIterable<StreamChunk>
|
|
76
|
+
* - Otherwise: Promise<TranscriptionResult>
|
|
77
|
+
*/
|
|
78
|
+
export type TranscriptionActivityResult<TStream extends boolean = false> =
|
|
79
|
+
TStream extends true
|
|
80
|
+
? AsyncIterable<StreamChunk>
|
|
81
|
+
: Promise<TranscriptionResult>
|
|
64
82
|
|
|
65
83
|
function createId(prefix: string): string {
|
|
66
84
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
@@ -101,13 +119,44 @@ function createId(prefix: string): string {
|
|
|
101
119
|
* console.log(`[${segment.start}s - ${segment.end}s]: ${segment.text}`)
|
|
102
120
|
* })
|
|
103
121
|
* ```
|
|
122
|
+
*
|
|
123
|
+
* @example Streaming transcription result
|
|
124
|
+
* ```ts
|
|
125
|
+
* for await (const chunk of generateTranscription({
|
|
126
|
+
* adapter: openaiTranscription('whisper-1'),
|
|
127
|
+
* audio: audioFile,
|
|
128
|
+
* stream: true
|
|
129
|
+
* })) {
|
|
130
|
+
* console.log(chunk)
|
|
131
|
+
* }
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export function generateTranscription<
|
|
135
|
+
TAdapter extends TranscriptionAdapter<string, object>,
|
|
136
|
+
TStream extends boolean = false,
|
|
137
|
+
>(
|
|
138
|
+
options: TranscriptionActivityOptions<TAdapter, TStream>,
|
|
139
|
+
): TranscriptionActivityResult<TStream> {
|
|
140
|
+
if (options.stream) {
|
|
141
|
+
return streamGenerationResult(() =>
|
|
142
|
+
runGenerateTranscription(options),
|
|
143
|
+
) as TranscriptionActivityResult<TStream>
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return runGenerateTranscription(
|
|
147
|
+
options,
|
|
148
|
+
) as TranscriptionActivityResult<TStream>
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Run non-streaming transcription
|
|
104
153
|
*/
|
|
105
|
-
|
|
154
|
+
async function runGenerateTranscription<
|
|
106
155
|
TAdapter extends TranscriptionAdapter<string, object>,
|
|
107
156
|
>(
|
|
108
|
-
options: TranscriptionActivityOptions<TAdapter>,
|
|
109
|
-
):
|
|
110
|
-
const { adapter, ...rest } = options
|
|
157
|
+
options: TranscriptionActivityOptions<TAdapter, boolean>,
|
|
158
|
+
): Promise<TranscriptionResult> {
|
|
159
|
+
const { adapter, stream: _stream, ...rest } = options
|
|
111
160
|
const model = adapter.model
|
|
112
161
|
const requestId = createId('transcription')
|
|
113
162
|
const startTime = Date.now()
|
|
@@ -149,9 +198,10 @@ export async function generateTranscription<
|
|
|
149
198
|
*/
|
|
150
199
|
export function createTranscriptionOptions<
|
|
151
200
|
TAdapter extends TranscriptionAdapter<string, object>,
|
|
201
|
+
TStream extends boolean = false,
|
|
152
202
|
>(
|
|
153
|
-
options: TranscriptionActivityOptions<TAdapter>,
|
|
154
|
-
): TranscriptionActivityOptions<TAdapter> {
|
|
203
|
+
options: TranscriptionActivityOptions<TAdapter, TStream>,
|
|
204
|
+
): TranscriptionActivityOptions<TAdapter, TStream> {
|
|
155
205
|
return options
|
|
156
206
|
}
|
|
157
207
|
|