import type { EmbeddingProvider } from "@rakazo/adapter-kit"; import { describe, expect, it } from "vitest"; import { EmulatorEmbeddingProvider, OpenAiCompatibleEmbeddingServerEmulator, } from "./embedding-emulator.js"; import { cosineSimilarity } from "./embedding-similarity.js"; import { OPENAI_COMPATIBLE_EMBEDDING_BATCH_SIZE, OpenAiCompatibleEmbeddingProvider, } from "./openai-compatible-embeddings.js"; const factories: Record EmbeddingProvider> = { emulator: () => new EmulatorEmbeddingProvider(), "openai-compatible": () => new OpenAiCompatibleEmbeddingProvider( { baseUrl: "http://127.0.0.1:8080", model: "nomic-embed-text" }, new OpenAiCompatibleEmbeddingServerEmulator().fetch, ), }; /** * Offline conformance: every EmbeddingProvider names its vector space, returns one * finite, equal-length vector per text in input order, is deterministic, keeps related * texts closer than unrelated ones, and honors cancellation. */ for (const [name, create] of Object.entries(factories)) { describe(`${name} embedding conformance (offline)`, () => { it("describes the contract and the vector space", () => { const descriptor = create().describe(); expect(descriptor.contractVersion).toBe("1"); expect(descriptor.capabilities.model.length).toBeGreaterThan(0); }); it("returns nothing for no texts", async () => { await expect(create().embed([])).resolves.toEqual([]); }); it("returns one finite vector per text, all of one length, deterministically", async () => { const provider = create(); const texts = ["Works as a nurse in Berlin.", "Prefers short answers.", ""]; const vectors = await provider.embed(texts); expect(vectors).toHaveLength(texts.length); for (const vector of vectors) { expect(vector.length).toBe(vectors[0]!.length); expect(vector.length).toBeGreaterThan(0); expect(vector.every(Number.isFinite)).toBe(true); } await expect(provider.embed(texts)).resolves.toEqual(vectors); }); it("keeps input order across batches", async () => { const provider = create(); const texts = Array.from( { length: OPENAI_COMPATIBLE_EMBEDDING_BATCH_SIZE * 2 + 3 }, (_, index) => `memory number ${index} about topic ${index % 7}`, ); const vectors = await provider.embed(texts); expect(vectors).toHaveLength(texts.length); for (const index of [0, OPENAI_COMPATIBLE_EMBEDDING_BATCH_SIZE, texts.length - 1]) { await expect(provider.embed([texts[index]!])).resolves.toEqual([vectors[index]]); } }); it("places related texts closer than unrelated ones", async () => { const [fact, restated, unrelated] = await create().embed([ "The user works as a nurse in Berlin", "works as a nurse in Berlin", "enjoys jazz records on weekends", ]); expect(cosineSimilarity(fact!, restated!)).toBeGreaterThan( cosineSimilarity(fact!, unrelated!), ); }); it("rejects when the caller cancels", async () => { await expect(create().embed(["anything"], AbortSignal.abort())).rejects.toThrow(); }); }); }