feat: add openai-compatible endpoint support for google-vertex provider (#10303)
Co-authored-by: BlueT - Matthew Lien - 練喆明 <BlueT@BlueT.org> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
This commit is contained in:
@@ -57,6 +57,39 @@ export namespace Provider {
|
||||
return isGpt5OrLater(modelID) && !modelID.startsWith("gpt-5-mini")
|
||||
}
|
||||
|
||||
function googleVertexVars(options: Record<string, any>) {
|
||||
const project =
|
||||
Env.get("GOOGLE_VERTEX_PROJECT") ??
|
||||
options["project"] ??
|
||||
Env.get("GOOGLE_CLOUD_PROJECT") ??
|
||||
Env.get("GCP_PROJECT") ??
|
||||
Env.get("GCLOUD_PROJECT")
|
||||
const location =
|
||||
Env.get("GOOGLE_VERTEX_LOCATION") ??
|
||||
options["location"] ??
|
||||
Env.get("GOOGLE_CLOUD_LOCATION") ??
|
||||
Env.get("VERTEX_LOCATION") ??
|
||||
"us-central1"
|
||||
const endpoint =
|
||||
Env.get("GOOGLE_VERTEX_ENDPOINT") ??
|
||||
(location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com`)
|
||||
return {
|
||||
GOOGLE_VERTEX_PROJECT: project,
|
||||
GOOGLE_VERTEX_LOCATION: location,
|
||||
GOOGLE_VERTEX_ENDPOINT: endpoint,
|
||||
}
|
||||
}
|
||||
|
||||
function loadBaseURL(model: Model, options: Record<string, any>) {
|
||||
const raw = options["baseURL"] ?? model.api.url
|
||||
if (typeof raw !== "string") return raw
|
||||
const vars = model.providerID === "google-vertex" ? googleVertexVars(options) : undefined
|
||||
return raw.replace(/\$\{([^}]+)\}/g, (match, key) => {
|
||||
const val = Env.get(String(key)) ?? vars?.[String(key) as keyof typeof vars]
|
||||
return val ?? match
|
||||
})
|
||||
}
|
||||
|
||||
const BUNDLED_PROVIDERS: Record<string, (options: any) => SDK> = {
|
||||
"@ai-sdk/amazon-bedrock": createAmazonBedrock,
|
||||
"@ai-sdk/anthropic": createAnthropic,
|
||||
@@ -353,9 +386,16 @@ export namespace Provider {
|
||||
},
|
||||
}
|
||||
},
|
||||
"google-vertex": async () => {
|
||||
const project = Env.get("GOOGLE_CLOUD_PROJECT") ?? Env.get("GCP_PROJECT") ?? Env.get("GCLOUD_PROJECT")
|
||||
const location = Env.get("GOOGLE_CLOUD_LOCATION") ?? Env.get("VERTEX_LOCATION") ?? "us-east5"
|
||||
"google-vertex": async (provider) => {
|
||||
const project =
|
||||
provider.options?.project ??
|
||||
Env.get("GOOGLE_CLOUD_PROJECT") ??
|
||||
Env.get("GCP_PROJECT") ??
|
||||
Env.get("GCLOUD_PROJECT")
|
||||
|
||||
const location =
|
||||
provider.options?.location ?? Env.get("GOOGLE_CLOUD_LOCATION") ?? Env.get("VERTEX_LOCATION") ?? "us-central1"
|
||||
|
||||
const autoload = Boolean(project)
|
||||
if (!autoload) return { autoload: false }
|
||||
return {
|
||||
@@ -363,6 +403,18 @@ export namespace Provider {
|
||||
options: {
|
||||
project,
|
||||
location,
|
||||
fetch: async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const { GoogleAuth } = await import(await BunProc.install("google-auth-library"))
|
||||
const auth = new GoogleAuth()
|
||||
const client = await auth.getApplicationDefault()
|
||||
const credentials = await client.credential
|
||||
const token = await credentials.getAccessToken()
|
||||
|
||||
const headers = new Headers(init?.headers)
|
||||
headers.set("Authorization", `Bearer ${token.token}`)
|
||||
|
||||
return fetch(input, { ...init, headers })
|
||||
},
|
||||
},
|
||||
async getModel(sdk: any, modelID: string) {
|
||||
const id = String(modelID).trim()
|
||||
@@ -994,11 +1046,16 @@ export namespace Provider {
|
||||
const provider = s.providers[model.providerID]
|
||||
const options = { ...provider.options }
|
||||
|
||||
if (model.providerID === "google-vertex" && !model.api.npm.includes("@ai-sdk/openai-compatible")) {
|
||||
delete options.fetch
|
||||
}
|
||||
|
||||
if (model.api.npm.includes("@ai-sdk/openai-compatible") && options["includeUsage"] !== false) {
|
||||
options["includeUsage"] = true
|
||||
}
|
||||
|
||||
if (!options["baseURL"]) options["baseURL"] = model.api.url
|
||||
const baseURL = loadBaseURL(model, options)
|
||||
if (baseURL !== undefined) options["baseURL"] = baseURL
|
||||
if (options["apiKey"] === undefined && provider.key) options["apiKey"] = provider.key
|
||||
if (model.headers)
|
||||
options["headers"] = {
|
||||
|
||||
@@ -2127,3 +2127,95 @@ test("custom model with variants enabled and disabled", async () => {
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("Google Vertex: retains baseURL for custom proxy", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
await Bun.write(
|
||||
path.join(dir, "opencode.json"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
provider: {
|
||||
"vertex-proxy": {
|
||||
name: "Vertex Proxy",
|
||||
npm: "@ai-sdk/google-vertex",
|
||||
api: "https://my-proxy.com/v1",
|
||||
env: ["GOOGLE_APPLICATION_CREDENTIALS"], // Mock env var requirement
|
||||
models: {
|
||||
"gemini-pro": {
|
||||
name: "Gemini Pro",
|
||||
tool_call: true,
|
||||
},
|
||||
},
|
||||
options: {
|
||||
project: "test-project",
|
||||
location: "us-central1",
|
||||
baseURL: "https://my-proxy.com/v1", // Should be retained
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
init: async () => {
|
||||
Env.set("GOOGLE_APPLICATION_CREDENTIALS", "test-creds")
|
||||
},
|
||||
fn: async () => {
|
||||
const providers = await Provider.list()
|
||||
expect(providers["vertex-proxy"]).toBeDefined()
|
||||
expect(providers["vertex-proxy"].options.baseURL).toBe("https://my-proxy.com/v1")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("Google Vertex: supports OpenAI compatible models", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
await Bun.write(
|
||||
path.join(dir, "opencode.json"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
provider: {
|
||||
"vertex-openai": {
|
||||
name: "Vertex OpenAI",
|
||||
npm: "@ai-sdk/google-vertex",
|
||||
env: ["GOOGLE_APPLICATION_CREDENTIALS"],
|
||||
models: {
|
||||
"gpt-4": {
|
||||
name: "GPT-4",
|
||||
provider: {
|
||||
npm: "@ai-sdk/openai-compatible",
|
||||
api: "https://api.openai.com/v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
options: {
|
||||
project: "test-project",
|
||||
location: "us-central1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
init: async () => {
|
||||
Env.set("GOOGLE_APPLICATION_CREDENTIALS", "test-creds")
|
||||
},
|
||||
fn: async () => {
|
||||
const providers = await Provider.list()
|
||||
const model = providers["vertex-openai"].models["gpt-4"]
|
||||
|
||||
expect(model).toBeDefined()
|
||||
expect(model.api.npm).toBe("@ai-sdk/openai-compatible")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user