diff --git a/.changeset/evil-clubs-attend.md b/.changeset/evil-clubs-attend.md new file mode 100644 index 000000000..304dd7cfd --- /dev/null +++ b/.changeset/evil-clubs-attend.md @@ -0,0 +1,5 @@ +--- +"@browserbasehq/stagehand": patch +--- + +Add support for google vertex provider diff --git a/packages/core/examples/custom_client_langchain.ts b/packages/core/examples/custom_client_langchain.ts index 3f1f1c62f..5a9604374 100644 --- a/packages/core/examples/custom_client_langchain.ts +++ b/packages/core/examples/custom_client_langchain.ts @@ -9,11 +9,9 @@ import { LangchainClient } from "./external_clients/langchain"; import { ChatOpenAI } from "@langchain/openai"; async function example() { - // @ts-expect-error Type instantiation is excessively deep and possibly infinite const stagehand = new Stagehand({ env: "BROWSERBASE", verbose: 1, - // @ts-expect-error Type instantiation is excessively deep and possibly infinite llmClient: new LangchainClient( new ChatOpenAI({ model: "gpt-4o", diff --git a/packages/core/lib/modelUtils.ts b/packages/core/lib/modelUtils.ts index 40f64c538..ec8d69689 100644 --- a/packages/core/lib/modelUtils.ts +++ b/packages/core/lib/modelUtils.ts @@ -1,4 +1,4 @@ -import { ModelConfiguration } from "./v3/types/public/model"; +import { ClientOptions, ModelConfiguration } from "./v3/types/public/model"; import { AVAILABLE_CUA_MODELS, AvailableCuaModel, @@ -17,7 +17,7 @@ export function splitModelName(model: string): { export function resolveModel(model: string | ModelConfiguration): { provider: string; modelName: string; - clientOptions: Record; + clientOptions: ClientOptions; isCua: boolean; } { // Extract the model string and client options diff --git a/packages/core/lib/utils.ts b/packages/core/lib/utils.ts index ade524cb9..13bf0c8b3 100644 --- a/packages/core/lib/utils.ts +++ b/packages/core/lib/utils.ts @@ -683,6 +683,7 @@ export const providerEnvVarMap: Partial< openai: "OPENAI_API_KEY", anthropic: "ANTHROPIC_API_KEY", google: ["GEMINI_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY"], + vertex: "GOOGLE_VERTEX_AI_API_KEY", groq: "GROQ_API_KEY", cerebras: "CEREBRAS_API_KEY", togetherai: "TOGETHER_AI_API_KEY", diff --git a/packages/core/lib/v3/agent/AgentClient.ts b/packages/core/lib/v3/agent/AgentClient.ts index cc530ac23..69c48c544 100644 --- a/packages/core/lib/v3/agent/AgentClient.ts +++ b/packages/core/lib/v3/agent/AgentClient.ts @@ -4,6 +4,7 @@ import { AgentType, AgentExecutionOptions, } from "../types/public/agent"; +import { ClientOptions } from "../types/public/model"; /** * Abstract base class for agent clients @@ -12,7 +13,7 @@ import { export abstract class AgentClient { public type: AgentType; public modelName: string; - public clientOptions: Record; + public clientOptions: ClientOptions; public userProvidedInstructions?: string; constructor( diff --git a/packages/core/lib/v3/agent/AgentProvider.ts b/packages/core/lib/v3/agent/AgentProvider.ts index 16f4ace41..30ea2fd9a 100644 --- a/packages/core/lib/v3/agent/AgentProvider.ts +++ b/packages/core/lib/v3/agent/AgentProvider.ts @@ -1,6 +1,7 @@ import { ToolSet } from "ai/dist"; import { AgentProviderType } from "../types/public/agent"; import { LogLine } from "../types/public/logs"; +import { ClientOptions } from "../types/public/model"; import { UnsupportedModelError, UnsupportedModelProviderError, @@ -40,7 +41,7 @@ export class AgentProvider { getClient( modelName: string, - clientOptions?: Record, + clientOptions?: ClientOptions, userProvidedInstructions?: string, tools?: ToolSet, ): AgentClient { diff --git a/packages/core/lib/v3/agent/AnthropicCUAClient.ts b/packages/core/lib/v3/agent/AnthropicCUAClient.ts index 62947f531..e1fbe5a1a 100644 --- a/packages/core/lib/v3/agent/AnthropicCUAClient.ts +++ b/packages/core/lib/v3/agent/AnthropicCUAClient.ts @@ -10,6 +10,7 @@ import { ToolUseItem, } from "../types/public/agent"; import { LogLine } from "../types/public/logs"; +import { ClientOptions } from "../types/public/model"; import { AgentScreenshotProviderError } from "../types/public/sdkErrors"; import Anthropic from "@anthropic-ai/sdk"; import { ToolSet } from "ai"; @@ -41,7 +42,7 @@ export class AnthropicCUAClient extends AgentClient { type: AgentType, modelName: string, userProvidedInstructions?: string, - clientOptions?: Record, + clientOptions?: ClientOptions, tools?: ToolSet, ) { super(type, modelName, userProvidedInstructions); @@ -65,7 +66,7 @@ export class AnthropicCUAClient extends AgentClient { }; if (this.baseURL) { - this.clientOptions.baseUrl = this.baseURL; + this.clientOptions.baseURL = this.baseURL; } // Initialize the Anthropic client diff --git a/packages/core/lib/v3/agent/GoogleCUAClient.ts b/packages/core/lib/v3/agent/GoogleCUAClient.ts index fb7462960..62df670dc 100644 --- a/packages/core/lib/v3/agent/GoogleCUAClient.ts +++ b/packages/core/lib/v3/agent/GoogleCUAClient.ts @@ -15,6 +15,7 @@ import { AgentType, AgentExecutionOptions, } from "../types/public/agent"; +import { ClientOptions } from "../types/public/model"; import { AgentClient } from "./AgentClient"; import { AgentScreenshotProviderError, @@ -51,7 +52,7 @@ export class GoogleCUAClient extends AgentClient { type: AgentType, modelName: string, userProvidedInstructions?: string, - clientOptions?: Record, + clientOptions?: ClientOptions, tools?: ToolSet, ) { super(type, modelName, userProvidedInstructions); diff --git a/packages/core/lib/v3/agent/MicrosoftCUAClient.ts b/packages/core/lib/v3/agent/MicrosoftCUAClient.ts index f1c9e2894..ddf6a19e7 100644 --- a/packages/core/lib/v3/agent/MicrosoftCUAClient.ts +++ b/packages/core/lib/v3/agent/MicrosoftCUAClient.ts @@ -6,6 +6,7 @@ import { AgentType, AgentExecutionOptions, } from "../types/public/agent"; +import { ClientOptions } from "../types/public/model"; import { AgentClient } from "./AgentClient"; import { AgentScreenshotProviderError } from "../types/public/sdkErrors"; import { mapKeyToPlaywright } from "./utils/cuaKeyMapping"; @@ -75,7 +76,7 @@ export class MicrosoftCUAClient extends AgentClient { type: AgentType, modelName: string, userProvidedInstructions?: string, - clientOptions?: Record, + clientOptions?: ClientOptions, ) { super(type, modelName || "fara-7b", userProvidedInstructions); diff --git a/packages/core/lib/v3/agent/OpenAICUAClient.ts b/packages/core/lib/v3/agent/OpenAICUAClient.ts index 573c18a0e..94ac18aa7 100644 --- a/packages/core/lib/v3/agent/OpenAICUAClient.ts +++ b/packages/core/lib/v3/agent/OpenAICUAClient.ts @@ -10,6 +10,7 @@ import { ComputerCallItem, FunctionCallItem, } from "../types/public/agent"; +import { ClientOptions } from "../types/public/model"; import { AgentClient } from "./AgentClient"; import { AgentScreenshotProviderError } from "../types/public/sdkErrors"; import { ToolSet } from "ai"; @@ -36,7 +37,7 @@ export class OpenAICUAClient extends AgentClient { type: AgentType, modelName: string, userProvidedInstructions?: string, - clientOptions?: Record, + clientOptions?: ClientOptions, tools?: ToolSet, ) { super(type, modelName, userProvidedInstructions); diff --git a/packages/core/lib/v3/llm/LLMProvider.ts b/packages/core/lib/v3/llm/LLMProvider.ts index 7c16f2118..9b49951cb 100644 --- a/packages/core/lib/v3/llm/LLMProvider.ts +++ b/packages/core/lib/v3/llm/LLMProvider.ts @@ -17,6 +17,7 @@ import { GroqClient } from "./GroqClient"; import { LLMClient } from "./LLMClient"; import { OpenAIClient } from "./OpenAIClient"; import { openai, createOpenAI } from "@ai-sdk/openai"; +import { vertex, createVertex } from "@ai-sdk/google-vertex"; import { anthropic, createAnthropic } from "@ai-sdk/anthropic"; import { google, createGoogleGenerativeAI } from "@ai-sdk/google"; import { xai, createXai } from "@ai-sdk/xai"; @@ -43,11 +44,13 @@ const AISDKProviders: Record = { deepseek, perplexity, ollama, + vertex, }; const AISDKProvidersWithAPIKey: Record = { openai: createOpenAI, anthropic: createAnthropic, google: createGoogleGenerativeAI, + vertex: createVertex, xai: createXai, azure: createAzure, groq: createGroq, @@ -96,10 +99,9 @@ const modelToProviderMap: { [key in AvailableModel]: ModelProvider } = { export function getAISDKLanguageModel( subProvider: string, subModelName: string, - apiKey?: string, - baseURL?: string, + clientOptions?: ClientOptions, ) { - if (apiKey) { + if (clientOptions && Object.keys(clientOptions).length > 0) { const creator = AISDKProvidersWithAPIKey[subProvider]; if (!creator) { throw new UnsupportedAISDKModelProviderError( @@ -107,12 +109,7 @@ export function getAISDKLanguageModel( Object.keys(AISDKProvidersWithAPIKey), ); } - // Create the provider instance with the API key and baseURL if provided - const providerConfig: { apiKey: string; baseURL?: string } = { apiKey }; - if (baseURL) { - providerConfig.baseURL = baseURL; - } - const provider = creator(providerConfig); + const provider = creator(clientOptions); // Get the specific model from the provider return provider(subModelName); } else { @@ -146,8 +143,7 @@ export class LLMProvider { const languageModel = getAISDKLanguageModel( subProvider, subModelName, - clientOptions?.apiKey, - clientOptions?.baseURL, + clientOptions, ); return new AISdkClient({ diff --git a/packages/core/lib/v3/types/public/agent.ts b/packages/core/lib/v3/types/public/agent.ts index aaf2bd489..3e76799a9 100644 --- a/packages/core/lib/v3/types/public/agent.ts +++ b/packages/core/lib/v3/types/public/agent.ts @@ -1,6 +1,7 @@ import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { ToolSet, ModelMessage, wrapLanguageModel, StreamTextResult } from "ai"; import { LogLine } from "./logs"; +import { ClientOptions } from "./model"; import { Page as PlaywrightPage } from "playwright-core"; import { Page as PuppeteerPage } from "puppeteer-core"; import { Page as PatchrightPage } from "patchright-core"; @@ -86,7 +87,7 @@ export interface AgentExecutionOptions< export interface AgentHandlerOptions { modelName: string; - clientOptions?: Record; + clientOptions?: ClientOptions; userProvidedInstructions?: string; experimental?: boolean; } diff --git a/packages/core/lib/v3/types/public/model.ts b/packages/core/lib/v3/types/public/model.ts index 30ab70533..c48e00921 100644 --- a/packages/core/lib/v3/types/public/model.ts +++ b/packages/core/lib/v3/types/public/model.ts @@ -1,8 +1,42 @@ -import type { ClientOptions as AnthropicClientOptions } from "@anthropic-ai/sdk"; +import type { ClientOptions as AnthropicClientOptionsBase } from "@anthropic-ai/sdk"; +import type { GoogleVertexProviderSettings as GoogleVertexProviderSettingsBase } from "@ai-sdk/google-vertex"; import type { LanguageModelV2 } from "@ai-sdk/provider"; -import type { ClientOptions as OpenAIClientOptions } from "openai"; +import type { ClientOptions as OpenAIClientOptionsBase } from "openai"; import type { AgentProviderType } from "./agent"; +export type OpenAIClientOptions = Omit< + OpenAIClientOptionsBase, + "httpAgent" | "fetch" | "defaultHeaders" | "dangerouslyAllowBrowser" +>; + +export type AnthropicClientOptions = Omit< + AnthropicClientOptionsBase, + "httpAgent" | "fetch" | "defaultHeaders" | "dangerouslyAllowBrowser" +>; + +export interface GoogleServiceAccountCredentials { + type?: string; + project_id?: string; + private_key_id?: string; + private_key?: string; + client_email?: string; + client_id?: string; + auth_uri?: string; + token_uri?: string; + auth_provider_x509_cert_url?: string; + client_x509_cert_url?: string; + universe_domain?: string; +} + +export type GoogleVertexProviderSettings = Omit< + GoogleVertexProviderSettingsBase, + "headers" | "fetch" | "googleAuthOptions" +> & { + googleAuthOptions?: { + credentials?: GoogleServiceAccountCredentials; + }; +}; + export type AnthropicJsonSchemaObject = { definitions?: { MySchema?: { @@ -23,9 +57,7 @@ export interface LLMTool { export type AISDKProvider = (modelName: string) => LanguageModelV2; // Represents a function that takes options (like apiKey) and returns an AISDKProvider -export type AISDKCustomProvider = (options: { - apiKey: string; -}) => AISDKProvider; +export type AISDKCustomProvider = (options: ClientOptions) => AISDKProvider; export type AvailableModel = | "gpt-4.1" @@ -67,8 +99,25 @@ export type ModelProvider = | "google" | "aisdk"; -export type ClientOptions = (OpenAIClientOptions | AnthropicClientOptions) & { +export type ClientOptions = ( + | OpenAIClientOptions + | AnthropicClientOptions + | GoogleVertexProviderSettings +) & { + apiKey?: string; provider?: AgentProviderType; + /** OpenAI organization ID */ + organization?: string; + /** Delay between agent actions in ms */ + waitBetweenActions?: number; + /** Anthropic thinking budget for extended thinking */ + thinkingBudget?: number; + /** Environment type for CUA agents (browser, mac, windows, ubuntu) */ + environment?: string; + /** Max images for Microsoft FARA agent */ + maxImages?: number; + /** Temperature for model inference */ + temperature?: number; }; export type ModelConfiguration = diff --git a/packages/core/package.json b/packages/core/package.json index 0152611f9..8ceb9161e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -40,7 +40,7 @@ "peerDependencies": { "deepmerge": "^4.3.1", "dotenv": "^16.4.5", - "zod": "3.25.76 || 4.1.8" + "zod": "^3.25.76 || ^4.1.8" }, "dependencies": { "@ai-sdk/provider": "^2.0.0", @@ -65,6 +65,7 @@ "@ai-sdk/cerebras": "^1.0.25", "@ai-sdk/deepseek": "^1.0.23", "@ai-sdk/google": "^2.0.23", + "@ai-sdk/google-vertex": "^3.0.70", "@ai-sdk/groq": "^2.0.24", "@ai-sdk/mistral": "^2.0.19", "@ai-sdk/openai": "^2.0.53", @@ -87,7 +88,7 @@ "tsx": "^4.10.5", "typescript": "^5.2.2", "vitest": "^4.0.8", - "zod": "3.25.76 || 4.1.8" + "zod": "^3.25.76 || ^4.1.8" }, "repository": { "type": "git",