@tanstack/ai 0.57.0 → 0.58.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.js +8 -6
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +4 -3
- package/dist/esm/activities/chat/middleware/types.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +2 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +2 -2
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/stream-to-response.js +12 -6
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/strip-to-spec-middleware.js +2 -1
- package/dist/esm/strip-to-spec-middleware.js.map +1 -1
- package/dist/esm/types.d.ts +18 -1
- package/dist/esm/utilities/durability-batch.d.ts +8 -0
- package/dist/esm/utilities/durability-batch.js +45 -0
- package/dist/esm/utilities/durability-batch.js.map +1 -0
- package/package.json +3 -3
- package/src/activities/chat/index.ts +11 -5
- package/src/activities/chat/middleware/types.ts +8 -2
- package/src/activities/chat/tools/tool-calls.ts +15 -5
- package/src/stream-to-response.ts +16 -6
- package/src/strip-to-spec-middleware.ts +2 -1
- package/src/types.ts +23 -1
- package/src/utilities/durability-batch.ts +48 -0
|
@@ -9,6 +9,10 @@ import { notifyRunDisconnected } from './delivery-disconnect'
|
|
|
9
9
|
import { resolveResumeRunId } from './stream-durability'
|
|
10
10
|
import { EventType } from './types'
|
|
11
11
|
import { toWireChunk } from './strip-to-spec-middleware'
|
|
12
|
+
import {
|
|
13
|
+
isDurabilityBatchedCustom,
|
|
14
|
+
stripDurabilityBatchHint,
|
|
15
|
+
} from './utilities/durability-batch'
|
|
12
16
|
import { resolveDebugOption } from './logger/resolve'
|
|
13
17
|
import { runErrorEventToError } from './utilities/errors'
|
|
14
18
|
import type { LockStore } from './activities/chat/middleware/locks'
|
|
@@ -319,23 +323,29 @@ function resolveBatchSize(batch: number | undefined): number {
|
|
|
319
323
|
|
|
320
324
|
/**
|
|
321
325
|
* Boundaries at which the batching producer flushes early, regardless of the
|
|
322
|
-
* batch size
|
|
323
|
-
*
|
|
324
|
-
* meaningful points.
|
|
326
|
+
* batch size: run-start, terminals, tool-call ends, and CUSTOM events that
|
|
327
|
+
* are not high-volume adapter output.
|
|
325
328
|
*
|
|
326
329
|
* `RUN_STARTED` matters especially for one-shot activities (image, speech,
|
|
327
330
|
* transcription, summarize): they emit `RUN_STARTED`, then await the provider
|
|
328
331
|
* for seconds, then a terminal. Without flushing `RUN_STARTED` the log stays
|
|
329
332
|
* empty for the whole run, so a mount-time `joinRun` finds nothing and its
|
|
330
|
-
* empty-log deadline fast-fails as "run gone"
|
|
333
|
+
* empty-log deadline fast-fails as "run gone" even though the run is alive.
|
|
331
334
|
* Flushing it immediately makes the run resumable from the instant it starts.
|
|
335
|
+
*
|
|
336
|
+
* CUSTOM progress events (compaction, tool progress, middleware) flush at
|
|
337
|
+
* emit time so a live indicator can render. `process.stdout`,
|
|
338
|
+
* `process.stderr`, `sandbox.file`, and `sandbox.file.diff` stay batched.
|
|
339
|
+
* `emitCustomEvent(name, value, { batch: true })` opts a single event into
|
|
340
|
+
* that same batch.
|
|
332
341
|
*/
|
|
333
342
|
function isDurabilityFlushBoundary(chunk: StreamChunk): boolean {
|
|
334
343
|
return (
|
|
335
344
|
chunk.type === 'RUN_STARTED' ||
|
|
336
345
|
chunk.type === 'RUN_FINISHED' ||
|
|
337
346
|
chunk.type === 'RUN_ERROR' ||
|
|
338
|
-
chunk.type === 'TOOL_CALL_END'
|
|
347
|
+
chunk.type === 'TOOL_CALL_END' ||
|
|
348
|
+
(chunk.type === 'CUSTOM' && !isDurabilityBatchedCustom(chunk))
|
|
339
349
|
)
|
|
340
350
|
}
|
|
341
351
|
|
|
@@ -446,7 +456,7 @@ export function durableStreamSource<TOffset extends string>(
|
|
|
446
456
|
|
|
447
457
|
async function* flush(): AsyncIterable<StreamChunk> {
|
|
448
458
|
if (batch.length === 0) return
|
|
449
|
-
const toForward = batch
|
|
459
|
+
const toForward = batch.map(stripDurabilityBatchHint)
|
|
450
460
|
batch = []
|
|
451
461
|
// Tag each chunk with the exact backend offset. Requiring one opaque
|
|
452
462
|
// token per chunk preserves exact-once resume at any batch size.
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
tanstackMetadata,
|
|
7
7
|
withTanstackMetadata,
|
|
8
8
|
} from './utilities/merge-metadata'
|
|
9
|
+
import { stripDurabilityBatchHint } from './utilities/durability-batch'
|
|
9
10
|
import { normalizeStreamChunk } from './utilities/normalize-stream-chunk'
|
|
10
11
|
import { isSpecTopLevelKey } from './utilities/spec-event-keys'
|
|
11
12
|
|
|
@@ -54,5 +55,5 @@ export function toWireChunk(
|
|
|
54
55
|
chunk: StreamChunk | AdapterYieldChunk,
|
|
55
56
|
): StreamChunk {
|
|
56
57
|
const [normalized] = normalizeStreamChunk(chunk)
|
|
57
|
-
return stripToSpec(normalized ?? chunk)
|
|
58
|
+
return stripDurabilityBatchHint(stripToSpec(normalized ?? chunk))
|
|
58
59
|
}
|
package/src/types.ts
CHANGED
|
@@ -629,6 +629,22 @@ type RuntimeContextField<TContext> =
|
|
|
629
629
|
context: TContext
|
|
630
630
|
}
|
|
631
631
|
|
|
632
|
+
/**
|
|
633
|
+
* Options for a single `emitCustomEvent` call, on both the tool-execution and
|
|
634
|
+
* middleware contexts.
|
|
635
|
+
*/
|
|
636
|
+
export interface EmitCustomEventOptions {
|
|
637
|
+
/**
|
|
638
|
+
* Keep this event in the durability batch with later chunks.
|
|
639
|
+
* CUSTOM events flush as soon as they are emitted, so a progress
|
|
640
|
+
* indicator can render at emit time. Pass `{ batch: true }` for a
|
|
641
|
+
* high-volume stream that should share appends with later output.
|
|
642
|
+
* `process.stdout`, `process.stderr`, `sandbox.file`, and
|
|
643
|
+
* `sandbox.file.diff` already batch.
|
|
644
|
+
*/
|
|
645
|
+
batch?: boolean
|
|
646
|
+
}
|
|
647
|
+
|
|
632
648
|
/**
|
|
633
649
|
* Context passed to tool execute functions, providing capabilities like
|
|
634
650
|
* emitting custom events during execution.
|
|
@@ -649,6 +665,8 @@ export type ToolExecutionContext<TContext = unknown> =
|
|
|
649
665
|
*
|
|
650
666
|
* @param eventName - Name of the custom event
|
|
651
667
|
* @param value - Event payload value
|
|
668
|
+
* @param options - Pass `{ batch: true }` to keep this event in the
|
|
669
|
+
* durability batch instead of flushing it immediately
|
|
652
670
|
*
|
|
653
671
|
* @example
|
|
654
672
|
* ```ts
|
|
@@ -661,7 +679,11 @@ export type ToolExecutionContext<TContext = unknown> =
|
|
|
661
679
|
* })
|
|
662
680
|
* ```
|
|
663
681
|
*/
|
|
664
|
-
emitCustomEvent: (
|
|
682
|
+
emitCustomEvent: (
|
|
683
|
+
eventName: string,
|
|
684
|
+
value: Record<string, any>,
|
|
685
|
+
options?: EmitCustomEventOptions,
|
|
686
|
+
) => void
|
|
665
687
|
}
|
|
666
688
|
|
|
667
689
|
export type ToolExecuteFunction<
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CUSTOM_EVENT } from '../custom-events'
|
|
2
|
+
import type { CustomEvent, StreamChunk } from '../types'
|
|
3
|
+
import { tanstackMetadata, withTanstackMetadata } from './merge-metadata'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* High-volume CUSTOM names that stay in the durability batch.
|
|
7
|
+
* Everything else flushes as soon as it is emitted.
|
|
8
|
+
*/
|
|
9
|
+
const BATCHED_CUSTOM_EVENT_NAMES = new Set<string>([
|
|
10
|
+
CUSTOM_EVENT.PROCESS_STDOUT,
|
|
11
|
+
CUSTOM_EVENT.PROCESS_STDERR,
|
|
12
|
+
'sandbox.file',
|
|
13
|
+
'sandbox.file.diff',
|
|
14
|
+
])
|
|
15
|
+
|
|
16
|
+
function hasBatchHint(chunk: StreamChunk): boolean {
|
|
17
|
+
const tanstack = tanstackMetadata(chunk)
|
|
18
|
+
if (tanstack == null) return false
|
|
19
|
+
return Reflect.get(tanstack, 'batch') === true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Mark a CUSTOM chunk so the durability producer keeps it in the batch. */
|
|
23
|
+
export function withDurabilityBatchHint(chunk: CustomEvent): CustomEvent {
|
|
24
|
+
return withTanstackMetadata(chunk, { batch: true })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function isDurabilityBatchedCustom(chunk: StreamChunk): boolean {
|
|
28
|
+
if (chunk.type !== 'CUSTOM') return false
|
|
29
|
+
if (BATCHED_CUSTOM_EVENT_NAMES.has(chunk.name)) return true
|
|
30
|
+
return hasBatchHint(chunk)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Drop the in-process batch hint so it does not sit in the log or on the wire.
|
|
35
|
+
*/
|
|
36
|
+
export function stripDurabilityBatchHint(chunk: StreamChunk): StreamChunk {
|
|
37
|
+
const tanstack = tanstackMetadata(chunk)
|
|
38
|
+
if (tanstack == null || Reflect.get(tanstack, 'batch') !== true) return chunk
|
|
39
|
+
Reflect.deleteProperty(tanstack, 'batch')
|
|
40
|
+
const metadata = chunk.metadata
|
|
41
|
+
if (metadata != null && Object.keys(tanstack).length === 0) {
|
|
42
|
+
Reflect.deleteProperty(metadata, 'tanstack')
|
|
43
|
+
if (Object.keys(metadata).length === 0) {
|
|
44
|
+
Reflect.deleteProperty(chunk, 'metadata')
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return chunk
|
|
48
|
+
}
|