Parse providers individually (#2611)

* Parse providers individually

* Remove .strict()

* Clean up tests
This commit is contained in:
Chris Estreich 2025-04-14 14:51:53 -07:00 • committed by GitHub
parent b196489b97
commit 6f8f8c6a1d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 66 additions and 8 deletions

View file

@ -1,7 +1,7 @@
import { ExtensionContext } from "vscode"
import { z, ZodError } from "zod"
import { providerSettingsSchema, ApiConfigMeta } from "../../schemas"
import { providerSettingsSchema, ApiConfigMeta, ProviderSettings } from "../../schemas"
import { Mode, modes } from "../../shared/modes"
import { telemetryService } from "../../services/telemetry/TelemetryService"
@ -115,20 +115,15 @@ export class ProviderSettingsManager {
}
if (rateLimitSeconds === undefined) {
// Failed to get the existing value, use the default
// Failed to get the existing value, use the default.
rateLimitSeconds = 0
}
for (const [name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) {
if (apiConfig.rateLimitSeconds === undefined) {
console.log(
`[MigrateRateLimitSeconds] Applying rate limit ${rateLimitSeconds}s to profile: ${name}`,
)
apiConfig.rateLimitSeconds = rateLimitSeconds
}
}
console.log(`[MigrateRateLimitSeconds] migration complete`)
} catch (error) {
console.error(`[MigrateRateLimitSeconds] Failed to migrate rate limit settings:`, error)
}
@ -321,7 +316,31 @@ export class ProviderSettingsManager {
private async load(): Promise<ProviderProfiles> {
try {
const content = await this.context.secrets.get(this.secretsKey)
return content ? providerProfilesSchema.parse(JSON.parse(content)) : this.defaultProviderProfiles
if (!content) {
return this.defaultProviderProfiles
}
const providerProfiles = providerProfilesSchema
.extend({
apiConfigs: z.record(z.string(), z.any()),
})
.parse(JSON.parse(content))
const apiConfigs = Object.entries(providerProfiles.apiConfigs).reduce(
(acc, [key, apiConfig]) => {
const result = providerSettingsWithIdSchema.safeParse(apiConfig)
return result.success ? { ...acc, [key]: result.data } : acc
},
{} as Record<string, ProviderSettingsWithId>,
)
return {
...providerProfiles,
apiConfigs: Object.fromEntries(
Object.entries(apiConfigs).filter(([_, apiConfig]) => apiConfig !== null),
),
}
} catch (error) {
if (error instanceof ZodError) {
telemetryService.captureSchemaValidationError({ schemaName: "ProviderProfiles", error })

View file

@ -437,6 +437,45 @@ describe("ProviderSettingsManager", () => {
"Failed to load config: Error: Failed to write provider profiles to secrets: Error: Storage failed",
)
})
it("should remove invalid profiles during load", async () => {
const invalidConfig = {
currentApiConfigName: "valid",
apiConfigs: {
valid: {
apiProvider: "anthropic",
apiKey: "valid-key",
apiModelId: "claude-3-opus-20240229",
rateLimitSeconds: 0,
},
invalid: {
// Invalid API provider.
id: "x.ai",
apiProvider: "x.ai",
},
// Incorrect type.
anotherInvalid: "not an object",
},
migrations: {
rateLimitSecondsMigrated: true,
},
}
mockSecrets.get.mockResolvedValue(JSON.stringify(invalidConfig))
await providerSettingsManager.initialize()
const storeCalls = mockSecrets.store.mock.calls
expect(storeCalls.length).toBeGreaterThan(0) // Ensure store was called at least once.
const finalStoredConfigJson = storeCalls[storeCalls.length - 1][1]
const storedConfig = JSON.parse(finalStoredConfigJson)
expect(storedConfig.apiConfigs.valid).toBeDefined()
expect(storedConfig.apiConfigs.invalid).toBeUndefined()
expect(storedConfig.apiConfigs.anotherInvalid).toBeUndefined()
expect(Object.keys(storedConfig.apiConfigs)).toEqual(["valid"])
expect(storedConfig.currentApiConfigName).toBe("valid")
})
})
describe("ResetAllConfigs", () => {