diff --git a/src/api/providers/fetchers/__tests__/litellm.spec.ts b/src/api/providers/fetchers/__tests__/litellm.spec.ts index c05cda88398..6bb08ce7a26 100644 --- a/src/api/providers/fetchers/__tests__/litellm.spec.ts +++ b/src/api/providers/fetchers/__tests__/litellm.spec.ts @@ -697,4 +697,44 @@ describe("getLiteLLMModels", () => { description: "model-with-only-max-output-tokens via LiteLLM proxy", }) }) + + it("sends request without Authorization header when apiKey is undefined", async () => { + const mockResponse = { + data: { + data: [], + }, + } + + mockedAxios.get.mockResolvedValue(mockResponse) + + await getLiteLLMModels(undefined, "http://localhost:4000") + + expect(mockedAxios.get).toHaveBeenCalledWith("http://localhost:4000/v1/model/info", { + headers: { + "Content-Type": "application/json", + ...DEFAULT_HEADERS, + }, + timeout: 5000, + }) + }) + + it("sends request without Authorization header when apiKey is empty string", async () => { + const mockResponse = { + data: { + data: [], + }, + } + + mockedAxios.get.mockResolvedValue(mockResponse) + + await getLiteLLMModels("", "http://localhost:4000") + + expect(mockedAxios.get).toHaveBeenCalledWith("http://localhost:4000/v1/model/info", { + headers: { + "Content-Type": "application/json", + ...DEFAULT_HEADERS, + }, + timeout: 5000, + }) + }) }) diff --git a/src/api/providers/fetchers/litellm.ts b/src/api/providers/fetchers/litellm.ts index de895d01fbf..639db46fb5f 100644 --- a/src/api/providers/fetchers/litellm.ts +++ b/src/api/providers/fetchers/litellm.ts @@ -11,7 +11,7 @@ import { DEFAULT_HEADERS } from "../constants" * @returns A promise that resolves to a record of model IDs to model info * @throws Will throw an error if the request fails or the response is not as expected. */ -export async function getLiteLLMModels(apiKey: string, baseUrl: string): Promise { +export async function getLiteLLMModels(apiKey: string | undefined, baseUrl: string): Promise { try { const headers: Record = { "Content-Type": "application/json", diff --git a/src/api/providers/lite-llm.ts b/src/api/providers/lite-llm.ts index cf8d16a1129..6ad8965ef67 100644 --- a/src/api/providers/lite-llm.ts +++ b/src/api/providers/lite-llm.ts @@ -26,7 +26,7 @@ export class LiteLLMHandler extends RouterProvider implements SingleCompletionHa options, name: "litellm", baseURL: `${options.litellmBaseUrl || "http://localhost:4000"}`, - apiKey: options.litellmApiKey || "dummy-key", + apiKey: options.litellmApiKey || "not-needed", modelId: options.litellmModelId, defaultModelId: litellmDefaultModelId, defaultModelInfo: litellmDefaultModelInfo, diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index d27fd6bec09..0bfd643632a 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1001,11 +1001,11 @@ export const webviewMessageHandler = async ( }, ] - // LiteLLM is conditional on baseUrl+apiKey + // LiteLLM is conditional on baseUrl (apiKey is optional) const litellmApiKey = apiConfiguration.litellmApiKey || message?.values?.litellmApiKey const litellmBaseUrl = apiConfiguration.litellmBaseUrl || message?.values?.litellmBaseUrl - if (litellmApiKey && litellmBaseUrl) { + if (litellmBaseUrl) { // If explicit credentials are provided in message.values (from Refresh Models button), // flush the cache first to ensure we fetch fresh data with the new credentials if (message?.values?.litellmApiKey || message?.values?.litellmBaseUrl) { diff --git a/src/shared/api.ts b/src/shared/api.ts index 52af6b20727..4721dbad630 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -171,7 +171,7 @@ type CommonFetchParams = { const dynamicProviderExtras = { openrouter: {} as {}, // eslint-disable-line @typescript-eslint/no-empty-object-type "vercel-ai-gateway": {} as {}, // eslint-disable-line @typescript-eslint/no-empty-object-type - litellm: {} as { apiKey: string; baseUrl: string }, + litellm: {} as { apiKey?: string; baseUrl: string }, requesty: {} as { apiKey?: string; baseUrl?: string }, unbound: {} as { apiKey?: string }, ollama: {} as {}, // eslint-disable-line @typescript-eslint/no-empty-object-type diff --git a/webview-ui/src/components/settings/providers/LiteLLM.tsx b/webview-ui/src/components/settings/providers/LiteLLM.tsx index 38ae1f3a96a..4891c69afca 100644 --- a/webview-ui/src/components/settings/providers/LiteLLM.tsx +++ b/webview-ui/src/components/settings/providers/LiteLLM.tsx @@ -86,13 +86,16 @@ export const LiteLLM = ({ const key = apiConfiguration.litellmApiKey const url = apiConfiguration.litellmBaseUrl - if (!key || !url) { + if (!url) { setRefreshStatus("error") setRefreshError(t("settings:providers.refreshModels.missingConfig")) return } - vscode.postMessage({ type: "requestRouterModels", values: { litellmApiKey: key, litellmBaseUrl: url } }) + vscode.postMessage({ + type: "requestRouterModels", + values: { ...(key ? { litellmApiKey: key } : {}), litellmBaseUrl: url }, + }) }, [apiConfiguration, setRefreshStatus, setRefreshError, t]) return ( @@ -121,9 +124,7 @@ export const LiteLLM = ({