mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Handle null [x]ModelInfo types, recover from settings schema parse errors (#2064)
This commit is contained in:
parent
e7e5511b65
commit
dbe0ede9a7
9 changed files with 112 additions and 99 deletions
|
|
@ -2,16 +2,17 @@ import * as vscode from "vscode"
|
|||
|
||||
import {
|
||||
PROVIDER_SETTINGS_KEYS,
|
||||
ProviderSettings,
|
||||
providerSettingsSchema,
|
||||
GlobalSettings,
|
||||
globalSettingsSchema,
|
||||
RooCodeSettings,
|
||||
GLOBAL_SETTINGS_KEYS,
|
||||
SECRET_STATE_KEYS,
|
||||
SecretState,
|
||||
isSecretStateKey,
|
||||
GLOBAL_STATE_KEYS,
|
||||
ProviderSettings,
|
||||
GlobalSettings,
|
||||
SecretState,
|
||||
GlobalState,
|
||||
RooCodeSettings,
|
||||
providerSettingsSchema,
|
||||
globalSettingsSchema,
|
||||
isSecretStateKey,
|
||||
} from "../../schemas"
|
||||
import { logger } from "../../utils/logging"
|
||||
|
||||
|
|
@ -151,7 +152,15 @@ export class ContextProxy {
|
|||
*/
|
||||
|
||||
public getGlobalSettings(): GlobalSettings {
|
||||
return globalSettingsSchema.parse({ ...this.stateCache })
|
||||
const values = this.getValues()
|
||||
|
||||
try {
|
||||
return globalSettingsSchema.parse(values)
|
||||
} catch (error) {
|
||||
// Log to Posthog?
|
||||
// We'll want to know about bad type assumptions or bad ExtensionState data.
|
||||
return GLOBAL_SETTINGS_KEYS.reduce((acc, key) => ({ ...acc, [key]: values[key] }), {} as GlobalSettings)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -159,7 +168,15 @@ export class ContextProxy {
|
|||
*/
|
||||
|
||||
public getProviderSettings(): ProviderSettings {
|
||||
return providerSettingsSchema.parse(this.getValues())
|
||||
const values = this.getValues()
|
||||
|
||||
try {
|
||||
return providerSettingsSchema.parse(values)
|
||||
} catch (error) {
|
||||
// Log to Posthog?
|
||||
// We'll want to know about bad type assumptions or bad ExtensionState data.
|
||||
return PROVIDER_SETTINGS_KEYS.reduce((acc, key) => ({ ...acc, [key]: values[key] }), {} as ProviderSettings)
|
||||
}
|
||||
}
|
||||
|
||||
public async setProviderSettings(values: ProviderSettings) {
|
||||
|
|
@ -206,7 +223,6 @@ export class ContextProxy {
|
|||
public async export(): Promise<GlobalSettings | undefined> {
|
||||
try {
|
||||
const globalSettings = globalSettingsExportSchema.parse(this.getValues())
|
||||
|
||||
return Object.fromEntries(Object.entries(globalSettings).filter(([_, value]) => value !== undefined))
|
||||
} catch (error) {
|
||||
console.log(error.message)
|
||||
|
|
|
|||
|
|
@ -16,18 +16,6 @@ export const providerProfilesSchema = z.object({
|
|||
|
||||
export type ProviderProfiles = z.infer<typeof providerProfilesSchema>
|
||||
|
||||
const providerProfilesExportSchema = providerProfilesSchema.extend({
|
||||
apiConfigs: z.record(
|
||||
z.string(),
|
||||
providerSettingsWithIdSchema.omit({
|
||||
glamaModelInfo: true,
|
||||
openRouterModelInfo: true,
|
||||
unboundModelInfo: true,
|
||||
requestyModelInfo: true,
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
export class ProviderSettingsManager {
|
||||
private static readonly SCOPE_PREFIX = "roo_cline_config_"
|
||||
|
||||
|
|
@ -246,7 +234,7 @@ export class ProviderSettingsManager {
|
|||
|
||||
public async export() {
|
||||
try {
|
||||
return await this.lock(async () => providerProfilesExportSchema.parse(await this.load()))
|
||||
return await this.lock(async () => providerProfilesSchema.parse(await this.load()))
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to export provider profiles: ${error}`)
|
||||
}
|
||||
|
|
|
|||
20
src/exports/roo-code.d.ts
vendored
20
src/exports/roo-code.d.ts
vendored
|
|
@ -27,7 +27,7 @@ type ProviderSettings = {
|
|||
anthropicBaseUrl?: string | undefined
|
||||
glamaModelId?: string | undefined
|
||||
glamaModelInfo?:
|
||||
| {
|
||||
| ({
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
|
|
@ -40,13 +40,13 @@ type ProviderSettings = {
|
|||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
} | null)
|
||||
| undefined
|
||||
glamaApiKey?: string | undefined
|
||||
openRouterApiKey?: string | undefined
|
||||
openRouterModelId?: string | undefined
|
||||
openRouterModelInfo?:
|
||||
| {
|
||||
| ({
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
|
|
@ -59,7 +59,7 @@ type ProviderSettings = {
|
|||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
} | null)
|
||||
| undefined
|
||||
openRouterBaseUrl?: string | undefined
|
||||
openRouterSpecificProvider?: string | undefined
|
||||
|
|
@ -83,7 +83,7 @@ type ProviderSettings = {
|
|||
openAiR1FormatEnabled?: boolean | undefined
|
||||
openAiModelId?: string | undefined
|
||||
openAiCustomModelInfo?:
|
||||
| {
|
||||
| ({
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
|
|
@ -96,7 +96,7 @@ type ProviderSettings = {
|
|||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
} | null)
|
||||
| undefined
|
||||
openAiUseAzure?: boolean | undefined
|
||||
azureApiVersion?: string | undefined
|
||||
|
|
@ -125,7 +125,7 @@ type ProviderSettings = {
|
|||
unboundApiKey?: string | undefined
|
||||
unboundModelId?: string | undefined
|
||||
unboundModelInfo?:
|
||||
| {
|
||||
| ({
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
|
|
@ -138,12 +138,12 @@ type ProviderSettings = {
|
|||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
} | null)
|
||||
| undefined
|
||||
requestyApiKey?: string | undefined
|
||||
requestyModelId?: string | undefined
|
||||
requestyModelInfo?:
|
||||
| {
|
||||
| ({
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
|
|
@ -156,7 +156,7 @@ type ProviderSettings = {
|
|||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
} | null)
|
||||
| undefined
|
||||
modelTemperature?: (number | null) | undefined
|
||||
modelMaxTokens?: number | undefined
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ type ProviderSettings = {
|
|||
anthropicBaseUrl?: string | undefined
|
||||
glamaModelId?: string | undefined
|
||||
glamaModelInfo?:
|
||||
| {
|
||||
| ({
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
|
|
@ -41,13 +41,13 @@ type ProviderSettings = {
|
|||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
} | null)
|
||||
| undefined
|
||||
glamaApiKey?: string | undefined
|
||||
openRouterApiKey?: string | undefined
|
||||
openRouterModelId?: string | undefined
|
||||
openRouterModelInfo?:
|
||||
| {
|
||||
| ({
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
|
|
@ -60,7 +60,7 @@ type ProviderSettings = {
|
|||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
} | null)
|
||||
| undefined
|
||||
openRouterBaseUrl?: string | undefined
|
||||
openRouterSpecificProvider?: string | undefined
|
||||
|
|
@ -84,7 +84,7 @@ type ProviderSettings = {
|
|||
openAiR1FormatEnabled?: boolean | undefined
|
||||
openAiModelId?: string | undefined
|
||||
openAiCustomModelInfo?:
|
||||
| {
|
||||
| ({
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
|
|
@ -97,7 +97,7 @@ type ProviderSettings = {
|
|||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
} | null)
|
||||
| undefined
|
||||
openAiUseAzure?: boolean | undefined
|
||||
azureApiVersion?: string | undefined
|
||||
|
|
@ -126,7 +126,7 @@ type ProviderSettings = {
|
|||
unboundApiKey?: string | undefined
|
||||
unboundModelId?: string | undefined
|
||||
unboundModelInfo?:
|
||||
| {
|
||||
| ({
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
|
|
@ -139,12 +139,12 @@ type ProviderSettings = {
|
|||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
} | null)
|
||||
| undefined
|
||||
requestyApiKey?: string | undefined
|
||||
requestyModelId?: string | undefined
|
||||
requestyModelInfo?:
|
||||
| {
|
||||
| ({
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
|
|
@ -157,7 +157,7 @@ type ProviderSettings = {
|
|||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
} | null)
|
||||
| undefined
|
||||
modelTemperature?: (number | null) | undefined
|
||||
modelMaxTokens?: number | undefined
|
||||
|
|
|
|||
|
|
@ -314,12 +314,12 @@ export const providerSettingsSchema = z.object({
|
|||
anthropicBaseUrl: z.string().optional(),
|
||||
// Glama
|
||||
glamaModelId: z.string().optional(),
|
||||
glamaModelInfo: modelInfoSchema.optional(),
|
||||
glamaModelInfo: modelInfoSchema.nullish(),
|
||||
glamaApiKey: z.string().optional(),
|
||||
// OpenRouter
|
||||
openRouterApiKey: z.string().optional(),
|
||||
openRouterModelId: z.string().optional(),
|
||||
openRouterModelInfo: modelInfoSchema.optional(),
|
||||
openRouterModelInfo: modelInfoSchema.nullish(),
|
||||
openRouterBaseUrl: z.string().optional(),
|
||||
openRouterSpecificProvider: z.string().optional(),
|
||||
openRouterUseMiddleOutTransform: z.boolean().optional(),
|
||||
|
|
@ -344,7 +344,7 @@ export const providerSettingsSchema = z.object({
|
|||
openAiApiKey: z.string().optional(),
|
||||
openAiR1FormatEnabled: z.boolean().optional(),
|
||||
openAiModelId: z.string().optional(),
|
||||
openAiCustomModelInfo: modelInfoSchema.optional(),
|
||||
openAiCustomModelInfo: modelInfoSchema.nullish(),
|
||||
openAiUseAzure: z.boolean().optional(),
|
||||
azureApiVersion: z.string().optional(),
|
||||
openAiStreamingEnabled: z.boolean().optional(),
|
||||
|
|
@ -379,11 +379,11 @@ export const providerSettingsSchema = z.object({
|
|||
// Unbound
|
||||
unboundApiKey: z.string().optional(),
|
||||
unboundModelId: z.string().optional(),
|
||||
unboundModelInfo: modelInfoSchema.optional(),
|
||||
unboundModelInfo: modelInfoSchema.nullish(),
|
||||
// Requesty
|
||||
requestyApiKey: z.string().optional(),
|
||||
requestyModelId: z.string().optional(),
|
||||
requestyModelInfo: modelInfoSchema.optional(),
|
||||
requestyModelInfo: modelInfoSchema.nullish(),
|
||||
// Claude 3.7 Sonnet Thinking
|
||||
modelTemperature: z.number().nullish(),
|
||||
modelMaxTokens: z.number().optional(),
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ import { Checkbox } from "vscrui"
|
|||
import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import { ExternalLinkIcon } from "@radix-ui/react-icons"
|
||||
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectSeparator, Button } from "@/components/ui"
|
||||
|
||||
import {
|
||||
ApiConfiguration,
|
||||
ModelInfo,
|
||||
|
|
@ -42,56 +40,23 @@ import {
|
|||
import { ExtensionMessage } from "../../../../src/shared/ExtensionMessage"
|
||||
|
||||
import { vscode } from "@/utils/vscode"
|
||||
import { validateApiConfiguration, validateModelId, validateBedrockArn } from "@/utils/validate"
|
||||
import {
|
||||
useOpenRouterModelProviders,
|
||||
OPENROUTER_DEFAULT_PROVIDER_NAME,
|
||||
} from "@/components/ui/hooks/useOpenRouterModelProviders"
|
||||
import { useOpenRouterKeyInfo } from "@/components/ui/hooks/useOpenRouterKeyInfo"
|
||||
import { useRequestyKeyInfo } from "@/components/ui/hooks/useRequestyKeyInfo"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectSeparator, Button } from "@/components/ui"
|
||||
|
||||
import { MODELS_BY_PROVIDER, PROVIDERS, AWS_REGIONS, VERTEX_REGIONS } from "./constants"
|
||||
import { VSCodeButtonLink } from "../common/VSCodeButtonLink"
|
||||
import { ModelInfoView } from "./ModelInfoView"
|
||||
import { ModelPicker } from "./ModelPicker"
|
||||
import { TemperatureControl } from "./TemperatureControl"
|
||||
import { validateApiConfiguration, validateModelId, validateBedrockArn } from "@/utils/validate"
|
||||
import { ApiErrorMessage } from "./ApiErrorMessage"
|
||||
import { ThinkingBudget } from "./ThinkingBudget"
|
||||
import { R1FormatSetting } from "./R1FormatSetting"
|
||||
|
||||
// Component to display OpenRouter API key balance
|
||||
const OpenRouterBalanceDisplay = ({ apiKey, baseUrl }: { apiKey: string; baseUrl?: string }) => {
|
||||
const { data: keyInfo } = useOpenRouterKeyInfo(apiKey, baseUrl)
|
||||
|
||||
if (!keyInfo || !keyInfo.limit) {
|
||||
return null
|
||||
}
|
||||
|
||||
const formattedBalance = (keyInfo.limit - keyInfo.usage).toFixed(2)
|
||||
|
||||
return (
|
||||
<VSCodeLink href="https://openrouter.ai/settings/keys" className="text-vscode-foreground hover:underline">
|
||||
${formattedBalance}
|
||||
</VSCodeLink>
|
||||
)
|
||||
}
|
||||
|
||||
const RequestyBalanceDisplay = ({ apiKey }: { apiKey: string }) => {
|
||||
const { data: keyInfo } = useRequestyKeyInfo(apiKey)
|
||||
|
||||
if (!keyInfo) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Parse the balance to a number and format it to 2 decimal places
|
||||
const balance = parseFloat(keyInfo.org_balance)
|
||||
const formattedBalance = balance.toFixed(2)
|
||||
|
||||
return (
|
||||
<VSCodeLink href="https://app.requesty.ai/settings" className="text-vscode-foreground hover:underline">
|
||||
${formattedBalance}
|
||||
</VSCodeLink>
|
||||
)
|
||||
}
|
||||
import { OpenRouterBalanceDisplay } from "./OpenRouterBalanceDisplay"
|
||||
import { RequestyBalanceDisplay } from "./RequestyBalanceDisplay"
|
||||
|
||||
interface ApiOptionsProps {
|
||||
uriScheme: string | undefined
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
|||
import { Trans } from "react-i18next"
|
||||
import { ChevronsUpDown, Check, X } from "lucide-react"
|
||||
|
||||
import { ProviderSettings, ModelInfo } from "../../../../src/schemas"
|
||||
|
||||
import { useAppTranslation } from "@/i18n/TranslationContext"
|
||||
import { cn } from "@/lib/utils"
|
||||
import {
|
||||
|
|
@ -18,30 +20,30 @@ import {
|
|||
Button,
|
||||
} from "@/components/ui"
|
||||
|
||||
import { ApiConfiguration, ModelInfo } from "../../../../src/shared/api"
|
||||
|
||||
import { normalizeApiConfiguration } from "./ApiOptions"
|
||||
import { ThinkingBudget } from "./ThinkingBudget"
|
||||
import { ModelInfoView } from "./ModelInfoView"
|
||||
|
||||
type ExtractType<T> = NonNullable<
|
||||
{ [K in keyof ApiConfiguration]: Required<ApiConfiguration>[K] extends T ? K : never }[keyof ApiConfiguration]
|
||||
type ModelIdKey = keyof Pick<
|
||||
ProviderSettings,
|
||||
"glamaModelId" | "openRouterModelId" | "unboundModelId" | "requestyModelId" | "openAiModelId"
|
||||
>
|
||||
|
||||
type ModelIdKeys = NonNullable<
|
||||
{ [K in keyof ApiConfiguration]: K extends `${string}ModelId` ? K : never }[keyof ApiConfiguration]
|
||||
type ModelInfoKey = keyof Pick<
|
||||
ProviderSettings,
|
||||
"glamaModelInfo" | "openRouterModelInfo" | "unboundModelInfo" | "requestyModelInfo" | "openAiCustomModelInfo"
|
||||
>
|
||||
|
||||
interface ModelPickerProps {
|
||||
defaultModelId: string
|
||||
defaultModelInfo?: ModelInfo
|
||||
models: Record<string, ModelInfo> | null
|
||||
modelIdKey: ModelIdKeys
|
||||
modelInfoKey: ExtractType<ModelInfo>
|
||||
modelIdKey: ModelIdKey
|
||||
modelInfoKey: ModelInfoKey
|
||||
serviceName: string
|
||||
serviceUrl: string
|
||||
apiConfiguration: ApiConfiguration
|
||||
setApiConfigurationField: <K extends keyof ApiConfiguration>(field: K, value: ApiConfiguration[K]) => void
|
||||
apiConfiguration: ProviderSettings
|
||||
setApiConfigurationField: <K extends keyof ProviderSettings>(field: K, value: ProviderSettings[K]) => void
|
||||
}
|
||||
|
||||
export const ModelPicker = ({
|
||||
|
|
@ -72,7 +74,9 @@ export const ModelPicker = ({
|
|||
|
||||
const onSelect = useCallback(
|
||||
(modelId: string) => {
|
||||
if (!modelId) return
|
||||
if (!modelId) {
|
||||
return
|
||||
}
|
||||
|
||||
setOpen(false)
|
||||
const modelInfo = models?.[modelId]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
import { useOpenRouterKeyInfo } from "@/components/ui/hooks/useOpenRouterKeyInfo"
|
||||
|
||||
export const OpenRouterBalanceDisplay = ({ apiKey, baseUrl }: { apiKey: string; baseUrl?: string }) => {
|
||||
const { data: keyInfo } = useOpenRouterKeyInfo(apiKey, baseUrl)
|
||||
|
||||
if (!keyInfo || !keyInfo.limit) {
|
||||
return null
|
||||
}
|
||||
|
||||
const formattedBalance = (keyInfo.limit - keyInfo.usage).toFixed(2)
|
||||
|
||||
return (
|
||||
<VSCodeLink href="https://openrouter.ai/settings/keys" className="text-vscode-foreground hover:underline">
|
||||
${formattedBalance}
|
||||
</VSCodeLink>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
import { useRequestyKeyInfo } from "@/components/ui/hooks/useRequestyKeyInfo"
|
||||
|
||||
export const RequestyBalanceDisplay = ({ apiKey }: { apiKey: string }) => {
|
||||
const { data: keyInfo } = useRequestyKeyInfo(apiKey)
|
||||
|
||||
if (!keyInfo) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Parse the balance to a number and format it to 2 decimal places.
|
||||
const balance = parseFloat(keyInfo.org_balance)
|
||||
const formattedBalance = balance.toFixed(2)
|
||||
|
||||
return (
|
||||
<VSCodeLink href="https://app.requesty.ai/settings" className="text-vscode-foreground hover:underline">
|
||||
${formattedBalance}
|
||||
</VSCodeLink>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue