fix(vertex): Vertex (Antropic) provider: use .rep.googleapis.com for continental multi-region endpoints (us, eu) (#28347)

Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
This commit is contained in:
JPFrancoia 2026-05-22 05:03:24 +01:00 committed by GitHub
parent 3e931152d1
commit 7a9724496b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 182 additions and 10 deletions

View file

@ -85,6 +85,13 @@ function wrapSSE(res: Response, ms: number, ctl: AbortController) {
})
}
function googleVertexAnthropicBaseURL(project: string | undefined, location: string | undefined) {
if (!project) return
if (location !== "eu" && location !== "us") return
// Continental multi-regions require Regional Endpoint Platform domains.
return `https://aiplatform.${location}.rep.googleapis.com/v1/projects/${project}/locations/${location}/publishers/anthropic/models`
}
type BundledSDK = {
languageModel(modelId: string): LanguageModelV3
}
@ -507,11 +514,13 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
const location = env["GOOGLE_CLOUD_LOCATION"] ?? env["VERTEX_LOCATION"] ?? "global"
const autoload = Boolean(project)
if (!autoload) return { autoload: false }
const baseURL = googleVertexAnthropicBaseURL(project, location)
return {
autoload: true,
options: {
project,
location,
...(baseURL && { baseURL }),
},
async getModel(sdk: any, modelID) {
const id = String(modelID).trim()
@ -1516,6 +1525,18 @@ export const layer = Layer.effect(
const provider = s.providers[model.providerID]
const options = { ...provider.options }
if (
model.providerID === "google-vertex" &&
model.api.npm === "@ai-sdk/google-vertex/anthropic" &&
!options.baseURL
) {
const baseURL = googleVertexAnthropicBaseURL(
typeof options.project === "string" ? options.project : undefined,
typeof options.location === "string" ? options.location : undefined,
)
if (baseURL) options.baseURL = baseURL
}
if (model.providerID === "google-vertex" && !model.api.npm.includes("@ai-sdk/openai-compatible")) {
delete options.fetch
}

View file

@ -73,6 +73,8 @@ const paid = (providers: Record<string, { models: Record<string, { cost: { input
return Object.values(item.models).filter((model) => model.cost.input > 0).length
}
const languageBaseURL = (language: unknown) => (language as { config: { baseURL: string } }).config.baseURL
const it = testEffect(Layer.mergeAll(Provider.defaultLayer, Env.defaultLayer, Plugin.defaultLayer))
const experimentalModels = testEffect(providerLayer({ enableExperimentalModels: true }))
@ -1546,6 +1548,54 @@ it.instance(
},
)
it.instance("Google Vertex: uses REP endpoint for Claude continental multi-regions", () =>
Effect.gen(function* () {
yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
yield* set("VERTEX_LOCATION", "eu")
const provider = yield* Provider.Service
const model = yield* provider.getModel(
ProviderID.make("google-vertex"),
ModelID.make("claude-sonnet-4-6@default"),
)
const language = yield* provider.getLanguage(model)
expect(languageBaseURL(language)).toBe(
"https://aiplatform.eu.rep.googleapis.com/v1/projects/test-project/locations/eu/publishers/anthropic/models",
)
}),
)
it.instance("Google Vertex Anthropic: uses REP endpoint for continental multi-regions", () =>
Effect.gen(function* () {
yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
yield* set("VERTEX_LOCATION", "us")
const provider = yield* Provider.Service
const model = yield* provider.getModel(
ProviderID.make("google-vertex-anthropic"),
ModelID.make("claude-sonnet-4-6@default"),
)
const language = yield* provider.getLanguage(model)
expect(languageBaseURL(language)).toBe(
"https://aiplatform.us.rep.googleapis.com/v1/projects/test-project/locations/us/publishers/anthropic/models",
)
}),
)
it.instance("Google Vertex: keeps regional Claude endpoints unchanged", () =>
Effect.gen(function* () {
yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
yield* set("VERTEX_LOCATION", "europe-west1")
const provider = yield* Provider.Service
const model = yield* provider.getModel(
ProviderID.make("google-vertex"),
ModelID.make("claude-sonnet-4-6@default"),
)
const language = yield* provider.getLanguage(model)
expect(languageBaseURL(language)).toBe(
"https://europe-west1-aiplatform.googleapis.com/v1/projects/test-project/locations/europe-west1/publishers/anthropic/models",
)
}),
)
it.instance("cloudflare-ai-gateway loads with env variables", () =>
Effect.gen(function* () {
yield* set("CLOUDFLARE_ACCOUNT_ID", "test-account")