Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions src/stagehand/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ class Stagehand(SyncAPIClient):
_local_ready_timeout_s: float
_local_shutdown_on_close: bool
_sea_server: SeaServerManager | None
_valkey_host: str | None
_valkey_port: int | None
_valkey_tls: bool | None
_valkey_password: str | None
_valkey_username: str | None
_valkey_cache_ttl: int | None
_valkey_key_prefix: str | None
### </END CUSTOM CODE>

### <CUSTOM CODE HANDWRITTEN BY STAGEHAND TEAM (not codegen)>
Expand All @@ -109,6 +116,13 @@ def __init__(
local_chrome_path: str | None = None,
local_ready_timeout_s: float = 10.0,
local_shutdown_on_close: bool = True,
valkey_host: str | None = None,
valkey_port: int | None = None,
valkey_tls: bool | None = None,
valkey_password: str | None = None,
valkey_username: str | None = None,
valkey_cache_ttl: int | None = None,
valkey_key_prefix: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
Expand Down Expand Up @@ -159,6 +173,13 @@ def __init__(
local_shutdown_on_close=local_shutdown_on_close,
base_url=base_url,
model_api_key=model_api_key,
valkey_host=valkey_host,
valkey_port=valkey_port,
valkey_tls=valkey_tls,
valkey_password=valkey_password,
valkey_username=valkey_username,
valkey_cache_ttl=valkey_cache_ttl,
valkey_key_prefix=valkey_key_prefix,
)
### </END CUSTOM CODE>

Expand Down Expand Up @@ -269,6 +290,13 @@ def copy(
local_chrome_path: str | None = None,
local_ready_timeout_s: float | None = None,
local_shutdown_on_close: bool | None = None,
valkey_host: str | None = None,
valkey_port: int | None = None,
valkey_tls: bool | None = None,
valkey_password: str | None = None,
valkey_username: str | None = None,
valkey_cache_ttl: int | None = None,
valkey_key_prefix: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.Client | None = None,
Expand Down Expand Up @@ -323,6 +351,13 @@ def copy(
local_chrome_path=local_chrome_path,
local_ready_timeout_s=local_ready_timeout_s,
local_shutdown_on_close=local_shutdown_on_close,
valkey_host=valkey_host,
valkey_port=valkey_port,
valkey_tls=valkey_tls,
valkey_password=valkey_password,
valkey_username=valkey_username,
valkey_cache_ttl=valkey_cache_ttl,
valkey_key_prefix=valkey_key_prefix,
),
**_extra_kwargs,
)
Expand Down Expand Up @@ -383,6 +418,13 @@ class AsyncStagehand(AsyncAPIClient):
_local_ready_timeout_s: float
_local_shutdown_on_close: bool
_sea_server: SeaServerManager | None
_valkey_host: str | None
_valkey_port: int | None
_valkey_tls: bool | None
_valkey_password: str | None
_valkey_username: str | None
_valkey_cache_ttl: int | None
_valkey_key_prefix: str | None
### </END CUSTOM CODE>

### <CUSTOM CODE HANDWRITTEN BY STAGEHAND TEAM (not codegen)>
Expand All @@ -400,6 +442,13 @@ def __init__(
local_chrome_path: str | None = None,
local_ready_timeout_s: float = 10.0,
local_shutdown_on_close: bool = True,
valkey_host: str | None = None,
valkey_port: int | None = None,
valkey_tls: bool | None = None,
valkey_password: str | None = None,
valkey_username: str | None = None,
valkey_cache_ttl: int | None = None,
valkey_key_prefix: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
Expand Down Expand Up @@ -450,6 +499,13 @@ def __init__(
local_shutdown_on_close=local_shutdown_on_close,
base_url=base_url,
model_api_key=model_api_key,
valkey_host=valkey_host,
valkey_port=valkey_port,
valkey_tls=valkey_tls,
valkey_password=valkey_password,
valkey_username=valkey_username,
valkey_cache_ttl=valkey_cache_ttl,
valkey_key_prefix=valkey_key_prefix,
)
### </END CUSTOM CODE>

Expand Down Expand Up @@ -560,6 +616,13 @@ def copy(
local_chrome_path: str | None = None,
local_ready_timeout_s: float | None = None,
local_shutdown_on_close: bool | None = None,
valkey_host: str | None = None,
valkey_port: int | None = None,
valkey_tls: bool | None = None,
valkey_password: str | None = None,
valkey_username: str | None = None,
valkey_cache_ttl: int | None = None,
valkey_key_prefix: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.AsyncClient | None = None,
Expand Down Expand Up @@ -614,6 +677,13 @@ def copy(
local_chrome_path=local_chrome_path,
local_ready_timeout_s=local_ready_timeout_s,
local_shutdown_on_close=local_shutdown_on_close,
valkey_host=valkey_host,
valkey_port=valkey_port,
valkey_tls=valkey_tls,
valkey_password=valkey_password,
valkey_username=valkey_username,
valkey_cache_ttl=valkey_cache_ttl,
valkey_key_prefix=valkey_key_prefix,
),
**_extra_kwargs,
)
Expand Down
97 changes: 97 additions & 0 deletions src/stagehand/_custom/sea_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import signal
import socket
import asyncio
import warnings
import subprocess
from pathlib import Path
from threading import Lock
Expand All @@ -28,6 +29,13 @@ class SeaServerConfig:
model_api_key: str | None
chrome_path: str | None
shutdown_on_close: bool
valkey_host: str | None = None
valkey_port: int | None = None
valkey_tls: bool | None = None
valkey_password: str | None = None
valkey_username: str | None = None
valkey_cache_ttl: int | None = None
valkey_key_prefix: str | None = None


class _HasLocalModeState(Protocol):
Expand All @@ -40,6 +48,13 @@ class _HasLocalModeState(Protocol):
_local_ready_timeout_s: float
_local_shutdown_on_close: bool
_sea_server: SeaServerManager | None
_valkey_host: str | None
_valkey_port: int | None
_valkey_tls: bool | None
_valkey_password: str | None
_valkey_username: str | None
_valkey_cache_ttl: int | None
_valkey_key_prefix: str | None


class LocalModeKwargs(TypedDict):
Expand All @@ -51,6 +66,13 @@ class LocalModeKwargs(TypedDict):
local_chrome_path: str | None
local_ready_timeout_s: float
local_shutdown_on_close: bool
valkey_host: str | None
valkey_port: int | None
valkey_tls: bool | None
valkey_password: str | None
valkey_username: str | None
valkey_cache_ttl: int | None
valkey_key_prefix: str | None


def _pick_free_port(host: str) -> int:
Expand Down Expand Up @@ -163,6 +185,28 @@ def _build_process_env(self, *, port: int) -> dict[str, str]:
if self._config.chrome_path:
proc_env["CHROME_PATH"] = self._config.chrome_path
proc_env["LIGHTHOUSE_CHROMIUM_PATH"] = self._config.chrome_path
if self._config.valkey_host is not None:
proc_env["VALKEY_HOST"] = self._config.valkey_host
if self._config.valkey_port is not None:
proc_env["VALKEY_PORT"] = str(self._config.valkey_port)
if self._config.valkey_tls is not None:
proc_env["VALKEY_TLS"] = "true" if self._config.valkey_tls else "false"
if self._config.valkey_password is not None:
host = self._config.valkey_host or ""
if not self._config.valkey_tls and host not in ("localhost", "127.0.0.1", "::1"):
warnings.warn(
"valkey_password is set but valkey_tls is not enabled. "
"Credentials will be sent in cleartext. Set valkey_tls=True for non-local hosts.",
UserWarning,
stacklevel=6,
)
proc_env["VALKEY_PASSWORD"] = self._config.valkey_password
if self._config.valkey_username is not None:
proc_env["VALKEY_USERNAME"] = self._config.valkey_username
if self._config.valkey_cache_ttl is not None:
proc_env["CACHE_TTL"] = str(self._config.valkey_cache_ttl)
if self._config.valkey_key_prefix is not None:
proc_env["VALKEY_KEY_PREFIX"] = self._config.valkey_key_prefix
return proc_env

def ensure_running_sync(self) -> str:
Expand Down Expand Up @@ -299,6 +343,13 @@ def configure_client_base_url(
local_shutdown_on_close: bool,
base_url: str | httpx.URL | None,
model_api_key: str | None,
valkey_host: str | None = None,
valkey_port: int | None = None,
valkey_tls: bool | None = None,
valkey_password: str | None = None,
valkey_username: str | None = None,
valkey_cache_ttl: int | None = None,
valkey_key_prefix: str | None = None,
) -> str | httpx.URL:
client._server_mode = server
client._local_stagehand_binary_path = _local_stagehand_binary_path
Expand All @@ -308,9 +359,34 @@ def configure_client_base_url(
client._local_chrome_path = local_chrome_path
client._local_ready_timeout_s = local_ready_timeout_s
client._local_shutdown_on_close = local_shutdown_on_close
client._valkey_host = valkey_host
client._valkey_port = valkey_port
client._valkey_tls = valkey_tls
client._valkey_password = valkey_password
client._valkey_username = valkey_username
client._valkey_cache_ttl = valkey_cache_ttl
client._valkey_key_prefix = valkey_key_prefix
client._sea_server = None

_valkey_params_given = any(
p is not None
for p in (valkey_host, valkey_port, valkey_tls, valkey_password, valkey_username, valkey_cache_ttl, valkey_key_prefix)
)
if server != "local" and _valkey_params_given:
warnings.warn(
"Client-level Valkey parameters (valkey_host, valkey_port, etc.) are only used in "
'server="local" mode. In remote mode, pass valkey_cache= to sessions.start() instead. '
"These parameters will have no effect.",
UserWarning,
stacklevel=3,
)

if server == "local":
# NOTE: Valkey caching in local mode requires the @valkey/valkey-glide native
# addon to be loadable by the SEA binary. Pre-built binaries do not bundle
# native addons, so the cache connection will silently fall back to disabled.
# Use server="remote" for Valkey caching, or supply a custom binary built
# with @valkey/valkey-glide installed alongside it.
if base_url is None:
base_url = "http://127.0.0.1"

Expand All @@ -323,6 +399,13 @@ def configure_client_base_url(
model_api_key=model_api_key,
chrome_path=local_chrome_path,
shutdown_on_close=local_shutdown_on_close,
valkey_host=valkey_host,
valkey_port=valkey_port,
valkey_tls=valkey_tls,
valkey_password=valkey_password,
valkey_username=valkey_username,
valkey_cache_ttl=valkey_cache_ttl,
valkey_key_prefix=valkey_key_prefix,
),
_local_stagehand_binary_path=_local_stagehand_binary_path,
)
Expand All @@ -346,6 +429,13 @@ def copy_local_mode_kwargs(
local_chrome_path: str | None,
local_ready_timeout_s: float | None,
local_shutdown_on_close: bool | None,
valkey_host: str | None = None,
valkey_port: int | None = None,
valkey_tls: bool | None = None,
valkey_password: str | None = None,
valkey_username: str | None = None,
valkey_cache_ttl: int | None = None,
valkey_key_prefix: str | None = None,
) -> LocalModeKwargs:
return {
"server": server or client._server_mode,
Expand All @@ -370,6 +460,13 @@ def copy_local_mode_kwargs(
if local_shutdown_on_close is not None
else client._local_shutdown_on_close
),
"valkey_host": valkey_host if valkey_host is not None else client._valkey_host,
"valkey_port": valkey_port if valkey_port is not None else client._valkey_port,
"valkey_tls": valkey_tls if valkey_tls is not None else client._valkey_tls,
"valkey_password": valkey_password if valkey_password is not None else client._valkey_password,
"valkey_username": valkey_username if valkey_username is not None else client._valkey_username,
"valkey_cache_ttl": valkey_cache_ttl if valkey_cache_ttl is not None else client._valkey_cache_ttl,
"valkey_key_prefix": valkey_key_prefix if valkey_key_prefix is not None else client._valkey_key_prefix,
}


Expand Down
4 changes: 4 additions & 0 deletions src/stagehand/_custom/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,7 @@ def _sync_start(
experimental: bool | Omit = omit,
self_heal: bool | Omit = omit,
system_prompt: str | Omit = omit,
valkey_cache: session_start_params.ValkeyCacheOptions | Omit = omit,
verbose: Literal[0, 1, 2] | Omit = omit,
wait_for_captcha_solves: bool | Omit = omit,
x_stream_response: Literal["true", "false"] | Omit = omit,
Expand All @@ -751,6 +752,7 @@ def _sync_start(
experimental=experimental,
self_heal=self_heal,
system_prompt=system_prompt,
valkey_cache=valkey_cache,
verbose=verbose,
wait_for_captcha_solves=wait_for_captcha_solves,
x_stream_response=x_stream_response,
Expand All @@ -776,6 +778,7 @@ async def _async_start(
experimental: bool | Omit = omit,
self_heal: bool | Omit = omit,
system_prompt: str | Omit = omit,
valkey_cache: session_start_params.ValkeyCacheOptions | Omit = omit,
verbose: Literal[0, 1, 2] | Omit = omit,
wait_for_captcha_solves: bool | Omit = omit,
x_stream_response: Literal["true", "false"] | Omit = omit,
Expand All @@ -795,6 +798,7 @@ async def _async_start(
experimental=experimental,
self_heal=self_heal,
system_prompt=system_prompt,
valkey_cache=valkey_cache,
verbose=verbose,
wait_for_captcha_solves=wait_for_captcha_solves,
x_stream_response=x_stream_response,
Expand Down
8 changes: 8 additions & 0 deletions src/stagehand/resources/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,7 @@ def start(
experimental: bool | Omit = omit,
self_heal: bool | Omit = omit,
system_prompt: str | Omit = omit,
valkey_cache: session_start_params.ValkeyCacheOptions | Omit = omit,
verbose: Literal[0, 1, 2] | Omit = omit,
wait_for_captcha_solves: bool | Omit = omit,
x_stream_response: Literal["true", "false"] | Omit = omit,
Expand Down Expand Up @@ -961,6 +962,8 @@ def start(

system_prompt: Custom system prompt for AI operations

valkey_cache: Valkey cache backend configuration. When set, uses Valkey for caching.

verbose: Logging verbosity level (0=quiet, 1=normal, 2=debug)

wait_for_captcha_solves: Wait for captcha solves (deprecated, v2 only)
Expand Down Expand Up @@ -999,6 +1002,7 @@ def start(
"experimental": experimental,
"self_heal": self_heal,
"system_prompt": system_prompt,
"valkey_cache": valkey_cache,
"verbose": verbose,
"wait_for_captcha_solves": wait_for_captcha_solves,
},
Expand Down Expand Up @@ -1892,6 +1896,7 @@ async def start(
experimental: bool | Omit = omit,
self_heal: bool | Omit = omit,
system_prompt: str | Omit = omit,
valkey_cache: session_start_params.ValkeyCacheOptions | Omit = omit,
verbose: Literal[0, 1, 2] | Omit = omit,
wait_for_captcha_solves: bool | Omit = omit,
x_stream_response: Literal["true", "false"] | Omit = omit,
Expand Down Expand Up @@ -1924,6 +1929,8 @@ async def start(

system_prompt: Custom system prompt for AI operations

valkey_cache: Valkey cache backend configuration. When set, uses Valkey for caching.

verbose: Logging verbosity level (0=quiet, 1=normal, 2=debug)

wait_for_captcha_solves: Wait for captcha solves (deprecated, v2 only)
Expand Down Expand Up @@ -1962,6 +1969,7 @@ async def start(
"experimental": experimental,
"self_heal": self_heal,
"system_prompt": system_prompt,
"valkey_cache": valkey_cache,
"verbose": verbose,
"wait_for_captcha_solves": wait_for_captcha_solves,
},
Expand Down
Loading