
Ai Ml Integration
- 70 installs
- 19 repo stars
- Updated January 20, 2026
- miles990/claude-software-skills
Helps with ai & agent building tasks.
About
ai-ml-integration is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- ai-ml-integration
- AI & Agent Building
- AI-coding skill
Ai Ml Integration by the numbers
- 70 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #5,726 of 16,544 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/miles990/claude-software-skills --skill ai-ml-integrationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 70 |
|---|---|
| repo stars | ★ 19 |
| Last updated | January 20, 2026 |
| Repository | miles990/claude-software-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
AI/ML Integration
Overview
Integrating AI and machine learning capabilities into applications, including LLM APIs, embeddings, and RAG patterns.
---
LLM Integration
OpenAI API
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
// Chat completion
async function chat(messages: Array<{ role: string; content: string }>) {
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
temperature: 0.7,
max_tokens: 1000,
});
return response.choices[0].message.content;
}
// Streaming response
async function* streamChat(prompt: string) {
const stream = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: prompt }],
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) {
yield content;
}
}
}
// Function calling
async function chatWithTools(message: string) {
const tools = [
{
type: 'function' as const,
function: {
name: 'get_weather',
description: 'Get current weather for a location',
parameters: {
type: 'object',
properties: {
location: { type: 'string', description: 'City name' },
unit: { type: 'string', enum: ['celsius', 'fahrenheit'] },
},
required: ['location'],
},
},
},
];
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: message }],
tools,
tool_choice: 'auto',
});
const toolCall = response.choices[0].message.tool_calls?.[0];
if (toolCall) {
const args = JSON.parse(toolCall.function.arguments);
// Execute the function
const result = await executeFunction(toolCall.function.name, args);
// Continue conversation with function result
return openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'user', content: message },
response.choices[0].message,
{
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(result),
},
],
});
}
return response;
}Anthropic Claude
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
// Basic message
async function chat(prompt: string) {
const message = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }],
});
return message.content[0].type === 'text' ? message.content[0].text : '';
}
// With system prompt
async function chatWithSystem(system: string, prompt: string) {
const message = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
system,
messages: [{ role: 'user', content: prompt }],
});
return message.content[0];
}
// Streaming
async function* streamChat(prompt: string) {
const stream = anthropic.messages.stream({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }],
});
for await (const event of stream) {
if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
yield event.delta.text;
}
}
}
// Tool use
async function chatWithTools(prompt: string) {
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
tools: [
{
name: 'search_database',
description: 'Search the database for relevant information',
input_schema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
limit: { type: 'number', description: 'Max results' },
},
required: ['query'],
},
},
],
messages: [{ role: 'user', content: prompt }],
});
// Handle tool use blocks
for (const block of response.content) {
if (block.type === 'tool_use') {
const result = await executeSearch(block.input);
// Continue with tool result...
}
}
}---
Embeddings
Text Embeddings
import OpenAI from 'openai';
const openai = new OpenAI();
// Generate embeddings
async function getEmbedding(text: string): Promise<number[]> {
const response = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: text,
});
return response.data[0].embedding;
}
// Batch embeddings
async function getEmbeddings(texts: string[]): Promise<number[][]> {
const response = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: texts,
});
return response.data.map(d => d.embedding);
}
// Cosine similarity
function cosineSimilarity(a: number[], b: number[]): number {
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];
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
// Find similar items
async function findSimilar(query: string, items: Array<{ text: string; embedding: number[] }>, topK = 5) {
const queryEmbedding = await getEmbedding(query);
const scored = items.map(item => ({
...item,
score: cosineSimilarity(queryEmbedding, item.embedding),
}));
return scored
.sort((a, b) => b.score - a.score)
.slice(0, topK);
}Vector Database (Pinecone)
import { Pinecone } from '@pinecone-database/pinecone';
const pinecone = new Pinecone({
apiKey: process.env.PINECONE_API_KEY,
});
const index = pinecone.index('my-index');
// Upsert vectors
async function upsertDocuments(documents: Document[]) {
const vectors = await Promise.all(
documents.map(async (doc) => ({
id: doc.id,
values: await getEmbedding(doc.content),
metadata: {
title: doc.title,
source: doc.source,
content: doc.content.slice(0, 1000), // Store truncated for retrieval
},
}))
);
await index.upsert(vectors);
}
// Query similar vectors
async function querySimilar(query: string, topK = 5, filter?: object) {
const queryEmbedding = await getEmbedding(query);
const results = await index.query({
vector: queryEmbedding,
topK,
includeMetadata: true,
filter,
});
return results.matches.map(match => ({
id: match.id,
score: match.score,
...match.metadata,
}));
}---
RAG (Retrieval-Augmented Generation)
Basic RAG Pipeline
class RAGPipeline {
constructor(
private vectorStore: VectorStore,
private llm: LLM,
private embeddings: EmbeddingModel
) {}
async query(question: string): Promise<string> {
// 1. Retrieve relevant documents
const relevantDocs = await this.retrieve(question);
// 2. Build context
const context = this.buildContext(relevantDocs);
// 3. Generate response with context
return this.generate(question, context);
}
private async retrieve(query: string, topK = 5) {
const queryEmbedding = await this.embeddings.embed(query);
return this.vectorStore.similaritySearch(queryEmbedding, topK);
}
private buildContext(docs: Document[]): string {
return docs
.map((doc, i) => `[Document ${i + 1}]\n${doc.content}`)
.join('\n\n');
}
private async generate(question: string, context: string): Promise<string> {
const prompt = `Answer the question based on the following context.
If the answer is not in the context, say "I don't have enough information."
Context:
${context}
Question: ${question}
Answer:`;
return this.llm.generate(prompt);
}
}Advanced RAG with Reranking
import { CohereClient } from 'cohere-ai';
const cohere = new CohereClient({ token: process.env.COHERE_API_KEY });
class AdvancedRAG {
async query(question: string): Promise<string> {
// 1. Initial retrieval (over-fetch)
const candidates = await this.vectorStore.similaritySearch(question, 20);
// 2. Rerank with cross-encoder
const reranked = await this.rerank(question, candidates, 5);
// 3. Generate with reranked context
return this.generate(question, reranked);
}
private async rerank(query: string, documents: Document[], topK: number) {
const response = await cohere.rerank({
model: 'rerank-english-v2.0',
query,
documents: documents.map(d => d.content),
topN: topK,
});
return response.results.map(r => documents[r.index]);
}
private async generate(question: string, context: Document[]) {
const systemPrompt = `You are a helpful assistant. Answer questions based on the provided context.
Cite your sources using [1], [2], etc.`;
const contextText = context
.map((doc, i) => `[${i + 1}] ${doc.content}`)
.join('\n\n');
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: `Context:\n${contextText}\n\nQuestion: ${question}` },
],
});
return response.choices[0].message.content;
}
}---
LangChain
Chain Composition
import { ChatOpenAI } from '@langchain/openai';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { RunnableSequence } from '@langchain/core/runnables';
const model = new ChatOpenAI({ model: 'gpt-4o' });
// Simple chain
const prompt = ChatPromptTemplate.fromTemplate(
'Summarize the following text in {style} style:\n\n{text}'
);
const chain = prompt.pipe(model).pipe(new StringOutputParser());
const result = await chain.invoke({
style: 'professional',
text: 'Long text to summarize...',
});
// Chain with multiple steps
const analysisChain = RunnableSequence.from([
ChatPromptTemplate.fromTemplate('Extract key points from:\n{text}'),
model,
new StringOutputParser(),
(keyPoints: string) => ({ keyPoints }),
ChatPromptTemplate.fromTemplate('Create a summary from these key points:\n{keyPoints}'),
model,
new StringOutputParser(),
]);
// Branching chain
const routerChain = RunnableSequence.from([
ChatPromptTemplate.fromTemplate(
'Classify this query as either "technical" or "general":\n{query}'
),
model,
new StringOutputParser(),
async (classification: string) => {
if (classification.includes('technical')) {
return technicalChain.invoke({ query });
}
return generalChain.invoke({ query });
},
]);Document Loading and Splitting
import { PDFLoader } from 'langchain/document_loaders/fs/pdf';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { OpenAIEmbeddings } from '@langchain/openai';
import { PineconeStore } from '@langchain/pinecone';
// Load documents
const loader = new PDFLoader('document.pdf');
const docs = await loader.load();
// Split into chunks
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
separators: ['\n\n', '\n', ' ', ''],
});
const chunks = await splitter.splitDocuments(docs);
// Create vector store
const vectorStore = await PineconeStore.fromDocuments(
chunks,
new OpenAIEmbeddings(),
{
pineconeIndex: index,
namespace: 'documents',
}
);
// Create retriever
const retriever = vectorStore.asRetriever({
k: 5,
filter: { type: 'technical' },
});---
Structured Output
import { z } from 'zod';
import OpenAI from 'openai';
import { zodResponseFormat } from 'openai/helpers/zod';
const PersonSchema = z.object({
name: z.string(),
age: z.number(),
occupation: z.string(),
skills: z.array(z.string()),
});
async function extractPerson(text: string) {
const response = await openai.beta.chat.completions.parse({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: 'Extract person information from the text.',
},
{ role: 'user', content: text },
],
response_format: zodResponseFormat(PersonSchema, 'person'),
});
return response.choices[0].message.parsed;
}
// With function calling for complex extraction
const extractionTools = [
{
type: 'function' as const,
function: {
name: 'extract_entities',
description: 'Extract named entities from text',
parameters: {
type: 'object',
properties: {
people: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
role: { type: 'string' },
},
},
},
organizations: {
type: 'array',
items: { type: 'string' },
},
dates: {
type: 'array',
items: { type: 'string' },
},
},
},
},
},
];---
Related Skills
- [[system-design]] - AI system architecture
- [[performance-optimization]] - ML inference optimization
- [[backend]] - API integration patterns
"""
Embedding Utilities Template
Usage: Generate and manage embeddings for RAG, search, and similarity
"""
import os
from typing import List, Optional, Union
from dataclasses import dataclass
import numpy as np
# ===========================================
# Configuration
# ===========================================
@dataclass
class EmbeddingConfig:
"""Embedding model configuration"""
provider: str = "openai" # openai, google, local
model: str = "text-embedding-3-small"
dimensions: int = 1536
batch_size: int = 100
normalize: bool = True
# Provider-specific models
EMBEDDING_MODELS = {
# OpenAI
"text-embedding-3-small": {"provider": "openai", "dimensions": 1536},
"text-embedding-3-large": {"provider": "openai", "dimensions": 3072},
"text-embedding-ada-002": {"provider": "openai", "dimensions": 1536},
# Google
"text-embedding-004": {"provider": "google", "dimensions": 768},
"text-multilingual-embedding-002": {"provider": "google", "dimensions": 768},
# Local (sentence-transformers)
"all-MiniLM-L6-v2": {"provider": "local", "dimensions": 384},
"all-mpnet-base-v2": {"provider": "local", "dimensions": 768},
}
def get_config_from_env() -> EmbeddingConfig:
"""Load configuration from environment variables"""
model = os.getenv("EMBEDDING_MODEL", "text-embedding-3-small")
model_info = EMBEDDING_MODELS.get(model, {})
return EmbeddingConfig(
provider=os.getenv("EMBEDDING_PROVIDER", model_info.get("provider", "openai")),
model=model,
dimensions=int(os.getenv("EMBEDDING_DIMENSIONS", model_info.get("dimensions", 1536))),
batch_size=int(os.getenv("EMBEDDING_BATCH_SIZE", "100")),
normalize=os.getenv("EMBEDDING_NORMALIZE", "true").lower() == "true",
)
# ===========================================
# Embedding Client (Abstract)
# ===========================================
class EmbeddingClient:
"""Base class for embedding clients"""
def __init__(self, config: Optional[EmbeddingConfig] = None):
self.config = config or get_config_from_env()
def embed(self, texts: Union[str, List[str]]) -> np.ndarray:
"""Generate embeddings for texts"""
if isinstance(texts, str):
texts = [texts]
# Process in batches
all_embeddings = []
for i in range(0, len(texts), self.config.batch_size):
batch = texts[i:i + self.config.batch_size]
embeddings = self._embed_batch(batch)
all_embeddings.extend(embeddings)
result = np.array(all_embeddings)
if self.config.normalize:
result = self._normalize(result)
return result
def _embed_batch(self, texts: List[str]) -> List[List[float]]:
"""Override in subclass"""
raise NotImplementedError("Implement _embed_batch() for your provider")
def _normalize(self, embeddings: np.ndarray) -> np.ndarray:
"""L2 normalize embeddings"""
norms = np.linalg.norm(embeddings, axis=1, keepdims=True)
return embeddings / np.maximum(norms, 1e-10)
# ===========================================
# Similarity Functions
# ===========================================
def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
"""Compute cosine similarity between two vectors"""
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
def cosine_similarity_matrix(embeddings: np.ndarray, query: np.ndarray) -> np.ndarray:
"""Compute cosine similarity between query and all embeddings"""
# Normalize
embeddings_norm = embeddings / np.linalg.norm(embeddings, axis=1, keepdims=True)
query_norm = query / np.linalg.norm(query)
return np.dot(embeddings_norm, query_norm)
def euclidean_distance(a: np.ndarray, b: np.ndarray) -> float:
"""Compute Euclidean distance between two vectors"""
return float(np.linalg.norm(a - b))
def dot_product(a: np.ndarray, b: np.ndarray) -> float:
"""Compute dot product between two vectors"""
return float(np.dot(a, b))
# ===========================================
# Search Functions
# ===========================================
@dataclass
class SearchResult:
"""Search result with score and metadata"""
index: int
score: float
text: Optional[str] = None
metadata: Optional[dict] = None
def search_similar(
query_embedding: np.ndarray,
embeddings: np.ndarray,
texts: Optional[List[str]] = None,
top_k: int = 5,
threshold: float = 0.0,
) -> List[SearchResult]:
"""Find most similar embeddings to query"""
# Compute similarities
similarities = cosine_similarity_matrix(embeddings, query_embedding)
# Get top-k indices
top_indices = np.argsort(similarities)[::-1][:top_k]
results = []
for idx in top_indices:
score = float(similarities[idx])
if score < threshold:
continue
results.append(SearchResult(
index=int(idx),
score=score,
text=texts[idx] if texts else None,
))
return results
def deduplicate_by_similarity(
embeddings: np.ndarray,
threshold: float = 0.95,
) -> List[int]:
"""Remove near-duplicate embeddings, return unique indices"""
n = len(embeddings)
unique_indices = []
for i in range(n):
is_duplicate = False
for j in unique_indices:
sim = cosine_similarity(embeddings[i], embeddings[j])
if sim >= threshold:
is_duplicate = True
break
if not is_duplicate:
unique_indices.append(i)
return unique_indices
# ===========================================
# Text Chunking
# ===========================================
def chunk_text(
text: str,
chunk_size: int = 500,
overlap: int = 50,
separator: str = "\n",
) -> List[str]:
"""Split text into overlapping chunks"""
# Split by separator first
paragraphs = text.split(separator)
chunks = []
current_chunk = []
current_size = 0
for para in paragraphs:
para_size = len(para)
if current_size + para_size > chunk_size and current_chunk:
# Save current chunk
chunks.append(separator.join(current_chunk))
# Keep overlap
overlap_text = separator.join(current_chunk)
if len(overlap_text) > overlap:
# Find a good break point for overlap
current_chunk = [current_chunk[-1]] if current_chunk else []
current_size = len(current_chunk[-1]) if current_chunk else 0
else:
current_chunk = []
current_size = 0
current_chunk.append(para)
current_size += para_size
# Don't forget the last chunk
if current_chunk:
chunks.append(separator.join(current_chunk))
return chunks
def chunk_by_tokens(
text: str,
max_tokens: int = 500,
overlap_tokens: int = 50,
) -> List[str]:
"""Split text by approximate token count (4 chars ≈ 1 token)"""
chars_per_token = 4
chunk_size = max_tokens * chars_per_token
overlap = overlap_tokens * chars_per_token
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
# Try to break at sentence boundary
if end < len(text):
for boundary in [". ", "! ", "? ", "\n"]:
last_boundary = text.rfind(boundary, start, end)
if last_boundary > start:
end = last_boundary + len(boundary)
break
chunks.append(text[start:end].strip())
start = end - overlap
return [c for c in chunks if c] # Remove empty chunks
# ===========================================
# Caching
# ===========================================
class EmbeddingCache:
"""Simple in-memory cache for embeddings"""
def __init__(self, max_size: int = 10000):
self.cache: dict = {}
self.max_size = max_size
def get(self, text: str) -> Optional[np.ndarray]:
"""Get cached embedding"""
return self.cache.get(hash(text))
def set(self, text: str, embedding: np.ndarray) -> None:
"""Cache embedding"""
if len(self.cache) >= self.max_size:
# Simple eviction: remove first item
first_key = next(iter(self.cache))
del self.cache[first_key]
self.cache[hash(text)] = embedding
def get_or_compute(
self,
text: str,
compute_fn: callable,
) -> np.ndarray:
"""Get from cache or compute"""
cached = self.get(text)
if cached is not None:
return cached
embedding = compute_fn(text)
self.set(text, embedding)
return embedding
# ===========================================
# Usage Example
# ===========================================
"""
from embedding_utils import (
EmbeddingClient,
get_config_from_env,
search_similar,
chunk_text,
)
# Initialize client
client = EmbeddingClient(get_config_from_env())
# Generate embeddings
texts = ["Hello world", "How are you?", "Machine learning is great"]
embeddings = client.embed(texts)
# Search
query_embedding = client.embed("Hi there")[0]
results = search_similar(query_embedding, embeddings, texts, top_k=2)
for r in results:
print(f"{r.score:.3f}: {r.text}")
# Chunk long text
long_text = open("document.txt").read()
chunks = chunk_text(long_text, chunk_size=500, overlap=50)
chunk_embeddings = client.embed(chunks)
"""
/**
* LLM Configuration Template
* Usage: Configure multiple LLM providers with fallback
*/
// ===========================================
// Types
// ===========================================
export interface LLMConfig {
provider: 'openai' | 'anthropic' | 'google' | 'azure' | 'local';
model: string;
apiKey?: string;
baseUrl?: string;
maxTokens?: number;
temperature?: number;
timeout?: number;
}
export interface LLMMessage {
role: 'system' | 'user' | 'assistant';
content: string;
}
export interface LLMResponse {
content: string;
model: string;
usage?: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
}
// ===========================================
// Provider Configurations
// ===========================================
export const PROVIDER_CONFIGS: Record<string, Partial<LLMConfig>> = {
// OpenAI
'gpt-4o': {
provider: 'openai',
model: 'gpt-4o',
maxTokens: 4096,
},
'gpt-4o-mini': {
provider: 'openai',
model: 'gpt-4o-mini',
maxTokens: 4096,
},
// Anthropic
'claude-sonnet': {
provider: 'anthropic',
model: 'claude-sonnet-4-20250514',
maxTokens: 8192,
},
'claude-haiku': {
provider: 'anthropic',
model: 'claude-3-5-haiku-20241022',
maxTokens: 8192,
},
// Google
'gemini-pro': {
provider: 'google',
model: 'gemini-2.0-flash',
maxTokens: 8192,
},
// Local (Ollama)
'llama3': {
provider: 'local',
model: 'llama3',
baseUrl: 'http://localhost:11434/v1',
maxTokens: 4096,
},
};
// ===========================================
// Default Configuration
// ===========================================
export const DEFAULT_CONFIG: LLMConfig = {
provider: 'openai',
model: 'gpt-4o-mini',
maxTokens: 4096,
temperature: 0.7,
timeout: 30000,
};
// ===========================================
// Environment-based Configuration
// ===========================================
export function getConfigFromEnv(): LLMConfig {
const provider = process.env.LLM_PROVIDER as LLMConfig['provider'] || 'openai';
const apiKeys: Record<string, string | undefined> = {
openai: process.env.OPENAI_API_KEY,
anthropic: process.env.ANTHROPIC_API_KEY,
google: process.env.GOOGLE_API_KEY,
azure: process.env.AZURE_OPENAI_API_KEY,
local: undefined,
};
return {
provider,
model: process.env.LLM_MODEL || DEFAULT_CONFIG.model,
apiKey: apiKeys[provider],
baseUrl: process.env.LLM_BASE_URL,
maxTokens: parseInt(process.env.LLM_MAX_TOKENS || '') || DEFAULT_CONFIG.maxTokens,
temperature: parseFloat(process.env.LLM_TEMPERATURE || '') || DEFAULT_CONFIG.temperature,
timeout: parseInt(process.env.LLM_TIMEOUT || '') || DEFAULT_CONFIG.timeout,
};
}
// ===========================================
// LLM Client Factory
// ===========================================
export function createLLMClient(config: Partial<LLMConfig> = {}) {
const finalConfig = { ...DEFAULT_CONFIG, ...config };
return {
config: finalConfig,
async chat(messages: LLMMessage[]): Promise<LLMResponse> {
// Implementation depends on provider
// This is a template - implement based on your SDK choice
throw new Error(`Implement chat() for provider: ${finalConfig.provider}`);
},
async complete(prompt: string): Promise<string> {
const response = await this.chat([{ role: 'user', content: prompt }]);
return response.content;
},
async stream(messages: LLMMessage[]): AsyncGenerator<string> {
// Streaming implementation
throw new Error(`Implement stream() for provider: ${finalConfig.provider}`);
},
};
}
// ===========================================
// Retry & Fallback Utilities
// ===========================================
export interface RetryConfig {
maxRetries: number;
initialDelay: number;
maxDelay: number;
backoffMultiplier: number;
}
export const DEFAULT_RETRY_CONFIG: RetryConfig = {
maxRetries: 3,
initialDelay: 1000,
maxDelay: 10000,
backoffMultiplier: 2,
};
export async function withRetry<T>(
fn: () => Promise<T>,
config: Partial<RetryConfig> = {}
): Promise<T> {
const { maxRetries, initialDelay, maxDelay, backoffMultiplier } = {
...DEFAULT_RETRY_CONFIG,
...config,
};
let lastError: Error | undefined;
let delay = initialDelay;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (attempt === maxRetries) break;
// Check if error is retryable
const isRetryable = isRetryableError(error);
if (!isRetryable) throw error;
console.warn(`Attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
await sleep(delay);
delay = Math.min(delay * backoffMultiplier, maxDelay);
}
}
throw lastError;
}
function isRetryableError(error: unknown): boolean {
if (error instanceof Error) {
// Rate limit errors
if (error.message.includes('rate limit')) return true;
if (error.message.includes('429')) return true;
// Temporary server errors
if (error.message.includes('500')) return true;
if (error.message.includes('502')) return true;
if (error.message.includes('503')) return true;
// Network errors
if (error.message.includes('ECONNRESET')) return true;
if (error.message.includes('ETIMEDOUT')) return true;
}
return false;
}
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// ===========================================
// Fallback Chain
// ===========================================
export async function withFallback<T>(
primary: () => Promise<T>,
fallbacks: Array<() => Promise<T>>
): Promise<T> {
try {
return await primary();
} catch (primaryError) {
console.warn('Primary LLM failed, trying fallbacks...', primaryError);
for (let i = 0; i < fallbacks.length; i++) {
try {
console.log(`Trying fallback ${i + 1}...`);
return await fallbacks[i]();
} catch (fallbackError) {
console.warn(`Fallback ${i + 1} failed`, fallbackError);
}
}
throw new Error('All LLM providers failed');
}
}
// ===========================================
// Usage Example
// ===========================================
/*
import { createLLMClient, getConfigFromEnv, withRetry, withFallback } from './llm-config';
// Simple usage
const client = createLLMClient(getConfigFromEnv());
const response = await client.complete('Hello, world!');
// With retry
const response = await withRetry(
() => client.complete('Hello!'),
{ maxRetries: 3 }
);
// With fallback
const response = await withFallback(
() => createLLMClient({ model: 'gpt-4o' }).complete('Hello!'),
[
() => createLLMClient({ model: 'claude-sonnet' }).complete('Hello!'),
() => createLLMClient({ model: 'gemini-pro' }).complete('Hello!'),
]
);
*/
AI/ML Integration Templates
Configuration and utility templates for LLM and embedding integration.
Files
| Template | Purpose |
|---|---|
llm-config.ts | Multi-provider LLM configuration with retry/fallback |
embedding-utils.py | Embedding generation, search, and chunking utilities |
Usage
LLM Configuration (TypeScript)
import {
createLLMClient,
getConfigFromEnv,
withRetry,
withFallback,
} from './llm-config';
// Simple usage
const client = createLLMClient({
provider: 'openai',
model: 'gpt-4o-mini',
apiKey: process.env.OPENAI_API_KEY,
});
const response = await client.complete('Hello!');
// With retry on rate limits
const response = await withRetry(
() => client.complete('Hello!'),
{ maxRetries: 3, initialDelay: 1000 }
);
// With fallback providers
const response = await withFallback(
() => createLLMClient({ model: 'gpt-4o' }).complete('Hello!'),
[
() => createLLMClient({ provider: 'anthropic', model: 'claude-sonnet' }).complete('Hello!'),
() => createLLMClient({ provider: 'google', model: 'gemini-pro' }).complete('Hello!'),
]
);Embedding Utilities (Python)
from embedding_utils import (
EmbeddingClient,
search_similar,
chunk_text,
cosine_similarity,
)
# Initialize client
client = EmbeddingClient()
# Generate embeddings
texts = ["Machine learning", "Deep learning", "Natural language"]
embeddings = client.embed(texts)
# Search similar
query = client.embed("AI techniques")[0]
results = search_similar(query, embeddings, texts, top_k=2)
# Chunk long documents
chunks = chunk_text(long_document, chunk_size=500, overlap=50)
chunk_embeddings = client.embed(chunks)Environment Variables
LLM
LLM_PROVIDER=openai # openai, anthropic, google, azure, local
LLM_MODEL=gpt-4o-mini
LLM_MAX_TOKENS=4096
LLM_TEMPERATURE=0.7
LLM_TIMEOUT=30000
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=...Embeddings
EMBEDDING_PROVIDER=openai
EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_DIMENSIONS=1536
EMBEDDING_BATCH_SIZE=100
EMBEDDING_NORMALIZE=trueSupported Models
LLM Providers
| Provider | Models |
|---|---|
| OpenAI | gpt-4o, gpt-4o-mini |
| Anthropic | claude-sonnet, claude-haiku |
| gemini-pro | |
| Local | llama3 (Ollama) |
Embedding Models
| Provider | Models | Dimensions |
|---|---|---|
| OpenAI | text-embedding-3-small | 1536 |
| OpenAI | text-embedding-3-large | 3072 |
| text-embedding-004 | 768 | |
| Local | all-MiniLM-L6-v2 | 384 |
Key Features
LLM Config
- Multi-provider support
- Exponential backoff retry
- Provider fallback chain
- Rate limit handling
Embedding Utils
- Batch processing
- L2 normalization
- Similarity search
- Text chunking (by chars or tokens)
- Deduplication
- In-memory caching
Integration Examples
RAG Pipeline
# 1. Chunk documents
chunks = chunk_text(document, chunk_size=500)
# 2. Generate embeddings
embeddings = client.embed(chunks)
# 3. Search relevant chunks
query_emb = client.embed(user_query)[0]
results = search_similar(query_emb, embeddings, chunks, top_k=3)
# 4. Build context
context = "\n".join([r.text for r in results])
# 5. Generate response
response = llm.complete(f"Context: {context}\n\nQuestion: {user_query}")Semantic Cache
cache = EmbeddingCache(max_size=10000)
def cached_embed(text):
return cache.get_or_compute(
text,
lambda t: client.embed(t)[0]
)Related skills
AI & Agent Buildingagents