refactor(ai): separate Vertex API routes (#37275)

This commit is contained in:
Shoubhit Dash 2026-07-16 17:17:15 +05:30 committed by GitHub
commit 282f3f7eb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 120 additions and 126 deletions

View file

@ -11,7 +11,7 @@ import * as Cloudflare from "../src/providers/cloudflare"
import * as GitHubCopilot from "../src/providers/github-copilot"
import * as Google from "../src/providers/google"
import * as GoogleVertex from "../src/providers/google-vertex"
import * as GoogleVertexAnthropic from "../src/providers/google-vertex-anthropic"
import * as GoogleVertexMessages from "../src/providers/google-vertex-messages"
import * as OpenAI from "../src/providers/openai"
import * as OpenAICompatible from "../src/providers/openai-compatible"
import * as OpenRouter from "../src/providers/openrouter"
@ -171,20 +171,20 @@ GoogleVertex.configure({ accessToken: "vertex-token", apiKey: "vertex-key", proj
// @ts-expect-error Vertex Gemini package settings accept only one auth source.
GoogleVertex.model("gemini-3.5-flash", { accessToken: "vertex-token", apiKey: "vertex-key", project: "project" })
GoogleVertexAnthropic.configure({ accessToken: "vertex-token", project: "project" }).model("claude-sonnet-4-6")
// @ts-expect-error Vertex Anthropic package settings do not accept API keys.
GoogleVertexAnthropic.model("claude-sonnet-4-6", { apiKey: "vertex-key", project: "project" })
GoogleVertexAnthropic.configure({ auth: RuntimeAuth.bearer("vertex-token"), project: "project" }).model(
GoogleVertexMessages.configure({ accessToken: "vertex-token", project: "project" }).model("claude-sonnet-4-6")
// @ts-expect-error Vertex Messages package settings do not accept API keys.
GoogleVertexMessages.model("claude-sonnet-4-6", { apiKey: "vertex-key", project: "project" })
GoogleVertexMessages.configure({ auth: RuntimeAuth.bearer("vertex-token"), project: "project" }).model(
"claude-sonnet-4-6",
)
GoogleVertexAnthropic.configure({ accessToken: "vertex-token", project: "project" }).model(
GoogleVertexMessages.configure({ accessToken: "vertex-token", project: "project" }).model(
"claude-sonnet-4-6",
// @ts-expect-error Vertex Anthropic model selectors only accept model ids.
// @ts-expect-error Vertex Messages model selectors only accept model ids.
{},
)
GoogleVertexAnthropic.configure({
GoogleVertexMessages.configure({
accessToken: "vertex-token",
// @ts-expect-error Vertex Anthropic config accepts only one auth source.
// @ts-expect-error Vertex Messages config accepts only one auth source.
auth: RuntimeAuth.bearer("vertex-token"),
project: "project",
})

View file

@ -17,12 +17,14 @@ describe("provider package entrypoints", () => {
import("@opencode-ai/ai/providers/azure/chat"),
import("@opencode-ai/ai/providers/google"),
import("@opencode-ai/ai/providers/google-vertex"),
import("@opencode-ai/ai/providers/google-vertex/anthropic"),
import("@opencode-ai/ai/providers/google-vertex/gemini"),
import("@opencode-ai/ai/providers/google-vertex/messages"),
])
for (const module of modules) expect(module.model).toBeFunction()
expect(modules[0].model).toBe(modules[1].model)
expect(modules[8].model).toBe(modules[9].model)
expect(modules[12].model).toBe(modules[13].model)
})
test("maps package settings onto the executable model", () => {
@ -179,20 +181,23 @@ describe("provider package entrypoints", () => {
test("selects Vertex entrypoints with the same model contract", async () => {
const GoogleVertex = await import("@opencode-ai/ai/providers/google-vertex")
const GoogleVertexAnthropic = await import("@opencode-ai/ai/providers/google-vertex/anthropic")
const GoogleVertexGemini = await import("@opencode-ai/ai/providers/google-vertex/gemini")
const GoogleVertexMessages = await import("@opencode-ai/ai/providers/google-vertex/messages")
const gemini = GoogleVertex.model("gemini-3.5-flash", {
apiKey: "fixture",
headers: { "x-application": "opencode" },
body: { safetySettings: [] },
limits: { context: 1_000_000, output: 65_536 },
})
const anthropic = GoogleVertexAnthropic.model("claude-sonnet-4-6", {
const messages = GoogleVertexMessages.model("claude-sonnet-4-6", {
accessToken: "fixture",
location: "global",
project: "vertex-project",
})
expect(GoogleVertexGemini.model).toBe(GoogleVertex.model)
expect(gemini.route.id).toBe("google-vertex-gemini")
expect(gemini.route.protocol).toBe("gemini")
expect(gemini.route.endpoint.baseURL).toBe("https://aiplatform.googleapis.com/v1/publishers/google")
expect(gemini.route.defaults.headers).toEqual({ "x-application": "opencode" })
expect(gemini.route.defaults.http?.body).toEqual({ safetySettings: [] })
@ -204,15 +209,16 @@ describe("provider package entrypoints", () => {
project: "vertex-project",
}).route.endpoint.baseURL,
).toBe("https://aiplatform.eu.rep.googleapis.com/v1beta1/projects/vertex-project/locations/eu/publishers/google")
expect(anthropic.route.id).toBe("google-vertex-anthropic")
expect(anthropic.route.endpoint.baseURL).toBe(
expect(messages.route.id).toBe("google-vertex-messages")
expect(messages.route.protocol).toBe("anthropic-messages")
expect(messages.route.endpoint.baseURL).toBe(
"https://aiplatform.googleapis.com/v1/projects/vertex-project/locations/global/publishers/anthropic/models",
)
})
test("rejects conflicting Vertex auth settings at runtime", async () => {
const GoogleVertex = await import("@opencode-ai/ai/providers/google-vertex")
const GoogleVertexAnthropic = await import("@opencode-ai/ai/providers/google-vertex/anthropic")
const GoogleVertexMessages = await import("@opencode-ai/ai/providers/google-vertex/messages")
const Providers = await import("@opencode-ai/ai/providers")
expect(() =>
Reflect.apply(GoogleVertex.model, undefined, [
@ -225,15 +231,15 @@ describe("provider package entrypoints", () => {
])
expect(() => configured.model("gemini-3.5-flash")).toThrow("Google Vertex accessToken cannot be combined with auth")
expect(() =>
Reflect.apply(GoogleVertexAnthropic.model, undefined, [
Reflect.apply(GoogleVertexMessages.model, undefined, [
"claude-sonnet-4-6",
{ apiKey: "fixture", project: "vertex-project" },
]),
).toThrow("Google Vertex Anthropic does not support API keys")
).toThrow("Google Vertex Messages does not support API keys")
expect(() =>
Reflect.apply(Providers.GoogleVertexAnthropic.configure, undefined, [
Reflect.apply(Providers.GoogleVertexMessages.configure, undefined, [
{ apiKey: "fixture", project: "vertex-project" },
]),
).toThrow("Google Vertex Anthropic does not support API keys")
).toThrow("Google Vertex Messages does not support API keys")
})
})

View file

@ -2,7 +2,7 @@ import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { HttpClientRequest } from "effect/unstable/http"
import { LLM } from "../../src"
import { GoogleVertex, GoogleVertexAnthropic } from "../../src/providers"
import { GoogleVertex, GoogleVertexMessages } from "../../src/providers"
import { LLMClient } from "../../src/route"
import { it } from "../lib/effect"
import { dynamicResponse } from "../lib/http"
@ -56,7 +56,7 @@ describe("Google Vertex providers", () => {
Effect.gen(function* () {
const response = yield* LLMClient.generate(
LLM.request({
model: GoogleVertexAnthropic.configure({
model: GoogleVertexMessages.configure({
accessToken: "vertex-token",
location: "eu",
project: "vertex-project",
@ -99,11 +99,11 @@ describe("Google Vertex providers", () => {
}),
)
it.effect("protects the Vertex Anthropic API version from body overlays", () =>
it.effect("protects the Vertex Messages API version from body overlays", () =>
Effect.gen(function* () {
const error = yield* LLMClient.prepare(
LLM.request({
model: GoogleVertexAnthropic.configure({
model: GoogleVertexMessages.configure({
accessToken: "vertex-token",
http: { body: { anthropic_version: "wrong" } },
project: "vertex-project",