diff --git a/src/api/providers/fetchers/__tests__/litellm.spec.ts b/src/api/providers/fetchers/__tests__/litellm.spec.ts index c05cda8839..212dc6a7c6 100644 --- a/src/api/providers/fetchers/__tests__/litellm.spec.ts +++ b/src/api/providers/fetchers/__tests__/litellm.spec.ts @@ -262,6 +262,26 @@ describe("getLiteLLMModels", () => { }) }) + it("makes request without authorization header when API key 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("handles computer use models correctly", async () => { const mockResponse = { data: { diff --git a/src/api/providers/fetchers/litellm.ts b/src/api/providers/fetchers/litellm.ts index de895d01fb..639db46fb5 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 cf8d16a112..e195a4da3b 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 || "", modelId: options.litellmModelId, defaultModelId: litellmDefaultModelId, defaultModelInfo: litellmDefaultModelInfo, diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index fac7ed10d5..00b8aca408 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -929,20 +929,27 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We { key: "vercel-ai-gateway", options: { provider: "vercel-ai-gateway" } }, ] - // LiteLLM is conditional on baseUrl+apiKey + // LiteLLM is conditional on baseUrl (apiKey is optional for self-hosted instances) 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) { - await flushModels({ provider: "litellm", apiKey: litellmApiKey, baseUrl: litellmBaseUrl }, true) + await flushModels( + { provider: "litellm", apiKey: litellmApiKey || "", baseUrl: litellmBaseUrl }, + true, + ) } candidates.push({ key: "litellm", - options: { provider: "litellm", apiKey: litellmApiKey, baseUrl: litellmBaseUrl }, + options: { + provider: "litellm", + apiKey: litellmApiKey || "", + baseUrl: litellmBaseUrl, + }, }) } diff --git a/src/shared/api.ts b/src/shared/api.ts index 66ed4f25ad..de520055cb 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 38ae1f3a96..ea30872ca3 100644 --- a/webview-ui/src/components/settings/providers/LiteLLM.tsx +++ b/webview-ui/src/components/settings/providers/LiteLLM.tsx @@ -86,7 +86,7 @@ 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 @@ -121,9 +121,7 @@ export const LiteLLM = ({