From 96f413f68bf9a820f4eb556a77bdcc6e6a88e9c6 Mon Sep 17 00:00:00 2001 From: Varun Chopra <113368492+VarunChopra11@users.noreply.github.com> Date: Sun, 8 Mar 2026 15:58:46 +0000 Subject: [PATCH 1/2] fix(memory): update Ollama embeddings to /api/embed endpoint --- src/memory/embeddings-ollama.test.ts | 12 ++++++------ src/memory/embeddings-ollama.ts | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/memory/embeddings-ollama.test.ts b/src/memory/embeddings-ollama.test.ts index 910a7515696..d8f2315db79 100644 --- a/src/memory/embeddings-ollama.test.ts +++ b/src/memory/embeddings-ollama.test.ts @@ -3,10 +3,10 @@ import type { OpenClawConfig } from "../config/config.js"; import { createOllamaEmbeddingProvider } from "./embeddings-ollama.js"; describe("embeddings-ollama", () => { - it("calls /api/embeddings and returns normalized vectors", async () => { + it("calls /api/embed and returns normalized vectors", async () => { const fetchMock = vi.fn( async () => - new Response(JSON.stringify({ embedding: [3, 4] }), { + new Response(JSON.stringify({ embeddings: [[3, 4]] }), { status: 200, headers: { "content-type": "application/json" }, }), @@ -31,7 +31,7 @@ describe("embeddings-ollama", () => { it("resolves baseUrl/apiKey/headers from models.providers.ollama and strips /v1", async () => { const fetchMock = vi.fn( async () => - new Response(JSON.stringify({ embedding: [1, 0] }), { + new Response(JSON.stringify({ embeddings: [[1, 0]] }), { status: 200, headers: { "content-type": "application/json" }, }), @@ -60,7 +60,7 @@ describe("embeddings-ollama", () => { await provider.embedQuery("hello"); expect(fetchMock).toHaveBeenCalledWith( - "http://127.0.0.1:11434/api/embeddings", + "http://127.0.0.1:11434/api/embed", expect.objectContaining({ method: "POST", headers: expect.objectContaining({ @@ -90,7 +90,7 @@ describe("embeddings-ollama", () => { it("falls back to env key when models.providers.ollama.apiKey is an unresolved SecretRef", async () => { const fetchMock = vi.fn( async () => - new Response(JSON.stringify({ embedding: [1, 0] }), { + new Response(JSON.stringify({ embeddings: [[1, 0]] }), { status: 200, headers: { "content-type": "application/json" }, }), @@ -118,7 +118,7 @@ describe("embeddings-ollama", () => { await provider.embedQuery("hello"); expect(fetchMock).toHaveBeenCalledWith( - "http://127.0.0.1:11434/api/embeddings", + "http://127.0.0.1:11434/api/embed", expect.objectContaining({ headers: expect.objectContaining({ Authorization: "Bearer ollama-env", diff --git a/src/memory/embeddings-ollama.ts b/src/memory/embeddings-ollama.ts index 4c9326df874..3b2910862eb 100644 --- a/src/memory/embeddings-ollama.ts +++ b/src/memory/embeddings-ollama.ts @@ -89,7 +89,7 @@ export async function createOllamaEmbeddingProvider( options: EmbeddingProviderOptions, ): Promise<{ provider: EmbeddingProvider; client: OllamaEmbeddingClient }> { const client = resolveOllamaEmbeddingClient(options); - const embedUrl = `${client.baseUrl.replace(/\/$/, "")}/api/embeddings`; + const embedUrl = `${client.baseUrl.replace(/\/$/, "")}/api/embed`; const embedOne = async (text: string): Promise => { const json = await withRemoteHttpResponse({ @@ -98,19 +98,19 @@ export async function createOllamaEmbeddingProvider( init: { method: "POST", headers: client.headers, - body: JSON.stringify({ model: client.model, prompt: text }), + body: JSON.stringify({ model: client.model, input: text }), }, onResponse: async (res) => { if (!res.ok) { throw new Error(`Ollama embeddings HTTP ${res.status}: ${await res.text()}`); } - return (await res.json()) as { embedding?: number[] }; + return (await res.json()) as { embeddings?: number[][] }; }, }); - if (!Array.isArray(json.embedding)) { - throw new Error(`Ollama embeddings response missing embedding[]`); + if (!Array.isArray(json.embeddings) || !Array.isArray(json.embeddings[0])) { + throw new Error(`Ollama embeddings response missing embeddings[]`); } - return sanitizeAndNormalizeEmbedding(json.embedding); + return sanitizeAndNormalizeEmbedding(json.embeddings[0]); }; const provider: EmbeddingProvider = { @@ -118,7 +118,7 @@ export async function createOllamaEmbeddingProvider( model: client.model, embedQuery: embedOne, embedBatch: async (texts: string[]) => { - // Ollama /api/embeddings accepts one prompt per request. + // Ollama /api/embed accepts one input per request. return await Promise.all(texts.map(embedOne)); }, }; From bddd6d823997b0e0d159d4e86ae1462c883c2484 Mon Sep 17 00:00:00 2001 From: Varun Chopra <113368492+VarunChopra11@users.noreply.github.com> Date: Sun, 8 Mar 2026 16:25:48 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix(memory):=20correct=20embedBatch=20comme?= =?UTF-8?q?nt=20=E2=80=94=20/api/embed=20supports=20batched=20input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/memory/embeddings-ollama.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/memory/embeddings-ollama.ts b/src/memory/embeddings-ollama.ts index 3b2910862eb..7dc7e3eb856 100644 --- a/src/memory/embeddings-ollama.ts +++ b/src/memory/embeddings-ollama.ts @@ -118,7 +118,8 @@ export async function createOllamaEmbeddingProvider( model: client.model, embedQuery: embedOne, embedBatch: async (texts: string[]) => { - // Ollama /api/embed accepts one input per request. + // Ollama /api/embed supports batched input, but we fan-out here to + // keep error handling and response normalisation consistent per text. return await Promise.all(texts.map(embedOne)); }, };