From ff5be41bf7ca00e60740a01c19ccfe8a48426440 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sun, 1 Nov 2026 21:21:46 -0600 Subject: [PATCH] refactor(ai): share the Vertex OAuth route setup (#61160) --- .../ai/src/providers/google-vertex-chat.ts | 51 ++++------------ .../src/providers/google-vertex-messages.ts | 52 +++++----------- .../ai/src/providers/google-vertex-mistral.ts | 51 ++++------------ .../src/providers/google-vertex-responses.ts | 51 ++++------------ .../ai/src/providers/google-vertex-shared.ts | 61 +++++++++++++++++++ 5 files changed, 108 insertions(+), 158 deletions(-) diff --git a/packages/ai/src/providers/google-vertex-chat.ts b/packages/ai/src/providers/google-vertex-chat.ts index b47a26b57ae8..53526463688e 100644 --- a/packages/ai/src/providers/google-vertex-chat.ts +++ b/packages/ai/src/providers/google-vertex-chat.ts @@ -2,7 +2,7 @@ import type { ProviderPackage } from "../provider-package.js" import { OpenAIChat } from "../protocols/openai-chat.js" import { Route, type RouteDefaultsInput } from "../route/client.js" import { Endpoint } from "../route/endpoint.js" -import { ProviderConfigurationError, ProviderID, type ModelID } from "../schema/index.js" +import { ProviderID, type ModelID } from "../schema/index.js" import { GoogleVertexShared } from "./google-vertex-shared.js" import type { OpenAIProviderOptionsInput } from "./openai-options.js" @@ -36,29 +36,14 @@ const route = Route.make({ export const routes = [route] -const configuredRoute = (input: Config) => { - if ("apiKey" in input && input.apiKey !== undefined) - throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Chat does not support API keys" }) - const { - accessToken: _accessToken, - auth: _auth, - baseURL, - location: inputLocation, - project: inputProject, - ...rest - } = input - const location = GoogleVertexShared.location(inputLocation) - const project = GoogleVertexShared.project(inputProject) - return route.with({ - ...rest, - endpoint: { - baseURL: - baseURL ?? - `https://aiplatform.googleapis.com/v1/projects/${GoogleVertexShared.requireProject(project)}/locations/${location}/endpoints/openapi`, - }, - auth: GoogleVertexShared.oauth(input, project), - }) -} +const configuredRoute = (input: Config) => + GoogleVertexShared.oauthRoute( + route, + "Chat", + input, + (project, location) => + `https://aiplatform.googleapis.com/v1/projects/${project}/locations/${location}/endpoints/openapi`, + ) export const configure = (input: Config = {}) => { const route = configuredRoute(input) @@ -74,19 +59,5 @@ export const provider = { configure, } -export const model: ProviderPackage.Model = ( - modelID, - { accessToken, apiKey, baseURL, body, headers, location, project, ...providerOptions }: Settings = {}, -) => { - if (apiKey !== undefined) - throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Chat does not support API keys" }) - return configure({ - accessToken, - baseURL, - headers, - http: body === undefined ? undefined : { body }, - location, - project, - providerOptions, - }).model(modelID) -} +export const model: ProviderPackage.Model = (modelID, settings = {}) => + configure(GoogleVertexShared.oauthSettings("Chat", settings)).model(modelID) diff --git a/packages/ai/src/providers/google-vertex-messages.ts b/packages/ai/src/providers/google-vertex-messages.ts index c5dbb22ece9b..43d72f567f13 100644 --- a/packages/ai/src/providers/google-vertex-messages.ts +++ b/packages/ai/src/providers/google-vertex-messages.ts @@ -5,7 +5,7 @@ import { Auth } from "../route/auth.js" import { Route, type RouteDefaultsInput } from "../route/client.js" import { Endpoint } from "../route/endpoint.js" import { Protocol } from "../route/protocol.js" -import { ProviderConfigurationError, ProviderID, type ModelID } from "../schema/index.js" +import { ProviderID, type ModelID } from "../schema/index.js" import { GoogleVertexShared } from "./google-vertex-shared.js" export type AnthropicOptionsInput = AnthropicMessages.OptionsInput @@ -66,29 +66,14 @@ const route = Route.make({ export const routes = [route] -const configuredRoute = (input: Config) => { - if ("apiKey" in input && input.apiKey !== undefined) - throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Messages does not support API keys" }) - const { - accessToken: _accessToken, - auth: _auth, - baseURL, - location: inputLocation, - project: inputProject, - ...rest - } = input - const location = GoogleVertexShared.location(inputLocation) - const project = GoogleVertexShared.project(inputProject) - return route.with({ - ...rest, - endpoint: { - baseURL: - baseURL ?? - `https://${GoogleVertexShared.host(location)}/v1/projects/${GoogleVertexShared.requireProject(project)}/locations/${location}/publishers/anthropic/models`, - }, - auth: GoogleVertexShared.oauth(input, project), - }) -} +const configuredRoute = (input: Config) => + GoogleVertexShared.oauthRoute( + route, + "Messages", + input, + (project, location) => + `https://${GoogleVertexShared.host(location)}/v1/projects/${project}/locations/${location}/publishers/anthropic/models`, + ) export const configure = (input: Config = {}) => { const route = configuredRoute(input) @@ -106,17 +91,8 @@ export const provider = { export const model: ProviderPackage.Model = ( modelID, - { accessToken, apiKey, baseURL, body, headers, location, project, ...providerOptions }: Settings = {}, -) => { - if (apiKey !== undefined) - throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Messages does not support API keys" }) - return configure({ - accessToken, - baseURL, - headers, - http: body === undefined ? undefined : { body }, - location, - project, - providerOptions, - }).model(modelID) -} + settings = {}, +) => + configure(GoogleVertexShared.oauthSettings("Messages", settings)).model( + modelID, + ) diff --git a/packages/ai/src/providers/google-vertex-mistral.ts b/packages/ai/src/providers/google-vertex-mistral.ts index e5db172b4d3c..09d1ff5d7515 100644 --- a/packages/ai/src/providers/google-vertex-mistral.ts +++ b/packages/ai/src/providers/google-vertex-mistral.ts @@ -6,7 +6,7 @@ import { Route, type RouteDefaultsInput } from "../route/client.js" import { Endpoint } from "../route/endpoint.js" import { Protocol } from "../route/protocol.js" import { HttpTransport } from "../route/transport/index.js" -import { ProviderConfigurationError, ProviderID, type ModelID } from "../schema/index.js" +import { ProviderID, type ModelID } from "../schema/index.js" import { GoogleVertexShared } from "./google-vertex-shared.js" export const id = ProviderID.make("google-vertex") @@ -52,29 +52,14 @@ const route = Route.make({ export const routes = [route] -const configuredRoute = (input: Config) => { - if ("apiKey" in input && input.apiKey !== undefined) - throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Mistral does not support API keys" }) - const { - accessToken: _accessToken, - auth: _auth, - baseURL, - location: inputLocation, - project: inputProject, - ...rest - } = input - const location = GoogleVertexShared.location(inputLocation) - const project = GoogleVertexShared.project(inputProject) - return route.with({ - ...rest, - endpoint: { - baseURL: - baseURL ?? - `https://${GoogleVertexShared.host(location)}/v1/projects/${GoogleVertexShared.requireProject(project)}/locations/${location}/publishers/mistralai/models`, - }, - auth: GoogleVertexShared.oauth(input, project), - }) -} +const configuredRoute = (input: Config) => + GoogleVertexShared.oauthRoute( + route, + "Mistral", + input, + (project, location) => + `https://${GoogleVertexShared.host(location)}/v1/projects/${project}/locations/${location}/publishers/mistralai/models`, + ) export const configure = (input: Config = {}) => { const route = configuredRoute(input) @@ -90,19 +75,5 @@ export const provider = { configure, } -export const model: ProviderPackage.Model = ( - modelID, - { accessToken, apiKey, baseURL, body, headers, location, project, ...providerOptions }: Settings = {}, -) => { - if (apiKey !== undefined) - throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Mistral does not support API keys" }) - return configure({ - accessToken, - baseURL, - headers, - http: body === undefined ? undefined : { body }, - location, - project, - providerOptions, - }).model(modelID) -} +export const model: ProviderPackage.Model = (modelID, settings = {}) => + configure(GoogleVertexShared.oauthSettings("Mistral", settings)).model(modelID) diff --git a/packages/ai/src/providers/google-vertex-responses.ts b/packages/ai/src/providers/google-vertex-responses.ts index f943054604fc..b86c840c7994 100644 --- a/packages/ai/src/providers/google-vertex-responses.ts +++ b/packages/ai/src/providers/google-vertex-responses.ts @@ -2,7 +2,7 @@ import type { ProviderPackage } from "../provider-package.js" import { OpenResponses } from "../protocols/open-responses.js" import { Route, type RouteDefaultsInput } from "../route/client.js" import { Endpoint } from "../route/endpoint.js" -import { ProviderConfigurationError, ProviderID, type ModelID } from "../schema/index.js" +import { ProviderID, type ModelID } from "../schema/index.js" import { GoogleVertexShared } from "./google-vertex-shared.js" import type { OpenResponsesProviderOptionsInput } from "./open-responses-options.js" @@ -37,29 +37,14 @@ const route = Route.make({ export const routes = [route] -const configuredRoute = (input: Config) => { - if ("apiKey" in input && input.apiKey !== undefined) - throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Responses does not support API keys" }) - const { - accessToken: _accessToken, - auth: _auth, - baseURL, - location: inputLocation, - project: inputProject, - ...rest - } = input - const location = GoogleVertexShared.location(inputLocation) - const project = GoogleVertexShared.project(inputProject) - return route.with({ - ...rest, - endpoint: { - baseURL: - baseURL ?? - `https://aiplatform.googleapis.com/v1/projects/${GoogleVertexShared.requireProject(project)}/locations/${location}/endpoints/openapi`, - }, - auth: GoogleVertexShared.oauth(input, project), - }) -} +const configuredRoute = (input: Config) => + GoogleVertexShared.oauthRoute( + route, + "Responses", + input, + (project, location) => + `https://aiplatform.googleapis.com/v1/projects/${project}/locations/${location}/endpoints/openapi`, + ) export const configure = (input: Config = {}) => { const route = configuredRoute(input) @@ -75,19 +60,5 @@ export const provider = { configure, } -export const model: ProviderPackage.Model = ( - modelID, - { accessToken, apiKey, baseURL, body, headers, location, project, ...providerOptions }: Settings = {}, -) => { - if (apiKey !== undefined) - throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Responses does not support API keys" }) - return configure({ - accessToken, - baseURL, - headers, - http: body === undefined ? undefined : { body }, - location, - project, - providerOptions, - }).model(modelID) -} +export const model: ProviderPackage.Model = (modelID, settings = {}) => + configure(GoogleVertexShared.oauthSettings("Responses", settings)).model(modelID) diff --git a/packages/ai/src/providers/google-vertex-shared.ts b/packages/ai/src/providers/google-vertex-shared.ts index 7588058687bc..fbf1572640d2 100644 --- a/packages/ai/src/providers/google-vertex-shared.ts +++ b/packages/ai/src/providers/google-vertex-shared.ts @@ -1,6 +1,8 @@ import type { AnyAuthClient, GoogleAuthOptions } from "google-auth-library" import { Effect, Redacted } from "effect" +import type { ProviderPackage } from "../provider-package.js" import { Auth, MissingCredentialError } from "../route/auth.js" +import type { CompactionOperations, Route, RouteDefaultsInput } from "../route/client.js" import { ProviderConfigurationError, ProviderID } from "../schema/index.js" const SCOPE = "https://www.googleapis.com/auth/cloud-platform" @@ -112,4 +114,63 @@ export const oauth = (input: OAuthOptions, project?: string) => { return adc(project) } +export const oauthRoute = ( + route: Route, + label: string, + input: RouteDefaultsInput & + OAuthOptions & { readonly baseURL?: string; readonly location?: string; readonly project?: string }, + url: (project: string, location: string) => string, +) => { + if ("apiKey" in input && input.apiKey !== undefined) + throw new ProviderConfigurationError({ provider: id, message: `Google Vertex ${label} does not support API keys` }) + const { + accessToken: _accessToken, + auth: _auth, + baseURL, + location: inputLocation, + project: inputProject, + ...rest + } = input + const resolvedLocation = location(inputLocation) + const resolvedProject = project(inputProject) + return route.with({ + ...rest, + endpoint: { baseURL: baseURL ?? url(requireProject(resolvedProject), resolvedLocation) }, + auth: oauth(input, resolvedProject), + }) +} + +export const oauthSettings = ( + label: string, + { + accessToken, + apiKey, + baseURL, + body, + headers, + location, + project, + ...providerOptions + }: ProviderPackage.Settings & + Options & { + readonly accessToken?: string + readonly apiKey?: never + readonly baseURL?: string + readonly location?: string + readonly project?: string + }, +) => { + if (apiKey !== undefined) + throw new ProviderConfigurationError({ provider: id, message: `Google Vertex ${label} does not support API keys` }) + return { + accessToken, + baseURL, + headers, + http: body === undefined ? undefined : { body }, + location, + project, + providerOptions, + } +} + export * as GoogleVertexShared from "./google-vertex-shared.js"