Skip to content

Commit acb6d99

Browse files
committed
Handle null [x]ModelInfo types, recover from settings schema parse errors
1 parent 62a7bc7 commit acb6d99

File tree

9 files changed

+111
-98
lines changed

9 files changed

+111
-98
lines changed

src/core/config/ContextProxy.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ import * as vscode from "vscode"
22

33
import {
44
PROVIDER_SETTINGS_KEYS,
5+
GLOBAL_SETTINGS_KEYS,
6+
SECRET_STATE_KEYS,
7+
GLOBAL_STATE_KEYS,
58
ProviderSettings,
6-
providerSettingsSchema,
79
GlobalSettings,
8-
globalSettingsSchema,
9-
RooCodeSettings,
10-
SECRET_STATE_KEYS,
1110
SecretState,
12-
isSecretStateKey,
13-
GLOBAL_STATE_KEYS,
1411
GlobalState,
12+
RooCodeSettings,
13+
providerSettingsSchema,
14+
globalSettingsSchema,
15+
isSecretStateKey,
1516
} from "../../schemas"
1617
import { logger } from "../../utils/logging"
1718

@@ -151,15 +152,31 @@ export class ContextProxy {
151152
*/
152153

153154
public getGlobalSettings(): GlobalSettings {
154-
return globalSettingsSchema.parse({ ...this.stateCache })
155+
const values = this.getValues()
156+
157+
try {
158+
return globalSettingsSchema.parse(values)
159+
} catch (error) {
160+
// Log to Posthog?
161+
// We'll want to know about bad type assumptions or bad ExtensionState data.
162+
return GLOBAL_SETTINGS_KEYS.reduce((acc, key) => ({ ...acc, [key]: values[key] }), {} as GlobalSettings)
163+
}
155164
}
156165

157166
/**
158167
* ProviderSettings
159168
*/
160169

161170
public getProviderSettings(): ProviderSettings {
162-
return providerSettingsSchema.parse(this.getValues())
171+
const values = this.getValues()
172+
173+
try {
174+
return providerSettingsSchema.parse(values)
175+
} catch (error) {
176+
// Log to Posthog?
177+
// We'll want to know about bad type assumptions or bad ExtensionState data.
178+
return PROVIDER_SETTINGS_KEYS.reduce((acc, key) => ({ ...acc, [key]: values[key] }), {} as ProviderSettings)
179+
}
163180
}
164181

165182
public async setProviderSettings(values: ProviderSettings) {
@@ -206,7 +223,6 @@ export class ContextProxy {
206223
public async export(): Promise<GlobalSettings | undefined> {
207224
try {
208225
const globalSettings = globalSettingsExportSchema.parse(this.getValues())
209-
210226
return Object.fromEntries(Object.entries(globalSettings).filter(([_, value]) => value !== undefined))
211227
} catch (error) {
212228
console.log(error.message)

src/core/config/ProviderSettingsManager.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,6 @@ export const providerProfilesSchema = z.object({
1616

1717
export type ProviderProfiles = z.infer<typeof providerProfilesSchema>
1818

19-
const providerProfilesExportSchema = providerProfilesSchema.extend({
20-
apiConfigs: z.record(
21-
z.string(),
22-
providerSettingsWithIdSchema.omit({
23-
glamaModelInfo: true,
24-
openRouterModelInfo: true,
25-
unboundModelInfo: true,
26-
requestyModelInfo: true,
27-
}),
28-
),
29-
})
30-
3119
export class ProviderSettingsManager {
3220
private static readonly SCOPE_PREFIX = "roo_cline_config_"
3321

@@ -246,7 +234,7 @@ export class ProviderSettingsManager {
246234

247235
public async export() {
248236
try {
249-
return await this.lock(async () => providerProfilesExportSchema.parse(await this.load()))
237+
return await this.lock(async () => providerProfilesSchema.parse(await this.load()))
250238
} catch (error) {
251239
throw new Error(`Failed to export provider profiles: ${error}`)
252240
}

src/exports/roo-code.d.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type ProviderSettings = {
2727
anthropicBaseUrl?: string | undefined
2828
glamaModelId?: string | undefined
2929
glamaModelInfo?:
30-
| {
30+
| ({
3131
maxTokens?: number | undefined
3232
contextWindow: number
3333
supportsImages?: boolean | undefined
@@ -40,13 +40,13 @@ type ProviderSettings = {
4040
description?: string | undefined
4141
reasoningEffort?: ("low" | "medium" | "high") | undefined
4242
thinking?: boolean | undefined
43-
}
43+
} | null)
4444
| undefined
4545
glamaApiKey?: string | undefined
4646
openRouterApiKey?: string | undefined
4747
openRouterModelId?: string | undefined
4848
openRouterModelInfo?:
49-
| {
49+
| ({
5050
maxTokens?: number | undefined
5151
contextWindow: number
5252
supportsImages?: boolean | undefined
@@ -59,7 +59,7 @@ type ProviderSettings = {
5959
description?: string | undefined
6060
reasoningEffort?: ("low" | "medium" | "high") | undefined
6161
thinking?: boolean | undefined
62-
}
62+
} | null)
6363
| undefined
6464
openRouterBaseUrl?: string | undefined
6565
openRouterSpecificProvider?: string | undefined
@@ -83,7 +83,7 @@ type ProviderSettings = {
8383
openAiR1FormatEnabled?: boolean | undefined
8484
openAiModelId?: string | undefined
8585
openAiCustomModelInfo?:
86-
| {
86+
| ({
8787
maxTokens?: number | undefined
8888
contextWindow: number
8989
supportsImages?: boolean | undefined
@@ -96,7 +96,7 @@ type ProviderSettings = {
9696
description?: string | undefined
9797
reasoningEffort?: ("low" | "medium" | "high") | undefined
9898
thinking?: boolean | undefined
99-
}
99+
} | null)
100100
| undefined
101101
openAiUseAzure?: boolean | undefined
102102
azureApiVersion?: string | undefined
@@ -125,7 +125,7 @@ type ProviderSettings = {
125125
unboundApiKey?: string | undefined
126126
unboundModelId?: string | undefined
127127
unboundModelInfo?:
128-
| {
128+
| ({
129129
maxTokens?: number | undefined
130130
contextWindow: number
131131
supportsImages?: boolean | undefined
@@ -138,12 +138,12 @@ type ProviderSettings = {
138138
description?: string | undefined
139139
reasoningEffort?: ("low" | "medium" | "high") | undefined
140140
thinking?: boolean | undefined
141-
}
141+
} | null)
142142
| undefined
143143
requestyApiKey?: string | undefined
144144
requestyModelId?: string | undefined
145145
requestyModelInfo?:
146-
| {
146+
| ({
147147
maxTokens?: number | undefined
148148
contextWindow: number
149149
supportsImages?: boolean | undefined
@@ -156,7 +156,7 @@ type ProviderSettings = {
156156
description?: string | undefined
157157
reasoningEffort?: ("low" | "medium" | "high") | undefined
158158
thinking?: boolean | undefined
159-
}
159+
} | null)
160160
| undefined
161161
modelTemperature?: (number | null) | undefined
162162
modelMaxTokens?: number | undefined

src/exports/types.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type ProviderSettings = {
2828
anthropicBaseUrl?: string | undefined
2929
glamaModelId?: string | undefined
3030
glamaModelInfo?:
31-
| {
31+
| ({
3232
maxTokens?: number | undefined
3333
contextWindow: number
3434
supportsImages?: boolean | undefined
@@ -41,13 +41,13 @@ type ProviderSettings = {
4141
description?: string | undefined
4242
reasoningEffort?: ("low" | "medium" | "high") | undefined
4343
thinking?: boolean | undefined
44-
}
44+
} | null)
4545
| undefined
4646
glamaApiKey?: string | undefined
4747
openRouterApiKey?: string | undefined
4848
openRouterModelId?: string | undefined
4949
openRouterModelInfo?:
50-
| {
50+
| ({
5151
maxTokens?: number | undefined
5252
contextWindow: number
5353
supportsImages?: boolean | undefined
@@ -60,7 +60,7 @@ type ProviderSettings = {
6060
description?: string | undefined
6161
reasoningEffort?: ("low" | "medium" | "high") | undefined
6262
thinking?: boolean | undefined
63-
}
63+
} | null)
6464
| undefined
6565
openRouterBaseUrl?: string | undefined
6666
openRouterSpecificProvider?: string | undefined
@@ -84,7 +84,7 @@ type ProviderSettings = {
8484
openAiR1FormatEnabled?: boolean | undefined
8585
openAiModelId?: string | undefined
8686
openAiCustomModelInfo?:
87-
| {
87+
| ({
8888
maxTokens?: number | undefined
8989
contextWindow: number
9090
supportsImages?: boolean | undefined
@@ -97,7 +97,7 @@ type ProviderSettings = {
9797
description?: string | undefined
9898
reasoningEffort?: ("low" | "medium" | "high") | undefined
9999
thinking?: boolean | undefined
100-
}
100+
} | null)
101101
| undefined
102102
openAiUseAzure?: boolean | undefined
103103
azureApiVersion?: string | undefined
@@ -126,7 +126,7 @@ type ProviderSettings = {
126126
unboundApiKey?: string | undefined
127127
unboundModelId?: string | undefined
128128
unboundModelInfo?:
129-
| {
129+
| ({
130130
maxTokens?: number | undefined
131131
contextWindow: number
132132
supportsImages?: boolean | undefined
@@ -139,12 +139,12 @@ type ProviderSettings = {
139139
description?: string | undefined
140140
reasoningEffort?: ("low" | "medium" | "high") | undefined
141141
thinking?: boolean | undefined
142-
}
142+
} | null)
143143
| undefined
144144
requestyApiKey?: string | undefined
145145
requestyModelId?: string | undefined
146146
requestyModelInfo?:
147-
| {
147+
| ({
148148
maxTokens?: number | undefined
149149
contextWindow: number
150150
supportsImages?: boolean | undefined
@@ -157,7 +157,7 @@ type ProviderSettings = {
157157
description?: string | undefined
158158
reasoningEffort?: ("low" | "medium" | "high") | undefined
159159
thinking?: boolean | undefined
160-
}
160+
} | null)
161161
| undefined
162162
modelTemperature?: (number | null) | undefined
163163
modelMaxTokens?: number | undefined

src/schemas/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,12 @@ export const providerSettingsSchema = z.object({
314314
anthropicBaseUrl: z.string().optional(),
315315
// Glama
316316
glamaModelId: z.string().optional(),
317-
glamaModelInfo: modelInfoSchema.optional(),
317+
glamaModelInfo: modelInfoSchema.nullish(),
318318
glamaApiKey: z.string().optional(),
319319
// OpenRouter
320320
openRouterApiKey: z.string().optional(),
321321
openRouterModelId: z.string().optional(),
322-
openRouterModelInfo: modelInfoSchema.optional(),
322+
openRouterModelInfo: modelInfoSchema.nullish(),
323323
openRouterBaseUrl: z.string().optional(),
324324
openRouterSpecificProvider: z.string().optional(),
325325
openRouterUseMiddleOutTransform: z.boolean().optional(),
@@ -344,7 +344,7 @@ export const providerSettingsSchema = z.object({
344344
openAiApiKey: z.string().optional(),
345345
openAiR1FormatEnabled: z.boolean().optional(),
346346
openAiModelId: z.string().optional(),
347-
openAiCustomModelInfo: modelInfoSchema.optional(),
347+
openAiCustomModelInfo: modelInfoSchema.nullish(),
348348
openAiUseAzure: z.boolean().optional(),
349349
azureApiVersion: z.string().optional(),
350350
openAiStreamingEnabled: z.boolean().optional(),
@@ -379,11 +379,11 @@ export const providerSettingsSchema = z.object({
379379
// Unbound
380380
unboundApiKey: z.string().optional(),
381381
unboundModelId: z.string().optional(),
382-
unboundModelInfo: modelInfoSchema.optional(),
382+
unboundModelInfo: modelInfoSchema.nullish(),
383383
// Requesty
384384
requestyApiKey: z.string().optional(),
385385
requestyModelId: z.string().optional(),
386-
requestyModelInfo: modelInfoSchema.optional(),
386+
requestyModelInfo: modelInfoSchema.nullish(),
387387
// Claude 3.7 Sonnet Thinking
388388
modelTemperature: z.number().nullish(),
389389
modelMaxTokens: z.number().optional(),

webview-ui/src/components/settings/ApiOptions.tsx

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import { Checkbox } from "vscrui"
88
import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
99
import { ExternalLinkIcon } from "@radix-ui/react-icons"
1010

11-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectSeparator, Button } from "@/components/ui"
12-
1311
import {
1412
ApiConfiguration,
1513
ModelInfo,
@@ -42,56 +40,23 @@ import {
4240
import { ExtensionMessage } from "../../../../src/shared/ExtensionMessage"
4341

4442
import { vscode } from "@/utils/vscode"
43+
import { validateApiConfiguration, validateModelId, validateBedrockArn } from "@/utils/validate"
4544
import {
4645
useOpenRouterModelProviders,
4746
OPENROUTER_DEFAULT_PROVIDER_NAME,
4847
} from "@/components/ui/hooks/useOpenRouterModelProviders"
49-
import { useOpenRouterKeyInfo } from "@/components/ui/hooks/useOpenRouterKeyInfo"
50-
import { useRequestyKeyInfo } from "@/components/ui/hooks/useRequestyKeyInfo"
48+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectSeparator, Button } from "@/components/ui"
49+
5150
import { MODELS_BY_PROVIDER, PROVIDERS, AWS_REGIONS, VERTEX_REGIONS } from "./constants"
5251
import { VSCodeButtonLink } from "../common/VSCodeButtonLink"
5352
import { ModelInfoView } from "./ModelInfoView"
5453
import { ModelPicker } from "./ModelPicker"
5554
import { TemperatureControl } from "./TemperatureControl"
56-
import { validateApiConfiguration, validateModelId, validateBedrockArn } from "@/utils/validate"
5755
import { ApiErrorMessage } from "./ApiErrorMessage"
5856
import { ThinkingBudget } from "./ThinkingBudget"
5957
import { R1FormatSetting } from "./R1FormatSetting"
60-
61-
// Component to display OpenRouter API key balance
62-
const OpenRouterBalanceDisplay = ({ apiKey, baseUrl }: { apiKey: string; baseUrl?: string }) => {
63-
const { data: keyInfo } = useOpenRouterKeyInfo(apiKey, baseUrl)
64-
65-
if (!keyInfo || !keyInfo.limit) {
66-
return null
67-
}
68-
69-
const formattedBalance = (keyInfo.limit - keyInfo.usage).toFixed(2)
70-
71-
return (
72-
<VSCodeLink href="https://openrouter.ai/settings/keys" className="text-vscode-foreground hover:underline">
73-
${formattedBalance}
74-
</VSCodeLink>
75-
)
76-
}
77-
78-
const RequestyBalanceDisplay = ({ apiKey }: { apiKey: string }) => {
79-
const { data: keyInfo } = useRequestyKeyInfo(apiKey)
80-
81-
if (!keyInfo) {
82-
return null
83-
}
84-
85-
// Parse the balance to a number and format it to 2 decimal places
86-
const balance = parseFloat(keyInfo.org_balance)
87-
const formattedBalance = balance.toFixed(2)
88-
89-
return (
90-
<VSCodeLink href="https://app.requesty.ai/settings" className="text-vscode-foreground hover:underline">
91-
${formattedBalance}
92-
</VSCodeLink>
93-
)
94-
}
58+
import { OpenRouterBalanceDisplay } from "./OpenRouterBalanceDisplay"
59+
import { RequestyBalanceDisplay } from "./RequestyBalanceDisplay"
9560

9661
interface ApiOptionsProps {
9762
uriScheme: string | undefined

0 commit comments

Comments
 (0)