stagehand icon indicating copy to clipboard operation
stagehand copied to clipboard

Act with Model Option fails

Open gal-checksum opened this issue 1 month ago • 1 comments

Stagehand:

  • Language/SDK: Typescript
  • Stagehand version: 3.0.1

AI Provider:

  • Provider:Google
  • Model: Any model

Issue Description

See code below. To reproduce as well as results

/**
 * Bug: stagehand.act() fails when passing model override parameter
 *
 * Expected: act() should work with model override, like observe() does
 * Actual: act() throws "An unexpected error occurred"
 */

const { Stagehand } = require("@browserbasehq/stagehand");

async function reproduceActBug() {
  const stagehand = new Stagehand({
    env: "BROWSERBASE",
    apiKey: process.env.BROWSERBASE_API_KEY,
    projectId: process.env.BROWSERBASE_PROJECT_ID,
    model: {
      modelName: "google/gemini-2.5-pro",
      apiKey: process.env.GOOGLE_API_KEY,
    },
  });

  await stagehand.init();
  const page = stagehand.context.pages()[0];
  await page.goto("https://example.com");

  // ✅ This works - observe() with model override
  console.log("Testing observe() with model override...");
  const observations = await stagehand.observe("Find the first link", {
    model: { modelName: "google/gemini-2.5-pro" },
  });
  console.log("✅ observe() SUCCESS -", observations.length, "elements found");

  // ❌ This fails - act() with model override
  console.log("\nTesting act() with model override...");
  try {
    await stagehand.act(observations[0], {
      model: { modelName: "google/gemini-2.5-pro" },
    });
    console.log("✅ act() SUCCESS");
  } catch (error) {
    console.log("❌ act() FAILED:", error.message);
  }

  await stagehand.close();
}

reproduceActBug().catch(console.error);

Results

[[email protected]] injecting env (3) from .env -- tip: 👥 sync secrets across teammates & machines: https://dotenvx.com/ops
[[email protected]] injecting env (0) from .env -- tip: 🛠️  run anywhere with `dotenvx run -- yourcommand`
[2025-11-13 12:29:50.853 -0800] INFO: Using Browserbase credentials
    category: "init"
[2025-11-13 12:29:50.854 -0800] INFO: Starting browserbase session
    category: "init"
[2025-11-13 12:29:50.854 -0800] INFO: Creating new browserbase session...
    category: "init"
[2025-11-13 12:29:52.961 -0800] INFO: Browserbase session started [REDUCTED]
[2025-11-13 12:29:53.327 -0800] INFO: Connecting to local browser
    category: "init"
Testing observe() with model override...
[2025-11-13 12:29:54.301 -0800] INFO: starting observation
    category: "observation"
    instruction: "Find the first link"
[2025-11-13 12:29:54.317 -0800] INFO: Got accessibility tree data
    category: "observation"
[2025-11-13 12:29:54.317 -0800] DEBUG: creating chat completion
    category: "aisdk"
    options: {
      "messages": [
        {
          "role": "system",
          "content": " You are helping the user automate the browser by finding elements based on what the user wants to observe in the page. You will be given: 1. a instruction of elements to observe 2. a hierarchical accessibility tree showing the semantic structure of the page. The tree is a hybrid of the DOM and the accessibility tree. Return an array of elements that match the instruction if they exist, otherwise return an empty array."
        },
        {
          "role": "user",
          "content": "instruction: Find the first link\nAccessibility Tree: \n[2-2] RootWebArea: Example Domain\n  [2-3] scrollable, html\n    [2-16] div\n      [2-17] heading: Example Domain\n      [2-19] paragraph\n        [2-20] StaticText: This domain is for use in documentation examples without needing permission. Avoid use in operations.\n      [2-21] paragraph\n        [2-22] link: Learn more\n"
        }
      ],
      "response_model": {
        "schema": {
          "_def": {
            "unknownKeys": "strip",
            "catchall": {
              "_def": {
                "typeName": "ZodNever"
              },
              "~standard": {
                "version": 1,
                "vendor": "zod"
              }
            },
            "typeName": "ZodObject"
          },
          "~standard": {
            "version": 1,
            "vendor": "zod"
          },
          "_cached": null
        },
        "name": "Observation"
      },
      "temperature": 0.1,
      "top_p": 1,
      "frequency_penalty": 0,
      "presence_penalty": 0
    }
    modelName: "gemini-2.5-pro"
✅ observe() SUCCESS - 1 elements found

Testing act() with model override...
[2025-11-13 12:29:58.175 -0800] INFO: response
    category: "aisdk"
    response: {
      "object": {
        "elements": [
          {
            "elementId": "2-22",
            "description": "A link with the text 'Learn more'.",
            "method": "click",
            "arguments": []
          }
        ]
      },
      "usage": {
        "inputTokens": 199,
        "outputTokens": 60,
        "totalTokens": 401,
        "reasoningTokens": 142
      },
      "finishReason": "stop"
    }
[2025-11-13 12:29:58.175 -0800] INFO: found elements
    category: "observation"
    elements: [
      {
        "description": "A link with the text 'Learn more'.",
        "method": "click",
        "arguments": [],
        "selector": "xpath=/html[1]/body[1]/div[1]/p[2]/a[1]"
      }
    ]
❌ act() FAILED: An unexpected error occurred
[2025-11-13 12:29:58.961 -0800] ERROR: No known environment variable for provider 'gpt-4o'
    category: "init"

gal-checksum avatar Nov 13 '25 20:11 gal-checksum

I will take a look,

JerryWu1234 avatar Nov 14 '25 11:11 JerryWu1234

Act with an Action parameter doesn't take in model options, for self-healing it reuses the model configuration passed on the stagehand constructor (docs link)

miguelg719 avatar Dec 03 '25 04:12 miguelg719

@miguelg719 @JerryWu1234 That's unrelated. See below example that uses Act with an instruction

/**
 * Bug: stagehand.act() fails when passing model override parameter
 *
 * Expected: act() should work with model override, like observe() does
 * Actual: act() throws "An unexpected error occurred"
 */

const { Stagehand } = require("@browserbasehq/stagehand");

async function reproduceActBug() {
  const stagehand = new Stagehand({
    env: "BROWSERBASE",
    apiKey: process.env.BROWSERBASE_API_KEY,
    projectId: process.env.BROWSERBASE_PROJECT_ID,
    verbose: 1,
    model: {
      modelName: "google/gemini-2.5-pro",
      apiKey: process.env.GOOGLE_API_KEY,
    },
  });

  await stagehand.init();
  const page = stagehand.context.pages()[0];
  await page.goto("https://example.com");

  // ✅ This works - observe() with model override
  console.log("Testing observe() with model override...");
  const observations = await stagehand.observe("Find the first link", {
    model: { modelName: "google/gemini-2.5-pro" },
  });
  console.log("✅ observe() SUCCESS -", observations.length, "elements found");

  // ❌ This fails - act() with model override
  console.log("\nTesting act() with model override...");
  try {
    await stagehand.act("Click on the first link", {
      model: { modelName: "anthropic/claude-sonnet-4-5" },
    });
    console.log("✅ act() SUCCESS");
  } catch (error) {
    console.log("❌ act() FAILED:", error.message);
  }

  await stagehand.close();
}

reproduceActBug().catch(console.error);

gal-checksum avatar Dec 04 '25 02:12 gal-checksum