EmbeddingService.tsā¢1.97 kB
import OpenAI from 'openai';
export class EmbeddingService {
private openai: OpenAI;
private model: string = 'text-embedding-3-small';
constructor() {
const apiKey = process.env.OPENAI_API_KEY?.trim();
if (!apiKey || apiKey === '') {
throw new Error('OPENAI_API_KEY environment variable is required and cannot be empty');
}
this.openai = new OpenAI({ apiKey });
}
/**
* Generate embedding for text content
* @param text - Text to embed (title + content)
* @returns Vector embedding as number array
*/
async generateEmbedding(text: string): Promise<number[]> {
try {
// Truncate text to avoid token limits (8191 tokens for text-embedding-3-small)
const truncatedText = text.substring(0, 8000);
const response = await this.openai.embeddings.create({
model: this.model,
input: truncatedText,
});
return response.data[0].embedding;
} catch (error) {
console.error('Failed to generate embedding:', error);
throw error;
}
}
/**
* Generate embeddings for multiple texts in batch
* @param texts - Array of texts to embed
* @returns Array of vector embeddings
*/
async generateEmbeddings(texts: string[]): Promise<number[][]> {
try {
// Truncate each text
const truncatedTexts = texts.map(text => text.substring(0, 8000));
const response = await this.openai.embeddings.create({
model: this.model,
input: truncatedTexts,
});
return response.data.map(item => item.embedding);
} catch (error) {
console.error('Failed to generate embeddings:', error);
throw error;
}
}
/**
* Prepare text for embedding (combine title and content)
* @param title - Article title
* @param content - Article content
* @returns Combined text for embedding
*/
prepareTextForEmbedding(title: string, content: string): string {
return `${title}\n\n${content}`;
}
}