Skip to content
Merged
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
8 changes: 4 additions & 4 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[bumpversion]
current_version = 0.3.8
current_version = 0.0.1
commit = True
tag = True

[bumpversion:file:setup.py]
search = version="{current_version}"
replace = version="{new_version}"
[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"

[bumpversion:file:stagehand/__init__.py]
search = __version__ = "{current_version}"
Expand Down
16 changes: 1 addition & 15 deletions .github/README_PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,7 @@ This project uses Ruff for linting and formatting. The workflow enforces these s

To run the same checks locally:
```bash
# Install Ruff
pip install ruff

# Run linting
ruff check .

# Check formatting
ruff format --check .

# Auto-fix issues where possible
ruff check --fix .
ruff format .

# Use Black to format the code
black .
./format.sh
```

## Troubleshooting
Expand Down
5 changes: 5 additions & 0 deletions .github/pull_request_template
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# why

# what changed

# test plan
36 changes: 36 additions & 0 deletions .github/workflows/linting.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Format Check

on:
pull_request:
branches: [ main, master ]
paths:
- '**.py'
- 'stagehand/**'
- 'pyproject.toml'

jobs:
format-check:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Install formatting dependencies
run: |
python -m pip install --upgrade pip
pip install black ruff

- name: Check Black formatting
run: |
echo "Checking Black formatting..."
black --check --diff stagehand

- name: Run Ruff linting
run: |
echo "Running Ruff linting..."
ruff check stagehand
60 changes: 54 additions & 6 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build twine wheel setuptools bumpversion ruff
pip install build twine wheel setuptools ruff
pip install -r requirements.txt
if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi

Expand All @@ -51,19 +51,67 @@ jobs:
run: |
pytest

- name: Update version
- name: Calculate new version
id: version
run: |
# Get current version from pyproject.toml
CURRENT_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT

# Parse version components
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"

# Calculate new version based on release type
case "${{ github.event.inputs.release_type }}" in
"major")
NEW_MAJOR=$((MAJOR + 1))
NEW_MINOR=0
NEW_PATCH=0
;;
"minor")
NEW_MAJOR=$MAJOR
NEW_MINOR=$((MINOR + 1))
NEW_PATCH=0
;;
"patch")
NEW_MAJOR=$MAJOR
NEW_MINOR=$MINOR
NEW_PATCH=$((PATCH + 1))
;;
esac

NEW_VERSION="${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}"
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "Bumping version from $CURRENT_VERSION to $NEW_VERSION"

- name: Update version files
run: |
CURRENT_VERSION="${{ steps.version.outputs.current_version }}"
NEW_VERSION="${{ steps.version.outputs.new_version }}"

# Update pyproject.toml
sed -i "s/version = \"$CURRENT_VERSION\"/version = \"$NEW_VERSION\"/" pyproject.toml

# Update __init__.py
sed -i "s/__version__ = \"$CURRENT_VERSION\"/__version__ = \"$NEW_VERSION\"/" stagehand/__init__.py

echo "Updated version to $NEW_VERSION in pyproject.toml and __init__.py"

- name: Commit version bump
run: |
git config --local user.email "[email protected]"
git config --local user.name "GitHub Action"
bumpversion ${{ github.event.inputs.release_type }}
git add pyproject.toml stagehand/__init__.py
git commit -m "Bump version to ${{ steps.version.outputs.new_version }}"
git tag "v${{ steps.version.outputs.new_version }}"

- name: Build package
run: |
python -m build

- name: Upload to PyPI
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
twine upload dist/*
Expand All @@ -77,6 +125,6 @@ jobs:
if: ${{ github.event.inputs.create_release == 'true' }}
uses: softprops/action-gh-release@v1
with:
tag_name: v$(python setup.py --version)
name: Release v$(python setup.py --version)
tag_name: v${{ steps.version.outputs.new_version }}
name: Release v${{ steps.version.outputs.new_version }}
generate_release_notes: true
12 changes: 4 additions & 8 deletions format.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
#!/bin/bash

# Define source directories (adjust as needed)
SOURCE_DIRS="evals stagehand"
SOURCE_DIRS="stagehand"

# Apply Black formatting only to source directories
# Apply Black formatting first
echo "Applying Black formatting..."
black $SOURCE_DIRS

# Fix import sorting (addresses I001 errors)
echo "Sorting imports..."
isort $SOURCE_DIRS

# Apply Ruff with autofix for remaining issues
echo "Applying Ruff autofixes..."
# Apply Ruff with autofix for all issues (including import sorting)
echo "Applying Ruff autofixes (including import sorting)..."
ruff check --fix $SOURCE_DIRS

echo "Checking for remaining issues..."
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "stagehand-py"
version = "0.3.10"
name = "stagehand"
version = "0.0.1"
description = "Python SDK for Stagehand"
readme = "README.md"
license = {text = "MIT"}
Expand Down
2 changes: 1 addition & 1 deletion stagehand/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
)
from .utils import configure_logging

__version__ = "0.3.9" #for pypi "stagehand"
__version__ = "0.0.1"

__all__ = [
"Stagehand",
Expand Down
26 changes: 16 additions & 10 deletions stagehand/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,23 @@ def __init__(
# Start with provided config or default config
if config is None:
config = default_config

# Apply any overrides
overrides = {}
if api_url is not None:
# api_url isn't in config, handle separately
pass
if model_api_key is not None:
# model_api_key isn't in config, handle separately
# model_api_key isn't in config, handle separately
pass
if session_id is not None:
overrides['browserbase_session_id'] = session_id
overrides["browserbase_session_id"] = session_id
if env is not None:
overrides['env'] = env
overrides["env"] = env

# Add any additional config overrides
overrides.update(config_overrides)

# Create final config with overrides
if overrides:
self.config = config.with_overrides(**overrides)
Expand All @@ -102,10 +102,14 @@ def __init__(
# Handle non-config parameters
self.api_url = api_url or os.getenv("STAGEHAND_API_URL")
self.model_api_key = model_api_key or os.getenv("MODEL_API_KEY")

# Extract frequently used values from config for convenience
self.browserbase_api_key = self.config.api_key or os.getenv("BROWSERBASE_API_KEY")
self.browserbase_project_id = self.config.project_id or os.getenv("BROWSERBASE_PROJECT_ID")
self.browserbase_api_key = self.config.api_key or os.getenv(
"BROWSERBASE_API_KEY"
)
self.browserbase_project_id = self.config.project_id or os.getenv(
"BROWSERBASE_PROJECT_ID"
)
self.session_id = self.config.browserbase_session_id
self.model_name = self.config.model_name
self.dom_settle_timeout_ms = self.config.dom_settle_timeout_ms
Expand All @@ -114,7 +118,9 @@ def __init__(
self.system_prompt = self.config.system_prompt
self.verbose = self.config.verbose
self.env = self.config.env.upper() if self.config.env else "BROWSERBASE"
self.local_browser_launch_options = self.config.local_browser_launch_options or {}
self.local_browser_launch_options = (
self.config.local_browser_launch_options or {}
)

# Handle model-related settings
self.model_client_options = {}
Expand Down
4 changes: 2 additions & 2 deletions stagehand/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ class StagehandConfig(BaseModel):
def with_overrides(self, **overrides) -> "StagehandConfig":
"""
Create a new config instance with the specified overrides.

Args:
**overrides: Key-value pairs to override in the config

Returns:
StagehandConfig: New config instance with overrides applied
"""
Expand Down
1 change: 1 addition & 0 deletions stagehand/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ async def wrapped_new_page(*args, **kwargs):

return wrapped_new_page
elif name == "pages":

async def wrapped_pages():
pw_pages = self._context.pages
# Return StagehandPage objects
Expand Down
2 changes: 1 addition & 1 deletion stagehand/handlers/act_handler_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ async def handle_possible_page_navigation(
},
)

if new_opened_tab and new_opened_tab.url != 'about:blank':
if new_opened_tab and new_opened_tab.url != "about:blank":
logger.info(
message="new page detected (new tab) with URL",
category="action",
Expand Down
3 changes: 3 additions & 0 deletions stagehand/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@ def convert_dict_keys_to_camel_case(data: dict[str, Any]) -> dict[str, Any]:

return result


def format_simplified_tree(node: AccessibilityNode, level: int = 0) -> str:
"""Formats a node and its children into a simplified string representation."""
indent = " " * level
Expand Down Expand Up @@ -1134,6 +1135,8 @@ def inject_url_at_path(obj, segments, id_to_url_mapping):
else:
# Continue traversing the path
inject_url_at_path(obj[key], rest, id_to_url_mapping)


# Convert any non-serializable objects to plain Python objects
def make_serializable(obj):
"""Recursively convert non-JSON-serializable objects to serializable ones."""
Expand Down
Loading