Files
oh-my-pi/packages/ai/src/providers/google-vertex.ts
T
roboomp 8fc200f6e8 fix(providers): discovered vertex project models
Added Google Vertex OpenAI-compatible model discovery with ADC auth and treated authoritative Vertex project catalogs as replacements for bundled Gemini fallbacks in the model registry.

Fixes #1412
2026-05-26 16:34:53 +00:00

92 lines
3.2 KiB
TypeScript

import { $env } from "@oh-my-pi/pi-utils";
import type { Context, Model, StreamFunction } from "../types";
import type { AssistantMessageEventStream } from "../utils/event-stream";
import { getVertexAccessToken } from "./google-auth";
import {
buildGoogleGenerateContentParams,
type GoogleGenAIRequestPlan,
type GoogleSharedStreamOptions,
streamGoogleGenAI,
} from "./google-shared";
export interface GoogleVertexOptions extends GoogleSharedStreamOptions {
project?: string;
location?: string;
}
const API_VERSION = "v1";
export const streamGoogleVertex: StreamFunction<"google-vertex"> = (
model: Model<"google-vertex">,
context: Context,
options?: GoogleVertexOptions,
): AssistantMessageEventStream =>
streamGoogleGenAI({
model,
options,
api: "google-vertex",
retainTextSignature: true,
prepare: async (): Promise<GoogleGenAIRequestPlan> => {
const apiKey = resolveApiKey(options);
const params = buildGoogleGenerateContentParams(model, context, options ?? {});
const baseHeaders: Record<string, string> = {
...(model.headers ?? {}),
...(options?.headers ?? {}),
};
if (apiKey) {
const url = `https://aiplatform.googleapis.com/${API_VERSION}/publishers/google/models/${model.id}:streamGenerateContent?alt=sse`;
return {
params,
url,
headers: { ...baseHeaders, "x-goog-api-key": apiKey },
fetch: options?.fetch,
};
}
const project = resolveProject(options);
const location = resolveLocation(options);
const accessToken = await getVertexAccessToken({ signal: options?.signal, fetch: options?.fetch });
const host = resolveEndpointHost(location);
const url = `https://${host}/${API_VERSION}/projects/${project}/locations/${location}/publishers/google/models/${model.id}:streamGenerateContent?alt=sse`;
return {
params,
url,
headers: { ...baseHeaders, Authorization: `Bearer ${accessToken}` },
fetch: options?.fetch,
};
},
});
function resolveApiKey(options?: GoogleVertexOptions): string | undefined {
// options.apiKey may contain sentinel values like "<authenticated>" or "N/A"
// leaked from the agent loop — only use it if it looks like a real API key.
const optKey = options?.apiKey;
const realKey = optKey && !optKey.startsWith("<") && optKey !== "N/A" ? optKey : undefined;
return realKey || $env.GOOGLE_CLOUD_API_KEY;
}
function resolveProject(options?: GoogleVertexOptions): string {
const project = options?.project || $env.GOOGLE_CLOUD_PROJECT || $env.GCP_PROJECT || $env.GCLOUD_PROJECT;
if (!project) {
throw new Error(
"Vertex AI requires a project ID. Set GOOGLE_CLOUD_PROJECT/GCP_PROJECT/GCLOUD_PROJECT or pass project in options.",
);
}
return project;
}
function resolveEndpointHost(location: string): string {
return location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com`;
}
function resolveLocation(options?: GoogleVertexOptions): string {
const location =
options?.location || $env.GOOGLE_VERTEX_LOCATION || $env.GOOGLE_CLOUD_LOCATION || $env.VERTEX_LOCATION;
if (!location) {
throw new Error(
"Vertex AI requires a location. Set GOOGLE_VERTEX_LOCATION/GOOGLE_CLOUD_LOCATION/VERTEX_LOCATION or pass location in options.",
);
}
return location;
}