@tanstack/ai 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/esm/activities/chat/index.js +47 -6
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +3 -0
- package/dist/esm/activities/chat/stream/processor.js +10 -4
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +5 -3
- package/dist/esm/activities/chat/tools/tool-calls.js +57 -3
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +2 -2
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/generateImage/adapter.d.ts +8 -2
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateImage/index.d.ts +8 -6
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.d.ts +11 -5
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +20 -14
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/extend-adapter.d.ts +114 -0
- package/dist/esm/extend-adapter.js +15 -0
- package/dist/esm/extend-adapter.js.map +1 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types.d.ts +35 -8
- package/package.json +1 -1
- package/src/activities/chat/index.ts +75 -4
- package/src/activities/chat/stream/processor.ts +22 -4
- package/src/activities/chat/tools/tool-calls.ts +88 -5
- package/src/activities/chat/tools/tool-definition.ts +3 -0
- package/src/activities/generateImage/adapter.ts +9 -2
- package/src/activities/generateImage/index.ts +19 -9
- package/src/activities/generateVideo/adapter.ts +19 -4
- package/src/activities/generateVideo/index.ts +30 -16
- package/src/extend-adapter.ts +182 -0
- package/src/index.ts +4 -0
- package/src/types.ts +36 -6
|
@@ -24,6 +24,7 @@ import type { AnyTextAdapter } from './adapter'
|
|
|
24
24
|
import type {
|
|
25
25
|
AgentLoopStrategy,
|
|
26
26
|
ConstrainedModelMessage,
|
|
27
|
+
CustomEvent,
|
|
27
28
|
InferSchemaType,
|
|
28
29
|
ModelMessage,
|
|
29
30
|
RunFinishedEvent,
|
|
@@ -601,17 +602,30 @@ class TextEngine<
|
|
|
601
602
|
|
|
602
603
|
const { approvals, clientToolResults } = this.collectClientState()
|
|
603
604
|
|
|
604
|
-
const
|
|
605
|
+
const generator = executeToolCalls(
|
|
605
606
|
pendingToolCalls,
|
|
606
607
|
this.tools,
|
|
607
608
|
approvals,
|
|
608
609
|
clientToolResults,
|
|
610
|
+
(eventName, data) => this.createCustomEventChunk(eventName, data),
|
|
609
611
|
)
|
|
610
612
|
|
|
613
|
+
// Consume the async generator, yielding custom events and collecting the return value
|
|
614
|
+
const executionResult = yield* this.drainToolCallGenerator(generator)
|
|
615
|
+
|
|
611
616
|
if (
|
|
612
617
|
executionResult.needsApproval.length > 0 ||
|
|
613
618
|
executionResult.needsClientExecution.length > 0
|
|
614
619
|
) {
|
|
620
|
+
if (executionResult.results.length > 0) {
|
|
621
|
+
for (const chunk of this.emitToolResults(
|
|
622
|
+
executionResult.results,
|
|
623
|
+
finishEvent,
|
|
624
|
+
)) {
|
|
625
|
+
yield chunk
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
615
629
|
for (const chunk of this.emitApprovalRequests(
|
|
616
630
|
executionResult.needsApproval,
|
|
617
631
|
finishEvent,
|
|
@@ -660,17 +674,30 @@ class TextEngine<
|
|
|
660
674
|
|
|
661
675
|
const { approvals, clientToolResults } = this.collectClientState()
|
|
662
676
|
|
|
663
|
-
const
|
|
677
|
+
const generator = executeToolCalls(
|
|
664
678
|
toolCalls,
|
|
665
679
|
this.tools,
|
|
666
680
|
approvals,
|
|
667
681
|
clientToolResults,
|
|
682
|
+
(eventName, data) => this.createCustomEventChunk(eventName, data),
|
|
668
683
|
)
|
|
669
684
|
|
|
685
|
+
// Consume the async generator, yielding custom events and collecting the return value
|
|
686
|
+
const executionResult = yield* this.drainToolCallGenerator(generator)
|
|
687
|
+
|
|
670
688
|
if (
|
|
671
689
|
executionResult.needsApproval.length > 0 ||
|
|
672
690
|
executionResult.needsClientExecution.length > 0
|
|
673
691
|
) {
|
|
692
|
+
if (executionResult.results.length > 0) {
|
|
693
|
+
for (const chunk of this.emitToolResults(
|
|
694
|
+
executionResult.results,
|
|
695
|
+
finishEvent,
|
|
696
|
+
)) {
|
|
697
|
+
yield chunk
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
674
701
|
for (const chunk of this.emitApprovalRequests(
|
|
675
702
|
executionResult.needsApproval,
|
|
676
703
|
finishEvent,
|
|
@@ -833,7 +860,7 @@ class TextEngine<
|
|
|
833
860
|
timestamp: Date.now(),
|
|
834
861
|
model: finishEvent.model,
|
|
835
862
|
name: 'approval-requested',
|
|
836
|
-
|
|
863
|
+
value: {
|
|
837
864
|
toolCallId: approval.toolCallId,
|
|
838
865
|
toolName: approval.toolName,
|
|
839
866
|
input: approval.input,
|
|
@@ -870,7 +897,7 @@ class TextEngine<
|
|
|
870
897
|
timestamp: Date.now(),
|
|
871
898
|
model: finishEvent.model,
|
|
872
899
|
name: 'tool-input-available',
|
|
873
|
-
|
|
900
|
+
value: {
|
|
874
901
|
toolCallId: clientTool.toolCallId,
|
|
875
902
|
toolName: clientTool.toolName,
|
|
876
903
|
input: clientTool.input,
|
|
@@ -1052,6 +1079,50 @@ class TextEngine<
|
|
|
1052
1079
|
}
|
|
1053
1080
|
}
|
|
1054
1081
|
|
|
1082
|
+
/**
|
|
1083
|
+
* Drain an executeToolCalls async generator, yielding any CustomEvent chunks
|
|
1084
|
+
* and returning the final ExecuteToolCallsResult.
|
|
1085
|
+
*/
|
|
1086
|
+
private async *drainToolCallGenerator(
|
|
1087
|
+
generator: AsyncGenerator<
|
|
1088
|
+
CustomEvent,
|
|
1089
|
+
{
|
|
1090
|
+
results: Array<ToolResult>
|
|
1091
|
+
needsApproval: Array<ApprovalRequest>
|
|
1092
|
+
needsClientExecution: Array<ClientToolRequest>
|
|
1093
|
+
},
|
|
1094
|
+
void
|
|
1095
|
+
>,
|
|
1096
|
+
): AsyncGenerator<
|
|
1097
|
+
StreamChunk,
|
|
1098
|
+
{
|
|
1099
|
+
results: Array<ToolResult>
|
|
1100
|
+
needsApproval: Array<ApprovalRequest>
|
|
1101
|
+
needsClientExecution: Array<ClientToolRequest>
|
|
1102
|
+
},
|
|
1103
|
+
void
|
|
1104
|
+
> {
|
|
1105
|
+
let next = await generator.next()
|
|
1106
|
+
while (!next.done) {
|
|
1107
|
+
yield next.value
|
|
1108
|
+
next = await generator.next()
|
|
1109
|
+
}
|
|
1110
|
+
return next.value
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
private createCustomEventChunk(
|
|
1114
|
+
eventName: string,
|
|
1115
|
+
value: Record<string, any>,
|
|
1116
|
+
): CustomEvent {
|
|
1117
|
+
return {
|
|
1118
|
+
type: 'CUSTOM',
|
|
1119
|
+
timestamp: Date.now(),
|
|
1120
|
+
model: this.params.model,
|
|
1121
|
+
name: eventName,
|
|
1122
|
+
value,
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1055
1126
|
private createId(prefix: string): string {
|
|
1056
1127
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
1057
1128
|
}
|
|
@@ -73,6 +73,13 @@ export interface StreamProcessorEvents {
|
|
|
73
73
|
approvalId: string
|
|
74
74
|
}) => void
|
|
75
75
|
|
|
76
|
+
// Custom events from server-side tools
|
|
77
|
+
onCustomEvent?: (
|
|
78
|
+
eventType: string,
|
|
79
|
+
data: unknown,
|
|
80
|
+
context: { toolCallId?: string },
|
|
81
|
+
) => void
|
|
82
|
+
|
|
76
83
|
// Granular events for UI optimization (character-by-character, state tracking)
|
|
77
84
|
onTextUpdate?: (messageId: string, content: string) => void
|
|
78
85
|
onToolCallStateChange?: (
|
|
@@ -807,8 +814,8 @@ export class StreamProcessor {
|
|
|
807
814
|
chunk: Extract<StreamChunk, { type: 'CUSTOM' }>,
|
|
808
815
|
): void {
|
|
809
816
|
// Handle client tool input availability - trigger client-side execution
|
|
810
|
-
if (chunk.name === 'tool-input-available' && chunk.
|
|
811
|
-
const { toolCallId, toolName, input } = chunk.
|
|
817
|
+
if (chunk.name === 'tool-input-available' && chunk.value) {
|
|
818
|
+
const { toolCallId, toolName, input } = chunk.value as {
|
|
812
819
|
toolCallId: string
|
|
813
820
|
toolName: string
|
|
814
821
|
input: any
|
|
@@ -820,11 +827,12 @@ export class StreamProcessor {
|
|
|
820
827
|
toolName,
|
|
821
828
|
input,
|
|
822
829
|
})
|
|
830
|
+
return
|
|
823
831
|
}
|
|
824
832
|
|
|
825
833
|
// Handle approval requests
|
|
826
|
-
if (chunk.name === 'approval-requested' && chunk.
|
|
827
|
-
const { toolCallId, toolName, input, approval } = chunk.
|
|
834
|
+
if (chunk.name === 'approval-requested' && chunk.value) {
|
|
835
|
+
const { toolCallId, toolName, input, approval } = chunk.value as {
|
|
828
836
|
toolCallId: string
|
|
829
837
|
toolName: string
|
|
830
838
|
input: any
|
|
@@ -849,6 +857,16 @@ export class StreamProcessor {
|
|
|
849
857
|
input,
|
|
850
858
|
approvalId: approval.id,
|
|
851
859
|
})
|
|
860
|
+
return
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Forward non-system custom events to onCustomEvent callback
|
|
864
|
+
if (this.events.onCustomEvent) {
|
|
865
|
+
const toolCallId =
|
|
866
|
+
chunk.value && typeof chunk.value === 'object'
|
|
867
|
+
? (chunk.value as any).toolCallId
|
|
868
|
+
: undefined
|
|
869
|
+
this.events.onCustomEvent(chunk.name, chunk.value, { toolCallId })
|
|
852
870
|
}
|
|
853
871
|
}
|
|
854
872
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isStandardSchema, parseWithStandardSchema } from './schema-converter'
|
|
2
2
|
import type {
|
|
3
|
+
CustomEvent,
|
|
3
4
|
ModelMessage,
|
|
4
5
|
RunFinishedEvent,
|
|
5
6
|
Tool,
|
|
@@ -7,6 +8,7 @@ import type {
|
|
|
7
8
|
ToolCallArgsEvent,
|
|
8
9
|
ToolCallEndEvent,
|
|
9
10
|
ToolCallStartEvent,
|
|
11
|
+
ToolExecutionContext,
|
|
10
12
|
} from '../../../types'
|
|
11
13
|
|
|
12
14
|
/**
|
|
@@ -250,7 +252,46 @@ interface ExecuteToolCallsResult {
|
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
/**
|
|
253
|
-
*
|
|
255
|
+
* Helper that runs a tool execution promise while polling for pending custom events.
|
|
256
|
+
* Yields any custom events that are emitted during execution, then returns the
|
|
257
|
+
* execution result.
|
|
258
|
+
*/
|
|
259
|
+
async function* executeWithEventPolling<T>(
|
|
260
|
+
executionPromise: Promise<T>,
|
|
261
|
+
pendingEvents: Array<CustomEvent>,
|
|
262
|
+
): AsyncGenerator<CustomEvent, T, void> {
|
|
263
|
+
// Use an object to track mutable state across the async boundary
|
|
264
|
+
const state = { done: false, result: undefined as T }
|
|
265
|
+
const executionWithFlag = executionPromise.then((r) => {
|
|
266
|
+
state.done = true
|
|
267
|
+
state.result = r
|
|
268
|
+
return r
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
while (!state.done) {
|
|
272
|
+
// Wait for either the execution to complete or a short timeout
|
|
273
|
+
await Promise.race([
|
|
274
|
+
executionWithFlag,
|
|
275
|
+
new Promise((resolve) => setTimeout(resolve, 10)),
|
|
276
|
+
])
|
|
277
|
+
|
|
278
|
+
// Flush any pending events
|
|
279
|
+
while (pendingEvents.length > 0) {
|
|
280
|
+
yield pendingEvents.shift()!
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Final flush in case events were emitted right at completion
|
|
285
|
+
while (pendingEvents.length > 0) {
|
|
286
|
+
yield pendingEvents.shift()!
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return state.result
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Execute tool calls based on their configuration.
|
|
294
|
+
* Yields CustomEvent chunks during tool execution for real-time progress updates.
|
|
254
295
|
*
|
|
255
296
|
* Handles three cases:
|
|
256
297
|
* 1. Client tools (no execute) - request client to execute
|
|
@@ -261,13 +302,18 @@ interface ExecuteToolCallsResult {
|
|
|
261
302
|
* @param tools - Available tools with their configurations
|
|
262
303
|
* @param approvals - Map of approval decisions (approval.id -> approved boolean)
|
|
263
304
|
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
305
|
+
* @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)
|
|
264
306
|
*/
|
|
265
|
-
export async function executeToolCalls(
|
|
307
|
+
export async function* executeToolCalls(
|
|
266
308
|
toolCalls: Array<ToolCall>,
|
|
267
309
|
tools: ReadonlyArray<Tool>,
|
|
268
310
|
approvals: Map<string, boolean> = new Map(),
|
|
269
311
|
clientResults: Map<string, any> = new Map(),
|
|
270
|
-
|
|
312
|
+
createCustomEventChunk?: (
|
|
313
|
+
eventName: string,
|
|
314
|
+
value: Record<string, any>,
|
|
315
|
+
) => CustomEvent,
|
|
316
|
+
): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void> {
|
|
271
317
|
const results: Array<ToolResult> = []
|
|
272
318
|
const needsApproval: Array<ApprovalRequest> = []
|
|
273
319
|
const needsClientExecution: Array<ClientToolRequest> = []
|
|
@@ -326,6 +372,29 @@ export async function executeToolCalls(
|
|
|
326
372
|
}
|
|
327
373
|
}
|
|
328
374
|
|
|
375
|
+
// Create a ToolExecutionContext for this tool call with event emission
|
|
376
|
+
const pendingEvents: Array<CustomEvent> = []
|
|
377
|
+
const context: ToolExecutionContext = {
|
|
378
|
+
toolCallId: toolCall.id,
|
|
379
|
+
emitCustomEvent: (eventName: string, value: Record<string, any>) => {
|
|
380
|
+
if (createCustomEventChunk) {
|
|
381
|
+
pendingEvents.push(
|
|
382
|
+
createCustomEventChunk(eventName, {
|
|
383
|
+
...value,
|
|
384
|
+
toolCallId: toolCall.id,
|
|
385
|
+
}),
|
|
386
|
+
)
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Helper to flush any pending events
|
|
392
|
+
function* flushEvents(): Generator<CustomEvent> {
|
|
393
|
+
while (pendingEvents.length > 0) {
|
|
394
|
+
yield pendingEvents.shift()!
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
329
398
|
// CASE 1: Client-side tool (no execute function)
|
|
330
399
|
if (!tool.execute) {
|
|
331
400
|
// Check if tool needs approval
|
|
@@ -402,8 +471,15 @@ export async function executeToolCalls(
|
|
|
402
471
|
// Execute after approval
|
|
403
472
|
const startTime = Date.now()
|
|
404
473
|
try {
|
|
405
|
-
|
|
474
|
+
const executionPromise = Promise.resolve(
|
|
475
|
+
tool.execute(input, context),
|
|
476
|
+
)
|
|
477
|
+
let result = yield* executeWithEventPolling(
|
|
478
|
+
executionPromise,
|
|
479
|
+
pendingEvents,
|
|
480
|
+
)
|
|
406
481
|
const duration = Date.now() - startTime
|
|
482
|
+
yield* flushEvents()
|
|
407
483
|
|
|
408
484
|
// Validate output against outputSchema if provided (for Standard Schema compliant schemas)
|
|
409
485
|
if (
|
|
@@ -426,6 +502,7 @@ export async function executeToolCalls(
|
|
|
426
502
|
})
|
|
427
503
|
} catch (error: unknown) {
|
|
428
504
|
const duration = Date.now() - startTime
|
|
505
|
+
yield* flushEvents()
|
|
429
506
|
const message =
|
|
430
507
|
error instanceof Error ? error.message : 'Unknown error'
|
|
431
508
|
results.push({
|
|
@@ -460,8 +537,13 @@ export async function executeToolCalls(
|
|
|
460
537
|
// CASE 3: Normal server tool - execute immediately
|
|
461
538
|
const startTime = Date.now()
|
|
462
539
|
try {
|
|
463
|
-
|
|
540
|
+
const executionPromise = Promise.resolve(tool.execute(input, context))
|
|
541
|
+
let result = yield* executeWithEventPolling(
|
|
542
|
+
executionPromise,
|
|
543
|
+
pendingEvents,
|
|
544
|
+
)
|
|
464
545
|
const duration = Date.now() - startTime
|
|
546
|
+
yield* flushEvents()
|
|
465
547
|
|
|
466
548
|
// Validate output against outputSchema if provided (for Standard Schema compliant schemas)
|
|
467
549
|
if (
|
|
@@ -482,6 +564,7 @@ export async function executeToolCalls(
|
|
|
482
564
|
})
|
|
483
565
|
} catch (error: unknown) {
|
|
484
566
|
const duration = Date.now() - startTime
|
|
567
|
+
yield* flushEvents()
|
|
485
568
|
const message = error instanceof Error ? error.message : 'Unknown error'
|
|
486
569
|
results.push({
|
|
487
570
|
toolCallId: toolCall.id,
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
JSONSchema,
|
|
5
5
|
SchemaInput,
|
|
6
6
|
Tool,
|
|
7
|
+
ToolExecutionContext,
|
|
7
8
|
} from '../../../types'
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -112,6 +113,7 @@ export interface ToolDefinition<
|
|
|
112
113
|
server: (
|
|
113
114
|
execute: (
|
|
114
115
|
args: InferSchemaType<TInput>,
|
|
116
|
+
context?: ToolExecutionContext,
|
|
115
117
|
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
116
118
|
) => ServerTool<TInput, TOutput, TName>
|
|
117
119
|
|
|
@@ -193,6 +195,7 @@ export function toolDefinition<
|
|
|
193
195
|
server(
|
|
194
196
|
execute: (
|
|
195
197
|
args: InferSchemaType<TInput>,
|
|
198
|
+
context?: ToolExecutionContext,
|
|
196
199
|
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
197
200
|
): ServerTool<TInput, TOutput, TName> {
|
|
198
201
|
return {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { ImageGenerationOptions, ImageGenerationResult } from '../../types'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the size type for a model from the model-size map.
|
|
5
|
+
* If the map has an index signature (i.e. no explicit keys), falls back to string.
|
|
6
|
+
* If the model is an explicit key, uses its mapped size type.
|
|
7
|
+
* Otherwise falls back to string.
|
|
8
|
+
*/
|
|
9
|
+
|
|
3
10
|
/**
|
|
4
11
|
* Configuration for image adapter instances
|
|
5
12
|
*/
|
|
@@ -49,7 +56,7 @@ export interface ImageAdapter<
|
|
|
49
56
|
* Generate images from a prompt
|
|
50
57
|
*/
|
|
51
58
|
generateImages: (
|
|
52
|
-
options: ImageGenerationOptions<TProviderOptions>,
|
|
59
|
+
options: ImageGenerationOptions<TProviderOptions, TModelSizeByName[TModel]>,
|
|
53
60
|
) => Promise<ImageGenerationResult>
|
|
54
61
|
}
|
|
55
62
|
|
|
@@ -95,7 +102,7 @@ export abstract class BaseImageAdapter<
|
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
abstract generateImages(
|
|
98
|
-
options: ImageGenerationOptions<TProviderOptions>,
|
|
105
|
+
options: ImageGenerationOptions<TProviderOptions, TModelSizeByName[TModel]>,
|
|
99
106
|
): Promise<ImageGenerationResult>
|
|
100
107
|
|
|
101
108
|
protected generateId(): string {
|
|
@@ -61,9 +61,9 @@ export type ImageSizeForModel<TAdapter, TModel extends string> =
|
|
|
61
61
|
*
|
|
62
62
|
* @template TAdapter - The image adapter type
|
|
63
63
|
*/
|
|
64
|
-
export
|
|
65
|
-
TAdapter extends ImageAdapter<string,
|
|
66
|
-
> {
|
|
64
|
+
export type ImageActivityOptions<
|
|
65
|
+
TAdapter extends ImageAdapter<string, any, any, any>,
|
|
66
|
+
> = {
|
|
67
67
|
/** The image adapter to use (must be created with a model) */
|
|
68
68
|
adapter: TAdapter & { kind: typeof kind }
|
|
69
69
|
/** Text description of the desired image(s) */
|
|
@@ -72,9 +72,19 @@ export interface ImageActivityOptions<
|
|
|
72
72
|
numberOfImages?: number
|
|
73
73
|
/** Image size in WIDTHxHEIGHT format (e.g., "1024x1024") */
|
|
74
74
|
size?: ImageSizeForModel<TAdapter, TAdapter['model']>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
} & ({} extends ImageProviderOptionsForModel<TAdapter, TAdapter['model']>
|
|
76
|
+
? {
|
|
77
|
+
/** Provider-specific options for image generation */ modelOptions?: ImageProviderOptionsForModel<
|
|
78
|
+
TAdapter,
|
|
79
|
+
TAdapter['model']
|
|
80
|
+
>
|
|
81
|
+
}
|
|
82
|
+
: {
|
|
83
|
+
/** Provider-specific options for image generation */ modelOptions: ImageProviderOptionsForModel<
|
|
84
|
+
TAdapter,
|
|
85
|
+
TAdapter['model']
|
|
86
|
+
>
|
|
87
|
+
})
|
|
78
88
|
|
|
79
89
|
// ===========================
|
|
80
90
|
// Activity Result Type
|
|
@@ -137,7 +147,7 @@ function createId(prefix: string): string {
|
|
|
137
147
|
* ```
|
|
138
148
|
*/
|
|
139
149
|
export async function generateImage<
|
|
140
|
-
TAdapter extends ImageAdapter<string,
|
|
150
|
+
TAdapter extends ImageAdapter<string, any, any, any>,
|
|
141
151
|
>(options: ImageActivityOptions<TAdapter>): ImageActivityResult {
|
|
142
152
|
const { adapter, ...rest } = options
|
|
143
153
|
const model = adapter.model
|
|
@@ -150,7 +160,7 @@ export async function generateImage<
|
|
|
150
160
|
model,
|
|
151
161
|
prompt: rest.prompt,
|
|
152
162
|
numberOfImages: rest.numberOfImages,
|
|
153
|
-
size: rest.size
|
|
163
|
+
size: rest.size,
|
|
154
164
|
modelOptions: rest.modelOptions as Record<string, unknown> | undefined,
|
|
155
165
|
timestamp: startTime,
|
|
156
166
|
})
|
|
@@ -193,7 +203,7 @@ export async function generateImage<
|
|
|
193
203
|
* Create typed options for the generateImage() function without executing.
|
|
194
204
|
*/
|
|
195
205
|
export function createImageOptions<
|
|
196
|
-
TAdapter extends ImageAdapter<string,
|
|
206
|
+
TAdapter extends ImageAdapter<string, any, any, any>,
|
|
197
207
|
>(options: ImageActivityOptions<TAdapter>): ImageActivityOptions<TAdapter> {
|
|
198
208
|
return options
|
|
199
209
|
}
|
|
@@ -29,10 +29,14 @@ export interface VideoAdapterConfig {
|
|
|
29
29
|
* Generic parameters:
|
|
30
30
|
* - TModel: The specific model name (e.g., 'sora-2')
|
|
31
31
|
* - TProviderOptions: Provider-specific options (already resolved)
|
|
32
|
+
* - TModelProviderOptionsByName: Map from model name to its specific provider options
|
|
33
|
+
* - TModelSizeByName: Map from model name to its supported sizes
|
|
32
34
|
*/
|
|
33
35
|
export interface VideoAdapter<
|
|
34
36
|
TModel extends string = string,
|
|
35
37
|
TProviderOptions extends object = Record<string, unknown>,
|
|
38
|
+
TModelProviderOptionsByName extends Record<string, any> = Record<string, any>,
|
|
39
|
+
TModelSizeByName extends Record<string, string> = Record<string, string>,
|
|
36
40
|
> {
|
|
37
41
|
/** Discriminator for adapter kind - used to determine API shape */
|
|
38
42
|
readonly kind: 'video'
|
|
@@ -46,6 +50,8 @@ export interface VideoAdapter<
|
|
|
46
50
|
*/
|
|
47
51
|
'~types': {
|
|
48
52
|
providerOptions: TProviderOptions
|
|
53
|
+
modelProviderOptionsByName: TModelProviderOptionsByName
|
|
54
|
+
modelSizeByName: TModelSizeByName
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
/**
|
|
@@ -53,7 +59,7 @@ export interface VideoAdapter<
|
|
|
53
59
|
* Returns a job ID that can be used to poll for status and retrieve the video.
|
|
54
60
|
*/
|
|
55
61
|
createVideoJob: (
|
|
56
|
-
options: VideoGenerationOptions<TProviderOptions>,
|
|
62
|
+
options: VideoGenerationOptions<TProviderOptions, TModelSizeByName[TModel]>,
|
|
57
63
|
) => Promise<VideoJobResult>
|
|
58
64
|
|
|
59
65
|
/**
|
|
@@ -72,7 +78,7 @@ export interface VideoAdapter<
|
|
|
72
78
|
* A VideoAdapter with any/unknown type parameters.
|
|
73
79
|
* Useful as a constraint in generic functions and interfaces.
|
|
74
80
|
*/
|
|
75
|
-
export type AnyVideoAdapter = VideoAdapter<any, any>
|
|
81
|
+
export type AnyVideoAdapter = VideoAdapter<any, any, any, any>
|
|
76
82
|
|
|
77
83
|
/**
|
|
78
84
|
* Abstract base class for video generation adapters.
|
|
@@ -85,7 +91,14 @@ export type AnyVideoAdapter = VideoAdapter<any, any>
|
|
|
85
91
|
export abstract class BaseVideoAdapter<
|
|
86
92
|
TModel extends string = string,
|
|
87
93
|
TProviderOptions extends object = Record<string, unknown>,
|
|
88
|
-
|
|
94
|
+
TModelProviderOptionsByName extends Record<string, any> = Record<string, any>,
|
|
95
|
+
TModelSizeByName extends Record<string, string> = Record<string, string>,
|
|
96
|
+
> implements VideoAdapter<
|
|
97
|
+
TModel,
|
|
98
|
+
TProviderOptions,
|
|
99
|
+
TModelProviderOptionsByName,
|
|
100
|
+
TModelSizeByName
|
|
101
|
+
> {
|
|
89
102
|
readonly kind = 'video' as const
|
|
90
103
|
abstract readonly name: string
|
|
91
104
|
readonly model: TModel
|
|
@@ -93,6 +106,8 @@ export abstract class BaseVideoAdapter<
|
|
|
93
106
|
// Type-only property - never assigned at runtime
|
|
94
107
|
declare '~types': {
|
|
95
108
|
providerOptions: TProviderOptions
|
|
109
|
+
modelProviderOptionsByName: TModelProviderOptionsByName
|
|
110
|
+
modelSizeByName: TModelSizeByName
|
|
96
111
|
}
|
|
97
112
|
|
|
98
113
|
protected config: VideoAdapterConfig
|
|
@@ -103,7 +118,7 @@ export abstract class BaseVideoAdapter<
|
|
|
103
118
|
}
|
|
104
119
|
|
|
105
120
|
abstract createVideoJob(
|
|
106
|
-
options: VideoGenerationOptions<TProviderOptions>,
|
|
121
|
+
options: VideoGenerationOptions<TProviderOptions, TModelSizeByName[TModel]>,
|
|
107
122
|
): Promise<VideoJobResult>
|
|
108
123
|
|
|
109
124
|
abstract getVideoStatus(jobId: string): Promise<VideoStatusResult>
|
|
@@ -30,10 +30,20 @@ export const kind = 'video' as const
|
|
|
30
30
|
* Extract provider options from a VideoAdapter via ~types.
|
|
31
31
|
*/
|
|
32
32
|
export type VideoProviderOptions<TAdapter> =
|
|
33
|
-
TAdapter extends VideoAdapter<any, any>
|
|
33
|
+
TAdapter extends VideoAdapter<any, any, any, any>
|
|
34
34
|
? TAdapter['~types']['providerOptions']
|
|
35
35
|
: object
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Extract the size type for a VideoAdapter's model via ~types.
|
|
39
|
+
*/
|
|
40
|
+
export type VideoSizeForAdapter<TAdapter> =
|
|
41
|
+
TAdapter extends VideoAdapter<infer TModel, any, any, infer TSizeMap>
|
|
42
|
+
? TModel extends keyof TSizeMap
|
|
43
|
+
? TSizeMap[TModel]
|
|
44
|
+
: string
|
|
45
|
+
: string
|
|
46
|
+
|
|
37
47
|
// ===========================
|
|
38
48
|
// Activity Options Types
|
|
39
49
|
|
|
@@ -47,7 +57,7 @@ function createId(prefix: string): string {
|
|
|
47
57
|
* The model is extracted from the adapter's model property.
|
|
48
58
|
*/
|
|
49
59
|
interface VideoActivityBaseOptions<
|
|
50
|
-
TAdapter extends VideoAdapter<string,
|
|
60
|
+
TAdapter extends VideoAdapter<string, any, any, any>,
|
|
51
61
|
> {
|
|
52
62
|
/** The video adapter to use (must be created with a model) */
|
|
53
63
|
adapter: TAdapter & { kind: typeof kind }
|
|
@@ -59,20 +69,24 @@ interface VideoActivityBaseOptions<
|
|
|
59
69
|
*
|
|
60
70
|
* @experimental Video generation is an experimental feature and may change.
|
|
61
71
|
*/
|
|
62
|
-
export
|
|
63
|
-
TAdapter extends VideoAdapter<string,
|
|
64
|
-
>
|
|
72
|
+
export type VideoCreateOptions<
|
|
73
|
+
TAdapter extends VideoAdapter<string, any, any, any>,
|
|
74
|
+
> = VideoActivityBaseOptions<TAdapter> & {
|
|
65
75
|
/** Request type - create a new job (default if not specified) */
|
|
66
76
|
request?: 'create'
|
|
67
77
|
/** Text description of the desired video */
|
|
68
78
|
prompt: string
|
|
69
|
-
/** Video size
|
|
70
|
-
size?:
|
|
79
|
+
/** Video size — format depends on the provider (e.g., "16:9", "1280x720") */
|
|
80
|
+
size?: VideoSizeForAdapter<TAdapter>
|
|
71
81
|
/** Video duration in seconds */
|
|
72
82
|
duration?: number
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
} & ({} extends VideoProviderOptions<TAdapter>
|
|
84
|
+
? {
|
|
85
|
+
/** Provider-specific options for video generation */ modelOptions?: VideoProviderOptions<TAdapter>
|
|
86
|
+
}
|
|
87
|
+
: {
|
|
88
|
+
/** Provider-specific options for video generation */ modelOptions: VideoProviderOptions<TAdapter>
|
|
89
|
+
})
|
|
76
90
|
|
|
77
91
|
/**
|
|
78
92
|
* Options for polling the status of a video generation job.
|
|
@@ -80,7 +94,7 @@ export interface VideoCreateOptions<
|
|
|
80
94
|
* @experimental Video generation is an experimental feature and may change.
|
|
81
95
|
*/
|
|
82
96
|
export interface VideoStatusOptions<
|
|
83
|
-
TAdapter extends VideoAdapter<string,
|
|
97
|
+
TAdapter extends VideoAdapter<string, any, any, any>,
|
|
84
98
|
> extends VideoActivityBaseOptions<TAdapter> {
|
|
85
99
|
/** Request type - get job status */
|
|
86
100
|
request: 'status'
|
|
@@ -94,7 +108,7 @@ export interface VideoStatusOptions<
|
|
|
94
108
|
* @experimental Video generation is an experimental feature and may change.
|
|
95
109
|
*/
|
|
96
110
|
export interface VideoUrlOptions<
|
|
97
|
-
TAdapter extends VideoAdapter<string,
|
|
111
|
+
TAdapter extends VideoAdapter<string, any, any, any>,
|
|
98
112
|
> extends VideoActivityBaseOptions<TAdapter> {
|
|
99
113
|
/** Request type - get video URL */
|
|
100
114
|
request: 'url'
|
|
@@ -109,7 +123,7 @@ export interface VideoUrlOptions<
|
|
|
109
123
|
* @experimental Video generation is an experimental feature and may change.
|
|
110
124
|
*/
|
|
111
125
|
export type VideoActivityOptions<
|
|
112
|
-
TAdapter extends VideoAdapter<string,
|
|
126
|
+
TAdapter extends VideoAdapter<string, any, any, any>,
|
|
113
127
|
TRequest extends 'create' | 'status' | 'url' = 'create',
|
|
114
128
|
> = TRequest extends 'status'
|
|
115
129
|
? VideoStatusOptions<TAdapter>
|
|
@@ -161,7 +175,7 @@ export type VideoActivityResult<
|
|
|
161
175
|
* ```
|
|
162
176
|
*/
|
|
163
177
|
export async function generateVideo<
|
|
164
|
-
TAdapter extends VideoAdapter<string,
|
|
178
|
+
TAdapter extends VideoAdapter<string, any, any, any>,
|
|
165
179
|
>(options: VideoCreateOptions<TAdapter>): Promise<VideoJobResult> {
|
|
166
180
|
const { adapter, prompt, size, duration, modelOptions } = options
|
|
167
181
|
const model = adapter.model
|
|
@@ -201,7 +215,7 @@ export async function generateVideo<
|
|
|
201
215
|
* ```
|
|
202
216
|
*/
|
|
203
217
|
export async function getVideoJobStatus<
|
|
204
|
-
TAdapter extends VideoAdapter<string,
|
|
218
|
+
TAdapter extends VideoAdapter<string, any, any, any>,
|
|
205
219
|
>(options: {
|
|
206
220
|
adapter: TAdapter & { kind: typeof kind }
|
|
207
221
|
jobId: string
|
|
@@ -301,7 +315,7 @@ export async function getVideoJobStatus<
|
|
|
301
315
|
* Create typed options for the generateVideo() function without executing.
|
|
302
316
|
*/
|
|
303
317
|
export function createVideoOptions<
|
|
304
|
-
TAdapter extends VideoAdapter<string,
|
|
318
|
+
TAdapter extends VideoAdapter<string, any, any, any>,
|
|
305
319
|
>(options: VideoCreateOptions<TAdapter>): VideoCreateOptions<TAdapter> {
|
|
306
320
|
return options
|
|
307
321
|
}
|