nexus-ai-pro 2.11.0 → 2.12.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 +44 -0
- package/CHANGELOG.md +117 -1
- package/README.md +9 -5
- package/dist/evaluate/automation.d.ts +256 -0
- package/dist/evaluate/automation.js +368 -0
- package/dist/evaluate/index.d.ts +2 -1
- package/dist/evaluate/index.js +1 -0
- package/dist/evaluate/media.d.ts +146 -0
- package/dist/evaluate/media.js +447 -0
- package/dist/evaluate/online.d.ts +25 -0
- package/dist/evaluate/online.js +29 -3
- package/dist/evaluate/trace.d.ts +21 -0
- package/dist/evaluate/trace.js +162 -0
- package/dist/media/catalog.d.ts +239 -0
- package/dist/media/catalog.js +319 -0
- package/dist/server/deployments.d.ts +109 -7
- package/dist/server/deployments.js +221 -47
- package/dist/server/runs.d.ts +8 -0
- package/dist/server/runs.js +38 -9
- package/dist/tracing/index.d.ts +1 -1
- package/dist/tracing/index.js +1 -1
- package/dist/tracing/instrument.d.ts +16 -0
- package/dist/tracing/instrument.js +29 -0
- package/dist/tracing/otlp-grpc.d.ts +61 -0
- package/dist/tracing/otlp-grpc.js +229 -0
- package/dist/tracing/otlp-proto.d.ts +24 -0
- package/dist/tracing/otlp-proto.js +192 -0
- package/dist/tracing/otlp.d.ts +69 -10
- package/dist/tracing/otlp.js +65 -14
- package/dist/tracing/spool.d.ts +111 -0
- package/dist/tracing/spool.js +245 -0
- package/dist/tracing/tracer.d.ts +8 -0
- package/dist/tracing/tracer.js +8 -1
- package/dist/types/evaluate.d.ts +106 -0
- package/dist/types/server.d.ts +29 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/evaluate/automation.d.ts +256 -0
- package/dist-cjs/evaluate/automation.js +377 -0
- package/dist-cjs/evaluate/index.d.ts +2 -1
- package/dist-cjs/evaluate/index.js +4 -1
- package/dist-cjs/evaluate/media.d.ts +146 -0
- package/dist-cjs/evaluate/media.js +453 -0
- package/dist-cjs/evaluate/online.d.ts +25 -0
- package/dist-cjs/evaluate/online.js +29 -3
- package/dist-cjs/evaluate/trace.d.ts +21 -0
- package/dist-cjs/evaluate/trace.js +167 -0
- package/dist-cjs/media/catalog.d.ts +239 -0
- package/dist-cjs/media/catalog.js +323 -0
- package/dist-cjs/server/deployments.d.ts +109 -7
- package/dist-cjs/server/deployments.js +221 -47
- package/dist-cjs/server/runs.d.ts +8 -0
- package/dist-cjs/server/runs.js +38 -9
- package/dist-cjs/tracing/index.d.ts +1 -1
- package/dist-cjs/tracing/index.js +2 -1
- package/dist-cjs/tracing/instrument.d.ts +16 -0
- package/dist-cjs/tracing/instrument.js +30 -0
- package/dist-cjs/tracing/otlp-grpc.d.ts +61 -0
- package/dist-cjs/tracing/otlp-grpc.js +237 -0
- package/dist-cjs/tracing/otlp-proto.d.ts +24 -0
- package/dist-cjs/tracing/otlp-proto.js +196 -0
- package/dist-cjs/tracing/otlp.d.ts +69 -10
- package/dist-cjs/tracing/otlp.js +69 -15
- package/dist-cjs/tracing/spool.d.ts +111 -0
- package/dist-cjs/tracing/spool.js +248 -0
- package/dist-cjs/tracing/tracer.d.ts +8 -0
- package/dist-cjs/tracing/tracer.js +8 -1
- package/dist-cjs/types/evaluate.d.ts +106 -0
- package/dist-cjs/types/server.d.ts +29 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +1 -1
- package/package.json +66 -1
package/API_STABILITY.md
CHANGED
|
@@ -41,6 +41,50 @@ Both are covered by this policy:
|
|
|
41
41
|
Deep imports into `dist`, `dist-cjs`, or `src` are not supported. Import only from `nexus-ai-pro` or
|
|
42
42
|
one of its explicit subpaths.
|
|
43
43
|
|
|
44
|
+
## 2.12 stage (2.12.0)
|
|
45
|
+
|
|
46
|
+
Everything 2.12 adds is stable from the start and additive, unless this section says otherwise.
|
|
47
|
+
|
|
48
|
+
New and stable:
|
|
49
|
+
- `nexus-ai-pro/tracing/spool`: `spoolingExporter()`, `SpoolingExporter`, `SpoolingExporterOptions`,
|
|
50
|
+
`TraceBatchSender`, `SpoolStats`, and `SpoolDrainReport`;
|
|
51
|
+
- `OtlpTraceExporter.send()`;
|
|
52
|
+
- `RunManagerOptions.tracer` and `AgentServerOptions.tracer`, and `RunRecord.traceId`;
|
|
53
|
+
- `Deployments.evaluationWorker()` and `DeploymentEvaluationWorkerOptions`; `RunEvaluationOptions`
|
|
54
|
+
gains `traces`, `operations`, `pollMs`, and the worker's options, and `RunEvaluationReport` gains
|
|
55
|
+
`failed`;
|
|
56
|
+
- in `nexus-ai-pro/evaluate`: `traceEvidence()`, `mediaParts()`, `EvaluationTrace`, `MediaPart`,
|
|
57
|
+
`TraceToolCall`, `TraceRetrieval`, `TraceDocument`, `TraceModelCall`, `TraceMedia`, and
|
|
58
|
+
`EvaluationContext.trace`;
|
|
59
|
+
- `OnlineEvaluationWorkerOptions.withTrace`, `onScored`, and `onError`, and `OnlineScored`;
|
|
60
|
+
- `traceRetriever()`, `RetrieverLike`, and `Tracer.within()`;
|
|
61
|
+
- `nexus-ai-pro/evaluate/automation`: `automationRules()`, `deploymentScores()`, `traceScores()`,
|
|
62
|
+
`memoryScores()`, `openReview()`, `sendWebhook()`, `markUnhealthy()`, `AutomationRule`,
|
|
63
|
+
`AutomationRules`, `AutomationRulesOptions`, `AutomationEvent`, `AutomationAction`,
|
|
64
|
+
`AutomationStatus`, `AutomationWebhookOptions`, `ScoreSample`, `ScoreSource`, `MemoryScoreSource`,
|
|
65
|
+
`ReviewOpener`, and `RuleStatistic`;
|
|
66
|
+
- `Deployments.markUnhealthy()`, `markHealthy()`, and `scores()`, `RunScore`, the `health`
|
|
67
|
+
`DeploymentChange`, `DeploymentRecord.unhealthy`, `RevisionHealthMark`,
|
|
68
|
+
`DeploymentChangeRecord.revision` and `healthy`, and `CanaryDecision.unhealthy`;
|
|
69
|
+
- `nexus-ai-pro/evaluate/media`: `mediaChecks()`, `mediaJudge()`, `mediaOf()`, `resolveMedia()`,
|
|
70
|
+
`MediaCheckOptions`, `MediaJudgeOptions`, `MediaResolveOptions`, `MediaSelection`, `MediaRange`,
|
|
71
|
+
`ResolvedMedia`, and `MediaSubjectKind`;
|
|
72
|
+
- `nexus-ai-pro/media/catalog`: `mediaCatalog()`, `catalogedAssetStore()`, `MediaCatalog`,
|
|
73
|
+
`MediaCatalogOptions`, `MediaCatalogEntry`, `MediaCatalogQuery`, `MediaCatalogPage`,
|
|
74
|
+
`MediaCatalogStats`, `MediaCatalogRecordOptions`, `CatalogScore`, `CatalogMediaKind`, and
|
|
75
|
+
`CatalogedAssetStoreOptions`;
|
|
76
|
+
- `nexus-ai-pro/tracing/otlp-grpc`: `OtlpGrpcTraceExporter`, `OtlpGrpcTraceExporterOptions`, and
|
|
77
|
+
`OtlpGrpcError`;
|
|
78
|
+
- in `nexus-ai-pro/tracing/otlp`: `OtlpTraceExporterOptions.protocol`, `encodeOtlpTraceRequest()`,
|
|
79
|
+
`decodeOtlpTraceResponse()`, `OtlpExportRequest`, `OtlpKeyValue`, `OtlpAttributeValue`,
|
|
80
|
+
`OtlpPartialSuccess`, and `OtlpPartialSuccessError`.
|
|
81
|
+
|
|
82
|
+
Changed: `DeploymentChange` gains a `health` action, so a `switch` over its actions that must be
|
|
83
|
+
exhaustive needs a case for it.
|
|
84
|
+
|
|
85
|
+
Changed: `Deployments.evaluate()` runs through the online-evaluation worker, scoring up to 4 runs at
|
|
86
|
+
once rather than one, so evaluators that are not safe to call concurrently need `concurrency: 1`.
|
|
87
|
+
|
|
44
88
|
## 2.11 stage (2.11.0)
|
|
45
89
|
|
|
46
90
|
Everything 2.11 adds is stable from the start and additive, unless this section says otherwise.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,121 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.12.0] - 2026-10-18
|
|
8
|
+
|
|
9
|
+
Observability that loses nothing, and media you can evaluate.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Spans that a collector being down cannot lose.** `spoolingExporter()`, on the new
|
|
14
|
+
`nexus-ai-pro/tracing/spool`, keeps each batch the collector refuses in an operation store (the
|
|
15
|
+
memory, Redis, Postgres, or SQLite one), retries it with backoff, and dead-letters it only once it is
|
|
16
|
+
too old or has failed too often, under a bound on what waits. Every replica on the store drains the
|
|
17
|
+
same spool, each batch once. `writeAhead` spools each batch before sending it. `stats()` reports
|
|
18
|
+
what was exported, retried, spooled, dropped, and dead-lettered, and the age of the oldest batch
|
|
19
|
+
waiting.
|
|
20
|
+
- `OtlpTraceExporter.send()`: sends runs as one batch now, and rejects when the collector refuses
|
|
21
|
+
them.
|
|
22
|
+
- **A deployment judged by what its runs did.** An agent server given a `tracer` makes each run the
|
|
23
|
+
root of a trace, with its input and output, and everything the assistant traces through the same
|
|
24
|
+
tracer beneath it; the run records the trace as `RunRecord.traceId`. `Deployments.evaluate()`
|
|
25
|
+
given those `traces` hands each evaluator the run's input and an `EvaluationTrace` of its tool
|
|
26
|
+
calls, retrievals, model calls, and media, read by the new `traceEvidence()`, which `mediaParts()`
|
|
27
|
+
backs. An assistant that traces itself is found by the run id it records.
|
|
28
|
+
- `Deployments.evaluationWorker()`: the online-evaluation worker over a deployment's runs, to start
|
|
29
|
+
on every replica, scoring each run once between them as it finishes.
|
|
30
|
+
- `onlineEvaluationWorker()` takes `withTrace`, which gives evaluators `context.trace`, and
|
|
31
|
+
`onScored`, which hears each run's scores once they are written, with `onError` for its failures.
|
|
32
|
+
- `traceRetriever()`: every retrieval as a `retriever` run, with the query and the documents.
|
|
33
|
+
- **Media you can evaluate.** On the new `nexus-ai-pro/evaluate/media`, `mediaChecks()` reads the
|
|
34
|
+
media an evaluation is about, from bytes, base64, a data URL, a URL it is allowed to fetch, a path,
|
|
35
|
+
or an asset store, and scores each check asked: the count, kind, MIME type, size, dimensions,
|
|
36
|
+
aspect ratio, and a WAV's duration. `mediaJudge()` shows them to a vision or audio model beside a
|
|
37
|
+
rubric and the brief, a stored asset sent as its bytes, and records the judge in an experiment.
|
|
38
|
+
`mediaOf()` finds the media in an output, or in what the run's trace says it produced, and
|
|
39
|
+
`resolveMedia()` reads one.
|
|
40
|
+
- **Automation rules.** On the new `nexus-ai-pro/evaluate/automation`, `automationRules()` judges
|
|
41
|
+
a score over each revision's most recent runs, and when it falls below a threshold over enough
|
|
42
|
+
samples (optionally by the upper bound of a confidence interval), acts once, across every replica
|
|
43
|
+
sharing its `state`: `openReview()` sends the worst runs to people, `sendWebhook()` posts a signed
|
|
44
|
+
event, and `markUnhealthy()` marks the revision unhealthy, which rolls a canary back. Rules read
|
|
45
|
+
scores a deployment's run records hold (`deploymentScores()`), a trace store's
|
|
46
|
+
(`traceScores()`), or this process's (`memoryScores()`), and `observe()` judges them as an online
|
|
47
|
+
evaluation writes them. A rule fires once per fall, recovers, and can repeat.
|
|
48
|
+
- **A revision's health.** `Deployments.markUnhealthy()` and `markHealthy()`: a `health` change in
|
|
49
|
+
the deployment's history, kept in `DeploymentRecord.unhealthy`. A canary marked unhealthy is rolled
|
|
50
|
+
back at once, and a canary guard rolls back any canary marked since it began, its `CanaryDecision`
|
|
51
|
+
carrying the mark. The studio shows each revision's health and marks one from the deployment view.
|
|
52
|
+
`Deployments.scores()` reads the scores of an assistant's runs.
|
|
53
|
+
- **A catalog of every stored asset.** `mediaCatalog()`, on the new `nexus-ai-pro/media/catalog`,
|
|
54
|
+
keeps one record of each stored asset in any long-term store (Postgres, SQLite, Redis, or memory):
|
|
55
|
+
what it is, who made it, for which run, its tags, and its latest score per key, written by
|
|
56
|
+
compare-and-set. `catalogedAssetStore()` wraps any asset store so every asset stored through it is
|
|
57
|
+
recorded and every one deleted is forgotten, an entry expiring with its asset. `list()` finds
|
|
58
|
+
entries by kind, type, provider, model, run, store, tag, time, and score, a page at a time;
|
|
59
|
+
`attach()` scores the assets a run's output refers to; `stats()`, `purgeExpired()`, and
|
|
60
|
+
`reconcile()` keep account.
|
|
61
|
+
- **OTLP over gRPC.** `OtlpGrpcTraceExporter`, on the new `nexus-ai-pro/tracing/otlp-grpc`, sends
|
|
62
|
+
spans to a collector's port 4317 with no gRPC library: the trace service's `Export` call framed by
|
|
63
|
+
hand over Node's HTTP/2 client, gzip-compressed, with metadata, a deadline, TLS options, one
|
|
64
|
+
connection kept and reopened, and a failed call rejecting with an `OtlpGrpcError` so a spool keeps
|
|
65
|
+
the batch. `OtlpTraceExporter` takes `protocol: 'http/protobuf'`, and both read a collector's
|
|
66
|
+
partial success into an `OtlpPartialSuccessError` for `onError`. `encodeOtlpTraceRequest()` and
|
|
67
|
+
`decodeOtlpTraceResponse()` are OTLP's protobuf encoding, written by hand, on any runtime.
|
|
68
|
+
- `Tracer.within()`: runs a function with a run started by `startRun()` in scope.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- `Deployments.evaluate()` runs through the online-evaluation worker, drained once, instead of beside
|
|
73
|
+
it. Replicas given one `operations` store score each run once between them, even calling at the
|
|
74
|
+
same moment, where before two calls could score a run twice; it takes the worker's budget, review
|
|
75
|
+
queue, and concurrency (4 at a time by default, one before), and `RunEvaluationReport` counts
|
|
76
|
+
`failed` evaluations. Which runs a `sampleRate` picks is unchanged.
|
|
77
|
+
|
|
78
|
+
### Proof
|
|
79
|
+
|
|
80
|
+
- `tests/trace-spool.test.ts`: a collector stopped for an hour while a span finishes every second,
|
|
81
|
+
and the process restarted halfway: every one of the 3,600 spans reaches it, once, on the memory,
|
|
82
|
+
SQLite, Postgres, and Redis operation stores. The bound, dead letters, two replicas on one spool,
|
|
83
|
+
and write-ahead through a process that dies mid-send. Without the spool, its bound, its claim, its
|
|
84
|
+
dead letters, write-ahead, or taking over a lapsed lease, a test fails.
|
|
85
|
+
- `tests/media-evaluation.test.ts`: checks that read PNG, GIF, WAV, and PDF media from a store,
|
|
86
|
+
base64, a data URL, and a fetch only when allowed; a judge model shown a stored asset as its bytes
|
|
87
|
+
beside the rubric and the brief; media found through the trace; and an experiment over a target
|
|
88
|
+
that draws. Without the sniffing, the store, the wildcard, the tolerance, the duration, the size
|
|
89
|
+
limit, the count, the media shown, the brief, or the trace fallback, a test fails.
|
|
90
|
+
- `tests/automation-rules.test.ts`: the proof that a canary is rolled back on a media evaluator's
|
|
91
|
+
score. A designer assistant's canary draws its banners square; two replicas score 80 runs with a
|
|
92
|
+
media judge shown each banner, and a rule on the judge's score marks the canary unhealthy, rolling
|
|
93
|
+
it back, opening review items for the five worst banners, and sending one signed webhook, once
|
|
94
|
+
between the replicas. The live revision is untouched, new runs go to it, and the media catalog
|
|
95
|
+
finds each of the canary's banners by its score. Then minimum samples, scopes, windows, the
|
|
96
|
+
confidence bound, the pass rate, recovery, repeats, filters, sources, failing actions, and a guard
|
|
97
|
+
rolling back a canary a rule only marked. Without the rollback, the guard's mark, the stale-mark
|
|
98
|
+
check, the claim, the minimum, the window, recovery, the bound, counting each run once, the
|
|
99
|
+
revision scope, the signature, or the repeat, a test fails.
|
|
100
|
+
- `tests/media-catalog.test.ts`: on the memory, SQLite, Postgres, and Redis stores, forty assets of
|
|
101
|
+
five kinds, two providers, and two tenants, some deleted and some expiring: every page lists each
|
|
102
|
+
remaining asset once, every filter finds exactly its assets, expired ones are never read and are
|
|
103
|
+
purged with their assets, and ten evaluators scoring one asset at once keep all ten scores. An
|
|
104
|
+
asset deleted around the catalog is reconciled away, and one the catalog cannot record is not left
|
|
105
|
+
stored. Without the forgetting, the expiry, the purge, compare-and-set, the cursor, the filters,
|
|
106
|
+
the compensation, or the reconciliation, a test fails.
|
|
107
|
+
- `tests/otlp-grpc.test.ts`: the OpenTelemetry Collector itself, in a container, takes spans over
|
|
108
|
+
gRPC and sends every one on as it came, ids, times, kinds, attributes, and status. Against a gRPC
|
|
109
|
+
server of the test's, with a protobuf reader written apart from the encoder: compression and
|
|
110
|
+
metadata, a refusal, a deadline, a partial success, a collector that is down, one that restarts
|
|
111
|
+
under a live connection, and a spool that keeps what is refused. Without the compressed flag, the
|
|
112
|
+
status, the deadline, the metadata, the parent span, the status message, typed values, the
|
|
113
|
+
reconnection, or the retry on a dropped connection, a test fails.
|
|
114
|
+
- `tests/deployment-trace-evaluation.test.ts`: a traced server whose assistant retrieves a policy,
|
|
115
|
+
calls a tool, and asks a model about a photo; its evaluators see the input, the tool call, the
|
|
116
|
+
documents, the model call, and the photo once. Two replicas evaluating at once over one operation
|
|
117
|
+
store score each of 40 runs once, where queues of their own score runs twice. A worker scores runs
|
|
118
|
+
as they finish and hears each score once, and an assistant tracing itself is found. Without the
|
|
119
|
+
trace, the input, the run scope, the shared queue, the fallback, the media deduplication, or the
|
|
120
|
+
trace written before the run settles, a test fails.
|
|
121
|
+
|
|
7
122
|
## [2.11.0] - 2026-10-18
|
|
8
123
|
|
|
9
124
|
Workspaces, and telephony in full.
|
|
@@ -2764,7 +2879,8 @@ shared policy vocabulary is in place for a future release that revisits this.
|
|
|
2764
2879
|
- URL fetching rejects unsafe private-network targets and limits response reads.
|
|
2765
2880
|
- CLI and audit findings no longer disclose detected secret values.
|
|
2766
2881
|
|
|
2767
|
-
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.
|
|
2882
|
+
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.12.0...HEAD
|
|
2883
|
+
[2.12.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.11.0...v2.12.0
|
|
2768
2884
|
[2.11.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.10.0...v2.11.0
|
|
2769
2885
|
[2.10.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.9.0...v2.10.0
|
|
2770
2886
|
[2.9.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.8.0...v2.9.0
|
package/README.md
CHANGED
|
@@ -303,14 +303,18 @@ Shipped so far:
|
|
|
303
303
|
- 2.11 workspaces, and telephony in full: an agent's files apart from where its code runs, in memory,
|
|
304
304
|
on disk, in any long-term store, read-only, or a composite of several; and a second phone provider,
|
|
305
305
|
Telnyx, beside Twilio, both answering, transferring, conferencing, recording under a retention
|
|
306
|
-
policy, and sending and receiving messages
|
|
306
|
+
policy, and sending and receiving messages;
|
|
307
|
+
- 2.12 observability that loses nothing, and media you can evaluate: spans spooled through a collector
|
|
308
|
+
outage, OTLP over gRPC, deployment evaluation that reads each run's whole trace, evaluators that
|
|
309
|
+
read and judge images, documents, audio, and video, a catalog of every stored asset, and automation
|
|
310
|
+
rules that roll a canary back when a score falls.
|
|
307
311
|
|
|
308
312
|
Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
|
|
309
313
|
[MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
|
|
310
|
-
Next, 2.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
+
Next, 2.13 runs agents across regions, keeps each tenant's requests and data in the region they
|
|
315
|
+
require, and resolves provider keys per tenant from a secret manager. Pinecone, images, the Gemini
|
|
316
|
+
embedding batch tier, media understanding, video, Gemini Live, SIP, and both phone providers graduate
|
|
317
|
+
once the live workflow passes against each. The
|
|
314
318
|
[roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) lists what comes after, in the order
|
|
315
319
|
applications ask for it.
|
|
316
320
|
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automation rules: when a score falls below a threshold over enough samples, act. Open review items
|
|
3
|
+
* for people, send a webhook, or mark a deployment's revision unhealthy, which rolls a canary back.
|
|
4
|
+
*
|
|
5
|
+
* Rules read scores from a shared source, such as a deployment's run records, so every replica judges
|
|
6
|
+
* the same scores, and a firing is claimed in a shared store, so it acts once however many replicas
|
|
7
|
+
* see it.
|
|
8
|
+
*/
|
|
9
|
+
import type { OnlineScored } from './online.js';
|
|
10
|
+
import type { Store } from '../types/store.js';
|
|
11
|
+
import type { RunQuery, TraceStore } from '../types/tracing.js';
|
|
12
|
+
/** One score, as automation rules judge them. */
|
|
13
|
+
export interface ScoreSample {
|
|
14
|
+
/** The score's key. */
|
|
15
|
+
key: string;
|
|
16
|
+
/** The score. */
|
|
17
|
+
score: number;
|
|
18
|
+
/** Whether it passed, when its evaluator judged. */
|
|
19
|
+
passed?: boolean;
|
|
20
|
+
/** The run it was given to. */
|
|
21
|
+
runId: string;
|
|
22
|
+
/** ISO-8601 time it was given. */
|
|
23
|
+
at: string;
|
|
24
|
+
/** Who gave it. */
|
|
25
|
+
source?: string;
|
|
26
|
+
/** The assistant that ran. */
|
|
27
|
+
assistant?: string;
|
|
28
|
+
/** The revision that served the run. */
|
|
29
|
+
revision?: string;
|
|
30
|
+
/** The run's tenant. */
|
|
31
|
+
tenantId?: string;
|
|
32
|
+
/** What the run received, for a person reviewing it. */
|
|
33
|
+
inputs?: unknown;
|
|
34
|
+
/** What the run produced. */
|
|
35
|
+
output?: unknown;
|
|
36
|
+
}
|
|
37
|
+
/** Where rules read scores: any replica reading the same source judges the same scores. */
|
|
38
|
+
export interface ScoreSource {
|
|
39
|
+
/** The most recent scores of one key, newest first, at most `limit`, and none older than `since`. */
|
|
40
|
+
samples(query: {
|
|
41
|
+
key: string;
|
|
42
|
+
since?: string;
|
|
43
|
+
limit: number;
|
|
44
|
+
}): Promise<ScoreSample[]> | ScoreSample[];
|
|
45
|
+
}
|
|
46
|
+
/** The scores of one assistant's runs, from its deployment's run records: shared by every replica. */
|
|
47
|
+
export declare function deploymentScores(deployments: {
|
|
48
|
+
scores(assistant: string, options: {
|
|
49
|
+
key?: string;
|
|
50
|
+
since?: string;
|
|
51
|
+
limit?: number;
|
|
52
|
+
}): Promise<Array<Omit<ScoreSample, 'passed' | 'inputs'>>>;
|
|
53
|
+
}, assistant: string): ScoreSource;
|
|
54
|
+
/**
|
|
55
|
+
* The scores recorded on a trace store's runs, as an online evaluation writes them: shared by every
|
|
56
|
+
* replica that reads the store. `query` narrows the runs, such as `{ kind: 'image' }`.
|
|
57
|
+
*/
|
|
58
|
+
export declare function traceScores(traces: TraceStore, query?: RunQuery): ScoreSource;
|
|
59
|
+
/** Scores held in this process, fed by `observe()` or `record()`: for one process, or a test. */
|
|
60
|
+
export interface MemoryScoreSource extends ScoreSource {
|
|
61
|
+
/** Keeps one score. */
|
|
62
|
+
record(sample: ScoreSample): void;
|
|
63
|
+
/** Keeps a run's scores as an online evaluation's `onScored` hears them. */
|
|
64
|
+
observe(scored: OnlineScored): void;
|
|
65
|
+
}
|
|
66
|
+
/** A score source in this process, keeping the latest `max` scores (10,000). */
|
|
67
|
+
export declare function memoryScores(options?: {
|
|
68
|
+
max?: number;
|
|
69
|
+
}): MemoryScoreSource;
|
|
70
|
+
/** What a rule judges a scope's samples by. */
|
|
71
|
+
export type RuleStatistic = 'mean' | 'pass-rate';
|
|
72
|
+
/** A rule: when `key` falls below `below` over enough samples, act. */
|
|
73
|
+
export interface AutomationRule {
|
|
74
|
+
/** Names the rule in events, in the shared state, and as `rule:<name>` in a deployment's history. */
|
|
75
|
+
name: string;
|
|
76
|
+
/** The score key judged, such as `banner` from a media judge. */
|
|
77
|
+
key: string;
|
|
78
|
+
/** Fires when the statistic is below this. */
|
|
79
|
+
below: number;
|
|
80
|
+
/** The mean score, the default, or the share that passed: `passed`, or a score of at least 0.5. */
|
|
81
|
+
statistic?: RuleStatistic;
|
|
82
|
+
/** Samples a scope needs before it is judged, each run counted once. Defaults to 20. */
|
|
83
|
+
minSamples?: number;
|
|
84
|
+
/** The most recent runs judged per scope. Defaults to 100. */
|
|
85
|
+
window?: number;
|
|
86
|
+
/** Only samples this recent, in milliseconds. */
|
|
87
|
+
windowMs?: number;
|
|
88
|
+
/**
|
|
89
|
+
* Judges by an interval instead of the bare statistic: fires only when its upper bound at this
|
|
90
|
+
* confidence, such as 0.95, is below the threshold, so a few unlucky samples never fire it.
|
|
91
|
+
*/
|
|
92
|
+
confidence?: number;
|
|
93
|
+
/** Only scores from this source. */
|
|
94
|
+
source?: string;
|
|
95
|
+
/** Narrows the samples further. */
|
|
96
|
+
where?: (sample: ScoreSample) => boolean;
|
|
97
|
+
/**
|
|
98
|
+
* What is judged on its own: each revision of each assistant, the default, so a canary is judged
|
|
99
|
+
* apart from the live revision; each assistant; everything as one; or a scope of your own.
|
|
100
|
+
*/
|
|
101
|
+
by?: 'revision' | 'assistant' | 'all' | ((sample: ScoreSample) => string);
|
|
102
|
+
/** What it does when it fires, in order. */
|
|
103
|
+
actions: AutomationAction[];
|
|
104
|
+
/**
|
|
105
|
+
* Fires again after this long while a scope stays below, in milliseconds. Defaults to never: once
|
|
106
|
+
* per fall, again only after it recovers, which takes `minSamples` runs back at the threshold or
|
|
107
|
+
* above.
|
|
108
|
+
*/
|
|
109
|
+
repeatAfterMs?: number;
|
|
110
|
+
}
|
|
111
|
+
/** A rule that fired, or recovered, for one scope. */
|
|
112
|
+
export interface AutomationEvent {
|
|
113
|
+
/** `fired` when it fell below, `recovered` when it came back. */
|
|
114
|
+
type: 'fired' | 'recovered';
|
|
115
|
+
/** The rule. */
|
|
116
|
+
rule: string;
|
|
117
|
+
/** The scope, such as `designer@v2`. */
|
|
118
|
+
scope: string;
|
|
119
|
+
/** The score key. */
|
|
120
|
+
key: string;
|
|
121
|
+
/** How it was judged. */
|
|
122
|
+
statistic: RuleStatistic;
|
|
123
|
+
/** The statistic's value. */
|
|
124
|
+
value: number;
|
|
125
|
+
/** The interval at the rule's confidence, when it has one. */
|
|
126
|
+
interval?: [number, number];
|
|
127
|
+
/** The threshold. */
|
|
128
|
+
below: number;
|
|
129
|
+
/** Runs judged. */
|
|
130
|
+
samples: number;
|
|
131
|
+
/** The assistant, when the scope has one. */
|
|
132
|
+
assistant?: string;
|
|
133
|
+
/** The revision, when the scope has one. */
|
|
134
|
+
revision?: string;
|
|
135
|
+
/** The lowest samples, at most five, so an event points at runs to read. */
|
|
136
|
+
worst: ScoreSample[];
|
|
137
|
+
/** ISO-8601 time it was judged. */
|
|
138
|
+
at: string;
|
|
139
|
+
/** A sentence saying what happened. */
|
|
140
|
+
summary: string;
|
|
141
|
+
}
|
|
142
|
+
/** What a rule does when it fires. A failure goes to `onError`, and the rule's other actions still run. */
|
|
143
|
+
export type AutomationAction = (event: AutomationEvent) => Promise<void> | void;
|
|
144
|
+
/** Where a rule judged a scope, from `AutomationRules.status()`. */
|
|
145
|
+
export interface AutomationStatus {
|
|
146
|
+
/** The rule. */
|
|
147
|
+
rule: string;
|
|
148
|
+
/** The scope. */
|
|
149
|
+
scope: string;
|
|
150
|
+
/** The statistic's value. */
|
|
151
|
+
value: number;
|
|
152
|
+
/** Runs judged. */
|
|
153
|
+
samples: number;
|
|
154
|
+
/** Whether it is below with enough samples. */
|
|
155
|
+
breached: boolean;
|
|
156
|
+
/** ISO-8601 time it last fired for this scope, while it stays below. */
|
|
157
|
+
firingSince?: string;
|
|
158
|
+
}
|
|
159
|
+
/** Options for `automationRules()`. */
|
|
160
|
+
export interface AutomationRulesOptions {
|
|
161
|
+
/** The rules. */
|
|
162
|
+
rules: AutomationRule[];
|
|
163
|
+
/** Where scores are read. Defaults to `memoryScores()`, fed by `observe()`. */
|
|
164
|
+
scores?: ScoreSource;
|
|
165
|
+
/**
|
|
166
|
+
* Where firings are claimed, shared by every replica, so a rule acts once for a fall however many
|
|
167
|
+
* replicas judge it: a long-term store with `putIfVersion()`. Defaults to memory, for one process.
|
|
168
|
+
*/
|
|
169
|
+
state?: Store;
|
|
170
|
+
/** The namespace firings are kept under. Defaults to `['nexus', 'automation']`. */
|
|
171
|
+
namespace?: readonly string[];
|
|
172
|
+
/** Most recent samples read per rule, across its scopes. Defaults to 5,000. */
|
|
173
|
+
scanLimit?: number;
|
|
174
|
+
/** Hears every event. */
|
|
175
|
+
onEvent?: (event: AutomationEvent) => void;
|
|
176
|
+
/** Hears failures of actions and reads, which never stop the rules. */
|
|
177
|
+
onError?: (error: unknown, event?: AutomationEvent) => void;
|
|
178
|
+
/** Replaces the system clock, for tests. */
|
|
179
|
+
now?: () => Date;
|
|
180
|
+
}
|
|
181
|
+
/** Running automation rules. */
|
|
182
|
+
export interface AutomationRules {
|
|
183
|
+
/** Judges every rule now, acting on what fell, and resolves to the events. */
|
|
184
|
+
check(): Promise<AutomationEvent[]>;
|
|
185
|
+
/**
|
|
186
|
+
* Hears a run's scores, as an online evaluation's `onScored`: kept by a memory source, and judged
|
|
187
|
+
* at once by the rules on their keys.
|
|
188
|
+
*/
|
|
189
|
+
observe(scored: OnlineScored): Promise<void>;
|
|
190
|
+
/** Where each rule stands for each scope. */
|
|
191
|
+
status(): Promise<AutomationStatus[]>;
|
|
192
|
+
/** Judges every `everyMs` (one minute) until `stop()`. */
|
|
193
|
+
start(options?: {
|
|
194
|
+
everyMs?: number;
|
|
195
|
+
}): void;
|
|
196
|
+
/** Stops `start()`. */
|
|
197
|
+
stop(): void;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Automation rules over scores: each rule judges the most recent runs of each scope (each revision by
|
|
201
|
+
* default) and acts when the statistic falls below its threshold over at least `minSamples` runs.
|
|
202
|
+
* A firing is claimed in `state` before it acts, so it acts once for a fall across every replica,
|
|
203
|
+
* and again only after the scope recovers, or after `repeatAfterMs`.
|
|
204
|
+
*
|
|
205
|
+
* ```ts
|
|
206
|
+
* const rules = automationRules({
|
|
207
|
+
* scores: deploymentScores(deployments, 'designer'),
|
|
208
|
+
* state: store,
|
|
209
|
+
* rules: [{ name: 'banner quality', key: 'banner', below: 0.6, minSamples: 20,
|
|
210
|
+
* actions: [markUnhealthy({ deployments }), openReview(queue), sendWebhook({ url })] }],
|
|
211
|
+
* });
|
|
212
|
+
* const worker = deployments.evaluationWorker('designer', { evaluators, operations, onScored: rules.observe });
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export declare function automationRules(options: AutomationRulesOptions): AutomationRules;
|
|
216
|
+
/** Anything review items are opened in, such as an `AnnotationQueue`. */
|
|
217
|
+
export interface ReviewOpener {
|
|
218
|
+
/** Opens one item. */
|
|
219
|
+
enqueue(subject: {
|
|
220
|
+
inputs?: unknown;
|
|
221
|
+
output?: unknown;
|
|
222
|
+
runId?: string;
|
|
223
|
+
}, metadata?: Record<string, unknown>): unknown;
|
|
224
|
+
}
|
|
225
|
+
/** Opens a review item for each of the lowest-scoring runs, at most `limit` (5), when a rule fires. */
|
|
226
|
+
export declare function openReview(queue: ReviewOpener, options?: {
|
|
227
|
+
limit?: number;
|
|
228
|
+
}): AutomationAction;
|
|
229
|
+
/** Options for `sendWebhook()`. */
|
|
230
|
+
export interface AutomationWebhookOptions {
|
|
231
|
+
/** Where the event is posted, as JSON. */
|
|
232
|
+
url: string;
|
|
233
|
+
/** Headers on the request, such as an authorization. */
|
|
234
|
+
headers?: Record<string, string>;
|
|
235
|
+
/**
|
|
236
|
+
* Signs the body with HMAC-SHA256 under this secret, in `x-nexus-signature: sha256=<hex>`, so the
|
|
237
|
+
* receiver knows it came from here.
|
|
238
|
+
*/
|
|
239
|
+
secret?: string;
|
|
240
|
+
/** Replaces the global `fetch`. */
|
|
241
|
+
fetch?: typeof globalThis.fetch;
|
|
242
|
+
}
|
|
243
|
+
/** Posts the event to a webhook when a rule fires. A refusal rejects, which `onError` hears. */
|
|
244
|
+
export declare function sendWebhook(options: AutomationWebhookOptions): AutomationAction;
|
|
245
|
+
/** Marks the revision a rule fired for unhealthy, which rolls back a canary, as `Deployments.markUnhealthy()` does. */
|
|
246
|
+
export declare function markUnhealthy(options: {
|
|
247
|
+
deployments: {
|
|
248
|
+
markUnhealthy(assistant: string, revision: string, options: {
|
|
249
|
+
reason: string;
|
|
250
|
+
by?: string;
|
|
251
|
+
rollback?: boolean;
|
|
252
|
+
}): Promise<unknown>;
|
|
253
|
+
};
|
|
254
|
+
/** Rolls a canary back at once. Defaults to true; false leaves it to a canary guard. */
|
|
255
|
+
rollback?: boolean;
|
|
256
|
+
}): AutomationAction;
|