Skip to main content
Glama
ollama-model.ts4.36 kB
/** * Ollama Embedding Model * * Implementation of embedding model using Ollama for local embedding generation. * Supports various embedding models (nomic-embed-text, mxbai-embed-large, etc.) * with automatic retry logic and timeout handling. * * Requirements: 2.1, 2.2, 2.3, 2.4, 2.5 */ import { EmbeddingError, NetworkError } from "../types"; import { BaseEmbeddingModel } from "./base-model"; /** * Configuration for Ollama embedding model */ export interface OllamaConfig { host: string; modelName: string; dimension: number; timeout?: number; maxRetries?: number; } /** * Ollama API response format */ interface OllamaEmbeddingResponse { embedding: number[]; } /** * Ollama embedding model implementation */ export class OllamaEmbeddingModel extends BaseEmbeddingModel { private readonly host: string; private readonly modelName: string; private readonly dimension: number; constructor(config: OllamaConfig) { super(config.timeout, config.maxRetries); this.host = config.host; this.modelName = config.modelName; this.dimension = config.dimension; } /** * Generate embedding vector for text using Ollama API */ async generate(text: string): Promise<number[]> { this.validateInput(text); return this.withRetry(async () => { return this.withTimeout(this.callOllamaAPI(text)); }); } /** * Get embedding dimension for this model */ getDimension(): number { return this.dimension; } /** * Get model name/identifier */ getModelName(): string { return this.modelName; } /** * Normalize embedding vector to unit length * This ensures embeddings are suitable for cosine similarity calculations */ private normalizeEmbedding(embedding: number[]): number[] { const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0)); // Avoid division by zero if (magnitude === 0) { return embedding; } return embedding.map((val) => val / magnitude); } /** * Call Ollama API to generate embedding */ private async callOllamaAPI(text: string): Promise<number[]> { const endpoint = `${this.host}/api/embeddings`; try { const response = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ model: this.modelName, prompt: text, }), }); if (!response.ok) { if (response.status === 404) { throw new NetworkError( `Model '${this.modelName}' not found. Please ensure the model is pulled in Ollama.`, endpoint, response.status ); } throw new NetworkError( `Ollama API request failed with status ${response.status}: ${response.statusText}`, endpoint, response.status ); } const data = (await response.json()) as OllamaEmbeddingResponse; if (!data.embedding || !Array.isArray(data.embedding)) { throw new EmbeddingError( "Invalid response format from Ollama API: missing or invalid 'embedding' field", "INVALID_RESPONSE", { response: data } ); } if (data.embedding.length !== this.dimension) { throw new EmbeddingError( `Embedding dimension mismatch: expected ${this.dimension}, got ${data.embedding.length}`, "DIMENSION_MISMATCH", { expected: this.dimension, actual: data.embedding.length } ); } // Normalize embedding to unit vector for cosine similarity return this.normalizeEmbedding(data.embedding); } catch (error) { // Re-throw our custom errors if (error instanceof NetworkError || error instanceof EmbeddingError) { throw error; } // Handle fetch errors (network issues, connection refused, etc.) if (error instanceof TypeError) { throw new NetworkError( `Failed to connect to Ollama at ${this.host}: ${error.message}`, endpoint ); } // Unknown error throw new EmbeddingError( `Unexpected error calling Ollama API: ${error instanceof Error ? error.message : String(error)}`, "UNKNOWN_ERROR", { originalError: error } ); } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ThoughtMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server