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.
- package/API_STABILITY.md +25 -0
- package/CHANGELOG.md +67 -1
- package/README.md +97 -0
- package/ROADMAP.md +55 -29
- 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/index.d.ts +7 -2
- package/dist/index.js +5 -1
- 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-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/index.d.ts +7 -2
- package/dist-cjs/index.js +28 -2
- package/dist-cjs/ops/rate-limiter.d.ts +11 -2
- 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/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.
|
|
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.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
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
|
|
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
|
|
113
|
-
`
|
|
114
|
-
`
|
|
115
|
-
|
|
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
|
|
135
|
+
### 2.2 Embeddings beyond text retrieval
|
|
121
136
|
|
|
122
|
-
`
|
|
123
|
-
|
|
124
|
-
|
|
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%.
|
|
164
|
-
|
|
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.
|
|
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.
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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.
|
package/dist/core/nexus.d.ts
CHANGED
|
@@ -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
|
*/
|
package/dist/core/nexus.js
CHANGED
|
@@ -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 {};
|