|
|
@@ -2,12 +2,12 @@ import React, { memo, useCallback, useEffect, useMemo, useState } from "react"
|
|
|
import { useAppTranslation } from "@/i18n/TranslationContext"
|
|
|
import { Trans } from "react-i18next"
|
|
|
import { useDebounce, useEvent } from "react-use"
|
|
|
-import { Checkbox, Dropdown, type DropdownOption } from "vscrui"
|
|
|
+import { LanguageModelChatSelector } from "vscode"
|
|
|
+import { Checkbox } from "vscrui"
|
|
|
import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
|
|
-import * as vscodemodels from "vscode"
|
|
|
import { ExternalLinkIcon } from "@radix-ui/react-icons"
|
|
|
|
|
|
-import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, Button } from "@/components/ui"
|
|
|
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectSeparator, Button } from "@/components/ui"
|
|
|
|
|
|
import {
|
|
|
ApiConfiguration,
|
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
unboundDefaultModelInfo,
|
|
|
requestyDefaultModelId,
|
|
|
requestyDefaultModelInfo,
|
|
|
+ ApiProvider,
|
|
|
} from "../../../../src/shared/api"
|
|
|
import { ExtensionMessage } from "../../../../src/shared/ExtensionMessage"
|
|
|
|
|
|
@@ -45,6 +46,7 @@ import {
|
|
|
OPENROUTER_DEFAULT_PROVIDER_NAME,
|
|
|
} from "@/components/ui/hooks/useOpenRouterModelProviders"
|
|
|
|
|
|
+import { MODELS_BY_PROVIDER, PROVIDERS, AWS_REGIONS, VERTEX_REGIONS } from "./constants"
|
|
|
import { VSCodeButtonLink } from "../common/VSCodeButtonLink"
|
|
|
import { ModelInfoView } from "./ModelInfoView"
|
|
|
import { ModelPicker } from "./ModelPicker"
|
|
|
@@ -53,35 +55,6 @@ import { validateApiConfiguration, validateModelId, validateBedrockArn } from "@
|
|
|
import { ApiErrorMessage } from "./ApiErrorMessage"
|
|
|
import { ThinkingBudget } from "./ThinkingBudget"
|
|
|
|
|
|
-const modelsByProvider: Record<string, Record<string, ModelInfo>> = {
|
|
|
- anthropic: anthropicModels,
|
|
|
- bedrock: bedrockModels,
|
|
|
- vertex: vertexModels,
|
|
|
- gemini: geminiModels,
|
|
|
- "openai-native": openAiNativeModels,
|
|
|
- deepseek: deepSeekModels,
|
|
|
- mistral: mistralModels,
|
|
|
-}
|
|
|
-
|
|
|
-const providers = [
|
|
|
- { value: "openrouter", label: "OpenRouter" },
|
|
|
- { value: "anthropic", label: "Anthropic" },
|
|
|
- { value: "gemini", label: "Google Gemini" },
|
|
|
- { value: "deepseek", label: "DeepSeek" },
|
|
|
- { value: "openai-native", label: "OpenAI" },
|
|
|
- { value: "openai", label: "OpenAI Compatible" },
|
|
|
- { value: "vertex", label: "GCP Vertex AI" },
|
|
|
- { value: "bedrock", label: "AWS Bedrock" },
|
|
|
- { value: "glama", label: "Glama" },
|
|
|
- { value: "vscode-lm", label: "VS Code LM API" },
|
|
|
- { value: "mistral", label: "Mistral" },
|
|
|
- { value: "lmstudio", label: "LM Studio" },
|
|
|
- { value: "ollama", label: "Ollama" },
|
|
|
- { value: "unbound", label: "Unbound" },
|
|
|
- { value: "requesty", label: "Requesty" },
|
|
|
- { value: "human-relay", label: "Human Relay" },
|
|
|
-]
|
|
|
-
|
|
|
interface ApiOptionsProps {
|
|
|
uriScheme: string | undefined
|
|
|
apiConfiguration: ApiConfiguration
|
|
|
@@ -103,7 +76,7 @@ const ApiOptions = ({
|
|
|
|
|
|
const [ollamaModels, setOllamaModels] = useState<string[]>([])
|
|
|
const [lmStudioModels, setLmStudioModels] = useState<string[]>([])
|
|
|
- const [vsCodeLmModels, setVsCodeLmModels] = useState<vscodemodels.LanguageModelChatSelector[]>([])
|
|
|
+ const [vsCodeLmModels, setVsCodeLmModels] = useState<LanguageModelChatSelector[]>([])
|
|
|
|
|
|
const [openRouterModels, setOpenRouterModels] = useState<Record<string, ModelInfo>>({
|
|
|
[openRouterDefaultModelId]: openRouterDefaultModelInfo,
|
|
|
@@ -132,9 +105,8 @@ const ApiOptions = ({
|
|
|
const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
|
|
|
|
|
|
const noTransform = <T,>(value: T) => value
|
|
|
+
|
|
|
const inputEventTransform = <E,>(event: E) => (event as { target: HTMLInputElement })?.target?.value as any
|
|
|
- const dropdownEventTransform = <T,>(event: DropdownOption | string | undefined) =>
|
|
|
- (typeof event == "string" ? event : event?.value) as T
|
|
|
|
|
|
const handleInputChange = useCallback(
|
|
|
<K extends keyof ApiConfiguration, E>(
|
|
|
@@ -258,40 +230,35 @@ const ApiOptions = ({
|
|
|
|
|
|
useEvent("message", onMessage)
|
|
|
|
|
|
- const selectedProviderModelOptions: DropdownOption[] = useMemo(
|
|
|
+ const selectedProviderModelOptions = useMemo(
|
|
|
() =>
|
|
|
- modelsByProvider[selectedProvider]
|
|
|
- ? [
|
|
|
- { value: "", label: "Select a model..." },
|
|
|
- ...Object.keys(modelsByProvider[selectedProvider]).map((modelId) => ({
|
|
|
- value: modelId,
|
|
|
- label: modelId,
|
|
|
- })),
|
|
|
- ]
|
|
|
+ MODELS_BY_PROVIDER[selectedProvider]
|
|
|
+ ? Object.keys(MODELS_BY_PROVIDER[selectedProvider]).map((modelId) => ({
|
|
|
+ value: modelId,
|
|
|
+ label: modelId,
|
|
|
+ }))
|
|
|
: [],
|
|
|
[selectedProvider],
|
|
|
)
|
|
|
|
|
|
return (
|
|
|
<div className="flex flex-col gap-3">
|
|
|
- <div className="dropdown-container">
|
|
|
- <label htmlFor="api-provider" className="font-medium">
|
|
|
- {t("settings:providers.apiProvider")}
|
|
|
- </label>
|
|
|
+ <div>
|
|
|
+ <label className="block font-medium mb-1">{t("settings:providers.apiProvider")}</label>
|
|
|
<Select
|
|
|
value={selectedProvider}
|
|
|
- onValueChange={handleInputChange("apiProvider", dropdownEventTransform)}>
|
|
|
+ onValueChange={(value) => setApiConfigurationField("apiProvider", value as ApiProvider)}>
|
|
|
<SelectTrigger className="w-full">
|
|
|
<SelectValue placeholder="Select" />
|
|
|
</SelectTrigger>
|
|
|
<SelectContent>
|
|
|
- <SelectGroup>
|
|
|
- {providers.map(({ value, label }) => (
|
|
|
- <SelectItem key={value} value={value}>
|
|
|
- {label}
|
|
|
- </SelectItem>
|
|
|
- ))}
|
|
|
- </SelectGroup>
|
|
|
+ <SelectItem value="openrouter">OpenRouter</SelectItem>
|
|
|
+ <SelectSeparator />
|
|
|
+ {PROVIDERS.map(({ value, label }) => (
|
|
|
+ <SelectItem key={value} value={value}>
|
|
|
+ {label}
|
|
|
+ </SelectItem>
|
|
|
+ ))}
|
|
|
</SelectContent>
|
|
|
</Select>
|
|
|
</div>
|
|
|
@@ -544,34 +511,22 @@ const ApiOptions = ({
|
|
|
</VSCodeTextField>
|
|
|
</>
|
|
|
)}
|
|
|
- <div className="dropdown-container">
|
|
|
- <label htmlFor="aws-region-dropdown" className="font-medium">
|
|
|
- {t("settings:providers.awsRegion")}
|
|
|
- </label>
|
|
|
- <Dropdown
|
|
|
- id="aws-region-dropdown"
|
|
|
+ <div>
|
|
|
+ <label className="block font-medium mb-1">{t("settings:providers.awsRegion")}</label>
|
|
|
+ <Select
|
|
|
value={apiConfiguration?.awsRegion || ""}
|
|
|
- onChange={handleInputChange("awsRegion", dropdownEventTransform)}
|
|
|
- options={[
|
|
|
- { value: "", label: "Select a region..." },
|
|
|
- { value: "us-east-1", label: "us-east-1" },
|
|
|
- { value: "us-east-2", label: "us-east-2" },
|
|
|
- { value: "us-west-2", label: "us-west-2" },
|
|
|
- { value: "ap-south-1", label: "ap-south-1" },
|
|
|
- { value: "ap-northeast-1", label: "ap-northeast-1" },
|
|
|
- { value: "ap-northeast-2", label: "ap-northeast-2" },
|
|
|
- { value: "ap-southeast-1", label: "ap-southeast-1" },
|
|
|
- { value: "ap-southeast-2", label: "ap-southeast-2" },
|
|
|
- { value: "ca-central-1", label: "ca-central-1" },
|
|
|
- { value: "eu-central-1", label: "eu-central-1" },
|
|
|
- { value: "eu-west-1", label: "eu-west-1" },
|
|
|
- { value: "eu-west-2", label: "eu-west-2" },
|
|
|
- { value: "eu-west-3", label: "eu-west-3" },
|
|
|
- { value: "sa-east-1", label: "sa-east-1" },
|
|
|
- { value: "us-gov-west-1", label: "us-gov-west-1" },
|
|
|
- ]}
|
|
|
- className="w-full"
|
|
|
- />
|
|
|
+ onValueChange={(value) => setApiConfigurationField("awsRegion", value)}>
|
|
|
+ <SelectTrigger className="w-full">
|
|
|
+ <SelectValue placeholder="Select" />
|
|
|
+ </SelectTrigger>
|
|
|
+ <SelectContent>
|
|
|
+ {AWS_REGIONS.map(({ value, label }) => (
|
|
|
+ <SelectItem key={value} value={value}>
|
|
|
+ {label}
|
|
|
+ </SelectItem>
|
|
|
+ ))}
|
|
|
+ </SelectContent>
|
|
|
+ </Select>
|
|
|
</div>
|
|
|
<Checkbox
|
|
|
checked={apiConfiguration?.awsUseCrossRegionInference || false}
|
|
|
@@ -628,24 +583,22 @@ const ApiOptions = ({
|
|
|
className="w-full">
|
|
|
<span className="font-medium">{t("settings:providers.googleCloudProjectId")}</span>
|
|
|
</VSCodeTextField>
|
|
|
- <div className="dropdown-container">
|
|
|
- <label htmlFor="vertex-region-dropdown" className="font-medium">
|
|
|
- {t("settings:providers.googleCloudRegion")}
|
|
|
- </label>
|
|
|
- <Dropdown
|
|
|
- id="vertex-region-dropdown"
|
|
|
+ <div>
|
|
|
+ <label className="block font-medium mb-1">{t("settings:providers.googleCloudRegion")}</label>
|
|
|
+ <Select
|
|
|
value={apiConfiguration?.vertexRegion || ""}
|
|
|
- onChange={handleInputChange("vertexRegion", dropdownEventTransform)}
|
|
|
- options={[
|
|
|
- { value: "", label: "Select a region..." },
|
|
|
- { value: "us-east5", label: "us-east5" },
|
|
|
- { value: "us-central1", label: "us-central1" },
|
|
|
- { value: "europe-west1", label: "europe-west1" },
|
|
|
- { value: "europe-west4", label: "europe-west4" },
|
|
|
- { value: "asia-southeast1", label: "asia-southeast1" },
|
|
|
- ]}
|
|
|
- className="w-full"
|
|
|
- />
|
|
|
+ onValueChange={(value) => setApiConfigurationField("vertexRegion", value)}>
|
|
|
+ <SelectTrigger className="w-full">
|
|
|
+ <SelectValue placeholder="Select" />
|
|
|
+ </SelectTrigger>
|
|
|
+ <SelectContent>
|
|
|
+ {VERTEX_REGIONS.map(({ value, label }) => (
|
|
|
+ <SelectItem key={value} value={value}>
|
|
|
+ {label}
|
|
|
+ </SelectItem>
|
|
|
+ ))}
|
|
|
+ </SelectContent>
|
|
|
+ </Select>
|
|
|
</div>
|
|
|
</>
|
|
|
)}
|
|
|
@@ -1247,10 +1200,8 @@ const ApiOptions = ({
|
|
|
|
|
|
{selectedProvider === "vscode-lm" && (
|
|
|
<>
|
|
|
- <div className="dropdown-container">
|
|
|
- <label htmlFor="vscode-lm-model" className="font-medium">
|
|
|
- {t("settings:providers.vscodeLmModel")}
|
|
|
- </label>
|
|
|
+ <div>
|
|
|
+ <label className="block font-medium mb-1">{t("settings:providers.vscodeLmModel")}</label>
|
|
|
{vsCodeLmModels.length > 0 ? (
|
|
|
<Select
|
|
|
value={
|
|
|
@@ -1258,23 +1209,21 @@ const ApiOptions = ({
|
|
|
? `${apiConfiguration.vsCodeLmModelSelector.vendor ?? ""}/${apiConfiguration.vsCodeLmModelSelector.family ?? ""}`
|
|
|
: ""
|
|
|
}
|
|
|
- onValueChange={handleInputChange("vsCodeLmModelSelector", (valueStr) => {
|
|
|
- const [vendor, family] = valueStr.split("/")
|
|
|
+ onValueChange={handleInputChange("vsCodeLmModelSelector", (value) => {
|
|
|
+ const [vendor, family] = value.split("/")
|
|
|
return { vendor, family }
|
|
|
})}>
|
|
|
<SelectTrigger className="w-full">
|
|
|
- <SelectValue placeholder="Select a model..." />
|
|
|
+ <SelectValue placeholder="Select" />
|
|
|
</SelectTrigger>
|
|
|
<SelectContent>
|
|
|
- <SelectGroup>
|
|
|
- {vsCodeLmModels.map((model) => (
|
|
|
- <SelectItem
|
|
|
- key={`${model.vendor}/${model.family}`}
|
|
|
- value={`${model.vendor}/${model.family}`}>
|
|
|
- {`${model.vendor} - ${model.family}`}
|
|
|
- </SelectItem>
|
|
|
- ))}
|
|
|
- </SelectGroup>
|
|
|
+ {vsCodeLmModels.map((model) => (
|
|
|
+ <SelectItem
|
|
|
+ key={`${model.vendor}/${model.family}`}
|
|
|
+ value={`${model.vendor}/${model.family}`}>
|
|
|
+ {`${model.vendor} - ${model.family}`}
|
|
|
+ </SelectItem>
|
|
|
+ ))}
|
|
|
</SelectContent>
|
|
|
</Select>
|
|
|
) : (
|
|
|
@@ -1379,51 +1328,52 @@ const ApiOptions = ({
|
|
|
/>
|
|
|
)}
|
|
|
|
|
|
- {openRouterModelProviders && (
|
|
|
- <>
|
|
|
- <div className="dropdown-container" style={{ marginTop: 3 }}>
|
|
|
+ {selectedProvider === "openrouter" &&
|
|
|
+ openRouterModelProviders &&
|
|
|
+ Object.keys(openRouterModelProviders).length > 0 && (
|
|
|
+ <div>
|
|
|
<div className="flex items-center gap-1">
|
|
|
- <label htmlFor="provider-routing" className="font-medium">
|
|
|
+ <label className="block font-medium mb-1">
|
|
|
{t("settings:providers.openRouter.providerRouting.title")}
|
|
|
</label>
|
|
|
<a href={`https://openrouter.ai/${selectedModelId}/providers`}>
|
|
|
<ExternalLinkIcon className="w-4 h-4" />
|
|
|
</a>
|
|
|
</div>
|
|
|
- <Dropdown
|
|
|
- id="provider-routing"
|
|
|
- value={apiConfiguration?.openRouterSpecificProvider || ""}
|
|
|
- onChange={(event) => {
|
|
|
- const provider = typeof event == "string" ? event : event?.value
|
|
|
- const providerModelInfo = provider ? openRouterModelProviders[provider] : undefined
|
|
|
-
|
|
|
- if (providerModelInfo) {
|
|
|
+ <Select
|
|
|
+ value={apiConfiguration?.openRouterSpecificProvider || OPENROUTER_DEFAULT_PROVIDER_NAME}
|
|
|
+ onValueChange={(value) => {
|
|
|
+ if (openRouterModelProviders[value]) {
|
|
|
setApiConfigurationField("openRouterModelInfo", {
|
|
|
...apiConfiguration.openRouterModelInfo,
|
|
|
- ...providerModelInfo,
|
|
|
+ ...openRouterModelProviders[value],
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- setApiConfigurationField("openRouterSpecificProvider", provider)
|
|
|
- }}
|
|
|
- options={[
|
|
|
- { value: OPENROUTER_DEFAULT_PROVIDER_NAME, label: OPENROUTER_DEFAULT_PROVIDER_NAME },
|
|
|
- ...Object.entries(openRouterModelProviders).map(([value, { label }]) => ({
|
|
|
- value,
|
|
|
- label,
|
|
|
- })),
|
|
|
- ]}
|
|
|
- className="w-full"
|
|
|
- />
|
|
|
- </div>
|
|
|
- <div className="text-sm text-vscode-descriptionForeground">
|
|
|
- {t("settings:providers.openRouter.providerRouting.description")}{" "}
|
|
|
- <a href="https://openrouter.ai/docs/features/provider-routing">
|
|
|
- {t("settings:providers.openRouter.providerRouting.learnMore")}.
|
|
|
- </a>
|
|
|
+ setApiConfigurationField("openRouterSpecificProvider", value)
|
|
|
+ }}>
|
|
|
+ <SelectTrigger className="w-full">
|
|
|
+ <SelectValue placeholder="Select" />
|
|
|
+ </SelectTrigger>
|
|
|
+ <SelectContent>
|
|
|
+ <SelectItem value={OPENROUTER_DEFAULT_PROVIDER_NAME}>
|
|
|
+ {OPENROUTER_DEFAULT_PROVIDER_NAME}
|
|
|
+ </SelectItem>
|
|
|
+ {Object.entries(openRouterModelProviders).map(([value, { label }]) => (
|
|
|
+ <SelectItem key={value} value={value}>
|
|
|
+ {label}
|
|
|
+ </SelectItem>
|
|
|
+ ))}
|
|
|
+ </SelectContent>
|
|
|
+ </Select>
|
|
|
+ <div className="text-sm text-vscode-descriptionForeground">
|
|
|
+ {t("settings:providers.openRouter.providerRouting.description")}{" "}
|
|
|
+ <a href="https://openrouter.ai/docs/features/provider-routing">
|
|
|
+ {t("settings:providers.openRouter.providerRouting.learnMore")}.
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </>
|
|
|
- )}
|
|
|
+ )}
|
|
|
|
|
|
{selectedProvider === "glama" && (
|
|
|
<ModelPicker
|
|
|
@@ -1469,30 +1419,33 @@ const ApiOptions = ({
|
|
|
|
|
|
{selectedProviderModelOptions.length > 0 && (
|
|
|
<>
|
|
|
- <div className="dropdown-container">
|
|
|
- <label htmlFor="model-id" className="font-medium">
|
|
|
- {t("settings:providers.model")}
|
|
|
- </label>
|
|
|
- <Dropdown
|
|
|
- id="model-id"
|
|
|
+ <div>
|
|
|
+ <label className="block font-medium mb-1">{t("settings:providers.model")}</label>
|
|
|
+
|
|
|
+ <Select
|
|
|
value={selectedModelId === "custom-arn" ? "custom-arn" : selectedModelId}
|
|
|
- onChange={(value) => {
|
|
|
- const modelValue = typeof value == "string" ? value : value?.value
|
|
|
- setApiConfigurationField("apiModelId", modelValue)
|
|
|
+ onValueChange={(value) => {
|
|
|
+ setApiConfigurationField("apiModelId", value)
|
|
|
|
|
|
- // Clear custom ARN if not using custom ARN option
|
|
|
- if (modelValue !== "custom-arn" && selectedProvider === "bedrock") {
|
|
|
+ // Clear custom ARN if not using custom ARN option.
|
|
|
+ if (value !== "custom-arn" && selectedProvider === "bedrock") {
|
|
|
setApiConfigurationField("awsCustomArn", "")
|
|
|
}
|
|
|
- }}
|
|
|
- options={[
|
|
|
- ...selectedProviderModelOptions,
|
|
|
- ...(selectedProvider === "bedrock"
|
|
|
- ? [{ value: "custom-arn", label: "Use custom ARN..." }]
|
|
|
- : []),
|
|
|
- ]}
|
|
|
- className="w-full"
|
|
|
- />
|
|
|
+ }}>
|
|
|
+ <SelectTrigger className="w-full">
|
|
|
+ <SelectValue placeholder="Select" />
|
|
|
+ </SelectTrigger>
|
|
|
+ <SelectContent>
|
|
|
+ {selectedProviderModelOptions.map((option) => (
|
|
|
+ <SelectItem key={option.value} value={option.value}>
|
|
|
+ {option.label}
|
|
|
+ </SelectItem>
|
|
|
+ ))}
|
|
|
+ {selectedProvider === "bedrock" && (
|
|
|
+ <SelectItem value="custom-arn">Use custom ARN...</SelectItem>
|
|
|
+ )}
|
|
|
+ </SelectContent>
|
|
|
+ </Select>
|
|
|
</div>
|
|
|
|
|
|
{selectedProvider === "bedrock" && selectedModelId === "custom-arn" && (
|