68 lines
3.4 KiB
TypeScript
68 lines
3.4 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest';
|
|
import {
|
|
createEmbeddingClientFromEnvironment,
|
|
embeddingEndpoint,
|
|
EmbeddingClientError,
|
|
OpenAiCompatibleEmbeddingClient
|
|
} from '../../server/compliance/embedding-client';
|
|
|
|
describe('regulatory embedding client', () => {
|
|
it('allows loopback HTTP, requires remote HTTPS, and requires explicit dimensions', () => {
|
|
expect(embeddingEndpoint('https://litellm.work.example/v1').href).toBe('https://litellm.work.example/v1/embeddings');
|
|
expect(embeddingEndpoint('http://127.0.0.1:8080/v1').href).toBe('http://127.0.0.1:8080/v1/embeddings');
|
|
expect(() => embeddingEndpoint('http://litellm.work.example/v1')).toThrow(/HTTPS/);
|
|
expect(() => createEmbeddingClientFromEnvironment({
|
|
ASSESSOR_RAG_EMBEDDING_BASE_URL: 'https://litellm.work.example/v1',
|
|
ASSESSOR_RAG_EMBEDDING_MODEL: 'multilingual-model',
|
|
ASSESSOR_RAG_EMBEDDING_API_KEY: 'secret'
|
|
})).toThrow(/DIMENSIONS is required/);
|
|
});
|
|
|
|
it('requests a shortened dimension only when explicitly configured', async () => {
|
|
const bodies: unknown[] = [];
|
|
const fetcher = vi.fn(async (_url: string | URL | Request, init?: RequestInit) => {
|
|
bodies.push(JSON.parse(String(init?.body)));
|
|
return new Response(JSON.stringify({ data: [{ index: 0, embedding: Array(64).fill(0) }] }), { status: 200 });
|
|
});
|
|
const client = createEmbeddingClientFromEnvironment({
|
|
ASSESSOR_RAG_EMBEDDING_BASE_URL: 'https://litellm.work.example/v1',
|
|
ASSESSOR_RAG_EMBEDDING_MODEL: 'text-embedding-3-large',
|
|
ASSESSOR_RAG_EMBEDDING_API_KEY: 'secret',
|
|
ASSESSOR_RAG_EMBEDDING_DIMENSIONS: '64',
|
|
ASSESSOR_RAG_EMBEDDING_REQUEST_DIMENSIONS: 'true'
|
|
}, fetcher);
|
|
|
|
await client.embed(['Synthetic text']);
|
|
|
|
expect(bodies).toEqual([{ model: 'text-embedding-3-large', input: ['Synthetic text'], encoding_format: 'float', dimensions: 64 }]);
|
|
});
|
|
|
|
it('returns embeddings in provider index order without exposing the API key', async () => {
|
|
const fetcher = vi.fn(async (_url: string | URL | Request, init?: RequestInit) => {
|
|
expect(init?.redirect).toBe('error');
|
|
expect(new Headers(init?.headers).get('authorization')).toBe('Bearer secret');
|
|
expect(JSON.parse(String(init?.body))).toEqual({ model: 'multilingual-model', input: ['Deutsch', 'English'], encoding_format: 'float' });
|
|
return new Response(JSON.stringify({ data: [
|
|
{ index: 1, embedding: [0, 1] },
|
|
{ index: 0, embedding: [1, 0] }
|
|
] }), { status: 200 });
|
|
});
|
|
const client = new OpenAiCompatibleEmbeddingClient({
|
|
endpoint: new URL('https://litellm.work.example/v1/embeddings'),
|
|
model: 'multilingual-model', apiKey: 'secret', dimensions: 2, timeoutMs: 1_000, fetcher
|
|
});
|
|
|
|
await expect(client.embed(['Deutsch', 'English'])).resolves.toEqual([[1, 0], [0, 1]]);
|
|
expect(fetcher).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it('rejects incompatible vector dimensions', async () => {
|
|
const client = new OpenAiCompatibleEmbeddingClient({
|
|
endpoint: new URL('https://litellm.work.example/v1/embeddings'),
|
|
model: 'multilingual-model', apiKey: 'secret', dimensions: 3, timeoutMs: 1_000,
|
|
fetcher: async () => new Response(JSON.stringify({ data: [{ index: 0, embedding: [1, 0] }] }), { status: 200 })
|
|
});
|
|
await expect(client.embed(['query'])).rejects.toMatchObject({ code: 'embedding_invalid_dimensions' } satisfies Partial<EmbeddingClientError>);
|
|
});
|
|
});
|