Move remaining provider settings into separate components (#3208)

This commit is contained in:
Chris Estreich 2025-05-05 23:09:24 -07:00 committed by GitHub
parent ce8fbbdafa
commit a649a53ec6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 920 additions and 561 deletions

View file

@ -355,7 +355,6 @@ export const providerSettingsSchema = z.object({
awsRegion: z.string().optional(),
awsUseCrossRegionInference: z.boolean().optional(),
awsUsePromptCache: z.boolean().optional(),
awspromptCacheId: z.string().optional(),
awsProfile: z.string().optional(),
awsUseProfile: z.boolean().optional(),
awsCustomArn: z.string().optional(),
@ -455,7 +454,6 @@ const providerSettingsRecord: ProviderSettingsRecord = {
awsRegion: undefined,
awsUseCrossRegionInference: undefined,
awsUsePromptCache: undefined,
awspromptCacheId: undefined,
awsProfile: undefined,
awsUseProfile: undefined,
awsCustomArn: undefined,

View file

@ -13,8 +13,8 @@ import { GlobalState, ProviderSettings, RooCodeSettings } from "../../schemas"
import { t } from "../../i18n"
import { setPanel } from "../../activate/registerCommands"
import {
ProviderName,
ApiConfiguration,
ApiProvider,
requestyDefaultModelId,
openRouterDefaultModelId,
glamaDefaultModelId,
@ -1297,7 +1297,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
const customModes = await this.customModesManager.getCustomModes()
// Determine apiProvider with the same logic as before.
const apiProvider: ApiProvider = stateValues.apiProvider ? stateValues.apiProvider : "anthropic"
const apiProvider: ProviderName = stateValues.apiProvider ? stateValues.apiProvider : "anthropic"
// Build the apiConfiguration object combining state values and secrets.
const providerSettings = this.contextProxy.getProviderSettings()

View file

@ -42,7 +42,6 @@ type ProviderSettings = {
awsRegion?: string | undefined
awsUseCrossRegionInference?: boolean | undefined
awsUsePromptCache?: boolean | undefined
awspromptCacheId?: string | undefined
awsProfile?: string | undefined
awsUseProfile?: boolean | undefined
awsCustomArn?: string | undefined

View file

@ -43,7 +43,6 @@ type ProviderSettings = {
awsRegion?: string | undefined
awsUseCrossRegionInference?: boolean | undefined
awsUsePromptCache?: boolean | undefined
awspromptCacheId?: string | undefined
awsProfile?: string | undefined
awsUseProfile?: boolean | undefined
awsCustomArn?: string | undefined

View file

@ -366,7 +366,6 @@ export const providerSettingsSchema = z.object({
awsRegion: z.string().optional(),
awsUseCrossRegionInference: z.boolean().optional(),
awsUsePromptCache: z.boolean().optional(),
awspromptCacheId: z.string().optional(),
awsProfile: z.string().optional(),
awsUseProfile: z.boolean().optional(),
awsCustomArn: z.string().optional(),
@ -471,7 +470,6 @@ const providerSettingsRecord: ProviderSettingsRecord = {
awsRegion: undefined,
awsUseCrossRegionInference: undefined,
awsUsePromptCache: undefined,
awspromptCacheId: undefined,
awsProfile: undefined,
awsUseProfile: undefined,
awsCustomArn: undefined,

View file

@ -1,6 +1,6 @@
import { ModelInfo, ProviderName, ProviderSettings } from "../schemas"
export type { ModelInfo, ProviderName as ApiProvider }
export type { ModelInfo, ProviderName }
export type ApiHandlerOptions = Omit<ProviderSettings, "apiProvider" | "id">

View file

@ -1,55 +1,55 @@
import React, { memo, useCallback, useEffect, useMemo, useState } from "react"
import { useDebounce } from "react-use"
import { VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ExternalLinkIcon } from "@radix-ui/react-icons"
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import {
ApiConfiguration,
glamaDefaultModelId,
mistralDefaultModelId,
type ProviderName,
type ApiConfiguration,
openRouterDefaultModelId,
unboundDefaultModelId,
requestyDefaultModelId,
ApiProvider,
glamaDefaultModelId,
unboundDefaultModelId,
} from "@roo/shared/api"
import { vscode } from "@src/utils/vscode"
import { validateApiConfiguration, validateModelId, validateBedrockArn } from "@src/utils/validate"
import { validateApiConfiguration, validateModelId } from "@src/utils/validate"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { useRouterModels } from "@src/components/ui/hooks/useRouterModels"
import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"
import {
useOpenRouterModelProviders,
OPENROUTER_DEFAULT_PROVIDER_NAME,
} from "@src/components/ui/hooks/useOpenRouterModelProviders"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Button } from "@src/components/ui"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { getRequestyApiKeyUrl, getGlamaAuthUrl } from "@src/oauth/urls"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui"
// Providers
import { Anthropic } from "./providers/Anthropic"
import { Bedrock } from "./providers/Bedrock"
import { Gemini } from "./providers/Gemini"
import { LMStudio } from "./providers/LMStudio"
import { Ollama } from "./providers/Ollama"
import { OpenAI } from "./providers/OpenAI"
import { OpenAICompatible } from "./providers/OpenAICompatible"
import { OpenRouter } from "./providers/OpenRouter"
import { Vertex } from "./providers/Vertex"
import { VSCodeLM } from "./providers/VSCodeLM"
import {
Anthropic,
Bedrock,
Chutes,
DeepSeek,
Gemini,
Glama,
Groq,
LMStudio,
Mistral,
Ollama,
OpenAI,
OpenAICompatible,
OpenRouter,
Requesty,
Unbound,
Vertex,
VSCodeLM,
XAI,
} from "./providers"
import { MODELS_BY_PROVIDER, PROVIDERS, REASONING_MODELS } from "./constants"
import { inputEventTransform, noTransform } from "./transforms"
import { ModelInfoView } from "./ModelInfoView"
import { ModelPicker } from "./ModelPicker"
import { ApiErrorMessage } from "./ApiErrorMessage"
import { ThinkingBudget } from "./ThinkingBudget"
import { RequestyBalanceDisplay } from "./RequestyBalanceDisplay"
import { ReasoningEffort } from "./ReasoningEffort"
import { PromptCachingControl } from "./PromptCachingControl"
import { DiffSettingsControl } from "./DiffSettingsControl"
import { TemperatureControl } from "./TemperatureControl"
import { RateLimitSecondsControl } from "./RateLimitSecondsControl"
import { BedrockCustomArn } from "./providers/BedrockCustomArn"
export interface ApiOptionsProps {
uriScheme: string | undefined
@ -75,8 +75,6 @@ const ApiOptions = ({
return Object.entries(headers)
})
const [requestyShowRefreshHint, setRequestyShowRefreshHint] = useState<boolean>()
useEffect(() => {
const propHeaders = apiConfiguration?.openAiHeaders || {}
@ -106,13 +104,14 @@ const ApiOptions = ({
return result
}
// Debounced effect to update the main configuration when local customHeaders state stabilizes.
// Debounced effect to update the main configuration when local
// customHeaders state stabilizes.
useDebounce(
() => {
const currentConfigHeaders = apiConfiguration?.openAiHeaders || {}
const newHeadersObject = convertHeadersToObject(customHeaders)
// Only update if the processed object is different from the current config
// Only update if the processed object is different from the current config.
if (JSON.stringify(currentConfigHeaders) !== JSON.stringify(newHeadersObject)) {
setApiConfigurationField("openAiHeaders", newHeadersObject)
}
@ -142,7 +141,7 @@ const ApiOptions = ({
const { data: routerModels, refetch: refetchRouterModels } = useRouterModels()
// Update apiConfiguration.aiModelId whenever selectedModelId changes.
// Update `apiModelId` whenever `selectedModelId` changes.
useEffect(() => {
if (selectedModelId) {
setApiConfigurationField("apiModelId", selectedModelId)
@ -193,16 +192,7 @@ const ApiOptions = ({
setErrorMessage(apiValidationResult)
}, [apiConfiguration, routerModels, setErrorMessage])
const { data: openRouterModelProviders } = useOpenRouterModelProviders(apiConfiguration?.openRouterModelId, {
enabled:
selectedProvider === "openrouter" &&
!!apiConfiguration?.openRouterModelId &&
routerModels?.openrouter &&
Object.keys(routerModels.openrouter).length > 1 &&
apiConfiguration.openRouterModelId in routerModels.openrouter,
})
const selectedProviderModelOptions = useMemo(
const selectedProviderModels = useMemo(
() =>
MODELS_BY_PROVIDER[selectedProvider]
? Object.keys(MODELS_BY_PROVIDER[selectedProvider]).map((modelId) => ({
@ -213,37 +203,8 @@ const ApiOptions = ({
[selectedProvider],
)
// Custom URL path mappings for providers with different slugs.
const providerUrlSlugs: Record<string, string> = {
"openai-native": "openai",
openai: "openai-compatible",
}
// Helper function to get provider display name from PROVIDERS constant.
const getProviderDisplayName = (providerKey: string): string | undefined => {
const provider = PROVIDERS.find((p) => p.value === providerKey)
return provider?.label
}
// Helper function to get the documentation URL and name for the currently selected provider
const getSelectedProviderDocUrl = (): { url: string; name: string } | undefined => {
const displayName = getProviderDisplayName(selectedProvider)
if (!displayName) {
return undefined
}
// Get the URL slug - use custom mapping if available, otherwise use the provider key
const urlSlug = providerUrlSlugs[selectedProvider] || selectedProvider
return {
url: `https://docs.roocode.com/providers/${urlSlug}`,
name: displayName,
}
}
const onApiProviderChange = useCallback(
(value: ApiProvider) => {
const onProviderChange = useCallback(
(value: ProviderName) => {
// It would be much easier to have a single attribute that stores
// the modelId, but we have a separate attribute for each of
// OpenRouter, Glama, Unbound, and Requesty.
@ -285,25 +246,40 @@ const ApiOptions = ({
],
)
const docs = useMemo(() => {
const provider = PROVIDERS.find(({ value }) => value === selectedProvider)
const name = provider?.label
if (!name) {
return undefined
}
// Get the URL slug - use custom mapping if available, otherwise use the provider key.
const slugs: Record<string, string> = {
"openai-native": "openai",
openai: "openai-compatible",
}
return {
url: `https://docs.roocode.com/providers/${slugs[selectedProvider] || selectedProvider}`,
name,
}
}, [selectedProvider])
return (
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-1 relative">
<div className="flex justify-between items-center">
<label className="block font-medium mb-1">{t("settings:providers.apiProvider")}</label>
{getSelectedProviderDocUrl() && (
{docs && (
<div className="text-xs text-vscode-descriptionForeground">
<VSCodeLink
href={getSelectedProviderDocUrl()!.url}
className="hover:text-vscode-foreground"
target="_blank">
{t("settings:providers.providerDocumentation", {
provider: getSelectedProviderDocUrl()!.name,
})}
<VSCodeLink href={docs.url} className="hover:text-vscode-foreground" target="_blank">
{t("settings:providers.providerDocumentation", { provider: docs.name })}
</VSCodeLink>
</div>
)}
</div>
<Select value={selectedProvider} onValueChange={(value) => onApiProviderChange(value as ApiProvider)}>
<Select value={selectedProvider} onValueChange={(value) => onProviderChange(value as ProviderName)}>
<SelectTrigger className="w-full">
<SelectValue placeholder={t("settings:common.select")} />
</SelectTrigger>
@ -323,124 +299,49 @@ const ApiOptions = ({
<OpenRouter
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
routerModels={routerModels}
selectedModelId={selectedModelId}
uriScheme={uriScheme}
fromWelcomeView={fromWelcomeView}
/>
)}
{selectedProvider === "requesty" && (
<Requesty
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
routerModels={routerModels}
refetchRouterModels={refetchRouterModels}
/>
)}
{selectedProvider === "glama" && (
<Glama
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
routerModels={routerModels}
uriScheme={uriScheme}
/>
)}
{selectedProvider === "unbound" && (
<Unbound
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
routerModels={routerModels}
/>
)}
{selectedProvider === "anthropic" && (
<Anthropic apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}
{selectedProvider === "glama" && (
<>
<VSCodeTextField
value={apiConfiguration?.glamaApiKey || ""}
type="password"
onInput={handleInputChange("glamaApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.glamaApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.glamaApiKey && (
<VSCodeButtonLink
href={getGlamaAuthUrl(uriScheme)}
style={{ width: "100%" }}
appearance="primary">
{t("settings:providers.getGlamaApiKey")}
</VSCodeButtonLink>
)}
</>
)}
{selectedProvider === "requesty" && (
<>
<VSCodeTextField
value={apiConfiguration?.requestyApiKey || ""}
type="password"
onInput={handleInputChange("requestyApiKey")}
placeholder={t("settings:providers.getRequestyApiKey")}
className="w-full">
<div className="flex justify-between items-center mb-1">
<label className="block font-medium">{t("settings:providers.requestyApiKey")}</label>
{apiConfiguration?.requestyApiKey && (
<RequestyBalanceDisplay apiKey={apiConfiguration.requestyApiKey} />
)}
</div>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.requestyApiKey && (
<VSCodeButtonLink href={getRequestyApiKeyUrl()} style={{ width: "100%" }} appearance="primary">
{t("settings:providers.getRequestyApiKey")}
</VSCodeButtonLink>
)}
<Button
variant="outline"
title={t("settings:providers.refetchModels")}
onClick={() => {
vscode.postMessage({ type: "flushRouterModels", text: "requesty" })
refetchRouterModels()
setRequestyShowRefreshHint(true)
}}>
<div className="flex items-center gap-2">
<span className="codicon codicon-refresh" />
{t("settings:providers.flushModelsCache")}
</div>
</Button>
{requestyShowRefreshHint && (
<div className="flex items-center text-vscode-errorForeground">
{t("settings:providers.flushedModelsCache")}
</div>
)}
</>
)}
{selectedProvider === "openai-native" && (
<OpenAI apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}
{selectedProvider === "mistral" && (
<>
<VSCodeTextField
value={apiConfiguration?.mistralApiKey || ""}
type="password"
onInput={handleInputChange("mistralApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<span className="font-medium">{t("settings:providers.mistralApiKey")}</span>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.mistralApiKey && (
<VSCodeButtonLink href="https://console.mistral.ai/" appearance="secondary">
{t("settings:providers.getMistralApiKey")}
</VSCodeButtonLink>
)}
{(apiConfiguration?.apiModelId?.startsWith("codestral-") ||
(!apiConfiguration?.apiModelId && mistralDefaultModelId.startsWith("codestral-"))) && (
<>
<VSCodeTextField
value={apiConfiguration?.mistralCodestralUrl || ""}
type="url"
onInput={handleInputChange("mistralCodestralUrl")}
placeholder="https://codestral.mistral.ai"
className="w-full">
<label className="block font-medium mb-1">
{t("settings:providers.codestralBaseUrl")}
</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.codestralBaseUrlDesc")}
</div>
</>
)}
</>
<Mistral apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}
{selectedProvider === "bedrock" && (
@ -471,24 +372,7 @@ const ApiOptions = ({
)}
{selectedProvider === "deepseek" && (
<>
<VSCodeTextField
value={apiConfiguration?.deepSeekApiKey || ""}
type="password"
onInput={handleInputChange("deepSeekApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.deepSeekApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.deepSeekApiKey && (
<VSCodeButtonLink href="https://platform.deepseek.com/" appearance="secondary">
{t("settings:providers.getDeepSeekApiKey")}
</VSCodeButtonLink>
)}
</>
<DeepSeek apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}
{selectedProvider === "vscode-lm" && (
@ -500,87 +384,15 @@ const ApiOptions = ({
)}
{selectedProvider === "xai" && (
<>
<VSCodeTextField
value={apiConfiguration?.xaiApiKey || ""}
type="password"
onInput={handleInputChange("xaiApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.xaiApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.xaiApiKey && (
<VSCodeButtonLink href="https://api.x.ai/docs" appearance="secondary">
{t("settings:providers.getXaiApiKey")}
</VSCodeButtonLink>
)}
</>
<XAI apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}
{selectedProvider === "groq" && (
<>
<VSCodeTextField
value={apiConfiguration?.groqApiKey || ""}
type="password"
onInput={handleInputChange("groqApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.groqApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.groqApiKey && (
<VSCodeButtonLink href="https://console.groq.com/keys" appearance="secondary">
{t("settings:providers.getGroqApiKey")}
</VSCodeButtonLink>
)}
</>
<Groq apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}
{selectedProvider === "chutes" && (
<>
<VSCodeTextField
value={apiConfiguration?.chutesApiKey || ""}
type="password"
onInput={handleInputChange("chutesApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.chutesApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.chutesApiKey && (
<VSCodeButtonLink href="https://chutes.ai/app/api" appearance="secondary">
{t("settings:providers.getChutesApiKey")}
</VSCodeButtonLink>
)}
</>
)}
{selectedProvider === "unbound" && (
<>
<VSCodeTextField
value={apiConfiguration?.unboundApiKey || ""}
type="password"
onInput={handleInputChange("unboundApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.unboundApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.unboundApiKey && (
<VSCodeButtonLink href="https://gateway.getunbound.ai" appearance="secondary">
{t("settings:providers.getUnboundApiKey")}
</VSCodeButtonLink>
)}
</>
<Chutes apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}
{selectedProvider === "human-relay" && (
@ -594,99 +406,10 @@ const ApiOptions = ({
</>
)}
{/* Model Pickers */}
{selectedProvider === "openrouter" && (
<ModelPicker
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
defaultModelId={openRouterDefaultModelId}
models={routerModels?.openrouter ?? {}}
modelIdKey="openRouterModelId"
serviceName="OpenRouter"
serviceUrl="https://openrouter.ai/models"
/>
)}
{selectedProvider === "openrouter" &&
openRouterModelProviders &&
Object.keys(openRouterModelProviders).length > 0 && (
<div>
<div className="flex items-center gap-1">
<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>
<Select
value={apiConfiguration?.openRouterSpecificProvider || OPENROUTER_DEFAULT_PROVIDER_NAME}
onValueChange={(value) => setApiConfigurationField("openRouterSpecificProvider", value)}>
<SelectTrigger className="w-full">
<SelectValue placeholder={t("settings:common.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 mt-1">
{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
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
defaultModelId={glamaDefaultModelId}
models={routerModels?.glama ?? {}}
modelIdKey="glamaModelId"
serviceName="Glama"
serviceUrl="https://glama.ai/models"
/>
)}
{selectedProvider === "unbound" && (
<ModelPicker
apiConfiguration={apiConfiguration}
defaultModelId={unboundDefaultModelId}
models={routerModels?.unbound ?? {}}
modelIdKey="unboundModelId"
serviceName="Unbound"
serviceUrl="https://api.getunbound.ai/models"
setApiConfigurationField={setApiConfigurationField}
/>
)}
{selectedProvider === "requesty" && (
<ModelPicker
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
defaultModelId={requestyDefaultModelId}
models={routerModels?.requesty ?? {}}
modelIdKey="requestyModelId"
serviceName="Requesty"
serviceUrl="https://requesty.ai"
/>
)}
{selectedProviderModelOptions.length > 0 && (
{selectedProviderModels.length > 0 && (
<>
<div>
<label className="block font-medium mb-1">{t("settings:providers.model")}</label>
<Select
value={selectedModelId === "custom-arn" ? "custom-arn" : selectedModelId}
onValueChange={(value) => {
@ -701,7 +424,7 @@ const ApiOptions = ({
<SelectValue placeholder={t("settings:common.select")} />
</SelectTrigger>
<SelectContent>
{selectedProviderModelOptions.map((option) => (
{selectedProviderModels.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
@ -714,58 +437,10 @@ const ApiOptions = ({
</div>
{selectedProvider === "bedrock" && selectedModelId === "custom-arn" && (
<>
<VSCodeTextField
value={apiConfiguration?.awsCustomArn || ""}
onInput={(e) => {
const value = (e.target as HTMLInputElement).value
setApiConfigurationField("awsCustomArn", value)
}}
placeholder={t("settings:placeholders.customArn")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:labels.customArn")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.awsCustomArnUse")}
<ul className="list-disc pl-5 mt-1">
<li>
arn:aws:bedrock:eu-west-1:123456789012:inference-profile/eu.anthropic.claude-3-7-sonnet-20250219-v1:0
</li>
<li>
arn:aws:bedrock:us-west-2:123456789012:provisioned-model/my-provisioned-model
</li>
<li>
arn:aws:bedrock:us-east-1:123456789012:default-prompt-router/anthropic.claude:1
</li>
</ul>
{t("settings:providers.awsCustomArnDesc")}
</div>
{apiConfiguration?.awsCustomArn &&
(() => {
const validation = validateBedrockArn(
apiConfiguration.awsCustomArn,
apiConfiguration.awsRegion,
)
if (!validation.isValid) {
return (
<div className="text-sm text-vscode-errorForeground mt-2">
{validation.errorMessage || t("settings:providers.invalidArnFormat")}
</div>
)
}
if (validation.errorMessage) {
return (
<div className="text-sm text-vscode-errorForeground mt-2">
{validation.errorMessage}
</div>
)
}
return null
})()}
</>
<BedrockCustomArn
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
/>
)}
<ModelInfoView

View file

@ -1,5 +1,5 @@
import {
ApiProvider,
ProviderName,
ModelInfo,
anthropicModels,
bedrockModels,
@ -9,15 +9,15 @@ import {
openAiNativeModels,
vertexModels,
xaiModels,
groqModels,
chutesModels,
groqModels,
chutesModels,
} from "@roo/shared/api"
export { REASONING_MODELS, PROMPT_CACHING_MODELS } from "@roo/shared/api"
export { AWS_REGIONS } from "@roo/shared/aws_regions"
export const MODELS_BY_PROVIDER: Partial<Record<ApiProvider, Record<string, ModelInfo>>> = {
export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, ModelInfo>>> = {
anthropic: anthropicModels,
bedrock: bedrockModels,
deepseek: deepSeekModels,
@ -26,8 +26,8 @@ export const MODELS_BY_PROVIDER: Partial<Record<ApiProvider, Record<string, Mode
"openai-native": openAiNativeModels,
vertex: vertexModels,
xai: xaiModels,
groq: groqModels,
chutes: chutesModels,
groq: groqModels,
chutes: chutesModels,
}
export const PROVIDERS = [
@ -47,9 +47,9 @@ export const PROVIDERS = [
{ value: "unbound", label: "Unbound" },
{ value: "requesty", label: "Requesty" },
{ value: "human-relay", label: "Human Relay" },
{ value: "xai", label: "xAI" },
{ value: "groq", label: "Groq" },
{ value: "chutes", label: "Chutes AI" },
{ value: "xai", label: "xAI (Grok)" },
{ value: "groq", label: "Groq" },
{ value: "chutes", label: "Chutes AI" },
].sort((a, b) => a.label.localeCompare(b.label))
export const VERTEX_REGIONS = [

View file

@ -0,0 +1,53 @@
import { useMemo } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ApiConfiguration } from "@roo/shared/api"
import { validateBedrockArn } from "@src/utils/validate"
import { useAppTranslation } from "@src/i18n/TranslationContext"
type BedrockCustomArnProps = {
apiConfiguration: ApiConfiguration
setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
}
export const BedrockCustomArn = ({ apiConfiguration, setApiConfigurationField }: BedrockCustomArnProps) => {
const { t } = useAppTranslation()
const validation = useMemo(() => {
const { awsCustomArn, awsRegion } = apiConfiguration
return awsCustomArn ? validateBedrockArn(awsCustomArn, awsRegion) : { isValid: true, errorMessage: undefined }
}, [apiConfiguration])
return (
<>
<VSCodeTextField
value={apiConfiguration?.awsCustomArn || ""}
onInput={(e) => setApiConfigurationField("awsCustomArn", (e.target as HTMLInputElement).value)}
placeholder={t("settings:placeholders.customArn")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:labels.customArn")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.awsCustomArnUse")}
<ul className="list-disc pl-5 mt-1">
<li>
arn:aws:bedrock:eu-west-1:123456789012:inference-profile/eu.anthropic.claude-3-7-sonnet-20250219-v1:0
</li>
<li>arn:aws:bedrock:us-west-2:123456789012:provisioned-model/my-provisioned-model</li>
<li>arn:aws:bedrock:us-east-1:123456789012:default-prompt-router/anthropic.claude:1</li>
</ul>
{t("settings:providers.awsCustomArnDesc")}
</div>
{!validation.isValid ? (
<div className="text-sm text-vscode-errorForeground mt-2">
{validation.errorMessage || t("settings:providers.invalidArnFormat")}
</div>
) : (
validation.errorMessage && (
<div className="text-sm text-vscode-errorForeground mt-2">{validation.errorMessage}</div>
)
)}
</>
)
}

View file

@ -0,0 +1,50 @@
import { useCallback } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ApiConfiguration } from "@roo/shared/api"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { inputEventTransform } from "../transforms"
type ChutesProps = {
apiConfiguration: ApiConfiguration
setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
}
export const Chutes = ({ apiConfiguration, setApiConfigurationField }: ChutesProps) => {
const { t } = useAppTranslation()
const handleInputChange = useCallback(
<K extends keyof ApiConfiguration, E>(
field: K,
transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)
return (
<>
<VSCodeTextField
value={apiConfiguration?.chutesApiKey || ""}
type="password"
onInput={handleInputChange("chutesApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.chutesApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.chutesApiKey && (
<VSCodeButtonLink href="https://chutes.ai/app/api" appearance="secondary">
{t("settings:providers.getChutesApiKey")}
</VSCodeButtonLink>
)}
</>
)
}

View file

@ -0,0 +1,50 @@
import { useCallback } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ApiConfiguration } from "@roo/shared/api"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { inputEventTransform } from "../transforms"
type DeepSeekProps = {
apiConfiguration: ApiConfiguration
setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
}
export const DeepSeek = ({ apiConfiguration, setApiConfigurationField }: DeepSeekProps) => {
const { t } = useAppTranslation()
const handleInputChange = useCallback(
<K extends keyof ApiConfiguration, E>(
field: K,
transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)
return (
<>
<VSCodeTextField
value={apiConfiguration?.deepSeekApiKey || ""}
type="password"
onInput={handleInputChange("deepSeekApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.deepSeekApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.deepSeekApiKey && (
<VSCodeButtonLink href="https://platform.deepseek.com/" appearance="secondary">
{t("settings:providers.getDeepSeekApiKey")}
</VSCodeButtonLink>
)}
</>
)
}

View file

@ -0,0 +1,63 @@
import { useCallback } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ApiConfiguration, RouterModels, glamaDefaultModelId } from "@roo/shared/api"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { getGlamaAuthUrl } from "@src/oauth/urls"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { inputEventTransform } from "../transforms"
import { ModelPicker } from "../ModelPicker"
type GlamaProps = {
apiConfiguration: ApiConfiguration
setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
routerModels?: RouterModels
uriScheme?: string
}
export const Glama = ({ apiConfiguration, setApiConfigurationField, routerModels, uriScheme }: GlamaProps) => {
const { t } = useAppTranslation()
const handleInputChange = useCallback(
<K extends keyof ApiConfiguration, E>(
field: K,
transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)
return (
<>
<VSCodeTextField
value={apiConfiguration?.glamaApiKey || ""}
type="password"
onInput={handleInputChange("glamaApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.glamaApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.glamaApiKey && (
<VSCodeButtonLink href={getGlamaAuthUrl(uriScheme)} style={{ width: "100%" }} appearance="primary">
{t("settings:providers.getGlamaApiKey")}
</VSCodeButtonLink>
)}
<ModelPicker
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
defaultModelId={glamaDefaultModelId}
models={routerModels?.glama ?? {}}
modelIdKey="glamaModelId"
serviceName="Glama"
serviceUrl="https://glama.ai/models"
/>
</>
)
}

View file

@ -0,0 +1,50 @@
import { useCallback } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ApiConfiguration } from "@roo/shared/api"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { inputEventTransform } from "../transforms"
type GroqProps = {
apiConfiguration: ApiConfiguration
setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
}
export const Groq = ({ apiConfiguration, setApiConfigurationField }: GroqProps) => {
const { t } = useAppTranslation()
const handleInputChange = useCallback(
<K extends keyof ApiConfiguration, E>(
field: K,
transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)
return (
<>
<VSCodeTextField
value={apiConfiguration?.groqApiKey || ""}
type="password"
onInput={handleInputChange("groqApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.groqApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.groqApiKey && (
<VSCodeButtonLink href="https://console.groq.com/keys" appearance="secondary">
{t("settings:providers.getGroqApiKey")}
</VSCodeButtonLink>
)}
</>
)
}

View file

@ -0,0 +1,67 @@
import { useCallback } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ApiConfiguration, RouterModels, mistralDefaultModelId } from "@roo/shared/api"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { inputEventTransform } from "../transforms"
type MistralProps = {
apiConfiguration: ApiConfiguration
setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
routerModels?: RouterModels
}
export const Mistral = ({ apiConfiguration, setApiConfigurationField }: MistralProps) => {
const { t } = useAppTranslation()
const handleInputChange = useCallback(
<K extends keyof ApiConfiguration, E>(
field: K,
transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)
return (
<>
<VSCodeTextField
value={apiConfiguration?.mistralApiKey || ""}
type="password"
onInput={handleInputChange("mistralApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<span className="font-medium">{t("settings:providers.mistralApiKey")}</span>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.mistralApiKey && (
<VSCodeButtonLink href="https://console.mistral.ai/" appearance="secondary">
{t("settings:providers.getMistralApiKey")}
</VSCodeButtonLink>
)}
{(apiConfiguration?.apiModelId?.startsWith("codestral-") ||
(!apiConfiguration?.apiModelId && mistralDefaultModelId.startsWith("codestral-"))) && (
<>
<VSCodeTextField
value={apiConfiguration?.mistralCodestralUrl || ""}
type="url"
onInput={handleInputChange("mistralCodestralUrl")}
placeholder="https://codestral.mistral.ai"
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.codestralBaseUrl")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.codestralBaseUrlDesc")}
</div>
</>
)}
</>
)
}

View file

@ -2,20 +2,29 @@ import { useCallback, useState } from "react"
import { Trans } from "react-i18next"
import { Checkbox } from "vscrui"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ExternalLinkIcon } from "@radix-ui/react-icons"
import { ApiConfiguration } from "@roo/shared/api"
import { ApiConfiguration, RouterModels, openRouterDefaultModelId } from "@roo/shared/api"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { getOpenRouterAuthUrl } from "@src/oauth/urls"
import {
useOpenRouterModelProviders,
OPENROUTER_DEFAULT_PROVIDER_NAME,
} from "@src/components/ui/hooks/useOpenRouterModelProviders"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui"
import { inputEventTransform, noTransform } from "../transforms"
import { ModelPicker } from "../ModelPicker"
import { OpenRouterBalanceDisplay } from "./OpenRouterBalanceDisplay"
type OpenRouterProps = {
apiConfiguration: ApiConfiguration
setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
routerModels?: RouterModels
selectedModelId: string
uriScheme: string | undefined
fromWelcomeView?: boolean
}
@ -23,6 +32,8 @@ type OpenRouterProps = {
export const OpenRouter = ({
apiConfiguration,
setApiConfigurationField,
routerModels,
selectedModelId,
uriScheme,
fromWelcomeView,
}: OpenRouterProps) => {
@ -41,6 +52,14 @@ export const OpenRouter = ({
[setApiConfigurationField],
)
const { data: openRouterModelProviders } = useOpenRouterModelProviders(apiConfiguration?.openRouterModelId, {
enabled:
!!apiConfiguration?.openRouterModelId &&
routerModels?.openrouter &&
Object.keys(routerModels.openrouter).length > 1 &&
apiConfiguration.openRouterModelId in routerModels.openrouter,
})
return (
<>
<VSCodeTextField
@ -104,6 +123,50 @@ export const OpenRouter = ({
</Checkbox>
</>
)}
<ModelPicker
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
defaultModelId={openRouterDefaultModelId}
models={routerModels?.openrouter ?? {}}
modelIdKey="openRouterModelId"
serviceName="OpenRouter"
serviceUrl="https://openrouter.ai/models"
/>
{openRouterModelProviders && Object.keys(openRouterModelProviders).length > 0 && (
<div>
<div className="flex items-center gap-1">
<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>
<Select
value={apiConfiguration?.openRouterSpecificProvider || OPENROUTER_DEFAULT_PROVIDER_NAME}
onValueChange={(value) => setApiConfigurationField("openRouterSpecificProvider", value)}>
<SelectTrigger className="w-full">
<SelectValue placeholder={t("settings:common.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 mt-1">
{t("settings:providers.openRouter.providerRouting.description")}{" "}
<a href="https://openrouter.ai/docs/features/provider-routing">
{t("settings:providers.openRouter.providerRouting.learnMore")}.
</a>
</div>
</div>
)}
</>
)
}

View file

@ -0,0 +1,97 @@
import { useCallback, useState } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ApiConfiguration, RouterModels, requestyDefaultModelId } from "@roo/shared/api"
import { vscode } from "@src/utils/vscode"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { Button } from "@src/components/ui"
import { inputEventTransform } from "../transforms"
import { ModelPicker } from "../ModelPicker"
import { RequestyBalanceDisplay } from "./RequestyBalanceDisplay"
type RequestyProps = {
apiConfiguration: ApiConfiguration
setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
routerModels?: RouterModels
refetchRouterModels: () => void
}
export const Requesty = ({
apiConfiguration,
setApiConfigurationField,
routerModels,
refetchRouterModels,
}: RequestyProps) => {
const { t } = useAppTranslation()
const [didRefetch, setDidRefetch] = useState<boolean>()
const handleInputChange = useCallback(
<K extends keyof ApiConfiguration, E>(
field: K,
transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)
return (
<>
<VSCodeTextField
value={apiConfiguration?.requestyApiKey || ""}
type="password"
onInput={handleInputChange("requestyApiKey")}
placeholder={t("settings:providers.getRequestyApiKey")}
className="w-full">
<div className="flex justify-between items-center mb-1">
<label className="block font-medium">{t("settings:providers.requestyApiKey")}</label>
{apiConfiguration?.requestyApiKey && (
<RequestyBalanceDisplay apiKey={apiConfiguration.requestyApiKey} />
)}
</div>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.requestyApiKey && (
<VSCodeButtonLink
href="https://app.requesty.ai/api-keys"
style={{ width: "100%" }}
appearance="primary">
{t("settings:providers.getRequestyApiKey")}
</VSCodeButtonLink>
)}
<Button
variant="outline"
onClick={() => {
vscode.postMessage({ type: "flushRouterModels", text: "requesty" })
refetchRouterModels()
setDidRefetch(true)
}}>
<div className="flex items-center gap-2">
<span className="codicon codicon-refresh" />
{t("settings:providers.refreshModels.label")}
</div>
</Button>
{didRefetch && (
<div className="flex items-center text-vscode-errorForeground">
{t("settings:providers.refreshModels.hint")}
</div>
)}
<ModelPicker
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
defaultModelId={requestyDefaultModelId}
models={routerModels?.requesty ?? {}}
modelIdKey="requestyModelId"
serviceName="Requesty"
serviceUrl="https://requesty.ai"
/>
</>
)
}

View file

@ -0,0 +1,61 @@
import { useCallback } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ApiConfiguration, RouterModels, unboundDefaultModelId } from "@roo/shared/api"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { inputEventTransform } from "../transforms"
import { ModelPicker } from "../ModelPicker"
type UnboundProps = {
apiConfiguration: ApiConfiguration
setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
routerModels?: RouterModels
}
export const Unbound = ({ apiConfiguration, setApiConfigurationField, routerModels }: UnboundProps) => {
const { t } = useAppTranslation()
const handleInputChange = useCallback(
<K extends keyof ApiConfiguration, E>(
field: K,
transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)
return (
<>
<VSCodeTextField
value={apiConfiguration?.unboundApiKey || ""}
type="password"
onInput={handleInputChange("unboundApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.unboundApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.unboundApiKey && (
<VSCodeButtonLink href="https://gateway.getunbound.ai" appearance="secondary">
{t("settings:providers.getUnboundApiKey")}
</VSCodeButtonLink>
)}
<ModelPicker
apiConfiguration={apiConfiguration}
defaultModelId={unboundDefaultModelId}
models={routerModels?.unbound ?? {}}
modelIdKey="unboundModelId"
serviceName="Unbound"
serviceUrl="https://api.getunbound.ai/models"
setApiConfigurationField={setApiConfigurationField}
/>
</>
)
}

View file

@ -0,0 +1,50 @@
import { useCallback } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { ApiConfiguration } from "@roo/shared/api"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
import { inputEventTransform } from "../transforms"
type XAIProps = {
apiConfiguration: ApiConfiguration
setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
}
export const XAI = ({ apiConfiguration, setApiConfigurationField }: XAIProps) => {
const { t } = useAppTranslation()
const handleInputChange = useCallback(
<K extends keyof ApiConfiguration, E>(
field: K,
transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)
return (
<>
<VSCodeTextField
value={apiConfiguration?.xaiApiKey || ""}
type="password"
onInput={handleInputChange("xaiApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.xaiApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.xaiApiKey && (
<VSCodeButtonLink href="https://api.x.ai/docs" appearance="secondary">
{t("settings:providers.getXaiApiKey")}
</VSCodeButtonLink>
)}
</>
)
}

View file

@ -0,0 +1,18 @@
export { Anthropic } from "./Anthropic"
export { Bedrock } from "./Bedrock"
export { Chutes } from "./Chutes"
export { DeepSeek } from "./DeepSeek"
export { Gemini } from "./Gemini"
export { Glama } from "./Glama"
export { Groq } from "./Groq"
export { LMStudio } from "./LMStudio"
export { Mistral } from "./Mistral"
export { Ollama } from "./Ollama"
export { OpenAI } from "./OpenAI"
export { OpenAICompatible } from "./OpenAICompatible"
export { OpenRouter } from "./OpenRouter"
export { Requesty } from "./Requesty"
export { Unbound } from "./Unbound"
export { Vertex } from "./Vertex"
export { VSCodeLM } from "./VSCodeLM"
export { XAI } from "./XAI"

View file

@ -1,7 +1,8 @@
import {
ApiConfiguration,
RouterModels,
ModelInfo,
type ProviderName,
type ApiConfiguration,
type RouterModels,
type ModelInfo,
anthropicDefaultModelId,
anthropicModels,
bedrockDefaultModelId,
@ -36,97 +37,137 @@ import { useRouterModels } from "./useRouterModels"
export const useSelectedModel = (apiConfiguration?: ApiConfiguration) => {
const { data: routerModels, isLoading, isError } = useRouterModels()
const provider = apiConfiguration?.apiProvider || "anthropic"
const id = apiConfiguration ? getSelectedModelId({ provider, apiConfiguration }) : anthropicDefaultModelId
const info = routerModels ? getSelectedModelInfo({ provider, id, apiConfiguration, routerModels }) : undefined
const { id, info } =
apiConfiguration && routerModels
? getSelectedModel({ provider, apiConfiguration, routerModels })
: { id: anthropicDefaultModelId, info: undefined }
return { provider, id, info, isLoading, isError }
}
function getSelectedModelId({ provider, apiConfiguration }: { provider: string; apiConfiguration: ApiConfiguration }) {
switch (provider) {
case "openrouter":
return apiConfiguration.openRouterModelId ?? openRouterDefaultModelId
case "requesty":
return apiConfiguration.requestyModelId ?? requestyDefaultModelId
case "glama":
return apiConfiguration.glamaModelId ?? glamaDefaultModelId
case "unbound":
return apiConfiguration.unboundModelId ?? unboundDefaultModelId
case "openai":
return apiConfiguration.openAiModelId || ""
case "ollama":
return apiConfiguration.ollamaModelId || ""
case "lmstudio":
return apiConfiguration.lmStudioModelId || ""
case "vscode-lm":
return apiConfiguration?.vsCodeLmModelSelector
? `${apiConfiguration.vsCodeLmModelSelector.vendor}/${apiConfiguration.vsCodeLmModelSelector.family}`
: ""
default:
return apiConfiguration.apiModelId ?? anthropicDefaultModelId
}
}
function getSelectedModelInfo({
function getSelectedModel({
provider,
id,
apiConfiguration,
routerModels,
}: {
provider: string
id: string
apiConfiguration?: ApiConfiguration
provider: ProviderName
apiConfiguration: ApiConfiguration
routerModels: RouterModels
}): ModelInfo {
}): { id: string; info: ModelInfo } {
switch (provider) {
case "openrouter":
return routerModels.openrouter[id] ?? routerModels.openrouter[openRouterDefaultModelId]
case "requesty":
return routerModels.requesty[id] ?? routerModels.requesty[requestyDefaultModelId]
case "glama":
return routerModels.glama[id] ?? routerModels.glama[glamaDefaultModelId]
case "unbound":
return routerModels.unbound[id] ?? routerModels.unbound[unboundDefaultModelId]
case "xai":
return xaiModels[id as keyof typeof xaiModels] ?? xaiModels[xaiDefaultModelId]
case "groq":
return groqModels[id as keyof typeof groqModels] ?? groqModels[groqDefaultModelId]
case "chutes":
return chutesModels[id as keyof typeof chutesModels] ?? chutesModels[chutesDefaultModelId]
case "bedrock":
case "openrouter": {
const id = apiConfiguration.openRouterModelId ?? openRouterDefaultModelId
const info = routerModels.openrouter[id]
return info
? { id, info }
: { id: openRouterDefaultModelId, info: routerModels.openrouter[openRouterDefaultModelId] }
}
case "requesty": {
const id = apiConfiguration.requestyModelId ?? requestyDefaultModelId
const info = routerModels.requesty[id]
return info
? { id, info }
: { id: requestyDefaultModelId, info: routerModels.requesty[requestyDefaultModelId] }
}
case "glama": {
const id = apiConfiguration.glamaModelId ?? glamaDefaultModelId
const info = routerModels.glama[id]
return info ? { id, info } : { id: glamaDefaultModelId, info: routerModels.glama[glamaDefaultModelId] }
}
case "unbound": {
const id = apiConfiguration.unboundModelId ?? unboundDefaultModelId
const info = routerModels.unbound[id]
return info
? { id, info }
: { id: unboundDefaultModelId, info: routerModels.unbound[unboundDefaultModelId] }
}
case "xai": {
const id = apiConfiguration.apiModelId ?? xaiDefaultModelId
const info = xaiModels[id as keyof typeof xaiModels]
return info ? { id, info } : { id: xaiDefaultModelId, info: xaiModels[xaiDefaultModelId] }
}
case "groq": {
const id = apiConfiguration.apiModelId ?? groqDefaultModelId
const info = groqModels[id as keyof typeof groqModels]
return info ? { id, info } : { id: groqDefaultModelId, info: groqModels[groqDefaultModelId] }
}
case "chutes": {
const id = apiConfiguration.apiModelId ?? chutesDefaultModelId
const info = chutesModels[id as keyof typeof chutesModels]
return info ? { id, info } : { id: chutesDefaultModelId, info: chutesModels[chutesDefaultModelId] }
}
case "bedrock": {
const id = apiConfiguration.apiModelId ?? bedrockDefaultModelId
const info = bedrockModels[id as keyof typeof bedrockModels]
// Special case for custom ARN.
if (id === "custom-arn") {
return { maxTokens: 5000, contextWindow: 128_000, supportsPromptCache: false, supportsImages: true }
return {
id,
info: { maxTokens: 5000, contextWindow: 128_000, supportsPromptCache: false, supportsImages: true },
}
}
return bedrockModels[id as keyof typeof bedrockModels] ?? bedrockModels[bedrockDefaultModelId]
case "vertex":
return vertexModels[id as keyof typeof vertexModels] ?? vertexModels[vertexDefaultModelId]
case "gemini":
return geminiModels[id as keyof typeof geminiModels] ?? geminiModels[geminiDefaultModelId]
case "deepseek":
return deepSeekModels[id as keyof typeof deepSeekModels] ?? deepSeekModels[deepSeekDefaultModelId]
case "openai-native":
return (
openAiNativeModels[id as keyof typeof openAiNativeModels] ??
openAiNativeModels[openAiNativeDefaultModelId]
)
case "mistral":
return mistralModels[id as keyof typeof mistralModels] ?? mistralModels[mistralDefaultModelId]
case "openai":
return apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults
case "ollama":
return openAiModelInfoSaneDefaults
case "lmstudio":
return openAiModelInfoSaneDefaults
case "vscode-lm":
return info ? { id, info } : { id: bedrockDefaultModelId, info: bedrockModels[bedrockDefaultModelId] }
}
case "vertex": {
const id = apiConfiguration.apiModelId ?? vertexDefaultModelId
const info = vertexModels[id as keyof typeof vertexModels]
return info ? { id, info } : { id: vertexDefaultModelId, info: vertexModels[vertexDefaultModelId] }
}
case "gemini": {
const id = apiConfiguration.apiModelId ?? geminiDefaultModelId
const info = geminiModels[id as keyof typeof geminiModels]
return info ? { id, info } : { id: geminiDefaultModelId, info: geminiModels[geminiDefaultModelId] }
}
case "deepseek": {
const id = apiConfiguration.apiModelId ?? deepSeekDefaultModelId
const info = deepSeekModels[id as keyof typeof deepSeekModels]
return info ? { id, info } : { id: deepSeekDefaultModelId, info: deepSeekModels[deepSeekDefaultModelId] }
}
case "openai-native": {
const id = apiConfiguration.apiModelId ?? openAiNativeDefaultModelId
const info = openAiNativeModels[id as keyof typeof openAiNativeModels]
return info
? { id, info }
: { id: openAiNativeDefaultModelId, info: openAiNativeModels[openAiNativeDefaultModelId] }
}
case "mistral": {
const id = apiConfiguration.apiModelId ?? mistralDefaultModelId
const info = mistralModels[id as keyof typeof mistralModels]
return info ? { id, info } : { id: mistralDefaultModelId, info: mistralModels[mistralDefaultModelId] }
}
case "openai": {
const id = apiConfiguration.openAiModelId ?? ""
const info = apiConfiguration?.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults
return { id, info }
}
case "ollama": {
const id = apiConfiguration.ollamaModelId ?? ""
const info = openAiModelInfoSaneDefaults
return { id, info }
}
case "lmstudio": {
const id = apiConfiguration.lmStudioModelId ?? ""
const info = openAiModelInfoSaneDefaults
return { id, info }
}
case "vscode-lm": {
const id = apiConfiguration?.vsCodeLmModelSelector
? `${apiConfiguration.vsCodeLmModelSelector.vendor}/${apiConfiguration.vsCodeLmModelSelector.family}`
: vscodeLlmDefaultModelId
const modelFamily = apiConfiguration?.vsCodeLmModelSelector?.family ?? vscodeLlmDefaultModelId
return {
...openAiModelInfoSaneDefaults,
...vscodeLlmModels[modelFamily as keyof typeof vscodeLlmModels],
supportsImages: false, // VSCode LM API currently doesn't support images.
}
default:
return anthropicModels[id as keyof typeof anthropicModels] ?? anthropicModels[anthropicDefaultModelId]
const info = vscodeLlmModels[modelFamily as keyof typeof vscodeLlmModels]
return { id, info: { ...openAiModelInfoSaneDefaults, ...info, supportsImages: false } } // VSCode LM API currently doesn't support images.
}
// case "anthropic":
// case "human-relay":
// case "fake-ai":
default: {
const id = apiConfiguration.apiModelId ?? anthropicDefaultModelId
const info = anthropicModels[id as keyof typeof anthropicModels]
return info ? { id, info } : { id: anthropicDefaultModelId, info: anthropicModels[anthropicDefaultModelId] }
}
}
}

View file

@ -119,9 +119,11 @@
"glamaApiKey": "Clau API de Glama",
"getGlamaApiKey": "Obtenir clau API de Glama",
"requestyApiKey": "Clau API de Requesty",
"flushModelsCache": "Netejar memòria cau de models",
"flushedModelsCache": "Memòria cau netejada, si us plau torna a obrir la vista de configuració",
"getRequestyApiKey": "Obtenir clau API de Requesty",
"refreshModels": {
"label": "Actualitzar models",
"hint": "Si us plau, torneu a obrir la configuració per veure els models més recents."
},
"anthropicApiKey": "Clau API d'Anthropic",
"getAnthropicApiKey": "Obtenir clau API d'Anthropic",
"anthropicUseAuthToken": "Passar la clau API d'Anthropic com a capçalera d'autorització en lloc de X-Api-Key",

View file

@ -106,8 +106,6 @@
"awsCustomArnUse": "Geben Sie eine gültige Amazon Bedrock ARN für das Modell ein, das Sie verwenden möchten. Formatbeispiele:",
"awsCustomArnDesc": "Stellen Sie sicher, dass die Region in der ARN mit Ihrer oben ausgewählten AWS-Region übereinstimmt.",
"openRouterApiKey": "OpenRouter API-Schlüssel",
"flushModelsCache": "Modell-Cache leeren",
"flushedModelsCache": "Cache geleert, bitte öffnen Sie die Einstellungsansicht erneut",
"getOpenRouterApiKey": "OpenRouter API-Schlüssel erhalten",
"apiKeyStorageNotice": "API-Schlüssel werden sicher im VSCode Secret Storage gespeichert",
"glamaApiKey": "Glama API-Schlüssel",
@ -121,6 +119,10 @@
"noCustomHeaders": "Keine benutzerdefinierten Headers definiert. Klicke auf die + Schaltfläche, um einen hinzuzufügen.",
"requestyApiKey": "Requesty API-Schlüssel",
"getRequestyApiKey": "Requesty API-Schlüssel erhalten",
"refreshModels": {
"label": "Modelle aktualisieren",
"hint": "Bitte öffne die Einstellungen erneut, um die neuesten Modelle zu sehen."
},
"openRouterTransformsText": "Prompts und Nachrichtenketten auf Kontextgröße komprimieren (<a>OpenRouter Transformationen</a>)",
"anthropicApiKey": "Anthropic API-Schlüssel",
"getAnthropicApiKey": "Anthropic API-Schlüssel erhalten",

View file

@ -118,8 +118,10 @@
"headerValue": "Header value",
"noCustomHeaders": "No custom headers defined. Click the + button to add one.",
"requestyApiKey": "Requesty API Key",
"flushModelsCache": "Flush cached models",
"flushedModelsCache": "Flushed cache, please reopen the settings view",
"refreshModels": {
"label": "Refresh Models",
"hint": "Please reopen the settings to see the latest models."
},
"getRequestyApiKey": "Get Requesty API Key",
"openRouterTransformsText": "Compress prompts and message chains to the context size (<a>OpenRouter Transforms</a>)",
"anthropicApiKey": "Anthropic API Key",

View file

@ -106,8 +106,6 @@
"awsCustomArnUse": "Ingrese un ARN de Amazon Bedrock válido para el modelo que desea utilizar. Ejemplos de formato:",
"awsCustomArnDesc": "Asegúrese de que la región en el ARN coincida con la región de AWS seleccionada anteriormente.",
"openRouterApiKey": "Clave API de OpenRouter",
"flushModelsCache": "Limpiar modelos en caché",
"flushedModelsCache": "Caché limpiada, por favor vuelva a abrir la vista de configuración",
"getOpenRouterApiKey": "Obtener clave API de OpenRouter",
"apiKeyStorageNotice": "Las claves API se almacenan de forma segura en el Almacenamiento Secreto de VSCode",
"glamaApiKey": "Clave API de Glama",
@ -121,6 +119,10 @@
"noCustomHeaders": "No hay encabezados personalizados definidos. Haga clic en el botón + para añadir uno.",
"requestyApiKey": "Clave API de Requesty",
"getRequestyApiKey": "Obtener clave API de Requesty",
"refreshModels": {
"label": "Actualizar modelos",
"hint": "Por favor, vuelve a abrir la configuración para ver los modelos más recientes."
},
"openRouterTransformsText": "Comprimir prompts y cadenas de mensajes al tamaño del contexto (<a>Transformaciones de OpenRouter</a>)",
"anthropicApiKey": "Clave API de Anthropic",
"getAnthropicApiKey": "Obtener clave API de Anthropic",

View file

@ -106,8 +106,6 @@
"awsCustomArnUse": "Entrez un ARN Amazon Bedrock valide pour le modèle que vous souhaitez utiliser. Exemples de format :",
"awsCustomArnDesc": "Assurez-vous que la région dans l'ARN correspond à la région AWS sélectionnée ci-dessus.",
"openRouterApiKey": "Clé API OpenRouter",
"flushModelsCache": "Vider le cache des modèles",
"flushedModelsCache": "Cache vidé, veuillez rouvrir la vue des paramètres",
"getOpenRouterApiKey": "Obtenir la clé API OpenRouter",
"apiKeyStorageNotice": "Les clés API sont stockées en toute sécurité dans le stockage sécurisé de VSCode",
"glamaApiKey": "Clé API Glama",
@ -121,6 +119,10 @@
"noCustomHeaders": "Aucun en-tête personnalisé défini. Cliquez sur le bouton + pour en ajouter un.",
"requestyApiKey": "Clé API Requesty",
"getRequestyApiKey": "Obtenir la clé API Requesty",
"refreshModels": {
"label": "Actualiser les modèles",
"hint": "Veuillez rouvrir les paramètres pour voir les modèles les plus récents."
},
"openRouterTransformsText": "Compresser les prompts et chaînes de messages à la taille du contexte (<a>Transformations OpenRouter</a>)",
"anthropicApiKey": "Clé API Anthropic",
"getAnthropicApiKey": "Obtenir la clé API Anthropic",

View file

@ -118,9 +118,11 @@
"headerValue": "हेडर मूल्य",
"noCustomHeaders": "कोई कस्टम हेडर परिभाषित नहीं है। एक जोड़ने के लिए + बटन पर क्लिक करें।",
"requestyApiKey": "Requesty API कुंजी",
"flushModelsCache": "मॉडल कैश साफ़ करें",
"flushedModelsCache": "कैश साफ़ किया गया, कृपया सेटिंग्स व्यू को फिर से खोलें",
"getRequestyApiKey": "Requesty API कुंजी प्राप्त करें",
"refreshModels": {
"label": "मॉडल रिफ्रेश करें",
"hint": "नवीनतम मॉडल देखने के लिए कृपया सेटिंग्स को फिर से खोलें।"
},
"openRouterTransformsText": "संदर्भ आकार के लिए प्रॉम्प्ट और संदेश श्रृंखलाओं को संपीड़ित करें (<a>OpenRouter ट्रांसफॉर्म</a>)",
"anthropicApiKey": "Anthropic API कुंजी",
"getAnthropicApiKey": "Anthropic API कुंजी प्राप्त करें",

View file

@ -118,9 +118,11 @@
"headerValue": "Valore intestazione",
"noCustomHeaders": "Nessuna intestazione personalizzata definita. Fai clic sul pulsante + per aggiungerne una.",
"requestyApiKey": "Chiave API Requesty",
"flushModelsCache": "Svuota cache dei modelli",
"flushedModelsCache": "Cache svuotata, riapri la vista delle impostazioni",
"getRequestyApiKey": "Ottieni chiave API Requesty",
"refreshModels": {
"label": "Aggiorna modelli",
"hint": "Riapri le impostazioni per vedere i modelli più recenti."
},
"openRouterTransformsText": "Comprimi prompt e catene di messaggi alla dimensione del contesto (<a>Trasformazioni OpenRouter</a>)",
"anthropicApiKey": "Chiave API Anthropic",
"getAnthropicApiKey": "Ottieni chiave API Anthropic",

View file

@ -118,9 +118,11 @@
"headerValue": "ヘッダー値",
"noCustomHeaders": "カスタムヘッダーが定義されていません。+ ボタンをクリックして追加してください。",
"requestyApiKey": "Requesty APIキー",
"flushModelsCache": "モデルキャッシュをクリア",
"flushedModelsCache": "キャッシュをクリアしました。設定ビューを再開してください",
"getRequestyApiKey": "Requesty APIキーを取得",
"refreshModels": {
"label": "モデルを更新",
"hint": "最新のモデルを表示するには設定を再度開いてください。"
},
"openRouterTransformsText": "プロンプトとメッセージチェーンをコンテキストサイズに圧縮 (<a>OpenRouter Transforms</a>)",
"anthropicApiKey": "Anthropic APIキー",
"getAnthropicApiKey": "Anthropic APIキーを取得",

View file

@ -118,9 +118,11 @@
"headerValue": "헤더 값",
"noCustomHeaders": "정의된 사용자 정의 헤더가 없습니다. + 버튼을 클릭하여 추가하세요.",
"requestyApiKey": "Requesty API 키",
"flushModelsCache": "모델 캐시 지우기",
"flushedModelsCache": "캐시가 지워졌습니다. 설정 보기를 다시 열어주세요",
"getRequestyApiKey": "Requesty API 키 받기",
"refreshModels": {
"label": "모델 새로고침",
"hint": "최신 모델을 보려면 설정을 다시 열어주세요."
},
"openRouterTransformsText": "프롬프트와 메시지 체인을 컨텍스트 크기로 압축 (<a>OpenRouter Transforms</a>)",
"anthropicApiKey": "Anthropic API 키",
"getAnthropicApiKey": "Anthropic API 키 받기",

View file

@ -118,9 +118,11 @@
"headerValue": "Wartość nagłówka",
"noCustomHeaders": "Brak zdefiniowanych niestandardowych nagłówków. Kliknij przycisk +, aby dodać.",
"requestyApiKey": "Klucz API Requesty",
"flushModelsCache": "Wyczyść pamięć podręczną modeli",
"flushedModelsCache": "Pamięć podręczna wyczyszczona, proszę ponownie otworzyć widok ustawień",
"getRequestyApiKey": "Uzyskaj klucz API Requesty",
"refreshModels": {
"label": "Odśwież modele",
"hint": "Proszę ponownie otworzyć ustawienia, aby zobaczyć najnowsze modele."
},
"openRouterTransformsText": "Kompresuj podpowiedzi i łańcuchy wiadomości do rozmiaru kontekstu (<a>Transformacje OpenRouter</a>)",
"anthropicApiKey": "Klucz API Anthropic",
"getAnthropicApiKey": "Uzyskaj klucz API Anthropic",

View file

@ -106,8 +106,6 @@
"awsCustomArnUse": "Insira um ARN Amazon Bedrock válido para o modelo que deseja usar. Exemplos de formato:",
"awsCustomArnDesc": "Certifique-se de que a região no ARN corresponde à região AWS selecionada acima.",
"openRouterApiKey": "Chave de API OpenRouter",
"flushModelsCache": "Limpar cache de modelos",
"flushedModelsCache": "Cache limpo, por favor reabra a visualização de configurações",
"getOpenRouterApiKey": "Obter chave de API OpenRouter",
"apiKeyStorageNotice": "As chaves de API são armazenadas com segurança no Armazenamento Secreto do VSCode",
"glamaApiKey": "Chave de API Glama",
@ -121,6 +119,10 @@
"noCustomHeaders": "Nenhum cabeçalho personalizado definido. Clique no botão + para adicionar um.",
"requestyApiKey": "Chave de API Requesty",
"getRequestyApiKey": "Obter chave de API Requesty",
"refreshModels": {
"label": "Atualizar modelos",
"hint": "Por favor, reabra as configurações para ver os modelos mais recentes."
},
"openRouterTransformsText": "Comprimir prompts e cadeias de mensagens para o tamanho do contexto (<a>Transformações OpenRouter</a>)",
"anthropicApiKey": "Chave de API Anthropic",
"getAnthropicApiKey": "Obter chave de API Anthropic",

View file

@ -118,9 +118,11 @@
"headerValue": "Значение заголовка",
"noCustomHeaders": "Пользовательские заголовки не определены. Нажмите кнопку +, чтобы добавить.",
"requestyApiKey": "Requesty API-ключ",
"flushModelsCache": "Очистить кэш моделей",
"flushedModelsCache": "Кэш очищен, пожалуйста, переоткройте представление настроек",
"getRequestyApiKey": "Получить Requesty API-ключ",
"refreshModels": {
"label": "Обновить модели",
"hint": "Пожалуйста, откройте настройки заново, чтобы увидеть последние модели."
},
"openRouterTransformsText": "Сжимать подсказки и цепочки сообщений до размера контекста (<a>OpenRouter Transforms</a>)",
"anthropicApiKey": "Anthropic API-ключ",
"getAnthropicApiKey": "Получить Anthropic API-ключ",

View file

@ -106,8 +106,6 @@
"awsCustomArnUse": "Kullanmak istediğiniz model için geçerli bir Amazon Bedrock ARN'si girin. Format örnekleri:",
"awsCustomArnDesc": "ARN içindeki bölgenin yukarıda seçilen AWS Bölgesiyle eşleştiğinden emin olun.",
"openRouterApiKey": "OpenRouter API Anahtarı",
"flushModelsCache": "Model önbelleğini temizle",
"flushedModelsCache": "Önbellek temizlendi, lütfen ayarlar görünümünü yeniden açın",
"getOpenRouterApiKey": "OpenRouter API Anahtarı Al",
"apiKeyStorageNotice": "API anahtarları VSCode'un Gizli Depolamasında güvenli bir şekilde saklanır",
"glamaApiKey": "Glama API Anahtarı",
@ -121,6 +119,10 @@
"noCustomHeaders": "Tanımlanmış özel başlık yok. Eklemek için + düğmesine tıklayın.",
"requestyApiKey": "Requesty API Anahtarı",
"getRequestyApiKey": "Requesty API Anahtarı Al",
"refreshModels": {
"label": "Modelleri Yenile",
"hint": "En son modelleri görmek için lütfen ayarları yeniden açın."
},
"openRouterTransformsText": "İstem ve mesaj zincirlerini bağlam boyutuna sıkıştır (<a>OpenRouter Dönüşümleri</a>)",
"anthropicApiKey": "Anthropic API Anahtarı",
"getAnthropicApiKey": "Anthropic API Anahtarı Al",

View file

@ -118,8 +118,6 @@
"headerValue": "Giá trị tiêu đề",
"noCustomHeaders": "Chưa có tiêu đề tùy chỉnh nào được định nghĩa. Nhấp vào nút + để thêm.",
"requestyApiKey": "Khóa API Requesty",
"flushModelsCache": "Xóa bộ nhớ đệm mô hình",
"flushedModelsCache": "Đã xóa bộ nhớ đệm, vui lòng mở lại chế độ xem cài đặt",
"getRequestyApiKey": "Lấy khóa API Requesty",
"anthropicApiKey": "Khóa API Anthropic",
"getAnthropicApiKey": "Lấy khóa API Anthropic",
@ -181,6 +179,10 @@
"warning": "Lưu ý: Roo Code sử dụng các lời nhắc phức tạp và hoạt động tốt nhất với các mô hình Claude. Các mô hình kém mạnh hơn có thể không hoạt động như mong đợi."
},
"openRouterTransformsText": "Nén lời nhắc và chuỗi tin nhắn theo kích thước ngữ cảnh (<a>OpenRouter Transforms</a>)",
"refreshModels": {
"label": "Làm mới mô hình",
"hint": "Vui lòng mở lại cài đặt để xem các mô hình mới nhất."
},
"unboundApiKey": "Khóa API Unbound",
"getUnboundApiKey": "Lấy khóa API Unbound",
"humanRelay": {

View file

@ -118,9 +118,11 @@
"glamaApiKey": "Glama API 密钥",
"getGlamaApiKey": "获取 Glama API 密钥",
"requestyApiKey": "Requesty API 密钥",
"flushModelsCache": "清除模型缓存",
"flushedModelsCache": "缓存已清除,请重新打开设置视图",
"getRequestyApiKey": "获取 Requesty API 密钥",
"refreshModels": {
"label": "刷新模型",
"hint": "请重新打开设置以查看最新模型。"
},
"openRouterTransformsText": "自动压缩提示词和消息链到上下文长度限制内 (<a>OpenRouter转换</a>)",
"anthropicApiKey": "Anthropic API 密钥",
"getAnthropicApiKey": "获取 Anthropic API 密钥",

View file

@ -118,9 +118,11 @@
"headerValue": "標頭值",
"noCustomHeaders": "尚未定義自訂標頭。點擊 + 按鈕以新增。",
"requestyApiKey": "Requesty API 金鑰",
"flushModelsCache": "清除模型快取",
"flushedModelsCache": "快取已清除,請重新開啟設定視圖",
"getRequestyApiKey": "取得 Requesty API 金鑰",
"refreshModels": {
"label": "重新整理模型",
"hint": "請重新開啟設定以查看最新模型。"
},
"openRouterTransformsText": "將提示和訊息鏈壓縮到上下文大小 (<a>OpenRouter 轉換</a>)",
"anthropicApiKey": "Anthropic API 金鑰",
"getAnthropicApiKey": "取得 Anthropic API 金鑰",

View file

@ -1,6 +1,5 @@
export function getCallbackUrl(provider: string, uriScheme?: string) {
const callbackUrl = `${uriScheme || "vscode"}://rooveterinaryinc.roo-cline/${provider}`
return encodeURIComponent(callbackUrl)
return encodeURIComponent(`${uriScheme || "vscode"}://rooveterinaryinc.roo-cline/${provider}`)
}
export function getGlamaAuthUrl(uriScheme?: string) {
@ -14,7 +13,3 @@ export function getOpenRouterAuthUrl(uriScheme?: string) {
export function getRequestyAuthUrl(uriScheme?: string) {
return `https://app.requesty.ai/oauth/authorize?callback_url=${getCallbackUrl("requesty", uriScheme)}`
}
export function getRequestyApiKeyUrl() {
return "https://app.requesty.ai/api-keys"
}