Docs/Skills and code/src/

src/analysis/embeddings.js

Code48 lines1.6 KBsrc/

const DEFAULT_BASE_URL = "http://localhost:11434";

export function embeddingConfig() {
  return {
    baseUrl: (process.env.OLLAMA_BASE_URL || DEFAULT_BASE_URL).replace(/\/$/, ""),
    model: process.env.OLLAMA_EMBEDDING_MODEL || "nomic-embed-text"
  };
}

export async function embedTexts(texts, { baseUrl, model } = embeddingConfig()) {
  if (!texts.length) return [];
  const response = await fetch(`${baseUrl}/api/embed`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ model, input: texts }),
    signal: AbortSignal.timeout(120000)
  });
  if (!response.ok) {
    const body = await response.text().catch(() => "");
    throw new Error(`Ollama embedding request failed (${response.status}): ${body.slice(0, 400)}`);
  }
  const payload = await response.json();
  const vectors = payload.embeddings;
  if (!Array.isArray(vectors) || vectors.length !== texts.length) {
    throw new Error(`Embedding response mismatch: expected ${texts.length}, received ${vectors?.length ?? 0}`);
  }
  for (const vector of vectors) {
    if (!Array.isArray(vector) || !vector.length || vector.some(n => !Number.isFinite(n))) {
      throw new Error("Ollama returned an invalid embedding vector.");
    }
  }
  return vectors;
}

export function cosineSimilarity(a, b) {
  if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length || !a.length) return null;
  let dot = 0;
  let normA = 0;
  let normB = 0;
  for (let i = 0; i < a.length; i++) {
    dot += a[i] * b[i];
    normA += a[i] * a[i];
    normB += b[i] * b[i];
  }
  if (!normA || !normB) return null;
  return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}

Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.