refactor(ai): import AIProvider instead of redeclaring it - #80
Merged
Vrun-design merged 1 commit intoSep 20, 2026
Merged
Conversation
`AIProvider` was declared twice: the canonical union in `src/store/types.ts` and an independent copy in `aiService.ts`. Neither imported the other, so adding a provider to one and not the other compiled cleanly and failed at runtime instead — `getEnvApiKey` would return undefined, or `resolveOpenAICompatibleBaseUrl` would throw `Unknown provider`. Import the canonical type and re-export it to keep the module surface unchanged. Verified by temporarily adding a fake provider to the union: tsc now reports errors across aiProviders.ts, aiSettings.ts and aiSettingsSchemas.ts, where previously aiService.ts stayed silent.
Vrun-design
approved these changes
Sep 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
AIProvideris declared twice, independently:src/store/types.ts:41— the canonical union, imported by the store, settings UI, hooks and zod schemassrc/services/aiService.ts:17— a standalone re-declaration of the same ten string literalsNeither imports the other, so they're unrelated types that happen to match today. Add a provider to one and not the other and TypeScript stays silent — both remain valid string-literal unions. The failure surfaces at runtime instead:
getEnvApiKey()falls through toundefined, orresolveOpenAICompatibleBaseUrl()throwsUnknown provider.Latent right now (both list the same ten providers), but it gets more likely with every provider added.
Changes
One file, two lines —
src/services/aiService.tsimports the canonical type and re-exports it:The re-export keeps the module's public surface unchanged. Nothing currently imports
AIProviderfromaiService(consumers all use@/storeor./types; onlyChatMessagecomes fromaiService), so a plain import would also compile — happy to drop the re-export if you'd prefer the smaller surface.Test plan
npx tsc --noEmit— cleannpm run lint— cleannpm test— 295 files, 1445 tests passingDrift regression check — temporarily added a fake
'testprovider'to the canonical union:aiService.tsreported nothing; a provider missing from its switch compiled finetscreports 7 errors acrossaiProviders.ts(all fiveRecord<AIProvider, …>maps),aiSettings.tsandaiSettingsSchemas.tsTemporary edit reverted; no behavior change, pure type-level fix.
Notes for reviewers
The two runtime provider lists were already protected and need no change —
AI_PROVIDERSinaiSettings.tsusessatisfies readonly AIProvider[], andaiProviderSchemainaiSettingsSchemas.tsusessatisfies [AIProvider, ...AIProvider[]].aiService.tswas the only unguarded copy.One gap this doesn't close:
getEnvApiKey's switch hasdefault: return undefined, so it isn't exhaustiveness-checked and won't itself error on an unknown provider. The fix works because the shared type propagates into the guarded maps. Tightening that switch would be a separate change — happy to include it if you'd like.