nexus-ai-pro 2.0.0 → 2.1.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/API_STABILITY.md +27 -0
- package/CHANGELOG.md +123 -1
- package/MIGRATING.md +1 -1
- package/README.md +8 -4
- package/dist/agent/create-agent.d.ts +9 -1
- package/dist/agent/create-agent.js +40 -1
- package/dist/cli/graph.d.ts +9 -0
- package/dist/cli/graph.js +47 -0
- package/dist/cli.js +9 -1
- package/dist/core/nexus.d.ts +6 -2
- package/dist/core/nexus.js +3 -1
- package/dist/evals/judge.d.ts +11 -0
- package/dist/evals/judge.js +33 -0
- package/dist/evaluate/index.d.ts +1 -0
- package/dist/evaluate/index.js +1 -0
- package/dist/evaluate/provenance.d.ts +22 -0
- package/dist/evaluate/provenance.js +10 -0
- package/dist/evaluate/run.js +20 -4
- package/dist/graph/durability.d.ts +27 -0
- package/dist/graph/durability.js +66 -0
- package/dist/graph/errors.d.ts +30 -4
- package/dist/graph/errors.js +18 -2
- package/dist/graph/event-stream.d.ts +75 -0
- package/dist/graph/event-stream.js +289 -0
- package/dist/graph/graph.d.ts +41 -3
- package/dist/graph/graph.js +270 -40
- package/dist/graph/index.d.ts +4 -2
- package/dist/graph/index.js +3 -1
- package/dist/graph/lint.d.ts +57 -0
- package/dist/graph/lint.js +141 -0
- package/dist/graph/run-control.d.ts +28 -0
- package/dist/graph/run-control.js +26 -0
- package/dist/operations/runner.js +13 -15
- package/dist/ops/health.d.ts +24 -0
- package/dist/ops/health.js +38 -10
- package/dist/ops/index.d.ts +1 -1
- package/dist/ops/index.js +1 -1
- package/dist/ops/rate-limit-adapters.d.ts +23 -0
- package/dist/ops/rate-limit-adapters.js +34 -0
- package/dist/ops/rate-limiter.d.ts +12 -2
- package/dist/ops/rate-limiter.js +29 -0
- package/dist/server/assistant.d.ts +40 -0
- package/dist/server/assistant.js +31 -0
- package/dist/server/runs.d.ts +4 -0
- package/dist/server/runs.js +21 -1
- package/dist/testing/record.d.ts +42 -0
- package/dist/testing/record.js +57 -5
- package/dist/tracing/index.d.ts +3 -3
- package/dist/tracing/index.js +2 -2
- package/dist/tracing/instrument.js +28 -0
- package/dist/tracing/otlp.d.ts +91 -0
- package/dist/tracing/otlp.js +124 -0
- package/dist/tracing/rollups.d.ts +100 -0
- package/dist/tracing/rollups.js +139 -0
- package/dist/tracing/stores.d.ts +17 -0
- package/dist/tracing/stores.js +23 -0
- package/dist/tracing/tracer.d.ts +50 -4
- package/dist/tracing/tracer.js +85 -6
- package/dist/types/config.d.ts +9 -0
- package/dist/types/evaluate.d.ts +37 -0
- package/dist/types/graph.d.ts +343 -8
- package/dist/types/graph.js +3 -1
- package/dist/types/operations.d.ts +16 -2
- package/dist/types/server.d.ts +9 -0
- package/dist/types/tracing.d.ts +11 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.js +1 -0
- package/dist-cjs/agent/create-agent.d.ts +9 -1
- package/dist-cjs/agent/create-agent.js +40 -1
- package/dist-cjs/cli/graph.d.ts +9 -0
- package/dist-cjs/cli/graph.js +50 -0
- package/dist-cjs/core/nexus.d.ts +6 -2
- package/dist-cjs/core/nexus.js +3 -1
- package/dist-cjs/evals/judge.d.ts +11 -0
- package/dist-cjs/evals/judge.js +33 -0
- package/dist-cjs/evaluate/index.d.ts +1 -0
- package/dist-cjs/evaluate/index.js +3 -1
- package/dist-cjs/evaluate/provenance.d.ts +22 -0
- package/dist-cjs/evaluate/provenance.js +14 -0
- package/dist-cjs/evaluate/run.js +20 -4
- package/dist-cjs/graph/durability.d.ts +27 -0
- package/dist-cjs/graph/durability.js +70 -0
- package/dist-cjs/graph/errors.d.ts +30 -4
- package/dist-cjs/graph/errors.js +20 -3
- package/dist-cjs/graph/event-stream.d.ts +75 -0
- package/dist-cjs/graph/event-stream.js +295 -0
- package/dist-cjs/graph/graph.d.ts +41 -3
- package/dist-cjs/graph/graph.js +269 -39
- package/dist-cjs/graph/index.d.ts +4 -2
- package/dist-cjs/graph/index.js +7 -1
- package/dist-cjs/graph/lint.d.ts +57 -0
- package/dist-cjs/graph/lint.js +144 -0
- package/dist-cjs/graph/run-control.d.ts +28 -0
- package/dist-cjs/graph/run-control.js +30 -0
- package/dist-cjs/operations/runner.js +13 -15
- package/dist-cjs/ops/health.d.ts +24 -0
- package/dist-cjs/ops/health.js +38 -10
- package/dist-cjs/ops/index.d.ts +1 -1
- package/dist-cjs/ops/rate-limit-adapters.d.ts +23 -0
- package/dist-cjs/ops/rate-limit-adapters.js +35 -1
- package/dist-cjs/ops/rate-limiter.d.ts +12 -2
- package/dist-cjs/ops/rate-limiter.js +30 -0
- package/dist-cjs/server/assistant.d.ts +40 -0
- package/dist-cjs/server/assistant.js +31 -0
- package/dist-cjs/server/runs.d.ts +4 -0
- package/dist-cjs/server/runs.js +21 -1
- package/dist-cjs/testing/record.d.ts +42 -0
- package/dist-cjs/testing/record.js +57 -5
- package/dist-cjs/tracing/index.d.ts +3 -3
- package/dist-cjs/tracing/index.js +4 -1
- package/dist-cjs/tracing/instrument.js +28 -0
- package/dist-cjs/tracing/otlp.d.ts +91 -0
- package/dist-cjs/tracing/otlp.js +129 -0
- package/dist-cjs/tracing/rollups.d.ts +100 -0
- package/dist-cjs/tracing/rollups.js +147 -0
- package/dist-cjs/tracing/stores.d.ts +17 -0
- package/dist-cjs/tracing/stores.js +24 -0
- package/dist-cjs/tracing/tracer.d.ts +50 -4
- package/dist-cjs/tracing/tracer.js +87 -6
- package/dist-cjs/types/config.d.ts +9 -0
- package/dist-cjs/types/evaluate.d.ts +37 -0
- package/dist-cjs/types/graph.d.ts +343 -8
- package/dist-cjs/types/graph.js +3 -1
- package/dist-cjs/types/operations.d.ts +16 -2
- package/dist-cjs/types/server.d.ts +9 -0
- package/dist-cjs/types/tracing.d.ts +11 -0
- package/dist-cjs/version.d.ts +5 -0
- package/dist-cjs/version.js +4 -0
- package/llms.txt +2 -2
- package/package.json +40 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const DEFAULT_MAX_PENDING = 8;
|
|
2
|
+
export class CheckpointWriter {
|
|
3
|
+
write;
|
|
4
|
+
mode;
|
|
5
|
+
maxPending;
|
|
6
|
+
chain = Promise.resolve();
|
|
7
|
+
inFlight = [];
|
|
8
|
+
failure;
|
|
9
|
+
unwritten;
|
|
10
|
+
constructor(write, mode, maxPending = DEFAULT_MAX_PENDING) {
|
|
11
|
+
this.write = write;
|
|
12
|
+
this.mode = mode;
|
|
13
|
+
this.maxPending = maxPending;
|
|
14
|
+
}
|
|
15
|
+
async persist(checkpoint, durable = false) {
|
|
16
|
+
this.throwFailure();
|
|
17
|
+
if (this.mode === 'sync') {
|
|
18
|
+
await this.write(checkpoint);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (this.mode === 'exit' && !durable) {
|
|
22
|
+
this.unwritten = checkpoint;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (this.mode === 'exit') {
|
|
26
|
+
this.unwritten = undefined;
|
|
27
|
+
await this.write(checkpoint);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const link = this.chain
|
|
31
|
+
.then(() => this.write(checkpoint))
|
|
32
|
+
.catch((error) => {
|
|
33
|
+
this.failure ??= { error };
|
|
34
|
+
})
|
|
35
|
+
.finally(() => {
|
|
36
|
+
const index = this.inFlight.indexOf(link);
|
|
37
|
+
if (index >= 0)
|
|
38
|
+
this.inFlight.splice(index, 1);
|
|
39
|
+
});
|
|
40
|
+
this.chain = link;
|
|
41
|
+
this.inFlight.push(link);
|
|
42
|
+
if (durable) {
|
|
43
|
+
await this.flush();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
while (this.inFlight.length > Math.max(1, this.maxPending))
|
|
47
|
+
await this.inFlight[0];
|
|
48
|
+
this.throwFailure();
|
|
49
|
+
}
|
|
50
|
+
async flush() {
|
|
51
|
+
await this.chain;
|
|
52
|
+
if (this.unwritten !== undefined) {
|
|
53
|
+
const latest = this.unwritten;
|
|
54
|
+
this.unwritten = undefined;
|
|
55
|
+
await this.write(latest);
|
|
56
|
+
}
|
|
57
|
+
this.throwFailure();
|
|
58
|
+
}
|
|
59
|
+
throwFailure() {
|
|
60
|
+
if (!this.failure)
|
|
61
|
+
return;
|
|
62
|
+
const { error } = this.failure;
|
|
63
|
+
this.failure = undefined;
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
package/dist/graph/errors.d.ts
CHANGED
|
@@ -98,7 +98,8 @@ export declare class GraphNotInterruptedError extends GraphError {
|
|
|
98
98
|
status: string);
|
|
99
99
|
}
|
|
100
100
|
/**
|
|
101
|
-
* Raised when a node outlives its
|
|
101
|
+
* Raised when a node outlives its run limit, or goes longer than its idle limit without showing
|
|
102
|
+
* progress.
|
|
102
103
|
*
|
|
103
104
|
* Separate from an ordinary failure because it is usually worth retrying, and because the node's
|
|
104
105
|
* signal was aborted underneath it rather than the node choosing to stop.
|
|
@@ -106,13 +107,38 @@ export declare class GraphNotInterruptedError extends GraphError {
|
|
|
106
107
|
export declare class GraphNodeTimeoutError extends GraphError {
|
|
107
108
|
/** The node. */
|
|
108
109
|
readonly node: string;
|
|
109
|
-
/** The
|
|
110
|
+
/** The limit it exceeded, in milliseconds. */
|
|
110
111
|
readonly timeoutMs: number;
|
|
112
|
+
/** `run` for the whole-attempt limit, `idle` for the limit between signs of progress. */
|
|
113
|
+
readonly kind: 'run' | 'idle';
|
|
111
114
|
constructor(
|
|
112
115
|
/** The node. */
|
|
113
116
|
node: string,
|
|
114
|
-
/** The
|
|
115
|
-
timeoutMs: number
|
|
117
|
+
/** The limit it exceeded, in milliseconds. */
|
|
118
|
+
timeoutMs: number,
|
|
119
|
+
/** `run` for the whole-attempt limit, `idle` for the limit between signs of progress. */
|
|
120
|
+
kind?: 'run' | 'idle');
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Raised when a run stops because its `RunControl` was drained.
|
|
124
|
+
*
|
|
125
|
+
* Not a failure: the superstep in flight finished and its checkpoint was written, so the thread is
|
|
126
|
+
* intact. Call `continue(threadId)` to resume it, on this worker or another.
|
|
127
|
+
*/
|
|
128
|
+
export declare class GraphDrainedError extends GraphError {
|
|
129
|
+
/** The thread to continue. */
|
|
130
|
+
readonly threadId: string;
|
|
131
|
+
/** Supersteps completed before the drain. */
|
|
132
|
+
readonly step: number;
|
|
133
|
+
/** Why the drain was requested. */
|
|
134
|
+
readonly reason?: string | undefined;
|
|
135
|
+
constructor(
|
|
136
|
+
/** The thread to continue. */
|
|
137
|
+
threadId: string,
|
|
138
|
+
/** Supersteps completed before the drain. */
|
|
139
|
+
step: number,
|
|
140
|
+
/** Why the drain was requested. */
|
|
141
|
+
reason?: string | undefined);
|
|
116
142
|
}
|
|
117
143
|
/**
|
|
118
144
|
* Stable key for one interrupt, so a replayed node picks up the answer it was given.
|
package/dist/graph/errors.js
CHANGED
|
@@ -71,13 +71,29 @@ export class GraphNotInterruptedError extends GraphError {
|
|
|
71
71
|
export class GraphNodeTimeoutError extends GraphError {
|
|
72
72
|
node;
|
|
73
73
|
timeoutMs;
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
kind;
|
|
75
|
+
constructor(node, timeoutMs, kind = 'run') {
|
|
76
|
+
super(kind === 'idle'
|
|
77
|
+
? `Graph node "${node}" showed no progress for ${timeoutMs}ms, its idle timeout`
|
|
78
|
+
: `Graph node "${node}" exceeded its ${timeoutMs}ms timeout`, 'GRAPH_NODE_TIMEOUT');
|
|
76
79
|
this.node = node;
|
|
77
80
|
this.timeoutMs = timeoutMs;
|
|
81
|
+
this.kind = kind;
|
|
78
82
|
this.name = 'GraphNodeTimeoutError';
|
|
79
83
|
}
|
|
80
84
|
}
|
|
85
|
+
export class GraphDrainedError extends GraphError {
|
|
86
|
+
threadId;
|
|
87
|
+
step;
|
|
88
|
+
reason;
|
|
89
|
+
constructor(threadId, step, reason) {
|
|
90
|
+
super(`Graph run on thread "${threadId}" drained after step ${step}${reason ? ` (${reason})` : ''}; continue() resumes it`, 'GRAPH_DRAINED');
|
|
91
|
+
this.threadId = threadId;
|
|
92
|
+
this.step = step;
|
|
93
|
+
this.reason = reason;
|
|
94
|
+
this.name = 'GraphDrainedError';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
81
97
|
export function interruptKey(pending) {
|
|
82
98
|
return `${pending.taskId ?? pending.node}:${pending.step}:${pending.index}`;
|
|
83
99
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ChannelSchema, GraphEvent, GraphResult, GraphStepEvent, GraphStreamEvent, GraphStreamOverflow, GraphStreamProjection, GraphStreamStats } from '../types/graph.js';
|
|
2
|
+
import { GraphError } from './errors.js';
|
|
3
|
+
/** Raised by an event stream whose reader fell behind with `overflow: 'error'`. The run is stopped. */
|
|
4
|
+
export declare class GraphStreamOverflowError extends GraphError {
|
|
5
|
+
/** The buffer that filled. */
|
|
6
|
+
readonly maxBuffered: number;
|
|
7
|
+
constructor(
|
|
8
|
+
/** The buffer that filled. */
|
|
9
|
+
maxBuffered: number);
|
|
10
|
+
}
|
|
11
|
+
/** What starts the run behind a stream: given the event listener and a signal, the step events. */
|
|
12
|
+
export type GraphRunStarter<S extends ChannelSchema> = (onEvent: (event: GraphEvent) => void, signal: AbortSignal) => AsyncIterable<GraphStepEvent<S>>;
|
|
13
|
+
interface StreamSettings {
|
|
14
|
+
include: GraphStreamProjection[];
|
|
15
|
+
subgraphs: boolean;
|
|
16
|
+
maxBuffered: number;
|
|
17
|
+
overflow: GraphStreamOverflow;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A graph run as one stream of typed events, with each projection readable on its own.
|
|
21
|
+
*
|
|
22
|
+
* Iterate the stream for every included event, or take `messages()`, `tools()`, `values()`, or
|
|
23
|
+
* `updates()` for one kind. Every reader gets its own bounded buffer. The run starts when the first
|
|
24
|
+
* reader asks for an event, so create every reader before reading any. It stops when every reader
|
|
25
|
+
* has stopped.
|
|
26
|
+
*/
|
|
27
|
+
export declare class GraphEventStream<S extends ChannelSchema, O extends keyof S = keyof S> implements AsyncIterable<GraphStreamEvent<S>> {
|
|
28
|
+
private readonly start;
|
|
29
|
+
private readonly toResult;
|
|
30
|
+
private readonly settings;
|
|
31
|
+
private readonly readers;
|
|
32
|
+
private readonly controller;
|
|
33
|
+
private started;
|
|
34
|
+
private readonly stats;
|
|
35
|
+
private resolveResult;
|
|
36
|
+
private rejectResult;
|
|
37
|
+
/** Resolves with the run's result once it ends, or rejects with its error. */
|
|
38
|
+
readonly result: Promise<GraphResult<S, O>>;
|
|
39
|
+
constructor(start: GraphRunStarter<S>, toResult: (last: GraphStepEvent<S> | undefined) => GraphResult<S, O>, settings: StreamSettings);
|
|
40
|
+
/** Every included event. */
|
|
41
|
+
[Symbol.asyncIterator](): AsyncIterator<GraphStreamEvent<S>>;
|
|
42
|
+
/** Model output, as nodes stream it. */
|
|
43
|
+
messages(): AsyncIterable<Extract<GraphStreamEvent<S>, {
|
|
44
|
+
type: 'messages';
|
|
45
|
+
}>>;
|
|
46
|
+
/** Tool calls: each starting, progressing, and returning or failing. */
|
|
47
|
+
tools(): AsyncIterable<Extract<GraphStreamEvent<S>, {
|
|
48
|
+
type: 'tools';
|
|
49
|
+
}>>;
|
|
50
|
+
/** The state after each superstep. */
|
|
51
|
+
values(): AsyncIterable<Extract<GraphStreamEvent<S>, {
|
|
52
|
+
type: 'values';
|
|
53
|
+
}>>;
|
|
54
|
+
/** Each task's write. */
|
|
55
|
+
updates(): AsyncIterable<Extract<GraphStreamEvent<S>, {
|
|
56
|
+
type: 'updates';
|
|
57
|
+
}>>;
|
|
58
|
+
/** How the stream kept up with its slowest reader so far. */
|
|
59
|
+
streamStats(): GraphStreamStats;
|
|
60
|
+
/** A reader for one projection, registered now so it sees the run from its first event. */
|
|
61
|
+
private only;
|
|
62
|
+
private reader;
|
|
63
|
+
private begin;
|
|
64
|
+
private publish;
|
|
65
|
+
private fail;
|
|
66
|
+
private pump;
|
|
67
|
+
}
|
|
68
|
+
/** Builds a stream's settings from its options. */
|
|
69
|
+
export declare function streamSettings(options: {
|
|
70
|
+
include?: GraphStreamProjection[];
|
|
71
|
+
subgraphs?: boolean;
|
|
72
|
+
maxBuffered?: number;
|
|
73
|
+
overflow?: GraphStreamOverflow;
|
|
74
|
+
}): StreamSettings;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { GraphError } from './errors.js';
|
|
2
|
+
const DEFAULT_INCLUDE = ['values', 'updates', 'messages', 'tools', 'custom'];
|
|
3
|
+
const DEFAULT_MAX_BUFFERED = 1_000;
|
|
4
|
+
export class GraphStreamOverflowError extends GraphError {
|
|
5
|
+
maxBuffered;
|
|
6
|
+
constructor(maxBuffered) {
|
|
7
|
+
super(`A graph event reader fell more than ${maxBuffered} events behind`, 'GRAPH_STREAM_OVERFLOW');
|
|
8
|
+
this.maxBuffered = maxBuffered;
|
|
9
|
+
this.name = 'GraphStreamOverflowError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
class Reader {
|
|
13
|
+
accepts;
|
|
14
|
+
settings;
|
|
15
|
+
stats;
|
|
16
|
+
onOverflow;
|
|
17
|
+
queue = [];
|
|
18
|
+
waiting;
|
|
19
|
+
failure;
|
|
20
|
+
drained = [];
|
|
21
|
+
ended = false;
|
|
22
|
+
error;
|
|
23
|
+
closed = false;
|
|
24
|
+
constructor(accepts, settings, stats, onOverflow) {
|
|
25
|
+
this.accepts = accepts;
|
|
26
|
+
this.settings = settings;
|
|
27
|
+
this.stats = stats;
|
|
28
|
+
this.onOverflow = onOverflow;
|
|
29
|
+
}
|
|
30
|
+
push(event) {
|
|
31
|
+
if (this.closed || !this.accepts(event))
|
|
32
|
+
return;
|
|
33
|
+
if (this.waiting) {
|
|
34
|
+
const resolve = this.waiting;
|
|
35
|
+
this.waiting = undefined;
|
|
36
|
+
this.failure = undefined;
|
|
37
|
+
resolve({ value: event, done: false });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (this.queue.length >= this.settings.maxBuffered && !this.makeRoom(event))
|
|
41
|
+
return;
|
|
42
|
+
this.queue.push(event);
|
|
43
|
+
this.stats.peak = Math.max(this.stats.peak, this.queue.length);
|
|
44
|
+
}
|
|
45
|
+
makeRoom(event) {
|
|
46
|
+
switch (this.settings.overflow) {
|
|
47
|
+
case 'drop-newest':
|
|
48
|
+
this.stats.dropped += 1;
|
|
49
|
+
return false;
|
|
50
|
+
case 'drop-oldest':
|
|
51
|
+
this.queue.shift();
|
|
52
|
+
this.stats.dropped += 1;
|
|
53
|
+
return true;
|
|
54
|
+
case 'error':
|
|
55
|
+
this.onOverflow(new GraphStreamOverflowError(this.settings.maxBuffered));
|
|
56
|
+
return false;
|
|
57
|
+
default: {
|
|
58
|
+
if (this.coalesce(event)) {
|
|
59
|
+
this.stats.coalesced += 1;
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
this.queue.shift();
|
|
63
|
+
this.stats.dropped += 1;
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
coalesce(event) {
|
|
69
|
+
for (let index = this.queue.length - 1; index >= 0; index -= 1) {
|
|
70
|
+
const queued = this.queue[index];
|
|
71
|
+
if (event.type === 'messages' && queued.type === 'messages') {
|
|
72
|
+
const sameMessage = queued.taskId === event.taskId &&
|
|
73
|
+
(queued.chunk.kind ?? 'text') === (event.chunk.kind ?? 'text') &&
|
|
74
|
+
queued.chunk.messageId === event.chunk.messageId &&
|
|
75
|
+
queued.namespace.join('/') === event.namespace.join('/');
|
|
76
|
+
if (!sameMessage)
|
|
77
|
+
continue;
|
|
78
|
+
this.queue[index] = {
|
|
79
|
+
...queued,
|
|
80
|
+
chunk: { ...queued.chunk, content: queued.chunk.content + event.chunk.content },
|
|
81
|
+
};
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
if (event.type === 'values' &&
|
|
85
|
+
queued.type === 'values' &&
|
|
86
|
+
queued.namespace.join('/') === event.namespace.join('/')) {
|
|
87
|
+
this.queue[index] = event;
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
whenDrained() {
|
|
94
|
+
if (this.closed || this.queue.length <= this.settings.maxBuffered / 2)
|
|
95
|
+
return Promise.resolve();
|
|
96
|
+
return new Promise((resolve) => this.drained.push(resolve));
|
|
97
|
+
}
|
|
98
|
+
next() {
|
|
99
|
+
const event = this.queue.shift();
|
|
100
|
+
if (this.queue.length <= this.settings.maxBuffered / 2)
|
|
101
|
+
this.releaseWaiters();
|
|
102
|
+
if (event)
|
|
103
|
+
return Promise.resolve({ value: event, done: false });
|
|
104
|
+
if (this.error !== undefined)
|
|
105
|
+
return Promise.reject(this.error);
|
|
106
|
+
if (this.ended || this.closed)
|
|
107
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
this.waiting = resolve;
|
|
110
|
+
this.failure = reject;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
end(error) {
|
|
114
|
+
this.ended = true;
|
|
115
|
+
if (error !== undefined)
|
|
116
|
+
this.error = error;
|
|
117
|
+
this.releaseWaiters();
|
|
118
|
+
if (!this.waiting)
|
|
119
|
+
return;
|
|
120
|
+
const resolve = this.waiting;
|
|
121
|
+
const reject = this.failure;
|
|
122
|
+
this.waiting = undefined;
|
|
123
|
+
this.failure = undefined;
|
|
124
|
+
if (error !== undefined)
|
|
125
|
+
reject?.(error);
|
|
126
|
+
else
|
|
127
|
+
resolve({ value: undefined, done: true });
|
|
128
|
+
}
|
|
129
|
+
close() {
|
|
130
|
+
this.closed = true;
|
|
131
|
+
this.queue.length = 0;
|
|
132
|
+
this.end();
|
|
133
|
+
}
|
|
134
|
+
releaseWaiters() {
|
|
135
|
+
const waiters = this.drained;
|
|
136
|
+
this.drained = [];
|
|
137
|
+
for (const resolve of waiters)
|
|
138
|
+
resolve();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export class GraphEventStream {
|
|
142
|
+
start;
|
|
143
|
+
toResult;
|
|
144
|
+
settings;
|
|
145
|
+
readers = [];
|
|
146
|
+
controller = new AbortController();
|
|
147
|
+
started = false;
|
|
148
|
+
stats = { peak: 0, coalesced: 0, dropped: 0 };
|
|
149
|
+
resolveResult;
|
|
150
|
+
rejectResult;
|
|
151
|
+
result;
|
|
152
|
+
constructor(start, toResult, settings) {
|
|
153
|
+
this.start = start;
|
|
154
|
+
this.toResult = toResult;
|
|
155
|
+
this.settings = settings;
|
|
156
|
+
this.result = new Promise((resolve, reject) => {
|
|
157
|
+
this.resolveResult = resolve;
|
|
158
|
+
this.rejectResult = reject;
|
|
159
|
+
});
|
|
160
|
+
this.result.catch(() => undefined);
|
|
161
|
+
}
|
|
162
|
+
[Symbol.asyncIterator]() {
|
|
163
|
+
return this.reader(() => true);
|
|
164
|
+
}
|
|
165
|
+
messages() {
|
|
166
|
+
return this.only('messages');
|
|
167
|
+
}
|
|
168
|
+
tools() {
|
|
169
|
+
return this.only('tools');
|
|
170
|
+
}
|
|
171
|
+
values() {
|
|
172
|
+
return this.only('values');
|
|
173
|
+
}
|
|
174
|
+
updates() {
|
|
175
|
+
return this.only('updates');
|
|
176
|
+
}
|
|
177
|
+
streamStats() {
|
|
178
|
+
return { ...this.stats };
|
|
179
|
+
}
|
|
180
|
+
only(type) {
|
|
181
|
+
const iterator = this.reader((event) => event.type === type);
|
|
182
|
+
return { [Symbol.asyncIterator]: () => iterator };
|
|
183
|
+
}
|
|
184
|
+
reader(accepts) {
|
|
185
|
+
const reader = new Reader(accepts, this.settings, this.stats, (error) => this.fail(error));
|
|
186
|
+
if (this.started)
|
|
187
|
+
reader.end();
|
|
188
|
+
else
|
|
189
|
+
this.readers.push(reader);
|
|
190
|
+
return {
|
|
191
|
+
next: () => {
|
|
192
|
+
this.begin();
|
|
193
|
+
return reader.next();
|
|
194
|
+
},
|
|
195
|
+
return: async () => {
|
|
196
|
+
reader.close();
|
|
197
|
+
if (this.readers.every((item) => item.closed))
|
|
198
|
+
this.controller.abort(new Error('Every reader stopped'));
|
|
199
|
+
return { value: undefined, done: true };
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
begin() {
|
|
204
|
+
if (this.started)
|
|
205
|
+
return;
|
|
206
|
+
this.started = true;
|
|
207
|
+
void this.pump();
|
|
208
|
+
}
|
|
209
|
+
publish(event) {
|
|
210
|
+
if (event.namespace.length > 0 && !this.settings.subgraphs)
|
|
211
|
+
return;
|
|
212
|
+
const projection = event.type;
|
|
213
|
+
if (!this.settings.include.includes(projection))
|
|
214
|
+
return;
|
|
215
|
+
for (const reader of this.readers)
|
|
216
|
+
reader.push(event);
|
|
217
|
+
}
|
|
218
|
+
fail(error) {
|
|
219
|
+
if (this.controller.signal.aborted)
|
|
220
|
+
return;
|
|
221
|
+
this.controller.abort(error);
|
|
222
|
+
for (const reader of this.readers)
|
|
223
|
+
reader.end(error);
|
|
224
|
+
this.rejectResult(error);
|
|
225
|
+
}
|
|
226
|
+
async pump() {
|
|
227
|
+
let last;
|
|
228
|
+
try {
|
|
229
|
+
for await (const step of this.start((event) => {
|
|
230
|
+
for (const item of fromGraphEvent(event))
|
|
231
|
+
this.publish(item);
|
|
232
|
+
}, this.controller.signal)) {
|
|
233
|
+
last = step;
|
|
234
|
+
this.publish({ type: 'values', step: step.step, status: step.status, state: step.state, namespace: [] });
|
|
235
|
+
await Promise.all(this.readers.map((reader) => reader.whenDrained()));
|
|
236
|
+
}
|
|
237
|
+
for (const reader of this.readers)
|
|
238
|
+
reader.end();
|
|
239
|
+
this.resolveResult(this.toResult(last));
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
const reason = this.controller.signal.reason;
|
|
243
|
+
this.fail(reason instanceof GraphStreamOverflowError ? reason : error);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
export function streamSettings(options) {
|
|
248
|
+
return {
|
|
249
|
+
include: options.include ?? DEFAULT_INCLUDE,
|
|
250
|
+
subgraphs: options.subgraphs ?? false,
|
|
251
|
+
maxBuffered: Math.max(2, options.maxBuffered ?? DEFAULT_MAX_BUFFERED),
|
|
252
|
+
overflow: options.overflow ?? 'coalesce',
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function fromGraphEvent(event) {
|
|
256
|
+
const namespace = event.namespace ?? [];
|
|
257
|
+
switch (event.type) {
|
|
258
|
+
case 'message':
|
|
259
|
+
return [
|
|
260
|
+
{ type: 'messages', step: event.step, node: event.node, taskId: event.taskId, chunk: event.chunk, namespace },
|
|
261
|
+
];
|
|
262
|
+
case 'tool':
|
|
263
|
+
return [{ type: 'tools', step: event.step, node: event.node, taskId: event.taskId, tool: event.tool, namespace }];
|
|
264
|
+
case 'custom':
|
|
265
|
+
return [
|
|
266
|
+
{ type: 'custom', step: event.step, node: event.node, taskId: event.taskId, data: event.data, namespace },
|
|
267
|
+
];
|
|
268
|
+
case 'checkpoint':
|
|
269
|
+
return [{ type: 'checkpoints', step: event.step, status: event.status, next: event.next, namespace }];
|
|
270
|
+
case 'task_end':
|
|
271
|
+
return [
|
|
272
|
+
{ type: 'tasks', event, namespace },
|
|
273
|
+
...(event.update === undefined
|
|
274
|
+
? []
|
|
275
|
+
: [
|
|
276
|
+
{
|
|
277
|
+
type: 'updates',
|
|
278
|
+
step: event.step,
|
|
279
|
+
node: event.node,
|
|
280
|
+
taskId: event.taskId,
|
|
281
|
+
update: event.update,
|
|
282
|
+
namespace,
|
|
283
|
+
},
|
|
284
|
+
]),
|
|
285
|
+
];
|
|
286
|
+
default:
|
|
287
|
+
return [{ type: 'tasks', event, namespace }];
|
|
288
|
+
}
|
|
289
|
+
}
|
package/dist/graph/graph.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { ChannelSchema, CompileOptions, EdgeRouter, GraphCheckpoint, GraphDescription, GraphInput, GraphProgress, GraphResult, GraphRunOptions, GraphStatus, GraphStepEvent, NodeFn, NodeOptions, StateUpdate } from '../types/graph.js';
|
|
1
|
+
import type { ChannelSchema, CompileOptions, EdgeRouter, GraphCheckpoint, GraphDescription, GraphEventsOptions, GraphInput, GraphProgress, GraphResult, GraphRunOptions, GraphStatus, GraphStepEvent, NodeFn, NodeOptions, StateUpdate } from '../types/graph.js';
|
|
2
|
+
import { GraphEventStream } from './event-stream.js';
|
|
2
3
|
interface GraphNodeDefinition<S extends ChannelSchema> {
|
|
3
4
|
fn: NodeFn<S>;
|
|
4
|
-
options: NodeOptions
|
|
5
|
+
options: NodeOptions<S>;
|
|
5
6
|
}
|
|
6
7
|
/** Which channels a caller may write and read, as `createGraph()` declared them. */
|
|
7
8
|
interface GraphScope {
|
|
@@ -29,7 +30,7 @@ export declare class StateGraph<S extends ChannelSchema, I extends keyof S = key
|
|
|
29
30
|
private readonly edges;
|
|
30
31
|
constructor(channels: S, scope?: GraphScope);
|
|
31
32
|
/** Adds a node. Returns the graph, for chaining. */
|
|
32
|
-
addNode(name: string, fn: NodeFn<S>, options?: NodeOptions): this;
|
|
33
|
+
addNode(name: string, fn: NodeFn<S>, options?: NodeOptions<S>): this;
|
|
33
34
|
/** Adds an edge that always runs `to` after `from`. Returns the graph, for chaining. */
|
|
34
35
|
addEdge(from: string, to: string): this;
|
|
35
36
|
/**
|
|
@@ -55,11 +56,33 @@ export declare class CompiledGraph<S extends ChannelSchema, I extends keyof S =
|
|
|
55
56
|
private readonly store;
|
|
56
57
|
/** Loaded the first time a node that declares `cache` runs, and not before. */
|
|
57
58
|
private nodeCache;
|
|
59
|
+
/** The checkpoint writers of runs in progress, so `flush()` can wait for them. */
|
|
60
|
+
private readonly writers;
|
|
58
61
|
constructor(channels: S, nodes: Map<string, GraphNodeDefinition<S>>, edges: Array<Edge<S>>, options: CompileOptions, scope?: GraphScope);
|
|
62
|
+
/**
|
|
63
|
+
* Waits until every checkpoint written by runs in progress is in the store. Only `async` and
|
|
64
|
+
* `exit` durability leave any outstanding; a run always waits for its own before it returns.
|
|
65
|
+
*/
|
|
66
|
+
flush(): Promise<void>;
|
|
59
67
|
/** Runs to completion, to an interrupt, or to the step limit. */
|
|
60
68
|
invoke(input?: GraphInput<S, I>, runOptions?: GraphRunOptions): Promise<GraphResult<S, O>>;
|
|
61
69
|
/** Yields one event per superstep, so a caller can render progress as it happens. */
|
|
62
70
|
stream(input?: GraphInput<S, I>, runOptions?: GraphRunOptions): AsyncIterable<GraphStepEvent<S>>;
|
|
71
|
+
/**
|
|
72
|
+
* Runs the graph as one stream of typed events: the state after each superstep, each task's write,
|
|
73
|
+
* model output as nodes stream it, tool calls, task progress, checkpoints, and custom events.
|
|
74
|
+
*
|
|
75
|
+
* Iterate it for everything included, or read one projection with `messages()`, `tools()`,
|
|
76
|
+
* `values()`, or `updates()`. Each reader has its own bounded buffer, and the run waits for the
|
|
77
|
+
* slowest at the end of every superstep, so a slow reader slows the graph rather than filling
|
|
78
|
+
* memory. `result` resolves with the run's result.
|
|
79
|
+
*/
|
|
80
|
+
events(input?: GraphInput<S, I>, options?: GraphEventsOptions): GraphEventStream<S, O>;
|
|
81
|
+
/** Like `events()`, answering the question a paused thread is waiting on. */
|
|
82
|
+
resumeEvents(threadId: string, value: unknown, options?: GraphEventsOptions): GraphEventStream<S, O>;
|
|
83
|
+
/** Like `events()`, carrying a thread on from a breakpoint, a drain, or a failure. */
|
|
84
|
+
continueEvents(threadId: string, options?: GraphEventsOptions): GraphEventStream<S, O>;
|
|
85
|
+
private eventStream;
|
|
63
86
|
/**
|
|
64
87
|
* Supplies the value a node asked for and continues.
|
|
65
88
|
*
|
|
@@ -137,9 +160,24 @@ export declare class CompiledGraph<S extends ChannelSchema, I extends keyof S =
|
|
|
137
160
|
* admitted before its first superstep and finishes with its last, however it ends.
|
|
138
161
|
*/
|
|
139
162
|
private run;
|
|
163
|
+
/**
|
|
164
|
+
* Runs the step loop with a checkpoint writer for the run's durability mode. However the run ends,
|
|
165
|
+
* every checkpoint it handed over is in the store before this returns or throws.
|
|
166
|
+
*/
|
|
140
167
|
private runSteps;
|
|
168
|
+
private steps;
|
|
141
169
|
/** Runs one task to a verdict, applying its retry policy and timeout. */
|
|
142
170
|
private runTask;
|
|
171
|
+
/**
|
|
172
|
+
* Hands a failure to the node's `onError`, and turns what it decides into the task's result. With
|
|
173
|
+
* no handler, or for a mistake in the graph itself, the task fails as it always did.
|
|
174
|
+
*/
|
|
175
|
+
private recover;
|
|
176
|
+
/** The node's retry policy over the graph's defaults. */
|
|
177
|
+
private retryPolicy;
|
|
178
|
+
/** The node's run and idle limits over the graph's defaults. `timeout.runMs` wins over `timeoutMs`. */
|
|
179
|
+
private timeoutOf;
|
|
180
|
+
private errorHandler;
|
|
143
181
|
private runNode;
|
|
144
182
|
private loadNodeCache;
|
|
145
183
|
/** Rejects writes to channels the graph does not accept from a caller. */
|