nexus-ai-pro 1.4.0 → 1.5.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 (57) hide show
  1. package/API_STABILITY.md +25 -0
  2. package/CHANGELOG.md +67 -1
  3. package/README.md +97 -0
  4. package/ROADMAP.md +55 -29
  5. package/dist/core/nexus.d.ts +26 -0
  6. package/dist/core/nexus.js +46 -0
  7. package/dist/embeddings/adapters.d.ts +66 -0
  8. package/dist/embeddings/adapters.js +246 -0
  9. package/dist/embeddings/errors.d.ts +36 -0
  10. package/dist/embeddings/errors.js +76 -0
  11. package/dist/embeddings/index.d.ts +8 -0
  12. package/dist/embeddings/index.js +7 -0
  13. package/dist/embeddings/manager.d.ts +62 -0
  14. package/dist/embeddings/manager.js +618 -0
  15. package/dist/embeddings/mock.d.ts +36 -0
  16. package/dist/embeddings/mock.js +64 -0
  17. package/dist/embeddings/models.d.ts +74 -0
  18. package/dist/embeddings/models.js +252 -0
  19. package/dist/embeddings/providers.d.ts +15 -0
  20. package/dist/embeddings/providers.js +10 -0
  21. package/dist/embeddings/register.d.ts +10 -0
  22. package/dist/embeddings/register.js +39 -0
  23. package/dist/index.d.ts +7 -2
  24. package/dist/index.js +5 -1
  25. package/dist/ops/rate-limiter.d.ts +11 -2
  26. package/dist/testing/embedding-provider-conformance.d.ts +33 -0
  27. package/dist/testing/embedding-provider-conformance.js +117 -0
  28. package/dist/types/config.d.ts +3 -0
  29. package/dist/types/embeddings.d.ts +230 -0
  30. package/dist/types/embeddings.js +1 -0
  31. package/dist-cjs/core/nexus.d.ts +26 -0
  32. package/dist-cjs/core/nexus.js +46 -0
  33. package/dist-cjs/embeddings/adapters.d.ts +66 -0
  34. package/dist-cjs/embeddings/adapters.js +254 -0
  35. package/dist-cjs/embeddings/errors.d.ts +36 -0
  36. package/dist-cjs/embeddings/errors.js +86 -0
  37. package/dist-cjs/embeddings/index.d.ts +8 -0
  38. package/dist-cjs/embeddings/index.js +42 -0
  39. package/dist-cjs/embeddings/manager.d.ts +62 -0
  40. package/dist-cjs/embeddings/manager.js +622 -0
  41. package/dist-cjs/embeddings/mock.d.ts +36 -0
  42. package/dist-cjs/embeddings/mock.js +68 -0
  43. package/dist-cjs/embeddings/models.d.ts +74 -0
  44. package/dist-cjs/embeddings/models.js +265 -0
  45. package/dist-cjs/embeddings/providers.d.ts +15 -0
  46. package/dist-cjs/embeddings/providers.js +11 -0
  47. package/dist-cjs/embeddings/register.d.ts +10 -0
  48. package/dist-cjs/embeddings/register.js +42 -0
  49. package/dist-cjs/index.d.ts +7 -2
  50. package/dist-cjs/index.js +28 -2
  51. package/dist-cjs/ops/rate-limiter.d.ts +11 -2
  52. package/dist-cjs/testing/embedding-provider-conformance.d.ts +33 -0
  53. package/dist-cjs/testing/embedding-provider-conformance.js +121 -0
  54. package/dist-cjs/types/config.d.ts +3 -0
  55. package/dist-cjs/types/embeddings.d.ts +230 -0
  56. package/dist-cjs/types/embeddings.js +2 -0
  57. package/package.json +53 -1
package/API_STABILITY.md CHANGED
@@ -106,6 +106,31 @@ 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
+
109
134
  ## Deprecation process
110
135
 
111
136
  Deprecated APIs are marked with `@deprecated` in declarations and described in the changelog. Removals
package/CHANGELOG.md CHANGED
@@ -4,6 +4,71 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.5.0] - 2026-08-31
8
+
9
+ Embeddings become a first-class operation. `ai.embed()` gets the routing, caching, batching, budget,
10
+ retry, audit, and metrics that until now only completions had, so embedding spend is visible next to
11
+ completion spend instead of leaving the platform entirely. Everything here is additive.
12
+
13
+ ### Added
14
+
15
+ - **`ai.embed()` and `ai.embedOne()`.** A provider-neutral embeddings operation family behind
16
+ `EmbeddingManager`, reached through `ai.embeddings` or the new `nexus-ai-pro/embeddings` subpath.
17
+ The manager is built on first access, so a runtime that never embeds pays nothing for it.
18
+ - **Adapters for OpenAI, Google, Cohere, Mistral, and Ollama**, plus any OpenAI-compatible
19
+ `/embeddings` server through `OpenAIEmbeddingProvider`'s `baseUrl` and `providerName`. They are
20
+ registered automatically from the provider credentials already in `providers`, so a configured
21
+ chat provider makes `ai.embedOne('text')` work with no embedding-specific configuration. Explicit
22
+ registration through `registerEmbeddingProvider()` always wins, and auto-registration can be
23
+ turned off with `embeddings.autoRegisterProviders: false`.
24
+ - **Batching, deduplication, and per-input caching.** A batch larger than the model's limit is split
25
+ and run with bounded concurrency, and the vectors still come back in input order. Repeated texts
26
+ within one request are answered by a single provider call, and caching is keyed per input, so a
27
+ partially repeated batch only sends the texts it has not seen. `meta.batches`, `meta.cachedInputs`,
28
+ and `meta.deduplicatedInputs` report what actually happened.
29
+ - **Structured usage and numeric cost for embeddings.** `meta.usage` and `meta.cost` reuse the same
30
+ `TokenUsage` and `ResponseCost` shapes as completions, priced from a new embedding model registry.
31
+ When a provider reports no token counts — Google's `batchEmbedContents` does not — tokens are
32
+ estimated locally rather than reported as zero, so a call is never priced at nothing.
33
+ - **An embedding model registry** with dimensions, supported truncation sizes, input limits, batch
34
+ limits, and prices for eleven models across five providers, with aliases (`auto`, `embed-fast`,
35
+ `embed-quality`, `embed-multilingual`, `embed-local`) and full override through
36
+ `embeddings.models.registry`. It is deliberately separate from `KNOWN_MODELS`, because completion
37
+ routing scores models on context window, output price, and tool support, none of which an
38
+ embedding model has.
39
+ - **Capability refusal.** An unsupported `dimensions`, `inputType`, `encodingFormat`, or `truncate`
40
+ value throws `EmbeddingCapabilityError` before the provider call. Embeddings refuse rather than
41
+ drop: a vector built with different dimensions is silently incompatible with the vectors already
42
+ in a store, and the mismatch only surfaces later as unexplained retrieval quality loss. An option
43
+ the registry says nothing about is still passed through, and `providerOptions` reaches the
44
+ provider body untouched.
45
+ - **`toEmbeddingFunction()`**, which adapts the family to the plain `EmbeddingProvider` function that
46
+ `MemoryVectorStore`, the semantic cache, and RAG ingestion already accept. An existing vector store
47
+ gains routing, caching, retry, and metrics without changing its own contract.
48
+ - **`normalize`** for unit-length vectors, applied locally when the provider does not already return
49
+ them, and reapplied after a locally truncated vector loses its length.
50
+ - **A deterministic `MockEmbeddingProvider`** and `runEmbeddingProviderConformance()`, which checks
51
+ one vector per input, input order, consistent width, optional determinism, and an honored abort.
52
+ - New subpaths `nexus-ai-pro/embeddings`, `embeddings/adapters`, `embeddings/mock`, and
53
+ `embeddings/models`.
54
+
55
+ ### Changed
56
+
57
+ - `RateLimiter.check()` accepts the new structural `RateLimitedRequest` instead of a
58
+ `CompletionRequest`, so one limiter instance and one budget cover every operation family.
59
+ `CompletionRequest` still satisfies it, so no call site changes.
60
+ - Coverage rose from 82.6% lines / 68.9% branches / 78.2% functions to 85.7% / 70.9% / 80.8%, with
61
+ 65 new tests. `embeddings/providers.ts`, previously the weakest file in the package at 37% lines,
62
+ is now at 99%.
63
+
64
+ ### Notes
65
+
66
+ - Bundled embedding dimensions and prices are defaults, not financial truth; override them through
67
+ `embeddings.models.registry` when exact numbers matter.
68
+ - Which concrete model an embedding alias resolves to can change in a minor release. Two targets do
69
+ not produce interchangeable vectors, so pin a concrete model when a stored index must stay valid
70
+ across upgrades.
71
+
7
72
  ## [1.4.0] - 2026-08-25
8
73
 
9
74
  Request parity and cost truth: the completion request can now express what current models actually
@@ -201,7 +266,8 @@ shared policy vocabulary is in place for a future release that revisits this.
201
266
  - URL fetching rejects unsafe private-network targets and limits response reads.
202
267
  - CLI and audit findings no longer disclose detected secret values.
203
268
 
204
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.4.0...HEAD
269
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.5.0...HEAD
270
+ [1.5.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.4.0...v1.5.0
205
271
  [1.4.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.3.0...v1.4.0
206
272
  [1.3.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.2.1...v1.3.0
207
273
  [1.2.1]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.2.0...v1.2.1
package/README.md CHANGED
@@ -83,6 +83,7 @@ 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
86
87
  - build persistent realtime voice agents with interruption, live tools, and normalized conversation state
87
88
  - keep TypeScript types around every request and response
88
89
 
@@ -416,6 +417,98 @@ const response = await ai.completeVerified(
416
417
  );
417
418
  ```
418
419
 
420
+ ## Embeddings
421
+
422
+ `ai.embed()` is a first-class operation, not a helper: it gets the same routing, caching, batching,
423
+ budget, retry, audit, and metrics as a completion, so embedding spend shows up next to completion
424
+ spend instead of being invisible.
425
+
426
+ The smallest call is one line, and needs no embedding-specific configuration — adapters are derived
427
+ from the provider credentials already in `providers`:
428
+
429
+ ```ts
430
+ import { NexusAI } from 'nexus-ai-pro';
431
+
432
+ const ai = new NexusAI({ providers: { openai: { apiKey: process.env.OPENAI_API_KEY! } } });
433
+
434
+ const vector = await ai.embedOne('Provider-neutral embeddings.');
435
+ ```
436
+
437
+ A batch returns vectors in input order regardless of how many provider calls the model's batch
438
+ limit required, and reports what the call cost:
439
+
440
+ ```ts
441
+ const response = await ai.embed({
442
+ input: documents.map((document) => document.text),
443
+ model: 'embed-quality',
444
+ inputType: 'document',
445
+ normalize: true,
446
+ });
447
+
448
+ response.vectors; // number[][], in input order
449
+ response.meta.batches; // provider calls the split required
450
+ response.meta.usage.inputTokens; // reported by the provider, or estimated when it reports none
451
+ response.meta.cost.amount; // numeric, priced from the embedding registry
452
+ response.meta.cachedInputs; // inputs answered from cache
453
+ response.meta.deduplicatedInputs; // repeated inputs answered from one call
454
+ ```
455
+
456
+ Caching is per input rather than per request, so a partially repeated batch only sends the texts it
457
+ has not seen. Repeated texts inside one batch are collapsed into a single provider call:
458
+
459
+ ```ts
460
+ const ai = new NexusAI({
461
+ providers: { openai: { apiKey: process.env.OPENAI_API_KEY! } },
462
+ embeddings: {
463
+ cache: { enabled: true, ttlSeconds: 86_400 },
464
+ costBudget: { enabled: true, maxEstimatedCost: 5 },
465
+ retry: { enabled: true, maxRetries: 2 },
466
+ fallback: [{ provider: 'cohere', model: 'embed-v4.0' }],
467
+ },
468
+ });
469
+ ```
470
+
471
+ Unlike a completion, an unsupported embedding option is **refused rather than dropped**. A vector
472
+ built with different dimensions or a different input type is silently incompatible with the vectors
473
+ already in a store, and the mismatch only surfaces later as unexplained retrieval quality loss:
474
+
475
+ ```ts
476
+ await ai.embed({ input: 'x', model: 'embed-english-v3.0', dimensions: 512 });
477
+ // EmbeddingCapabilityError: model has a fixed size of 1024 dimensions
478
+ ```
479
+
480
+ An option the registry says nothing about is still passed through, because an undeclared capability
481
+ means unknown rather than unsupported, and `providerOptions` reaches the provider body untouched for
482
+ anything the neutral contract does not model yet.
483
+
484
+ Existing vector stores keep their contract. `toEmbeddingFunction()` adapts the family to the plain
485
+ function `MemoryVectorStore`, the semantic cache, and RAG ingestion already accept:
486
+
487
+ ```ts
488
+ import { MemoryVectorStore, toEmbeddingFunction } from 'nexus-ai-pro';
489
+
490
+ const store = new MemoryVectorStore(toEmbeddingFunction(ai, { inputType: 'document' }));
491
+ ```
492
+
493
+ Bundled adapters cover OpenAI (and any OpenAI-compatible `/embeddings` server), Google, Cohere,
494
+ Mistral, and Ollama. A custom adapter implements `EmbeddingsProvider` and is checked against the
495
+ neutral contract by `runEmbeddingProviderConformance()`:
496
+
497
+ ```ts
498
+ import { OpenAIEmbeddingProvider } from 'nexus-ai-pro/embeddings/adapters';
499
+ import { MockEmbeddingProvider } from 'nexus-ai-pro/embeddings/mock';
500
+
501
+ ai.registerEmbeddingProvider('gateway', new OpenAIEmbeddingProvider({
502
+ apiKey: process.env.GATEWAY_KEY!,
503
+ baseUrl: 'https://gateway.internal/v1',
504
+ providerName: 'gateway',
505
+ }));
506
+ ai.registerEmbeddingProvider('mock', new MockEmbeddingProvider());
507
+ ```
508
+
509
+ Bundled embedding dimensions and prices are defaults, not financial truth. Override them through
510
+ `embeddings.models.registry` when exact numbers matter.
511
+
419
512
  ## Image Generation and Editing (Experimental)
420
513
 
421
514
  Image operations use a separate provider-neutral manager because generated assets have different
@@ -1133,6 +1226,10 @@ import { negotiateCompletionRequest } from 'nexus-ai-pro/capabilities';
1133
1226
  import { guardrailPolicy } from 'nexus-ai-pro/security';
1134
1227
  import { RedisCacheAdapter } from 'nexus-ai-pro/cache';
1135
1228
  import { DeepSeekProvider } from 'nexus-ai-pro/providers/deepseek';
1229
+ import { EmbeddingManager } from 'nexus-ai-pro/embeddings';
1230
+ import { OpenAIEmbeddingProvider } from 'nexus-ai-pro/embeddings/adapters';
1231
+ import { MockEmbeddingProvider } from 'nexus-ai-pro/embeddings/mock';
1232
+ import { KNOWN_EMBEDDING_MODELS } from 'nexus-ai-pro/embeddings/models';
1136
1233
  import { ImageManager } from 'nexus-ai-pro/images';
1137
1234
  import { MemoryAssetStore } from 'nexus-ai-pro/images/assets';
1138
1235
  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.4.0**. 55 export subpaths, 12 completion providers, 99 registry models plus 63
7
- aliases. 163 unit tests pass; coverage sits at **82.6% lines / 68.9% branches / 78.2% functions**
8
- against gates of 80/68/75. CI verifies lint, format, build, tests, coverage, mock conformance,
9
- packed-package smoke, API contract, consumer type resolution, and clean install on Node 22 and 24.
6
+ Status baseline: **1.5.0**. 59 export subpaths, 12 completion providers, 5 embedding providers, 99
7
+ completion registry models plus 63 aliases, and 11 embedding models plus 5 aliases. 234 unit tests
8
+ pass; coverage sits at **85.7% lines / 70.9% branches / 80.8% 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,17 @@ 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 Operations, evaluation, and packaging
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 Operations, evaluation, and packaging
95
106
 
96
107
  Rate limiting, audit logging, in-memory and OpenTelemetry metrics sinks, Prometheus export, an
97
108
  OpenTelemetry trace exporter, provider health monitoring, `EvalRunner` with LLM-as-judge and a
@@ -107,21 +118,27 @@ trusted publishing with provenance.
107
118
 
108
119
  Ordered by how much each one costs a consumer today.
109
120
 
110
- ### 2.1 Only completions get the platform
121
+ ### 2.1 Media families still do not get the platform
122
+
123
+ Narrowed in 1.5.0: `EmbeddingManager` shares the runtime's rate limiter, audit log, and metrics
124
+ collector, and `RateLimiter` now buckets on a structural request rather than a `CompletionRequest`,
125
+ so a family no longer has to be a completion to be governed. `ImageManager`, `VoiceManager`,
126
+ `TelephonyManager`, and `RealtimeSession` still emit nothing to `MetricsCollector` and pass through
127
+ no rate limit or audit stage, so an application running phone agents and image generation has
128
+ observability for only part of its spend.
111
129
 
112
- The pipeline, rate limiter, audit log, metrics collector, and cost budget are wired into
113
- `complete()` and `stream()` only. `ImageManager`, `VoiceManager`, `TelephonyManager`, and
114
- `RealtimeSession` emit nothing to `MetricsCollector` and pass through no rate limit or audit stage.
115
- An application running phone agents and image generation has observability for the smallest part of
116
- its spend. `PipelineContext` is also typed strictly around `CompletionRequest`/`NexusResponse`, so
117
- other families cannot reuse it without a refactor, and `PipelineStepName` ends in `| string`, which
118
- erases the union it defines.
130
+ The traced pipeline itself remains completion-only. `PipelineContext` is typed strictly around
131
+ `CompletionRequest`/`NexusResponse`, so other families cannot reuse it without a refactor, and
132
+ `PipelineStepName` ends in `| string`, which erases the union it defines. Embeddings therefore share
133
+ the observability primitives but not the step trace.
119
134
 
120
- ### 2.2 No first-class embeddings operation
135
+ ### 2.2 Embeddings beyond text retrieval
121
136
 
122
- `src/embeddings/providers.ts` exports OpenAI/Gemini/Cohere factory functions used only by the
123
- semantic cache and vector store. There is no `ai.embed()`, so embeddings get no routing, caching,
124
- budget, retry, audit, or metrics — and no provider-neutral batch contract.
137
+ Closed for text in 1.5.0: `ai.embed()` is an operation family with routing, per-input caching,
138
+ batching, deduplication, budget, retry, audit, and metrics, over five adapters and its own model
139
+ registry. What remains is everything the family does not yet cover: a provider batch-API tier for
140
+ large asynchronous ingestion jobs, image and multimodal embeddings, a distributed cache adapter for
141
+ vectors shared across processes, and reranking as a sibling operation.
125
142
 
126
143
  ### 2.3 The model registry is still hand-maintained
127
144
 
@@ -160,14 +177,14 @@ local estimate, and a published latency benchmark methodology remain follow-on w
160
177
  ### 2.7 Test coverage weak spots
161
178
 
162
179
  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%. What is still
164
- thin:
180
+ from 34% to 91%, and the evaluation metric library from 40% (5% of functions) to 99%. 1.5.0 took
181
+ `embeddings/providers.ts` from 37% to 99% and landed the new embeddings family at 95–100% across its
182
+ seven files. What is still thin:
165
183
 
166
184
  | Area | Lines | Note |
167
185
  | --- | --- | --- |
168
186
  | `hallucination/rag.ts`, `verification.ts` | 38% / 47% | Grounding claims deserve tests. |
169
187
  | `workflow/chains.ts`, `domain.ts` | 51% / 50% | |
170
- | `embeddings/providers.ts` | 37% | Should be covered as part of the embeddings operation family. |
171
188
  | `security/pii-detector.ts`, `semantic-injection-classifier.ts` | 49% / 49% | Security-relevant. |
172
189
  | `jobs/queue.ts`, `batch.ts` | 47% / 53% | |
173
190
  | `rag/file-ingestion.ts` | 43% | |
@@ -219,7 +236,18 @@ policy vocabulary is in place for a later release that revisits this.
219
236
 
220
237
  ---
221
238
 
222
- ## 4. Next release: 1.5.0 — durable operations and batch economics
239
+ ## 4. Shipped in 1.5.0 — embeddings as an operation family
240
+
241
+ `ai.embed()` with routing, per-input caching, batching, deduplication, budget, retry, audit, and
242
+ metrics; the existing factory functions became one adapter shape behind it, and
243
+ `toEmbeddingFunction()` keeps `MemoryVectorStore`, the semantic cache, and RAG ingestion working
244
+ unchanged. Embeddings refuse an unsupported option rather than dropping it, because a mis-sized
245
+ vector is silently incompatible with a populated store rather than merely different.
246
+
247
+ Deliberately deferred: the provider batch-API tier, which belongs with the durable operation handle
248
+ below rather than with the synchronous path.
249
+
250
+ ## 5. Next release: 1.6.0 — durable operations and batch economics
223
251
 
224
252
  The theme is work that outlives a process.
225
253
 
@@ -235,19 +263,17 @@ The theme is work that outlives a process.
235
263
  - **Distributed rate limiting and circuit breaking.** Redis-backed limiter adapter, plus a breaker
236
264
  that consumes existing `ProviderHealthMonitor` signals and trips routing away from a failing
237
265
  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
266
  - **Filesystem and S3-compatible asset stores** implementing the existing `AssetStore` interface,
241
267
  with retention, tenant ownership, checksums, streaming, and signing. Provider URLs remain temporary
242
268
  delivery locations, never durable storage.
243
269
  - **Generated model registry** from versioned provider data, consuming the 1.4.0 provenance fields.
244
270
  - **Cross-family observability.** Route image, voice, telephony, and realtime operations through the
245
- rate limiter, audit log, and metrics collector, closing gap 2.3 without waiting for the full
271
+ rate limiter, audit log, and metrics collector, closing gap 2.1 without waiting for the full
246
272
  lifecycle refactor.
247
273
 
248
274
  ---
249
275
 
250
- ## 5. 1.6.0 — image portability, then promotion
276
+ ## 6. 1.7.0 — image portability, then promotion
251
277
 
252
278
  Images cannot leave experimental until the neutral contract survives a second wire protocol.
253
279
 
@@ -275,7 +301,7 @@ label come off.
275
301
 
276
302
  ---
277
303
 
278
- ## 6. 2.0 candidate — one lifecycle for every operation
304
+ ## 7. 2.0 candidate — one lifecycle for every operation
279
305
 
280
306
  Create an internal typed lifecycle and adapt every family to it:
281
307
 
@@ -298,7 +324,7 @@ Also in the 2.0 window, because each requires a breaking change:
298
324
 
299
325
  ---
300
326
 
301
- ## 7. Longer-term backlog
327
+ ## 8. Longer-term backlog
302
328
 
303
329
  1. MCP client and server adapters with asset and tool interoperability.
304
330
  2. Video generation through the same asynchronous operation, job, and asset contracts.
@@ -323,7 +349,7 @@ Also in the 2.0 window, because each requires a breaking change:
323
349
 
324
350
  ---
325
351
 
326
- ## 8. Design notes carried forward
352
+ ## 9. Design notes carried forward
327
353
 
328
354
  These decisions predate this revision and still hold.
329
355
 
@@ -375,7 +401,7 @@ await ai.images.edit({
375
401
 
376
402
  ---
377
403
 
378
- ## 9. How an item graduates
404
+ ## 10. How an item graduates
379
405
 
380
406
  1. Provider-neutral types and a deterministic mock land first.
381
407
  2. One real adapter proves the contract; conformance fixtures cover it.
@@ -5,11 +5,13 @@ import type { NexusResponse, NexusStream } from '../types/response.js';
5
5
  import type { AgentConfig, AgentResult } from '../types/agent.js';
6
6
  import type { SpeechRequest, SpeechResponse, TranscriptionRequest, TranscriptionResponse, VoiceProvider, VoiceSessionConfig, VoiceTurnRequest, VoiceTurnResponse } from '../types/voice.js';
7
7
  import type { ImageProvider } from '../types/images.js';
8
+ import type { EmbeddingRequest, EmbeddingResponse, EmbeddingsProvider } from '../types/embeddings.js';
8
9
  import type { CreateCallRequest, CreateCallResponse, EndCallRequest, GetCallRequest, ListPhoneNumbersRequest, TelephonyCallDetails, TelephonyMediaStreamEvent, TelephonyOutboundAudioMessage, TelephonyPhoneNumber, TelephonyProvider, TelephonyResponseRequest, TelephonyStatusCallback, TelephonyWebhookResponse, TelephonyWebhookValidationRequest, UpdatePhoneNumberRequest } from '../types/telephony.js';
9
10
  import type { PipelineStep } from '../pipeline/types.js';
10
11
  import type { BaseProvider } from '../providers/base.js';
11
12
  import { type VoiceSession } from '../voice/index.js';
12
13
  import { ImageManager } from '../images/manager.js';
14
+ import { EmbeddingManager } from '../embeddings/manager.js';
13
15
  import { type VerificationOptions } from '../hallucination/verification.js';
14
16
  import { type SelfConsistencyOptions } from '../hallucination/consistency.js';
15
17
  import { type BatchOptions, type BatchItemResult } from '../jobs/batch.js';
@@ -25,6 +27,7 @@ export declare class NexusAI {
25
27
  /** Provider-neutral image generation and editing operations. */
26
28
  readonly images: ImageManager;
27
29
  private config;
30
+ private embeddingManager?;
28
31
  private providers;
29
32
  private router;
30
33
  private failover;
@@ -129,6 +132,23 @@ export declare class NexusAI {
129
132
  * Points a phone number at a voice webhook and/or status callback.
130
133
  */
131
134
  updatePhoneNumber(request: UpdatePhoneNumberRequest): Promise<TelephonyPhoneNumber>;
135
+ /**
136
+ * Provider-neutral embeddings, sharing this runtime's metrics, audit log, and rate limiter.
137
+ *
138
+ * Built on first access, so a runtime that never embeds pays nothing for the family. Adapters are
139
+ * derived from the provider credentials already in `providers`, which makes `ai.embed('text')`
140
+ * work without any embedding-specific configuration.
141
+ */
142
+ get embeddings(): EmbeddingManager;
143
+ /**
144
+ * Embeds one text or a batch through the configured embedding provider.
145
+ *
146
+ * `vectors[0]` is the answer for the single-string form; a batch comes back in input order no
147
+ * matter how many provider calls the model's batch limit required.
148
+ */
149
+ embed(request: EmbeddingRequest): Promise<EmbeddingResponse>;
150
+ /** Embeds one text and returns the vector alone. */
151
+ embedOne(text: string, options?: Omit<EmbeddingRequest, 'input'>): Promise<number[]>;
132
152
  /**
133
153
  * Registers a custom text provider at runtime.
134
154
  */
@@ -145,10 +165,15 @@ export declare class NexusAI {
145
165
  * Registers a custom telephony provider at runtime.
146
166
  */
147
167
  registerTelephonyProvider(name: string, provider: TelephonyProvider): this;
168
+ /**
169
+ * Registers a custom embedding provider at runtime.
170
+ */
171
+ registerEmbeddingProvider(name: string, provider: EmbeddingsProvider): this;
148
172
  hasProvider(name: string): boolean;
149
173
  hasVoiceProvider(name: string): boolean;
150
174
  hasImageProvider(name: string): boolean;
151
175
  hasTelephonyProvider(name: string): boolean;
176
+ hasEmbeddingProvider(name: string): boolean;
152
177
  /**
153
178
  * Lists configured text providers.
154
179
  */
@@ -156,6 +181,7 @@ export declare class NexusAI {
156
181
  listVoiceProviders(): string[];
157
182
  listImageProviders(): string[];
158
183
  listTelephonyProviders(): string[];
184
+ listEmbeddingProviders(): string[];
159
185
  /**
160
186
  * Adds a custom pipeline step.
161
187
  */
@@ -16,6 +16,7 @@ import { SecurityPipeline } from '../security/index.js';
16
16
  import { ContextWindowManager } from '../context/index.js';
17
17
  import { VoiceManager } from '../voice/index.js';
18
18
  import { ImageManager } from '../images/manager.js';
19
+ import { EmbeddingManager } from '../embeddings/manager.js';
19
20
  import { TelephonyManager } from '../telephony/index.js';
20
21
  import { TokenOptimizer } from '../optimizer/index.js';
21
22
  import { AgentLoop } from '../agent/loop.js';
@@ -47,6 +48,7 @@ export class NexusAI {
47
48
  /** Provider-neutral image generation and editing operations. */
48
49
  images;
49
50
  config;
51
+ embeddingManager;
50
52
  providers = new Map();
51
53
  router = new Router();
52
54
  failover = new FailoverExecutor();
@@ -506,6 +508,37 @@ export class NexusAI {
506
508
  async updatePhoneNumber(request) {
507
509
  return this.telephonyManager.updatePhoneNumber(request);
508
510
  }
511
+ /**
512
+ * Provider-neutral embeddings, sharing this runtime's metrics, audit log, and rate limiter.
513
+ *
514
+ * Built on first access, so a runtime that never embeds pays nothing for the family. Adapters are
515
+ * derived from the provider credentials already in `providers`, which makes `ai.embed('text')`
516
+ * work without any embedding-specific configuration.
517
+ */
518
+ get embeddings() {
519
+ if (!this.embeddingManager) {
520
+ this.embeddingManager = new EmbeddingManager(this.config.embeddings || {}, {
521
+ metrics: this.metrics,
522
+ auditLogger: this.auditLogger,
523
+ rateLimiter: this.rateLimiter,
524
+ providers: this.config.providers,
525
+ });
526
+ }
527
+ return this.embeddingManager;
528
+ }
529
+ /**
530
+ * Embeds one text or a batch through the configured embedding provider.
531
+ *
532
+ * `vectors[0]` is the answer for the single-string form; a batch comes back in input order no
533
+ * matter how many provider calls the model's batch limit required.
534
+ */
535
+ embed(request) {
536
+ return this.embeddings.embed(request);
537
+ }
538
+ /** Embeds one text and returns the vector alone. */
539
+ embedOne(text, options = {}) {
540
+ return this.embeddings.embedOne(text, options);
541
+ }
509
542
  /**
510
543
  * Registers a custom text provider at runtime.
511
544
  */
@@ -534,6 +567,13 @@ export class NexusAI {
534
567
  this.telephonyManager.registerProvider(name, provider);
535
568
  return this;
536
569
  }
570
+ /**
571
+ * Registers a custom embedding provider at runtime.
572
+ */
573
+ registerEmbeddingProvider(name, provider) {
574
+ this.embeddings.registerEmbeddingProvider(name, provider);
575
+ return this;
576
+ }
537
577
  hasProvider(name) {
538
578
  return this.providers.has(name);
539
579
  }
@@ -546,6 +586,9 @@ export class NexusAI {
546
586
  hasTelephonyProvider(name) {
547
587
  return this.telephonyManager.hasProvider(name);
548
588
  }
589
+ hasEmbeddingProvider(name) {
590
+ return this.embeddings.hasEmbeddingProvider(name);
591
+ }
549
592
  /**
550
593
  * Lists configured text providers.
551
594
  */
@@ -561,6 +604,9 @@ export class NexusAI {
561
604
  listTelephonyProviders() {
562
605
  return this.telephonyManager.listProviders();
563
606
  }
607
+ listEmbeddingProviders() {
608
+ return this.embeddings.listEmbeddingProviders();
609
+ }
564
610
  /**
565
611
  * Adds a custom pipeline step.
566
612
  */
@@ -0,0 +1,66 @@
1
+ import type { EmbeddingProviderCallContext, EmbeddingProviderCapabilities, EmbeddingProviderInfo, EmbeddingProviderRequest, EmbeddingProviderResult, EmbeddingsProvider } from '../types/embeddings.js';
2
+ /**
3
+ * Hosted embedding adapters.
4
+ *
5
+ * They share one file because each is a single request/response mapping over `fetch` with no SDK
6
+ * and no shared state; splitting them would repeat the HTTP and decoding helpers five times.
7
+ */
8
+ export interface EmbeddingAdapterOptions {
9
+ apiKey?: string;
10
+ baseUrl?: string;
11
+ model?: string;
12
+ headers?: Record<string, string>;
13
+ /** Replaces the global `fetch`, for proxying or tests. */
14
+ fetch?: typeof fetch;
15
+ }
16
+ /** Options for any server that speaks the OpenAI `/embeddings` protocol. */
17
+ export interface OpenAICompatibleEmbeddingOptions extends EmbeddingAdapterOptions {
18
+ /** Name this adapter reports and registers under. */
19
+ providerName?: string;
20
+ /** Replaces the declared capabilities, for a compatible server with different limits. */
21
+ capabilities?: Partial<EmbeddingProviderCapabilities>;
22
+ }
23
+ declare abstract class HttpEmbeddingProvider implements EmbeddingsProvider {
24
+ protected readonly options: EmbeddingAdapterOptions;
25
+ abstract readonly info: EmbeddingProviderInfo;
26
+ constructor(options?: EmbeddingAdapterOptions);
27
+ abstract embed(request: EmbeddingProviderRequest, context: EmbeddingProviderCallContext): Promise<EmbeddingProviderResult>;
28
+ protected post(url: string, body: unknown, model: string, signal: AbortSignal): Promise<unknown>;
29
+ protected authHeaders(): Record<string, string>;
30
+ protected baseUrl(fallback: string): string;
31
+ }
32
+ /**
33
+ * OpenAI and any OpenAI-compatible `/embeddings` endpoint.
34
+ *
35
+ * `baseUrl` points it at Azure OpenAI, a gateway, or a self-hosted server; `model` sets the default
36
+ * when the request names none.
37
+ */
38
+ export declare class OpenAIEmbeddingProvider extends HttpEmbeddingProvider {
39
+ readonly info: EmbeddingProviderInfo;
40
+ constructor(options?: OpenAICompatibleEmbeddingOptions);
41
+ embed(request: EmbeddingProviderRequest, context: EmbeddingProviderCallContext): Promise<EmbeddingProviderResult>;
42
+ }
43
+ /** Google Generative Language `batchEmbedContents`. */
44
+ export declare class GoogleEmbeddingProvider extends HttpEmbeddingProvider {
45
+ readonly info: EmbeddingProviderInfo;
46
+ constructor(options?: EmbeddingAdapterOptions);
47
+ protected authHeaders(): Record<string, string>;
48
+ embed(request: EmbeddingProviderRequest, context: EmbeddingProviderCallContext): Promise<EmbeddingProviderResult>;
49
+ }
50
+ /** Cohere v2 `/embed`. */
51
+ export declare class CohereEmbeddingProvider extends HttpEmbeddingProvider {
52
+ readonly info: EmbeddingProviderInfo;
53
+ constructor(options?: EmbeddingAdapterOptions);
54
+ embed(request: EmbeddingProviderRequest, context: EmbeddingProviderCallContext): Promise<EmbeddingProviderResult>;
55
+ }
56
+ /** Mistral `/v1/embeddings`, which follows the OpenAI request and response shape. */
57
+ export declare class MistralEmbeddingProvider extends OpenAIEmbeddingProvider {
58
+ constructor(options?: EmbeddingAdapterOptions);
59
+ }
60
+ /** Local Ollama `/api/embed`. */
61
+ export declare class OllamaEmbeddingProvider extends HttpEmbeddingProvider {
62
+ readonly info: EmbeddingProviderInfo;
63
+ constructor(options?: EmbeddingAdapterOptions);
64
+ embed(request: EmbeddingProviderRequest, context: EmbeddingProviderCallContext): Promise<EmbeddingProviderResult>;
65
+ }
66
+ export {};