-
Notifications
You must be signed in to change notification settings - Fork 220
refactor(types): canonicalize shared provider settings identifiers #1109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -428,40 +428,40 @@ const defaultSchema = z.object({ | |
| }) | ||
|
|
||
| export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProvider", [ | ||
| anthropicSchema.merge(z.object({ apiProvider: z.literal("anthropic") })), | ||
| openRouterSchema.merge(z.object({ apiProvider: z.literal("openrouter") })), | ||
| bedrockSchema.merge(z.object({ apiProvider: z.literal("bedrock") })), | ||
| vertexSchema.merge(z.object({ apiProvider: z.literal("vertex") })), | ||
| openAiSchema.merge(z.object({ apiProvider: z.literal("openai") })), | ||
| ollamaSchema.merge(z.object({ apiProvider: z.literal("ollama") })), | ||
| vsCodeLmSchema.merge(z.object({ apiProvider: z.literal("vscode-lm") })), | ||
| lmStudioSchema.merge(z.object({ apiProvider: z.literal("lmstudio") })), | ||
| geminiSchema.merge(z.object({ apiProvider: z.literal("gemini") })), | ||
| geminiCliSchema.merge(z.object({ apiProvider: z.literal("gemini-cli") })), | ||
| openAiCodexSchema.merge(z.object({ apiProvider: z.literal("openai-codex") })), | ||
| openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })), | ||
| mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })), | ||
| deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })), | ||
| poeSchema.merge(z.object({ apiProvider: z.literal("poe") })), | ||
| moonshotSchema.merge(z.object({ apiProvider: z.literal("moonshot") })), | ||
| kimiCodeSchema.merge(z.object({ apiProvider: z.literal("kimi-code") })), | ||
| minimaxSchema.merge(z.object({ apiProvider: z.literal("minimax") })), | ||
| mimoSchema.merge(z.object({ apiProvider: z.literal("mimo") })), | ||
| requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })), | ||
| unboundSchema.merge(z.object({ apiProvider: z.literal("unbound") })), | ||
| fakeAiSchema.merge(z.object({ apiProvider: z.literal("fake-ai") })), | ||
| xaiSchema.merge(z.object({ apiProvider: z.literal("xai") })), | ||
| basetenSchema.merge(z.object({ apiProvider: z.literal("baseten") })), | ||
| litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })), | ||
| sambaNovaSchema.merge(z.object({ apiProvider: z.literal("sambanova") })), | ||
| zaiSchema.merge(z.object({ apiProvider: z.literal("zai") })), | ||
| fireworksSchema.merge(z.object({ apiProvider: z.literal("fireworks") })), | ||
| friendliSchema.merge(z.object({ apiProvider: z.literal("friendli") })), | ||
| qwenCodeSchema.merge(z.object({ apiProvider: z.literal("qwen-code") })), | ||
| vercelAiGatewaySchema.merge(z.object({ apiProvider: z.literal("vercel-ai-gateway") })), | ||
| opencodeGoSchema.merge(z.object({ apiProvider: z.literal("opencode-go") })), | ||
| kenariSchema.merge(z.object({ apiProvider: z.literal("kenari") })), | ||
| zooGatewaySchema.merge(z.object({ apiProvider: z.literal("zoo-gateway") })), | ||
| anthropicSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.anthropic) })), | ||
| openRouterSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.openrouter) })), | ||
| bedrockSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.bedrock) })), | ||
| vertexSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.vertex) })), | ||
| openAiSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.openai) })), | ||
| ollamaSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.ollama) })), | ||
| vsCodeLmSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.vscodeLm) })), | ||
| lmStudioSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.lmstudio) })), | ||
| geminiSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.gemini) })), | ||
| geminiCliSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.geminiCli) })), | ||
| openAiCodexSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.openaiCodex) })), | ||
| openAiNativeSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.openaiNative) })), | ||
| mistralSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.mistral) })), | ||
| deepSeekSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.deepseek) })), | ||
| poeSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.poe) })), | ||
| moonshotSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.moonshot) })), | ||
| kimiCodeSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.kimiCode) })), | ||
| minimaxSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.minimax) })), | ||
| mimoSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.mimo) })), | ||
| requestySchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.requesty) })), | ||
| unboundSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.unbound) })), | ||
| fakeAiSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.fakeAi) })), | ||
| xaiSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.xai) })), | ||
| basetenSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.baseten) })), | ||
| litellmSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.litellm) })), | ||
| sambaNovaSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.sambanova) })), | ||
| zaiSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.zai) })), | ||
| fireworksSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.fireworks) })), | ||
| friendliSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.friendli) })), | ||
| qwenCodeSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.qwenCode) })), | ||
| vercelAiGatewaySchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.vercelAiGateway) })), | ||
| opencodeGoSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.opencodeGo) })), | ||
| kenariSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.kenari) })), | ||
| zooGatewaySchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.zooGateway) })), | ||
| defaultSchema, | ||
| ]) | ||
|
|
||
|
|
@@ -553,37 +553,37 @@ export const isTypicalProvider = (key: unknown): key is TypicalProvider => | |
| isProviderName(key) && !isInternalProvider(key) && !isCustomProvider(key) && !isFauxProvider(key) | ||
|
|
||
| export const modelIdKeysByProvider: Record<TypicalProvider, ModelIdKey> = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The keys here are type-checked against |
||
| anthropic: "apiModelId", | ||
| openrouter: "openRouterModelId", | ||
| bedrock: "apiModelId", | ||
| vertex: "apiModelId", | ||
| "openai-codex": "apiModelId", | ||
| "openai-native": "openAiModelId", | ||
| ollama: "ollamaModelId", | ||
| lmstudio: "lmStudioModelId", | ||
| gemini: "apiModelId", | ||
| "gemini-cli": "apiModelId", | ||
| mistral: "apiModelId", | ||
| moonshot: "apiModelId", | ||
| "kimi-code": "apiModelId", | ||
| minimax: "apiModelId", | ||
| mimo: "apiModelId", | ||
| deepseek: "apiModelId", | ||
| poe: "apiModelId", | ||
| "qwen-code": "apiModelId", | ||
| requesty: "requestyModelId", | ||
| unbound: "unboundModelId", | ||
| xai: "apiModelId", | ||
| baseten: "apiModelId", | ||
| litellm: "litellmModelId", | ||
| sambanova: "apiModelId", | ||
| zai: "apiModelId", | ||
| fireworks: "apiModelId", | ||
| friendli: "apiModelId", | ||
| "vercel-ai-gateway": "vercelAiGatewayModelId", | ||
| "opencode-go": "opencodeGoModelId", | ||
| kenari: "kenariModelId", | ||
| "zoo-gateway": "zooGatewayModelId", | ||
| [providerIdentifiers.anthropic]: "apiModelId", | ||
| [providerIdentifiers.openrouter]: "openRouterModelId", | ||
| [providerIdentifiers.bedrock]: "apiModelId", | ||
| [providerIdentifiers.vertex]: "apiModelId", | ||
| [providerIdentifiers.openaiCodex]: "apiModelId", | ||
| [providerIdentifiers.openaiNative]: "openAiModelId", | ||
| [providerIdentifiers.ollama]: "ollamaModelId", | ||
| [providerIdentifiers.lmstudio]: "lmStudioModelId", | ||
| [providerIdentifiers.gemini]: "apiModelId", | ||
| [providerIdentifiers.geminiCli]: "apiModelId", | ||
| [providerIdentifiers.mistral]: "apiModelId", | ||
| [providerIdentifiers.moonshot]: "apiModelId", | ||
| [providerIdentifiers.kimiCode]: "apiModelId", | ||
| [providerIdentifiers.minimax]: "apiModelId", | ||
| [providerIdentifiers.mimo]: "apiModelId", | ||
| [providerIdentifiers.deepseek]: "apiModelId", | ||
| [providerIdentifiers.poe]: "apiModelId", | ||
| [providerIdentifiers.qwenCode]: "apiModelId", | ||
| [providerIdentifiers.requesty]: "requestyModelId", | ||
| [providerIdentifiers.unbound]: "unboundModelId", | ||
| [providerIdentifiers.xai]: "apiModelId", | ||
| [providerIdentifiers.baseten]: "apiModelId", | ||
| [providerIdentifiers.litellm]: "litellmModelId", | ||
| [providerIdentifiers.sambanova]: "apiModelId", | ||
| [providerIdentifiers.zai]: "apiModelId", | ||
| [providerIdentifiers.fireworks]: "apiModelId", | ||
| [providerIdentifiers.friendli]: "apiModelId", | ||
| [providerIdentifiers.vercelAiGateway]: "vercelAiGatewayModelId", | ||
| [providerIdentifiers.opencodeGo]: "opencodeGoModelId", | ||
| [providerIdentifiers.kenari]: "kenariModelId", | ||
| [providerIdentifiers.zooGateway]: "zooGatewayModelId", | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -653,106 +653,125 @@ export const getApiProtocol = (provider: ProviderName | undefined, modelId?: str | |
| */ | ||
|
|
||
| export const MODELS_BY_PROVIDER: Record< | ||
| Exclude<ProviderName, "fake-ai" | "gemini-cli" | "openai">, | ||
| Exclude< | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could use a one-line comment on why these three are excluded ( |
||
| ProviderName, | ||
| typeof providerIdentifiers.fakeAi | typeof providerIdentifiers.geminiCli | typeof providerIdentifiers.openai | ||
| >, | ||
| { id: ProviderName; label: string; models: string[] } | ||
| > = { | ||
| anthropic: { | ||
| id: "anthropic", | ||
| [providerIdentifiers.anthropic]: { | ||
| id: providerIdentifiers.anthropic, | ||
| label: "Anthropic", | ||
| models: Object.keys(anthropicModels), | ||
| }, | ||
| bedrock: { | ||
| id: "bedrock", | ||
| [providerIdentifiers.bedrock]: { | ||
| id: providerIdentifiers.bedrock, | ||
| label: "Amazon Bedrock", | ||
| models: Object.keys(bedrockModels), | ||
| }, | ||
| deepseek: { | ||
| id: "deepseek", | ||
| [providerIdentifiers.deepseek]: { | ||
| id: providerIdentifiers.deepseek, | ||
| label: "DeepSeek", | ||
| models: Object.keys(deepSeekModels), | ||
| }, | ||
| fireworks: { | ||
| id: "fireworks", | ||
| [providerIdentifiers.fireworks]: { | ||
| id: providerIdentifiers.fireworks, | ||
| label: "Fireworks", | ||
| models: Object.keys(fireworksModels), | ||
| }, | ||
| friendli: { | ||
| id: "friendli", | ||
| [providerIdentifiers.friendli]: { | ||
| id: providerIdentifiers.friendli, | ||
| label: "Friendli", | ||
| models: Object.keys(friendliModels), | ||
| }, | ||
| gemini: { | ||
| id: "gemini", | ||
| [providerIdentifiers.gemini]: { | ||
| id: providerIdentifiers.gemini, | ||
| label: "Google Gemini", | ||
| models: Object.keys(geminiModels), | ||
| }, | ||
| mistral: { | ||
| id: "mistral", | ||
| [providerIdentifiers.mistral]: { | ||
| id: providerIdentifiers.mistral, | ||
| label: "Mistral", | ||
| models: Object.keys(mistralModels), | ||
| }, | ||
| moonshot: { | ||
| id: "moonshot", | ||
| [providerIdentifiers.moonshot]: { | ||
| id: providerIdentifiers.moonshot, | ||
| label: "Moonshot", | ||
| models: Object.keys(moonshotModels), | ||
| }, | ||
| "kimi-code": { | ||
| id: "kimi-code", | ||
| [providerIdentifiers.kimiCode]: { | ||
| id: providerIdentifiers.kimiCode, | ||
| label: "Kimi Code", | ||
| models: [], | ||
| }, | ||
| minimax: { | ||
| id: "minimax", | ||
| [providerIdentifiers.minimax]: { | ||
| id: providerIdentifiers.minimax, | ||
| label: "MiniMax", | ||
| models: Object.keys(minimaxModels), | ||
| }, | ||
| mimo: { | ||
| id: "mimo", | ||
| [providerIdentifiers.mimo]: { | ||
| id: providerIdentifiers.mimo, | ||
| label: "Xiaomi MiMo", | ||
| models: Object.keys(mimoModels), | ||
| }, | ||
| "openai-codex": { | ||
| id: "openai-codex", | ||
| [providerIdentifiers.openaiCodex]: { | ||
| id: providerIdentifiers.openaiCodex, | ||
| label: "OpenAI - ChatGPT Plus/Pro", | ||
| models: Object.keys(openAiCodexModels), | ||
| }, | ||
| "openai-native": { | ||
| id: "openai-native", | ||
| [providerIdentifiers.openaiNative]: { | ||
| id: providerIdentifiers.openaiNative, | ||
| label: "OpenAI", | ||
| models: Object.keys(openAiNativeModels), | ||
| }, | ||
| "qwen-code": { id: "qwen-code", label: "Qwen Code", models: Object.keys(qwenCodeModels) }, | ||
| sambanova: { | ||
| id: "sambanova", | ||
| [providerIdentifiers.qwenCode]: { | ||
| id: providerIdentifiers.qwenCode, | ||
| label: "Qwen Code", | ||
| models: Object.keys(qwenCodeModels), | ||
| }, | ||
| [providerIdentifiers.sambanova]: { | ||
| id: providerIdentifiers.sambanova, | ||
| label: "SambaNova", | ||
| models: Object.keys(sambaNovaModels), | ||
| }, | ||
| vertex: { | ||
| id: "vertex", | ||
| [providerIdentifiers.vertex]: { | ||
| id: providerIdentifiers.vertex, | ||
| label: "GCP Vertex AI", | ||
| models: Object.keys(vertexModels), | ||
| }, | ||
| "vscode-lm": { | ||
| id: "vscode-lm", | ||
| [providerIdentifiers.vscodeLm]: { | ||
| id: providerIdentifiers.vscodeLm, | ||
| label: "VS Code LM API", | ||
| models: Object.keys(vscodeLlmModels), | ||
| }, | ||
| xai: { id: "xai", label: "xAI (Grok)", models: Object.keys(xaiModels) }, | ||
| zai: { id: "zai", label: "Z.ai", models: Object.keys(internationalZAiModels) }, | ||
| baseten: { id: "baseten", label: "Baseten", models: Object.keys(basetenModels) }, | ||
| [providerIdentifiers.xai]: { id: providerIdentifiers.xai, label: "xAI (Grok)", models: Object.keys(xaiModels) }, | ||
| [providerIdentifiers.zai]: { | ||
| id: providerIdentifiers.zai, | ||
| label: "Z.ai", | ||
| models: Object.keys(internationalZAiModels), | ||
| }, | ||
| [providerIdentifiers.baseten]: { | ||
| id: providerIdentifiers.baseten, | ||
| label: "Baseten", | ||
| models: Object.keys(basetenModels), | ||
| }, | ||
|
|
||
| // Dynamic providers; models pulled from remote APIs. | ||
| poe: { id: "poe", label: "Poe", models: [] }, | ||
| litellm: { id: "litellm", label: "LiteLLM", models: [] }, | ||
| openrouter: { id: "openrouter", label: "OpenRouter", models: [] }, | ||
| requesty: { id: "requesty", label: "Requesty", models: [] }, | ||
| unbound: { id: "unbound", label: "Unbound", models: [] }, | ||
| "vercel-ai-gateway": { id: "vercel-ai-gateway", label: "Vercel AI Gateway", models: [] }, | ||
| "opencode-go": { id: "opencode-go", label: "Opencode Go", models: [] }, | ||
| kenari: { id: "kenari", label: "Kenari", models: [] }, | ||
| "zoo-gateway": { id: "zoo-gateway", label: "Zoo Gateway", models: [] }, | ||
| [providerIdentifiers.poe]: { id: providerIdentifiers.poe, label: "Poe", models: [] }, | ||
| [providerIdentifiers.litellm]: { id: providerIdentifiers.litellm, label: "LiteLLM", models: [] }, | ||
| [providerIdentifiers.openrouter]: { id: providerIdentifiers.openrouter, label: "OpenRouter", models: [] }, | ||
| [providerIdentifiers.requesty]: { id: providerIdentifiers.requesty, label: "Requesty", models: [] }, | ||
| [providerIdentifiers.unbound]: { id: providerIdentifiers.unbound, label: "Unbound", models: [] }, | ||
| [providerIdentifiers.vercelAiGateway]: { | ||
| id: providerIdentifiers.vercelAiGateway, | ||
| label: "Vercel AI Gateway", | ||
| models: [], | ||
| }, | ||
| [providerIdentifiers.opencodeGo]: { id: providerIdentifiers.opencodeGo, label: "Opencode Go", models: [] }, | ||
| [providerIdentifiers.kenari]: { id: providerIdentifiers.kenari, label: "Kenari", models: [] }, | ||
| [providerIdentifiers.zooGateway]: { id: providerIdentifiers.zooGateway, label: "Zoo Gateway", models: [] }, | ||
|
|
||
| // Local providers; models discovered from localhost endpoints. | ||
| lmstudio: { id: "lmstudio", label: "LM Studio", models: [] }, | ||
| ollama: { id: "ollama", label: "Ollama", models: [] }, | ||
| [providerIdentifiers.lmstudio]: { id: providerIdentifiers.lmstudio, label: "LM Studio", models: [] }, | ||
| [providerIdentifiers.ollama]: { id: providerIdentifiers.ollama, label: "Ollama", models: [] }, | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since every discriminator here now comes from
providerIdentifiersrather than an inline literal, a typo (e.g. picking the wrong registry key) would compile fine and only 32 of the 34 branches are unparsed by any test in the repo — onlyopenaiCodexandkimi-codeget a round-trip throughproviderSettingsSchemaDiscriminated. Worth adding a loop overObject.values(providerIdentifiers)that assertssafeParsesucceeds for each?