diff --git a/ui/litellm-dashboard/src/components/chat_ui/llm_calls/embeddings_api.test.tsx b/ui/litellm-dashboard/src/components/chat_ui/llm_calls/embeddings_api.test.tsx index c8d4d49cbe..f24a50074c 100644 --- a/ui/litellm-dashboard/src/components/chat_ui/llm_calls/embeddings_api.test.tsx +++ b/ui/litellm-dashboard/src/components/chat_ui/llm_calls/embeddings_api.test.tsx @@ -1,44 +1,40 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { makeOpenAIEmbeddingsRequest } from "./embeddings_api"; -import OpenAI from "openai"; -vi.mock("openai"); +vi.mock("@/components/networking", () => ({ + getProxyBaseUrl: vi.fn(() => "https://example.com"), +})); describe("embeddings_api", () => { - const mockCreate = vi.fn(); const mockUpdateEmbeddingsUI = vi.fn(); + const mockFetch = vi.fn(); beforeEach(() => { - // Mock the response structure from OpenAI embeddings API - mockCreate.mockResolvedValue({ - data: [ - { - embedding: [0.1, 0.2, 0.3, 0.4, 0.5], - index: 0, - object: "embedding", - }, - ], - model: "text-embedding-3-small", - object: "list", - usage: { - prompt_tokens: 5, - total_tokens: 5, - }, - }); + mockFetch.mockResolvedValue({ + ok: true, + json: async () => ({ + data: [ + { + embedding: [0.1, 0.2, 0.3, 0.4, 0.5], + index: 0, + object: "embedding", + }, + ], + model: "text-embedding-3-small", + object: "list", + }), + text: async () => "", + } as Response); - // Mock the OpenAI constructor and its methods - (OpenAI as any).mockImplementation(() => ({ - embeddings: { - create: mockCreate, - }, - })); + // @ts-ignore - assigning to global for test environment + global.fetch = mockFetch; }); afterEach(() => { vi.clearAllMocks(); }); - it("should make a request to the embeddings API", async () => { + it("should make a request to the embeddings endpoint", async () => { await makeOpenAIEmbeddingsRequest( "Hello, world!", mockUpdateEmbeddingsUI, @@ -47,13 +43,36 @@ describe("embeddings_api", () => { [], ); - expect(mockCreate).toHaveBeenCalledWith({ - model: "text-embedding-3-small", - input: "Hello, world!", + expect(mockFetch).toHaveBeenCalledTimes(1); + expect(mockFetch).toHaveBeenCalledWith("https://example.com/embeddings", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer 1234567890", + }, + body: JSON.stringify({ + model: "text-embedding-3-small", + input: "Hello, world!", + }), }); expect(mockUpdateEmbeddingsUI).toHaveBeenCalledWith( JSON.stringify([0.1, 0.2, 0.3, 0.4, 0.5]), "text-embedding-3-small", ); }); + + it("should not include encoding_format when making the request", async () => { + await makeOpenAIEmbeddingsRequest("Sample text", mockUpdateEmbeddingsUI, "text-embedding-3-small", "abcdef", []); + + const fetchCall = mockFetch.mock.calls[0]; + const options = fetchCall[1] as RequestInit; + const body = options.body as string; + const parsedBody = JSON.parse(body); + + expect(parsedBody).not.toHaveProperty("encoding_format"); + expect(parsedBody).toEqual({ + model: "text-embedding-3-small", + input: "Sample text", + }); + }); }); diff --git a/ui/litellm-dashboard/src/components/chat_ui/llm_calls/embeddings_api.tsx b/ui/litellm-dashboard/src/components/chat_ui/llm_calls/embeddings_api.tsx index 7a5ab21d9f..832d29bb85 100644 --- a/ui/litellm-dashboard/src/components/chat_ui/llm_calls/embeddings_api.tsx +++ b/ui/litellm-dashboard/src/components/chat_ui/llm_calls/embeddings_api.tsx @@ -1,6 +1,5 @@ import NotificationManager from "@/components/molecules/notifications_manager"; import { getProxyBaseUrl } from "@/components/networking"; -import openai from "openai"; export async function makeOpenAIEmbeddingsRequest( input: string, @@ -26,20 +25,36 @@ export async function makeOpenAIEmbeddingsRequest( headers["x-litellm-tags"] = tags.join(","); } - const client = new openai.OpenAI({ - apiKey: accessToken, - baseURL: proxyBaseUrl, - dangerouslyAllowBrowser: true, - defaultHeaders: headers, - }); - try { - const response = await client.embeddings.create({ - model: selectedModel, - input: input, + const normalizedBaseUrl = proxyBaseUrl.endsWith("/") ? proxyBaseUrl.slice(0, -1) : proxyBaseUrl; + const requestUrl = `${normalizedBaseUrl}/embeddings`; + + const response = await fetch(requestUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, + ...headers, + }, + body: JSON.stringify({ + model: selectedModel, + input, + }), }); - updateEmbeddingsUI(JSON.stringify(response.data[0].embedding), selectedModel); + if (!response.ok) { + const errorText = await response.text(); + throw new Error(errorText || `Request failed with status ${response.status}`); + } + + const responseData = await response.json(); + const embedding = responseData?.data?.[0]?.embedding; + + if (!embedding) { + throw new Error("No embedding returned from server"); + } + + updateEmbeddingsUI(JSON.stringify(embedding), responseData?.model ?? selectedModel); } catch (error: unknown) { NotificationManager.fromBackend( `Error occurred while making embeddings request. Please try again. Error: ${error}`,