@tanstack/ai-client 0.25.1 → 0.26.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/README.md +2 -1
- package/dist/esm/chat-client.d.ts +2 -2
- package/dist/esm/chat-client.js +48 -29
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/connection-adapters.js +37 -21
- package/dist/esm/connection-adapters.js.map +1 -1
- package/dist/esm/generation-client.js +4 -2
- package/dist/esm/generation-client.js.map +1 -1
- package/dist/esm/generation-types.js +4 -2
- package/dist/esm/generation-types.js.map +1 -1
- package/dist/esm/types.d.ts +17 -0
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/video-generation-client.js +4 -2
- package/dist/esm/video-generation-client.js.map +1 -1
- package/package.json +3 -3
- package/src/chat-client.ts +50 -17
- package/src/connection-adapters.ts +67 -41
- package/src/generation-client.ts +5 -5
- package/src/generation-types.ts +8 -2
- package/src/types.ts +17 -0
- package/src/video-generation-client.ts +5 -5
package/src/chat-client.ts
CHANGED
|
@@ -3,8 +3,11 @@ import {
|
|
|
3
3
|
convertSchemaToJsonSchema,
|
|
4
4
|
generateMessageId,
|
|
5
5
|
isStandardSchema,
|
|
6
|
+
mergeMetadata,
|
|
6
7
|
normalizeToUIMessage,
|
|
7
8
|
parseWithStandardSchema,
|
|
9
|
+
restoreInboundChunk,
|
|
10
|
+
tanstackMetadata,
|
|
8
11
|
} from '@tanstack/ai/client'
|
|
9
12
|
import { createNoOpChatDevtoolsBridge } from './devtools-noop'
|
|
10
13
|
import {
|
|
@@ -189,6 +192,7 @@ export function normalizeQueueOption(
|
|
|
189
192
|
* Merge a run of queued messages into a single send for `drain: 'batch'`.
|
|
190
193
|
* All-string content is joined with newlines; mixed/multimodal content is
|
|
191
194
|
* flattened into a single `ContentPart` array. The last item's `body` wins.
|
|
195
|
+
* Object-form metadata is merged last-write-wins per key.
|
|
192
196
|
*/
|
|
193
197
|
function mergeQueuedMessages(items: Array<InternalQueuedMessage>): {
|
|
194
198
|
content: string | MultimodalContent
|
|
@@ -209,17 +213,24 @@ function mergeQueuedMessages(items: Array<InternalQueuedMessage>): {
|
|
|
209
213
|
}
|
|
210
214
|
}
|
|
211
215
|
const parts: Array<ContentPart> = []
|
|
216
|
+
let metadata: Record<string, any> | undefined
|
|
212
217
|
for (const item of items) {
|
|
213
218
|
if (typeof item.content === 'string') {
|
|
214
219
|
parts.push({ type: 'text', content: item.content })
|
|
215
|
-
|
|
220
|
+
continue
|
|
221
|
+
}
|
|
222
|
+
if (typeof item.content.content === 'string') {
|
|
216
223
|
parts.push({ type: 'text', content: item.content.content })
|
|
217
224
|
} else {
|
|
218
225
|
parts.push(...item.content.content)
|
|
219
226
|
}
|
|
227
|
+
metadata = mergeMetadata(metadata, item.content.metadata)
|
|
220
228
|
}
|
|
221
229
|
return {
|
|
222
|
-
content: {
|
|
230
|
+
content: {
|
|
231
|
+
content: parts,
|
|
232
|
+
...(metadata !== undefined ? { metadata } : {}),
|
|
233
|
+
},
|
|
223
234
|
...(body !== undefined ? { body } : {}),
|
|
224
235
|
}
|
|
225
236
|
}
|
|
@@ -1747,12 +1758,14 @@ export class ChatClient<
|
|
|
1747
1758
|
chunk: StreamChunk,
|
|
1748
1759
|
options?: { defer?: boolean },
|
|
1749
1760
|
): Promise<void> {
|
|
1761
|
+
chunk = restoreInboundChunk(chunk)
|
|
1750
1762
|
if (
|
|
1751
1763
|
chunk.type === 'RUN_ERROR' &&
|
|
1752
1764
|
this.isActiveInterruptSubmissionFailure(chunk)
|
|
1753
1765
|
) {
|
|
1766
|
+
const interruptErrors = tanstackMetadata(chunk)?.interruptErrors
|
|
1754
1767
|
this.interruptSubmissionFailure = {
|
|
1755
|
-
errors:
|
|
1768
|
+
errors: Array.isArray(interruptErrors) ? interruptErrors : [],
|
|
1756
1769
|
}
|
|
1757
1770
|
}
|
|
1758
1771
|
if (this.connectionStatus === 'connecting') {
|
|
@@ -1776,11 +1789,13 @@ export class ChatClient<
|
|
|
1776
1789
|
this.processor.processChunk(chunk)
|
|
1777
1790
|
this.updateRunLifecycle(chunk)
|
|
1778
1791
|
this.observeInterruptState(chunk)
|
|
1779
|
-
//
|
|
1780
|
-
//
|
|
1781
|
-
//
|
|
1782
|
-
|
|
1783
|
-
|
|
1792
|
+
// Live path: yield a macrotask so the UI can paint. Skip when the page is
|
|
1793
|
+
// hidden. Browsers clamp setTimeout there, and that wait paces stream pull.
|
|
1794
|
+
// Replay passes defer: false so a backlog applies in one batch.
|
|
1795
|
+
if (
|
|
1796
|
+
options?.defer !== false &&
|
|
1797
|
+
(typeof document === 'undefined' || !document.hidden)
|
|
1798
|
+
) {
|
|
1784
1799
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
1785
1800
|
}
|
|
1786
1801
|
this.resolveJoinedRun(chunk)
|
|
@@ -1790,8 +1805,10 @@ export class ChatClient<
|
|
|
1790
1805
|
chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,
|
|
1791
1806
|
): boolean {
|
|
1792
1807
|
const submission = this.activeInterruptSubmission
|
|
1793
|
-
const errors = chunk
|
|
1794
|
-
if (!submission || !errors || errors.length === 0)
|
|
1808
|
+
const errors = tanstackMetadata(chunk)?.interruptErrors
|
|
1809
|
+
if (!submission || !Array.isArray(errors) || errors.length === 0) {
|
|
1810
|
+
return false
|
|
1811
|
+
}
|
|
1795
1812
|
const runId = getChunkRunId(chunk)
|
|
1796
1813
|
if (runId !== undefined && runId !== this.currentRunId) return false
|
|
1797
1814
|
if (
|
|
@@ -1800,12 +1817,22 @@ export class ChatClient<
|
|
|
1800
1817
|
) {
|
|
1801
1818
|
return false
|
|
1802
1819
|
}
|
|
1803
|
-
return errors.every(
|
|
1804
|
-
(
|
|
1820
|
+
return errors.every((error) => {
|
|
1821
|
+
if (
|
|
1822
|
+
error == null ||
|
|
1823
|
+
typeof error !== 'object' ||
|
|
1824
|
+
typeof error.threadId !== 'string' ||
|
|
1825
|
+
typeof error.interruptedRunId !== 'string' ||
|
|
1826
|
+
typeof error.generation !== 'number'
|
|
1827
|
+
) {
|
|
1828
|
+
return false
|
|
1829
|
+
}
|
|
1830
|
+
return (
|
|
1805
1831
|
error.threadId === submission.threadId &&
|
|
1806
1832
|
error.interruptedRunId === submission.interruptedRunId &&
|
|
1807
|
-
error.generation === submission.generation
|
|
1808
|
-
|
|
1833
|
+
error.generation === submission.generation
|
|
1834
|
+
)
|
|
1835
|
+
})
|
|
1809
1836
|
}
|
|
1810
1837
|
|
|
1811
1838
|
private resolveJoinedRun(chunk: StreamChunk): void {
|
|
@@ -1980,6 +2007,7 @@ export class ChatClient<
|
|
|
1980
2007
|
const userMessage = this.processor.addUserMessage(
|
|
1981
2008
|
normalizedContent.content,
|
|
1982
2009
|
normalizedContent.id,
|
|
2010
|
+
normalizedContent.metadata,
|
|
1983
2011
|
)
|
|
1984
2012
|
this.events.messageSent(userMessage.id, normalizedContent.content)
|
|
1985
2013
|
return await this.streamResponse()
|
|
@@ -2039,17 +2067,22 @@ export class ChatClient<
|
|
|
2039
2067
|
}
|
|
2040
2068
|
|
|
2041
2069
|
/**
|
|
2042
|
-
* Normalize the message input to extract content
|
|
2043
|
-
* Trims string content
|
|
2070
|
+
* Normalize the message input to extract content, optional id, and
|
|
2071
|
+
* optional metadata. String form has no metadata. Trims string content.
|
|
2044
2072
|
*/
|
|
2045
2073
|
private normalizeMessageInput(input: string | MultimodalContent): {
|
|
2046
2074
|
content: string | Array<ContentPart>
|
|
2047
2075
|
id?: string
|
|
2076
|
+
metadata?: Record<string, any>
|
|
2048
2077
|
} {
|
|
2049
2078
|
if (typeof input === 'string') {
|
|
2050
2079
|
return { content: input.trim() }
|
|
2051
2080
|
}
|
|
2052
|
-
return {
|
|
2081
|
+
return {
|
|
2082
|
+
content: input.content,
|
|
2083
|
+
id: input.id,
|
|
2084
|
+
...(input.metadata !== undefined ? { metadata: input.metadata } : {}),
|
|
2085
|
+
}
|
|
2053
2086
|
}
|
|
2054
2087
|
|
|
2055
2088
|
/**
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
EventType,
|
|
3
|
+
getChunkRunId as getNormalizedChunkRunId,
|
|
4
|
+
restoreInboundChunk,
|
|
5
|
+
tanstackMetadata,
|
|
6
|
+
uiMessagesToWire,
|
|
7
|
+
withTanstackMetadata,
|
|
8
|
+
} from '@tanstack/ai/client'
|
|
2
9
|
import {
|
|
3
10
|
createResponseStreamTextDecoder,
|
|
4
11
|
getResponseStreamReader,
|
|
@@ -8,7 +15,6 @@ import type {
|
|
|
8
15
|
ModelMessage,
|
|
9
16
|
RunAgentResumeItem,
|
|
10
17
|
RunErrorEvent,
|
|
11
|
-
RunFinishedEvent,
|
|
12
18
|
StreamChunk,
|
|
13
19
|
UIMessage,
|
|
14
20
|
} from '@tanstack/ai/client'
|
|
@@ -34,12 +40,7 @@ export function getChunkRunId(chunk: StreamChunk): string | undefined {
|
|
|
34
40
|
// the client's run identity to win when a provider stamps its own id; for
|
|
35
41
|
// resumable reconnect/join the two ids match, so precedence is moot there.
|
|
36
42
|
const requestRunId = chunkRunIds.get(chunk)
|
|
37
|
-
return (
|
|
38
|
-
requestRunId ??
|
|
39
|
-
('runId' in chunk && typeof chunk.runId === 'string'
|
|
40
|
-
? chunk.runId
|
|
41
|
-
: undefined)
|
|
42
|
-
)
|
|
43
|
+
return requestRunId ?? getNormalizedChunkRunId(chunk)
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
/**
|
|
@@ -385,6 +386,22 @@ function isNdjsonEnvelope(
|
|
|
385
386
|
)
|
|
386
387
|
}
|
|
387
388
|
|
|
389
|
+
/** Rebuild pre-wire extras after SSE/NDJSON ingest. */
|
|
390
|
+
function restoreInboundUsage(chunk: StreamChunk): StreamChunk {
|
|
391
|
+
return restoreInboundChunk(chunk)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function sseChunkModel(chunk: StreamChunk): string | undefined {
|
|
395
|
+
const tanstackModel = tanstackMetadata(chunk)?.model
|
|
396
|
+
if (typeof tanstackModel === 'string') return tanstackModel
|
|
397
|
+
const usage = 'usage' in chunk ? chunk.usage : undefined
|
|
398
|
+
if (Array.isArray(usage)) {
|
|
399
|
+
const model = (usage[0] as { model?: unknown } | undefined)?.model
|
|
400
|
+
if (typeof model === 'string') return model
|
|
401
|
+
}
|
|
402
|
+
return undefined
|
|
403
|
+
}
|
|
404
|
+
|
|
388
405
|
/**
|
|
389
406
|
* Parse SSE-format lines into stream events, pairing each chunk with the `id:`
|
|
390
407
|
* offset of the event it arrived on. Shared by the fetch- and XHR-backed SSE
|
|
@@ -431,27 +448,31 @@ async function* linesToSSEEvents(
|
|
|
431
448
|
}
|
|
432
449
|
const data = parseSseDataLine(line)
|
|
433
450
|
if (data === '[DONE]') {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
451
|
+
yield {
|
|
452
|
+
chunk: withTanstackMetadata(
|
|
453
|
+
{
|
|
454
|
+
type: EventType.RUN_FINISHED,
|
|
455
|
+
threadId: lastThreadId ?? fallbackIds?.threadId ?? '',
|
|
456
|
+
runId: lastRunId ?? fallbackIds?.runId ?? '',
|
|
457
|
+
timestamp: Date.now(),
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
finishReason: 'stop',
|
|
461
|
+
...(lastModel !== undefined ? { model: lastModel } : {}),
|
|
462
|
+
},
|
|
463
|
+
) as StreamChunk,
|
|
441
464
|
}
|
|
442
|
-
yield { chunk: synthetic }
|
|
443
465
|
return
|
|
444
466
|
}
|
|
445
|
-
const chunk = JSON.parse(data) as StreamChunk
|
|
467
|
+
const chunk = restoreInboundUsage(JSON.parse(data) as StreamChunk)
|
|
446
468
|
if ('threadId' in chunk && typeof chunk.threadId === 'string') {
|
|
447
469
|
lastThreadId = chunk.threadId
|
|
448
470
|
}
|
|
449
471
|
if ('runId' in chunk && typeof chunk.runId === 'string') {
|
|
450
472
|
lastRunId = chunk.runId
|
|
451
473
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}
|
|
474
|
+
const model = sseChunkModel(chunk)
|
|
475
|
+
if (model !== undefined) lastModel = model
|
|
455
476
|
const id = pendingId
|
|
456
477
|
pendingId = undefined
|
|
457
478
|
yield { chunk, ...(id !== undefined ? { id } : {}) }
|
|
@@ -470,9 +491,9 @@ async function* linesToNdjsonEvents(
|
|
|
470
491
|
for await (const line of lines) {
|
|
471
492
|
const parsed = JSON.parse(line) as unknown
|
|
472
493
|
if (isNdjsonEnvelope(parsed)) {
|
|
473
|
-
yield { chunk: parsed.chunk, id: parsed.id }
|
|
494
|
+
yield { chunk: restoreInboundUsage(parsed.chunk), id: parsed.id }
|
|
474
495
|
} else {
|
|
475
|
-
yield { chunk: parsed as StreamChunk }
|
|
496
|
+
yield { chunk: restoreInboundUsage(parsed as StreamChunk) }
|
|
476
497
|
}
|
|
477
498
|
}
|
|
478
499
|
}
|
|
@@ -1064,21 +1085,24 @@ export function normalizeConnectionAdapter(
|
|
|
1064
1085
|
// observed, but stamp the caller's request runId so getChunkRunId()
|
|
1065
1086
|
// correlates to activeRunIds / currentRunId (same as real stream chunks).
|
|
1066
1087
|
if (!abortSignal?.aborted && !hasTerminalEvent) {
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1088
|
+
push(
|
|
1089
|
+
withTanstackMetadata(
|
|
1090
|
+
{
|
|
1091
|
+
type: EventType.RUN_FINISHED,
|
|
1092
|
+
threadId: requireSyntheticId(
|
|
1093
|
+
upstreamThreadId ?? runContext?.threadId,
|
|
1094
|
+
'threadId',
|
|
1095
|
+
),
|
|
1096
|
+
runId: requireSyntheticId(
|
|
1097
|
+
upstreamRunId ?? runContext?.runId,
|
|
1098
|
+
'runId',
|
|
1099
|
+
),
|
|
1100
|
+
timestamp: Date.now(),
|
|
1101
|
+
},
|
|
1102
|
+
{ finishReason: 'stop', model: 'connect-wrapper' },
|
|
1103
|
+
) as StreamChunk,
|
|
1104
|
+
runContext?.runId,
|
|
1105
|
+
)
|
|
1082
1106
|
}
|
|
1083
1107
|
} catch (err) {
|
|
1084
1108
|
if (!abortSignal?.aborted && !hasTerminalEvent) {
|
|
@@ -2143,9 +2167,9 @@ export function webSocket(
|
|
|
2143
2167
|
}
|
|
2144
2168
|
if (isPingFrame(parsed)) return
|
|
2145
2169
|
const envelopeId = isNdjsonEnvelope(parsed) ? parsed.id : undefined
|
|
2146
|
-
const chunk =
|
|
2147
|
-
? parsed.chunk
|
|
2148
|
-
|
|
2170
|
+
const chunk = restoreInboundUsage(
|
|
2171
|
+
isNdjsonEnvelope(parsed) ? parsed.chunk : (parsed as StreamChunk),
|
|
2172
|
+
)
|
|
2149
2173
|
|
|
2150
2174
|
// Thread durable chunks through the active run session's tracker (if
|
|
2151
2175
|
// any) so a later reconnect knows the last offset and can skip a
|
|
@@ -2325,7 +2349,9 @@ export function webSocket(
|
|
|
2325
2349
|
}
|
|
2326
2350
|
if (isPingFrame(parsed)) return
|
|
2327
2351
|
pipe.push(
|
|
2328
|
-
|
|
2352
|
+
restoreInboundUsage(
|
|
2353
|
+
isNdjsonEnvelope(parsed) ? parsed.chunk : (parsed as StreamChunk),
|
|
2354
|
+
),
|
|
2329
2355
|
)
|
|
2330
2356
|
}
|
|
2331
2357
|
ws.onclose = (event?: CloseEvent) => {
|
package/src/generation-client.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from './generation-types'
|
|
11
11
|
import { createNoOpGenerationDevtoolsBridge } from './devtools-noop'
|
|
12
12
|
import { parseSSEResponse } from './sse-parser'
|
|
13
|
+
import { restoreInboundChunk } from '@tanstack/ai/client'
|
|
13
14
|
import type { StreamChunk } from '@tanstack/ai/client'
|
|
14
15
|
import type {
|
|
15
16
|
ConnectConnectionAdapter,
|
|
@@ -349,9 +350,10 @@ export class GenerationClient<
|
|
|
349
350
|
let streamRunId: string | undefined
|
|
350
351
|
let sawTerminalChunk = false
|
|
351
352
|
|
|
352
|
-
for await (const
|
|
353
|
+
for await (const raw of source) {
|
|
353
354
|
if (signal.aborted) break
|
|
354
355
|
|
|
356
|
+
const chunk = restoreInboundChunk(raw)
|
|
355
357
|
this.callbacksRef.onChunk?.(chunk)
|
|
356
358
|
this.observeResumeSnapshot(chunk)
|
|
357
359
|
const chunkRunId =
|
|
@@ -390,11 +392,9 @@ export class GenerationClient<
|
|
|
390
392
|
this.devtoolsBridge.ensureRunStarted(
|
|
391
393
|
chunkRunId ?? streamRunId ?? fallbackRunId,
|
|
392
394
|
)
|
|
393
|
-
//
|
|
395
|
+
// Spec RUN_ERROR message. Missing message uses this fallback.
|
|
394
396
|
const msg =
|
|
395
|
-
(chunk.message as string | undefined) ||
|
|
396
|
-
chunk.error?.message ||
|
|
397
|
-
'An error occurred'
|
|
397
|
+
(chunk.message as string | undefined) || 'An error occurred'
|
|
398
398
|
throw new Error(msg)
|
|
399
399
|
}
|
|
400
400
|
default:
|
package/src/generation-types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tanstackMetadata } from '@tanstack/ai/client'
|
|
1
2
|
import type {
|
|
2
3
|
MediaPrompt,
|
|
3
4
|
PersistedArtifactRef,
|
|
@@ -461,8 +462,13 @@ export function updateGenerationResumeSnapshot(
|
|
|
461
462
|
previous: GenerationResumeSnapshot | null | undefined,
|
|
462
463
|
chunk: StreamChunk,
|
|
463
464
|
): GenerationResumeSnapshot {
|
|
464
|
-
const
|
|
465
|
-
const
|
|
465
|
+
const tanstack = tanstackMetadata(chunk)
|
|
466
|
+
const threadId =
|
|
467
|
+
stringField(chunk, 'threadId') ??
|
|
468
|
+
(typeof tanstack?.threadId === 'string' ? tanstack.threadId : undefined)
|
|
469
|
+
const runId =
|
|
470
|
+
stringField(chunk, 'runId') ??
|
|
471
|
+
(typeof tanstack?.runId === 'string' ? tanstack.runId : undefined)
|
|
466
472
|
const carried = chunk.type === 'RUN_STARTED' ? undefined : previous
|
|
467
473
|
const previousArtifacts = carried?.pendingArtifacts ?? []
|
|
468
474
|
const next: GenerationResumeSnapshot = {
|
package/src/types.ts
CHANGED
|
@@ -397,6 +397,18 @@ export interface MultimodalContent {
|
|
|
397
397
|
* If not provided, a unique ID will be generated.
|
|
398
398
|
*/
|
|
399
399
|
id?: string
|
|
400
|
+
/**
|
|
401
|
+
* Optional AG-UI metadata bag copied onto the resulting UIMessage.
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* ```ts
|
|
405
|
+
* await client.sendMessage({
|
|
406
|
+
* content: 'Show me failed logins',
|
|
407
|
+
* metadata: { author: { id: 'user-42', name: 'Dana' } },
|
|
408
|
+
* })
|
|
409
|
+
* ```
|
|
410
|
+
*/
|
|
411
|
+
metadata?: Record<string, any>
|
|
400
412
|
}
|
|
401
413
|
|
|
402
414
|
/**
|
|
@@ -616,6 +628,11 @@ export interface UIMessage<
|
|
|
616
628
|
role: 'system' | 'user' | 'assistant'
|
|
617
629
|
parts: Array<MessagePart<TTools, TData>>
|
|
618
630
|
createdAt?: Date
|
|
631
|
+
/**
|
|
632
|
+
* Optional AG-UI metadata bag. TanStack writes the `tanstack` key.
|
|
633
|
+
* User keys stay at the top.
|
|
634
|
+
*/
|
|
635
|
+
metadata?: Record<string, any>
|
|
619
636
|
}
|
|
620
637
|
|
|
621
638
|
/**
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from './generation-types'
|
|
11
11
|
import { createNoOpVideoDevtoolsBridge } from './devtools-noop'
|
|
12
12
|
import { parseSSEResponse } from './sse-parser'
|
|
13
|
+
import { restoreInboundChunk } from '@tanstack/ai/client'
|
|
13
14
|
import type { StreamChunk } from '@tanstack/ai/client'
|
|
14
15
|
import type {
|
|
15
16
|
ConnectConnectionAdapter,
|
|
@@ -343,9 +344,10 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
343
344
|
let streamRunId: string | undefined
|
|
344
345
|
let sawTerminalChunk = false
|
|
345
346
|
|
|
346
|
-
for await (const
|
|
347
|
+
for await (const raw of source) {
|
|
347
348
|
if (signal.aborted) break
|
|
348
349
|
|
|
350
|
+
const chunk = restoreInboundChunk(raw)
|
|
349
351
|
this.callbacksRef.onChunk?.(chunk)
|
|
350
352
|
this.observeResumeSnapshot(chunk)
|
|
351
353
|
const chunkRunId =
|
|
@@ -395,11 +397,9 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
395
397
|
this.devtoolsBridge.ensureRunStarted(
|
|
396
398
|
chunkRunId ?? streamRunId ?? fallbackRunId,
|
|
397
399
|
)
|
|
398
|
-
//
|
|
400
|
+
// Spec RUN_ERROR message. Missing message uses this fallback.
|
|
399
401
|
const msg =
|
|
400
|
-
(chunk.message as string | undefined) ||
|
|
401
|
-
chunk.error?.message ||
|
|
402
|
-
'An error occurred'
|
|
402
|
+
(chunk.message as string | undefined) || 'An error occurred'
|
|
403
403
|
throw new Error(msg)
|
|
404
404
|
}
|
|
405
405
|
default:
|