Skip to content

Commit 589de91

Browse files
committed
Merge branch 'main' into allow-newer-litellm-versions
2 parents 8c89833 + 1e7086e commit 589de91

21 files changed

+1003
-171
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
11
# Stagehand Python Changelog
22

3+
## 0.5.5
4+
5+
### Patch Changes
6+
7+
[#215](https://github.com/browserbase/stagehand-python/pull/215) [`cb35254`](https://github.com/browserbase/stagehand-python/commit/cb35254) Thanks @derekmeegan! - Fix ability to pass raw JSON to Extract schema
8+
[#225](https://github.com/browserbase/stagehand-python/pull/225) [`b23e005`](https://github.com/browserbase/stagehand-python/commit/b23e005) Thanks @derekmeegan! - add local cua example, remove root model from types
9+
[#218](https://github.com/browserbase/stagehand-python/pull/218) [`1a919ad`](https://github.com/browserbase/stagehand-python/commit/1a919ad) Thanks @derekmeegan! - Pass api_timeout param to Stagehand API correctly
10+
[#223](https://github.com/browserbase/stagehand-python/pull/223) [`de7d883`](https://github.com/browserbase/stagehand-python/commit/de7d883) Thanks @derekmeegan! - Fix search, navigate, go back, and go forward for gemini cua agent
11+
[#221](https://github.com/browserbase/stagehand-python/pull/221) [`da570a1`](https://github.com/browserbase/stagehand-python/commit/da570a1) Thanks @miguelg719! - Add support for Haiku 4.5 CUA
12+
13+
## 0.5.4
14+
15+
### Patch Changes
16+
17+
[#205](https://github.com/browserbase/stagehand-python/pull/205) [`3bcdd05`](https://github.com/browserbase/stagehand-python/commit/3bcdd05) Thanks @derekmeegan! - Make litellm client async
18+
[#213](https://github.com/browserbase/stagehand-python/pull/213) [`1d0577d`](https://github.com/browserbase/stagehand-python/commit/1d0577d) Thanks @miguelg719! - Added support for Gemini Computer Use models
19+
20+
## 0.5.3
21+
22+
### Patch Changes
23+
24+
[#196](https://github.com/browserbase/stagehand-python/pull/196) [`93f5c97`](https://github.com/browserbase/stagehand-python/commit/93f5c97) Thanks @chrisreadsf, @miguelg719 and Derek Meegan! - remove duplicate project id if already passed to Stagehand
25+
[#203](https://github.com/browserbase/stagehand-python/pull/203) [`82c6fed`](https://github.com/browserbase/stagehand-python/commit/82c6fed) Thanks @miguelg719! - Bump openai dependency version
26+
[#198](https://github.com/browserbase/stagehand-python/pull/198) [`057b38b`](https://github.com/browserbase/stagehand-python/commit/057b38b) Thanks @Zach10za! - Fix draw_overlay on env:LOCAL
27+
328
## 0.5.2
429

530
### Patch Changes

examples/agent_example.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,37 +36,34 @@ async def main():
3636
# Build a unified configuration object for Stagehand
3737
config = StagehandConfig(
3838
env="BROWSERBASE",
39-
# env="LOCAL",
4039
api_key=os.getenv("BROWSERBASE_API_KEY"),
4140
project_id=os.getenv("BROWSERBASE_PROJECT_ID"),
42-
model_name="gpt-4o",
43-
self_heal=True,
4441
system_prompt="You are a browser automation assistant that helps users navigate websites effectively.",
4542
model_client_options={"apiKey": os.getenv("MODEL_API_KEY")},
46-
verbose=1,
43+
self_heal=True,
44+
verbose=2,
4745
)
4846

4947
# Create a Stagehand client using the configuration object.
5048
stagehand = Stagehand(config)
5149

5250
# Initialize - this creates a new session automatically.
5351
console.print("\n🚀 [info]Initializing Stagehand...[/]")
54-
await stagehand.init()
55-
if stagehand.env == "BROWSERBASE":
56-
console.print(f"\n[yellow]Created new session:[/] {stagehand.session_id}")
57-
console.print(
58-
f"🌐 [white]View your live browser:[/] [url]https://www.browserbase.com/sessions/{stagehand.session_id}[/]"
59-
)
52+
await stagehand.init()
53+
console.print(f"\n[yellow]Created new session:[/] {stagehand.session_id}")
54+
console.print(
55+
f"🌐 [white]View your live browser:[/] [url]https://www.browserbase.com/sessions/{stagehand.session_id}[/]"
56+
)
6057

6158
console.print("\n▶️ [highlight] Navigating[/] to Google")
6259
await stagehand.page.goto("https://google.com/")
6360
console.print("✅ [success]Navigated to Google[/]")
6461

6562
console.print("\n▶️ [highlight] Using Agent to perform a task[/]: playing a game of 2048")
6663
agent = stagehand.agent(
67-
model="computer-use-preview",
64+
model="gemini-2.5-computer-use-preview-10-2025",
6865
instructions="You are a helpful web navigation assistant that helps users find information. You are currently on the following page: google.com. Do not ask follow up questions, the user will trust your judgement.",
69-
options={"apiKey": os.getenv("MODEL_API_KEY")}
66+
options={"apiKey": os.getenv("GEMINI_API_KEY")}
7067
)
7168
agent_result = await agent.execute(
7269
instruction="Play a game of 2048",

examples/agent_example_local.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import asyncio
2+
import logging
3+
import os
4+
5+
from dotenv import load_dotenv
6+
from rich.console import Console
7+
from rich.panel import Panel
8+
from rich.theme import Theme
9+
10+
from stagehand import Stagehand, StagehandConfig, configure_logging
11+
12+
# Create a custom theme for consistent styling
13+
custom_theme = Theme(
14+
{
15+
"info": "cyan",
16+
"success": "green",
17+
"warning": "yellow",
18+
"error": "red bold",
19+
"highlight": "magenta",
20+
"url": "blue underline",
21+
}
22+
)
23+
24+
# Create a Rich console instance with our theme
25+
console = Console(theme=custom_theme)
26+
27+
load_dotenv()
28+
29+
# Configure logging with the utility function
30+
configure_logging(
31+
level=logging.INFO, # Set to INFO for regular logs, DEBUG for detailed
32+
quiet_dependencies=True, # Reduce noise from dependencies
33+
)
34+
35+
async def main():
36+
# Build a unified configuration object for Stagehand
37+
config = StagehandConfig(
38+
env="LOCAL",
39+
system_prompt="You are a browser automation assistant that helps users navigate websites effectively.",
40+
model_client_options={"apiKey": os.getenv("MODEL_API_KEY")},
41+
self_heal=True,
42+
verbose=2,
43+
)
44+
45+
# Create a Stagehand client using the configuration object.
46+
stagehand = Stagehand(config)
47+
48+
# Initialize - this creates a new session automatically.
49+
console.print("\n🚀 [info]Initializing Stagehand...[/]")
50+
await stagehand.init()
51+
52+
console.print("\n▶️ [highlight] Navigating[/] to Google")
53+
await stagehand.page.goto("https://google.com/")
54+
console.print("✅ [success]Navigated to Google[/]")
55+
56+
console.print("\n▶️ [highlight] Using Agent to perform a task[/]: playing a game of 2048")
57+
agent = stagehand.agent(
58+
model="gemini-2.5-computer-use-preview-10-2025",
59+
instructions="You are a helpful web navigation assistant that helps users find information. You are currently on the following page: google.com. Do not ask follow up questions, the user will trust your judgement.",
60+
options={"apiKey": os.getenv("GEMINI_API_KEY")}
61+
)
62+
agent_result = await agent.execute(
63+
instruction="Play a game of 2048",
64+
max_steps=20,
65+
auto_screenshot=True,
66+
)
67+
68+
console.print(agent_result)
69+
70+
console.print("📊 [info]Agent execution result:[/]")
71+
console.print(f"🎯 Completed: [bold]{'Yes' if agent_result.completed else 'No'}[/]")
72+
if agent_result.message:
73+
console.print(f"💬 Message: [italic]{agent_result.message}[/]")
74+
75+
if agent_result.actions:
76+
console.print(f"🔄 Actions performed: [bold]{len(agent_result.actions)}[/]")
77+
for i, action in enumerate(agent_result.actions):
78+
action_type = action.type
79+
80+
console.print(f" Action {i+1}: {action_type if action_type else 'Unknown'}")
81+
82+
# For debugging, you can also print the full JSON
83+
console.print("[dim]Full response JSON:[/]")
84+
console.print_json(f"{agent_result.model_dump_json()}")
85+
86+
# Close the session
87+
console.print("\n⏹️ [warning]Closing session...[/]")
88+
await stagehand.close()
89+
console.print("✅ [success]Session closed successfully![/]")
90+
console.rule("[bold]End of Example[/]")
91+
92+
93+
if __name__ == "__main__":
94+
# Add a fancy header
95+
console.print(
96+
"\n",
97+
Panel(
98+
"[light_gray]Stagehand 🤘 Agent Example[/]",
99+
border_style="green",
100+
padding=(1, 10),
101+
),
102+
)
103+
asyncio.run(main())

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "stagehand"
7-
version = "0.5.2"
7+
version = "0.5.5"
88
description = "Python SDK for Stagehand"
99
readme = "README.md"
1010
classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent",]
1111
requires-python = ">=3.9"
12-
dependencies = [ "httpx>=0.24.0", "python-dotenv>=1.0.0", "pydantic>=1.10.0", "playwright>=1.42.1", "requests>=2.31.0", "browserbase>=1.4.0", "rich>=13.7.0", "openai>=1.83.0,<2.0.0", "anthropic>=0.51.0", "litellm>=1.72.0,<2.0.0", "nest-asyncio>=1.6.0",]
12+
dependencies = [ "httpx>=0.24.0", "python-dotenv>=1.0.0", "pydantic>=1.10.0", "playwright>=1.42.1", "requests>=2.31.0", "browserbase>=1.4.0", "rich>=13.7.0", "openai>=1.99.6", "anthropic>=0.51.0", "litellm>=1.72.0,<2.0.0", "nest-asyncio>=1.6.0", "google-genai>=1.40.0",]
1313
[[project.authors]]
1414
name = "Browserbase, Inc."
1515

stagehand/agent/agent.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,26 @@
1313
)
1414
from .anthropic_cua import AnthropicCUAClient
1515
from .client import AgentClient
16+
from .google_cua import GoogleCUAClient
1617
from .openai_cua import OpenAICUAClient
1718

1819
MODEL_TO_CLIENT_CLASS_MAP: dict[str, type[AgentClient]] = {
19-
"computer-use-preview": OpenAICUAClient,
20+
"computer-use-preview-2025-03-11": OpenAICUAClient,
2021
"claude-3-5-sonnet-latest": AnthropicCUAClient,
2122
"claude-3-7-sonnet-latest": AnthropicCUAClient,
23+
"claude-haiku-4-5-20251001": AnthropicCUAClient,
2224
"claude-sonnet-4-20250514": AnthropicCUAClient,
25+
"claude-sonnet-4-5-20250929": AnthropicCUAClient,
26+
"gemini-2.5-computer-use-preview-10-2025": GoogleCUAClient,
2327
}
2428
MODEL_TO_PROVIDER_MAP: dict[str, AgentProvider] = {
25-
"computer-use-preview": AgentProvider.OPENAI,
29+
"computer-use-preview-2025-03-11": AgentProvider.OPENAI,
2630
"claude-3-5-sonnet-20240620": AgentProvider.ANTHROPIC,
2731
"claude-3-7-sonnet-20250219": AgentProvider.ANTHROPIC,
32+
"claude-haiku-4-5-20251001": AgentProvider.ANTHROPIC,
2833
"claude-sonnet-4-20250514": AgentProvider.ANTHROPIC,
34+
"claude-sonnet-4-5-20250929": AgentProvider.ANTHROPIC,
35+
"gemini-2.5-computer-use-preview-10-2025": AgentProvider.GOOGLE,
2936
# Add more mappings as needed
3037
}
3138

@@ -163,13 +170,10 @@ async def execute(
163170
f"Agent execution finished. Success: {agent_result.completed}. Message: {agent_result.message}",
164171
category="agent",
165172
)
166-
# To clean up pydantic model output
167-
actions_repr = [action.root for action in agent_result.actions]
168173
self.logger.debug(
169-
f"Agent actions: {actions_repr}",
174+
f"Agent actions: {agent_result.actions}",
170175
category="agent",
171176
)
172-
agent_result.actions = actions_repr
173177
return agent_result
174178
else:
175179
agent_config_payload = self.config.model_dump(

0 commit comments

Comments
 (0)