import { OpenAIEmbeddingProvider } from './openai-provider.js';
import type { EmbeddingConfig, EmbeddingProvider } from './provider.js';
export class EmbeddingService {
private provider: EmbeddingProvider;
constructor(config: EmbeddingConfig) {
this.provider = this.createProvider(config);
}
private createProvider(config: EmbeddingConfig): EmbeddingProvider {
switch (config.provider) {
case 'openai':
if (!config.apiKey) {
throw new Error('OpenAI API key is required for OpenAI provider');
}
return new OpenAIEmbeddingProvider(config.apiKey, config.model, config.baseURL);
case 'local':
throw new Error('Local embedding provider not yet implemented');
default:
throw new Error(`Unknown embedding provider: ${config.provider}`);
}
}
async generateEmbedding(text: string): Promise<number[]> {
if (!text.trim()) {
throw new Error('Text cannot be empty');
}
return this.provider.generateEmbedding(text);
}
getDimensions(): number {
return this.provider.getDimensions();
}
getModelName(): string {
return this.provider.getModelName();
}
static cosineSimilarity(a: number[], b: number[]): number {
if (a.length !== b.length) {
throw new Error('Embeddings must have the same length');
}
let dotProduct = 0;
let normA = 0;
let normB = 0;
for (let i = 0; i < a.length; i++) {
dotProduct += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
normA = Math.sqrt(normA);
normB = Math.sqrt(normB);
if (normA === 0 || normB === 0) {
return 0;
}
return dotProduct / (normA * normB);
}
static preprocessText(text: string): string {
return text
.trim()
.replace(/\s+/g, ' ')
.replace(/[\r\n]+/g, ' ')
.substring(0, 8000);
}
}