nexus-ai-pro 1.4.0 → 1.6.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 +52 -0
- package/CHANGELOG.md +123 -1
- package/README.md +191 -0
- package/ROADMAP.md +98 -41
- package/dist/core/nexus.d.ts +26 -0
- package/dist/core/nexus.js +46 -0
- package/dist/embeddings/adapters.d.ts +66 -0
- package/dist/embeddings/adapters.js +246 -0
- package/dist/embeddings/errors.d.ts +36 -0
- package/dist/embeddings/errors.js +76 -0
- package/dist/embeddings/index.d.ts +8 -0
- package/dist/embeddings/index.js +7 -0
- package/dist/embeddings/manager.d.ts +62 -0
- package/dist/embeddings/manager.js +618 -0
- package/dist/embeddings/mock.d.ts +36 -0
- package/dist/embeddings/mock.js +64 -0
- package/dist/embeddings/models.d.ts +74 -0
- package/dist/embeddings/models.js +252 -0
- package/dist/embeddings/providers.d.ts +15 -0
- package/dist/embeddings/providers.js +10 -0
- package/dist/embeddings/register.d.ts +10 -0
- package/dist/embeddings/register.js +39 -0
- package/dist/images/manager.js +7 -121
- package/dist/index.d.ts +16 -2
- package/dist/index.js +13 -1
- package/dist/operations/adapters.d.ts +69 -0
- package/dist/operations/adapters.js +111 -0
- package/dist/operations/errors.d.ts +59 -0
- package/dist/operations/errors.js +99 -0
- package/dist/operations/handle.d.ts +75 -0
- package/dist/operations/handle.js +229 -0
- package/dist/operations/index.d.ts +9 -0
- package/dist/operations/index.js +8 -0
- package/dist/operations/runner.d.ts +72 -0
- package/dist/operations/runner.js +489 -0
- package/dist/operations/state-machine.d.ts +11 -0
- package/dist/operations/state-machine.js +47 -0
- package/dist/operations/store.d.ts +36 -0
- package/dist/operations/store.js +140 -0
- package/dist/operations/webhooks.d.ts +33 -0
- package/dist/operations/webhooks.js +84 -0
- package/dist/ops/rate-limiter.d.ts +11 -2
- package/dist/testing/embedding-provider-conformance.d.ts +33 -0
- package/dist/testing/embedding-provider-conformance.js +117 -0
- package/dist/types/config.d.ts +3 -0
- package/dist/types/embeddings.d.ts +230 -0
- package/dist/types/embeddings.js +1 -0
- package/dist/types/images.d.ts +5 -45
- package/dist/types/operations.d.ts +227 -0
- package/dist/types/operations.js +12 -0
- package/dist-cjs/core/nexus.d.ts +26 -0
- package/dist-cjs/core/nexus.js +46 -0
- package/dist-cjs/embeddings/adapters.d.ts +66 -0
- package/dist-cjs/embeddings/adapters.js +254 -0
- package/dist-cjs/embeddings/errors.d.ts +36 -0
- package/dist-cjs/embeddings/errors.js +86 -0
- package/dist-cjs/embeddings/index.d.ts +8 -0
- package/dist-cjs/embeddings/index.js +42 -0
- package/dist-cjs/embeddings/manager.d.ts +62 -0
- package/dist-cjs/embeddings/manager.js +622 -0
- package/dist-cjs/embeddings/mock.d.ts +36 -0
- package/dist-cjs/embeddings/mock.js +68 -0
- package/dist-cjs/embeddings/models.d.ts +74 -0
- package/dist-cjs/embeddings/models.js +265 -0
- package/dist-cjs/embeddings/providers.d.ts +15 -0
- package/dist-cjs/embeddings/providers.js +11 -0
- package/dist-cjs/embeddings/register.d.ts +10 -0
- package/dist-cjs/embeddings/register.js +42 -0
- package/dist-cjs/images/manager.js +7 -121
- package/dist-cjs/index.d.ts +16 -2
- package/dist-cjs/index.js +76 -15
- package/dist-cjs/operations/adapters.d.ts +69 -0
- package/dist-cjs/operations/adapters.js +116 -0
- package/dist-cjs/operations/errors.d.ts +59 -0
- package/dist-cjs/operations/errors.js +110 -0
- package/dist-cjs/operations/handle.d.ts +75 -0
- package/dist-cjs/operations/handle.js +234 -0
- package/dist-cjs/operations/index.d.ts +9 -0
- package/dist-cjs/operations/index.js +37 -0
- package/dist-cjs/operations/runner.d.ts +72 -0
- package/dist-cjs/operations/runner.js +493 -0
- package/dist-cjs/operations/state-machine.d.ts +11 -0
- package/dist-cjs/operations/state-machine.js +55 -0
- package/dist-cjs/operations/store.d.ts +36 -0
- package/dist-cjs/operations/store.js +145 -0
- package/dist-cjs/operations/webhooks.d.ts +33 -0
- package/dist-cjs/operations/webhooks.js +90 -0
- package/dist-cjs/ops/rate-limiter.d.ts +11 -2
- package/dist-cjs/security/schema-validator.d.ts +24 -24
- package/dist-cjs/testing/embedding-provider-conformance.d.ts +33 -0
- package/dist-cjs/testing/embedding-provider-conformance.js +121 -0
- package/dist-cjs/types/config.d.ts +3 -0
- package/dist-cjs/types/embeddings.d.ts +230 -0
- package/dist-cjs/types/embeddings.js +2 -0
- package/dist-cjs/types/images.d.ts +5 -45
- package/dist-cjs/types/operations.d.ts +227 -0
- package/dist-cjs/types/operations.js +16 -0
- package/package.json +92 -1
package/API_STABILITY.md
CHANGED
|
@@ -106,6 +106,58 @@ exactly-once provider execution. Those capabilities will use additive adapters a
|
|
|
106
106
|
`MemoryAssetStore` is likewise process-local and does not promise cross-process durability or shared
|
|
107
107
|
tenant state.
|
|
108
108
|
|
|
109
|
+
## Embeddings API stage
|
|
110
|
+
|
|
111
|
+
The `nexus-ai-pro/embeddings` family is public and follows the 1.x rules, including the explicit
|
|
112
|
+
`embeddings/adapters`, `embeddings/mock`, and `embeddings/models` subpaths. This covers
|
|
113
|
+
`EmbeddingManager`, `ai.embed()` and `ai.embedOne()`, the `EmbeddingsProvider` contract, the
|
|
114
|
+
normalized request, embedding, and meta shapes, the error subclasses, and `toEmbeddingFunction()`.
|
|
115
|
+
|
|
116
|
+
`EmbeddingsProvider` is a distinct contract from the pre-existing `EmbeddingProvider` function type
|
|
117
|
+
used by `MemoryVectorStore`, the semantic cache, and RAG ingestion. That function type is unchanged
|
|
118
|
+
and remains supported; `toEmbeddingFunction()` converts one into the other.
|
|
119
|
+
|
|
120
|
+
Two behaviors are guaranteed and differ deliberately from completions. Vectors are returned in input
|
|
121
|
+
order regardless of how the request was split, deduplicated, or served from cache. And an option the
|
|
122
|
+
target model or adapter declares it cannot honor is **refused**, not dropped, because a vector built
|
|
123
|
+
with different dimensions or a different input type is silently incompatible with vectors already in
|
|
124
|
+
a store. An option nothing in the registry describes is still passed through, on the same
|
|
125
|
+
absence-means-unknown rule the completion path uses.
|
|
126
|
+
|
|
127
|
+
Embedding model entries are registry data, not compatibility guarantees. Bundled dimensions, input
|
|
128
|
+
limits, batch limits, and prices can change in a minor release when a provider changes its lineup,
|
|
129
|
+
and are overridable through `embeddings.models.registry`. Which concrete model an alias such as
|
|
130
|
+
`embed-quality` resolves to can also change in a minor release: the vectors two targets produce are
|
|
131
|
+
not interchangeable, so pin a concrete model whenever a stored index must stay valid across
|
|
132
|
+
upgrades. Provider payloads under `raw` and reported token counts are controlled by the provider.
|
|
133
|
+
|
|
134
|
+
## Operations API stage
|
|
135
|
+
|
|
136
|
+
The `nexus-ai-pro/operations` family is public and follows the 1.x rules, including the
|
|
137
|
+
`operations/adapters` and `operations/webhooks` subpaths. This covers `OperationRunner`,
|
|
138
|
+
`LocalOperationHandle`, `MemoryOperationStore`, the `OperationStore` and `OperationDispatcher`
|
|
139
|
+
contracts, the Redis and BullMQ adapters, the webhook signing and verification helpers, the state
|
|
140
|
+
machine predicates, and the normalized record, event, and error shapes.
|
|
141
|
+
|
|
142
|
+
The lifecycle types moved out of `types/images.ts` and are re-exported from it, so existing image
|
|
143
|
+
imports of `OperationStatus`, `OperationEvent`, `OperationHandle`, `OperationEventBase`, and
|
|
144
|
+
`OperationErrorDescriptor` resolve unchanged. `OperationStatus` gained `retrying`, and
|
|
145
|
+
`OperationEvent` gained `progress` and `retrying` variants and an `attempt` field on `running` —
|
|
146
|
+
additive changes this policy permits in a minor release. Code that switches exhaustively on either
|
|
147
|
+
union should add the new members.
|
|
148
|
+
|
|
149
|
+
Three behaviors are guaranteed. Terminal statuses are final, so a late provider callback cannot
|
|
150
|
+
resurrect a settled operation. Store writes are compare-and-set on `sequence`, so a losing writer
|
|
151
|
+
is told it lost rather than silently overwriting. And a record carrying raw bytes is refused with
|
|
152
|
+
`OperationSerializationError` rather than persisted, because a base64 round trip through a queue
|
|
153
|
+
payload is a failure mode that otherwise appears only as a truncated job.
|
|
154
|
+
|
|
155
|
+
Durability is a property of the configured store, not of the runner. `MemoryOperationStore` does
|
|
156
|
+
not survive a restart and does not promise cross-process recovery. `RedisOperationStore` is atomic
|
|
157
|
+
only when the client exposes `eval`; without it the compare-and-set degrades to a read-compare-write
|
|
158
|
+
that narrows but does not close the race, and that limitation is documented rather than hidden.
|
|
159
|
+
Webhook delivery is best-effort and never fails the operation it describes.
|
|
160
|
+
|
|
109
161
|
## Deprecation process
|
|
110
162
|
|
|
111
163
|
Deprecated APIs are marked with `@deprecated` in declarations and described in the changelog. Removals
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,126 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.6.0] - 2026-09-03
|
|
8
|
+
|
|
9
|
+
Durable operations, the first half of the theme *work that outlives a process*. Long-running work now
|
|
10
|
+
runs through one lifecycle that survives a worker crash. Everything here is additive.
|
|
11
|
+
|
|
12
|
+
The rest of the theme — provider batch APIs, distributed rate limiting and circuit breaking,
|
|
13
|
+
filesystem and S3 asset stores, a generated model registry, and cross-family observability — moves to
|
|
14
|
+
1.7.0. The operation handle they all sit behind ships here, which is what unblocks them.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`OperationRunner` and a shared operation lifecycle.** `queued → running → succeeded | failed`,
|
|
19
|
+
with `retrying`, `cancelling`, `cancelled`, and `expired` covering the rest, exposed through the
|
|
20
|
+
new `nexus-ai-pro/operations` subpath. The runner persists a record before doing any work, claims
|
|
21
|
+
a lease, heartbeats while the executor runs, retries with backoff and jitter, dead-letters what
|
|
22
|
+
never succeeds, and emits signed webhooks.
|
|
23
|
+
- **Restart survival through leases rather than locks.** A worker that dies leaves a record whose
|
|
24
|
+
lease lapses; another worker's `recover()` sweep resumes it. Every store write is a compare-and-set
|
|
25
|
+
on the record's `sequence`, so two workers racing on one operation cannot both win, and the loser
|
|
26
|
+
is told it lost instead of silently overwriting.
|
|
27
|
+
- **`RedisOperationStore` and `BullMQOperationDispatcher`.** Redis persists records, with the
|
|
28
|
+
compare-and-set done in one Lua call when the client exposes `eval`. BullMQ dispatch queues the
|
|
29
|
+
operation id and its routing metadata only, never a payload.
|
|
30
|
+
- **Idempotency keys.** A key matching an existing record replays that operation instead of starting
|
|
31
|
+
a second one, so an ambiguous timeout cannot produce a duplicate charge.
|
|
32
|
+
- **Progress and cancellation.** An executor reports progress through `context.report()`, which
|
|
33
|
+
reaches both `handle.events()` and the persisted record. `runner.cancel()` settles an operation
|
|
34
|
+
owned by another worker, observed there through its heartbeat.
|
|
35
|
+
- **Signed webhooks, with the verifying half included.** Deliveries are HMAC-SHA256 over
|
|
36
|
+
`${timestamp}.${body}`, so a captured delivery cannot be replayed indefinitely.
|
|
37
|
+
`verifyOperationWebhook()` ships alongside `signOperationWebhook()` rather than leaving every
|
|
38
|
+
receiver to hand-roll a constant-time comparison. A failed delivery is reported and never fails
|
|
39
|
+
the operation it describes.
|
|
40
|
+
- **A binary-payload guard.** Persisting a result carrying a `Uint8Array`, `Buffer`, `Blob`, or
|
|
41
|
+
stream throws `OperationSerializationError` naming the exact path and pointing at `AssetStore`.
|
|
42
|
+
Base64 in a job payload inflates it by a third and most queue backends cap job size well below one
|
|
43
|
+
image, so this fails loudly at the boundary instead of appearing later as a truncated job.
|
|
44
|
+
- New subpaths `nexus-ai-pro/operations`, `operations/adapters`, and `operations/webhooks`.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- The operation lifecycle types moved from `types/images.ts` to a family-neutral `types/operations.ts`
|
|
49
|
+
and are re-exported from their old home, so existing image imports resolve unchanged. The image
|
|
50
|
+
family's private handle was replaced by the shared `LocalOperationHandle`, which is told to reject
|
|
51
|
+
with `ImageOperationCancelledError` so callers catching that error see no difference.
|
|
52
|
+
- `OperationStatus` gained `retrying`; `OperationEvent` gained `progress` and `retrying` variants and
|
|
53
|
+
an `attempt` field on `running`. Additive, but code switching exhaustively on either union should
|
|
54
|
+
add the new members.
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- A handle driven by the runner stayed in `queued` for the whole run, so no `running` event was
|
|
59
|
+
emitted and every `context.report()` call was silently dropped. Found while testing the runner's
|
|
60
|
+
event sequence; the handle now exposes `markRunning()` and the runner calls it before each attempt.
|
|
61
|
+
|
|
62
|
+
## [1.5.0] - 2026-08-31
|
|
63
|
+
|
|
64
|
+
Embeddings become a first-class operation. `ai.embed()` gets the routing, caching, batching, budget,
|
|
65
|
+
retry, audit, and metrics that until now only completions had, so embedding spend is visible next to
|
|
66
|
+
completion spend instead of leaving the platform entirely. Everything here is additive.
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
|
|
70
|
+
- **`ai.embed()` and `ai.embedOne()`.** A provider-neutral embeddings operation family behind
|
|
71
|
+
`EmbeddingManager`, reached through `ai.embeddings` or the new `nexus-ai-pro/embeddings` subpath.
|
|
72
|
+
The manager is built on first access, so a runtime that never embeds pays nothing for it.
|
|
73
|
+
- **Adapters for OpenAI, Google, Cohere, Mistral, and Ollama**, plus any OpenAI-compatible
|
|
74
|
+
`/embeddings` server through `OpenAIEmbeddingProvider`'s `baseUrl` and `providerName`. They are
|
|
75
|
+
registered automatically from the provider credentials already in `providers`, so a configured
|
|
76
|
+
chat provider makes `ai.embedOne('text')` work with no embedding-specific configuration. Explicit
|
|
77
|
+
registration through `registerEmbeddingProvider()` always wins, and auto-registration can be
|
|
78
|
+
turned off with `embeddings.autoRegisterProviders: false`.
|
|
79
|
+
- **Batching, deduplication, and per-input caching.** A batch larger than the model's limit is split
|
|
80
|
+
and run with bounded concurrency, and the vectors still come back in input order. Repeated texts
|
|
81
|
+
within one request are answered by a single provider call, and caching is keyed per input, so a
|
|
82
|
+
partially repeated batch only sends the texts it has not seen. `meta.batches`, `meta.cachedInputs`,
|
|
83
|
+
and `meta.deduplicatedInputs` report what actually happened.
|
|
84
|
+
- **Structured usage and numeric cost for embeddings.** `meta.usage` and `meta.cost` reuse the same
|
|
85
|
+
`TokenUsage` and `ResponseCost` shapes as completions, priced from a new embedding model registry.
|
|
86
|
+
When a provider reports no token counts — Google's `batchEmbedContents` does not — tokens are
|
|
87
|
+
estimated locally rather than reported as zero, so a call is never priced at nothing.
|
|
88
|
+
- **An embedding model registry** with dimensions, supported truncation sizes, input limits, batch
|
|
89
|
+
limits, and prices for eleven models across five providers, with aliases (`auto`, `embed-fast`,
|
|
90
|
+
`embed-quality`, `embed-multilingual`, `embed-local`) and full override through
|
|
91
|
+
`embeddings.models.registry`. It is deliberately separate from `KNOWN_MODELS`, because completion
|
|
92
|
+
routing scores models on context window, output price, and tool support, none of which an
|
|
93
|
+
embedding model has.
|
|
94
|
+
- **Capability refusal.** An unsupported `dimensions`, `inputType`, `encodingFormat`, or `truncate`
|
|
95
|
+
value throws `EmbeddingCapabilityError` before the provider call. Embeddings refuse rather than
|
|
96
|
+
drop: a vector built with different dimensions is silently incompatible with the vectors already
|
|
97
|
+
in a store, and the mismatch only surfaces later as unexplained retrieval quality loss. An option
|
|
98
|
+
the registry says nothing about is still passed through, and `providerOptions` reaches the
|
|
99
|
+
provider body untouched.
|
|
100
|
+
- **`toEmbeddingFunction()`**, which adapts the family to the plain `EmbeddingProvider` function that
|
|
101
|
+
`MemoryVectorStore`, the semantic cache, and RAG ingestion already accept. An existing vector store
|
|
102
|
+
gains routing, caching, retry, and metrics without changing its own contract.
|
|
103
|
+
- **`normalize`** for unit-length vectors, applied locally when the provider does not already return
|
|
104
|
+
them, and reapplied after a locally truncated vector loses its length.
|
|
105
|
+
- **A deterministic `MockEmbeddingProvider`** and `runEmbeddingProviderConformance()`, which checks
|
|
106
|
+
one vector per input, input order, consistent width, optional determinism, and an honored abort.
|
|
107
|
+
- New subpaths `nexus-ai-pro/embeddings`, `embeddings/adapters`, `embeddings/mock`, and
|
|
108
|
+
`embeddings/models`.
|
|
109
|
+
|
|
110
|
+
### Changed
|
|
111
|
+
|
|
112
|
+
- `RateLimiter.check()` accepts the new structural `RateLimitedRequest` instead of a
|
|
113
|
+
`CompletionRequest`, so one limiter instance and one budget cover every operation family.
|
|
114
|
+
`CompletionRequest` still satisfies it, so no call site changes.
|
|
115
|
+
- Coverage rose from 82.6% lines / 68.9% branches / 78.2% functions to 85.7% / 70.9% / 80.8%, with
|
|
116
|
+
65 new tests. `embeddings/providers.ts`, previously the weakest file in the package at 37% lines,
|
|
117
|
+
is now at 99%.
|
|
118
|
+
|
|
119
|
+
### Notes
|
|
120
|
+
|
|
121
|
+
- Bundled embedding dimensions and prices are defaults, not financial truth; override them through
|
|
122
|
+
`embeddings.models.registry` when exact numbers matter.
|
|
123
|
+
- Which concrete model an embedding alias resolves to can change in a minor release. Two targets do
|
|
124
|
+
not produce interchangeable vectors, so pin a concrete model when a stored index must stay valid
|
|
125
|
+
across upgrades.
|
|
126
|
+
|
|
7
127
|
## [1.4.0] - 2026-08-25
|
|
8
128
|
|
|
9
129
|
Request parity and cost truth: the completion request can now express what current models actually
|
|
@@ -201,7 +321,9 @@ shared policy vocabulary is in place for a future release that revisits this.
|
|
|
201
321
|
- URL fetching rejects unsafe private-network targets and limits response reads.
|
|
202
322
|
- CLI and audit findings no longer disclose detected secret values.
|
|
203
323
|
|
|
204
|
-
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.
|
|
324
|
+
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.6.0...HEAD
|
|
325
|
+
[1.6.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.5.0...v1.6.0
|
|
326
|
+
[1.5.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.4.0...v1.5.0
|
|
205
327
|
[1.4.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.3.0...v1.4.0
|
|
206
328
|
[1.3.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.2.1...v1.3.0
|
|
207
329
|
[1.2.1]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.2.0...v1.2.1
|
package/README.md
CHANGED
|
@@ -83,6 +83,8 @@ You can still pass a plain `NexusAIConfig` to `new NexusAI(...)` when you want f
|
|
|
83
83
|
- estimate tokens and cost before provider calls
|
|
84
84
|
- cache exact or semantically similar prompts
|
|
85
85
|
- add tools, agents, RAG context, evals, batch jobs, and queues
|
|
86
|
+
- embed text through the same routing, caching, batching, budget, retry, and metrics as a completion
|
|
87
|
+
- run long operations that survive a restart, with leases, retries, dead-lettering, and signed webhooks
|
|
86
88
|
- build persistent realtime voice agents with interruption, live tools, and normalized conversation state
|
|
87
89
|
- keep TypeScript types around every request and response
|
|
88
90
|
|
|
@@ -416,6 +418,188 @@ const response = await ai.completeVerified(
|
|
|
416
418
|
);
|
|
417
419
|
```
|
|
418
420
|
|
|
421
|
+
## Durable Operations
|
|
422
|
+
|
|
423
|
+
Long-running work — an image render, a batch, anything asynchronous — runs through one lifecycle:
|
|
424
|
+
`queued → running → succeeded | failed`, with `retrying`, `cancelling`, `cancelled`, and `expired`
|
|
425
|
+
covering the rest. `OperationRunner` owns it end to end, so a crashed worker does not lose work.
|
|
426
|
+
|
|
427
|
+
The default store is in-process, so the small case needs no infrastructure:
|
|
428
|
+
|
|
429
|
+
```ts
|
|
430
|
+
import { OperationRunner } from 'nexus-ai-pro/operations';
|
|
431
|
+
|
|
432
|
+
const runner = new OperationRunner<string>({ retry: { maxAttempts: 3, baseDelayMs: 500 } });
|
|
433
|
+
|
|
434
|
+
const handle = await runner.submit(async (context) => {
|
|
435
|
+
context.report({ completed: 1, total: 3 });
|
|
436
|
+
return doTheWork(context.signal);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
for await (const event of handle.events()) {
|
|
440
|
+
console.log(event.type, event.sequence);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const value = await handle.result();
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Swapping the store makes the same code survive a restart. Nothing else changes:
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
import { OperationRunner } from 'nexus-ai-pro/operations';
|
|
450
|
+
import { BullMQOperationDispatcher, RedisOperationStore } from 'nexus-ai-pro/operations/adapters';
|
|
451
|
+
|
|
452
|
+
const runner = new OperationRunner({
|
|
453
|
+
store: new RedisOperationStore(redis),
|
|
454
|
+
dispatcher: new BullMQOperationDispatcher(queue),
|
|
455
|
+
owner: process.env.HOSTNAME,
|
|
456
|
+
leaseMs: 30_000,
|
|
457
|
+
webhook: { url: 'https://app.example/hooks/operations', secret: process.env.HOOK_SECRET! },
|
|
458
|
+
});
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
**How restart survival actually works.** A worker claims a lease before running and heartbeats
|
|
462
|
+
while it works. If the process dies, the lease simply lapses; another worker's `recover()` sweep
|
|
463
|
+
finds the record and resumes it. There is no distributed lock — every store write is a
|
|
464
|
+
compare-and-set on the record's `sequence`, so two workers racing on the same operation cannot both
|
|
465
|
+
win.
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
// On startup, in each worker.
|
|
469
|
+
const resumed = await runner.recover(executor);
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Recovery is deliberately conservative: a record past its `expiresAt` is **expired** rather than
|
|
473
|
+
re-run, and one that has used every attempt is **dead-lettered** and parked for inspection, so a
|
|
474
|
+
permanently failing operation cannot be recovered forever.
|
|
475
|
+
|
|
476
|
+
**Idempotency.** An `idempotencyKey` that matches an existing record replays that operation instead
|
|
477
|
+
of starting a second one, which is what stops an ambiguous timeout from double-charging:
|
|
478
|
+
|
|
479
|
+
```ts
|
|
480
|
+
const handle = await runner.submit(chargeAndRender, { idempotencyKey: `render:${orderId}` });
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
**Webhooks** are signed with HMAC-SHA256 over `${timestamp}.${body}`, so a captured delivery cannot
|
|
484
|
+
be replayed indefinitely. The verifying half ships too — a receiver should never have to hand-roll a
|
|
485
|
+
constant-time comparison:
|
|
486
|
+
|
|
487
|
+
```ts
|
|
488
|
+
import { OPERATION_WEBHOOK_SIGNATURE_HEADER, verifyOperationWebhook } from 'nexus-ai-pro/operations/webhooks';
|
|
489
|
+
|
|
490
|
+
app.post('/hooks/operations', (request, response) => {
|
|
491
|
+
const ok = verifyOperationWebhook(
|
|
492
|
+
request.rawBody,
|
|
493
|
+
request.header(OPERATION_WEBHOOK_SIGNATURE_HEADER),
|
|
494
|
+
process.env.HOOK_SECRET!,
|
|
495
|
+
);
|
|
496
|
+
response.sendStatus(ok ? 204 : 400);
|
|
497
|
+
});
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
A failed delivery is reported through `onWebhookError` and never turns a completed operation into a
|
|
501
|
+
failed one.
|
|
502
|
+
|
|
503
|
+
**Binary payloads are refused, not truncated.** Persisting a result that carries a `Uint8Array`,
|
|
504
|
+
`Buffer`, or `Blob` throws `OperationSerializationError` naming the exact path. Base64 in a job
|
|
505
|
+
payload inflates it by a third and most queue backends cap job size well below one image, so the
|
|
506
|
+
bytes belong in an `AssetStore` with only a reference on the record. The BullMQ dispatcher likewise
|
|
507
|
+
queues the operation id and nothing else.
|
|
508
|
+
|
|
509
|
+
The image family already runs on this lifecycle, so `ai.images.submit()` reports the same events.
|
|
510
|
+
|
|
511
|
+
## Embeddings
|
|
512
|
+
|
|
513
|
+
`ai.embed()` is a first-class operation, not a helper: it gets the same routing, caching, batching,
|
|
514
|
+
budget, retry, audit, and metrics as a completion, so embedding spend shows up next to completion
|
|
515
|
+
spend instead of being invisible.
|
|
516
|
+
|
|
517
|
+
The smallest call is one line, and needs no embedding-specific configuration — adapters are derived
|
|
518
|
+
from the provider credentials already in `providers`:
|
|
519
|
+
|
|
520
|
+
```ts
|
|
521
|
+
import { NexusAI } from 'nexus-ai-pro';
|
|
522
|
+
|
|
523
|
+
const ai = new NexusAI({ providers: { openai: { apiKey: process.env.OPENAI_API_KEY! } } });
|
|
524
|
+
|
|
525
|
+
const vector = await ai.embedOne('Provider-neutral embeddings.');
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
A batch returns vectors in input order regardless of how many provider calls the model's batch
|
|
529
|
+
limit required, and reports what the call cost:
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
const response = await ai.embed({
|
|
533
|
+
input: documents.map((document) => document.text),
|
|
534
|
+
model: 'embed-quality',
|
|
535
|
+
inputType: 'document',
|
|
536
|
+
normalize: true,
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
response.vectors; // number[][], in input order
|
|
540
|
+
response.meta.batches; // provider calls the split required
|
|
541
|
+
response.meta.usage.inputTokens; // reported by the provider, or estimated when it reports none
|
|
542
|
+
response.meta.cost.amount; // numeric, priced from the embedding registry
|
|
543
|
+
response.meta.cachedInputs; // inputs answered from cache
|
|
544
|
+
response.meta.deduplicatedInputs; // repeated inputs answered from one call
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Caching is per input rather than per request, so a partially repeated batch only sends the texts it
|
|
548
|
+
has not seen. Repeated texts inside one batch are collapsed into a single provider call:
|
|
549
|
+
|
|
550
|
+
```ts
|
|
551
|
+
const ai = new NexusAI({
|
|
552
|
+
providers: { openai: { apiKey: process.env.OPENAI_API_KEY! } },
|
|
553
|
+
embeddings: {
|
|
554
|
+
cache: { enabled: true, ttlSeconds: 86_400 },
|
|
555
|
+
costBudget: { enabled: true, maxEstimatedCost: 5 },
|
|
556
|
+
retry: { enabled: true, maxRetries: 2 },
|
|
557
|
+
fallback: [{ provider: 'cohere', model: 'embed-v4.0' }],
|
|
558
|
+
},
|
|
559
|
+
});
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
Unlike a completion, an unsupported embedding option is **refused rather than dropped**. A vector
|
|
563
|
+
built with different dimensions or a different input type is silently incompatible with the vectors
|
|
564
|
+
already in a store, and the mismatch only surfaces later as unexplained retrieval quality loss:
|
|
565
|
+
|
|
566
|
+
```ts
|
|
567
|
+
await ai.embed({ input: 'x', model: 'embed-english-v3.0', dimensions: 512 });
|
|
568
|
+
// EmbeddingCapabilityError: model has a fixed size of 1024 dimensions
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
An option the registry says nothing about is still passed through, because an undeclared capability
|
|
572
|
+
means unknown rather than unsupported, and `providerOptions` reaches the provider body untouched for
|
|
573
|
+
anything the neutral contract does not model yet.
|
|
574
|
+
|
|
575
|
+
Existing vector stores keep their contract. `toEmbeddingFunction()` adapts the family to the plain
|
|
576
|
+
function `MemoryVectorStore`, the semantic cache, and RAG ingestion already accept:
|
|
577
|
+
|
|
578
|
+
```ts
|
|
579
|
+
import { MemoryVectorStore, toEmbeddingFunction } from 'nexus-ai-pro';
|
|
580
|
+
|
|
581
|
+
const store = new MemoryVectorStore(toEmbeddingFunction(ai, { inputType: 'document' }));
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
Bundled adapters cover OpenAI (and any OpenAI-compatible `/embeddings` server), Google, Cohere,
|
|
585
|
+
Mistral, and Ollama. A custom adapter implements `EmbeddingsProvider` and is checked against the
|
|
586
|
+
neutral contract by `runEmbeddingProviderConformance()`:
|
|
587
|
+
|
|
588
|
+
```ts
|
|
589
|
+
import { OpenAIEmbeddingProvider } from 'nexus-ai-pro/embeddings/adapters';
|
|
590
|
+
import { MockEmbeddingProvider } from 'nexus-ai-pro/embeddings/mock';
|
|
591
|
+
|
|
592
|
+
ai.registerEmbeddingProvider('gateway', new OpenAIEmbeddingProvider({
|
|
593
|
+
apiKey: process.env.GATEWAY_KEY!,
|
|
594
|
+
baseUrl: 'https://gateway.internal/v1',
|
|
595
|
+
providerName: 'gateway',
|
|
596
|
+
}));
|
|
597
|
+
ai.registerEmbeddingProvider('mock', new MockEmbeddingProvider());
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
Bundled embedding dimensions and prices are defaults, not financial truth. Override them through
|
|
601
|
+
`embeddings.models.registry` when exact numbers matter.
|
|
602
|
+
|
|
419
603
|
## Image Generation and Editing (Experimental)
|
|
420
604
|
|
|
421
605
|
Image operations use a separate provider-neutral manager because generated assets have different
|
|
@@ -1133,6 +1317,13 @@ import { negotiateCompletionRequest } from 'nexus-ai-pro/capabilities';
|
|
|
1133
1317
|
import { guardrailPolicy } from 'nexus-ai-pro/security';
|
|
1134
1318
|
import { RedisCacheAdapter } from 'nexus-ai-pro/cache';
|
|
1135
1319
|
import { DeepSeekProvider } from 'nexus-ai-pro/providers/deepseek';
|
|
1320
|
+
import { OperationRunner } from 'nexus-ai-pro/operations';
|
|
1321
|
+
import { RedisOperationStore } from 'nexus-ai-pro/operations/adapters';
|
|
1322
|
+
import { verifyOperationWebhook } from 'nexus-ai-pro/operations/webhooks';
|
|
1323
|
+
import { EmbeddingManager } from 'nexus-ai-pro/embeddings';
|
|
1324
|
+
import { OpenAIEmbeddingProvider } from 'nexus-ai-pro/embeddings/adapters';
|
|
1325
|
+
import { MockEmbeddingProvider } from 'nexus-ai-pro/embeddings/mock';
|
|
1326
|
+
import { KNOWN_EMBEDDING_MODELS } from 'nexus-ai-pro/embeddings/models';
|
|
1136
1327
|
import { ImageManager } from 'nexus-ai-pro/images';
|
|
1137
1328
|
import { MemoryAssetStore } from 'nexus-ai-pro/images/assets';
|
|
1138
1329
|
import { MockImageProvider } from 'nexus-ai-pro/images/mock';
|
package/ROADMAP.md
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
This roadmap is a design proposal, not a compatibility promise. Stable and experimental
|
|
4
4
|
surfaces are defined in [API_STABILITY.md](./API_STABILITY.md).
|
|
5
5
|
|
|
6
|
-
Status baseline: **1.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
Status baseline: **1.6.0**. 62 export subpaths, 12 completion providers, 5 embedding providers, 99
|
|
7
|
+
completion registry models plus 63 aliases, and 11 embedding models plus 5 aliases. 292 unit tests
|
|
8
|
+
pass; coverage sits at **86.3% lines / 71.6% branches / 81.4% functions** against gates of
|
|
9
|
+
82/67/73. CI verifies lint, format, build, tests, coverage, mock conformance, packed-package smoke,
|
|
10
|
+
API contract, consumer type resolution, and clean install on Node 22 and 24.
|
|
10
11
|
|
|
11
12
|
---
|
|
12
13
|
|
|
@@ -91,7 +92,26 @@ parallel-tool-call, seed, top-k, and penalty controls; structured `usage` with c
|
|
|
91
92
|
cached-write, and reasoning token counts; numeric `cost` priced per token class; and capability
|
|
92
93
|
negotiation under a `strict`, `warn`, or `off` policy with registry provenance and a freshness check.
|
|
93
94
|
|
|
94
|
-
### 1.8
|
|
95
|
+
### 1.8 Embeddings
|
|
96
|
+
|
|
97
|
+
Delivered in 1.5.0. `ai.embed()` and `ai.embedOne()` over a provider-neutral `EmbeddingsProvider`
|
|
98
|
+
contract with adapters for OpenAI (and any OpenAI-compatible server), Google, Cohere, Mistral, and
|
|
99
|
+
Ollama, auto-registered from existing provider credentials. Around them: model-registry routing with
|
|
100
|
+
aliases and per-provider fallback, batch splitting with bounded concurrency, within-request
|
|
101
|
+
deduplication, per-input caching, cost budgets, retry, structured usage and numeric cost, capability
|
|
102
|
+
refusal rather than silent option dropping, `toEmbeddingFunction()` for existing vector stores, a
|
|
103
|
+
deterministic mock, and a conformance harness.
|
|
104
|
+
|
|
105
|
+
### 1.9 Durable operations
|
|
106
|
+
|
|
107
|
+
Delivered in 1.6.0. `OperationRunner` over an `OperationStore` contract, with a checked state
|
|
108
|
+
machine, leases and heartbeats, delayed retry with backoff and jitter, dead-lettering, progress
|
|
109
|
+
events, idempotency replay, crash recovery, and HMAC-signed webhooks with a matching verifier.
|
|
110
|
+
`MemoryOperationStore` keeps the single-process case dependency-free; `RedisOperationStore` and
|
|
111
|
+
`BullMQOperationDispatcher` make the same code survive a restart. Persisting raw bytes is refused
|
|
112
|
+
so binary media cannot end up in a queue payload.
|
|
113
|
+
|
|
114
|
+
### 1.10 Operations, evaluation, and packaging
|
|
95
115
|
|
|
96
116
|
Rate limiting, audit logging, in-memory and OpenTelemetry metrics sinks, Prometheus export, an
|
|
97
117
|
OpenTelemetry trace exporter, provider health monitoring, `EvalRunner` with LLM-as-judge and a
|
|
@@ -107,21 +127,27 @@ trusted publishing with provenance.
|
|
|
107
127
|
|
|
108
128
|
Ordered by how much each one costs a consumer today.
|
|
109
129
|
|
|
110
|
-
### 2.1
|
|
130
|
+
### 2.1 Media families still do not get the platform
|
|
111
131
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
erases the union it defines.
|
|
132
|
+
Narrowed in 1.5.0: `EmbeddingManager` shares the runtime's rate limiter, audit log, and metrics
|
|
133
|
+
collector, and `RateLimiter` now buckets on a structural request rather than a `CompletionRequest`,
|
|
134
|
+
so a family no longer has to be a completion to be governed. `ImageManager`, `VoiceManager`,
|
|
135
|
+
`TelephonyManager`, and `RealtimeSession` still emit nothing to `MetricsCollector` and pass through
|
|
136
|
+
no rate limit or audit stage, so an application running phone agents and image generation has
|
|
137
|
+
observability for only part of its spend.
|
|
119
138
|
|
|
120
|
-
|
|
139
|
+
The traced pipeline itself remains completion-only. `PipelineContext` is typed strictly around
|
|
140
|
+
`CompletionRequest`/`NexusResponse`, so other families cannot reuse it without a refactor, and
|
|
141
|
+
`PipelineStepName` ends in `| string`, which erases the union it defines. Embeddings therefore share
|
|
142
|
+
the observability primitives but not the step trace.
|
|
121
143
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
144
|
+
### 2.2 Embeddings beyond text retrieval
|
|
145
|
+
|
|
146
|
+
Closed for text in 1.5.0: `ai.embed()` is an operation family with routing, per-input caching,
|
|
147
|
+
batching, deduplication, budget, retry, audit, and metrics, over five adapters and its own model
|
|
148
|
+
registry. What remains is everything the family does not yet cover: a provider batch-API tier for
|
|
149
|
+
large asynchronous ingestion jobs, image and multimodal embeddings, a distributed cache adapter for
|
|
150
|
+
vectors shared across processes, and reranking as a sibling operation.
|
|
125
151
|
|
|
126
152
|
### 2.3 The model registry is still hand-maintained
|
|
127
153
|
|
|
@@ -130,13 +156,17 @@ visible. The data itself is still a hand-written TypeScript literal of 99 models
|
|
|
130
156
|
Generation from versioned provider data remains outstanding, and the Claude 5 reasoning bug 1.4.0
|
|
131
157
|
fixed is the kind of error generation would have prevented.
|
|
132
158
|
|
|
133
|
-
### 2.4 Durable
|
|
159
|
+
### 2.4 Durable execution is only half distributed
|
|
160
|
+
|
|
161
|
+
Closed in 1.6.0 for operations: `OperationHandle` is no longer process-bound, cross-process recovery
|
|
162
|
+
works through lapsed leases, and idempotency keys deduplicate across workers.
|
|
134
163
|
|
|
135
|
-
`RateLimiter` is an in-memory token bucket with no distributed adapter
|
|
136
|
-
(health monitoring exists, but nothing opens a circuit)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
164
|
+
Still process-local: `RateLimiter` is an in-memory token bucket with no distributed adapter, there is
|
|
165
|
+
no circuit breaker (health monitoring exists, but nothing opens a circuit), `MemoryAssetStore` holds
|
|
166
|
+
assets in one process, and realtime conversation snapshots and exports live in memory. Provider-state
|
|
167
|
+
replay remains an application responsibility. One caveat on what did land: `RedisOperationStore` is
|
|
168
|
+
atomic only when the client exposes `eval`; without it the compare-and-set degrades to a
|
|
169
|
+
read-compare-write that narrows but does not close the race.
|
|
140
170
|
|
|
141
171
|
### 2.5 Image family blockers
|
|
142
172
|
|
|
@@ -160,14 +190,14 @@ local estimate, and a published latency benchmark methodology remain follow-on w
|
|
|
160
190
|
### 2.7 Test coverage weak spots
|
|
161
191
|
|
|
162
192
|
1.4.0 closed the worst of these: the agent loop went from 40% to 95% line coverage, the rules router
|
|
163
|
-
from 34% to 91%, and the evaluation metric library from 40% (5% of functions) to 99%.
|
|
164
|
-
|
|
193
|
+
from 34% to 91%, and the evaluation metric library from 40% (5% of functions) to 99%. 1.5.0 took
|
|
194
|
+
`embeddings/providers.ts` from 37% to 99% and landed the new embeddings family at 95–100% across its
|
|
195
|
+
seven files. What is still thin:
|
|
165
196
|
|
|
166
197
|
| Area | Lines | Note |
|
|
167
198
|
| --- | --- | --- |
|
|
168
199
|
| `hallucination/rag.ts`, `verification.ts` | 38% / 47% | Grounding claims deserve tests. |
|
|
169
200
|
| `workflow/chains.ts`, `domain.ts` | 51% / 50% | |
|
|
170
|
-
| `embeddings/providers.ts` | 37% | Should be covered as part of the embeddings operation family. |
|
|
171
201
|
| `security/pii-detector.ts`, `semantic-injection-classifier.ts` | 49% / 49% | Security-relevant. |
|
|
172
202
|
| `jobs/queue.ts`, `batch.ts` | 47% / 53% | |
|
|
173
203
|
| `rag/file-ingestion.ts` | 43% | |
|
|
@@ -219,15 +249,44 @@ policy vocabulary is in place for a later release that revisits this.
|
|
|
219
249
|
|
|
220
250
|
---
|
|
221
251
|
|
|
222
|
-
## 4.
|
|
252
|
+
## 4. Shipped in 1.5.0 — embeddings as an operation family
|
|
253
|
+
|
|
254
|
+
`ai.embed()` with routing, per-input caching, batching, deduplication, budget, retry, audit, and
|
|
255
|
+
metrics; the existing factory functions became one adapter shape behind it, and
|
|
256
|
+
`toEmbeddingFunction()` keeps `MemoryVectorStore`, the semantic cache, and RAG ingestion working
|
|
257
|
+
unchanged. Embeddings refuse an unsupported option rather than dropping it, because a mis-sized
|
|
258
|
+
vector is silently incompatible with a populated store rather than merely different.
|
|
259
|
+
|
|
260
|
+
Deliberately deferred: the provider batch-API tier, which belongs with the durable operation handle
|
|
261
|
+
below rather than with the synchronous path.
|
|
262
|
+
|
|
263
|
+
## 5. Shipped in 1.6.0 — durable operations
|
|
264
|
+
|
|
265
|
+
The first half of *work that outlives a process*. One lifecycle now covers every long-running
|
|
266
|
+
family, and it survives a worker crash.
|
|
267
|
+
|
|
268
|
+
- **Operation state machine.** `queued → running → retrying → succeeded | failed | cancelling |
|
|
269
|
+
cancelled | expired`, with leases, heartbeats, progress events, delayed retry, dead-letter
|
|
270
|
+
handling, timestamps, signed webhooks, and trace propagation. The image family runs on it, and the
|
|
271
|
+
lifecycle types moved to a family-neutral `types/operations.ts` re-exported from their old home.
|
|
272
|
+
- **Durable operation adapters.** `RedisOperationStore` persists records with a compare-and-set on
|
|
273
|
+
`sequence`, done in one Lua call when the client exposes `eval`. `BullMQOperationDispatcher`
|
|
274
|
+
queues the operation id only. A record carrying raw bytes is refused rather than serialized.
|
|
275
|
+
- **Idempotency and recovery.** A matching key replays an accepted operation instead of starting a
|
|
276
|
+
second; `recover()` resumes records whose lease lapsed, expiring those past their deadline and
|
|
277
|
+
dead-lettering those that used every attempt.
|
|
278
|
+
|
|
279
|
+
Restart survival comes from leases rather than locks. There is no distributed lock anywhere: every
|
|
280
|
+
store write is a compare-and-set, so a losing writer is told it lost instead of overwriting.
|
|
281
|
+
|
|
282
|
+
Deliberately deferred: everything below, which needed the operation handle to exist first.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## 6. Next release: 1.7.0 — batch economics and distributed limits
|
|
223
287
|
|
|
224
|
-
The theme
|
|
288
|
+
The rest of the theme, now that the handle they sit behind exists.
|
|
225
289
|
|
|
226
|
-
- **Operation state machine.** `queued → running → succeeded | failed | cancelling | cancelled | expired`,
|
|
227
|
-
with leases, heartbeats, progress events, delayed retry, dead-letter handling, timestamps, signed
|
|
228
|
-
webhooks, and trace propagation. Applies to images first, then to any long-running family.
|
|
229
|
-
- **Durable operation adapters.** Redis and BullMQ-backed handles so `submit()` survives a restart.
|
|
230
|
-
Queue asset references only; never JSON-serialize binary media into job payloads.
|
|
231
290
|
- **Provider batch APIs.** OpenAI Batch and Anthropic Message Batches behind the same operation
|
|
232
291
|
handle, exposing the discounted asynchronous tier that local `runBatch()` concurrency cannot reach.
|
|
233
292
|
Idempotency keys replay an accepted operation; ambiguous timeouts must not produce duplicate
|
|
@@ -235,19 +294,17 @@ The theme is work that outlives a process.
|
|
|
235
294
|
- **Distributed rate limiting and circuit breaking.** Redis-backed limiter adapter, plus a breaker
|
|
236
295
|
that consumes existing `ProviderHealthMonitor` signals and trips routing away from a failing
|
|
237
296
|
provider.
|
|
238
|
-
- **Embeddings as an operation family.** `ai.embed()` with routing, caching, batching, budget, retry,
|
|
239
|
-
audit, and metrics; existing factories become adapters behind it.
|
|
240
297
|
- **Filesystem and S3-compatible asset stores** implementing the existing `AssetStore` interface,
|
|
241
298
|
with retention, tenant ownership, checksums, streaming, and signing. Provider URLs remain temporary
|
|
242
299
|
delivery locations, never durable storage.
|
|
243
300
|
- **Generated model registry** from versioned provider data, consuming the 1.4.0 provenance fields.
|
|
244
301
|
- **Cross-family observability.** Route image, voice, telephony, and realtime operations through the
|
|
245
|
-
rate limiter, audit log, and metrics collector, closing gap 2.
|
|
302
|
+
rate limiter, audit log, and metrics collector, closing gap 2.1 without waiting for the full
|
|
246
303
|
lifecycle refactor.
|
|
247
304
|
|
|
248
305
|
---
|
|
249
306
|
|
|
250
|
-
##
|
|
307
|
+
## 7. 1.8.0 — image portability, then promotion
|
|
251
308
|
|
|
252
309
|
Images cannot leave experimental until the neutral contract survives a second wire protocol.
|
|
253
310
|
|
|
@@ -275,7 +332,7 @@ label come off.
|
|
|
275
332
|
|
|
276
333
|
---
|
|
277
334
|
|
|
278
|
-
##
|
|
335
|
+
## 8. 2.0 candidate — one lifecycle for every operation
|
|
279
336
|
|
|
280
337
|
Create an internal typed lifecycle and adapt every family to it:
|
|
281
338
|
|
|
@@ -298,7 +355,7 @@ Also in the 2.0 window, because each requires a breaking change:
|
|
|
298
355
|
|
|
299
356
|
---
|
|
300
357
|
|
|
301
|
-
##
|
|
358
|
+
## 9. Longer-term backlog
|
|
302
359
|
|
|
303
360
|
1. MCP client and server adapters with asset and tool interoperability.
|
|
304
361
|
2. Video generation through the same asynchronous operation, job, and asset contracts.
|
|
@@ -323,7 +380,7 @@ Also in the 2.0 window, because each requires a breaking change:
|
|
|
323
380
|
|
|
324
381
|
---
|
|
325
382
|
|
|
326
|
-
##
|
|
383
|
+
## 10. Design notes carried forward
|
|
327
384
|
|
|
328
385
|
These decisions predate this revision and still hold.
|
|
329
386
|
|
|
@@ -375,7 +432,7 @@ await ai.images.edit({
|
|
|
375
432
|
|
|
376
433
|
---
|
|
377
434
|
|
|
378
|
-
##
|
|
435
|
+
## 11. How an item graduates
|
|
379
436
|
|
|
380
437
|
1. Provider-neutral types and a deterministic mock land first.
|
|
381
438
|
2. One real adapter proves the contract; conformance fixtures cover it.
|