gl-browser-use-binary 0.0.0b9__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gl_browser_use/__init__.pyi +12 -0
- gl_browser_use/client.pyi +57 -0
- gl_browser_use/config.pyi +43 -0
- gl_browser_use/errors.pyi +41 -0
- gl_browser_use/events.pyi +29 -0
- gl_browser_use/infrastructure/__init__.pyi +7 -0
- gl_browser_use/infrastructure/base.pyi +55 -0
- gl_browser_use/infrastructure/opensandbox.pyi +55 -0
- gl_browser_use/infrastructure/playwright_recorder.pyi +22 -0
- gl_browser_use/infrastructure/recording_utils.pyi +12 -0
- gl_browser_use/infrastructure/steel.pyi +52 -0
- gl_browser_use/models.pyi +32 -0
- gl_browser_use/parsing.pyi +41 -0
- gl_browser_use/parsing_recovery.pyi +35 -0
- gl_browser_use/session_errors.pyi +16 -0
- gl_browser_use/storage/__init__.pyi +6 -0
- gl_browser_use/storage/base.pyi +46 -0
- gl_browser_use/storage/minio_compatible.pyi +15 -0
- gl_browser_use.cp313-win_amd64.pyd +0 -0
- gl_browser_use.pyi +83 -0
- gl_browser_use_binary-0.0.0b9.dist-info/METADATA +287 -0
- gl_browser_use_binary-0.0.0b9.dist-info/RECORD +24 -0
- gl_browser_use_binary-0.0.0b9.dist-info/WHEEL +5 -0
- gl_browser_use_binary-0.0.0b9.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from gl_browser_use.client import BrowserUseClient as BrowserUseClient
|
|
2
|
+
from gl_browser_use.config import BrowserUseClientConfig as BrowserUseClientConfig
|
|
3
|
+
from gl_browser_use.errors import BrowserUseConfigurationError as BrowserUseConfigurationError, BrowserUseDependencyError as BrowserUseDependencyError, BrowserUseExecutionError as BrowserUseExecutionError, BrowserUseMissingDependencyError as BrowserUseMissingDependencyError, BrowserUseRetryExhaustedError as BrowserUseRetryExhaustedError, BrowserUseSDKError as BrowserUseSDKError
|
|
4
|
+
from gl_browser_use.infrastructure.base import BrowserInfrastructure as BrowserInfrastructure, BrowserInfrastructureContext as BrowserInfrastructureContext
|
|
5
|
+
from gl_browser_use.models import BrowserUseRunResult as BrowserUseRunResult, BrowserUseStreamEvent as BrowserUseStreamEvent
|
|
6
|
+
from gl_browser_use.storage.base import ObjectStorage as ObjectStorage
|
|
7
|
+
|
|
8
|
+
__all__ = ['BrowserUseClient', 'BrowserUseClientConfig', 'BrowserUseConfigurationError', 'BrowserUseDependencyError', 'BrowserUseExecutionError', 'BrowserUseMissingDependencyError', 'BrowserUseRetryExhaustedError', 'BrowserUseSDKError', 'BrowserUseRunResult', 'BrowserUseStreamEvent', 'BrowserInfrastructure', 'BrowserInfrastructureContext', 'SteelBrowserInfrastructure', 'ObjectStorage', 'MinIOS3CompatibleStorage']
|
|
9
|
+
|
|
10
|
+
# Names in __all__ with no definition:
|
|
11
|
+
# MinIOS3CompatibleStorage
|
|
12
|
+
# SteelBrowserInfrastructure
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from collections.abc import AsyncGenerator, Awaitable, Callable
|
|
3
|
+
from gl_browser_use.config import BrowserUseClientConfig as BrowserUseClientConfig
|
|
4
|
+
from gl_browser_use.errors import BrowserUseExecutionError as BrowserUseExecutionError, BrowserUseRetryExhaustedError as BrowserUseRetryExhaustedError
|
|
5
|
+
from gl_browser_use.events import RECORDING_MESSAGE as RECORDING_MESSAGE, STREAMING_MESSAGE as STREAMING_MESSAGE, TASK_COMPLETED_MESSAGE as TASK_COMPLETED_MESSAGE, build_activity_event as build_activity_event, build_error_event as build_error_event, build_retry_event as build_retry_event, build_step_content as build_step_content, build_step_event as build_step_event, build_thinking_event as build_thinking_event
|
|
6
|
+
from gl_browser_use.infrastructure.base import BrowserInfrastructure as BrowserInfrastructure, BrowserInfrastructureContext as BrowserInfrastructureContext
|
|
7
|
+
from gl_browser_use.models import BrowserUseRunResult as BrowserUseRunResult, BrowserUseRunStatus as BrowserUseRunStatus, BrowserUseStreamEvent as BrowserUseStreamEvent
|
|
8
|
+
from gl_browser_use.parsing import coerce_string as coerce_string, extract_final_output as extract_final_output, extract_tool_calls as extract_tool_calls, summarize_terminal_error as summarize_terminal_error, validate_structured_data_output as validate_structured_data_output
|
|
9
|
+
from gl_browser_use.session_errors import is_recoverable_session_error as is_recoverable_session_error
|
|
10
|
+
from gl_browser_use.storage.base import ObjectStorage as ObjectStorage
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
logger: Incomplete
|
|
14
|
+
EmitCallable = Callable[[BrowserUseStreamEvent | None], Awaitable[None] | None]
|
|
15
|
+
|
|
16
|
+
def recording_metadata(*, infrastructure: BrowserInfrastructure | None, storage: ObjectStorage | None, browser_context: BrowserInfrastructureContext | None) -> dict[str, Any]:
|
|
17
|
+
"""Return recording status metadata aligned with implementation behavior."""
|
|
18
|
+
|
|
19
|
+
class BrowserUseClient:
|
|
20
|
+
"""Facade for running browser automation tasks.
|
|
21
|
+
|
|
22
|
+
Note: ``infrastructure`` and ``storage`` attributes are read by ``run``/
|
|
23
|
+
``run_once``/``run_sync`` on each call. Swapping them while a run is in
|
|
24
|
+
flight is undefined behavior.
|
|
25
|
+
"""
|
|
26
|
+
config: Incomplete
|
|
27
|
+
infrastructure: Incomplete
|
|
28
|
+
storage: Incomplete
|
|
29
|
+
def __init__(self, *, config: BrowserUseClientConfig, infrastructure: BrowserInfrastructure | None = None, storage: ObjectStorage | None = None) -> None:
|
|
30
|
+
"""Initialize the client."""
|
|
31
|
+
async def run(self, task: str) -> AsyncGenerator[BrowserUseStreamEvent, None]:
|
|
32
|
+
"""Run the client."""
|
|
33
|
+
async def run_once(self, task: str) -> BrowserUseRunResult:
|
|
34
|
+
"""Run the client once and return a single aggregated result.
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
------
|
|
38
|
+
BrowserUseExecutionError
|
|
39
|
+
When the task input is invalid.
|
|
40
|
+
BrowserUseConfigurationError
|
|
41
|
+
When required configuration is missing.
|
|
42
|
+
BrowserUseRetryExhaustedError
|
|
43
|
+
When all recoverable session retries are exhausted.
|
|
44
|
+
"""
|
|
45
|
+
def run_sync(self, task: str) -> BrowserUseRunResult:
|
|
46
|
+
"""Run the client synchronously.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
------
|
|
50
|
+
BrowserUseExecutionError
|
|
51
|
+
When invoked from a thread that already has a running event loop,
|
|
52
|
+
or when task validation fails.
|
|
53
|
+
BrowserUseConfigurationError
|
|
54
|
+
When required configuration is missing.
|
|
55
|
+
BrowserUseRetryExhaustedError
|
|
56
|
+
When all recoverable session retries are exhausted.
|
|
57
|
+
"""
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from gl_browser_use.errors import BrowserUseConfigurationError as BrowserUseConfigurationError
|
|
3
|
+
from openai.types.shared_params.chat_model import ChatModel
|
|
4
|
+
from openai.types.shared_params.reasoning_effort import ReasoningEffort
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
from typing import Any, Literal
|
|
7
|
+
|
|
8
|
+
class BrowserUseClientConfig(BaseModel):
|
|
9
|
+
"""Typed runtime configuration for :class:`BrowserUseClient`."""
|
|
10
|
+
model_config: Incomplete
|
|
11
|
+
llm_openai_api_key: str | None
|
|
12
|
+
llm_openai_model: ChatModel | str
|
|
13
|
+
llm_openai_temperature: float | None
|
|
14
|
+
llm_openai_reasoning_effort: ReasoningEffort
|
|
15
|
+
llm_openai_base_url: str | None
|
|
16
|
+
page_extraction_llm_openai_api_key: str | None
|
|
17
|
+
page_extraction_llm_openai_model: ChatModel | str
|
|
18
|
+
page_extraction_llm_openai_temperature: float | None
|
|
19
|
+
page_extraction_llm_openai_reasoning_effort: ReasoningEffort
|
|
20
|
+
page_extraction_llm_openai_base_url: str | None
|
|
21
|
+
sensitive_data: dict[str, str | dict[str, str]] | None
|
|
22
|
+
extend_system_message: str | None
|
|
23
|
+
vision_detail_level: Literal['auto', 'low', 'high']
|
|
24
|
+
llm_timeout_in_s: int | None
|
|
25
|
+
step_timeout_in_s: int
|
|
26
|
+
agent_kwargs: dict[str, Any]
|
|
27
|
+
enable_cloud_sync: bool
|
|
28
|
+
logging_level: Literal['debug', 'info', 'warning', 'error', 'result']
|
|
29
|
+
max_steps: int
|
|
30
|
+
max_session_retries: int
|
|
31
|
+
session_retry_delay_in_s: float
|
|
32
|
+
def validate(self, *, strict: bool = False) -> list[str]:
|
|
33
|
+
"""Validate required runtime values.
|
|
34
|
+
|
|
35
|
+
Returns a list of field-level validation issues. If ``strict`` is
|
|
36
|
+
``True``, raise :class:`BrowserUseConfigurationError` on any issues.
|
|
37
|
+
"""
|
|
38
|
+
def redacted_dict(self, *, include_secrets: bool = False) -> dict[str, Any]:
|
|
39
|
+
'''Return a config dictionary safe to log.
|
|
40
|
+
|
|
41
|
+
Fields marked with ``json_schema_extra={"secret": True}`` are replaced
|
|
42
|
+
with ``"***"`` unless ``include_secrets`` is ``True``.
|
|
43
|
+
'''
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from typing import Any, Final
|
|
3
|
+
|
|
4
|
+
__all__ = ['MINIO_EXTRA', 'OPENSANDBOX_EXTRA', 'PACKAGE_NAME', 'STEEL_EXTRA', 'BrowserUseConfigurationError', 'BrowserUseDependencyError', 'BrowserUseExecutionError', 'BrowserUseMissingDependencyError', 'BrowserUseRetryExhaustedError', 'BrowserUseSDKError', 'install_hint']
|
|
5
|
+
|
|
6
|
+
PACKAGE_NAME: Final[str]
|
|
7
|
+
STEEL_EXTRA: Final[str]
|
|
8
|
+
OPENSANDBOX_EXTRA: Final[str]
|
|
9
|
+
MINIO_EXTRA: Final[str]
|
|
10
|
+
|
|
11
|
+
def install_hint(extra: str) -> str:
|
|
12
|
+
"""Return a ``pip install`` hint for the given optional extra."""
|
|
13
|
+
|
|
14
|
+
class BrowserUseSDKError(RuntimeError):
|
|
15
|
+
"""Base exception for all SDK errors."""
|
|
16
|
+
message: Incomplete
|
|
17
|
+
details: Incomplete
|
|
18
|
+
origin: Incomplete
|
|
19
|
+
def __init__(self, message: str, *, details: dict[str, Any] | None = None, origin: BaseException | None = None) -> None:
|
|
20
|
+
"""Initialize the error."""
|
|
21
|
+
|
|
22
|
+
class BrowserUseDependencyError(BrowserUseSDKError):
|
|
23
|
+
"""Raised when required optional dependencies are unavailable."""
|
|
24
|
+
missing_dependency: Incomplete
|
|
25
|
+
package_hint: Incomplete
|
|
26
|
+
def __init__(self, message: str, *, missing_dependency: str | None = None, package_hint: str | None = None, details: dict[str, Any] | None = None, origin: BaseException | None = None) -> None:
|
|
27
|
+
"""Initialize the error."""
|
|
28
|
+
|
|
29
|
+
class BrowserUseMissingDependencyError(BrowserUseDependencyError):
|
|
30
|
+
"""Raised when an optional extra dependency is not installed."""
|
|
31
|
+
class BrowserUseConfigurationError(BrowserUseSDKError):
|
|
32
|
+
"""Raised when required configuration values are missing or invalid."""
|
|
33
|
+
class BrowserUseExecutionError(BrowserUseSDKError):
|
|
34
|
+
"""Raised when execution of a run fails."""
|
|
35
|
+
|
|
36
|
+
class BrowserUseRetryExhaustedError(BrowserUseExecutionError):
|
|
37
|
+
"""Raised when session retries are exhausted after recoverable errors."""
|
|
38
|
+
attempt: Incomplete
|
|
39
|
+
max_attempts: Incomplete
|
|
40
|
+
def __init__(self, message: str, *, attempt: int, max_attempts: int, details: dict[str, Any] | None = None, origin: BaseException | None = None) -> None:
|
|
41
|
+
"""Initialize the error."""
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from gl_browser_use.models import BrowserUseStreamEvent
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
__all__ = ['EVENT_ID', 'PROCESSING_MESSAGE', 'RECORDING_MESSAGE', 'STREAMING_MESSAGE', 'TASK_COMPLETED_MESSAGE', 'build_activity_event', 'build_error_event', 'build_fallback_thinking_message', 'build_retry_event', 'build_step_content', 'build_step_event', 'build_thinking_event']
|
|
5
|
+
|
|
6
|
+
EVENT_ID: str
|
|
7
|
+
TASK_COMPLETED_MESSAGE: str
|
|
8
|
+
PROCESSING_MESSAGE: str
|
|
9
|
+
STREAMING_MESSAGE: str
|
|
10
|
+
RECORDING_MESSAGE: str
|
|
11
|
+
|
|
12
|
+
def build_activity_event(url: str, content: str) -> BrowserUseStreamEvent:
|
|
13
|
+
"""Return an activity event carrying ``url`` in the standard iframe payload."""
|
|
14
|
+
def build_thinking_event(marker: str, *, content: str, final: bool = False) -> BrowserUseStreamEvent:
|
|
15
|
+
"""Return a thinking-marker event for ``thinking_start``/``thinking_end``/``thinking``."""
|
|
16
|
+
def build_step_event(tool_calls: list[Any], content: str, *, is_final: bool) -> BrowserUseStreamEvent:
|
|
17
|
+
"""Return a step event summarizing ``tool_calls`` with a fallback thinking narration."""
|
|
18
|
+
def build_error_event(error_message: str, *, recording_url: str = '') -> BrowserUseStreamEvent:
|
|
19
|
+
"""Return a terminal tool-result error event with optional ``recording_url``.
|
|
20
|
+
|
|
21
|
+
``error_message`` is expected to be already summarized by the caller via
|
|
22
|
+
:func:`gl_browser_use.parsing.summarize_terminal_error`.
|
|
23
|
+
"""
|
|
24
|
+
def build_retry_event(attempt: int, max_attempts: int, reason: str) -> BrowserUseStreamEvent:
|
|
25
|
+
"""Return a retry status event carrying attempt counters and the recoverable reason."""
|
|
26
|
+
def build_fallback_thinking_message(content: str, tool_calls: list[Any]) -> str:
|
|
27
|
+
"""Build a fallback thinking narration that lists recent tool-call outputs."""
|
|
28
|
+
def build_step_content(tool_calls: list[Any], is_done: bool) -> str:
|
|
29
|
+
"""Return a user-visible content string for a step event based on ``tool_calls``."""
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from gl_browser_use.infrastructure.base import BrowserInfrastructure as BrowserInfrastructure, BrowserInfrastructureContext as BrowserInfrastructureContext
|
|
2
|
+
|
|
3
|
+
__all__ = ['BrowserInfrastructure', 'BrowserInfrastructureContext', 'OpenSandboxBrowserInfrastructure', 'SteelBrowserInfrastructure']
|
|
4
|
+
|
|
5
|
+
# Names in __all__ with no definition:
|
|
6
|
+
# OpenSandboxBrowserInfrastructure
|
|
7
|
+
# SteelBrowserInfrastructure
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from gl_browser_use.storage.base import ObjectStorage
|
|
4
|
+
from typing import Any, Generic, TypeVar
|
|
5
|
+
|
|
6
|
+
__all__ = ['BrowserInfrastructure', 'BrowserInfrastructureContext']
|
|
7
|
+
|
|
8
|
+
ClientType = TypeVar('ClientType')
|
|
9
|
+
SessionType = TypeVar('SessionType')
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class BrowserInfrastructureContext(Generic[ClientType, SessionType]):
|
|
13
|
+
"""Runtime context for an active browser infrastructure session."""
|
|
14
|
+
client: ClientType
|
|
15
|
+
session: SessionType
|
|
16
|
+
session_id: str
|
|
17
|
+
streaming_url: str
|
|
18
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
19
|
+
|
|
20
|
+
class BrowserInfrastructure(ABC, Generic[ClientType, SessionType]):
|
|
21
|
+
"""Abstract contract for browser infrastructure providers.
|
|
22
|
+
|
|
23
|
+
Provider-specific configuration (API keys, endpoints, timeouts) belongs to
|
|
24
|
+
the concrete subclass; this base only defines the behavioral surface.
|
|
25
|
+
"""
|
|
26
|
+
@property
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def provider(self) -> str:
|
|
29
|
+
"""Return the human-readable infrastructure provider name."""
|
|
30
|
+
@property
|
|
31
|
+
def supports_recording(self) -> bool:
|
|
32
|
+
"""Whether this infrastructure can produce session recordings."""
|
|
33
|
+
@abstractmethod
|
|
34
|
+
async def create_session(self) -> BrowserInfrastructureContext[ClientType, SessionType]:
|
|
35
|
+
"""Create a new browser infrastructure session and return the active context."""
|
|
36
|
+
@abstractmethod
|
|
37
|
+
async def release_session(self, context: BrowserInfrastructureContext[ClientType, SessionType]) -> None:
|
|
38
|
+
"""Release or dispose browser infrastructure session resources."""
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def cdp_url(self, session_id: str) -> str:
|
|
41
|
+
"""Build a CDP endpoint URL for the provided browser infrastructure session ID."""
|
|
42
|
+
def recording_filename(self, session_id: str) -> str:
|
|
43
|
+
"""Return the preferred recording object name for the provided browser infrastructure session ID."""
|
|
44
|
+
def resolve_recording_url(self, session_id: str, storage: ObjectStorage | None) -> str | None:
|
|
45
|
+
"""Resolve a recording URL from storage without scheduling any new work."""
|
|
46
|
+
def can_record(self, context: BrowserInfrastructureContext[ClientType, SessionType] | None, storage: ObjectStorage | None) -> bool:
|
|
47
|
+
'''Return True when an active recording can be produced for ``context``.
|
|
48
|
+
|
|
49
|
+
This encapsulates the "recording is supported, a live context exists,
|
|
50
|
+
and storage is available" check that clients use before scheduling a
|
|
51
|
+
recording or resolving a recording URL.
|
|
52
|
+
'''
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def schedule_recording(self, context: BrowserInfrastructureContext[ClientType, SessionType], storage: ObjectStorage | None) -> None:
|
|
55
|
+
"""Optionally schedule background recording for the provided context."""
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
from gl_browser_use.infrastructure.base import BrowserInfrastructure, BrowserInfrastructureContext
|
|
4
|
+
from gl_browser_use.storage.base import ObjectStorage
|
|
5
|
+
from opensandbox.sandbox import Sandbox
|
|
6
|
+
|
|
7
|
+
__all__ = ['DEFAULT_OPENSANDBOX_DOMAIN', 'DEFAULT_OPENSANDBOX_IMAGE', 'OpenSandboxBrowserInfrastructure', '_build_novnc_url', '_extract_browser_id', '_fetch_web_socket_debugger_url', '_parse_domain', '_rewrite_cdp_url']
|
|
8
|
+
|
|
9
|
+
DEFAULT_OPENSANDBOX_DOMAIN: str
|
|
10
|
+
DEFAULT_OPENSANDBOX_IMAGE: str
|
|
11
|
+
|
|
12
|
+
def _parse_domain(raw: str | None) -> tuple[str, str]:
|
|
13
|
+
"""Return ``(protocol, domain)`` from a domain string or URL."""
|
|
14
|
+
def _fetch_web_socket_debugger_url(devtools_endpoint: str, *, protocol: str, poll_timeout_s: float = ..., poll_interval_s: float = ...) -> str:
|
|
15
|
+
"""Poll DevTools ``/json/version`` until ``webSocketDebuggerUrl`` is available."""
|
|
16
|
+
def _extract_browser_id(web_socket_debugger_url: str) -> str: ...
|
|
17
|
+
def _rewrite_cdp_url(devtools_endpoint: str, browser_id: str, ws_protocol: str) -> str: ...
|
|
18
|
+
def _build_novnc_url(novnc_endpoint: str, http_protocol: str) -> str:
|
|
19
|
+
"""Build a noVNC URL that auto-connects to the sandbox VNC stream."""
|
|
20
|
+
|
|
21
|
+
class OpenSandboxBrowserInfrastructure(BrowserInfrastructure[Sandbox, Sandbox]):
|
|
22
|
+
"""Concrete OpenSandbox implementation for browser infrastructure sessions."""
|
|
23
|
+
domain: Incomplete
|
|
24
|
+
protocol: Incomplete
|
|
25
|
+
api_key: Incomplete
|
|
26
|
+
use_server_proxy: Incomplete
|
|
27
|
+
image: Incomplete
|
|
28
|
+
entrypoint: Incomplete
|
|
29
|
+
timeout: Incomplete
|
|
30
|
+
devtools_poll_timeout_s: Incomplete
|
|
31
|
+
def __init__(self, *, domain: str | None = None, api_key: str | None = None, protocol: str | None = None, use_server_proxy: bool | None = None, image: str = ..., entrypoint: list[str] | None = None, timeout: timedelta = ..., devtools_poll_timeout_s: float = ...) -> None:
|
|
32
|
+
"""Initialize OpenSandbox browser infrastructure.
|
|
33
|
+
|
|
34
|
+
``domain`` defaults to ``OPENSANDBOX_DOMAIN`` or ``localhost:8080``.
|
|
35
|
+
``api_key`` defaults to ``OPENSANDBOX_API_KEY``.
|
|
36
|
+
"""
|
|
37
|
+
@property
|
|
38
|
+
def provider(self) -> str:
|
|
39
|
+
"""Return the human-readable infrastructure provider name."""
|
|
40
|
+
@property
|
|
41
|
+
def supports_recording(self) -> bool:
|
|
42
|
+
"""Whether Playwright-based noVNC recording is available."""
|
|
43
|
+
async def create_session(self) -> BrowserInfrastructureContext[Sandbox, Sandbox]:
|
|
44
|
+
"""Create a new OpenSandbox session and return the active context."""
|
|
45
|
+
async def release_session(self, context: BrowserInfrastructureContext[Sandbox, Sandbox]) -> None:
|
|
46
|
+
"""Release OpenSandbox resources.
|
|
47
|
+
|
|
48
|
+
Stops an orphaned recorder when recording was started but never scheduled.
|
|
49
|
+
"""
|
|
50
|
+
def cdp_url(self, session_id: str) -> str:
|
|
51
|
+
"""Build a CDP endpoint URL for the provided session ID."""
|
|
52
|
+
def recording_filename(self, session_id: str) -> str:
|
|
53
|
+
"""Return the preferred recording object name for the provided session ID."""
|
|
54
|
+
def schedule_recording(self, context: BrowserInfrastructureContext[Sandbox, Sandbox], storage: ObjectStorage | None) -> None:
|
|
55
|
+
"""Stop the recorder and schedule background upload to object storage."""
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
from gl_browser_use.storage.base import ObjectStorage
|
|
3
|
+
|
|
4
|
+
__all__ = ['PlaywrightRecorder', 'is_playwright_available']
|
|
5
|
+
|
|
6
|
+
def is_playwright_available() -> bool:
|
|
7
|
+
"""Return True when Playwright is installed."""
|
|
8
|
+
|
|
9
|
+
class PlaywrightRecorder:
|
|
10
|
+
"""Record a noVNC page in headless Chromium and upload to object storage."""
|
|
11
|
+
def __init__(self) -> None: ...
|
|
12
|
+
async def start(self, novnc_url: str, width: int = 1280, height: int = 1024) -> None:
|
|
13
|
+
"""Open the noVNC URL and start recording."""
|
|
14
|
+
async def stop(self) -> tuple[bytes, str] | None:
|
|
15
|
+
"""Stop recording and return WebM bytes."""
|
|
16
|
+
async def stop_and_upload(self, storage: ObjectStorage, object_name: str) -> str | None:
|
|
17
|
+
"""Stop recording, upload WebM via object storage, and return the remote name."""
|
|
18
|
+
async def stop_best_effort(self) -> tuple[bytes, str] | None:
|
|
19
|
+
"""Stop recording, suppressing errors."""
|
|
20
|
+
@classmethod
|
|
21
|
+
def schedule_stop_and_upload_in_background(cls, recorder: PlaywrightRecorder, storage: ObjectStorage | None, object_name: str, *, session_id: str = '') -> threading.Thread | None:
|
|
22
|
+
"""Finalize Playwright on the event loop and upload in a background thread."""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from os import PathLike
|
|
2
|
+
|
|
3
|
+
__all__ = ['cleanup_temp_directory', 'recording_filename', 'safe_session_id', 'RECORDING_FILENAME_PREFIX']
|
|
4
|
+
|
|
5
|
+
RECORDING_FILENAME_PREFIX: str
|
|
6
|
+
|
|
7
|
+
def safe_session_id(session_id: str) -> str:
|
|
8
|
+
"""Return a storage-safe session identifier."""
|
|
9
|
+
def recording_filename(session_id: str, extension: str = '.mp4') -> str:
|
|
10
|
+
"""Return the preferred recording file name for a session ID."""
|
|
11
|
+
def cleanup_temp_directory(path: str | bytes | PathLike[str] | PathLike[bytes]) -> None:
|
|
12
|
+
"""Remove a temporary recording directory, ignoring missing paths."""
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
from _typeshed import Incomplete
|
|
3
|
+
from gl_browser_use.infrastructure.base import BrowserInfrastructure, BrowserInfrastructureContext
|
|
4
|
+
from gl_browser_use.storage.base import ObjectStorage
|
|
5
|
+
from steel import Steel
|
|
6
|
+
from steel.types.session import Session
|
|
7
|
+
|
|
8
|
+
__all__ = ['SteelBrowserInfrastructure']
|
|
9
|
+
|
|
10
|
+
class _SteelSessionRecorder:
|
|
11
|
+
"""Capture Steel HLS sessions and upload recordings to object storage."""
|
|
12
|
+
base_url: Incomplete
|
|
13
|
+
api_key: Incomplete
|
|
14
|
+
storage: Incomplete
|
|
15
|
+
def __init__(self, base_url: str, api_key: str, *, storage: ObjectStorage | None = None) -> None: ...
|
|
16
|
+
async def record_session_to_video(self, session_id: str) -> bool: ...
|
|
17
|
+
@classmethod
|
|
18
|
+
def schedule_recording_in_background(cls, session_id: str, base_url: str, api_key: str, storage: ObjectStorage | None) -> threading.Thread | None: ...
|
|
19
|
+
|
|
20
|
+
class SteelBrowserInfrastructure(BrowserInfrastructure[Steel, Session]):
|
|
21
|
+
"""Concrete Steel implementation for browser infrastructure sessions."""
|
|
22
|
+
api_key: Incomplete
|
|
23
|
+
base_url: Incomplete
|
|
24
|
+
ws_url: Incomplete
|
|
25
|
+
timeout_in_ms: Incomplete
|
|
26
|
+
def __init__(self, api_key: str | None = None, base_url: str = ..., ws_url: str = ..., timeout_in_ms: int = ...) -> None:
|
|
27
|
+
"""Initialize the Steel browser infrastructure.
|
|
28
|
+
|
|
29
|
+
``api_key`` defaults to the ``STEEL_API_KEY`` environment variable
|
|
30
|
+
resolved at construction time (not import time) when not provided.
|
|
31
|
+
"""
|
|
32
|
+
@property
|
|
33
|
+
def provider(self) -> str:
|
|
34
|
+
"""Return the human-readable infrastructure provider name."""
|
|
35
|
+
@property
|
|
36
|
+
def supports_recording(self) -> bool:
|
|
37
|
+
"""Whether this infrastructure can produce session recordings.
|
|
38
|
+
|
|
39
|
+
``True`` indicates Steel supports recording. Recording is only
|
|
40
|
+
actually started when an available :class:`ObjectStorage` is also
|
|
41
|
+
provided to the :class:`BrowserUseClient`.
|
|
42
|
+
"""
|
|
43
|
+
async def create_session(self) -> BrowserInfrastructureContext[Steel, Session]:
|
|
44
|
+
"""Create a new Steel session and return the active context."""
|
|
45
|
+
async def release_session(self, context: BrowserInfrastructureContext[Steel, Session]) -> None:
|
|
46
|
+
"""Release or dispose Steel session resources."""
|
|
47
|
+
def cdp_url(self, session_id: str) -> str:
|
|
48
|
+
"""Build a CDP endpoint URL for the provided Steel session ID."""
|
|
49
|
+
def recording_filename(self, session_id: str) -> str:
|
|
50
|
+
"""Return the preferred recording object name for the provided Steel session ID."""
|
|
51
|
+
def schedule_recording(self, context: BrowserInfrastructureContext[Steel, Session], storage: ObjectStorage | None) -> None:
|
|
52
|
+
"""Schedule background recording for the provided context."""
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
BrowserUseRunStatus: Incomplete
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class BrowserUseStreamEvent:
|
|
9
|
+
"""Structured stream event emitted during ``BrowserUseClient`` execution."""
|
|
10
|
+
event_type: str
|
|
11
|
+
content: str
|
|
12
|
+
thinking_and_activity_info: dict[str, Any] = field(default_factory=dict)
|
|
13
|
+
is_final: bool = ...
|
|
14
|
+
tool_info: dict[str, Any] | None = ...
|
|
15
|
+
metadata: dict[str, Any] | None = ...
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class BrowserUseRunResult:
|
|
19
|
+
"""Final result returned for a completed run."""
|
|
20
|
+
status: BrowserUseRunStatus
|
|
21
|
+
task: str
|
|
22
|
+
final_output: str
|
|
23
|
+
session_id: str | None = ...
|
|
24
|
+
streaming_url: str | None = ...
|
|
25
|
+
recording_url: str | None = ...
|
|
26
|
+
steps: int = ...
|
|
27
|
+
error: str | None = ...
|
|
28
|
+
events: list[BrowserUseStreamEvent] = field(default_factory=list)
|
|
29
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
30
|
+
@property
|
|
31
|
+
def is_success(self) -> bool:
|
|
32
|
+
"""Return True if the run was successful."""
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from browser_use import Agent
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
__all__ = ['ToolCall', 'coerce_string', 'extract_final_output', 'extract_outputs', 'extract_tool_calls', 'parse_structured_data', 'read_tool_call', 'summarize_terminal_error', 'validate_structured_data_output']
|
|
6
|
+
|
|
7
|
+
def coerce_string(value: Any) -> str:
|
|
8
|
+
'''Return a stable ``str`` representation of ``value`` (``None`` becomes ``""``).'''
|
|
9
|
+
def summarize_terminal_error(error_message: str, *, max_length: int = 200) -> str:
|
|
10
|
+
"""Collapse whitespace in ``error_message`` and truncate to ``max_length`` characters."""
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ToolCall:
|
|
14
|
+
"""Normalized tool-call snapshot extracted from the underlying agent state."""
|
|
15
|
+
name: str
|
|
16
|
+
args: dict[str, Any]
|
|
17
|
+
output: str = ...
|
|
18
|
+
|
|
19
|
+
def read_tool_call(call: Any) -> tuple[str, dict[str, Any], str]:
|
|
20
|
+
"""Return a ``(name, args, output)`` triple for a tool call of any supported shape.
|
|
21
|
+
|
|
22
|
+
``name`` may be an empty string when the underlying payload is missing
|
|
23
|
+
a name; callers requiring a fallback label should apply one explicitly.
|
|
24
|
+
"""
|
|
25
|
+
def extract_outputs(last_result: list[Any]) -> list[str]:
|
|
26
|
+
"""Return one display string per entry in ``last_result``."""
|
|
27
|
+
def extract_tool_calls(agent: Agent) -> list[ToolCall]:
|
|
28
|
+
"""Extract serialized tool-call snapshots from the agent's last model output."""
|
|
29
|
+
def extract_final_output(agent: Agent) -> str:
|
|
30
|
+
"""Return the final textual output from the agent, or an empty string if absent."""
|
|
31
|
+
def parse_structured_data(output: str) -> tuple[dict[str, Any] | list[Any] | None, str | None]:
|
|
32
|
+
"""Parse the ``Extracted Content:`` block in ``output`` with fallback recovery.
|
|
33
|
+
|
|
34
|
+
The parser (1) trims trailers and slices from the first ``{``/``[``;
|
|
35
|
+
(2) attempts a direct ``json.loads``; (3) attempts to recover
|
|
36
|
+
concatenated top-level JSON objects; (4) extracts a balanced-prefix
|
|
37
|
+
JSON value with a depth-aware scanner that respects string literals
|
|
38
|
+
and parses it; and finally (5) falls back to ``json_repair.repair_json``.
|
|
39
|
+
"""
|
|
40
|
+
def validate_structured_data_output(tool_calls: list[ToolCall]) -> str | None:
|
|
41
|
+
"""Return a user-facing error when structured data extraction failed or yielded no usable entries."""
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
__all__ = ['extract_balanced_json_value', 'recover_concatenated_json_objects', 'repair_json_blob']
|
|
5
|
+
|
|
6
|
+
def extract_balanced_json_value(text: str) -> str | None:
|
|
7
|
+
"""Return the first balanced JSON value found in ``text`` (respecting string literals).
|
|
8
|
+
|
|
9
|
+
Returns ``None`` when no ``{``/``[`` is found or when the scan never
|
|
10
|
+
closes cleanly (unbalanced, truncated, or ends inside a string).
|
|
11
|
+
"""
|
|
12
|
+
def recover_concatenated_json_objects(json_blob: str) -> dict[str, Any] | None:
|
|
13
|
+
"""Normalize concatenated JSON object strings into a single structured payload.
|
|
14
|
+
|
|
15
|
+
Returns ``None`` if ``json_blob`` does not contain multiple top-level JSON
|
|
16
|
+
objects or they cannot be parsed individually.
|
|
17
|
+
"""
|
|
18
|
+
def repair_json_blob(json_blob: str) -> str | None:
|
|
19
|
+
"""Apply ``json_repair.repair_json`` to ``json_blob`` and return the mutated payload.
|
|
20
|
+
|
|
21
|
+
Returns ``None`` when ``json_repair`` could not repair the input or when
|
|
22
|
+
the repair produced no changes.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
class _JsonObjectSplitter:
|
|
26
|
+
"""Split concatenated top-level JSON objects while respecting string literals."""
|
|
27
|
+
json_blob: Incomplete
|
|
28
|
+
segments: list[str]
|
|
29
|
+
depth: int
|
|
30
|
+
start: int | None
|
|
31
|
+
last_end: int
|
|
32
|
+
in_string: bool
|
|
33
|
+
escaping: bool
|
|
34
|
+
def __init__(self, json_blob: str) -> None: ...
|
|
35
|
+
def split_objects(self) -> list[str]: ...
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
__all__ = ['SessionErrorCategory', 'categorize_fatal_session_message', 'is_recoverable_session_error']
|
|
4
|
+
|
|
5
|
+
@dataclass(frozen=True)
|
|
6
|
+
class SessionErrorCategory:
|
|
7
|
+
"""Session error classification for retry decisions."""
|
|
8
|
+
name: str
|
|
9
|
+
markers: tuple[str, ...]
|
|
10
|
+
fatal: bool
|
|
11
|
+
retryable: bool
|
|
12
|
+
|
|
13
|
+
def categorize_fatal_session_message(message: str) -> SessionErrorCategory | None:
|
|
14
|
+
"""Return the matching :class:`SessionErrorCategory` for ``message`` or ``None``."""
|
|
15
|
+
def is_recoverable_session_error(message: str) -> bool:
|
|
16
|
+
"""Return True when ``message`` matches a recoverable session-failure category."""
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
__all__ = ['ObjectStorage', 'OBJECT_NAME_PREFIX']
|
|
5
|
+
|
|
6
|
+
OBJECT_NAME_PREFIX: str
|
|
7
|
+
|
|
8
|
+
class ObjectStorage(ABC):
|
|
9
|
+
"""Abstract contract for object storage providers."""
|
|
10
|
+
endpoint: Incomplete
|
|
11
|
+
access_key: Incomplete
|
|
12
|
+
secret_key: Incomplete
|
|
13
|
+
bucket_name: Incomplete
|
|
14
|
+
directory_prefix: Incomplete
|
|
15
|
+
secure: Incomplete
|
|
16
|
+
def __init__(self, *, endpoint: str | None = None, access_key: str | None = None, secret_key: str | None = None, bucket_name: str | None = None, directory_prefix: str | None = None, secure: bool | None = None) -> None:
|
|
17
|
+
"""Initialize the object storage.
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
------
|
|
21
|
+
BrowserUseConfigurationError
|
|
22
|
+
When any of ``endpoint``, ``access_key``, ``secret_key`` or
|
|
23
|
+
``bucket_name`` is missing from both the arguments and the
|
|
24
|
+
corresponding ``OBJECT_STORAGE_*`` environment variable.
|
|
25
|
+
"""
|
|
26
|
+
@property
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def provider(self) -> str:
|
|
29
|
+
"""Return the human-readable storage provider name."""
|
|
30
|
+
@property
|
|
31
|
+
def is_available(self) -> bool:
|
|
32
|
+
"""Whether this storage provider is currently available."""
|
|
33
|
+
@property
|
|
34
|
+
def last_error(self) -> str | None:
|
|
35
|
+
"""Return the last error encountered during client initialization or operation."""
|
|
36
|
+
@classmethod
|
|
37
|
+
def from_environment(cls) -> ObjectStorage:
|
|
38
|
+
"""Create a storage instance from environment variables."""
|
|
39
|
+
def resolve_object_name(self, object_name: str) -> str:
|
|
40
|
+
"""Return the effective remote object name."""
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def upload_file(self, file_path: str, object_name: str) -> str | None:
|
|
43
|
+
"""Upload a file and optionally return the stored object name."""
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def get_file_url(self, object_name: str) -> str | None:
|
|
46
|
+
"""Return an object URL when available."""
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from gl_browser_use.storage.base import ObjectStorage
|
|
2
|
+
|
|
3
|
+
__all__ = ['MinIOS3CompatibleStorage']
|
|
4
|
+
|
|
5
|
+
class MinIOS3CompatibleStorage(ObjectStorage):
|
|
6
|
+
"""Concrete S3-compatible storage implemented with MinIO."""
|
|
7
|
+
def __init__(self, *, endpoint: str | None = None, access_key: str | None = None, secret_key: str | None = None, bucket_name: str | None = None, directory_prefix: str | None = None, secure: bool | None = None) -> None:
|
|
8
|
+
"""Initialize the MinIO/S3-compatible storage."""
|
|
9
|
+
@property
|
|
10
|
+
def provider(self) -> str:
|
|
11
|
+
"""Return the human-readable storage provider name."""
|
|
12
|
+
def upload_file(self, file_path: str, object_name: str) -> str | None:
|
|
13
|
+
"""Upload a file and optionally return the stored object name."""
|
|
14
|
+
def get_file_url(self, object_name: str) -> str | None:
|
|
15
|
+
"""Return an object URL when available."""
|
|
Binary file
|
gl_browser_use.pyi
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# This file was generated by Nuitka
|
|
2
|
+
|
|
3
|
+
# Stubs included by default
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from gl_browser_use.client import BrowserUseClient
|
|
7
|
+
from gl_browser_use.config import BrowserUseClientConfig
|
|
8
|
+
from gl_browser_use.errors import BrowserUseConfigurationError, BrowserUseDependencyError, BrowserUseExecutionError, BrowserUseMissingDependencyError, BrowserUseRetryExhaustedError, BrowserUseSDKError
|
|
9
|
+
from gl_browser_use.infrastructure import __getattr__
|
|
10
|
+
from gl_browser_use.infrastructure.base import BrowserInfrastructure, BrowserInfrastructureContext
|
|
11
|
+
from gl_browser_use.models import BrowserUseRunResult, BrowserUseStreamEvent
|
|
12
|
+
from gl_browser_use.storage import __getattr__
|
|
13
|
+
from gl_browser_use.storage.base import ObjectStorage
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
__all__ = ['BrowserUseClient', 'BrowserUseClientConfig', 'BrowserUseConfigurationError', 'BrowserUseDependencyError', 'BrowserUseExecutionError', 'BrowserUseMissingDependencyError', 'BrowserUseRetryExhaustedError', 'BrowserUseSDKError', 'BrowserUseRunResult', 'BrowserUseStreamEvent', 'BrowserInfrastructure', 'BrowserInfrastructureContext', 'SteelBrowserInfrastructure', 'ObjectStorage', 'MinIOS3CompatibleStorage']
|
|
17
|
+
def __getattr__(name: str) -> Any:
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
def __dir__() -> list[str]:
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
__version__ = '0.1.0'
|
|
24
|
+
|
|
25
|
+
__name__ = ...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Modules used internally, to allow implicit dependencies to be seen:
|
|
30
|
+
import os
|
|
31
|
+
import __future__
|
|
32
|
+
import gl_browser_use.infrastructure.__getattr__
|
|
33
|
+
import gl_browser_use.storage.__getattr__
|
|
34
|
+
import asyncio
|
|
35
|
+
import contextlib
|
|
36
|
+
import copy
|
|
37
|
+
import dataclasses
|
|
38
|
+
import inspect
|
|
39
|
+
import logging
|
|
40
|
+
import time
|
|
41
|
+
import collections
|
|
42
|
+
import collections.abc
|
|
43
|
+
import typing
|
|
44
|
+
import browser_use
|
|
45
|
+
import browser_use.agent
|
|
46
|
+
import browser_use.agent.cloud_events
|
|
47
|
+
import browser_use.agent.views
|
|
48
|
+
import browser_use.browser
|
|
49
|
+
import browser_use.llm
|
|
50
|
+
import browser_use.logging_config
|
|
51
|
+
import bubus
|
|
52
|
+
import openai
|
|
53
|
+
import openai.types
|
|
54
|
+
import openai.types.shared_params
|
|
55
|
+
import openai.types.shared_params.chat_model
|
|
56
|
+
import openai.types.shared_params.reasoning_effort
|
|
57
|
+
import urllib
|
|
58
|
+
import urllib.parse
|
|
59
|
+
import pydantic
|
|
60
|
+
import json
|
|
61
|
+
import textwrap
|
|
62
|
+
import abc
|
|
63
|
+
import re
|
|
64
|
+
import datetime
|
|
65
|
+
import requests
|
|
66
|
+
import opensandbox
|
|
67
|
+
import opensandbox.config
|
|
68
|
+
import opensandbox.sandbox
|
|
69
|
+
import tempfile
|
|
70
|
+
import threading
|
|
71
|
+
import pathlib
|
|
72
|
+
import pathlib.Path
|
|
73
|
+
import importlib
|
|
74
|
+
import playwright
|
|
75
|
+
import playwright.async_api
|
|
76
|
+
import shutil
|
|
77
|
+
import io
|
|
78
|
+
import steel
|
|
79
|
+
import steel.types
|
|
80
|
+
import steel.types.session
|
|
81
|
+
import json_repair
|
|
82
|
+
import mimetypes
|
|
83
|
+
import minio
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: gl-browser-use-binary
|
|
3
|
+
Version: 0.0.0b9
|
|
4
|
+
Summary: A typed Python SDK for running browser automation tasks through `browser-use`
|
|
5
|
+
Author-email: Reinhart Linanda <reinhart.linanda@gdplabs.id>
|
|
6
|
+
Requires-Python: <3.14,>=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: browser-use<0.12.0,>=0.11.13
|
|
9
|
+
Requires-Dist: json-repair<0.61.0,>=0.60.1
|
|
10
|
+
Requires-Dist: pydantic<3.0.0,>=2.11.7
|
|
11
|
+
Requires-Dist: requests<3.0.0,>=2.32.4
|
|
12
|
+
Requires-Dist: pydantic-settings<3.0.0,>=2.5.2
|
|
13
|
+
Provides-Extra: steel
|
|
14
|
+
Requires-Dist: steel-sdk<0.17.0,>=0.16.0; extra == "steel"
|
|
15
|
+
Provides-Extra: opensandbox
|
|
16
|
+
Requires-Dist: opensandbox<0.2.0,>=0.1.9; extra == "opensandbox"
|
|
17
|
+
Requires-Dist: playwright<2.0.0,>=1.40.0; extra == "opensandbox"
|
|
18
|
+
Provides-Extra: infrastructure
|
|
19
|
+
Requires-Dist: gl-browser-use-binary[opensandbox,steel]; extra == "infrastructure"
|
|
20
|
+
Provides-Extra: minio
|
|
21
|
+
Requires-Dist: minio<8.0.0,>=7.2.20; extra == "minio"
|
|
22
|
+
Provides-Extra: storage
|
|
23
|
+
Requires-Dist: gl-browser-use-binary[minio]; extra == "storage"
|
|
24
|
+
Provides-Extra: full
|
|
25
|
+
Requires-Dist: gl-browser-use-binary[infrastructure,storage]; extra == "full"
|
|
26
|
+
|
|
27
|
+
# GL Browser Use
|
|
28
|
+
|
|
29
|
+
GL Browser Use is a typed Python SDK for running browser automation tasks through `browser-use`. It provides a stable client facade, structured stream events, explicit result objects, optional Steel or OpenSandbox browser infrastructure, optional MinIO/S3-compatible recording storage, and bounded retries for recoverable browser-session failures.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
Install the core SDK:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install gl-browser-use
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Install optional providers only when you need them:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install "gl-browser-use[steel]" # Steel browser infrastructure
|
|
43
|
+
pip install "gl-browser-use[opensandbox]" # OpenSandbox browser infrastructure
|
|
44
|
+
pip install "gl-browser-use[infrastructure]" # All browser infrastructure providers
|
|
45
|
+
pip install "gl-browser-use[minio]" # MinIO/S3-compatible object storage
|
|
46
|
+
pip install "gl-browser-use[storage]" # All object storage providers
|
|
47
|
+
pip install "gl-browser-use[full]" # Infrastructure + storage providers
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use the concrete extras (`steel`, `opensandbox`, `minio`) in application dependency files when you want to pin exactly which provider you depend on. The slot extras (`infrastructure`, `storage`, `full`) are convenience aliases and may include more providers later.
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
`BrowserUseClient` supports streaming and non-streaming execution. API keys can be passed directly or read from environment variables.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import asyncio
|
|
58
|
+
|
|
59
|
+
from gl_browser_use import BrowserUseClient, BrowserUseClientConfig
|
|
60
|
+
from gl_browser_use.infrastructure import SteelBrowserInfrastructure
|
|
61
|
+
from gl_browser_use.storage import MinIOS3CompatibleStorage
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
async def main() -> None:
|
|
65
|
+
client = BrowserUseClient(
|
|
66
|
+
config=BrowserUseClientConfig(
|
|
67
|
+
llm_openai_api_key="...",
|
|
68
|
+
page_extraction_llm_openai_api_key="...",
|
|
69
|
+
max_session_retries=2,
|
|
70
|
+
session_retry_delay_in_s=3.0,
|
|
71
|
+
),
|
|
72
|
+
infrastructure=SteelBrowserInfrastructure(), # reads STEEL_API_KEY by default
|
|
73
|
+
storage=MinIOS3CompatibleStorage.from_environment(),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
async for event in client.run("Open Hacker News and list five article titles"):
|
|
77
|
+
print(event.content)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
asyncio.run(main())
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
For a single aggregated result:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from gl_browser_use import BrowserUseClient, BrowserUseClientConfig
|
|
87
|
+
from gl_browser_use.infrastructure import SteelBrowserInfrastructure
|
|
88
|
+
|
|
89
|
+
client = BrowserUseClient(
|
|
90
|
+
config=BrowserUseClientConfig(
|
|
91
|
+
llm_openai_api_key="...",
|
|
92
|
+
page_extraction_llm_openai_api_key="...",
|
|
93
|
+
),
|
|
94
|
+
infrastructure=SteelBrowserInfrastructure(),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
result = client.run_sync("Open Hacker News and list five article titles")
|
|
98
|
+
|
|
99
|
+
print(result.status)
|
|
100
|
+
print(result.final_output)
|
|
101
|
+
print(result.session_id)
|
|
102
|
+
print(result.streaming_url)
|
|
103
|
+
print(result.recording_url)
|
|
104
|
+
print(result.metadata)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Configuration
|
|
108
|
+
|
|
109
|
+
`BrowserUseClientConfig` validates required values when the client is created. If `llm_openai_api_key` or `page_extraction_llm_openai_api_key` is not passed, both default to `OPENAI_API_KEY`.
|
|
110
|
+
|
|
111
|
+
Common client options:
|
|
112
|
+
|
|
113
|
+
1. `llm_openai_model`: primary browser-control model. Default: `o3`.
|
|
114
|
+
2. `page_extraction_llm_openai_model`: page extraction model. Default: `gpt-5-mini`.
|
|
115
|
+
3. `sensitive_data`: optional browser-use placeholder map. It accepts either a flat
|
|
116
|
+
placeholder-to-value map or a domain-scoped map. Values are excluded from normal
|
|
117
|
+
config serialization and redacted from `redacted_dict()` output.
|
|
118
|
+
4. `extend_system_message`: optional system prompt extension.
|
|
119
|
+
5. `vision_detail_level`: `auto`, `low`, or `high`. Default: `auto`.
|
|
120
|
+
6. `llm_timeout_in_s`: optional LLM timeout.
|
|
121
|
+
7. `step_timeout_in_s`: per-step browser timeout. Default: `180`.
|
|
122
|
+
8. `agent_kwargs`: additional `browser_use.Agent` constructor arguments. It cannot
|
|
123
|
+
override SDK-managed arguments such as the task, LLMs, browser session, `sensitive_data`,
|
|
124
|
+
or timeouts.
|
|
125
|
+
9. `enable_cloud_sync`: controls `browser-use` cloud sync. Default: `False`.
|
|
126
|
+
10. `logging_level`: `debug`, `info`, `warning`, `error`, or `result`. Default: `info`.
|
|
127
|
+
11. `max_session_retries`: recoverable session retries. Default: `2`.
|
|
128
|
+
12. `session_retry_delay_in_s`: delay between retries. Default: `3.0`.
|
|
129
|
+
|
|
130
|
+
### Sensitive data and Agent options
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
config = BrowserUseClientConfig(
|
|
134
|
+
llm_openai_api_key="...",
|
|
135
|
+
page_extraction_llm_openai_api_key="...",
|
|
136
|
+
sensitive_data={"username": "example-user", "password": "example-password"},
|
|
137
|
+
agent_kwargs={"use_vision": False, "max_actions_per_step": 2},
|
|
138
|
+
)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Keep one configured client per credential scope. Browser-use also accepts domain-scoped
|
|
142
|
+
`sensitive_data` maps. Never put real values in logs, serialized configuration, or
|
|
143
|
+
`agent_kwargs`.
|
|
144
|
+
|
|
145
|
+
When supplying sensitive data, restrict browser navigation to the expected domains (for
|
|
146
|
+
example, through the infrastructure session's browser-use `BrowserProfile(allowed_domains=...)`)
|
|
147
|
+
to reduce prompt-injection exfiltration risk. `agent_kwargs` cannot override browser/session
|
|
148
|
+
ownership (`browser`, `browser_profile`, and `browser_session` are reserved); configure those
|
|
149
|
+
controls through the selected infrastructure instead.
|
|
150
|
+
|
|
151
|
+
Optional provider environment variables:
|
|
152
|
+
|
|
153
|
+
1. `STEEL_API_KEY`: used by `SteelBrowserInfrastructure()` when `api_key` is not passed.
|
|
154
|
+
2. `OPENSANDBOX_DOMAIN`: OpenSandbox control-plane host. Default: `localhost:8080`.
|
|
155
|
+
3. `OPENSANDBOX_API_KEY`: required for shared OpenSandbox clusters; optional for local insecure servers.
|
|
156
|
+
4. `OBJECT_STORAGE_URL`: MinIO/S3 endpoint, for example `localhost:9001` or `https://storage.example`.
|
|
157
|
+
5. `OBJECT_STORAGE_USERNAME`: object storage access key.
|
|
158
|
+
6. `OBJECT_STORAGE_PASSWORD`: object storage secret key.
|
|
159
|
+
7. `OBJECT_STORAGE_BUCKET_NAME`: target bucket name.
|
|
160
|
+
8. `OBJECT_STORAGE_DIRECTORY_PREFIX`: optional object key prefix.
|
|
161
|
+
9. `OBJECT_STORAGE_SECURE`: `true` for HTTPS when the endpoint has no scheme.
|
|
162
|
+
|
|
163
|
+
Copy `.env.example` to `.env` for local development, then load it with your application environment manager.
|
|
164
|
+
|
|
165
|
+
## Runtime API
|
|
166
|
+
|
|
167
|
+
The client exposes three run methods:
|
|
168
|
+
|
|
169
|
+
1. `run(task)`: async generator that yields `BrowserUseStreamEvent` values as work progresses.
|
|
170
|
+
2. `run_once(task)`: async method that returns one `BrowserUseRunResult` and includes emitted events in `result.events`.
|
|
171
|
+
3. `run_sync(task)`: blocking wrapper around `run_once()` using `asyncio.run()`.
|
|
172
|
+
|
|
173
|
+
Do not call `run_sync()` from an already running event loop. Use `await run_once()` or `async for event in run()` in async applications.
|
|
174
|
+
|
|
175
|
+
Cancelling the stream by breaking from `async for` or calling `await stream.aclose()` cancels the underlying run task. Browser sessions and infrastructure sessions are released in cleanup.
|
|
176
|
+
|
|
177
|
+
## Result Contract
|
|
178
|
+
|
|
179
|
+
`BrowserUseRunResult` contains:
|
|
180
|
+
|
|
181
|
+
1. `status`: `success`, `error`, or `cancelled`.
|
|
182
|
+
2. `task`: normalized task string.
|
|
183
|
+
3. `final_output`: final text extracted from the underlying agent, or `Task completed` when no final text is available.
|
|
184
|
+
4. `session_id`: infrastructure session ID when an external browser session was used.
|
|
185
|
+
5. `streaming_url`: browser debug or streaming URL when available.
|
|
186
|
+
6. `recording_url`: expected recording URL when recording is configured.
|
|
187
|
+
7. `steps`: number of browser-use steps executed.
|
|
188
|
+
8. `error`: terminal error message for error results.
|
|
189
|
+
9. `events`: collected stream events for `run_once()`.
|
|
190
|
+
10. `metadata`: attempt, retry, and recording metadata.
|
|
191
|
+
|
|
192
|
+
Recording metadata uses these statuses:
|
|
193
|
+
|
|
194
|
+
1. `disabled`: infrastructure, storage, or browser context is not available.
|
|
195
|
+
2. `unsupported`: the selected infrastructure does not support recording.
|
|
196
|
+
3. `unavailable`: storage is configured but not available.
|
|
197
|
+
4. `scheduled`: a background recording upload has been scheduled.
|
|
198
|
+
5. `unknown`: recording may have started, but the terminal error did not include enough context to determine the final state.
|
|
199
|
+
|
|
200
|
+
## Streaming Contract
|
|
201
|
+
|
|
202
|
+
`BrowserUseStreamEvent` contains:
|
|
203
|
+
|
|
204
|
+
1. `event_type`
|
|
205
|
+
2. `content`
|
|
206
|
+
3. `thinking_and_activity_info`
|
|
207
|
+
4. `is_final`
|
|
208
|
+
5. `tool_info`
|
|
209
|
+
6. `metadata`
|
|
210
|
+
|
|
211
|
+
Important event content values:
|
|
212
|
+
|
|
213
|
+
1. `Receive streaming URL`: emitted when a browser debug or streaming URL is available.
|
|
214
|
+
2. `Receive recording URL`: emitted when a recording URL can be resolved.
|
|
215
|
+
3. `Task completed`: emitted for the final successful step.
|
|
216
|
+
|
|
217
|
+
Activity events encode iframe URLs in `thinking_and_activity_info["data_value"]` as a JSON string:
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
{"type": "iframe", "message": "<url>"}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Step events include serialized tool calls in `tool_info["tool_calls"]`.
|
|
224
|
+
|
|
225
|
+
## Retries And Errors
|
|
226
|
+
|
|
227
|
+
The client retries only classified recoverable browser-session failures, such as browser closure or websocket disconnect messages. Before each retry, it emits a retry status event. Retries are bounded by `max_session_retries`, so total attempts are `max_session_retries + 1`.
|
|
228
|
+
|
|
229
|
+
Non-recoverable task failures return `BrowserUseRunResult(status="error")`. Recoverable failures that exhaust all attempts raise `BrowserUseRetryExhaustedError`.
|
|
230
|
+
|
|
231
|
+
SDK error types:
|
|
232
|
+
|
|
233
|
+
1. `BrowserUseConfigurationError`: missing or invalid runtime configuration.
|
|
234
|
+
2. `BrowserUseDependencyError`: optional provider dependency problem.
|
|
235
|
+
3. `BrowserUseMissingDependencyError`: optional provider extra is not installed.
|
|
236
|
+
4. `BrowserUseExecutionError`: execution-time failure.
|
|
237
|
+
5. `BrowserUseRetryExhaustedError`: recoverable session retries were exhausted.
|
|
238
|
+
|
|
239
|
+
## Optional Providers
|
|
240
|
+
|
|
241
|
+
Optional providers are lazy-loaded. Importing `gl_browser_use` does not require Steel, OpenSandbox, or MinIO to be installed.
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
from gl_browser_use.infrastructure import OpenSandboxBrowserInfrastructure, SteelBrowserInfrastructure
|
|
245
|
+
from gl_browser_use.storage import MinIOS3CompatibleStorage
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
`SteelBrowserInfrastructure` creates Steel browser sessions and provides CDP/streaming URLs.
|
|
249
|
+
|
|
250
|
+
`OpenSandboxBrowserInfrastructure` provisions the repo-committed Chrome example image (`opensandbox/chrome:latest`, built from `examples/opensandbox/chrome`) through a local or shared OpenSandbox server, exposes noVNC inspection URLs, connects over proxied CDP, and uploads WebM session recordings to object storage with the same deferred `recording_url` semantics as Steel. Pass `storage` to `BrowserUseClient` when recording is enabled.
|
|
251
|
+
|
|
252
|
+
**DevTools note:** the Chrome image exposes CDP on `0.0.0.0:9222` (`--remote-debugging-address=0.0.0.0` plus a `socat` forward for Chromium M113+). Rebuild from `examples/opensandbox/chrome` after pulling changes; the SDK discovers CDP via `sandbox.get_endpoint(9222)`. See `examples/opensandbox/chrome/README.md`.
|
|
253
|
+
|
|
254
|
+
OpenSandbox local setup:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
DOCKER_HOST=unix://${HOME}/.docker/desktop/docker.sock \
|
|
258
|
+
OPENSANDBOX_INSECURE_SERVER=YES opensandbox-server
|
|
259
|
+
|
|
260
|
+
cd libs/gl-browser-use/examples/opensandbox/chrome
|
|
261
|
+
docker build -t opensandbox/chrome:latest .
|
|
262
|
+
playwright install chromium
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`MinIOS3CompatibleStorage` uploads session recordings to MinIO or an S3-compatible service and returns presigned URLs.
|
|
266
|
+
|
|
267
|
+
## Development
|
|
268
|
+
|
|
269
|
+
From `libs/gl-browser-use`:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
make install-dev
|
|
273
|
+
make test-unit
|
|
274
|
+
make test-integration
|
|
275
|
+
make lint
|
|
276
|
+
make build-check
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Useful targets:
|
|
280
|
+
|
|
281
|
+
1. `make test`: run all tests.
|
|
282
|
+
2. `make test-unit`: run unit tests with coverage.
|
|
283
|
+
3. `make test-integration`: run integration-marked contract tests.
|
|
284
|
+
4. `make lint`: run Ruff checks.
|
|
285
|
+
5. `make format`: run Ruff fixes and formatter.
|
|
286
|
+
6. `make pre-commit`: run pre-commit hooks.
|
|
287
|
+
7. `make build-check`: build the package and validate artifacts with Twine.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
gl_browser_use.cp313-win_amd64.pyd,sha256=s7TpsZ6Ztus48T-JbV4Tg_h1SN7odRx-OHf9rLFn9Qo,959488
|
|
2
|
+
gl_browser_use.pyi,sha256=p6iz4o2Hs4jLimApvxcZRQUcSr8UOu4SdRBJLMU_gLM,2528
|
|
3
|
+
gl_browser_use/__init__.pyi,sha256=1uchB2q9Iq7GBPac_MQf4okzXalqcwNQeBUAVpXDU8E,1417
|
|
4
|
+
gl_browser_use/client.pyi,sha256=_UWgAWXn85NHNVSfUboJOxDRSUvg_F-xrz4-Zn_UBu8,3552
|
|
5
|
+
gl_browser_use/config.pyi,sha256=73-MZI978JcBtUgeyjUftry2pB4ReuBoz4RKuCpfps4,1974
|
|
6
|
+
gl_browser_use/errors.pyi,sha256=LlpVLDHEQCyqekY0Jq88iqjJgQuSzDsM1gGQBkeWUMw,2088
|
|
7
|
+
gl_browser_use/events.pyi,sha256=A7o-FcXn8FHbrxeIZqQ75byYo6OkUVYekaIrU-nVWiY,1888
|
|
8
|
+
gl_browser_use/models.pyi,sha256=KcNLDd6XPQqHa7Ccuf5ezfDsJuLkVFQNqk67SA1A_1A,1078
|
|
9
|
+
gl_browser_use/parsing.pyi,sha256=K14ptm7O5DgGRxtRxi-1SrKsMdm4XqzXhrBXeNE9V3w,2267
|
|
10
|
+
gl_browser_use/parsing_recovery.pyi,sha256=LCaHWb0oCSGgwPxEwG3z1f25UCLpb6y5WZnrXO7unMA,1416
|
|
11
|
+
gl_browser_use/session_errors.pyi,sha256=fQOcS7Gh2FSawL0VaIRICtCsd-3jdgJUAdExszWmwWU,662
|
|
12
|
+
gl_browser_use/infrastructure/__init__.pyi,sha256=AmrO_89UUoEn8Xe_tiDLZ38rFwDd3Xs-rOaM6EHCYZw,406
|
|
13
|
+
gl_browser_use/infrastructure/base.pyi,sha256=kLTlBvVwpQrStuoo5FfdE_VoG1qCfApEThvb8xC48g4,2817
|
|
14
|
+
gl_browser_use/infrastructure/opensandbox.pyi,sha256=Tj9tg7WMbRbne3qkcOsiVZYkfQ3ZrsIV9iBAg14OSDA,3234
|
|
15
|
+
gl_browser_use/infrastructure/playwright_recorder.pyi,sha256=A27qsALvrhmwAwtNqe4VxzGaqzT1DwrY7DWjGPNczzA,1227
|
|
16
|
+
gl_browser_use/infrastructure/recording_utils.pyi,sha256=pcaDHoN_HeKeyg7fSVLNIfgU6MkZNDp6QPJb5zoAXVk,579
|
|
17
|
+
gl_browser_use/infrastructure/steel.pyi,sha256=Rwu-BqKFNu1UR_u_bVIKzaT5tLheeXF_tG6vWePymtI,2726
|
|
18
|
+
gl_browser_use/storage/__init__.pyi,sha256=y1Yp7iB0WWc2sklofSSZe7xbUzbsBsm0K1OA9W_bnAY,267
|
|
19
|
+
gl_browser_use/storage/base.pyi,sha256=_V26BMVpbCqY4jqWLTKrgXhsQ9pQ3NCgXEd4WHrgHGc,1936
|
|
20
|
+
gl_browser_use/storage/minio_compatible.pyi,sha256=MQnQ1IxTnCx74uiBOlEdzs9BHf88YL37UFZZWTzoTag,871
|
|
21
|
+
gl_browser_use_binary-0.0.0b9.dist-info/METADATA,sha256=-MvHMUQdl_8ddPeFnwy6OnN8jUnEWyTdgFVgvIMZ1yg,12766
|
|
22
|
+
gl_browser_use_binary-0.0.0b9.dist-info/WHEEL,sha256=ikXeVivtRq-oqOZfN0jwm7M68yNYx-wxpXplGP7L4Is,97
|
|
23
|
+
gl_browser_use_binary-0.0.0b9.dist-info/top_level.txt,sha256=I3agYm4bLWMzPbZo_gLgMdmn91EFAXX-hziarvf9VWY,15
|
|
24
|
+
gl_browser_use_binary-0.0.0b9.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gl_browser_use
|