@voyantjs/catalog-rag 0.19.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/README.md +48 -0
- package/dist/embeddings/contract.d.ts +85 -0
- package/dist/embeddings/contract.d.ts.map +1 -0
- package/dist/embeddings/contract.js +42 -0
- package/dist/embeddings/contract.test.d.ts +2 -0
- package/dist/embeddings/contract.test.d.ts.map +1 -0
- package/dist/embeddings/contract.test.js +30 -0
- package/dist/embeddings/gemini.d.ts +110 -0
- package/dist/embeddings/gemini.d.ts.map +1 -0
- package/dist/embeddings/gemini.js +118 -0
- package/dist/embeddings/gemini.test.d.ts +2 -0
- package/dist/embeddings/gemini.test.d.ts.map +1 -0
- package/dist/embeddings/gemini.test.js +126 -0
- package/dist/embeddings/model-registry.d.ts +62 -0
- package/dist/embeddings/model-registry.d.ts.map +1 -0
- package/dist/embeddings/model-registry.js +78 -0
- package/dist/embeddings/model-registry.test.d.ts +2 -0
- package/dist/embeddings/model-registry.test.d.ts.map +1 -0
- package/dist/embeddings/model-registry.test.js +81 -0
- package/dist/embeddings/openai.d.ts +81 -0
- package/dist/embeddings/openai.d.ts.map +1 -0
- package/dist/embeddings/openai.js +123 -0
- package/dist/embeddings/openai.test.d.ts +2 -0
- package/dist/embeddings/openai.test.d.ts.map +1 -0
- package/dist/embeddings/openai.test.js +157 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/search/federate.d.ts +57 -0
- package/dist/search/federate.d.ts.map +1 -0
- package/dist/search/federate.js +103 -0
- package/dist/search/federate.test.d.ts +2 -0
- package/dist/search/federate.test.d.ts.map +1 -0
- package/dist/search/federate.test.js +146 -0
- package/dist/search/semantic.d.ts +58 -0
- package/dist/search/semantic.d.ts.map +1 -0
- package/dist/search/semantic.js +71 -0
- package/dist/search/semantic.test.d.ts +2 -0
- package/dist/search/semantic.test.d.ts.map +1 -0
- package/dist/search/semantic.test.js +143 -0
- package/package.json +75 -0
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# @voyantjs/catalog-rag
|
|
2
|
+
|
|
3
|
+
Phase 2 of the catalog plane. Adds vector embeddings, AI-agent access patterns, and the MCP server scaffolding on top of the Phase 1 foundation in `@voyantjs/catalog`.
|
|
4
|
+
|
|
5
|
+
See [`docs/architecture/catalog-rag-architecture.md`](../../docs/architecture/catalog-rag-architecture.md) for the full design.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @voyantjs/catalog-rag
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## What's in the box
|
|
14
|
+
|
|
15
|
+
- **`./embeddings/contract`** — `EmbeddingProvider` interface plus capability declarations (model id, dimensions, max tokens, max batch size, supported languages).
|
|
16
|
+
- **`./embeddings/openai`** — Default `EmbeddingProvider` implementation backed by OpenAI's embeddings API. Uses native `fetch` (works in Cloudflare Workers + Node).
|
|
17
|
+
- **`./embeddings/model-registry`** — Helpers for tracking embedding model identity per document, validating dimension compatibility at deployment startup, and supporting mixed-model migration windows.
|
|
18
|
+
- **`./search/semantic`** — Search orchestration helpers: build a hybrid `SearchRequest` with `mode: "semantic" | "hybrid" | "keyword"`, attach a `query_embedding` if the caller brought one, and delegate to the underlying `IndexerAdapter`.
|
|
19
|
+
- **`./search/federate`** — Cross-audience federated query helper for staff actors that need to search non-staff audience pools (architecture §7).
|
|
20
|
+
|
|
21
|
+
## Phase relationship
|
|
22
|
+
|
|
23
|
+
Phase 2 is **additive** on Phase 1. It does not modify the field-policy contract, the overlay store, the snapshot graph, or the source-adapter contract. The `IndexerAdapter` capability flags (`supportsVectorFields`, `supportsHybridSearch`, `vectorDimensions`, `supportsCrossAudienceFederation`) are already declared in Phase 1; Phase 2 deployments fill them in.
|
|
24
|
+
|
|
25
|
+
## Architectural rules (enforced by code, not just convention)
|
|
26
|
+
|
|
27
|
+
- **AI agents query the API, not the vector database directly.** Visibility filtering, overlay resolution, and audit all happen at the API layer. The vector DB is implementation detail.
|
|
28
|
+
- **Per-audience embedding pools.** Vectors are strictly per-audience — no cross-audience denormalization on the vector side. Customer chatbots' nearest-neighbor search runs against vectors that only ever saw customer-visible content.
|
|
29
|
+
- **Model versioning is explicit.** Each search-index document carries an `embedding_model_id`. Switching models is a deliberate `bulkReindex` migration, not silent.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { createOpenAIEmbeddingProvider } from "@voyantjs/catalog-rag/embeddings/openai"
|
|
35
|
+
|
|
36
|
+
const embeddings = createOpenAIEmbeddingProvider({
|
|
37
|
+
apiKey: env.OPENAI_API_KEY,
|
|
38
|
+
model: "text-embedding-3-small", // 1536 dimensions, multilingual
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Generate embeddings for a batch of catalog texts
|
|
42
|
+
const vectors = await embeddings.embed([
|
|
43
|
+
"Bali Wellness Retreat",
|
|
44
|
+
"Sunset Yacht Cruise",
|
|
45
|
+
])
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
See `docs/architecture/catalog-rag-architecture.md` for the full design and integration patterns.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmbeddingProvider contract — engine-agnostic interface for generating
|
|
3
|
+
* vector embeddings from text.
|
|
4
|
+
*
|
|
5
|
+
* Voyant ships native OpenAI as the v1 default (see `./openai.ts`).
|
|
6
|
+
* Deployments swap in Voyage AI, local sentence-transformers, Cohere,
|
|
7
|
+
* or any other engine by satisfying this contract.
|
|
8
|
+
*
|
|
9
|
+
* See `docs/architecture/catalog-rag-architecture.md` §6 for the design.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Capability metadata declared by the provider. Used by the catalog plane
|
|
13
|
+
* at deployment startup to validate that the configured embedding model is
|
|
14
|
+
* compatible with the configured `IndexerAdapter`'s `vectorDimensions`.
|
|
15
|
+
*/
|
|
16
|
+
export interface EmbeddingProviderCapabilities {
|
|
17
|
+
/**
|
|
18
|
+
* Stable identifier for this provider+model combo. Conventional shape:
|
|
19
|
+
* `<vendor>/<model-name>/<version>` — e.g. `openai/text-embedding-3-small/v1`.
|
|
20
|
+
* Stamped onto every search-index document for migration safety.
|
|
21
|
+
*/
|
|
22
|
+
modelId: string;
|
|
23
|
+
/**
|
|
24
|
+
* Fixed dimensionality of vectors produced by `embed()`. Must match the
|
|
25
|
+
* `IndexerAdapter`'s declared `vectorDimensions` or the catalog plane
|
|
26
|
+
* fails fast at deployment.
|
|
27
|
+
*/
|
|
28
|
+
dimensions: number;
|
|
29
|
+
/**
|
|
30
|
+
* Maximum input token length per text. Texts longer than this should be
|
|
31
|
+
* truncated by the caller before passing in (the provider rejects
|
|
32
|
+
* oversize inputs rather than truncating silently).
|
|
33
|
+
*/
|
|
34
|
+
maxTokensPerInput: number;
|
|
35
|
+
/**
|
|
36
|
+
* Maximum batch size for a single `embed()` call. Larger batches must be
|
|
37
|
+
* chunked by the caller. Common values: OpenAI 2048, Voyage 128.
|
|
38
|
+
*/
|
|
39
|
+
maxBatchSize: number;
|
|
40
|
+
/**
|
|
41
|
+
* ISO language codes the model handles well. `null` means multilingual
|
|
42
|
+
* or language-agnostic (e.g. OpenAI's `text-embedding-3-small`).
|
|
43
|
+
*/
|
|
44
|
+
supportedLanguages?: string[] | null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The EmbeddingProvider contract. Implementations come from anywhere —
|
|
48
|
+
* vendor SDKs, local models, custom wrappers. No implementer is privileged.
|
|
49
|
+
*
|
|
50
|
+
* Synchronous-shaped at the type level; concrete impls return promises.
|
|
51
|
+
*/
|
|
52
|
+
export interface EmbeddingProvider {
|
|
53
|
+
readonly capabilities: EmbeddingProviderCapabilities;
|
|
54
|
+
/**
|
|
55
|
+
* Generate one embedding per input text. Returns vectors in the same
|
|
56
|
+
* order as the input. The result array has `texts.length` entries,
|
|
57
|
+
* each with `capabilities.dimensions` floats.
|
|
58
|
+
*
|
|
59
|
+
* Throws on:
|
|
60
|
+
* - Provider/transport errors (rate limits, auth, network)
|
|
61
|
+
* - Inputs exceeding `capabilities.maxBatchSize` or `maxTokensPerInput`
|
|
62
|
+
*
|
|
63
|
+
* Implementations MUST NOT silently truncate or drop inputs.
|
|
64
|
+
*/
|
|
65
|
+
embed(texts: string[]): Promise<number[][]>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Standard error code adapters should throw when a constraint is violated.
|
|
69
|
+
* The catalog plane translates these into structured error responses.
|
|
70
|
+
*/
|
|
71
|
+
export declare const EMBEDDING_BATCH_TOO_LARGE: "EMBEDDING_BATCH_TOO_LARGE";
|
|
72
|
+
export declare const EMBEDDING_INPUT_TOO_LONG: "EMBEDDING_INPUT_TOO_LONG";
|
|
73
|
+
export declare const EMBEDDING_PROVIDER_ERROR: "EMBEDDING_PROVIDER_ERROR";
|
|
74
|
+
export declare class EmbeddingProviderError extends Error {
|
|
75
|
+
readonly code: typeof EMBEDDING_BATCH_TOO_LARGE | typeof EMBEDDING_INPUT_TOO_LONG | typeof EMBEDDING_PROVIDER_ERROR;
|
|
76
|
+
readonly cause?: unknown | undefined;
|
|
77
|
+
constructor(code: typeof EMBEDDING_BATCH_TOO_LARGE | typeof EMBEDDING_INPUT_TOO_LONG | typeof EMBEDDING_PROVIDER_ERROR, message: string, cause?: unknown | undefined);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Thin helper for chunking a large input array into batches the provider
|
|
81
|
+
* can handle. Provider implementations call this internally; callers can
|
|
82
|
+
* also use it directly for finer-grained control.
|
|
83
|
+
*/
|
|
84
|
+
export declare function chunkForBatch<T>(items: T[], maxBatchSize: number): T[][];
|
|
85
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/embeddings/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAA;IAEf;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAA;IAElB;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,CAAA;IAEzB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAA;IAEpB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,YAAY,EAAE,6BAA6B,CAAA;IAEpD;;;;;;;;;;OAUG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;CAC5C;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAG,2BAAoC,CAAA;AAC7E,eAAO,MAAM,wBAAwB,EAAG,0BAAmC,CAAA;AAC3E,eAAO,MAAM,wBAAwB,EAAG,0BAAmC,CAAA;AAE3E,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,IAAI,EAChB,OAAO,yBAAyB,GAChC,OAAO,wBAAwB,GAC/B,OAAO,wBAAwB;aAEnB,KAAK,CAAC,EAAE,OAAO;gBALf,IAAI,EAChB,OAAO,yBAAyB,GAChC,OAAO,wBAAwB,GAC/B,OAAO,wBAAwB,EACnC,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,OAAO,YAAA;CAKlC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CASxE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmbeddingProvider contract — engine-agnostic interface for generating
|
|
3
|
+
* vector embeddings from text.
|
|
4
|
+
*
|
|
5
|
+
* Voyant ships native OpenAI as the v1 default (see `./openai.ts`).
|
|
6
|
+
* Deployments swap in Voyage AI, local sentence-transformers, Cohere,
|
|
7
|
+
* or any other engine by satisfying this contract.
|
|
8
|
+
*
|
|
9
|
+
* See `docs/architecture/catalog-rag-architecture.md` §6 for the design.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Standard error code adapters should throw when a constraint is violated.
|
|
13
|
+
* The catalog plane translates these into structured error responses.
|
|
14
|
+
*/
|
|
15
|
+
export const EMBEDDING_BATCH_TOO_LARGE = "EMBEDDING_BATCH_TOO_LARGE";
|
|
16
|
+
export const EMBEDDING_INPUT_TOO_LONG = "EMBEDDING_INPUT_TOO_LONG";
|
|
17
|
+
export const EMBEDDING_PROVIDER_ERROR = "EMBEDDING_PROVIDER_ERROR";
|
|
18
|
+
export class EmbeddingProviderError extends Error {
|
|
19
|
+
code;
|
|
20
|
+
cause;
|
|
21
|
+
constructor(code, message, cause) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.cause = cause;
|
|
25
|
+
this.name = "EmbeddingProviderError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Thin helper for chunking a large input array into batches the provider
|
|
30
|
+
* can handle. Provider implementations call this internally; callers can
|
|
31
|
+
* also use it directly for finer-grained control.
|
|
32
|
+
*/
|
|
33
|
+
export function chunkForBatch(items, maxBatchSize) {
|
|
34
|
+
if (maxBatchSize <= 0) {
|
|
35
|
+
throw new Error("maxBatchSize must be positive");
|
|
36
|
+
}
|
|
37
|
+
const batches = [];
|
|
38
|
+
for (let i = 0; i < items.length; i += maxBatchSize) {
|
|
39
|
+
batches.push(items.slice(i, i + maxBatchSize));
|
|
40
|
+
}
|
|
41
|
+
return batches;
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.test.d.ts","sourceRoot":"","sources":["../../src/embeddings/contract.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { chunkForBatch, EMBEDDING_BATCH_TOO_LARGE, EmbeddingProviderError } from "./contract.js";
|
|
3
|
+
describe("chunkForBatch", () => {
|
|
4
|
+
it("returns a single batch when items fit in maxBatchSize", () => {
|
|
5
|
+
expect(chunkForBatch([1, 2, 3], 5)).toEqual([[1, 2, 3]]);
|
|
6
|
+
});
|
|
7
|
+
it("splits into multiple batches when items exceed maxBatchSize", () => {
|
|
8
|
+
expect(chunkForBatch([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]);
|
|
9
|
+
});
|
|
10
|
+
it("returns an empty array for an empty input", () => {
|
|
11
|
+
expect(chunkForBatch([], 10)).toEqual([]);
|
|
12
|
+
});
|
|
13
|
+
it("throws on non-positive maxBatchSize", () => {
|
|
14
|
+
expect(() => chunkForBatch([1], 0)).toThrow(/positive/);
|
|
15
|
+
expect(() => chunkForBatch([1], -1)).toThrow(/positive/);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe("EmbeddingProviderError", () => {
|
|
19
|
+
it("carries the standard error code", () => {
|
|
20
|
+
const err = new EmbeddingProviderError(EMBEDDING_BATCH_TOO_LARGE, "too big");
|
|
21
|
+
expect(err.code).toBe(EMBEDDING_BATCH_TOO_LARGE);
|
|
22
|
+
expect(err.message).toBe("too big");
|
|
23
|
+
expect(err.name).toBe("EmbeddingProviderError");
|
|
24
|
+
});
|
|
25
|
+
it("optionally retains the underlying cause", () => {
|
|
26
|
+
const cause = new Error("original");
|
|
27
|
+
const err = new EmbeddingProviderError(EMBEDDING_BATCH_TOO_LARGE, "wrapped", cause);
|
|
28
|
+
expect(err.cause).toBe(cause);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmbeddingProvider implementation backed by Google's Gemini embeddings API.
|
|
3
|
+
*
|
|
4
|
+
* Uses native `fetch` so it works in Cloudflare Workers + Node + browsers
|
|
5
|
+
* without an SDK dependency. Templates pass in the API key (and optionally
|
|
6
|
+
* a custom `baseUrl` for proxies).
|
|
7
|
+
*
|
|
8
|
+
* Models supported by default:
|
|
9
|
+
* - `gemini-embedding-001` — 3072d, multilingual, current recommendation.
|
|
10
|
+
* Supports Matryoshka representation learning (MRL) — request a smaller
|
|
11
|
+
* output dimension via `outputDimensionality` to reduce storage cost.
|
|
12
|
+
* - `text-embedding-004` — 768d, multilingual, legacy stable.
|
|
13
|
+
*
|
|
14
|
+
* See `docs/architecture/catalog-rag-architecture.md` §6 for the design.
|
|
15
|
+
*/
|
|
16
|
+
import { chunkForBatch, type EmbeddingProvider } from "./contract.js";
|
|
17
|
+
/**
|
|
18
|
+
* Known Gemini embedding models. `dimensions` is the *native* size; when
|
|
19
|
+
* `outputDimensionality` is set in the provider options, the effective
|
|
20
|
+
* vector size is whatever the caller requested (Gemini truncates server-side
|
|
21
|
+
* via MRL on `gemini-embedding-001`).
|
|
22
|
+
*/
|
|
23
|
+
declare const GEMINI_MODELS: {
|
|
24
|
+
readonly "gemini-embedding-001": {
|
|
25
|
+
readonly dimensions: 3072;
|
|
26
|
+
readonly maxTokensPerInput: 2048;
|
|
27
|
+
readonly maxBatchSize: 100;
|
|
28
|
+
readonly multilingual: true;
|
|
29
|
+
readonly supportsOutputDimensionality: true;
|
|
30
|
+
};
|
|
31
|
+
readonly "text-embedding-004": {
|
|
32
|
+
readonly dimensions: 768;
|
|
33
|
+
readonly maxTokensPerInput: 2048;
|
|
34
|
+
readonly maxBatchSize: 100;
|
|
35
|
+
readonly multilingual: true;
|
|
36
|
+
readonly supportsOutputDimensionality: false;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export type GeminiEmbeddingModel = keyof typeof GEMINI_MODELS;
|
|
40
|
+
/**
|
|
41
|
+
* Tasks Gemini optimizes embeddings for. `RETRIEVAL_DOCUMENT` is the right
|
|
42
|
+
* default for indexed catalog docs; switch to `RETRIEVAL_QUERY` when
|
|
43
|
+
* embedding a search query at read time. The provider keeps a single task
|
|
44
|
+
* type per instance — wire two providers if you index and query separately.
|
|
45
|
+
*/
|
|
46
|
+
export type GeminiTaskType = "RETRIEVAL_DOCUMENT" | "RETRIEVAL_QUERY" | "SEMANTIC_SIMILARITY" | "CLASSIFICATION" | "CLUSTERING" | "QUESTION_ANSWERING" | "FACT_VERIFICATION" | "CODE_RETRIEVAL_QUERY";
|
|
47
|
+
export interface GeminiEmbeddingProviderOptions {
|
|
48
|
+
/**
|
|
49
|
+
* API key to authenticate with. In `auth: "google"` mode, this is the
|
|
50
|
+
* Google AI Studio key. In `auth: "bearer"` mode (e.g. when routing
|
|
51
|
+
* through the Voyant Cloud AI gateway), this is the gateway's bearer
|
|
52
|
+
* token.
|
|
53
|
+
*/
|
|
54
|
+
apiKey: string;
|
|
55
|
+
/**
|
|
56
|
+
* How to attach the API key to outbound requests.
|
|
57
|
+
* - `"google"` (default) — `x-goog-api-key: <apiKey>`. Use when
|
|
58
|
+
* talking directly to `generativelanguage.googleapis.com`.
|
|
59
|
+
* - `"bearer"` — `Authorization: Bearer <apiKey>`. Use when routing
|
|
60
|
+
* through the Voyant Cloud `/ai/v1/gemini` gateway, which forwards
|
|
61
|
+
* to Google with the org's saved provider key.
|
|
62
|
+
*/
|
|
63
|
+
auth?: "google" | "bearer";
|
|
64
|
+
/**
|
|
65
|
+
* Embedding model to use. Default: `gemini-embedding-001`.
|
|
66
|
+
* Switching models is a deliberate `bulkReindex` operation — the catalog
|
|
67
|
+
* plane scopes vector queries to documents matching the active
|
|
68
|
+
* `embedding_model_id`, so mid-migration mixes are handled cleanly.
|
|
69
|
+
*/
|
|
70
|
+
model?: GeminiEmbeddingModel;
|
|
71
|
+
/**
|
|
72
|
+
* Output vector size for MRL-capable models. When omitted, the model's
|
|
73
|
+
* native dimension is used. Only `gemini-embedding-001` supports this.
|
|
74
|
+
* Smaller dims reduce storage / query cost at some quality loss.
|
|
75
|
+
*/
|
|
76
|
+
outputDimensionality?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Task type the embedded text will be used for. Default:
|
|
79
|
+
* `RETRIEVAL_DOCUMENT` (right for ingestion). Use `RETRIEVAL_QUERY` for
|
|
80
|
+
* read-side query embedding.
|
|
81
|
+
*/
|
|
82
|
+
taskType?: GeminiTaskType;
|
|
83
|
+
/**
|
|
84
|
+
* Override the API base URL — useful for a corporate proxy or a custom
|
|
85
|
+
* Vertex-compatible deployment. Default:
|
|
86
|
+
* `https://generativelanguage.googleapis.com/v1beta`.
|
|
87
|
+
*/
|
|
88
|
+
baseUrl?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Optional `fetch` override for testing or custom transport. Default:
|
|
91
|
+
* the global `fetch`. Must follow the standard Fetch API contract.
|
|
92
|
+
*/
|
|
93
|
+
fetchImpl?: typeof fetch;
|
|
94
|
+
/**
|
|
95
|
+
* Override the model id stamped onto search-index documents. Defaults
|
|
96
|
+
* to `gemini/<model>/v1` — keep this stable across deployments so
|
|
97
|
+
* documents stay queryable across instances.
|
|
98
|
+
*/
|
|
99
|
+
modelId?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Build a Gemini-backed EmbeddingProvider.
|
|
103
|
+
*/
|
|
104
|
+
export declare function createGeminiEmbeddingProvider(options: GeminiEmbeddingProviderOptions): EmbeddingProvider;
|
|
105
|
+
/**
|
|
106
|
+
* Re-export the chunking helper alongside the Gemini provider so callers
|
|
107
|
+
* can `embedBatched(provider, texts)` for very large inputs.
|
|
108
|
+
*/
|
|
109
|
+
export { chunkForBatch, GEMINI_MODELS };
|
|
110
|
+
//# sourceMappingURL=gemini.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/embeddings/gemini.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,aAAa,EAIb,KAAK,iBAAiB,EAGvB,MAAM,eAAe,CAAA;AAEtB;;;;;GAKG;AACH,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;CAeT,CAAA;AAEV,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,aAAa,CAAA;AAE7D;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GACtB,oBAAoB,GACpB,iBAAiB,GACjB,qBAAqB,GACrB,gBAAgB,GAChB,YAAY,GACZ,oBAAoB,GACpB,mBAAmB,GACnB,sBAAsB,CAAA;AAE1B,MAAM,WAAW,8BAA8B;IAC7C;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC1B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,oBAAoB,CAAA;IAC5B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAcD;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,8BAA8B,GACtC,iBAAiB,CA4FnB;AAED;;;GAGG;AACH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,CAAA"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmbeddingProvider implementation backed by Google's Gemini embeddings API.
|
|
3
|
+
*
|
|
4
|
+
* Uses native `fetch` so it works in Cloudflare Workers + Node + browsers
|
|
5
|
+
* without an SDK dependency. Templates pass in the API key (and optionally
|
|
6
|
+
* a custom `baseUrl` for proxies).
|
|
7
|
+
*
|
|
8
|
+
* Models supported by default:
|
|
9
|
+
* - `gemini-embedding-001` — 3072d, multilingual, current recommendation.
|
|
10
|
+
* Supports Matryoshka representation learning (MRL) — request a smaller
|
|
11
|
+
* output dimension via `outputDimensionality` to reduce storage cost.
|
|
12
|
+
* - `text-embedding-004` — 768d, multilingual, legacy stable.
|
|
13
|
+
*
|
|
14
|
+
* See `docs/architecture/catalog-rag-architecture.md` §6 for the design.
|
|
15
|
+
*/
|
|
16
|
+
import { chunkForBatch, EMBEDDING_BATCH_TOO_LARGE, EMBEDDING_INPUT_TOO_LONG, EMBEDDING_PROVIDER_ERROR, EmbeddingProviderError, } from "./contract.js";
|
|
17
|
+
/**
|
|
18
|
+
* Known Gemini embedding models. `dimensions` is the *native* size; when
|
|
19
|
+
* `outputDimensionality` is set in the provider options, the effective
|
|
20
|
+
* vector size is whatever the caller requested (Gemini truncates server-side
|
|
21
|
+
* via MRL on `gemini-embedding-001`).
|
|
22
|
+
*/
|
|
23
|
+
const GEMINI_MODELS = {
|
|
24
|
+
"gemini-embedding-001": {
|
|
25
|
+
dimensions: 3072,
|
|
26
|
+
maxTokensPerInput: 2048,
|
|
27
|
+
maxBatchSize: 100,
|
|
28
|
+
multilingual: true,
|
|
29
|
+
supportsOutputDimensionality: true,
|
|
30
|
+
},
|
|
31
|
+
"text-embedding-004": {
|
|
32
|
+
dimensions: 768,
|
|
33
|
+
maxTokensPerInput: 2048,
|
|
34
|
+
maxBatchSize: 100,
|
|
35
|
+
multilingual: true,
|
|
36
|
+
supportsOutputDimensionality: false,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Build a Gemini-backed EmbeddingProvider.
|
|
41
|
+
*/
|
|
42
|
+
export function createGeminiEmbeddingProvider(options) {
|
|
43
|
+
const model = options.model ?? "gemini-embedding-001";
|
|
44
|
+
const modelInfo = GEMINI_MODELS[model];
|
|
45
|
+
const baseUrl = (options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta").replace(/\/$/, "");
|
|
46
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
47
|
+
const taskType = options.taskType ?? "RETRIEVAL_DOCUMENT";
|
|
48
|
+
const auth = options.auth ?? "google";
|
|
49
|
+
const outputDimensionality = options.outputDimensionality && modelInfo.supportsOutputDimensionality
|
|
50
|
+
? options.outputDimensionality
|
|
51
|
+
: undefined;
|
|
52
|
+
const dimensions = outputDimensionality ?? modelInfo.dimensions;
|
|
53
|
+
const capabilities = {
|
|
54
|
+
modelId: options.modelId ?? `gemini/${model}/v1`,
|
|
55
|
+
dimensions,
|
|
56
|
+
maxTokensPerInput: modelInfo.maxTokensPerInput,
|
|
57
|
+
maxBatchSize: modelInfo.maxBatchSize,
|
|
58
|
+
supportedLanguages: modelInfo.multilingual ? null : undefined,
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
capabilities,
|
|
62
|
+
async embed(texts) {
|
|
63
|
+
if (texts.length === 0)
|
|
64
|
+
return [];
|
|
65
|
+
if (texts.length > capabilities.maxBatchSize) {
|
|
66
|
+
throw new EmbeddingProviderError(EMBEDDING_BATCH_TOO_LARGE, `Gemini embedding batch size ${texts.length} exceeds max ${capabilities.maxBatchSize}; chunk inputs via chunkForBatch() first`);
|
|
67
|
+
}
|
|
68
|
+
const url = `${baseUrl}/models/${model}:batchEmbedContents`;
|
|
69
|
+
const body = JSON.stringify({
|
|
70
|
+
requests: texts.map((text) => ({
|
|
71
|
+
model: `models/${model}`,
|
|
72
|
+
content: { parts: [{ text }] },
|
|
73
|
+
taskType,
|
|
74
|
+
...(outputDimensionality ? { outputDimensionality } : {}),
|
|
75
|
+
})),
|
|
76
|
+
});
|
|
77
|
+
const authHeaders = auth === "bearer"
|
|
78
|
+
? { Authorization: `Bearer ${options.apiKey}` }
|
|
79
|
+
: { "x-goog-api-key": options.apiKey };
|
|
80
|
+
let response;
|
|
81
|
+
try {
|
|
82
|
+
response = await fetchImpl(url, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
headers: {
|
|
85
|
+
"Content-Type": "application/json",
|
|
86
|
+
...authHeaders,
|
|
87
|
+
},
|
|
88
|
+
body,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch (cause) {
|
|
92
|
+
throw new EmbeddingProviderError(EMBEDDING_PROVIDER_ERROR, "Gemini embeddings request failed at the network layer", cause);
|
|
93
|
+
}
|
|
94
|
+
if (!response.ok) {
|
|
95
|
+
const text = await response.text().catch(() => "");
|
|
96
|
+
let parsed;
|
|
97
|
+
try {
|
|
98
|
+
parsed = JSON.parse(text);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// ignore parse failure; surface the raw text
|
|
102
|
+
}
|
|
103
|
+
const message = parsed?.error?.message ?? text ?? `HTTP ${response.status}`;
|
|
104
|
+
const status = parsed?.error?.status ?? "";
|
|
105
|
+
const code = status === "INVALID_ARGUMENT" ? EMBEDDING_INPUT_TOO_LONG : EMBEDDING_PROVIDER_ERROR;
|
|
106
|
+
throw new EmbeddingProviderError(code, `Gemini embeddings request failed (${response.status}): ${message}`);
|
|
107
|
+
}
|
|
108
|
+
const json = (await response.json());
|
|
109
|
+
// Gemini returns embeddings in input order — no `index` field.
|
|
110
|
+
return json.embeddings.map((entry) => entry.values);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Re-export the chunking helper alongside the Gemini provider so callers
|
|
116
|
+
* can `embedBatched(provider, texts)` for very large inputs.
|
|
117
|
+
*/
|
|
118
|
+
export { chunkForBatch, GEMINI_MODELS };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.test.d.ts","sourceRoot":"","sources":["../../src/embeddings/gemini.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { EMBEDDING_BATCH_TOO_LARGE, EmbeddingProviderError } from "./contract.js";
|
|
3
|
+
import { createGeminiEmbeddingProvider, GEMINI_MODELS } from "./gemini.js";
|
|
4
|
+
function mockFetch(response) {
|
|
5
|
+
return vi.fn(async () => {
|
|
6
|
+
return new Response(typeof response.json === "function" ? JSON.stringify(await response.json()) : "", {
|
|
7
|
+
status: response.status ?? (response.ok ? 200 : 400),
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
describe("createGeminiEmbeddingProvider", () => {
|
|
12
|
+
it("declares correct capabilities for the default model", () => {
|
|
13
|
+
const provider = createGeminiEmbeddingProvider({
|
|
14
|
+
apiKey: "key",
|
|
15
|
+
fetchImpl: mockFetch({ ok: true, json: async () => ({ embeddings: [] }) }),
|
|
16
|
+
});
|
|
17
|
+
expect(provider.capabilities.modelId).toBe("gemini/gemini-embedding-001/v1");
|
|
18
|
+
expect(provider.capabilities.dimensions).toBe(3072);
|
|
19
|
+
expect(provider.capabilities.maxBatchSize).toBe(100);
|
|
20
|
+
});
|
|
21
|
+
it("honors outputDimensionality on MRL-capable models", () => {
|
|
22
|
+
const provider = createGeminiEmbeddingProvider({
|
|
23
|
+
apiKey: "key",
|
|
24
|
+
outputDimensionality: 768,
|
|
25
|
+
fetchImpl: mockFetch({ ok: true, json: async () => ({ embeddings: [] }) }),
|
|
26
|
+
});
|
|
27
|
+
expect(provider.capabilities.dimensions).toBe(768);
|
|
28
|
+
});
|
|
29
|
+
it("ignores outputDimensionality on legacy text-embedding-004", () => {
|
|
30
|
+
const provider = createGeminiEmbeddingProvider({
|
|
31
|
+
apiKey: "key",
|
|
32
|
+
model: "text-embedding-004",
|
|
33
|
+
outputDimensionality: 1536,
|
|
34
|
+
fetchImpl: mockFetch({ ok: true, json: async () => ({ embeddings: [] }) }),
|
|
35
|
+
});
|
|
36
|
+
expect(provider.capabilities.dimensions).toBe(GEMINI_MODELS["text-embedding-004"].dimensions);
|
|
37
|
+
});
|
|
38
|
+
it("returns vectors in input order from a batch response", async () => {
|
|
39
|
+
const provider = createGeminiEmbeddingProvider({
|
|
40
|
+
apiKey: "key",
|
|
41
|
+
fetchImpl: mockFetch({
|
|
42
|
+
ok: true,
|
|
43
|
+
json: async () => ({
|
|
44
|
+
embeddings: [{ values: [0.1] }, { values: [0.2] }, { values: [0.3] }],
|
|
45
|
+
}),
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
const vectors = await provider.embed(["a", "b", "c"]);
|
|
49
|
+
expect(vectors).toEqual([[0.1], [0.2], [0.3]]);
|
|
50
|
+
});
|
|
51
|
+
it("returns an empty array for empty input without hitting the API", async () => {
|
|
52
|
+
const fetchSpy = vi.fn();
|
|
53
|
+
const provider = createGeminiEmbeddingProvider({ apiKey: "key", fetchImpl: fetchSpy });
|
|
54
|
+
expect(await provider.embed([])).toEqual([]);
|
|
55
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
it("throws EMBEDDING_BATCH_TOO_LARGE when input exceeds maxBatchSize", async () => {
|
|
58
|
+
const provider = createGeminiEmbeddingProvider({
|
|
59
|
+
apiKey: "key",
|
|
60
|
+
fetchImpl: mockFetch({ ok: true, json: async () => ({ embeddings: [] }) }),
|
|
61
|
+
});
|
|
62
|
+
const tooMany = Array.from({ length: 101 }, (_, i) => `t-${i}`);
|
|
63
|
+
await expect(provider.embed(tooMany)).rejects.toMatchObject({
|
|
64
|
+
code: EMBEDDING_BATCH_TOO_LARGE,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
it("wraps non-2xx responses as EmbeddingProviderError", async () => {
|
|
68
|
+
const provider = createGeminiEmbeddingProvider({
|
|
69
|
+
apiKey: "key",
|
|
70
|
+
fetchImpl: mockFetch({
|
|
71
|
+
ok: false,
|
|
72
|
+
status: 401,
|
|
73
|
+
json: async () => ({ error: { code: 401, message: "API key not valid" } }),
|
|
74
|
+
}),
|
|
75
|
+
});
|
|
76
|
+
await expect(provider.embed(["x"])).rejects.toBeInstanceOf(EmbeddingProviderError);
|
|
77
|
+
});
|
|
78
|
+
it("sends the api key as x-goog-api-key header", async () => {
|
|
79
|
+
const fetchSpy = vi.fn(async () => {
|
|
80
|
+
return new Response(JSON.stringify({ embeddings: [{ values: [0.1] }] }), { status: 200 });
|
|
81
|
+
});
|
|
82
|
+
const provider = createGeminiEmbeddingProvider({ apiKey: "key-xyz", fetchImpl: fetchSpy });
|
|
83
|
+
await provider.embed(["x"]);
|
|
84
|
+
// biome-ignore lint/suspicious/noExplicitAny: vi.fn return type
|
|
85
|
+
const init = fetchSpy.mock.calls[0]?.[1];
|
|
86
|
+
const headers = init.headers;
|
|
87
|
+
expect(headers["x-goog-api-key"]).toBe("key-xyz");
|
|
88
|
+
});
|
|
89
|
+
it("uses Bearer auth when auth mode is 'bearer' (Voyant Cloud gateway)", async () => {
|
|
90
|
+
const fetchSpy = vi.fn(async () => {
|
|
91
|
+
return new Response(JSON.stringify({ embeddings: [{ values: [0] }] }), { status: 200 });
|
|
92
|
+
});
|
|
93
|
+
const provider = createGeminiEmbeddingProvider({
|
|
94
|
+
apiKey: "vc-token-abc",
|
|
95
|
+
auth: "bearer",
|
|
96
|
+
baseUrl: "https://api.voyantjs.com/ai/v1/gemini",
|
|
97
|
+
fetchImpl: fetchSpy,
|
|
98
|
+
});
|
|
99
|
+
await provider.embed(["x"]);
|
|
100
|
+
// biome-ignore lint/suspicious/noExplicitAny: vi.fn return type
|
|
101
|
+
const init = fetchSpy.mock.calls[0]?.[1];
|
|
102
|
+
const headers = init.headers;
|
|
103
|
+
expect(headers.Authorization).toBe("Bearer vc-token-abc");
|
|
104
|
+
expect(headers["x-goog-api-key"]).toBeUndefined();
|
|
105
|
+
// biome-ignore lint/suspicious/noExplicitAny: vi.fn return type
|
|
106
|
+
const url = fetchSpy.mock.calls[0]?.[0];
|
|
107
|
+
expect(url).toBe("https://api.voyantjs.com/ai/v1/gemini/models/gemini-embedding-001:batchEmbedContents");
|
|
108
|
+
});
|
|
109
|
+
it("passes outputDimensionality + taskType in each request body", async () => {
|
|
110
|
+
const fetchSpy = vi.fn(async () => {
|
|
111
|
+
return new Response(JSON.stringify({ embeddings: [{ values: [0] }] }), { status: 200 });
|
|
112
|
+
});
|
|
113
|
+
const provider = createGeminiEmbeddingProvider({
|
|
114
|
+
apiKey: "key",
|
|
115
|
+
outputDimensionality: 768,
|
|
116
|
+
taskType: "RETRIEVAL_QUERY",
|
|
117
|
+
fetchImpl: fetchSpy,
|
|
118
|
+
});
|
|
119
|
+
await provider.embed(["q"]);
|
|
120
|
+
// biome-ignore lint/suspicious/noExplicitAny: vi.fn return type
|
|
121
|
+
const init = fetchSpy.mock.calls[0]?.[1];
|
|
122
|
+
const body = JSON.parse(String(init.body));
|
|
123
|
+
expect(body.requests[0].outputDimensionality).toBe(768);
|
|
124
|
+
expect(body.requests[0].taskType).toBe("RETRIEVAL_QUERY");
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding model registry helpers.
|
|
3
|
+
*
|
|
4
|
+
* Each search-index document carries an `embedding_model_id` field that
|
|
5
|
+
* identifies which model produced its vector. The registry's job is to:
|
|
6
|
+
*
|
|
7
|
+
* 1. Validate at deployment startup that the configured embedding
|
|
8
|
+
* provider's `dimensions` matches the configured `IndexerAdapter`'s
|
|
9
|
+
* `vectorDimensions`. Mismatch → fail loudly.
|
|
10
|
+
* 2. Track the active model id so search queries can scope vector
|
|
11
|
+
* lookups to documents using a compatible model. During a model
|
|
12
|
+
* migration window the index can hold mixed-model documents — old
|
|
13
|
+
* ones get skipped on vector queries and re-embedded by the
|
|
14
|
+
* `bulkReindex(forceReembed: true)` job.
|
|
15
|
+
*
|
|
16
|
+
* See `docs/architecture/catalog-rag-architecture.md` §8.
|
|
17
|
+
*/
|
|
18
|
+
import type { IndexerCapabilities } from "@voyantjs/catalog";
|
|
19
|
+
import type { EmbeddingProviderCapabilities } from "./contract.js";
|
|
20
|
+
/**
|
|
21
|
+
* Validate that an embedding provider's capabilities are compatible with
|
|
22
|
+
* the search engine's vector configuration. Call this at deployment
|
|
23
|
+
* startup; throw if incompatible.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateEmbeddingCompatibility(providerCapabilities: EmbeddingProviderCapabilities, indexerCapabilities: IndexerCapabilities): void;
|
|
26
|
+
/**
|
|
27
|
+
* Returns true if a given document's `embedding_model_id` matches the
|
|
28
|
+
* deployment's active model. Vector queries should filter to active-model
|
|
29
|
+
* documents; non-matching documents fall through to keyword-only
|
|
30
|
+
* scoring until `bulkReindex(forceReembed: true)` re-embeds them.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isActiveEmbeddingModel(documentModelId: string | undefined, activeModelId: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Convenience: stamp an `IndexerDocument`'s `embedding_model_id` from a
|
|
35
|
+
* provider's capabilities. Use this when constructing documents in the
|
|
36
|
+
* embedding pipeline so the active model id propagates to the index.
|
|
37
|
+
*/
|
|
38
|
+
export declare function stampEmbeddingModelId(providerCapabilities: EmbeddingProviderCapabilities): {
|
|
39
|
+
embedding_model_id: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Plan the embedding workload for a re-index pipeline. Given the current
|
|
43
|
+
* document set (each row tagged with its embedding_model_id) and the
|
|
44
|
+
* active model, returns lists of:
|
|
45
|
+
* - `embedded` — already on the active model; no work
|
|
46
|
+
* - `pending` — never embedded; needs first-time embedding
|
|
47
|
+
* - `migrating` — embedded under an older model; needs re-embedding
|
|
48
|
+
*
|
|
49
|
+
* Drives the `bulkReindex(forceReembed: true)` migration UX.
|
|
50
|
+
*/
|
|
51
|
+
export interface EmbeddingMigrationPlan {
|
|
52
|
+
embedded: string[];
|
|
53
|
+
pending: string[];
|
|
54
|
+
migrating: string[];
|
|
55
|
+
totalDocuments: number;
|
|
56
|
+
activeModelId: string;
|
|
57
|
+
}
|
|
58
|
+
export declare function planEmbeddingMigration(documents: ReadonlyArray<{
|
|
59
|
+
id: string;
|
|
60
|
+
embedding_model_id?: string | null;
|
|
61
|
+
}>, activeModelId: string): EmbeddingMigrationPlan;
|
|
62
|
+
//# sourceMappingURL=model-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-registry.d.ts","sourceRoot":"","sources":["../../src/embeddings/model-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAE5D,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAA;AAElE;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,oBAAoB,EAAE,6BAA6B,EACnD,mBAAmB,EAAE,mBAAmB,GACvC,IAAI,CAyBN;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,aAAa,EAAE,MAAM,GACpB,OAAO,CAET;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,oBAAoB,EAAE,6BAA6B,GAAG;IAC1F,kBAAkB,EAAE,MAAM,CAAA;CAC3B,CAEA;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,aAAa,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,EAC5E,aAAa,EAAE,MAAM,GACpB,sBAAsB,CAoBxB"}
|