Compare commits

...

4 Commits

Author SHA1 Message Date
Toshii 49a9748bc5 Merge branch 'main' into to/lite-llm 2025-06-26 22:45:03 -07:00
0xtoshii 3101377338 base 2025-06-26 22:43:35 -07:00
0xtoshii 00c427f610 base 3 2025-06-26 22:18:19 -07:00
0xtoshii 4f1b263a28 base 2
Co-authored-by: StvLz <lizarazo.steven@gmail.com>
2025-06-26 21:59:47 -07:00
2 changed files with 240 additions and 195 deletions
@@ -1,7 +1,5 @@
import { useExtensionState } from "@/context/ExtensionStateContext"
import { ModelsServiceClient } from "@/services/grpc-client"
import { getAsVar, VSC_DESCRIPTION_FOREGROUND } from "@/utils/vscStyles"
import { ApiConfiguration, geminiModels, liteLlmModelInfoSaneDefaults, ModelInfo } from "@shared/api"
import { EmptyRequest, StringRequest } from "@shared/proto/common"
import { UpdateApiConfigurationRequest } from "@shared/proto/models"
import { convertApiConfigurationToProto } from "@shared/proto-conversions/models/api-configuration-conversion"
@@ -18,9 +16,7 @@ import { Fragment, memo, useCallback, useEffect, useMemo, useState } from "react
import { useInterval } from "react-use"
import styled from "styled-components"
import * as vscodemodels from "vscode"
import OllamaModelPicker from "./OllamaModelPicker"
import OpenRouterModelPicker, { ModelDescriptionMarkdown, OPENROUTER_MODEL_PICKER_Z_INDEX } from "./OpenRouterModelPicker"
import ThinkingBudgetSlider from "./ThinkingBudgetSlider"
import { normalizeApiConfiguration } from "./utils/providerUtils"
import { ClineProvider } from "./providers/ClineProvider"
@@ -47,6 +43,7 @@ import { ClaudeCodeProvider } from "./providers/ClaudeCodeProvider"
import { SapAiCoreProvider } from "./providers/SapAiCoreProvider"
import { BedrockProvider } from "./providers/BedrockProvider"
import { NebiusProvider } from "./providers/NebiusProvider"
import { LiteLlmProvider } from "./providers/LiteLlmProvider"
import { VSCodeLmProvider } from "./providers/VSCodeLmProvider"
import { LMStudioProvider } from "./providers/LMStudioProvider"
@@ -411,197 +408,14 @@ const ApiOptions = ({
<VSCodeLmProvider apiConfiguration={apiConfiguration} handleInputChange={handleInputChange} />
)}
{selectedProvider === "litellm" && (
<div>
<VSCodeTextField
value={apiConfiguration?.liteLlmBaseUrl || ""}
style={{ width: "100%" }}
type="url"
onInput={handleInputChange("liteLlmBaseUrl")}
placeholder={"Default: http://localhost:4000"}>
<span style={{ fontWeight: 500 }}>Base URL (optional)</span>
</VSCodeTextField>
<VSCodeTextField
value={apiConfiguration?.liteLlmApiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("liteLlmApiKey")}
placeholder="Default: noop">
<span style={{ fontWeight: 500 }}>API Key</span>
</VSCodeTextField>
<VSCodeTextField
value={apiConfiguration?.liteLlmModelId || ""}
style={{ width: "100%" }}
onInput={handleInputChange("liteLlmModelId")}
placeholder={"e.g. anthropic/claude-sonnet-4-20250514"}>
<span style={{ fontWeight: 500 }}>Model ID</span>
</VSCodeTextField>
<div style={{ display: "flex", flexDirection: "column", marginTop: 10, marginBottom: 10 }}>
{selectedModelInfo.supportsPromptCache && (
<>
<VSCodeCheckbox
checked={apiConfiguration?.liteLlmUsePromptCache || false}
onChange={(e: any) => {
const isChecked = e.target.checked === true
setApiConfiguration({
...apiConfiguration,
liteLlmUsePromptCache: isChecked,
})
}}
style={{ fontWeight: 500, color: "var(--vscode-charts-green)" }}>
Use prompt caching (GA)
</VSCodeCheckbox>
<p style={{ fontSize: "12px", marginTop: 3, color: "var(--vscode-charts-green)" }}>
Prompt caching requires a supported provider and model
</p>
</>
)}
</div>
<>
<ThinkingBudgetSlider apiConfiguration={apiConfiguration} setApiConfiguration={setApiConfiguration} />
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
Extended thinking is available for models such as Sonnet-4, o3-mini, Deepseek R1, etc. More info on{" "}
<VSCodeLink
href="https://docs.litellm.ai/docs/reasoning_content"
style={{ display: "inline", fontSize: "inherit" }}>
thinking mode configuration
</VSCodeLink>
</p>
</>
<div
style={{
color: getAsVar(VSC_DESCRIPTION_FOREGROUND),
display: "flex",
margin: "10px 0",
cursor: "pointer",
alignItems: "center",
}}
onClick={() => setModelConfigurationSelected((val) => !val)}>
<span
className={`codicon ${modelConfigurationSelected ? "codicon-chevron-down" : "codicon-chevron-right"}`}
style={{
marginRight: "4px",
}}></span>
<span
style={{
fontWeight: 700,
textTransform: "uppercase",
}}>
Model Configuration
</span>
</div>
{modelConfigurationSelected && (
<>
<VSCodeCheckbox
checked={!!apiConfiguration?.liteLlmModelInfo?.supportsImages}
onChange={(e: any) => {
const isChecked = e.target.checked === true
const modelInfo = apiConfiguration?.liteLlmModelInfo
? apiConfiguration.liteLlmModelInfo
: { ...liteLlmModelInfoSaneDefaults }
modelInfo.supportsImages = isChecked
setApiConfiguration({
...apiConfiguration,
liteLlmModelInfo: modelInfo,
})
}}>
Supports Images
</VSCodeCheckbox>
<div style={{ display: "flex", gap: 10, marginTop: "5px" }}>
<VSCodeTextField
value={
apiConfiguration?.liteLlmModelInfo?.contextWindow
? apiConfiguration.liteLlmModelInfo.contextWindow.toString()
: liteLlmModelInfoSaneDefaults.contextWindow?.toString()
}
style={{ flex: 1 }}
onInput={(input: any) => {
const modelInfo = apiConfiguration?.liteLlmModelInfo
? apiConfiguration.liteLlmModelInfo
: { ...liteLlmModelInfoSaneDefaults }
modelInfo.contextWindow = Number(input.target.value)
setApiConfiguration({
...apiConfiguration,
liteLlmModelInfo: modelInfo,
})
}}>
<span style={{ fontWeight: 500 }}>Context Window Size</span>
</VSCodeTextField>
<VSCodeTextField
value={
apiConfiguration?.liteLlmModelInfo?.maxTokens
? apiConfiguration.liteLlmModelInfo.maxTokens.toString()
: liteLlmModelInfoSaneDefaults.maxTokens?.toString()
}
style={{ flex: 1 }}
onInput={(input: any) => {
const modelInfo = apiConfiguration?.liteLlmModelInfo
? apiConfiguration.liteLlmModelInfo
: { ...liteLlmModelInfoSaneDefaults }
modelInfo.maxTokens = input.target.value
setApiConfiguration({
...apiConfiguration,
liteLlmModelInfo: modelInfo,
})
}}>
<span style={{ fontWeight: 500 }}>Max Output Tokens</span>
</VSCodeTextField>
</div>
<div style={{ display: "flex", gap: 10, marginTop: "5px" }}>
<VSCodeTextField
value={
apiConfiguration?.liteLlmModelInfo?.temperature !== undefined
? apiConfiguration.liteLlmModelInfo.temperature.toString()
: liteLlmModelInfoSaneDefaults.temperature?.toString()
}
onInput={(input: any) => {
const modelInfo = apiConfiguration?.liteLlmModelInfo
? apiConfiguration.liteLlmModelInfo
: { ...liteLlmModelInfoSaneDefaults }
// Check if the input ends with a decimal point or has trailing zeros after decimal
const value = input.target.value
const shouldPreserveFormat =
value.endsWith(".") || (value.includes(".") && value.endsWith("0"))
modelInfo.temperature =
value === ""
? liteLlmModelInfoSaneDefaults.temperature
: shouldPreserveFormat
? value // Keep as string to preserve decimal format
: parseFloat(value)
setApiConfiguration({
...apiConfiguration,
liteLlmModelInfo: modelInfo,
})
}}>
<span style={{ fontWeight: 500 }}>Temperature</span>
</VSCodeTextField>
</div>
</>
)}
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
LiteLLM provides a unified interface to access various LLM providers' models. See their{" "}
<VSCodeLink href="https://docs.litellm.ai/docs/" style={{ display: "inline", fontSize: "inherit" }}>
quickstart guide
</VSCodeLink>{" "}
for more information.
</p>
</div>
{apiConfiguration && selectedProvider === "litellm" && (
<LiteLlmProvider
apiConfiguration={apiConfiguration}
handleInputChange={handleInputChange}
showModelOptions={showModelOptions}
isPopup={isPopup}
setApiConfiguration={setApiConfiguration}
/>
)}
{apiConfiguration && selectedProvider === "lmstudio" && (
@@ -0,0 +1,231 @@
import { useState } from "react"
import { ApiConfiguration, liteLlmModelInfoSaneDefaults } from "@shared/api"
import { VSCodeTextField, VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { getAsVar, VSC_DESCRIPTION_FOREGROUND } from "@/utils/vscStyles"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { ModelInfoView } from "../common/ModelInfoView"
import ThinkingBudgetSlider from "../ThinkingBudgetSlider"
/**
* Props for the LiteLlmProvider component
*/
interface LiteLlmProviderProps {
apiConfiguration: ApiConfiguration
handleInputChange: (field: keyof ApiConfiguration) => (event: any) => void
showModelOptions: boolean
isPopup?: boolean
setApiConfiguration: (config: ApiConfiguration) => void
}
/**
* The LiteLLM provider configuration component
*/
export const LiteLlmProvider = ({
apiConfiguration,
handleInputChange,
showModelOptions,
isPopup,
setApiConfiguration,
}: LiteLlmProviderProps) => {
// Get the normalized configuration
const { selectedModelId, selectedModelInfo } = normalizeApiConfiguration(apiConfiguration)
// Local state for collapsible model configuration section
const [modelConfigurationSelected, setModelConfigurationSelected] = useState(false)
return (
<div>
<VSCodeTextField
value={apiConfiguration?.liteLlmBaseUrl || ""}
style={{ width: "100%" }}
type="url"
onInput={handleInputChange("liteLlmBaseUrl")}
placeholder={"Default: http://localhost:4000"}>
<span style={{ fontWeight: 500 }}>Base URL (optional)</span>
</VSCodeTextField>
<VSCodeTextField
value={apiConfiguration?.liteLlmApiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("liteLlmApiKey")}
placeholder="Default: noop">
<span style={{ fontWeight: 500 }}>API Key</span>
</VSCodeTextField>
<VSCodeTextField
value={apiConfiguration?.liteLlmModelId || ""}
style={{ width: "100%" }}
onInput={handleInputChange("liteLlmModelId")}
placeholder={"e.g. anthropic/claude-sonnet-4-20250514"}>
<span style={{ fontWeight: 500 }}>Model ID</span>
</VSCodeTextField>
<div style={{ display: "flex", flexDirection: "column", marginTop: 10, marginBottom: 10 }}>
{selectedModelInfo.supportsPromptCache && (
<>
<VSCodeCheckbox
checked={apiConfiguration?.liteLlmUsePromptCache || false}
onChange={(e: any) => {
const isChecked = e.target.checked === true
setApiConfiguration({
...apiConfiguration,
liteLlmUsePromptCache: isChecked,
})
}}
style={{ fontWeight: 500, color: "var(--vscode-charts-green)" }}>
Use prompt caching (GA)
</VSCodeCheckbox>
<p style={{ fontSize: "12px", marginTop: 3, color: "var(--vscode-charts-green)" }}>
Prompt caching requires a supported provider and model
</p>
</>
)}
</div>
<>
<ThinkingBudgetSlider apiConfiguration={apiConfiguration} setApiConfiguration={setApiConfiguration} />
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
Extended thinking is available for models such as Sonnet-4, o3-mini, Deepseek R1, etc. More info on{" "}
<VSCodeLink
href="https://docs.litellm.ai/docs/reasoning_content"
style={{ display: "inline", fontSize: "inherit" }}>
thinking mode configuration
</VSCodeLink>
</p>
</>
<div
style={{
color: getAsVar(VSC_DESCRIPTION_FOREGROUND),
display: "flex",
margin: "10px 0",
cursor: "pointer",
alignItems: "center",
}}
onClick={() => setModelConfigurationSelected((val) => !val)}>
<span
className={`codicon ${modelConfigurationSelected ? "codicon-chevron-down" : "codicon-chevron-right"}`}
style={{
marginRight: "4px",
}}></span>
<span
style={{
fontWeight: 700,
textTransform: "uppercase",
}}>
Model Configuration
</span>
</div>
{modelConfigurationSelected && (
<>
<VSCodeCheckbox
checked={!!apiConfiguration?.liteLlmModelInfo?.supportsImages}
onChange={(e: any) => {
const isChecked = e.target.checked === true
const modelInfo = apiConfiguration?.liteLlmModelInfo
? apiConfiguration.liteLlmModelInfo
: { ...liteLlmModelInfoSaneDefaults }
modelInfo.supportsImages = isChecked
setApiConfiguration({
...apiConfiguration,
liteLlmModelInfo: modelInfo,
})
}}>
Supports Images
</VSCodeCheckbox>
<div style={{ display: "flex", gap: 10, marginTop: "5px" }}>
<VSCodeTextField
value={
apiConfiguration?.liteLlmModelInfo?.contextWindow
? apiConfiguration.liteLlmModelInfo.contextWindow.toString()
: liteLlmModelInfoSaneDefaults.contextWindow?.toString()
}
style={{ flex: 1 }}
onInput={(input: any) => {
const modelInfo = apiConfiguration?.liteLlmModelInfo
? apiConfiguration.liteLlmModelInfo
: { ...liteLlmModelInfoSaneDefaults }
modelInfo.contextWindow = Number(input.target.value)
setApiConfiguration({
...apiConfiguration,
liteLlmModelInfo: modelInfo,
})
}}>
<span style={{ fontWeight: 500 }}>Context Window Size</span>
</VSCodeTextField>
<VSCodeTextField
value={
apiConfiguration?.liteLlmModelInfo?.maxTokens
? apiConfiguration.liteLlmModelInfo.maxTokens.toString()
: liteLlmModelInfoSaneDefaults.maxTokens?.toString()
}
style={{ flex: 1 }}
onInput={(input: any) => {
const modelInfo = apiConfiguration?.liteLlmModelInfo
? apiConfiguration.liteLlmModelInfo
: { ...liteLlmModelInfoSaneDefaults }
modelInfo.maxTokens = input.target.value
setApiConfiguration({
...apiConfiguration,
liteLlmModelInfo: modelInfo,
})
}}>
<span style={{ fontWeight: 500 }}>Max Output Tokens</span>
</VSCodeTextField>
</div>
<div style={{ display: "flex", gap: 10, marginTop: "5px" }}>
<VSCodeTextField
value={
apiConfiguration?.liteLlmModelInfo?.temperature !== undefined
? apiConfiguration.liteLlmModelInfo.temperature.toString()
: liteLlmModelInfoSaneDefaults.temperature?.toString()
}
onInput={(input: any) => {
const modelInfo = apiConfiguration?.liteLlmModelInfo
? apiConfiguration.liteLlmModelInfo
: { ...liteLlmModelInfoSaneDefaults }
// Check if the input ends with a decimal point or has trailing zeros after decimal
const value = input.target.value
const shouldPreserveFormat = value.endsWith(".") || (value.includes(".") && value.endsWith("0"))
modelInfo.temperature =
value === ""
? liteLlmModelInfoSaneDefaults.temperature
: shouldPreserveFormat
? value // Keep as string to preserve decimal format
: parseFloat(value)
setApiConfiguration({
...apiConfiguration,
liteLlmModelInfo: modelInfo,
})
}}>
<span style={{ fontWeight: 500 }}>Temperature</span>
</VSCodeTextField>
</div>
</>
)}
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
LiteLLM provides a unified interface to access various LLM providers' models. See their{" "}
<VSCodeLink href="https://docs.litellm.ai/docs/" style={{ display: "inline", fontSize: "inherit" }}>
quickstart guide
</VSCodeLink>{" "}
for more information.
</p>
{showModelOptions && (
<ModelInfoView selectedModelId={selectedModelId} modelInfo={selectedModelInfo} isPopup={isPopup} />
)}
</div>
)
}