OpenAI integrations for LangChain.js providing chat models, embeddings, tools, and Azure support.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Vector embeddings for semantic similarity, search, and retrieval-augmented generation (RAG) applications. Convert text into high-dimensional vectors for AI applications.
The primary embeddings class providing access to OpenAI's text embedding models.
/**
* OpenAI embeddings integration
* Converts text into high-dimensional vectors for semantic similarity and search
*/
class OpenAIEmbeddings extends Embeddings {
constructor(fields?: Partial<OpenAIEmbeddingsParams>);
/** Model configuration */
model: string; // Default: "text-embedding-ada-002"
batchSize: number; // Batch size for API calls (default: 512)
stripNewLines: boolean; // Strip newlines from text (default: true)
dimensions?: number; // Output dimensions (text-embedding-3-* only)
/** Client configuration */
openAIApiKey?: string; // OpenAI API key
organization?: string; // OpenAI organization ID
baseURL?: string; // Custom base URL
timeout?: number; // Request timeout in milliseconds
maxRetries?: number; // Maximum retry attempts
/** Performance options */
maxConcurrency?: number; // Maximum concurrent requests
/** Embed multiple documents */
embedDocuments(texts: string[]): Promise<number[][]>;
/** Embed a single query */
embedQuery(text: string): Promise<number[]>;
/** Make embedding requests with retry logic */
embeddingWithRetry<T>(
request: OpenAIClient.EmbeddingCreateParams,
options?: OpenAICallOptions
): Promise<T>;
/** Get number of tokens in text */
getNumTokens(text: string): Promise<number>;
}Configuration interface for OpenAI embeddings.
interface OpenAIEmbeddingsParams extends EmbeddingsParams {
/** Model name for embeddings */
model: string;
/** Number of texts to batch together for embedding */
batchSize: number;
/** Whether to strip newlines from input text */
stripNewLines: boolean;
/** Output dimensions for embedding-3 models */
dimensions?: number;
/** OpenAI API configuration */
openAIApiKey?: string;
organization?: string;
baseURL?: string;
timeout?: number;
maxRetries?: number;
maxConcurrency?: number;
}import { OpenAIEmbeddings } from "@langchain/openai";
const embeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small",
apiKey: process.env.OPENAI_API_KEY
});
// Embed a single query
const queryEmbedding = await embeddings.embedQuery(
"What is the capital of France?"
);
console.log(`Query embedding dimensions: ${queryEmbedding.length}`);
// Embed multiple documents
const documents = [
"Paris is the capital of France.",
"Berlin is the capital of Germany.",
"Tokyo is the capital of Japan.",
"London is the capital of the United Kingdom."
];
const documentEmbeddings = await embeddings.embedDocuments(documents);
console.log(`Embedded ${documentEmbeddings.length} documents`);
console.log(`Each embedding has ${documentEmbeddings[0].length} dimensions`);import { OpenAIEmbeddings } from "@langchain/openai";
const embeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small"
});
// Create a knowledge base
const knowledgeBase = [
"The Eiffel Tower is in Paris, France.",
"The Statue of Liberty is in New York, USA.",
"The Colosseum is in Rome, Italy.",
"Big Ben is in London, England.",
"The Sydney Opera House is in Sydney, Australia."
];
// Embed all documents
const docEmbeddings = await embeddings.embedDocuments(knowledgeBase);
// Function to calculate cosine similarity
function cosineSimilarity(a: number[], b: number[]): number {
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
return dotProduct / (magnitudeA * magnitudeB);
}
// Search function
async function searchKnowledgeBase(query: string, topK = 3) {
const queryEmbedding = await embeddings.embedQuery(query);
const similarities = docEmbeddings.map((docEmb, index) => ({
index,
document: knowledgeBase[index],
similarity: cosineSimilarity(queryEmbedding, docEmb)
}));
return similarities
.sort((a, b) => b.similarity - a.similarity)
.slice(0, topK);
}
// Find relevant documents
const results = await searchKnowledgeBase("famous landmarks in Europe");
results.forEach((result, i) => {
console.log(`${i + 1}. (${result.similarity.toFixed(3)}) ${result.document}`);
});const embeddings = new OpenAIEmbeddings({
model: "text-embedding-3-large",
batchSize: 100, // Process 100 texts per batch
maxConcurrency: 5, // Max 5 concurrent requests
timeout: 30000, // 30 second timeout
maxRetries: 3
});
// Process large dataset efficiently
async function embedLargeDataset(texts: string[]) {
console.log(`Processing ${texts.length} texts...`);
const startTime = Date.now();
const embeddings_result = await embeddings.embedDocuments(texts);
const endTime = Date.now();
console.log(`Completed in ${(endTime - startTime) / 1000}s`);
console.log(`Average time per text: ${(endTime - startTime) / texts.length}ms`);
return embeddings_result;
}
// Example with 1000 texts
const largeDocs = Array.from({ length: 1000 }, (_, i) =>
`This is document number ${i + 1} with some sample content for testing embeddings performance.`
);
const results = await embedLargeDataset(largeDocs);// Compare different embedding models
const models = [
"text-embedding-ada-002",
"text-embedding-3-small",
"text-embedding-3-large"
];
const sampleText = "Machine learning is a subset of artificial intelligence.";
for (const model of models) {
const embedder = new OpenAIEmbeddings({ model });
const embedding = await embedder.embedQuery(sampleText);
console.log(`${model}:`);
console.log(` Dimensions: ${embedding.length}`);
console.log(` First 5 values: [${embedding.slice(0, 5).map(x => x.toFixed(4)).join(', ')}]`);
}// Use custom dimensions to reduce storage and improve performance
const compactEmbeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small",
dimensions: 512 // Reduce from default 1536 to 512
});
const standardEmbeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small"
// Uses default 1536 dimensions
});
const text = "This is a test for dimension comparison.";
const compact = await compactEmbeddings.embedQuery(text);
const standard = await standardEmbeddings.embedQuery(text);
console.log(`Compact dimensions: ${compact.length}`); // 512
console.log(`Standard dimensions: ${standard.length}`); // 1536
// Note: Reduced dimensions may impact quality for some use casesimport { OpenAIEmbeddings } from "@langchain/openai";
import { ChatOpenAI } from "@langchain/openai";
class SimpleRAG {
private embeddings: OpenAIEmbeddings;
private chatModel: ChatOpenAI;
private documents: string[] = [];
private docEmbeddings: number[][] = [];
constructor() {
this.embeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small"
});
this.chatModel = new ChatOpenAI({
model: "gpt-4o-mini",
temperature: 0
});
}
async addDocuments(docs: string[]) {
this.documents.push(...docs);
const newEmbeddings = await this.embeddings.embedDocuments(docs);
this.docEmbeddings.push(...newEmbeddings);
}
async search(query: string, topK = 3): Promise<string[]> {
const queryEmbedding = await this.embeddings.embedQuery(query);
const similarities = this.docEmbeddings.map((docEmb, index) => ({
index,
similarity: this.cosineSimilarity(queryEmbedding, docEmb)
}));
return similarities
.sort((a, b) => b.similarity - a.similarity)
.slice(0, topK)
.map(item => this.documents[item.index]);
}
async answer(question: string): Promise<string> {
const relevantDocs = await this.search(question);
const context = relevantDocs.join('\n\n');
const prompt = `
Context:
${context}
Question: ${question}
Answer the question based on the provided context. If the answer cannot be found in the context, say "I cannot answer based on the provided information."
`;
const response = await this.chatModel.invoke(prompt);
return response.content as string;
}
private cosineSimilarity(a: number[], b: number[]): number {
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
return dotProduct / (magnitudeA * magnitudeB);
}
}
// Usage
const rag = new SimpleRAG();
await rag.addDocuments([
"The solar system contains eight planets: Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and Neptune.",
"Earth is the third planet from the Sun and the only known planet with life.",
"Jupiter is the largest planet in the solar system and has over 70 moons.",
"Mars is often called the Red Planet due to its reddish appearance."
]);
const answer = await rag.answer("What is the largest planet?");
console.log(answer); // "Jupiter is the largest planet in the solar system..."import { OpenAIEmbeddings } from "@langchain/openai";
const embeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small",
maxRetries: 3,
timeout: 60000
});
async function robustEmbedding(texts: string[]) {
try {
const result = await embeddings.embedDocuments(texts);
return result;
} catch (error) {
console.error("Embedding failed:", error);
if (error.code === 'rate_limit_exceeded') {
console.log("Rate limit hit, implementing backoff...");
// Implement exponential backoff
await new Promise(resolve => setTimeout(resolve, 5000));
return robustEmbedding(texts);
}
if (error.code === 'context_length_exceeded') {
console.log("Text too long, chunking...");
// Split into smaller chunks
const chunks = texts.map(text =>
text.length > 8000 ? text.substring(0, 8000) : text
);
return embeddings.embedDocuments(chunks);
}
throw error;
}
}
// Monitor token usage
async function embedWithMonitoring(texts: string[]) {
const startTime = Date.now();
// Estimate tokens (rough approximation)
const estimatedTokens = texts.reduce((sum, text) =>
sum + Math.ceil(text.length / 4), 0
);
console.log(`Embedding ${texts.length} texts (~${estimatedTokens} tokens)`);
const result = await embeddings.embedDocuments(texts);
const duration = Date.now() - startTime;
console.log(`Completed in ${duration}ms (${(duration / texts.length).toFixed(1)}ms per text)`);
return result;
}The OpenAI Embeddings class supports these models:
| Model | Dimensions | Max Input | Cost | Use Case |
|---|---|---|---|---|
text-embedding-ada-002 | 1536 | 8191 tokens | Lower | General purpose (legacy) |
text-embedding-3-small | 1536* | 8191 tokens | Lowest | High efficiency |
text-embedding-3-large | 3072* | 8191 tokens | Higher | Maximum performance |
*Supports custom dimensions
// For high-volume, cost-sensitive applications
const efficientEmbeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small",
dimensions: 512 // Reduce dimensions for cost savings
});
// For maximum quality and performance
const highQualityEmbeddings = new OpenAIEmbeddings({
model: "text-embedding-3-large"
// Uses full 3072 dimensions
});
// For legacy compatibility
const legacyEmbeddings = new OpenAIEmbeddings({
model: "text-embedding-ada-002"
});// Optimize for throughput
const throughputOptimized = new OpenAIEmbeddings({
model: "text-embedding-3-small",
batchSize: 2048, // Large batches
maxConcurrency: 10, // High concurrency
dimensions: 256 // Smaller dimensions
});
// Optimize for quality
const qualityOptimized = new OpenAIEmbeddings({
model: "text-embedding-3-large",
batchSize: 100, // Smaller batches for stability
maxConcurrency: 3, // Conservative concurrency
// Full dimensions for maximum quality
});const embeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small",
stripNewLines: true // Remove newlines by default
});
// Custom preprocessing
function preprocessText(text: string): string {
return text
.toLowerCase() // Normalize case
.replace(/\s+/g, ' ') // Normalize whitespace
.trim() // Remove leading/trailing space
.substring(0, 8000); // Ensure within token limits
}
const documents = [
"This is SAMPLE text with weird spacing\n\n",
"Another document with\nNewlines and CAPS"
];
const preprocessed = documents.map(preprocessText);
const embeddings_result = await embeddings.embedDocuments(preprocessed);class EmbeddingCache {
private cache = new Map<string, number[]>();
private embeddings: OpenAIEmbeddings;
constructor() {
this.embeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small"
});
}
async embedQuery(text: string): Promise<number[]> {
const key = this.hashText(text);
if (this.cache.has(key)) {
console.log("Cache hit!");
return this.cache.get(key)!;
}
console.log("Cache miss, computing embedding...");
const embedding = await this.embeddings.embedQuery(text);
this.cache.set(key, embedding);
return embedding;
}
private hashText(text: string): string {
// Simple hash function - use a proper one in production
return Buffer.from(text).toString('base64');
}
}
const cachedEmbeddings = new EmbeddingCache();function chunkDocument(text: string, maxChunkSize = 7000): string[] {
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
const chunks: string[] = [];
let currentChunk = "";
for (const sentence of sentences) {
if (currentChunk.length + sentence.length > maxChunkSize && currentChunk) {
chunks.push(currentChunk.trim());
currentChunk = "";
}
currentChunk += sentence + ". ";
}
if (currentChunk.trim()) {
chunks.push(currentChunk.trim());
}
return chunks;
}
// Usage with long documents
const longDocument = "Very long document text..."; // Imagine this is 20,000+ characters
const chunks = chunkDocument(longDocument);
const chunkEmbeddings = await embeddings.embedDocuments(chunks);
// Store with metadata for retrieval
const documentChunks = chunks.map((chunk, index) => ({
text: chunk,
embedding: chunkEmbeddings[index],
documentId: "doc_123",
chunkIndex: index
}));