From 2f6d6f9c29ce2d3d43ef5bf9d882fa2a8d3c1676 Mon Sep 17 00:00:00 2001 From: Nahim El Atmani <2959826+Naam@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:33:31 -0700 Subject: [PATCH] Fix Ollama API URL normalization by removing trailing slashes Ensures consistent behavior when constructing API endpoint URLs by normalizing the base URL to remove any trailing slashes. This prevents issues when the base URL is provided with trailing slashes. Added tests to verify URL normalization works correctly with: - URLs with a single trailing slash - URLs with multiple trailing slashes - URLs without trailing slashes --- .../embedders/__tests__/ollama.spec.ts | 84 +++++++++++++++++++ src/services/code-index/embedders/ollama.ts | 7 +- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/services/code-index/embedders/__tests__/ollama.spec.ts b/src/services/code-index/embedders/__tests__/ollama.spec.ts index ad95a18a3a0..253158dd500 100644 --- a/src/services/code-index/embedders/__tests__/ollama.spec.ts +++ b/src/services/code-index/embedders/__tests__/ollama.spec.ts @@ -80,6 +80,90 @@ describe("CodeIndexOllamaEmbedder", () => { const embedderWithDefaults = new CodeIndexOllamaEmbedder({}) expect(embedderWithDefaults.embedderInfo.name).toBe("ollama") }) + + it("should normalize URLs with trailing slashes", async () => { + // Create embedder with URL that has a trailing slash + const embedderWithTrailingSlash = new CodeIndexOllamaEmbedder({ + ollamaBaseUrl: "http://localhost:11434/", + ollamaModelId: "nomic-embed-text", + }) + + // Mock successful /api/tags call to test the normalized URL + mockFetch.mockImplementationOnce(() => + Promise.resolve({ + ok: true, + status: 200, + json: () => Promise.resolve({ models: [{ name: "nomic-embed-text" }] }), + } as Response), + ) + + // Call a method that uses the baseUrl + await embedderWithTrailingSlash.validateConfiguration() + + // Verify the URL used in the fetch call doesn't have a trailing slash + expect(mockFetch).toHaveBeenCalledWith( + "http://localhost:11434/api/tags", + expect.objectContaining({ + method: "GET", + }), + ) + }) + + it("should not modify URLs without trailing slashes", async () => { + // Create embedder with URL that doesn't have a trailing slash + const embedderWithoutTrailingSlash = new CodeIndexOllamaEmbedder({ + ollamaBaseUrl: "http://localhost:11434", + ollamaModelId: "nomic-embed-text", + }) + + // Mock successful /api/tags call + mockFetch.mockImplementationOnce(() => + Promise.resolve({ + ok: true, + status: 200, + json: () => Promise.resolve({ models: [{ name: "nomic-embed-text" }] }), + } as Response), + ) + + // Call a method that uses the baseUrl + await embedderWithoutTrailingSlash.validateConfiguration() + + // Verify the URL used in the fetch call is correct + expect(mockFetch).toHaveBeenCalledWith( + "http://localhost:11434/api/tags", + expect.objectContaining({ + method: "GET", + }), + ) + }) + + it("should handle multiple trailing slashes", async () => { + // Create embedder with URL that has multiple trailing slashes + const embedderWithMultipleTrailingSlashes = new CodeIndexOllamaEmbedder({ + ollamaBaseUrl: "http://localhost:11434///", + ollamaModelId: "nomic-embed-text", + }) + + // Mock successful /api/tags call + mockFetch.mockImplementationOnce(() => + Promise.resolve({ + ok: true, + status: 200, + json: () => Promise.resolve({ models: [{ name: "nomic-embed-text" }] }), + } as Response), + ) + + // Call a method that uses the baseUrl + await embedderWithMultipleTrailingSlashes.validateConfiguration() + + // Verify the URL used in the fetch call doesn't have trailing slashes + expect(mockFetch).toHaveBeenCalledWith( + "http://localhost:11434/api/tags", + expect.objectContaining({ + method: "GET", + }), + ) + }) }) describe("validateConfiguration", () => { diff --git a/src/services/code-index/embedders/ollama.ts b/src/services/code-index/embedders/ollama.ts index c160d39490e..9688a15ff04 100644 --- a/src/services/code-index/embedders/ollama.ts +++ b/src/services/code-index/embedders/ollama.ts @@ -20,7 +20,12 @@ export class CodeIndexOllamaEmbedder implements IEmbedder { constructor(options: ApiHandlerOptions) { // Ensure ollamaBaseUrl and ollamaModelId exist on ApiHandlerOptions or add defaults - this.baseUrl = options.ollamaBaseUrl || "http://localhost:11434" + let baseUrl = options.ollamaBaseUrl || "http://localhost:11434" + + // Normalize the baseUrl by removing all trailing slashes + baseUrl = baseUrl.replace(/\/+$/, "") + + this.baseUrl = baseUrl this.defaultModelId = options.ollamaModelId || "nomic-embed-text:latest" }