nexus-ai-pro 2.0.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.
Files changed (282) hide show
  1. package/API_STABILITY.md +65 -0
  2. package/CHANGELOG.md +267 -1
  3. package/MIGRATING.md +1 -1
  4. package/README.md +17 -8
  5. package/dist/agent/create-agent.d.ts +9 -1
  6. package/dist/agent/create-agent.js +40 -1
  7. package/dist/cache/memory-cache.d.ts +8 -0
  8. package/dist/cache/memory-cache.js +9 -0
  9. package/dist/cache/semantic-cache.d.ts +6 -3
  10. package/dist/cache/semantic-cache.js +5 -2
  11. package/dist/cli/args.js +3 -0
  12. package/dist/cli/db.d.ts +16 -4
  13. package/dist/cli/db.js +169 -22
  14. package/dist/cli/doctor.d.ts +17 -0
  15. package/dist/cli/doctor.js +53 -0
  16. package/dist/cli/graph.d.ts +9 -0
  17. package/dist/cli/graph.js +47 -0
  18. package/dist/cli.js +21 -2
  19. package/dist/core/lifecycle.js +11 -6
  20. package/dist/core/nexus.d.ts +12 -2
  21. package/dist/core/nexus.js +106 -23
  22. package/dist/doctor/index.d.ts +90 -0
  23. package/dist/doctor/index.js +237 -0
  24. package/dist/evals/judge.d.ts +11 -0
  25. package/dist/evals/judge.js +33 -0
  26. package/dist/evaluate/index.d.ts +1 -0
  27. package/dist/evaluate/index.js +1 -0
  28. package/dist/evaluate/provenance.d.ts +22 -0
  29. package/dist/evaluate/provenance.js +10 -0
  30. package/dist/evaluate/run.js +20 -4
  31. package/dist/graph/durability.d.ts +27 -0
  32. package/dist/graph/durability.js +66 -0
  33. package/dist/graph/errors.d.ts +30 -4
  34. package/dist/graph/errors.js +18 -2
  35. package/dist/graph/event-stream.d.ts +75 -0
  36. package/dist/graph/event-stream.js +289 -0
  37. package/dist/graph/functional.d.ts +80 -7
  38. package/dist/graph/functional.js +138 -35
  39. package/dist/graph/graph.d.ts +56 -8
  40. package/dist/graph/graph.js +331 -49
  41. package/dist/graph/index.d.ts +4 -2
  42. package/dist/graph/index.js +3 -1
  43. package/dist/graph/lint.d.ts +57 -0
  44. package/dist/graph/lint.js +141 -0
  45. package/dist/graph/run-control.d.ts +28 -0
  46. package/dist/graph/run-control.js +26 -0
  47. package/dist/images/evals.js +4 -2
  48. package/dist/operations/adapters.d.ts +52 -4
  49. package/dist/operations/adapters.js +194 -7
  50. package/dist/operations/runner.js +14 -15
  51. package/dist/operations/stats.js +8 -1
  52. package/dist/operations/store.d.ts +5 -1
  53. package/dist/operations/store.js +7 -0
  54. package/dist/ops/circuit-breaker.d.ts +52 -0
  55. package/dist/ops/circuit-breaker.js +4 -0
  56. package/dist/ops/circuit-store.d.ts +13 -2
  57. package/dist/ops/circuit-store.js +68 -0
  58. package/dist/ops/circuit-sync.d.ts +17 -0
  59. package/dist/ops/circuit-sync.js +86 -0
  60. package/dist/ops/health.d.ts +24 -0
  61. package/dist/ops/health.js +38 -10
  62. package/dist/ops/index.d.ts +1 -1
  63. package/dist/ops/index.js +1 -1
  64. package/dist/ops/rate-limit-adapters.d.ts +23 -0
  65. package/dist/ops/rate-limit-adapters.js +34 -0
  66. package/dist/ops/rate-limiter.d.ts +14 -2
  67. package/dist/ops/rate-limiter.js +31 -0
  68. package/dist/postgres/circuits.d.ts +21 -4
  69. package/dist/postgres/circuits.js +65 -4
  70. package/dist/postgres/client.d.ts +0 -2
  71. package/dist/postgres/client.js +0 -4
  72. package/dist/postgres/evaluate.d.ts +16 -4
  73. package/dist/postgres/evaluate.js +17 -5
  74. package/dist/postgres/index.d.ts +32 -11
  75. package/dist/postgres/index.js +65 -31
  76. package/dist/postgres/migrations.d.ts +60 -0
  77. package/dist/postgres/migrations.js +121 -0
  78. package/dist/postgres/operations.d.ts +14 -3
  79. package/dist/postgres/operations.js +45 -15
  80. package/dist/postgres/prompts.d.ts +11 -2
  81. package/dist/postgres/prompts.js +14 -3
  82. package/dist/postgres/rollups.d.ts +35 -0
  83. package/dist/postgres/rollups.js +129 -0
  84. package/dist/postgres/store.d.ts +11 -2
  85. package/dist/postgres/store.js +14 -3
  86. package/dist/postgres/traces.d.ts +13 -3
  87. package/dist/postgres/traces.js +25 -3
  88. package/dist/postgres/vectors.d.ts +11 -2
  89. package/dist/postgres/vectors.js +14 -3
  90. package/dist/router/auto-router.d.ts +34 -7
  91. package/dist/router/auto-router.js +201 -68
  92. package/dist/server/assistant.d.ts +44 -0
  93. package/dist/server/assistant.js +39 -0
  94. package/dist/server/errors.d.ts +12 -0
  95. package/dist/server/errors.js +7 -0
  96. package/dist/server/index.d.ts +1 -1
  97. package/dist/server/index.js +1 -1
  98. package/dist/server/runs.d.ts +28 -1
  99. package/dist/server/runs.js +110 -14
  100. package/dist/sqlite/index.d.ts +16 -3
  101. package/dist/sqlite/index.js +16 -3
  102. package/dist/sqlite/migrations.d.ts +26 -0
  103. package/dist/sqlite/migrations.js +63 -0
  104. package/dist/sqlite/operations.d.ts +14 -3
  105. package/dist/sqlite/operations.js +42 -15
  106. package/dist/sqlite/store.d.ts +9 -2
  107. package/dist/sqlite/store.js +13 -3
  108. package/dist/sqlite/vectors.d.ts +11 -2
  109. package/dist/sqlite/vectors.js +13 -2
  110. package/dist/store/tenant.d.ts +12 -0
  111. package/dist/store/tenant.js +29 -0
  112. package/dist/tenancy/index.d.ts +113 -0
  113. package/dist/tenancy/index.js +233 -0
  114. package/dist/testing/record.d.ts +42 -0
  115. package/dist/testing/record.js +57 -5
  116. package/dist/tracing/index.d.ts +3 -3
  117. package/dist/tracing/index.js +2 -2
  118. package/dist/tracing/instrument.js +30 -0
  119. package/dist/tracing/otlp.d.ts +91 -0
  120. package/dist/tracing/otlp.js +124 -0
  121. package/dist/tracing/rollups.d.ts +100 -0
  122. package/dist/tracing/rollups.js +139 -0
  123. package/dist/tracing/stores.d.ts +17 -0
  124. package/dist/tracing/stores.js +23 -0
  125. package/dist/tracing/tracer.d.ts +50 -4
  126. package/dist/tracing/tracer.js +85 -6
  127. package/dist/types/config.d.ts +33 -2
  128. package/dist/types/evaluate.d.ts +37 -0
  129. package/dist/types/graph.d.ts +354 -9
  130. package/dist/types/graph.js +3 -1
  131. package/dist/types/messages.d.ts +5 -0
  132. package/dist/types/operations.d.ts +22 -2
  133. package/dist/types/response.d.ts +20 -0
  134. package/dist/types/server.d.ts +18 -0
  135. package/dist/types/tracing.d.ts +11 -0
  136. package/dist/utils/schema-migrations.d.ts +97 -0
  137. package/dist/utils/schema-migrations.js +91 -0
  138. package/dist/utils/signals.d.ts +17 -0
  139. package/dist/utils/signals.js +26 -0
  140. package/dist/utils/tenant.d.ts +14 -0
  141. package/dist/utils/tenant.js +16 -0
  142. package/dist/version.d.ts +5 -0
  143. package/dist/version.js +1 -0
  144. package/dist-cjs/agent/create-agent.d.ts +9 -1
  145. package/dist-cjs/agent/create-agent.js +40 -1
  146. package/dist-cjs/cache/memory-cache.d.ts +8 -0
  147. package/dist-cjs/cache/memory-cache.js +9 -0
  148. package/dist-cjs/cache/semantic-cache.d.ts +6 -3
  149. package/dist-cjs/cache/semantic-cache.js +5 -2
  150. package/dist-cjs/cli/args.js +3 -0
  151. package/dist-cjs/cli/db.d.ts +16 -4
  152. package/dist-cjs/cli/db.js +200 -20
  153. package/dist-cjs/cli/doctor.d.ts +17 -0
  154. package/dist-cjs/cli/doctor.js +56 -0
  155. package/dist-cjs/cli/graph.d.ts +9 -0
  156. package/dist-cjs/cli/graph.js +50 -0
  157. package/dist-cjs/core/lifecycle.js +11 -6
  158. package/dist-cjs/core/nexus.d.ts +12 -2
  159. package/dist-cjs/core/nexus.js +106 -23
  160. package/dist-cjs/doctor/index.d.ts +90 -0
  161. package/dist-cjs/doctor/index.js +276 -0
  162. package/dist-cjs/evals/judge.d.ts +11 -0
  163. package/dist-cjs/evals/judge.js +33 -0
  164. package/dist-cjs/evaluate/index.d.ts +1 -0
  165. package/dist-cjs/evaluate/index.js +3 -1
  166. package/dist-cjs/evaluate/provenance.d.ts +22 -0
  167. package/dist-cjs/evaluate/provenance.js +14 -0
  168. package/dist-cjs/evaluate/run.js +20 -4
  169. package/dist-cjs/graph/durability.d.ts +27 -0
  170. package/dist-cjs/graph/durability.js +70 -0
  171. package/dist-cjs/graph/errors.d.ts +30 -4
  172. package/dist-cjs/graph/errors.js +20 -3
  173. package/dist-cjs/graph/event-stream.d.ts +75 -0
  174. package/dist-cjs/graph/event-stream.js +295 -0
  175. package/dist-cjs/graph/functional.d.ts +80 -7
  176. package/dist-cjs/graph/functional.js +137 -34
  177. package/dist-cjs/graph/graph.d.ts +56 -8
  178. package/dist-cjs/graph/graph.js +330 -48
  179. package/dist-cjs/graph/index.d.ts +4 -2
  180. package/dist-cjs/graph/index.js +7 -1
  181. package/dist-cjs/graph/lint.d.ts +57 -0
  182. package/dist-cjs/graph/lint.js +144 -0
  183. package/dist-cjs/graph/run-control.d.ts +28 -0
  184. package/dist-cjs/graph/run-control.js +30 -0
  185. package/dist-cjs/images/evals.js +4 -2
  186. package/dist-cjs/operations/adapters.d.ts +52 -4
  187. package/dist-cjs/operations/adapters.js +193 -6
  188. package/dist-cjs/operations/runner.js +14 -15
  189. package/dist-cjs/operations/stats.js +8 -1
  190. package/dist-cjs/operations/store.d.ts +5 -1
  191. package/dist-cjs/operations/store.js +7 -0
  192. package/dist-cjs/ops/circuit-breaker.d.ts +52 -0
  193. package/dist-cjs/ops/circuit-breaker.js +4 -0
  194. package/dist-cjs/ops/circuit-store.d.ts +13 -2
  195. package/dist-cjs/ops/circuit-store.js +68 -0
  196. package/dist-cjs/ops/circuit-sync.d.ts +17 -0
  197. package/dist-cjs/ops/circuit-sync.js +87 -0
  198. package/dist-cjs/ops/health.d.ts +24 -0
  199. package/dist-cjs/ops/health.js +38 -10
  200. package/dist-cjs/ops/index.d.ts +1 -1
  201. package/dist-cjs/ops/rate-limit-adapters.d.ts +23 -0
  202. package/dist-cjs/ops/rate-limit-adapters.js +35 -1
  203. package/dist-cjs/ops/rate-limiter.d.ts +14 -2
  204. package/dist-cjs/ops/rate-limiter.js +32 -0
  205. package/dist-cjs/postgres/circuits.d.ts +21 -4
  206. package/dist-cjs/postgres/circuits.js +98 -3
  207. package/dist-cjs/postgres/client.d.ts +0 -2
  208. package/dist-cjs/postgres/client.js +0 -5
  209. package/dist-cjs/postgres/evaluate.d.ts +16 -4
  210. package/dist-cjs/postgres/evaluate.js +50 -4
  211. package/dist-cjs/postgres/index.d.ts +32 -11
  212. package/dist-cjs/postgres/index.js +69 -20
  213. package/dist-cjs/postgres/migrations.d.ts +60 -0
  214. package/dist-cjs/postgres/migrations.js +129 -0
  215. package/dist-cjs/postgres/operations.d.ts +14 -3
  216. package/dist-cjs/postgres/operations.js +78 -14
  217. package/dist-cjs/postgres/prompts.d.ts +11 -2
  218. package/dist-cjs/postgres/prompts.js +47 -2
  219. package/dist-cjs/postgres/rollups.d.ts +35 -0
  220. package/dist-cjs/postgres/rollups.js +168 -0
  221. package/dist-cjs/postgres/store.d.ts +11 -2
  222. package/dist-cjs/postgres/store.js +47 -2
  223. package/dist-cjs/postgres/traces.d.ts +13 -3
  224. package/dist-cjs/postgres/traces.js +58 -2
  225. package/dist-cjs/postgres/vectors.d.ts +11 -2
  226. package/dist-cjs/postgres/vectors.js +47 -2
  227. package/dist-cjs/router/auto-router.d.ts +34 -7
  228. package/dist-cjs/router/auto-router.js +201 -68
  229. package/dist-cjs/server/assistant.d.ts +44 -0
  230. package/dist-cjs/server/assistant.js +39 -0
  231. package/dist-cjs/server/errors.d.ts +12 -0
  232. package/dist-cjs/server/errors.js +9 -1
  233. package/dist-cjs/server/index.d.ts +1 -1
  234. package/dist-cjs/server/index.js +2 -1
  235. package/dist-cjs/server/runs.d.ts +28 -1
  236. package/dist-cjs/server/runs.js +109 -13
  237. package/dist-cjs/sqlite/index.d.ts +16 -3
  238. package/dist-cjs/sqlite/index.js +31 -10
  239. package/dist-cjs/sqlite/migrations.d.ts +26 -0
  240. package/dist-cjs/sqlite/migrations.js +71 -0
  241. package/dist-cjs/sqlite/operations.d.ts +14 -3
  242. package/dist-cjs/sqlite/operations.js +76 -15
  243. package/dist-cjs/sqlite/store.d.ts +9 -2
  244. package/dist-cjs/sqlite/store.js +47 -3
  245. package/dist-cjs/sqlite/vectors.d.ts +11 -2
  246. package/dist-cjs/sqlite/vectors.js +47 -2
  247. package/dist-cjs/store/tenant.d.ts +12 -0
  248. package/dist-cjs/store/tenant.js +33 -0
  249. package/dist-cjs/tenancy/index.d.ts +113 -0
  250. package/dist-cjs/tenancy/index.js +250 -0
  251. package/dist-cjs/testing/record.d.ts +42 -0
  252. package/dist-cjs/testing/record.js +57 -5
  253. package/dist-cjs/tracing/index.d.ts +3 -3
  254. package/dist-cjs/tracing/index.js +4 -1
  255. package/dist-cjs/tracing/instrument.js +30 -0
  256. package/dist-cjs/tracing/otlp.d.ts +91 -0
  257. package/dist-cjs/tracing/otlp.js +129 -0
  258. package/dist-cjs/tracing/rollups.d.ts +100 -0
  259. package/dist-cjs/tracing/rollups.js +147 -0
  260. package/dist-cjs/tracing/stores.d.ts +17 -0
  261. package/dist-cjs/tracing/stores.js +24 -0
  262. package/dist-cjs/tracing/tracer.d.ts +50 -4
  263. package/dist-cjs/tracing/tracer.js +87 -6
  264. package/dist-cjs/types/config.d.ts +33 -2
  265. package/dist-cjs/types/evaluate.d.ts +37 -0
  266. package/dist-cjs/types/graph.d.ts +354 -9
  267. package/dist-cjs/types/graph.js +3 -1
  268. package/dist-cjs/types/messages.d.ts +5 -0
  269. package/dist-cjs/types/operations.d.ts +22 -2
  270. package/dist-cjs/types/response.d.ts +20 -0
  271. package/dist-cjs/types/server.d.ts +18 -0
  272. package/dist-cjs/types/tracing.d.ts +11 -0
  273. package/dist-cjs/utils/schema-migrations.d.ts +97 -0
  274. package/dist-cjs/utils/schema-migrations.js +102 -0
  275. package/dist-cjs/utils/signals.d.ts +17 -0
  276. package/dist-cjs/utils/signals.js +29 -0
  277. package/dist-cjs/utils/tenant.d.ts +14 -0
  278. package/dist-cjs/utils/tenant.js +22 -0
  279. package/dist-cjs/version.d.ts +5 -0
  280. package/dist-cjs/version.js +4 -0
  281. package/llms.txt +4 -3
  282. package/package.json +110 -2
@@ -1,5 +1,7 @@
1
1
  import { randomBytes } from 'node:crypto';
2
+ import { NEXUS_VERSION } from '../version.js';
2
3
  import { evaluationCacheKey } from './cache.js';
4
+ import { declaredProvenance } from './provenance.js';
3
5
  export async function evaluate(target, dataset, evaluators, options = {}) {
4
6
  if (dataset.examples.length === 0)
5
7
  throw new RangeError(`Dataset "${dataset.name}" has no examples`);
@@ -14,8 +16,9 @@ export async function evaluate(target, dataset, evaluators, options = {}) {
14
16
  example,
15
17
  run,
16
18
  })));
19
+ const produced = evaluators.map(() => new Set());
17
20
  await runConcurrently(jobs, options.concurrency ?? 4, async ({ example, run }) => {
18
- const result = await evaluateOne(target, example, run, evaluators, options);
21
+ const result = await evaluateOne(target, example, run, evaluators, options, produced);
19
22
  results.push(result);
20
23
  options.onResult?.(result);
21
24
  }, options.signal);
@@ -35,6 +38,16 @@ export async function evaluate(target, dataset, evaluators, options = {}) {
35
38
  results,
36
39
  metrics: summarize(results),
37
40
  summary,
41
+ evaluators: evaluators.map((evaluator, position) => {
42
+ const declared = declaredProvenance(evaluator);
43
+ return {
44
+ ...declared,
45
+ position,
46
+ name: declared?.name ?? (evaluator.name || `evaluator-${position}`),
47
+ keys: [...(produced[position] ?? [])],
48
+ };
49
+ }),
50
+ framework: { name: 'nexus-ai-pro', version: NEXUS_VERSION },
38
51
  ...(options.metadata ? { metadata: options.metadata } : {}),
39
52
  ...(options.cache
40
53
  ? {
@@ -57,7 +70,7 @@ function repetitionsOf(example, setting) {
57
70
  }
58
71
  return count;
59
72
  }
60
- async function evaluateOne(target, example, run, evaluators, options) {
73
+ async function evaluateOne(target, example, run, evaluators, options, produced) {
61
74
  const started = Date.now();
62
75
  let output;
63
76
  let error;
@@ -97,9 +110,12 @@ async function evaluateOne(target, example, run, evaluators, options) {
97
110
  });
98
111
  }
99
112
  const scores = [];
100
- for (const evaluator of evaluators) {
113
+ for (const [position, evaluator] of evaluators.entries()) {
101
114
  try {
102
- scores.push(...normalizeScores(await evaluator(context), evaluator));
115
+ const made = normalizeScores(await evaluator(context), evaluator);
116
+ for (const score of made)
117
+ produced[position]?.add(score.key);
118
+ scores.push(...made);
103
119
  }
104
120
  catch (caught) {
105
121
  scores.push({
@@ -0,0 +1,27 @@
1
+ import type { DurabilityMode } from '../types/graph.js';
2
+ /**
3
+ * Writes one run's checkpoints under a durability mode.
4
+ *
5
+ * `sync` writes each one before returning. `async` chains writes behind each other, so they land in
6
+ * order, and makes the run wait only when `maxPending` are already in flight. `exit` keeps only the
7
+ * latest and writes it when the run stops. A write that fails is kept and thrown from the next call,
8
+ * so a background failure still fails the run.
9
+ */
10
+ export declare class CheckpointWriter<T> {
11
+ private readonly write;
12
+ private readonly mode;
13
+ private readonly maxPending;
14
+ private chain;
15
+ private readonly inFlight;
16
+ private failure;
17
+ private unwritten;
18
+ constructor(write: (checkpoint: T) => Promise<void>, mode: DurabilityMode, maxPending?: number);
19
+ /**
20
+ * Hands over a checkpoint. A `durable` one is persisted, with everything before it, before this
21
+ * resolves, whatever the mode: a pause, a failure, the end of a run, or a recovery decision.
22
+ */
23
+ persist(checkpoint: T, durable?: boolean): Promise<void>;
24
+ /** Waits for every write handed over so far, and writes what `exit` mode was holding. */
25
+ flush(): Promise<void>;
26
+ private throwFailure;
27
+ }
@@ -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
+ }
@@ -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 `timeoutMs`.
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 timeout it exceeded, in milliseconds. */
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 timeout it exceeded, in milliseconds. */
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.
@@ -71,13 +71,29 @@ export class GraphNotInterruptedError extends GraphError {
71
71
  export class GraphNodeTimeoutError extends GraphError {
72
72
  node;
73
73
  timeoutMs;
74
- constructor(node, timeoutMs) {
75
- super(`Graph node "${node}" exceeded its ${timeoutMs}ms timeout`, 'GRAPH_NODE_TIMEOUT');
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
+ }