mirror of
https://github.com/browser-use/browser-use.git
synced 2026-09-14 19:59:47 +08:00
Promote beta core package quickstart
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# Rust Agent Integration Feature/Proof Ledger
|
||||
|
||||
This branch keeps the Python `Agent` unchanged unless callers explicitly import
|
||||
`from browser_use.rust import Agent`.
|
||||
`from browser_use.beta import Agent`.
|
||||
|
||||
## Current Features
|
||||
|
||||
1. Rust SDK server execution path
|
||||
- `browser_use.rust.Agent` now runs normal tasks through the terminal SDK server
|
||||
- `browser_use.beta.Agent` now runs normal tasks through the terminal SDK server
|
||||
(`browser-use-terminal sdk-server --transport stdio`) using the normalized
|
||||
`agent.run_task` request/response protocol.
|
||||
- Follow-up tasks reuse the returned SDK `agent_id`, `session_id`, and `browser_id`
|
||||
@@ -64,7 +64,7 @@ This branch keeps the Python `Agent` unchanged unless callers explicitly import
|
||||
- Terminal Rust runs only advertise sub-agent tools when the run has a
|
||||
configured child-agent runner, and SDK JSON-RPC runs now attach the same
|
||||
runtime-backed child runner as CLI runs. This enables terminal's advanced
|
||||
sub-agent system in `browser_use.rust.Agent` evals without exposing
|
||||
sub-agent system in `browser_use.beta.Agent` evals without exposing
|
||||
spawn tools on unsupported run surfaces.
|
||||
- Eval GitHub runners now refresh Convex progress during long `run_agent`
|
||||
and `evaluate` stages, so slow Rust or Agent SDK judge calls stay visible
|
||||
@@ -176,7 +176,7 @@ This branch keeps the Python `Agent` unchanged unless callers explicitly import
|
||||
traces against that combined stream, so sub-agent model calls contribute to
|
||||
run token/cost totals without letting child `session.done` events override
|
||||
the parent final answer.
|
||||
- `browser_use.rust.Agent` no longer treats `_run_process` monkeypatches as an
|
||||
- `browser_use.beta.Agent` no longer treats `_run_process` monkeypatches as an
|
||||
alternate runtime. Production `run()` and `follow_up()` now require the
|
||||
Rust SDK server path, so the legacy terminal CLI adapter cannot silently
|
||||
replace the new server protocol while the normal Python `browser_use.Agent`
|
||||
@@ -193,7 +193,7 @@ This branch keeps the Python `Agent` unchanged unless callers explicitly import
|
||||
telemetry payloads when the terminal server returns both live and projected
|
||||
event streams.
|
||||
- Browser-use no longer sends a Rust SDK `tool_allowlist` override. The
|
||||
terminal SDK server now owns its tool registry for `browser_use.rust.Agent`
|
||||
terminal SDK server now owns its tool registry for `browser_use.beta.Agent`
|
||||
runs, so local search, web/search helpers, and v2 sub-agent controls are
|
||||
available in evals when the Rust core registers them.
|
||||
- Reconstructed browser history ignores internal browser-control endpoint
|
||||
@@ -244,21 +244,21 @@ This branch keeps the Python `Agent` unchanged unless callers explicitly import
|
||||
- terminal `cargo test -p browser-use-cli sdk_json_rpc_agent_run_task_executes_fake_backend_with_normalized_history -- --nocapture`
|
||||
- terminal `cargo test -p browser-use-llm stream_ -- --nocapture`
|
||||
- terminal `cargo test -p browser-use-cli sdk_provider_run_config_maps_browser_use_options_to_rust_core -- --nocapture`
|
||||
- browser-use `uv run python -m py_compile browser_use/rust/service.py`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py -k 'browser_script_lifecycle_outputs_as_result or initial_actions_pre_navigate_existing_cdp_session or run_hands_off_completed_initial_navigation_as_context' -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py -k 'printed_browser_script_page_info_as_state or browser_script_lifecycle_outputs_as_result or initial_actions_pre_navigate_existing_cdp_session or run_hands_off_completed_initial_navigation_as_context' -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_history_surfaces_running_browser_script_observe_instruction -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py -k 'initial_actions_pre_navigate_existing_cdp_session or run_executes_initial_actions_before_sdk or run_hands_off_completed_initial_navigation_as_context' -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py -k 'sdk_client_reads_large_json_rpc_lines or sdk_and_reuses_session or translates_browser_use_args_to_terminal'`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py -k 'sdk_client_queues_agent_notifications_before_response or sdk_client_reads_large_json_rpc_lines or sdk_and_reuses_session or translates_browser_use_args_to_terminal'`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py -k 'rust_sdk_client_reads_large_json_rpc_lines or rust_sdk_client_queues_agent_notifications_before_response' -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_sdk_client_reads_large_json_rpc_lines tests/ci/test_rust_agent.py::test_rust_agent_run_leaves_initial_navigation_for_sdk_by_default tests/ci/test_rust_agent.py::test_rust_agent_initial_actions_can_pre_navigate_existing_cdp_session tests/ci/test_rust_agent.py::test_rust_agent_translates_browser_use_args_to_terminal -q`
|
||||
- browser-use `PYTHONPATH=. uv run pytest tests/ci/test_rust_agent.py -q -k 'pre_navigates_cdp_session_before_sdk_by_default or initial_actions_can_pre_navigate_existing_cdp_session or direct_initial_navigation_defaults_on_for_cdp or direct_initial_navigation_can_be_disabled'`
|
||||
- browser-use `uv run pytest -q tests/ci/test_rust_agent.py -k "pre_navigates_cdp_session_before_sdk_by_default or keeps_initial_navigation_when_direct_state_mismatches or initial_actions_can_pre_navigate_existing_cdp_session or direct_initial_navigation_defaults_on_for_cdp"`
|
||||
- browser-use `uv run pytest -q tests/ci/test_rust_agent.py -k "terminal_token_count_usage or sums_token_count_last_usage_when_latest_total_underreports or terminal_usage_prices_token_count_events or terminal_usage_sums_token_count_cache_creation"`
|
||||
- browser-use `uv run pytest -q tests/ci/test_rust_agent.py -k "terminal_nested_model_usage or token_count_does_not_shrink_model_usage_totals or terminal_usage_prices_token_count_events or terminal_usage_prices_anthropic_raw_cache_reads or terminal_usage_sums_token_count_cache_creation or priced_summary_sums_cache_read_tokens or mixed_events_do_not_shrink_totals or priced_usage_prefers_model_usage_over_token_count or sums_token_count_last_usage_when_latest_total_underreports"`
|
||||
- browser-use `python -m py_compile browser_use/rust/service.py`
|
||||
- browser-use `uv run python -m py_compile browser_use/beta/service.py`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py -k 'browser_script_lifecycle_outputs_as_result or initial_actions_pre_navigate_existing_cdp_session or run_hands_off_completed_initial_navigation_as_context' -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py -k 'printed_browser_script_page_info_as_state or browser_script_lifecycle_outputs_as_result or initial_actions_pre_navigate_existing_cdp_session or run_hands_off_completed_initial_navigation_as_context' -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_rust_history_surfaces_running_browser_script_observe_instruction -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py -k 'initial_actions_pre_navigate_existing_cdp_session or run_executes_initial_actions_before_sdk or run_hands_off_completed_initial_navigation_as_context' -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py -k 'sdk_client_reads_large_json_rpc_lines or sdk_and_reuses_session or translates_browser_use_args_to_terminal'`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py -k 'sdk_client_queues_agent_notifications_before_response or sdk_client_reads_large_json_rpc_lines or sdk_and_reuses_session or translates_browser_use_args_to_terminal'`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py -k 'rust_sdk_client_reads_large_json_rpc_lines or rust_sdk_client_queues_agent_notifications_before_response' -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_rust_sdk_client_reads_large_json_rpc_lines tests/ci/test_beta_agent.py::test_beta_agent_run_leaves_initial_navigation_for_sdk_by_default tests/ci/test_beta_agent.py::test_beta_agent_initial_actions_can_pre_navigate_existing_cdp_session tests/ci/test_beta_agent.py::test_beta_agent_translates_browser_use_args_to_terminal -q`
|
||||
- browser-use `PYTHONPATH=. uv run pytest tests/ci/test_beta_agent.py -q -k 'pre_navigates_cdp_session_before_sdk_by_default or initial_actions_can_pre_navigate_existing_cdp_session or direct_initial_navigation_defaults_on_for_cdp or direct_initial_navigation_can_be_disabled'`
|
||||
- browser-use `uv run pytest -q tests/ci/test_beta_agent.py -k "pre_navigates_cdp_session_before_sdk_by_default or keeps_initial_navigation_when_direct_state_mismatches or initial_actions_can_pre_navigate_existing_cdp_session or direct_initial_navigation_defaults_on_for_cdp"`
|
||||
- browser-use `uv run pytest -q tests/ci/test_beta_agent.py -k "terminal_token_count_usage or sums_token_count_last_usage_when_latest_total_underreports or terminal_usage_prices_token_count_events or terminal_usage_sums_token_count_cache_creation"`
|
||||
- browser-use `uv run pytest -q tests/ci/test_beta_agent.py -k "terminal_nested_model_usage or token_count_does_not_shrink_model_usage_totals or terminal_usage_prices_token_count_events or terminal_usage_prices_anthropic_raw_cache_reads or terminal_usage_sums_token_count_cache_creation or priced_summary_sums_cache_read_tokens or mixed_events_do_not_shrink_totals or priced_usage_prefers_model_usage_over_token_count or sums_token_count_last_usage_when_latest_total_underreports"`
|
||||
- browser-use `python -m py_compile browser_use/beta/service.py`
|
||||
- terminal `cargo test -p browser-use-cli sdk_transport -- --nocapture`
|
||||
- terminal `cargo test -p browser-use-providers server_overloaded -- --nocapture`
|
||||
- terminal `cargo test -p browser-use-agent observe_timeout -- --nocapture`
|
||||
@@ -269,22 +269,22 @@ This branch keeps the Python `Agent` unchanged unless callers explicitly import
|
||||
- terminal `cargo test -p browser-use-agent search -- --nocapture`
|
||||
- terminal `cargo test -p browser-use-agent dispatcher -- --nocapture`
|
||||
- terminal `cargo test -p browser-use-cli sdk_json_rpc_agent_run_returns_child_usage_events_separately -- --nocapture`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_agent_prices_sdk_child_usage_events_without_overriding_parent_result -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_agent_runs_through_sdk_and_reuses_session_for_followup tests/ci/test_rust_agent.py::test_rust_agent_recovers_final_result_from_sdk_notifications_after_transport_error tests/ci/test_rust_agent.py::test_rust_agent_preserves_sdk_notification_history_on_cancel -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_agent_recovers_final_result_from_sdk_notifications_after_transport_error tests/ci/test_rust_agent.py::test_rust_agent_recovers_nested_sdk_notification_events tests/ci/test_rust_agent.py::test_rust_agent_prices_sdk_child_usage_events_without_overriding_parent_result -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_agent_recovers_nested_sdk_notification_events tests/ci/test_rust_agent.py::test_rust_agent_prefers_notification_final_when_response_history_lacks_result tests/ci/test_rust_agent.py::test_rust_agent_uses_sdk_history_usage_when_events_do_not_include_usage tests/ci/test_rust_agent.py::test_rust_agent_prices_sdk_child_usage_events_without_overriding_parent_result -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_agent_recovers_final_result_from_sdk_notifications_after_transport_error tests/ci/test_rust_agent.py::test_rust_agent_recovers_nested_sdk_notification_events tests/ci/test_rust_agent.py::test_rust_agent_recovers_projected_sdk_final_events tests/ci/test_rust_agent.py::test_rust_agent_prefers_notification_final_when_response_history_lacks_result tests/ci/test_rust_agent.py::test_rust_agent_uses_sdk_history_usage_when_events_do_not_include_usage tests/ci/test_rust_agent.py::test_rust_agent_prices_sdk_child_usage_events_without_overriding_parent_result -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_agent_runs_through_sdk_and_reuses_session_for_followup -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_beta_agent_prices_sdk_child_usage_events_without_overriding_parent_result -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_beta_agent_runs_through_sdk_and_reuses_session_for_followup tests/ci/test_beta_agent.py::test_beta_agent_recovers_final_result_from_sdk_notifications_after_transport_error tests/ci/test_beta_agent.py::test_beta_agent_preserves_sdk_notification_history_on_cancel -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_beta_agent_recovers_final_result_from_sdk_notifications_after_transport_error tests/ci/test_beta_agent.py::test_beta_agent_recovers_nested_sdk_notification_events tests/ci/test_beta_agent.py::test_beta_agent_prices_sdk_child_usage_events_without_overriding_parent_result -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_beta_agent_recovers_nested_sdk_notification_events tests/ci/test_beta_agent.py::test_beta_agent_prefers_notification_final_when_response_history_lacks_result tests/ci/test_beta_agent.py::test_beta_agent_uses_sdk_history_usage_when_events_do_not_include_usage tests/ci/test_beta_agent.py::test_beta_agent_prices_sdk_child_usage_events_without_overriding_parent_result -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_beta_agent_recovers_final_result_from_sdk_notifications_after_transport_error tests/ci/test_beta_agent.py::test_beta_agent_recovers_nested_sdk_notification_events tests/ci/test_beta_agent.py::test_beta_agent_recovers_projected_sdk_final_events tests/ci/test_beta_agent.py::test_beta_agent_prefers_notification_final_when_response_history_lacks_result tests/ci/test_beta_agent.py::test_beta_agent_uses_sdk_history_usage_when_events_do_not_include_usage tests/ci/test_beta_agent.py::test_beta_agent_prices_sdk_child_usage_events_without_overriding_parent_result -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_beta_agent_runs_through_sdk_and_reuses_session_for_followup -q`
|
||||
- terminal `cargo fmt --check`
|
||||
- terminal `cargo test -p browser-use-cli sdk_json_rpc_browser_create_preserves_browser_use_settings -- --nocapture`
|
||||
- terminal `cargo test -p browser-use-cli sdk_provider_run_config_maps_browser_use_options_to_rust_core -- --nocapture`
|
||||
- terminal `cargo test -p browser-use-agent stored_cloud_profile_uses_sdk_proxy_country_env_when_connecting -- --nocapture`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_agent_sdk_browser_payload_includes_profile_domains_window_and_proxy -q`
|
||||
- browser-use `uv run python -m py_compile browser_use/rust/service.py`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_sdk_event_dedupe_removes_projected_usage_duplicates tests/ci/test_rust_agent.py::test_rust_history_ignores_internal_browser_connection_url tests/ci/test_rust_agent.py::test_rust_agent_default_llm_matches_browser_use_default tests/ci/test_rust_agent.py::test_rust_agent_default_llm_respects_default_llm_env tests/ci/test_rust_agent.py::test_rust_agent_exposes_logging_helper_methods tests/ci/test_rust_agent.py::test_rust_agent_telemetry_filters_empty_reconstructed_urls -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py::test_rust_agent_translates_browser_use_args_to_terminal tests/ci/test_rust_agent.py::test_rust_agent_sdk_params_leave_terminal_tools_unrestricted -q`
|
||||
- browser-use `uv run pytest tests/ci/test_rust_agent.py -q`
|
||||
- browser-use `uv run ruff check browser_use/rust/service.py tests/ci/test_rust_agent.py tests/ci/models/test_llm_model_factory.py`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_beta_agent_sdk_browser_payload_includes_profile_domains_window_and_proxy -q`
|
||||
- browser-use `uv run python -m py_compile browser_use/beta/service.py`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_rust_sdk_event_dedupe_removes_projected_usage_duplicates tests/ci/test_beta_agent.py::test_rust_history_ignores_internal_browser_connection_url tests/ci/test_beta_agent.py::test_beta_agent_default_llm_matches_browser_use_default tests/ci/test_beta_agent.py::test_beta_agent_default_llm_respects_default_llm_env tests/ci/test_beta_agent.py::test_beta_agent_exposes_logging_helper_methods tests/ci/test_beta_agent.py::test_beta_agent_telemetry_filters_empty_reconstructed_urls -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py::test_beta_agent_translates_browser_use_args_to_terminal tests/ci/test_beta_agent.py::test_beta_agent_sdk_params_leave_terminal_tools_unrestricted -q`
|
||||
- browser-use `uv run pytest tests/ci/test_beta_agent.py -q`
|
||||
- browser-use `uv run ruff check browser_use/beta/service.py tests/ci/test_beta_agent.py tests/ci/models/test_llm_model_factory.py`
|
||||
- terminal `cargo test -p browser-use-browser browser_script_http_get_many_preserves_order_and_errors -- --nocapture`
|
||||
- terminal `cargo test -p browser-use-browser browser_script_browser_fetch_single_returns_structured_errors_by_default -- --nocapture`
|
||||
- terminal `cargo test -p browser-use-browser browser_script_js_accepts_anonymous_function_snippets -- --nocapture`
|
||||
@@ -49,18 +49,20 @@
|
||||
|
||||
# 👋 Human Quickstart
|
||||
|
||||
**1. Create environment and install Browser-Use with [uv](https://docs.astral.sh/uv/) (Python>=3.11):**
|
||||
```bash
|
||||
uv init && uv add browser-use && uv sync
|
||||
# uvx browser-use install # Run if you don't have Chromium installed
|
||||
Browser Use 0.13 introduces a new beta agent powered by a Rust core and a browser harness built for current frontier models. It gives the model a real browser/computer action space, persistent tools, and recovery loops inspired by coding agents.
|
||||
|
||||
```text
|
||||
Python API -> Rust core -> Browser harness -> Web task done
|
||||
```
|
||||
|
||||
**Experimental Rust agent:** install Browser Use Terminal for `from browser_use.rust import Agent`:
|
||||
**1. Install Browser Use with the native core runtime (Python>=3.11):**
|
||||
```bash
|
||||
curl -fsSL https://browser-use.com/terminal/install.sh | sh
|
||||
browser
|
||||
uv add "browser-use[core]"
|
||||
# or: pip install "browser-use[core]"
|
||||
```
|
||||
|
||||
The `[core]` extra installs the native Browser Use runtime for your platform.
|
||||
|
||||
**2. [Optional] Get your API key from [Browser Use Cloud](https://cloud.browser-use.com/new-api-key?utm_source=github&utm_medium=readme-quickstart-api-key):**
|
||||
```
|
||||
# .env
|
||||
@@ -71,39 +73,23 @@ BROWSER_USE_API_KEY=your-key
|
||||
|
||||
**3. Run your first agent:**
|
||||
```python
|
||||
from browser_use import Agent, Browser, ChatBrowserUse
|
||||
# from browser_use import ChatGoogle # ChatGoogle(model='gemini-3-flash-preview')
|
||||
# from browser_use import ChatAnthropic # ChatAnthropic(model='claude-sonnet-4-6')
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
browser = Browser(
|
||||
# use_cloud=True, # Use a stealth browser on Browser Use Cloud
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
task="Find the number of stars of the browser-use repo",
|
||||
llm=ChatBrowserUse(),
|
||||
# llm=ChatGoogle(model='gemini-3-flash-preview'),
|
||||
# llm=ChatAnthropic(model='claude-sonnet-4-6'),
|
||||
browser=browser,
|
||||
)
|
||||
await agent.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
**4. [Optional] Try the experimental Rust core:**
|
||||
```python
|
||||
from browser_use import ChatBrowserUse
|
||||
from browser_use.rust import Agent
|
||||
from browser_use.beta import Agent, BrowserProfile, ChatBrowserUse
|
||||
# from browser_use.beta import ChatOpenAI # ChatOpenAI(model='gpt-5.5')
|
||||
# from browser_use.beta import ChatGoogle # ChatGoogle(model='gemini-3.1-pro-preview')
|
||||
# from browser_use.beta import ChatAnthropic # ChatAnthropic(model='claude-opus-4-8')
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
agent = Agent(
|
||||
task="Find the number of stars of the browser-use repo",
|
||||
llm=ChatBrowserUse(),
|
||||
# llm=ChatOpenAI(model='gpt-5.5'),
|
||||
# llm=ChatGoogle(model='gemini-3.1-pro-preview'),
|
||||
# llm=ChatAnthropic(model='claude-opus-4-8'), # Sonnet also works well.
|
||||
browser_profile=BrowserProfile(
|
||||
headless=False,
|
||||
allowed_domains=["*.github.com"],
|
||||
),
|
||||
)
|
||||
history = await agent.run()
|
||||
print(history.final_result())
|
||||
@@ -112,6 +98,8 @@ if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
Existing Python agent users can keep using `from browser_use import Agent`. The new Rust-powered beta agent is `from browser_use.beta import Agent`.
|
||||
|
||||
Check out the [library docs](https://docs.browser-use.com/open-source/introduction) and the [cloud docs](https://docs.cloud.browser-use.com?utm_source=github&utm_medium=readme-cloud-docs) for more!
|
||||
|
||||
<br/>
|
||||
|
||||
@@ -6,11 +6,11 @@ from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from browser_use.agent.service import Agent
|
||||
from browser_use.rust.service import RustAgentError
|
||||
from browser_use.beta.service import BetaAgentError
|
||||
|
||||
_LAZY_IMPORTS = {
|
||||
'Agent': ('browser_use.agent.service', 'Agent'),
|
||||
'RustAgentError': ('browser_use.rust.service', 'RustAgentError'),
|
||||
'BetaAgentError': ('browser_use.beta.service', 'BetaAgentError'),
|
||||
}
|
||||
|
||||
|
||||
@@ -28,5 +28,5 @@ def __getattr__(name: str):
|
||||
|
||||
__all__ = [
|
||||
'Agent',
|
||||
'RustAgentError',
|
||||
'BetaAgentError',
|
||||
]
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
"""Beta Browser Use integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from browser_use.beta.service import Agent, BetaAgentError, find_browser_use_terminal_binary
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from browser_use.browser import BrowserProfile, BrowserSession
|
||||
from browser_use.browser import BrowserSession as Browser
|
||||
from browser_use.llm.anthropic.chat import ChatAnthropic
|
||||
from browser_use.llm.azure.chat import ChatAzureOpenAI
|
||||
from browser_use.llm.browser_use.chat import ChatBrowserUse
|
||||
from browser_use.llm.google.chat import ChatGoogle
|
||||
from browser_use.llm.groq.chat import ChatGroq
|
||||
from browser_use.llm.litellm.chat import ChatLiteLLM
|
||||
from browser_use.llm.mistral.chat import ChatMistral
|
||||
from browser_use.llm.oci_raw.chat import ChatOCIRaw
|
||||
from browser_use.llm.ollama.chat import ChatOllama
|
||||
from browser_use.llm.openai.chat import ChatOpenAI
|
||||
from browser_use.llm.vercel.chat import ChatVercel
|
||||
|
||||
_LAZY_IMPORTS = {
|
||||
'Browser': ('browser_use.browser', 'BrowserSession'),
|
||||
'BrowserProfile': ('browser_use.browser', 'BrowserProfile'),
|
||||
'BrowserSession': ('browser_use.browser', 'BrowserSession'),
|
||||
'ChatOpenAI': ('browser_use.llm.openai.chat', 'ChatOpenAI'),
|
||||
'ChatGoogle': ('browser_use.llm.google.chat', 'ChatGoogle'),
|
||||
'ChatAnthropic': ('browser_use.llm.anthropic.chat', 'ChatAnthropic'),
|
||||
'ChatBrowserUse': ('browser_use.llm.browser_use.chat', 'ChatBrowserUse'),
|
||||
'ChatGroq': ('browser_use.llm.groq.chat', 'ChatGroq'),
|
||||
'ChatLiteLLM': ('browser_use.llm.litellm.chat', 'ChatLiteLLM'),
|
||||
'ChatMistral': ('browser_use.llm.mistral.chat', 'ChatMistral'),
|
||||
'ChatAzureOpenAI': ('browser_use.llm.azure.chat', 'ChatAzureOpenAI'),
|
||||
'ChatOCIRaw': ('browser_use.llm.oci_raw.chat', 'ChatOCIRaw'),
|
||||
'ChatOllama': ('browser_use.llm.ollama.chat', 'ChatOllama'),
|
||||
'ChatVercel': ('browser_use.llm.vercel.chat', 'ChatVercel'),
|
||||
}
|
||||
|
||||
|
||||
def __getattr__(name: str):
|
||||
if name in _LAZY_IMPORTS:
|
||||
module_path, attr_name = _LAZY_IMPORTS[name]
|
||||
from importlib import import_module
|
||||
|
||||
module = import_module(module_path)
|
||||
attr = getattr(module, attr_name)
|
||||
globals()[name] = attr
|
||||
return attr
|
||||
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Agent',
|
||||
'BetaAgentError',
|
||||
'Browser',
|
||||
'BrowserProfile',
|
||||
'BrowserSession',
|
||||
'ChatAnthropic',
|
||||
'ChatAzureOpenAI',
|
||||
'ChatBrowserUse',
|
||||
'ChatGoogle',
|
||||
'ChatGroq',
|
||||
'ChatLiteLLM',
|
||||
'ChatMistral',
|
||||
'ChatOCIRaw',
|
||||
'ChatOllama',
|
||||
'ChatOpenAI',
|
||||
'ChatVercel',
|
||||
'find_browser_use_terminal_binary',
|
||||
]
|
||||
@@ -94,8 +94,8 @@ except ImportError:
|
||||
Laminar = None # type: ignore
|
||||
|
||||
|
||||
class RustAgentError(RuntimeError):
|
||||
"""Raised when the Rust terminal core cannot run a task."""
|
||||
class BetaAgentError(RuntimeError):
|
||||
"""Raised when the beta agent cannot run a task."""
|
||||
|
||||
|
||||
def _laminar_ready() -> bool:
|
||||
@@ -203,7 +203,7 @@ def find_browser_use_terminal_binary() -> str:
|
||||
path_binary = shutil.which('browser-use-terminal')
|
||||
if path_binary:
|
||||
return path_binary
|
||||
raise RustAgentError(
|
||||
raise BetaAgentError(
|
||||
f'Could not find browser-use-terminal. Install Browser Use Terminal with `{TERMINAL_INSTALL_COMMAND}`, '
|
||||
'install browser-use-core, or set BROWSER_USE_TERMINAL_BINARY to a built terminal CLI.'
|
||||
)
|
||||
@@ -220,7 +220,7 @@ def _find_packaged_browser_use_terminal_binary() -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
class RustSdkJsonRpcError(RustAgentError):
|
||||
class RustSdkJsonRpcError(BetaAgentError):
|
||||
"""Raised when the Rust SDK server returns a JSON-RPC error."""
|
||||
|
||||
def __init__(self, code: int, message: str) -> None:
|
||||
@@ -262,7 +262,7 @@ class RustSdkClient:
|
||||
)
|
||||
except (FileNotFoundError, PermissionError) as exc:
|
||||
command = self.command[0] if self.command else 'browser-use-terminal'
|
||||
raise RustAgentError(
|
||||
raise BetaAgentError(
|
||||
f'Could not start Rust SDK server command {command!r}. '
|
||||
f'Install Browser Use Terminal with `{TERMINAL_INSTALL_COMMAND}`, '
|
||||
'or set BROWSER_USE_TERMINAL_BINARY to a built terminal CLI.'
|
||||
@@ -273,7 +273,7 @@ class RustSdkClient:
|
||||
async def call(self, method: str, params: dict[str, Any] | None = None) -> Any:
|
||||
await self.start()
|
||||
if self.process is None or self.process.stdin is None:
|
||||
raise RustAgentError('Rust SDK server stdin is unavailable')
|
||||
raise BetaAgentError('Rust SDK server stdin is unavailable')
|
||||
loop = asyncio.get_running_loop()
|
||||
async with self._write_lock:
|
||||
request_id = self._next_id
|
||||
@@ -291,7 +291,7 @@ class RustSdkClient:
|
||||
await self.process.stdin.drain()
|
||||
except (BrokenPipeError, ConnectionResetError) as exc:
|
||||
self._pending.pop(request_id, None)
|
||||
raise RustAgentError(f'Rust SDK server pipe closed while sending {method}') from exc
|
||||
raise BetaAgentError(f'Rust SDK server pipe closed while sending {method}') from exc
|
||||
try:
|
||||
return await future
|
||||
except asyncio.CancelledError:
|
||||
@@ -320,7 +320,7 @@ class RustSdkClient:
|
||||
*(task for task in (self._reader_task, self._stderr_task) if task is not None and task is not asyncio.current_task()),
|
||||
return_exceptions=True,
|
||||
)
|
||||
self._fail_all(RustAgentError('Rust SDK server closed'))
|
||||
self._fail_all(BetaAgentError('Rust SDK server closed'))
|
||||
self.process = None
|
||||
|
||||
async def _read_stdout(self) -> None:
|
||||
@@ -344,19 +344,19 @@ class RustSdkClient:
|
||||
if not self._handle_stdout_line(raw_line):
|
||||
return
|
||||
if len(buffer) > self.max_line_bytes:
|
||||
self._fail_all(RustAgentError(f'Rust SDK JSON-RPC line exceeded {self.max_line_bytes} bytes without newline'))
|
||||
self._fail_all(BetaAgentError(f'Rust SDK JSON-RPC line exceeded {self.max_line_bytes} bytes without newline'))
|
||||
return
|
||||
except Exception as exc:
|
||||
message = f'Rust SDK stdout reader failed: {exc}'
|
||||
self.stderr_lines.append(message)
|
||||
self._fail_all(RustAgentError(message))
|
||||
self._fail_all(BetaAgentError(message))
|
||||
finally:
|
||||
if self._pending:
|
||||
detail = '\n'.join(self.stderr_lines[-20:])
|
||||
message = 'Rust SDK server exited before responding'
|
||||
if detail:
|
||||
message = f'{message}: {detail}'
|
||||
self._fail_all(RustAgentError(message))
|
||||
self._fail_all(BetaAgentError(message))
|
||||
|
||||
def _handle_stdout_line(self, raw_line: bytes) -> bool:
|
||||
line = raw_line.decode('utf-8', errors='replace').strip()
|
||||
@@ -365,7 +365,7 @@ class RustSdkClient:
|
||||
try:
|
||||
message = json.loads(line)
|
||||
except json.JSONDecodeError as exc:
|
||||
self._fail_all(RustAgentError(f'Invalid Rust SDK JSON-RPC line: {line}: {exc}'))
|
||||
self._fail_all(BetaAgentError(f'Invalid Rust SDK JSON-RPC line: {line}: {exc}'))
|
||||
return False
|
||||
self._handle_message(message)
|
||||
return True
|
||||
@@ -379,7 +379,7 @@ class RustSdkClient:
|
||||
|
||||
def _handle_message(self, message: Any) -> None:
|
||||
if not isinstance(message, dict):
|
||||
self._fail_all(RustAgentError(f'Rust SDK server emitted non-object JSON-RPC message: {message!r}'))
|
||||
self._fail_all(BetaAgentError(f'Rust SDK server emitted non-object JSON-RPC message: {message!r}'))
|
||||
return
|
||||
method = message.get('method')
|
||||
if method in {'agent.event', 'agent.projected_event'}:
|
||||
@@ -395,7 +395,7 @@ class RustSdkClient:
|
||||
return
|
||||
request_id = message.get('id')
|
||||
if not isinstance(request_id, int):
|
||||
self._fail_all(RustAgentError('Rust SDK JSON-RPC response id must be an integer'))
|
||||
self._fail_all(BetaAgentError('Rust SDK JSON-RPC response id must be an integer'))
|
||||
return
|
||||
future = self._pending.pop(request_id, None)
|
||||
if future is None or future.done():
|
||||
@@ -3743,7 +3743,7 @@ def _record_laminar_terminal_tool_spans(events: list[dict[str, Any]], *, max_spa
|
||||
with _laminar_start_span(f'rust_core.tool.{tool_call.get("name") or "tool"}', input=input_messages, span_type='TOOL'):
|
||||
_laminar_set_span_attributes(
|
||||
{
|
||||
'runtime': 'browser_use.rust',
|
||||
'runtime': 'browser_use.beta',
|
||||
'tool_name': tool_call.get('name'),
|
||||
'tool_call_id': tool_call_id,
|
||||
'tool_index': span_index,
|
||||
@@ -3798,7 +3798,7 @@ def _record_laminar_terminal_llm_spans(
|
||||
span_output_messages = _terminal_laminar_span_output_messages(span_output)
|
||||
_laminar_set_span_attributes(
|
||||
{
|
||||
'runtime': 'browser_use.rust',
|
||||
'runtime': 'browser_use.beta',
|
||||
'model': str(span_input.get('model') or default_model),
|
||||
'provider': str(span_input.get('provider') or default_provider),
|
||||
'turn_index': span_index,
|
||||
@@ -3904,7 +3904,7 @@ def _load_rust_history(file_path: str | Path) -> AgentHistoryList:
|
||||
with open(file_path, encoding='utf-8') as history_file:
|
||||
data = json.load(history_file)
|
||||
if not isinstance(data, dict):
|
||||
raise RustAgentError(f'Invalid Browser Use history file: {file_path}')
|
||||
raise BetaAgentError(f'Invalid Browser Use history file: {file_path}')
|
||||
for item in data.get('history', []):
|
||||
if isinstance(item, dict):
|
||||
item['model_output'] = None
|
||||
@@ -4669,7 +4669,7 @@ class Agent(Generic[Context, AgentStructuredOutput]):
|
||||
model = getattr(self.llm, 'model', None) or self.model
|
||||
provider = getattr(self.llm, 'provider', None) or 'rust-terminal'
|
||||
summary = {
|
||||
'runtime': 'browser_use.rust',
|
||||
'runtime': 'browser_use.beta',
|
||||
'model': model,
|
||||
'provider': provider,
|
||||
'max_steps': max_steps,
|
||||
@@ -4914,7 +4914,7 @@ class Agent(Generic[Context, AgentStructuredOutput]):
|
||||
started=started,
|
||||
finished=finished,
|
||||
output_model_schema=self.output_model_schema,
|
||||
process_error='Rust agent stopped before terminal run.',
|
||||
process_error='Beta agent stopped before terminal run.',
|
||||
)
|
||||
self.history = self.result
|
||||
await self._apply_terminal_usage_costs([])
|
||||
@@ -4922,9 +4922,9 @@ class Agent(Generic[Context, AgentStructuredOutput]):
|
||||
self._record_laminar_run_observability(
|
||||
max_steps=max_steps,
|
||||
duration_seconds=finished - started,
|
||||
process_error='Rust agent stopped before terminal run.',
|
||||
process_error='Beta agent stopped before terminal run.',
|
||||
)
|
||||
self._record_run_telemetry(max_steps=max_steps, agent_run_error='Rust agent stopped before terminal run.')
|
||||
self._record_run_telemetry(max_steps=max_steps, agent_run_error='Beta agent stopped before terminal run.')
|
||||
self._dispatch_run_update_event()
|
||||
self._log_final_outcome_messages()
|
||||
await self._finalize_run_cleanup()
|
||||
@@ -4944,7 +4944,7 @@ class Agent(Generic[Context, AgentStructuredOutput]):
|
||||
started=started,
|
||||
finished=finished,
|
||||
output_model_schema=self.output_model_schema,
|
||||
process_error='Rust agent stopped before terminal run.',
|
||||
process_error='Beta agent stopped before terminal run.',
|
||||
)
|
||||
self.history = self.result
|
||||
await self._apply_terminal_usage_costs([])
|
||||
@@ -4952,9 +4952,9 @@ class Agent(Generic[Context, AgentStructuredOutput]):
|
||||
self._record_laminar_run_observability(
|
||||
max_steps=max_steps,
|
||||
duration_seconds=finished - started,
|
||||
process_error='Rust agent stopped before terminal run.',
|
||||
process_error='Beta agent stopped before terminal run.',
|
||||
)
|
||||
self._record_run_telemetry(max_steps=max_steps, agent_run_error='Rust agent stopped before terminal run.')
|
||||
self._record_run_telemetry(max_steps=max_steps, agent_run_error='Beta agent stopped before terminal run.')
|
||||
self._dispatch_run_update_event()
|
||||
self._log_final_outcome_messages()
|
||||
await self._finalize_run_cleanup()
|
||||
@@ -4994,7 +4994,7 @@ class Agent(Generic[Context, AgentStructuredOutput]):
|
||||
enqueue_timeout: int | None = None,
|
||||
) -> AgentHistoryList[AgentStructuredOutput]:
|
||||
if not self.terminal_session_id or not self._sdk_agent_id:
|
||||
raise RustAgentError('No active Rust session. Call run() before follow_up().')
|
||||
raise BetaAgentError('No active Rust session. Call run() before follow_up().')
|
||||
resolved_max_steps = max_steps if max_steps is not None else self.kwargs.get('max_steps', 100)
|
||||
self.add_new_task(task)
|
||||
self.state.follow_up_task = False
|
||||
@@ -5913,7 +5913,7 @@ class Agent(Generic[Context, AgentStructuredOutput]):
|
||||
self.logger.debug('Initial actions completed')
|
||||
|
||||
async def _execute_direct_initial_navigation_actions(self) -> list[ActionResult] | None:
|
||||
"""Pre-navigate existing CDP-backed browser sessions before the Rust agent starts."""
|
||||
"""Pre-navigate existing CDP-backed browser sessions before the Beta agent starts."""
|
||||
self._completed_initial_navigation_urls = []
|
||||
self._completed_initial_navigation_states = []
|
||||
if not _direct_initial_navigation_enabled():
|
||||
@@ -6268,13 +6268,13 @@ class Agent(Generic[Context, AgentStructuredOutput]):
|
||||
except Exception as exc:
|
||||
await self._sdk_client.close()
|
||||
self._sdk_client = None
|
||||
raise RustAgentError(
|
||||
raise BetaAgentError(
|
||||
'Failed to negotiate browser-use-terminal SDK protocol via runtime.ping. '
|
||||
'Install a compatible browser-use-core package or set BROWSER_USE_TERMINAL_BINARY to a compatible binary.'
|
||||
) from exc
|
||||
await self._sdk_client.close()
|
||||
self._sdk_client = None
|
||||
raise RustAgentError(
|
||||
raise BetaAgentError(
|
||||
f'Unsupported browser-use-terminal SDK protocol {protocol_version!r}; expected 1. '
|
||||
'Install a compatible browser-use-core package or set BROWSER_USE_TERMINAL_BINARY to a compatible binary.'
|
||||
)
|
||||
@@ -6450,7 +6450,7 @@ class Agent(Generic[Context, AgentStructuredOutput]):
|
||||
|
||||
def _conversation_snapshot(self) -> dict[str, Any]:
|
||||
return {
|
||||
'agent': 'browser_use.rust.Agent',
|
||||
'agent': 'browser_use.beta.Agent',
|
||||
'task_id': self.task_id,
|
||||
'session_id': self.session_id,
|
||||
'terminal_session_id': self.terminal_session_id,
|
||||
@@ -6598,12 +6598,12 @@ def _align_browser_use_agent_signatures() -> None:
|
||||
for name, browser_use_method in vars(_PythonAgent).items():
|
||||
if name.startswith('__') and name != '__init__':
|
||||
continue
|
||||
rust_method = getattr(Agent, name, None)
|
||||
if rust_method is None or not callable(browser_use_method) or not callable(rust_method):
|
||||
beta_method = getattr(Agent, name, None)
|
||||
if beta_method is None or not callable(browser_use_method) or not callable(beta_method):
|
||||
continue
|
||||
try:
|
||||
rust_method.__signature__ = inspect.signature(browser_use_method)
|
||||
rust_method.__annotations__ = dict(getattr(browser_use_method, '__annotations__', {}))
|
||||
beta_method.__signature__ = inspect.signature(browser_use_method)
|
||||
beta_method.__annotations__ = dict(getattr(browser_use_method, '__annotations__', {}))
|
||||
except (TypeError, ValueError, AttributeError):
|
||||
continue
|
||||
try:
|
||||
@@ -6612,12 +6612,12 @@ def _align_browser_use_agent_signatures() -> None:
|
||||
pass
|
||||
agent_hook_func = Callable[[_PythonAgent], Awaitable[None]]
|
||||
for name in ('run', 'run_sync'):
|
||||
rust_method = getattr(Agent, name, None)
|
||||
if rust_method is None:
|
||||
beta_method = getattr(Agent, name, None)
|
||||
if beta_method is None:
|
||||
continue
|
||||
try:
|
||||
rust_method.__annotations__['on_step_start'] = agent_hook_func | None
|
||||
rust_method.__annotations__['on_step_end'] = agent_hook_func | None
|
||||
beta_method.__annotations__['on_step_start'] = agent_hook_func | None
|
||||
beta_method.__annotations__['on_step_end'] = agent_hook_func | None
|
||||
except (TypeError, AttributeError, KeyError):
|
||||
continue
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
"""Rust-core Browser Use integration."""
|
||||
|
||||
from browser_use.rust.service import Agent, RustAgentError, find_browser_use_terminal_binary
|
||||
|
||||
__all__ = [
|
||||
'Agent',
|
||||
'RustAgentError',
|
||||
'find_browser_use_terminal_binary',
|
||||
]
|
||||
@@ -7,8 +7,11 @@ Set BU_CDP_URL or BROWSER_USE_CDP_URL to attach to a remote Browser Use cloud br
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from browser_use import BrowserSession
|
||||
from browser_use.rust import Agent
|
||||
from browser_use.beta import Agent, BrowserSession, ChatBrowserUse
|
||||
|
||||
# from browser_use.beta import ChatOpenAI # ChatOpenAI(model='gpt-5.5')
|
||||
# from browser_use.beta import ChatGoogle # ChatGoogle(model='gemini-3.1-pro-preview')
|
||||
# from browser_use.beta import ChatAnthropic # ChatAnthropic(model='claude-opus-4-8')
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
@@ -17,7 +20,14 @@ async def main() -> None:
|
||||
task = os.environ.get('BU_TASK', 'Open https://example.com and report the page title.')
|
||||
max_steps = int(os.environ.get('BU_MAX_STEPS', '20'))
|
||||
|
||||
agent = Agent(task=task, browser_session=browser_session)
|
||||
agent = Agent(
|
||||
task=task,
|
||||
llm=ChatBrowserUse(),
|
||||
# llm=ChatOpenAI(model='gpt-5.5'),
|
||||
# llm=ChatGoogle(model='gemini-3.1-pro-preview'),
|
||||
# llm=ChatAnthropic(model='claude-opus-4-8'), # Sonnet also works well.
|
||||
browser_session=browser_session,
|
||||
)
|
||||
history = await agent.run(max_steps=max_steps)
|
||||
print(history.final_result() or '(no final result)')
|
||||
|
||||
+5
-5
@@ -2,7 +2,7 @@
|
||||
name = "browser-use"
|
||||
description = "Make websites accessible for AI agents"
|
||||
authors = [{ name = "Gregor Zunic" }]
|
||||
version = "0.13.0rc2"
|
||||
version = "0.13.0rc3"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11,<4.0"
|
||||
classifiers = [
|
||||
@@ -59,7 +59,7 @@ dependencies = [
|
||||
|
||||
[project.optional-dependencies]
|
||||
cli = ["textual==7.4.0"]
|
||||
rust = [
|
||||
core = [
|
||||
"browser-use-core==0.13.0rc2; sys_platform == 'darwin' and platform_machine == 'arm64'",
|
||||
"browser-use-core==0.13.0rc2; sys_platform == 'darwin' and platform_machine == 'x86_64'",
|
||||
"browser-use-core==0.13.0rc2; sys_platform == 'linux' and platform_machine == 'x86_64'",
|
||||
@@ -161,9 +161,9 @@ exclude = [
|
||||
"browser_use/llm/oci_raw/*",
|
||||
"browser_use/llm/tests/test_chat_models.py",
|
||||
"browser_use/llm/tests/test_single_step.py",
|
||||
# Dynamic Rust compatibility wrapper; covered by tests/ci/test_rust_agent.py.
|
||||
"browser_use/rust/service.py",
|
||||
"tests/ci/test_rust_agent.py",
|
||||
# Dynamic beta agent wrapper; covered by tests/ci/test_beta_agent.py.
|
||||
"browser_use/beta/service.py",
|
||||
"tests/ci/test_beta_agent.py",
|
||||
"product_extraction.py",
|
||||
"discover/",
|
||||
"list/",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user