nexus-ai-pro 2.1.0 → 2.2.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 +38 -0
- package/CHANGELOG.md +145 -1
- package/README.md +13 -8
- package/dist/cache/memory-cache.d.ts +8 -0
- package/dist/cache/memory-cache.js +9 -0
- package/dist/cache/semantic-cache.d.ts +6 -3
- package/dist/cache/semantic-cache.js +5 -2
- package/dist/cli/args.js +3 -0
- package/dist/cli/db.d.ts +16 -4
- package/dist/cli/db.js +169 -22
- package/dist/cli/doctor.d.ts +17 -0
- package/dist/cli/doctor.js +53 -0
- package/dist/cli.js +12 -1
- package/dist/core/lifecycle.js +11 -6
- package/dist/core/nexus.d.ts +6 -0
- package/dist/core/nexus.js +103 -22
- package/dist/doctor/index.d.ts +90 -0
- package/dist/doctor/index.js +237 -0
- package/dist/graph/functional.d.ts +80 -7
- package/dist/graph/functional.js +138 -35
- package/dist/graph/graph.d.ts +15 -5
- package/dist/graph/graph.js +72 -20
- package/dist/images/evals.js +4 -2
- package/dist/operations/adapters.d.ts +52 -4
- package/dist/operations/adapters.js +194 -7
- package/dist/operations/runner.js +1 -0
- package/dist/operations/stats.js +8 -1
- package/dist/operations/store.d.ts +5 -1
- package/dist/operations/store.js +7 -0
- package/dist/ops/circuit-breaker.d.ts +52 -0
- package/dist/ops/circuit-breaker.js +4 -0
- package/dist/ops/circuit-store.d.ts +13 -2
- package/dist/ops/circuit-store.js +68 -0
- package/dist/ops/circuit-sync.d.ts +17 -0
- package/dist/ops/circuit-sync.js +86 -0
- package/dist/ops/rate-limiter.d.ts +2 -0
- package/dist/ops/rate-limiter.js +2 -0
- package/dist/postgres/circuits.d.ts +21 -4
- package/dist/postgres/circuits.js +65 -4
- package/dist/postgres/client.d.ts +0 -2
- package/dist/postgres/client.js +0 -4
- package/dist/postgres/evaluate.d.ts +16 -4
- package/dist/postgres/evaluate.js +17 -5
- package/dist/postgres/index.d.ts +32 -11
- package/dist/postgres/index.js +65 -31
- package/dist/postgres/migrations.d.ts +60 -0
- package/dist/postgres/migrations.js +121 -0
- package/dist/postgres/operations.d.ts +14 -3
- package/dist/postgres/operations.js +45 -15
- package/dist/postgres/prompts.d.ts +11 -2
- package/dist/postgres/prompts.js +14 -3
- package/dist/postgres/rollups.d.ts +35 -0
- package/dist/postgres/rollups.js +129 -0
- package/dist/postgres/store.d.ts +11 -2
- package/dist/postgres/store.js +14 -3
- package/dist/postgres/traces.d.ts +13 -3
- package/dist/postgres/traces.js +25 -3
- package/dist/postgres/vectors.d.ts +11 -2
- package/dist/postgres/vectors.js +14 -3
- package/dist/router/auto-router.d.ts +34 -7
- package/dist/router/auto-router.js +201 -68
- package/dist/server/assistant.d.ts +4 -0
- package/dist/server/assistant.js +8 -0
- package/dist/server/errors.d.ts +12 -0
- package/dist/server/errors.js +7 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/runs.d.ts +24 -1
- package/dist/server/runs.js +90 -14
- package/dist/sqlite/index.d.ts +16 -3
- package/dist/sqlite/index.js +16 -3
- package/dist/sqlite/migrations.d.ts +26 -0
- package/dist/sqlite/migrations.js +63 -0
- package/dist/sqlite/operations.d.ts +14 -3
- package/dist/sqlite/operations.js +42 -15
- package/dist/sqlite/store.d.ts +9 -2
- package/dist/sqlite/store.js +13 -3
- package/dist/sqlite/vectors.d.ts +11 -2
- package/dist/sqlite/vectors.js +13 -2
- package/dist/store/tenant.d.ts +12 -0
- package/dist/store/tenant.js +29 -0
- package/dist/tenancy/index.d.ts +113 -0
- package/dist/tenancy/index.js +233 -0
- package/dist/tracing/instrument.js +2 -0
- package/dist/types/config.d.ts +24 -2
- package/dist/types/graph.d.ts +11 -1
- package/dist/types/messages.d.ts +5 -0
- package/dist/types/operations.d.ts +6 -0
- package/dist/types/response.d.ts +20 -0
- package/dist/types/server.d.ts +10 -1
- package/dist/utils/schema-migrations.d.ts +97 -0
- package/dist/utils/schema-migrations.js +91 -0
- package/dist/utils/signals.d.ts +17 -0
- package/dist/utils/signals.js +26 -0
- package/dist/utils/tenant.d.ts +14 -0
- package/dist/utils/tenant.js +16 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/cache/memory-cache.d.ts +8 -0
- package/dist-cjs/cache/memory-cache.js +9 -0
- package/dist-cjs/cache/semantic-cache.d.ts +6 -3
- package/dist-cjs/cache/semantic-cache.js +5 -2
- package/dist-cjs/cli/args.js +3 -0
- package/dist-cjs/cli/db.d.ts +16 -4
- package/dist-cjs/cli/db.js +200 -20
- package/dist-cjs/cli/doctor.d.ts +17 -0
- package/dist-cjs/cli/doctor.js +56 -0
- package/dist-cjs/core/lifecycle.js +11 -6
- package/dist-cjs/core/nexus.d.ts +6 -0
- package/dist-cjs/core/nexus.js +103 -22
- package/dist-cjs/doctor/index.d.ts +90 -0
- package/dist-cjs/doctor/index.js +276 -0
- package/dist-cjs/graph/functional.d.ts +80 -7
- package/dist-cjs/graph/functional.js +137 -34
- package/dist-cjs/graph/graph.d.ts +15 -5
- package/dist-cjs/graph/graph.js +72 -20
- package/dist-cjs/images/evals.js +4 -2
- package/dist-cjs/operations/adapters.d.ts +52 -4
- package/dist-cjs/operations/adapters.js +193 -6
- package/dist-cjs/operations/runner.js +1 -0
- package/dist-cjs/operations/stats.js +8 -1
- package/dist-cjs/operations/store.d.ts +5 -1
- package/dist-cjs/operations/store.js +7 -0
- package/dist-cjs/ops/circuit-breaker.d.ts +52 -0
- package/dist-cjs/ops/circuit-breaker.js +4 -0
- package/dist-cjs/ops/circuit-store.d.ts +13 -2
- package/dist-cjs/ops/circuit-store.js +68 -0
- package/dist-cjs/ops/circuit-sync.d.ts +17 -0
- package/dist-cjs/ops/circuit-sync.js +87 -0
- package/dist-cjs/ops/rate-limiter.d.ts +2 -0
- package/dist-cjs/ops/rate-limiter.js +2 -0
- package/dist-cjs/postgres/circuits.d.ts +21 -4
- package/dist-cjs/postgres/circuits.js +98 -3
- package/dist-cjs/postgres/client.d.ts +0 -2
- package/dist-cjs/postgres/client.js +0 -5
- package/dist-cjs/postgres/evaluate.d.ts +16 -4
- package/dist-cjs/postgres/evaluate.js +50 -4
- package/dist-cjs/postgres/index.d.ts +32 -11
- package/dist-cjs/postgres/index.js +69 -20
- package/dist-cjs/postgres/migrations.d.ts +60 -0
- package/dist-cjs/postgres/migrations.js +129 -0
- package/dist-cjs/postgres/operations.d.ts +14 -3
- package/dist-cjs/postgres/operations.js +78 -14
- package/dist-cjs/postgres/prompts.d.ts +11 -2
- package/dist-cjs/postgres/prompts.js +47 -2
- package/dist-cjs/postgres/rollups.d.ts +35 -0
- package/dist-cjs/postgres/rollups.js +168 -0
- package/dist-cjs/postgres/store.d.ts +11 -2
- package/dist-cjs/postgres/store.js +47 -2
- package/dist-cjs/postgres/traces.d.ts +13 -3
- package/dist-cjs/postgres/traces.js +58 -2
- package/dist-cjs/postgres/vectors.d.ts +11 -2
- package/dist-cjs/postgres/vectors.js +47 -2
- package/dist-cjs/router/auto-router.d.ts +34 -7
- package/dist-cjs/router/auto-router.js +201 -68
- package/dist-cjs/server/assistant.d.ts +4 -0
- package/dist-cjs/server/assistant.js +8 -0
- package/dist-cjs/server/errors.d.ts +12 -0
- package/dist-cjs/server/errors.js +9 -1
- package/dist-cjs/server/index.d.ts +1 -1
- package/dist-cjs/server/index.js +2 -1
- package/dist-cjs/server/runs.d.ts +24 -1
- package/dist-cjs/server/runs.js +89 -13
- package/dist-cjs/sqlite/index.d.ts +16 -3
- package/dist-cjs/sqlite/index.js +31 -10
- package/dist-cjs/sqlite/migrations.d.ts +26 -0
- package/dist-cjs/sqlite/migrations.js +71 -0
- package/dist-cjs/sqlite/operations.d.ts +14 -3
- package/dist-cjs/sqlite/operations.js +76 -15
- package/dist-cjs/sqlite/store.d.ts +9 -2
- package/dist-cjs/sqlite/store.js +47 -3
- package/dist-cjs/sqlite/vectors.d.ts +11 -2
- package/dist-cjs/sqlite/vectors.js +47 -2
- package/dist-cjs/store/tenant.d.ts +12 -0
- package/dist-cjs/store/tenant.js +33 -0
- package/dist-cjs/tenancy/index.d.ts +113 -0
- package/dist-cjs/tenancy/index.js +250 -0
- package/dist-cjs/tracing/instrument.js +2 -0
- package/dist-cjs/types/config.d.ts +24 -2
- package/dist-cjs/types/graph.d.ts +11 -1
- package/dist-cjs/types/messages.d.ts +5 -0
- package/dist-cjs/types/operations.d.ts +6 -0
- package/dist-cjs/types/response.d.ts +20 -0
- package/dist-cjs/types/server.d.ts +10 -1
- package/dist-cjs/utils/schema-migrations.d.ts +97 -0
- package/dist-cjs/utils/schema-migrations.js +102 -0
- package/dist-cjs/utils/signals.d.ts +17 -0
- package/dist-cjs/utils/signals.js +29 -0
- package/dist-cjs/utils/tenant.d.ts +14 -0
- package/dist-cjs/utils/tenant.js +22 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +4 -3
- package/package.json +71 -2
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import type { OperationLifecycleLike } from '../types/lifecycle.js';
|
|
2
2
|
import { GraphError } from './errors.js';
|
|
3
|
-
import type { GraphCheckpoint, GraphCheckpointer, GraphDescription, GraphEvent, GraphStatus, InterruptRequest, PendingInterrupt, RetryPolicy } from '../types/graph.js';
|
|
3
|
+
import type { GraphCheckpoint, GraphCheckpointer, GraphDescription, GraphEvent, GraphStatus, InterruptRequest, PendingInterrupt, RetryPolicy, RunControlLike } from '../types/graph.js';
|
|
4
4
|
import type { Store } from '../types/store.js';
|
|
5
5
|
/** What a workflow checkpoint holds: the input, every completed step's result, and the output. */
|
|
6
6
|
export interface WorkflowState<I = unknown, O = unknown> {
|
|
7
7
|
/** The input the workflow was started with. */
|
|
8
8
|
input: I;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Each completed step's result, keyed by step name — `name#2` for the second call with a name. A
|
|
11
|
+
* step whose `onError` supplied its result after its retries ran out also records `recovered`.
|
|
12
|
+
*/
|
|
10
13
|
steps: Record<string, {
|
|
11
14
|
value?: unknown;
|
|
15
|
+
recovered?: StepRecovery;
|
|
12
16
|
}>;
|
|
13
17
|
/** The workflow's return value, once it has completed. */
|
|
14
18
|
output?: O;
|
|
@@ -23,21 +27,68 @@ export interface StepContext {
|
|
|
23
27
|
readonly attempt: number;
|
|
24
28
|
/** Aborted when the run is cancelled or the step exceeds its timeout. */
|
|
25
29
|
readonly signal: AbortSignal;
|
|
30
|
+
/**
|
|
31
|
+
* Shows the step is still making progress, which refreshes its idle timeout. For long work that
|
|
32
|
+
* sends nothing else, such as a large upload or a slow tool call.
|
|
33
|
+
*/
|
|
34
|
+
heartbeat(): void;
|
|
35
|
+
}
|
|
36
|
+
/** Limits on one attempt of a step: its whole run, and the time it may go without progress. */
|
|
37
|
+
export interface StepTimeout {
|
|
38
|
+
/** Fails the attempt when it runs longer than this, in milliseconds. */
|
|
39
|
+
runMs?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Fails the attempt when this long passes without `context.heartbeat()`, in milliseconds, which
|
|
42
|
+
* tells a hung step from a slow one that is working.
|
|
43
|
+
*/
|
|
44
|
+
idleMs?: number;
|
|
45
|
+
}
|
|
46
|
+
/** What a step's `onError` receives once its retries have run out. */
|
|
47
|
+
export interface StepFailure {
|
|
48
|
+
/** The step's name. */
|
|
49
|
+
name: string;
|
|
50
|
+
/** The key its result is recorded under. */
|
|
51
|
+
key: string;
|
|
52
|
+
/** Attempts made. */
|
|
53
|
+
attempts: number;
|
|
54
|
+
/** The last attempt's error. */
|
|
55
|
+
error: unknown;
|
|
56
|
+
}
|
|
57
|
+
/** How a step's result was recovered, as recorded in the workflow state. */
|
|
58
|
+
export interface StepRecovery {
|
|
59
|
+
/** The error that exhausted the step's retries. */
|
|
60
|
+
error: {
|
|
61
|
+
name: string;
|
|
62
|
+
message: string;
|
|
63
|
+
};
|
|
64
|
+
/** Attempts made before `onError` decided. */
|
|
65
|
+
attempts: number;
|
|
26
66
|
}
|
|
27
67
|
/** How one step runs. */
|
|
28
|
-
export interface StepOptions {
|
|
68
|
+
export interface StepOptions<T = unknown> {
|
|
29
69
|
/** Retries a failing attempt, overriding the workflow's default policy. */
|
|
30
70
|
retry?: RetryPolicy;
|
|
31
71
|
/** Aborts the step's signal and fails the attempt when it runs longer than this, in milliseconds. */
|
|
32
72
|
timeoutMs?: number;
|
|
73
|
+
/** Run and idle limits on each attempt. `runMs` is `timeoutMs` by another name; `timeoutMs` wins when both are set. */
|
|
74
|
+
timeout?: StepTimeout;
|
|
75
|
+
/**
|
|
76
|
+
* Decides what happens once the step's retries run out. What it returns becomes the step's
|
|
77
|
+
* result, recorded with `recovered`, so a resumed run does not run the step again — a refund after
|
|
78
|
+
* a charge that kept failing, say. Throwing fails the step with that error. A cancelled run, a
|
|
79
|
+
* question, or a drain is never passed to it.
|
|
80
|
+
*/
|
|
81
|
+
onError?: (failure: StepFailure) => T | Promise<T>;
|
|
33
82
|
}
|
|
34
83
|
/** What a workflow function receives besides its input. */
|
|
35
84
|
export interface WorkflowContext {
|
|
36
85
|
/** The thread this run belongs to. */
|
|
37
86
|
readonly threadId: string;
|
|
87
|
+
/** The tenant the run is for, when it has one. */
|
|
88
|
+
readonly tenantId?: string;
|
|
38
89
|
/** Aborted when the run is cancelled. */
|
|
39
90
|
readonly signal: AbortSignal;
|
|
40
|
-
/** Long-term memory, when the workflow was created with a store. */
|
|
91
|
+
/** Long-term memory, when the workflow was created with a store. In a tenant's run, the tenant's view of it. */
|
|
41
92
|
readonly store?: Store;
|
|
42
93
|
/**
|
|
43
94
|
* Runs a piece of work once per thread and records its result.
|
|
@@ -46,7 +97,7 @@ export interface WorkflowContext {
|
|
|
46
97
|
* running. Name steps so the same call has the same name every time; a name used more than once
|
|
47
98
|
* is numbered in call order. The result must survive JSON serialization.
|
|
48
99
|
*/
|
|
49
|
-
step<T>(name: string, run: (context: StepContext) => Promise<T> | T, options?: StepOptions): Promise<T>;
|
|
100
|
+
step<T>(name: string, run: (context: StepContext) => Promise<T> | T, options?: StepOptions<T>): Promise<T>;
|
|
50
101
|
/**
|
|
51
102
|
* Suspends the workflow until a person answers.
|
|
52
103
|
*
|
|
@@ -73,6 +124,11 @@ export interface WorkflowOptions {
|
|
|
73
124
|
store?: Store;
|
|
74
125
|
/** Default retry policy for every step. A step's own policy wins. Defaults to one attempt. */
|
|
75
126
|
retry?: RetryPolicy;
|
|
127
|
+
/**
|
|
128
|
+
* Defaults for every step: its retries, its run and idle timeouts, and its `onError`. A step's own
|
|
129
|
+
* options win, field by field.
|
|
130
|
+
*/
|
|
131
|
+
stepDefaults?: Pick<StepOptions, 'retry' | 'timeout' | 'onError'>;
|
|
76
132
|
/** Steps that may run at once, for a workflow that starts many with `Promise.all`. Defaults to 16. */
|
|
77
133
|
maxConcurrency?: number;
|
|
78
134
|
/**
|
|
@@ -93,6 +149,19 @@ export interface WorkflowRunOptions {
|
|
|
93
149
|
metadata?: Record<string, unknown>;
|
|
94
150
|
/** Receives each step starting, retrying, and finishing, each checkpoint, and custom events. */
|
|
95
151
|
onEvent?: (event: GraphEvent) => void;
|
|
152
|
+
/**
|
|
153
|
+
* Lets the caller stop the run between steps: pass a `RunControl` and call `drain()`. Steps in
|
|
154
|
+
* flight finish and are recorded, no new step starts, and the run ends with `GraphDrainedError`;
|
|
155
|
+
* `continue()` carries it on, on this worker or another. The agent server drains a workflow it
|
|
156
|
+
* serves this way.
|
|
157
|
+
*/
|
|
158
|
+
control?: RunControlLike;
|
|
159
|
+
/**
|
|
160
|
+
* The tenant the run is for. Recorded on every checkpoint as `metadata.tenantId`, read as
|
|
161
|
+
* `context.tenantId`, and scoping `context.store`; another tenant's thread is not found. A run
|
|
162
|
+
* without one carries on the thread's.
|
|
163
|
+
*/
|
|
164
|
+
tenantId?: string;
|
|
96
165
|
}
|
|
97
166
|
/** One event of a streamed workflow run. */
|
|
98
167
|
export type WorkflowEvent<O = unknown> = {
|
|
@@ -152,17 +221,21 @@ export interface WorkflowResult<O = unknown> {
|
|
|
152
221
|
/** Every pending question. */
|
|
153
222
|
interrupts?: PendingInterrupt[];
|
|
154
223
|
}
|
|
155
|
-
/** Raised when a step outlives its
|
|
224
|
+
/** Raised when a step outlives its run timeout, or goes longer than its idle timeout without progress. */
|
|
156
225
|
export declare class WorkflowStepTimeoutError extends GraphError {
|
|
157
226
|
/** The step. */
|
|
158
227
|
readonly stepName: string;
|
|
159
228
|
/** The limit it exceeded, in milliseconds. */
|
|
160
229
|
readonly timeoutMs: number;
|
|
230
|
+
/** Which limit: `run` for the whole attempt, `idle` for the time without progress. */
|
|
231
|
+
readonly kind: 'run' | 'idle';
|
|
161
232
|
constructor(
|
|
162
233
|
/** The step. */
|
|
163
234
|
stepName: string,
|
|
164
235
|
/** The limit it exceeded, in milliseconds. */
|
|
165
|
-
timeoutMs: number
|
|
236
|
+
timeoutMs: number,
|
|
237
|
+
/** Which limit: `run` for the whole attempt, `idle` for the time without progress. */
|
|
238
|
+
kind?: 'run' | 'idle');
|
|
166
239
|
}
|
|
167
240
|
/**
|
|
168
241
|
* A durable workflow: a function whose steps are checkpointed, so it survives interrupts, restarts,
|
package/dist/graph/functional.js
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { MemoryGraphCheckpointer } from './checkpointer.js';
|
|
2
2
|
import { withLifecycle } from '../lifecycle/run.js';
|
|
3
3
|
import { migrateCheckpoint, toCheckpoint } from './checkpoint-migration.js';
|
|
4
|
-
import {
|
|
4
|
+
import { tenantStore } from '../store/tenant.js';
|
|
5
|
+
import { GraphDrainedError, GraphError, GraphNotInterruptedError, GraphThreadNotFoundError } from './errors.js';
|
|
5
6
|
export class WorkflowStepTimeoutError extends GraphError {
|
|
6
7
|
stepName;
|
|
7
8
|
timeoutMs;
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
kind;
|
|
10
|
+
constructor(stepName, timeoutMs, kind = 'run') {
|
|
11
|
+
super(kind === 'idle'
|
|
12
|
+
? `Step "${stepName}" made no progress for ${timeoutMs} ms`
|
|
13
|
+
: `Step "${stepName}" exceeded its ${timeoutMs} ms timeout`, 'WORKFLOW_STEP_TIMEOUT');
|
|
10
14
|
this.stepName = stepName;
|
|
11
15
|
this.timeoutMs = timeoutMs;
|
|
16
|
+
this.kind = kind;
|
|
12
17
|
this.name = 'WorkflowStepTimeoutError';
|
|
13
18
|
}
|
|
14
19
|
}
|
|
20
|
+
class WorkflowDraining extends Error {
|
|
21
|
+
constructor() {
|
|
22
|
+
super('The workflow is draining');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
15
25
|
class WorkflowSuspended extends Error {
|
|
16
26
|
pending;
|
|
17
27
|
constructor(pending) {
|
|
@@ -40,10 +50,16 @@ export class Workflow {
|
|
|
40
50
|
}
|
|
41
51
|
stream(input, runOptions = {}) {
|
|
42
52
|
const threadId = runOptions.threadId ?? generatedId();
|
|
43
|
-
return this.execute(threadId, runOptions, async () =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
return this.execute(threadId, runOptions, async () => {
|
|
54
|
+
const latest = await this.latest(threadId);
|
|
55
|
+
if (latest && runOptions.tenantId !== undefined) {
|
|
56
|
+
const owner = latest.metadata?.tenantId;
|
|
57
|
+
if (typeof owner === 'string' && owner !== runOptions.tenantId) {
|
|
58
|
+
throw new GraphError(`Thread "${threadId}" cannot be used by this tenant; choose another id`, 'GRAPH_VALIDATION_ERROR');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { ...this.fresh(threadId, input), step: latest?.step ?? 0 };
|
|
62
|
+
});
|
|
47
63
|
}
|
|
48
64
|
resume(threadId, value, runOptions = {}) {
|
|
49
65
|
return this.execute(threadId, runOptions, async () => {
|
|
@@ -137,11 +153,21 @@ export class Workflow {
|
|
|
137
153
|
}
|
|
138
154
|
async run(threadId, runOptions, start, queue) {
|
|
139
155
|
const initial = await start();
|
|
156
|
+
const owner = typeof initial.metadata?.tenantId === 'string' ? initial.metadata.tenantId : undefined;
|
|
157
|
+
if (runOptions.tenantId !== undefined && owner !== undefined && owner !== runOptions.tenantId) {
|
|
158
|
+
throw new GraphThreadNotFoundError(threadId);
|
|
159
|
+
}
|
|
160
|
+
const tenantId = runOptions.tenantId ?? owner;
|
|
140
161
|
const checkpointer = this.checkpointer();
|
|
141
162
|
const state = structuredClone(initial.state);
|
|
142
163
|
state.steps ??= {};
|
|
143
164
|
const resolved = { ...initial.resolved };
|
|
144
|
-
const metadata = {
|
|
165
|
+
const metadata = {
|
|
166
|
+
...initial.metadata,
|
|
167
|
+
graph: this.name,
|
|
168
|
+
...runOptions.metadata,
|
|
169
|
+
...(tenantId === undefined ? {} : { tenantId }),
|
|
170
|
+
};
|
|
145
171
|
let stepNumber = initial.step;
|
|
146
172
|
let writes = Promise.resolve();
|
|
147
173
|
const persist = (checkpoint) => {
|
|
@@ -171,11 +197,16 @@ export class Workflow {
|
|
|
171
197
|
const inFlight = new Set();
|
|
172
198
|
const suspended = [];
|
|
173
199
|
let interruptIndex = 0;
|
|
200
|
+
let drained = false;
|
|
174
201
|
const limiter = new Limiter(this.options.maxConcurrency ?? 16);
|
|
202
|
+
const defaults = this.options.stepDefaults ?? {};
|
|
175
203
|
const context = {
|
|
176
204
|
threadId,
|
|
205
|
+
...(tenantId === undefined ? {} : { tenantId }),
|
|
177
206
|
signal: controller.signal,
|
|
178
|
-
...(this.options.store
|
|
207
|
+
...(this.options.store
|
|
208
|
+
? { store: tenantId === undefined ? this.options.store : tenantStore(this.options.store, tenantId) }
|
|
209
|
+
: {}),
|
|
179
210
|
step: (name, run, options = {}) => {
|
|
180
211
|
const count = (counts.get(name) ?? 0) + 1;
|
|
181
212
|
counts.set(name, count);
|
|
@@ -185,10 +216,22 @@ export class Workflow {
|
|
|
185
216
|
queue.push({ type: 'step', step: stepNumber, name, key, cached: true, state: snapshot(), status: 'running' });
|
|
186
217
|
return Promise.resolve(recorded.value);
|
|
187
218
|
}
|
|
219
|
+
if (runOptions.control?.draining) {
|
|
220
|
+
drained = true;
|
|
221
|
+
return Promise.reject(new WorkflowDraining());
|
|
222
|
+
}
|
|
223
|
+
const merged = {
|
|
224
|
+
...defaults,
|
|
225
|
+
...options,
|
|
226
|
+
timeout: { ...defaults.timeout, ...options.timeout },
|
|
227
|
+
};
|
|
188
228
|
const work = limiter
|
|
189
|
-
.run(() => this.runStep(name, key, run, { ...this.options.retry, ...options.retry },
|
|
190
|
-
.then(async (value) => {
|
|
191
|
-
state.steps[key] =
|
|
229
|
+
.run(() => this.runStep(name, key, run, { ...this.options.retry, ...defaults.retry, ...options.retry }, merged, controller.signal, runOptions, stepNumber))
|
|
230
|
+
.then(async ({ value, recovered }) => {
|
|
231
|
+
state.steps[key] = {
|
|
232
|
+
...(value === undefined ? {} : { value: structuredClone(value) }),
|
|
233
|
+
...(recovered ? { recovered } : {}),
|
|
234
|
+
};
|
|
192
235
|
await persist({ state: state, next: [this.name], status: 'running' });
|
|
193
236
|
queue.push({
|
|
194
237
|
type: 'step',
|
|
@@ -262,6 +305,18 @@ export class Workflow {
|
|
|
262
305
|
queue.push({ type: 'interrupt', step: stepNumber, state: snapshot(), status: 'interrupted' });
|
|
263
306
|
return;
|
|
264
307
|
}
|
|
308
|
+
if (drained && outcome.kind === 'failed') {
|
|
309
|
+
const reason = runOptions.control?.reason;
|
|
310
|
+
await persist({
|
|
311
|
+
state: state,
|
|
312
|
+
next: [this.name],
|
|
313
|
+
status: 'interrupted',
|
|
314
|
+
resolved,
|
|
315
|
+
drained: { ...(reason ? { reason } : {}), at: this.now().toISOString() },
|
|
316
|
+
});
|
|
317
|
+
await writes;
|
|
318
|
+
throw new GraphDrainedError(threadId, stepNumber, reason);
|
|
319
|
+
}
|
|
265
320
|
if (outcome.kind === 'failed') {
|
|
266
321
|
const error = outcome.error;
|
|
267
322
|
await persist({
|
|
@@ -284,41 +339,87 @@ export class Workflow {
|
|
|
284
339
|
}
|
|
285
340
|
async runStep(name, key, run, retry, options, signal, runOptions, step) {
|
|
286
341
|
const policy = { ...DEFAULT_RETRY, ...retry };
|
|
342
|
+
const runMs = options.timeoutMs ?? options.timeout?.runMs;
|
|
343
|
+
const idleMs = options.timeout?.idleMs;
|
|
287
344
|
for (let attempt = 1;; attempt += 1) {
|
|
288
345
|
notify(runOptions, { type: 'task_start', step, taskId: key, node: name, attempt });
|
|
289
346
|
const stepController = new AbortController();
|
|
290
347
|
const forward = () => stepController.abort(signal.reason);
|
|
291
348
|
signal.addEventListener('abort', forward, { once: true });
|
|
292
|
-
let
|
|
293
|
-
let
|
|
349
|
+
let runTimer;
|
|
350
|
+
let idleTimer;
|
|
351
|
+
let timedOut;
|
|
352
|
+
let expire = () => undefined;
|
|
353
|
+
const limits = runMs === undefined && idleMs === undefined
|
|
354
|
+
? undefined
|
|
355
|
+
: new Promise((_, reject) => {
|
|
356
|
+
expire = (error) => {
|
|
357
|
+
if (timedOut)
|
|
358
|
+
return;
|
|
359
|
+
timedOut = error;
|
|
360
|
+
reject(error);
|
|
361
|
+
stepController.abort();
|
|
362
|
+
};
|
|
363
|
+
});
|
|
364
|
+
limits?.catch(() => undefined);
|
|
365
|
+
if (runMs !== undefined)
|
|
366
|
+
runTimer = setTimeout(() => expire(new WorkflowStepTimeoutError(name, runMs)), runMs);
|
|
367
|
+
const armIdle = () => {
|
|
368
|
+
if (idleMs === undefined || timedOut)
|
|
369
|
+
return;
|
|
370
|
+
if (idleTimer)
|
|
371
|
+
clearTimeout(idleTimer);
|
|
372
|
+
idleTimer = setTimeout(() => expire(new WorkflowStepTimeoutError(name, idleMs, 'idle')), idleMs);
|
|
373
|
+
};
|
|
374
|
+
armIdle();
|
|
294
375
|
try {
|
|
295
|
-
const work = Promise.resolve(run({ name, key, attempt, signal: stepController.signal }));
|
|
296
|
-
const value =
|
|
297
|
-
? await work
|
|
298
|
-
: await Promise.race([
|
|
299
|
-
work,
|
|
300
|
-
new Promise((_, reject) => {
|
|
301
|
-
timer = setTimeout(() => {
|
|
302
|
-
timedOut = true;
|
|
303
|
-
reject(new WorkflowStepTimeoutError(name, options.timeoutMs));
|
|
304
|
-
stepController.abort();
|
|
305
|
-
}, options.timeoutMs);
|
|
306
|
-
}),
|
|
307
|
-
]);
|
|
376
|
+
const work = Promise.resolve(run({ name, key, attempt, signal: stepController.signal, heartbeat: armIdle }));
|
|
377
|
+
const value = limits ? await Promise.race([work, limits]) : await work;
|
|
308
378
|
if (timedOut)
|
|
309
|
-
throw
|
|
379
|
+
throw timedOut;
|
|
310
380
|
notify(runOptions, { type: 'task_end', step, taskId: key, node: name, update: value });
|
|
311
|
-
return value;
|
|
381
|
+
return { value };
|
|
312
382
|
}
|
|
313
383
|
catch (error) {
|
|
314
384
|
if (signal.aborted || error instanceof WorkflowSuspended)
|
|
315
385
|
throw error;
|
|
316
|
-
const failure = timedOut
|
|
386
|
+
const failure = timedOut ?? error;
|
|
317
387
|
const retryable = policy.retryOn
|
|
318
388
|
? policy.retryOn(failure, attempt)
|
|
319
|
-
: !(failure instanceof GraphError) || timedOut;
|
|
320
|
-
if (attempt >= Math.max(1, policy.maxAttempts) || !retryable)
|
|
321
|
-
|
|
389
|
+
: !(failure instanceof GraphError) || timedOut !== undefined;
|
|
390
|
+
if (attempt >= Math.max(1, policy.maxAttempts) || !retryable) {
|
|
391
|
+
if (!options.onError)
|
|
392
|
+
throw failure;
|
|
393
|
+
const failed = (recovered) => notify(runOptions, {
|
|
394
|
+
type: 'task_failed',
|
|
395
|
+
step,
|
|
396
|
+
taskId: key,
|
|
397
|
+
node: name,
|
|
398
|
+
attempts: attempt,
|
|
399
|
+
error: failure instanceof Error ? failure.message : String(failure),
|
|
400
|
+
recovered,
|
|
401
|
+
});
|
|
402
|
+
let value;
|
|
403
|
+
try {
|
|
404
|
+
value = await options.onError({ name, key, attempts: attempt, error: failure });
|
|
405
|
+
}
|
|
406
|
+
catch (decision) {
|
|
407
|
+
failed(false);
|
|
408
|
+
throw decision;
|
|
409
|
+
}
|
|
410
|
+
failed(true);
|
|
411
|
+
notify(runOptions, { type: 'task_end', step, taskId: key, node: name, update: value });
|
|
412
|
+
return {
|
|
413
|
+
value,
|
|
414
|
+
recovered: {
|
|
415
|
+
error: {
|
|
416
|
+
name: failure instanceof Error ? failure.name : 'Error',
|
|
417
|
+
message: failure instanceof Error ? failure.message : String(failure),
|
|
418
|
+
},
|
|
419
|
+
attempts: attempt,
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
}
|
|
322
423
|
notify(runOptions, {
|
|
323
424
|
type: 'task_retry',
|
|
324
425
|
step,
|
|
@@ -330,8 +431,10 @@ export class Workflow {
|
|
|
330
431
|
await sleep(backoff(policy, attempt), signal);
|
|
331
432
|
}
|
|
332
433
|
finally {
|
|
333
|
-
if (
|
|
334
|
-
clearTimeout(
|
|
434
|
+
if (runTimer)
|
|
435
|
+
clearTimeout(runTimer);
|
|
436
|
+
if (idleTimer)
|
|
437
|
+
clearTimeout(idleTimer);
|
|
335
438
|
signal.removeEventListener('abort', forward);
|
|
336
439
|
}
|
|
337
440
|
}
|
package/dist/graph/graph.d.ts
CHANGED
|
@@ -111,10 +111,17 @@ export declare class CompiledGraph<S extends ChannelSchema, I extends keyof S =
|
|
|
111
111
|
* once the rewound checkpoint is written.
|
|
112
112
|
*/
|
|
113
113
|
resumeFrom(threadId: string, step: number, runOptions?: GraphRunOptions): AsyncIterable<GraphStepEvent<S>>;
|
|
114
|
-
/**
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Latest checkpoint, or the one at `step`. With a `tenantId`, a thread another tenant owns reads
|
|
116
|
+
* as absent.
|
|
117
|
+
*/
|
|
118
|
+
state(threadId: string, step?: number, options?: {
|
|
119
|
+
tenantId?: string;
|
|
120
|
+
}): Promise<GraphCheckpoint<S> | undefined>;
|
|
121
|
+
/** Checkpoints for a thread, newest first. With a `tenantId`, another tenant's thread has none. */
|
|
122
|
+
history(threadId: string, limit?: number, options?: {
|
|
123
|
+
tenantId?: string;
|
|
124
|
+
}): Promise<Array<GraphCheckpoint<S>>>;
|
|
118
125
|
/**
|
|
119
126
|
* Wraps this graph as a node in another graph.
|
|
120
127
|
*
|
|
@@ -143,17 +150,20 @@ export declare class CompiledGraph<S extends ChannelSchema, I extends keyof S =
|
|
|
143
150
|
*/
|
|
144
151
|
updateState(threadId: string, update: StateUpdate<S>, options?: {
|
|
145
152
|
asNode?: string;
|
|
153
|
+
tenantId?: string;
|
|
146
154
|
}): Promise<GraphCheckpoint<S>>;
|
|
147
155
|
/**
|
|
148
156
|
* Copies a thread's history up to `step` into a new thread, and returns the new thread's id.
|
|
149
157
|
*
|
|
150
158
|
* Rewinding with `resumeFrom()` replaces the original timeline. A fork keeps it: both threads stay
|
|
151
159
|
* readable and runnable, so two answers to the same question can be compared side by side. Every
|
|
152
|
-
* copied checkpoint records `metadata.forkedFrom
|
|
160
|
+
* copied checkpoint records `metadata.forkedFrom`, and keeps the tenant of the thread it came from.
|
|
161
|
+
* With a `tenantId`, another tenant's thread is not found.
|
|
153
162
|
*/
|
|
154
163
|
fork(threadId: string, options?: {
|
|
155
164
|
step?: number;
|
|
156
165
|
threadId?: string;
|
|
166
|
+
tenantId?: string;
|
|
157
167
|
}): Promise<string>;
|
|
158
168
|
/**
|
|
159
169
|
* Runs the supersteps, as one operation of the compiled lifecycle when there is one. The run is
|
package/dist/graph/graph.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
2
|
import { Command, END, Send, START } from '../types/graph.js';
|
|
3
|
+
import { tenantStore } from '../store/tenant.js';
|
|
4
|
+
import { linkSignals } from '../utils/signals.js';
|
|
3
5
|
import { MemoryGraphCheckpointer } from './checkpointer.js';
|
|
4
6
|
import { migrateCheckpoint, toCheckpoint } from './checkpoint-migration.js';
|
|
5
7
|
import { CheckpointWriter } from './durability.js';
|
|
@@ -150,6 +152,12 @@ export class CompiledGraph {
|
|
|
150
152
|
const threadId = resolveThreadId(runOptions.threadId);
|
|
151
153
|
return this.run(threadId, runOptions, async () => {
|
|
152
154
|
this.assertInput(input);
|
|
155
|
+
if (runOptions.tenantId !== undefined) {
|
|
156
|
+
const existing = await this.checkpointer()?.get(threadId);
|
|
157
|
+
if (existing && !ownedBy(existing, runOptions.tenantId)) {
|
|
158
|
+
throw new GraphValidationError(`Thread "${threadId}" cannot be used by this tenant; choose another id`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
153
161
|
const state = this.seedState(input);
|
|
154
162
|
const next = this.entryNodes();
|
|
155
163
|
return { threadId, step: 0, state, next, status: 'running', createdAt: this.now().toISOString() };
|
|
@@ -166,16 +174,19 @@ export class CompiledGraph {
|
|
|
166
174
|
return this.eventStream(threadId, options, (runOptions) => this.continue(threadId, runOptions));
|
|
167
175
|
}
|
|
168
176
|
eventStream(threadId, options, run) {
|
|
169
|
-
return new GraphEventStream((onEvent, signal) =>
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
options.
|
|
176
|
-
onEvent(event)
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
return new GraphEventStream((onEvent, signal) => {
|
|
178
|
+
const linked = linkSignals([options.signal, signal]);
|
|
179
|
+
return releasing(run({
|
|
180
|
+
...options,
|
|
181
|
+
threadId,
|
|
182
|
+
signal: linked.signal,
|
|
183
|
+
...(options.subgraphs ? { subgraphEvents: true } : {}),
|
|
184
|
+
onEvent: (event) => {
|
|
185
|
+
options.onEvent?.(event);
|
|
186
|
+
onEvent(event);
|
|
187
|
+
},
|
|
188
|
+
}), linked.dispose);
|
|
189
|
+
}, (last) => this.toResult(threadId, last), streamSettings(options));
|
|
179
190
|
}
|
|
180
191
|
resume(threadId, value, runOptions = {}) {
|
|
181
192
|
return this.run(threadId, runOptions, async () => {
|
|
@@ -237,12 +248,16 @@ export class CompiledGraph {
|
|
|
237
248
|
return { ...migrateCheckpoint(stored), status: 'running' };
|
|
238
249
|
});
|
|
239
250
|
}
|
|
240
|
-
async state(threadId, step) {
|
|
251
|
+
async state(threadId, step, options = {}) {
|
|
241
252
|
const stored = await this.checkpointer()?.get(threadId, step);
|
|
242
|
-
|
|
253
|
+
if (!stored || !ownedBy(stored, options.tenantId))
|
|
254
|
+
return undefined;
|
|
255
|
+
return migrateCheckpoint(stored);
|
|
243
256
|
}
|
|
244
|
-
async history(threadId, limit) {
|
|
257
|
+
async history(threadId, limit, options = {}) {
|
|
245
258
|
const stored = (await this.checkpointer()?.history(threadId, limit)) ?? [];
|
|
259
|
+
if (stored[0] && !ownedBy(stored[0], options.tenantId))
|
|
260
|
+
return [];
|
|
246
261
|
return stored.map((item) => migrateCheckpoint(item));
|
|
247
262
|
}
|
|
248
263
|
asNode() {
|
|
@@ -346,7 +361,7 @@ export class CompiledGraph {
|
|
|
346
361
|
};
|
|
347
362
|
}
|
|
348
363
|
async updateState(threadId, update, options = {}) {
|
|
349
|
-
const checkpoint = await this.requireCheckpoint(threadId);
|
|
364
|
+
const checkpoint = await this.requireCheckpoint(threadId, options.tenantId);
|
|
350
365
|
const state = this.reduce(checkpoint.state, [update]);
|
|
351
366
|
const createdAt = this.now().toISOString();
|
|
352
367
|
if (!options.asNode) {
|
|
@@ -381,8 +396,9 @@ export class CompiledGraph {
|
|
|
381
396
|
if (!store)
|
|
382
397
|
throw new GraphValidationError('fork() needs a checkpointer to copy history from');
|
|
383
398
|
const history = (await store.history(threadId, Number.MAX_SAFE_INTEGER)).map((item) => migrateCheckpoint(item));
|
|
384
|
-
if (history.length === 0)
|
|
399
|
+
if (history.length === 0 || !ownedBy(history[0], options.tenantId)) {
|
|
385
400
|
throw new GraphThreadNotFoundError(threadId);
|
|
401
|
+
}
|
|
386
402
|
const cutoff = options.step ?? history[0].step;
|
|
387
403
|
const lineage = history.filter((item) => item.step <= cutoff).reverse();
|
|
388
404
|
if (lineage.at(-1)?.step !== cutoff) {
|
|
@@ -460,6 +476,16 @@ export class CompiledGraph {
|
|
|
460
476
|
const stopBefore = runOptions.interruptBefore ?? this.options.interruptBefore ?? [];
|
|
461
477
|
const stopAfter = runOptions.interruptAfter ?? this.options.interruptAfter ?? [];
|
|
462
478
|
let checkpoint = await seed();
|
|
479
|
+
const owner = tenantOf(checkpoint);
|
|
480
|
+
if (runOptions.tenantId !== undefined && owner !== undefined && owner !== runOptions.tenantId) {
|
|
481
|
+
throw new GraphThreadNotFoundError(threadId);
|
|
482
|
+
}
|
|
483
|
+
const tenantId = runOptions.tenantId ?? owner;
|
|
484
|
+
if (tenantId !== undefined) {
|
|
485
|
+
runOptions = { ...runOptions, tenantId, metadata: { ...runOptions.metadata, tenantId } };
|
|
486
|
+
if (owner === undefined)
|
|
487
|
+
checkpoint = { ...checkpoint, metadata: { ...checkpoint.metadata, tenantId } };
|
|
488
|
+
}
|
|
463
489
|
let passBreakpoint = checkpoint.breakpoint?.when === 'before';
|
|
464
490
|
if (checkpoint.breakpoint)
|
|
465
491
|
checkpoint = { ...checkpoint, breakpoint: undefined };
|
|
@@ -670,8 +696,8 @@ export class CompiledGraph {
|
|
|
670
696
|
attempts += 1;
|
|
671
697
|
notify(runOptions, { type: 'task_start', step, taskId: task.id, node: task.node, attempt: attempts });
|
|
672
698
|
const timer = attemptTimer(task.node, limits);
|
|
673
|
-
const
|
|
674
|
-
const signal =
|
|
699
|
+
const linked = linkSignals([stepSignal, runOptions.signal, timer?.signal]);
|
|
700
|
+
const signal = linked.signal;
|
|
675
701
|
try {
|
|
676
702
|
const running = this.runNode(definition.fn, task, step, threadId, checkpoint, runOptions, signal, attempts, timer?.touch);
|
|
677
703
|
if (timer)
|
|
@@ -743,6 +769,7 @@ export class CompiledGraph {
|
|
|
743
769
|
}
|
|
744
770
|
finally {
|
|
745
771
|
timer?.stop();
|
|
772
|
+
linked.dispose();
|
|
746
773
|
}
|
|
747
774
|
}
|
|
748
775
|
}
|
|
@@ -822,11 +849,18 @@ export class CompiledGraph {
|
|
|
822
849
|
node,
|
|
823
850
|
step,
|
|
824
851
|
threadId,
|
|
852
|
+
...(runOptions.tenantId !== undefined ? { tenantId: runOptions.tenantId } : {}),
|
|
825
853
|
taskId: task.id,
|
|
826
854
|
input: task.input,
|
|
827
855
|
attempt,
|
|
828
856
|
signal,
|
|
829
|
-
...(this.options.store
|
|
857
|
+
...(this.options.store
|
|
858
|
+
? {
|
|
859
|
+
store: runOptions.tenantId !== undefined
|
|
860
|
+
? tenantStore(this.options.store, runOptions.tenantId)
|
|
861
|
+
: this.options.store,
|
|
862
|
+
}
|
|
863
|
+
: {}),
|
|
830
864
|
interrupt: (request) => {
|
|
831
865
|
const index = interruptIndex++;
|
|
832
866
|
const key = interruptKey({ node, taskId: task.id, step, index });
|
|
@@ -976,9 +1010,9 @@ export class CompiledGraph {
|
|
|
976
1010
|
}
|
|
977
1011
|
return this.reduce(state, [input]);
|
|
978
1012
|
}
|
|
979
|
-
async requireCheckpoint(threadId) {
|
|
1013
|
+
async requireCheckpoint(threadId, tenantId) {
|
|
980
1014
|
const checkpoint = await this.checkpointer()?.get(threadId);
|
|
981
|
-
if (!checkpoint)
|
|
1015
|
+
if (!checkpoint || !ownedBy(checkpoint, tenantId))
|
|
982
1016
|
throw new GraphThreadNotFoundError(threadId);
|
|
983
1017
|
return migrateCheckpoint(checkpoint);
|
|
984
1018
|
}
|
|
@@ -1180,3 +1214,21 @@ function attemptTimer(node, limits) {
|
|
|
1180
1214
|
function isRetryable(error) {
|
|
1181
1215
|
return !(error instanceof GraphValidationError);
|
|
1182
1216
|
}
|
|
1217
|
+
function tenantOf(checkpoint) {
|
|
1218
|
+
const tenantId = checkpoint.metadata?.tenantId;
|
|
1219
|
+
return typeof tenantId === 'string' ? tenantId : undefined;
|
|
1220
|
+
}
|
|
1221
|
+
function ownedBy(checkpoint, tenantId) {
|
|
1222
|
+
if (tenantId === undefined)
|
|
1223
|
+
return true;
|
|
1224
|
+
const owner = tenantOf(checkpoint);
|
|
1225
|
+
return owner === undefined || owner === tenantId;
|
|
1226
|
+
}
|
|
1227
|
+
async function* releasing(source, release) {
|
|
1228
|
+
try {
|
|
1229
|
+
yield* source;
|
|
1230
|
+
}
|
|
1231
|
+
finally {
|
|
1232
|
+
release();
|
|
1233
|
+
}
|
|
1234
|
+
}
|
package/dist/images/evals.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
+
import { linkSignals } from '../utils/signals.js';
|
|
2
3
|
import { ImageValidationError } from './errors.js';
|
|
3
4
|
export class MemoryReviewQueue {
|
|
4
5
|
items = [];
|
|
@@ -32,7 +33,8 @@ export class MediaEvalRunner {
|
|
|
32
33
|
const runsOf = new Map(examples.map((example, index) => [example.id, runCounts[index]]));
|
|
33
34
|
const outcomes = new Map();
|
|
34
35
|
const stop = new AbortController();
|
|
35
|
-
const
|
|
36
|
+
const linked = linkSignals([stop.signal, runOptions.signal]);
|
|
37
|
+
const signal = linked.signal;
|
|
36
38
|
const { evaluate } = await import('../evaluate/run.js');
|
|
37
39
|
const experiment = await evaluate(async (evalCase, { example, run }) => {
|
|
38
40
|
try {
|
|
@@ -57,7 +59,7 @@ export class MediaEvalRunner {
|
|
|
57
59
|
signal,
|
|
58
60
|
...(runOptions.store ? { store: runOptions.store } : {}),
|
|
59
61
|
...(runOptions.metadata ? { metadata: runOptions.metadata } : {}),
|
|
60
|
-
});
|
|
62
|
+
}).finally(linked.dispose);
|
|
61
63
|
const caseReports = [];
|
|
62
64
|
const allScores = {};
|
|
63
65
|
const safety = { truePositives: 0, falsePositives: 0, trueNegatives: 0, falseNegatives: 0 };
|