Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/api/providers/fetchers/__tests__/litellm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
})
})
2 changes: 1 addition & 1 deletion src/api/providers/fetchers/litellm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ModelRecord> {
export async function getLiteLLMModels(apiKey: string | undefined, baseUrl: string): Promise<ModelRecord> {
try {
const headers: Record<string, string> = {
"Content-Type": "application/json",
Expand Down
2 changes: 1 addition & 1 deletion src/api/providers/lite-llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/shared/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 6 additions & 5 deletions webview-ui/src/components/settings/providers/LiteLLM.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -121,9 +124,7 @@ export const LiteLLM = ({
<Button
variant="outline"
onClick={handleRefreshModels}
disabled={
refreshStatus === "loading" || !apiConfiguration.litellmApiKey || !apiConfiguration.litellmBaseUrl
}
disabled={refreshStatus === "loading" || !apiConfiguration.litellmBaseUrl}
className="w-full">
<div className="flex items-center gap-2">
{refreshStatus === "loading" ? (
Expand Down
1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@
},
"validation": {
"apiKey": "You must provide a valid API key.",
"baseUrl": "You must provide a valid base URL.",
"awsRegion": "You must choose a region to use with Amazon Bedrock.",
"googleCloud": "You must provide a valid Google Cloud Project ID and Region.",
"modelId": "You must provide a valid model ID.",
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/utils/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ function validateModelsAndKeysProvided(apiConfiguration: ProviderSettings): stri
}
break
case "litellm":
if (!apiConfiguration.litellmApiKey) {
return i18next.t("settings:validation.apiKey")
if (!apiConfiguration.litellmBaseUrl) {
return i18next.t("settings:validation.baseUrl")
}
break
case "anthropic":
Expand Down
Loading