Skip to content

Commit f1cd4d7

Browse files
authored
Merge pull request #3376 from matchai/feat/vercel-ai-gateway
feat(provider): add Vercel AI Gateway support
2 parents 680d05a + e3b5c2b commit f1cd4d7

22 files changed

+967
-112
lines changed

src/cli/__snapshots__/model-fallback.test.ts.snap

Lines changed: 584 additions & 0 deletions
Large diffs are not rendered by default.

src/cli/cli-installer.telemetry.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ describe("runCliInstaller telemetry isolation", () => {
2222
hasZaiCodingPlan: false,
2323
hasKimiForCoding: false,
2424
hasOpencodeGo: false,
25+
hasVercelAiGateway: false,
2526
}),
2627
spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(true),
2728
spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.4.0"),

src/cli/cli-installer.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ describe("runCliInstaller", () => {
3737
hasZaiCodingPlan: false,
3838
hasKimiForCoding: false,
3939
hasOpencodeGo: false,
40+
hasVercelAiGateway: false,
4041
}),
4142
spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(true),
4243
spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.3.9"),
@@ -83,6 +84,7 @@ describe("runCliInstaller", () => {
8384
hasZaiCodingPlan: false,
8485
hasKimiForCoding: false,
8586
hasOpencodeGo: false,
87+
hasVercelAiGateway: false,
8688
}),
8789
spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(true),
8890
spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.4.0"),

src/cli/cli-installer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi
138138
!config.hasOpenAI &&
139139
!config.hasGemini &&
140140
!config.hasCopilot &&
141-
!config.hasOpencodeZen
141+
!config.hasOpencodeZen &&
142+
!config.hasVercelAiGateway
142143
) {
143144
printWarning("No model providers configured. Using opencode/big-pickle as fallback.")
144145
}

src/cli/cli-program.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,23 @@ program
3333
.option("--zai-coding-plan <value>", "Z.ai Coding Plan subscription: no, yes (default: no)")
3434
.option("--kimi-for-coding <value>", "Kimi For Coding subscription: no, yes (default: no)")
3535
.option("--opencode-go <value>", "OpenCode Go subscription: no, yes (default: no)")
36+
.option("--vercel-ai-gateway <value>", "Vercel AI Gateway: no, yes (default: no)")
3637
.option("--skip-auth", "Skip authentication setup hints")
3738
.addHelpText("after", `
3839
Examples:
3940
$ bunx oh-my-opencode install
4041
$ bunx oh-my-opencode install --no-tui --claude=max20 --openai=yes --gemini=yes --copilot=no
4142
$ bunx oh-my-opencode install --no-tui --claude=no --gemini=no --copilot=yes --opencode-zen=yes
4243
43-
Model Providers (Priority: Native > Copilot > OpenCode Zen > Z.ai > Kimi):
44+
Model Providers (Priority: Native > Copilot > OpenCode Zen > Z.ai > Kimi > Vercel):
4445
Claude Native anthropic/ models (Opus, Sonnet, Haiku)
4546
OpenAI Native openai/ models (GPT-5.4 for Oracle)
4647
Gemini Native google/ models (Gemini 3.1 Pro, Flash)
4748
Copilot github-copilot/ models (fallback)
4849
OpenCode Zen opencode/ models (opencode/claude-opus-4-6, etc.)
49-
Z.ai zai-coding-plan/glm-5 (visual-engineering fallback)
50+
Z.ai zai-coding-plan/glm-5 (visual-engineering fallback)
5051
Kimi kimi-for-coding/k2p5 (Sisyphus/Prometheus fallback)
52+
Vercel vercel/ models (universal proxy, always last fallback)
5153
`)
5254
.action(async (options) => {
5355
const args: InstallArgs = {
@@ -60,6 +62,7 @@ Model Providers (Priority: Native > Copilot > OpenCode Zen > Z.ai > Kimi):
6062
zaiCodingPlan: options.zaiCodingPlan,
6163
kimiForCoding: options.kimiForCoding,
6264
opencodeGo: options.opencodeGo,
65+
vercelAiGateway: options.vercelAiGateway,
6366
skipAuth: options.skipAuth ?? false,
6467
}
6568
const exitCode = await install(args)

src/cli/config-manager/detect-current-config.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ function detectProvidersFromOmoConfig(): {
1212
hasZaiCodingPlan: boolean
1313
hasKimiForCoding: boolean
1414
hasOpencodeGo: boolean
15+
hasVercelAiGateway: boolean
1516
} {
1617
const omoConfigPath = getOmoConfigPath()
1718
if (!existsSync(omoConfigPath)) {
@@ -21,6 +22,7 @@ function detectProvidersFromOmoConfig(): {
2122
hasZaiCodingPlan: false,
2223
hasKimiForCoding: false,
2324
hasOpencodeGo: false,
25+
hasVercelAiGateway: false,
2426
}
2527
}
2628

@@ -34,6 +36,7 @@ function detectProvidersFromOmoConfig(): {
3436
hasZaiCodingPlan: false,
3537
hasKimiForCoding: false,
3638
hasOpencodeGo: false,
39+
hasVercelAiGateway: false,
3740
}
3841
}
3942

@@ -43,15 +46,17 @@ function detectProvidersFromOmoConfig(): {
4346
const hasZaiCodingPlan = configStr.includes('"zai-coding-plan/')
4447
const hasKimiForCoding = configStr.includes('"kimi-for-coding/')
4548
const hasOpencodeGo = configStr.includes('"opencode-go/')
49+
const hasVercelAiGateway = configStr.includes('"vercel/')
4650

47-
return { hasOpenAI, hasOpencodeZen, hasZaiCodingPlan, hasKimiForCoding, hasOpencodeGo }
51+
return { hasOpenAI, hasOpencodeZen, hasZaiCodingPlan, hasKimiForCoding, hasOpencodeGo, hasVercelAiGateway }
4852
} catch {
4953
return {
5054
hasOpenAI: true,
5155
hasOpencodeZen: true,
5256
hasZaiCodingPlan: false,
5357
hasKimiForCoding: false,
5458
hasOpencodeGo: false,
59+
hasVercelAiGateway: false,
5560
}
5661
}
5762
}
@@ -78,6 +83,7 @@ export function detectCurrentConfig(): DetectedConfig {
7883
hasZaiCodingPlan: false,
7984
hasKimiForCoding: false,
8085
hasOpencodeGo: false,
86+
hasVercelAiGateway: false,
8187
}
8288

8389
const { format, path } = detectConfigFormat()
@@ -106,12 +112,13 @@ export function detectCurrentConfig(): DetectedConfig {
106112
const providers = openCodeConfig.provider as Record<string, unknown> | undefined
107113
result.hasGemini = providers ? "google" in providers : false
108114

109-
const { hasOpenAI, hasOpencodeZen, hasZaiCodingPlan, hasKimiForCoding, hasOpencodeGo } = detectProvidersFromOmoConfig()
115+
const { hasOpenAI, hasOpencodeZen, hasZaiCodingPlan, hasKimiForCoding, hasOpencodeGo, hasVercelAiGateway } = detectProvidersFromOmoConfig()
110116
result.hasOpenAI = hasOpenAI
111117
result.hasOpencodeZen = hasOpencodeZen
112118
result.hasZaiCodingPlan = hasZaiCodingPlan
113119
result.hasKimiForCoding = hasKimiForCoding
114120
result.hasOpencodeGo = hasOpencodeGo
121+
result.hasVercelAiGateway = hasVercelAiGateway
115122

116123
return result
117124
}

src/cli/config-manager/generate-omo-config.test.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ describe("generateOmoConfig - model fallback system", () => {
1818
hasZaiCodingPlan: false,
1919
hasKimiForCoding: false,
2020
hasOpencodeGo: false,
21+
hasVercelAiGateway: false,
2122
}
2223

2324
//#when
@@ -42,6 +43,7 @@ describe("generateOmoConfig - model fallback system", () => {
4243
hasZaiCodingPlan: false,
4344
hasKimiForCoding: false,
4445
hasOpencodeGo: false,
46+
hasVercelAiGateway: false,
4547
}
4648

4749
//#when
@@ -64,14 +66,15 @@ describe("generateOmoConfig - model fallback system", () => {
6466
hasZaiCodingPlan: true,
6567
hasKimiForCoding: false,
6668
hasOpencodeGo: false,
69+
hasVercelAiGateway: false,
6770
}
6871

6972
//#when
7073
const result = generateOmoConfig(config)
7174

7275
//#then
7376
expect((result.agents as Record<string, { model: string }>).librarian.model).toBe("zai-coding-plan/glm-4.7")
74-
expect((result.agents as Record<string, { model: string }>).sisyphus.model).toBe("anthropic/claude-opus-4-6")
77+
expect((result.agents as Record<string, { model: string }>).sisyphus.model).toBe("anthropic/claude-opus-4.6")
7578
})
7679

7780
test("uses native OpenAI models when only ChatGPT available", () => {
@@ -86,6 +89,7 @@ describe("generateOmoConfig - model fallback system", () => {
8689
hasZaiCodingPlan: false,
8790
hasKimiForCoding: false,
8891
hasOpencodeGo: false,
92+
hasVercelAiGateway: false,
8993
}
9094

9195
//#when
@@ -110,6 +114,7 @@ describe("generateOmoConfig - model fallback system", () => {
110114
hasZaiCodingPlan: false,
111115
hasKimiForCoding: false,
112116
hasOpencodeGo: false,
117+
hasVercelAiGateway: false,
113118
}
114119

115120
//#when
@@ -126,7 +131,7 @@ describe("generateOmoConfig - model fallback system", () => {
126131
}>
127132

128133
//#then
129-
expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4-6")
134+
expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4.6")
130135
expect(agents.sisyphus.fallback_models).toEqual([
131136
{
132137
model: "openai/gpt-5.4",
@@ -136,7 +141,7 @@ describe("generateOmoConfig - model fallback system", () => {
136141
expect(categories.deep.model).toBe("openai/gpt-5.4")
137142
expect(categories.deep.fallback_models).toEqual([
138143
{
139-
model: "anthropic/claude-opus-4-6",
144+
model: "anthropic/claude-opus-4.6",
140145
variant: "max",
141146
},
142147
])
@@ -154,6 +159,7 @@ describe("generateOmoConfig - model fallback system", () => {
154159
hasZaiCodingPlan: false,
155160
hasKimiForCoding: false,
156161
hasOpencodeGo: false,
162+
hasVercelAiGateway: false,
157163
}
158164

159165
//#when
@@ -175,6 +181,7 @@ describe("generateOmoConfig - model fallback system", () => {
175181
hasZaiCodingPlan: false,
176182
hasKimiForCoding: false,
177183
hasOpencodeGo: false,
184+
hasVercelAiGateway: false,
178185
}
179186

180187
//#when

src/cli/config-manager/write-omo-config.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const installConfig: InstallConfig = {
2020
hasZaiCodingPlan: false,
2121
hasKimiForCoding: false,
2222
hasOpencodeGo: false,
23+
hasVercelAiGateway: false,
2324
}
2425

2526
function getRecord(value: unknown): Record<string, unknown> {

src/cli/install-validators.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export function formatConfigSummary(config: InstallConfig): string {
4040
lines.push(formatProvider("OpenCode Zen", config.hasOpencodeZen, "opencode/ models"))
4141
lines.push(formatProvider("Z.ai Coding Plan", config.hasZaiCodingPlan, "Librarian/Multimodal"))
4242
lines.push(formatProvider("Kimi For Coding", config.hasKimiForCoding, "Sisyphus/Prometheus fallback"))
43+
lines.push(formatProvider("Vercel AI Gateway", config.hasVercelAiGateway, "universal proxy"))
4344

4445
lines.push("")
4546
lines.push(color.dim("─".repeat(40)))
@@ -153,6 +154,10 @@ export function validateNonTuiArgs(args: InstallArgs): { valid: boolean; errors:
153154
errors.push(`Invalid --kimi-for-coding value: ${args.kimiForCoding} (expected: no, yes)`)
154155
}
155156

157+
if (args.vercelAiGateway !== undefined && !["no", "yes"].includes(args.vercelAiGateway)) {
158+
errors.push(`Invalid --vercel-ai-gateway value: ${args.vercelAiGateway} (expected: no, yes)`)
159+
}
160+
156161
return { valid: errors.length === 0, errors }
157162
}
158163

@@ -167,6 +172,7 @@ export function argsToConfig(args: InstallArgs): InstallConfig {
167172
hasZaiCodingPlan: args.zaiCodingPlan === "yes",
168173
hasKimiForCoding: args.kimiForCoding === "yes",
169174
hasOpencodeGo: args.opencodeGo === "yes",
175+
hasVercelAiGateway: args.vercelAiGateway === "yes",
170176
}
171177
}
172178

@@ -179,6 +185,7 @@ export function detectedToInitialValues(detected: DetectedConfig): {
179185
zaiCodingPlan: BooleanArg
180186
kimiForCoding: BooleanArg
181187
opencodeGo: BooleanArg
188+
vercelAiGateway: BooleanArg
182189
} {
183190
let claude: ClaudeSubscription = "no"
184191
if (detected.hasClaude) {
@@ -194,5 +201,6 @@ kimiForCoding: BooleanArg
194201
zaiCodingPlan: detected.hasZaiCodingPlan ? "yes" : "no",
195202
kimiForCoding: detected.hasKimiForCoding ? "yes" : "no",
196203
opencodeGo: detected.hasOpencodeGo ? "yes" : "no",
204+
vercelAiGateway: detected.hasVercelAiGateway ? "yes" : "no",
197205
}
198206
}

src/cli/model-fallback-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface ProviderAvailability {
1111
zai: boolean
1212
kimiForCoding: boolean
1313
opencodeGo: boolean
14+
vercelAiGateway: boolean
1415
isMaxPlan: boolean
1516
}
1617

0 commit comments

Comments
 (0)