diff --git a/packages/core/lib/v3/types/public/agent.ts b/packages/core/lib/v3/types/public/agent.ts index 846ebe70e8..830fb1c966 100644 --- a/packages/core/lib/v3/types/public/agent.ts +++ b/packages/core/lib/v3/types/public/agent.ts @@ -445,7 +445,8 @@ export type AgentType = | "anthropic" | "google" | "microsoft" - | "bedrock"; + | "bedrock" + | "vertex"; export const AVAILABLE_CUA_MODELS = [ "openai/gpt-5.4", diff --git a/packages/core/lib/v3/types/public/api.ts b/packages/core/lib/v3/types/public/api.ts index 73b77ad52f..d5696852bc 100644 --- a/packages/core/lib/v3/types/public/api.ts +++ b/packages/core/lib/v3/types/public/api.ts @@ -56,10 +56,48 @@ export const LocalBrowserLaunchOptionsSchema = z .meta({ id: "LocalBrowserLaunchOptions" }); /** Detailed model configuration object */ +export const GoogleServiceAccountCredentialsSchema = z + .object({ + type: z.literal("service_account").optional(), + project_id: z.string().optional(), + private_key_id: z.string().optional(), + private_key: z.string(), + client_email: z.string(), + client_id: z.string().optional(), + auth_uri: z.url().optional(), + token_uri: z.url().optional(), + auth_provider_x509_cert_url: z.url().optional(), + client_x509_cert_url: z.url().optional(), + universe_domain: z.string().optional(), + }) + .strict() + .meta({ id: "GoogleServiceAccountCredentials" }); + +export const GoogleAuthOptionsSchema = z + .object({ + credentials: GoogleServiceAccountCredentialsSchema.optional().meta({ + description: "Google Cloud service account credentials", + }), + scopes: z + .union([z.string(), z.array(z.string())]) + .optional() + .meta({ + description: "Google auth scopes for the desired API request", + }), + projectId: z.string().optional().meta({ + description: "Google Cloud project ID used by google-auth-library", + }), + universeDomain: z.string().optional().meta({ + description: "Google Cloud universe domain", + }), + }) + .strict() + .meta({ id: "GoogleAuthOptions" }); + export const ModelConfigObjectSchema = z .object({ provider: z - .enum(["openai", "anthropic", "google", "microsoft", "bedrock"]) + .enum(["openai", "anthropic", "google", "microsoft", "bedrock", "vertex"]) .optional() .meta({ description: @@ -83,6 +121,18 @@ export const ModelConfigObjectSchema = z description: "Custom headers sent with every request to the model provider", }), + project: z.string().optional().meta({ + description: "Google Cloud project ID for Vertex AI models", + example: "my-gcp-project", + }), + location: z.string().optional().meta({ + description: "Google Cloud location for Vertex AI models", + example: "us-central1", + }), + googleAuthOptions: GoogleAuthOptionsSchema.optional().meta({ + description: + "google-auth-library options used to authenticate Vertex AI models", + }), }) .meta({ id: "ModelConfigObject" }); diff --git a/packages/core/lib/v3/types/public/model.ts b/packages/core/lib/v3/types/public/model.ts index 0bc88365a2..8e8e8a7e39 100644 --- a/packages/core/lib/v3/types/public/model.ts +++ b/packages/core/lib/v3/types/public/model.ts @@ -18,11 +18,11 @@ export type AnthropicClientOptions = Pick< >; export interface GoogleServiceAccountCredentials { - type?: string; + type?: "service_account"; project_id?: string; private_key_id?: string; - private_key?: string; - client_email?: string; + private_key: string; + client_email: string; client_id?: string; auth_uri?: string; token_uri?: string; @@ -31,13 +31,18 @@ export interface GoogleServiceAccountCredentials { universe_domain?: string; } +export interface GoogleVertexAuthOptions { + credentials?: GoogleServiceAccountCredentials; + scopes?: string | string[]; + projectId?: string; + universeDomain?: string; +} + export type GoogleVertexProviderSettings = Pick< GoogleVertexProviderSettingsBase, - "project" | "location" | "headers" + "project" | "location" | "headers" | "baseURL" > & { - googleAuthOptions?: { - credentials?: GoogleServiceAccountCredentials; - }; + googleAuthOptions?: GoogleVertexAuthOptions; }; export type AnthropicJsonSchemaObject = { diff --git a/packages/core/tests/unit/api-variables-schema.test.ts b/packages/core/tests/unit/api-variables-schema.test.ts index ecd33723f5..39364520de 100644 --- a/packages/core/tests/unit/api-variables-schema.test.ts +++ b/packages/core/tests/unit/api-variables-schema.test.ts @@ -62,3 +62,133 @@ describe("API variable schemas", () => { }); }); }); + +describe("API model config schemas", () => { + const vertexModel = { + provider: "vertex", + modelName: "vertex/gemini-2.5-flash", + project: "test-gcp-project", + location: "us-central1", + googleAuthOptions: { + credentials: { + type: "service_account", + project_id: "test-gcp-project", + private_key_id: "test-key-id", + client_email: "vertex@example.iam.gserviceaccount.com", + private_key: + "-----BEGIN PRIVATE KEY-----\\ntest\\n-----END PRIVATE KEY-----", + token_uri: "https://oauth2.googleapis.com/token", + }, + scopes: ["https://www.googleapis.com/auth/cloud-platform"], + projectId: "test-gcp-project", + universeDomain: "googleapis.com", + }, + }; + + it("preserves Vertex auth params for act requests", () => { + const result = Api.ActRequestSchema.safeParse({ + input: "click the search button", + options: { + model: vertexModel, + }, + }); + + expect(result.success).toBe(true); + if (!result.success) throw result.error; + expect(result.data.options?.model).toEqual(vertexModel); + }); + + it("accepts minimal Vertex service account credentials", () => { + const result = Api.ActRequestSchema.safeParse({ + input: "click the search button", + options: { + model: { + provider: "vertex", + modelName: "vertex/gemini-2.5-flash", + project: "test-gcp-project", + location: "us-central1", + googleAuthOptions: { + credentials: { + client_email: "vertex@example.iam.gserviceaccount.com", + private_key: + "-----BEGIN PRIVATE KEY-----\\ntest\\n-----END PRIVATE KEY-----", + }, + }, + }, + }, + }); + + expect(result.success).toBe(true); + if (!result.success) throw result.error; + const parsedModel = result.data.options?.model; + expect(typeof parsedModel).toBe("object"); + if (typeof parsedModel !== "object" || parsedModel === null) { + throw new Error("Expected object model config"); + } + expect(parsedModel.googleAuthOptions).toEqual({ + credentials: { + client_email: "vertex@example.iam.gserviceaccount.com", + private_key: + "-----BEGIN PRIVATE KEY-----\\ntest\\n-----END PRIVATE KEY-----", + }, + }); + }); + + it("preserves Vertex auth params for agent model configs", () => { + const result = Api.AgentExecuteRequestSchema.safeParse({ + agentConfig: { + model: vertexModel, + executionModel: vertexModel, + }, + executeOptions: { + instruction: "find the search box", + }, + }); + + expect(result.success).toBe(true); + if (!result.success) throw result.error; + expect(result.data.agentConfig.model).toEqual(vertexModel); + expect(result.data.agentConfig.executionModel).toEqual(vertexModel); + }); + + it("rejects Vertex key file paths and external credential sources", () => { + for (const blockedAuthOptions of [ + { keyFilename: "/etc/passwd" }, + { keyFile: "/etc/passwd" }, + { apiKey: "vertex-express-key" }, + ]) { + const result = Api.ActRequestSchema.safeParse({ + input: "click the search button", + options: { + model: { + provider: "vertex", + modelName: "vertex/gemini-2.5-flash", + googleAuthOptions: blockedAuthOptions, + }, + }, + }); + + expect(result.success).toBe(false); + } + + const externalAccountResult = Api.ActRequestSchema.safeParse({ + input: "click the search button", + options: { + model: { + provider: "vertex", + modelName: "vertex/gemini-2.5-flash", + googleAuthOptions: { + credentials: { + type: "external_account", + credential_source: { + file: "/etc/passwd", + }, + }, + }, + }, + }, + }); + + expect(externalAccountResult.success).toBe(false); + }); +}); diff --git a/packages/server-v3/openapi.v3.yaml b/packages/server-v3/openapi.v3.yaml index fdc224dc94..92be95e3e6 100644 --- a/packages/server-v3/openapi.v3.yaml +++ b/packages/server-v3/openapi.v3.yaml @@ -168,6 +168,60 @@ components: acceptDownloads: type: boolean additionalProperties: false + GoogleServiceAccountCredentials: + type: object + properties: + type: + type: string + const: service_account + project_id: + type: string + private_key_id: + type: string + private_key: + type: string + client_email: + type: string + client_id: + type: string + auth_uri: + type: string + format: uri + token_uri: + type: string + format: uri + auth_provider_x509_cert_url: + type: string + format: uri + client_x509_cert_url: + type: string + format: uri + universe_domain: + type: string + required: + - private_key + - client_email + additionalProperties: false + GoogleAuthOptions: + type: object + properties: + credentials: + description: Google Cloud service account credentials + $ref: "#/components/schemas/GoogleServiceAccountCredentials" + scopes: + description: Google auth scopes for the desired API request + anyOf: + - type: string + - type: array + items: + type: string + projectId: + description: Google Cloud project ID used by google-auth-library + type: string + universeDomain: + description: Google Cloud universe domain + type: string + additionalProperties: false ModelConfigObject: type: object properties: @@ -181,6 +235,7 @@ components: - google - microsoft - bedrock + - vertex modelName: description: Model name string with provider prefix (e.g., 'openai/gpt-5-nano') example: openai/gpt-5.4-mini @@ -201,6 +256,17 @@ components: type: string additionalProperties: type: string + project: + description: Google Cloud project ID for Vertex AI models + example: my-gcp-project + type: string + location: + description: Google Cloud location for Vertex AI models + example: us-central1 + type: string + googleAuthOptions: + description: google-auth-library options used to authenticate Vertex AI models + $ref: "#/components/schemas/GoogleAuthOptions" required: - modelName ModelConfig: @@ -1251,6 +1317,7 @@ components: - google - microsoft - bedrock + - vertex modelName: description: Model name string with provider prefix (e.g., 'openai/gpt-5-nano') example: openai/gpt-5.4-mini @@ -1271,6 +1338,17 @@ components: type: string additionalProperties: type: string + project: + description: Google Cloud project ID for Vertex AI models + example: my-gcp-project + type: string + location: + description: Google Cloud location for Vertex AI models + example: us-central1 + type: string + googleAuthOptions: + description: google-auth-library options used to authenticate Vertex AI models + $ref: "#/components/schemas/GoogleAuthOptions" required: - modelName additionalProperties: false diff --git a/packages/server-v3/scripts/gen-openapi.ts b/packages/server-v3/scripts/gen-openapi.ts index 5bcfbe4272..0dbc1beee4 100644 --- a/packages/server-v3/scripts/gen-openapi.ts +++ b/packages/server-v3/scripts/gen-openapi.ts @@ -42,6 +42,9 @@ async function main() { BrowserbaseRegion: Api.BrowserbaseRegionSchema, // Shared components LocalBrowserLaunchOptions: Api.LocalBrowserLaunchOptionsSchema, + GoogleServiceAccountCredentials: + Api.GoogleServiceAccountCredentialsSchema, + GoogleAuthOptions: Api.GoogleAuthOptionsSchema, ModelConfigObject: Api.ModelConfigObjectSchema, ModelConfig: Api.ModelConfigSchema, Action: Api.ActionSchema,