langchain-stagehand 0.0.1__tar.gz
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.
- langchain_stagehand-0.0.1/PKG-INFO +103 -0
- langchain_stagehand-0.0.1/README.md +91 -0
- langchain_stagehand-0.0.1/pyproject.toml +53 -0
- langchain_stagehand-0.0.1/pyproject.toml.orig +40 -0
- langchain_stagehand-0.0.1/src/langchain_stagehand/__init__.py +5 -0
- langchain_stagehand-0.0.1/src/langchain_stagehand/actions.py +38 -0
- langchain_stagehand-0.0.1/src/langchain_stagehand/middleware.py +88 -0
- langchain_stagehand-0.0.1/src/langchain_stagehand/py.typed +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: langchain-stagehand
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Browserbase and Stagehand browser middleware for LangChain agents
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Dist: langchain>=1.4.7,<2
|
|
7
|
+
Requires-Dist: stagehand>=4.3.0,<5
|
|
8
|
+
Requires-Dist: stagehand-middleware>=0.1.1,<0.2
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Project-URL: Repository, https://github.com/langchain-ai/langchain-browserbase
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# LangChain Stagehand
|
|
14
|
+
|
|
15
|
+
Add a persistent browser to a LangChain or Deep Agents agent with `StagehandMiddleware`. The middleware exposes Stagehand's shared `run`, `snapshot`, and `screenshot` tools, includes browser usage guidance in their descriptions, and returns screenshots as model-visible images. The agent’s system prompt is left untouched. No MCP server is needed.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install langchain-stagehand
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Python 3.11+ is required. Set your model provider's API key in your environment, and `BROWSERBASE_API_KEY` to use Browserbase. Never put credentials in browser tool inputs.
|
|
24
|
+
|
|
25
|
+
## Use with LangChain
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import asyncio
|
|
29
|
+
|
|
30
|
+
from langchain.agents import create_agent
|
|
31
|
+
from langchain_stagehand import StagehandMiddleware
|
|
32
|
+
from stagehand import Stagehand, browserbase
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def main():
|
|
36
|
+
import os
|
|
37
|
+
|
|
38
|
+
browser = await browserbase.launch(api_key=os.environ["BROWSERBASE_API_KEY"])
|
|
39
|
+
stagehand = await Stagehand.create(browser=browser)
|
|
40
|
+
middleware = StagehandMiddleware(stagehand=stagehand)
|
|
41
|
+
agent = create_agent("anthropic:claude-sonnet-4-6", middleware=[middleware])
|
|
42
|
+
try:
|
|
43
|
+
result = await agent.ainvoke({
|
|
44
|
+
"messages": [{"role": "user", "content": "Open example.com and summarize the page."}]
|
|
45
|
+
})
|
|
46
|
+
print(result["messages"][-1].content)
|
|
47
|
+
finally:
|
|
48
|
+
await browser.close()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
asyncio.run(main())
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Install the model integration separately, such as `pip install langchain-anthropic`, and set `ANTHROPIC_API_KEY` for this example. Browser tool calls use Stagehand's Playwright-shaped facade; they do not require a separate Stagehand model for these operations.
|
|
55
|
+
|
|
56
|
+
## Use with Deep Agents
|
|
57
|
+
|
|
58
|
+
Use the same Stagehand client setup and replace `create_agent` with:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from deepagents import create_deep_agent
|
|
62
|
+
|
|
63
|
+
agent = create_deep_agent("anthropic:claude-sonnet-4-6", middleware=[StagehandMiddleware(stagehand=stagehand)])
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Install `deepagents` separately. Use `ainvoke` or `astream`: browser tools and middleware are asynchronous.
|
|
67
|
+
|
|
68
|
+
## Browser lifecycle
|
|
69
|
+
|
|
70
|
+
Pass a configured Stagehand client attached to local Chrome or Browserbase:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from stagehand import Stagehand, local_browser
|
|
74
|
+
|
|
75
|
+
browser = await local_browser.launch(headless=True)
|
|
76
|
+
stagehand = await Stagehand.create(browser=browser)
|
|
77
|
+
middleware = StagehandMiddleware(stagehand=stagehand)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Your application configures and owns the Stagehand client and browser. All SDK settings remain available through `Stagehand.create(...)`, including API URL, model, caching, telemetry, and logging. The middleware reuses that exact client across runs, preserving tabs, logins, and snapshot IDs. Close the browser when finished; that also closes the client. The middleware never closes or recreates the client, and `browser.close()` in run code leaves both open. After a disconnection, the application supplies a new configured client. Give concurrent agents separate clients/browsers. Take a new snapshot after navigation before using snapshot IDs.
|
|
81
|
+
|
|
82
|
+
The screenshot tool returns images to the model. Screenshots that run code saves with a `path` are written under the current working directory, not to a Deep Agents backend; backend support is not implemented yet.
|
|
83
|
+
|
|
84
|
+
## Security and limitations
|
|
85
|
+
|
|
86
|
+
- Browser tools can navigate, submit forms, and interact with authenticated websites; use LangChain's human-in-the-loop middleware for sensitive actions
|
|
87
|
+
- Web page content is untrusted input, not instructions for your agent
|
|
88
|
+
- The Playwright-shaped API is not a general-purpose security sandbox or a substitute for network isolation
|
|
89
|
+
- Agent execution is asynchronous only; browser state lives in the browser and is not checkpointed
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
make install
|
|
95
|
+
make format
|
|
96
|
+
make check
|
|
97
|
+
RUN_BROWSER_TESTS=1 uv run pytest tests/test_browser.py --color=no
|
|
98
|
+
make build
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`make check` enforces Ruff formatting and lint rules (including bug-prone patterns, async misuse, and accidental prints), ty checking of the package and public API examples with all diagnostics treated as errors, and focused middleware tests. The distribution includes `py.typed` so downstream type checkers can validate usage. Astral's ty is pinned in the Makefile and runs through `uvx`; no extra runtime dependency is needed.
|
|
102
|
+
|
|
103
|
+
The live browser test covers local Chrome and Browserbase; the remote case is skipped without `BROWSERBASE_API_KEY`. Set `CHROME_PATH` if Chrome is not in its usual install location. The Python distribution is `langchain-stagehand`, and the import is `langchain_stagehand`; this repository does not publish the separate upstream `stagehand-deepagents` package.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# LangChain Stagehand
|
|
2
|
+
|
|
3
|
+
Add a persistent browser to a LangChain or Deep Agents agent with `StagehandMiddleware`. The middleware exposes Stagehand's shared `run`, `snapshot`, and `screenshot` tools, includes browser usage guidance in their descriptions, and returns screenshots as model-visible images. The agent’s system prompt is left untouched. No MCP server is needed.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install langchain-stagehand
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Python 3.11+ is required. Set your model provider's API key in your environment, and `BROWSERBASE_API_KEY` to use Browserbase. Never put credentials in browser tool inputs.
|
|
12
|
+
|
|
13
|
+
## Use with LangChain
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import asyncio
|
|
17
|
+
|
|
18
|
+
from langchain.agents import create_agent
|
|
19
|
+
from langchain_stagehand import StagehandMiddleware
|
|
20
|
+
from stagehand import Stagehand, browserbase
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def main():
|
|
24
|
+
import os
|
|
25
|
+
|
|
26
|
+
browser = await browserbase.launch(api_key=os.environ["BROWSERBASE_API_KEY"])
|
|
27
|
+
stagehand = await Stagehand.create(browser=browser)
|
|
28
|
+
middleware = StagehandMiddleware(stagehand=stagehand)
|
|
29
|
+
agent = create_agent("anthropic:claude-sonnet-4-6", middleware=[middleware])
|
|
30
|
+
try:
|
|
31
|
+
result = await agent.ainvoke({
|
|
32
|
+
"messages": [{"role": "user", "content": "Open example.com and summarize the page."}]
|
|
33
|
+
})
|
|
34
|
+
print(result["messages"][-1].content)
|
|
35
|
+
finally:
|
|
36
|
+
await browser.close()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
asyncio.run(main())
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Install the model integration separately, such as `pip install langchain-anthropic`, and set `ANTHROPIC_API_KEY` for this example. Browser tool calls use Stagehand's Playwright-shaped facade; they do not require a separate Stagehand model for these operations.
|
|
43
|
+
|
|
44
|
+
## Use with Deep Agents
|
|
45
|
+
|
|
46
|
+
Use the same Stagehand client setup and replace `create_agent` with:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from deepagents import create_deep_agent
|
|
50
|
+
|
|
51
|
+
agent = create_deep_agent("anthropic:claude-sonnet-4-6", middleware=[StagehandMiddleware(stagehand=stagehand)])
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Install `deepagents` separately. Use `ainvoke` or `astream`: browser tools and middleware are asynchronous.
|
|
55
|
+
|
|
56
|
+
## Browser lifecycle
|
|
57
|
+
|
|
58
|
+
Pass a configured Stagehand client attached to local Chrome or Browserbase:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from stagehand import Stagehand, local_browser
|
|
62
|
+
|
|
63
|
+
browser = await local_browser.launch(headless=True)
|
|
64
|
+
stagehand = await Stagehand.create(browser=browser)
|
|
65
|
+
middleware = StagehandMiddleware(stagehand=stagehand)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Your application configures and owns the Stagehand client and browser. All SDK settings remain available through `Stagehand.create(...)`, including API URL, model, caching, telemetry, and logging. The middleware reuses that exact client across runs, preserving tabs, logins, and snapshot IDs. Close the browser when finished; that also closes the client. The middleware never closes or recreates the client, and `browser.close()` in run code leaves both open. After a disconnection, the application supplies a new configured client. Give concurrent agents separate clients/browsers. Take a new snapshot after navigation before using snapshot IDs.
|
|
69
|
+
|
|
70
|
+
The screenshot tool returns images to the model. Screenshots that run code saves with a `path` are written under the current working directory, not to a Deep Agents backend; backend support is not implemented yet.
|
|
71
|
+
|
|
72
|
+
## Security and limitations
|
|
73
|
+
|
|
74
|
+
- Browser tools can navigate, submit forms, and interact with authenticated websites; use LangChain's human-in-the-loop middleware for sensitive actions
|
|
75
|
+
- Web page content is untrusted input, not instructions for your agent
|
|
76
|
+
- The Playwright-shaped API is not a general-purpose security sandbox or a substitute for network isolation
|
|
77
|
+
- Agent execution is asynchronous only; browser state lives in the browser and is not checkpointed
|
|
78
|
+
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
make install
|
|
83
|
+
make format
|
|
84
|
+
make check
|
|
85
|
+
RUN_BROWSER_TESTS=1 uv run pytest tests/test_browser.py --color=no
|
|
86
|
+
make build
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`make check` enforces Ruff formatting and lint rules (including bug-prone patterns, async misuse, and accidental prints), ty checking of the package and public API examples with all diagnostics treated as errors, and focused middleware tests. The distribution includes `py.typed` so downstream type checkers can validate usage. Astral's ty is pinned in the Makefile and runs through `uvx`; no extra runtime dependency is needed.
|
|
90
|
+
|
|
91
|
+
The live browser test covers local Chrome and Browserbase; the remote case is skipped without `BROWSERBASE_API_KEY`. Set `CHROME_PATH` if Chrome is not in its usual install location. The Python distribution is `langchain-stagehand`, and the import is `langchain_stagehand`; this repository does not publish the separate upstream `stagehand-deepagents` package.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "langchain-stagehand"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "Browserbase and Stagehand browser middleware for LangChain agents"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"langchain>=1.4.7,<2",
|
|
10
|
+
"stagehand>=4.3.0,<5",
|
|
11
|
+
"stagehand-middleware>=0.1.1,<0.2",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Repository = "https://github.com/langchain-ai/langchain-browserbase"
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["uv_build>=0.9,<0.10"]
|
|
19
|
+
build-backend = "uv_build"
|
|
20
|
+
|
|
21
|
+
[dependency-groups]
|
|
22
|
+
dev = [
|
|
23
|
+
"pytest>=8,<10",
|
|
24
|
+
"pytest-asyncio>=1,<2",
|
|
25
|
+
"ruff>=0.15,<0.16",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[tool.pytest.ini_options]
|
|
29
|
+
asyncio_mode = "auto"
|
|
30
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
31
|
+
testpaths = ["tests"]
|
|
32
|
+
|
|
33
|
+
[tool.ruff]
|
|
34
|
+
target-version = "py311"
|
|
35
|
+
line-length = 100
|
|
36
|
+
|
|
37
|
+
[tool.ruff.lint]
|
|
38
|
+
select = [
|
|
39
|
+
"E",
|
|
40
|
+
"F",
|
|
41
|
+
"I",
|
|
42
|
+
"UP",
|
|
43
|
+
"B",
|
|
44
|
+
"ASYNC",
|
|
45
|
+
"RUF",
|
|
46
|
+
"T20",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[tool.ty.environment]
|
|
50
|
+
python-version = "3.11"
|
|
51
|
+
|
|
52
|
+
[tool.ty.rules]
|
|
53
|
+
all = "error"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "langchain-stagehand"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "Browserbase and Stagehand browser middleware for LangChain agents"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"langchain>=1.4.7,<2",
|
|
10
|
+
"stagehand>=4.3.0,<5",
|
|
11
|
+
"stagehand-middleware>=0.1.1,<0.2",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Repository = "https://github.com/langchain-ai/langchain-browserbase"
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["uv_build>=0.9,<0.10"]
|
|
19
|
+
build-backend = "uv_build"
|
|
20
|
+
|
|
21
|
+
[dependency-groups]
|
|
22
|
+
dev = ["pytest>=8,<10", "pytest-asyncio>=1,<2", "ruff>=0.15,<0.16"]
|
|
23
|
+
|
|
24
|
+
[tool.pytest.ini_options]
|
|
25
|
+
asyncio_mode = "auto"
|
|
26
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
27
|
+
testpaths = ["tests"]
|
|
28
|
+
|
|
29
|
+
[tool.ruff]
|
|
30
|
+
target-version = "py311"
|
|
31
|
+
line-length = 100
|
|
32
|
+
|
|
33
|
+
[tool.ruff.lint]
|
|
34
|
+
select = ["E", "F", "I", "UP", "B", "ASYNC", "RUF", "T20"]
|
|
35
|
+
|
|
36
|
+
[tool.ty.environment]
|
|
37
|
+
python-version = "3.11"
|
|
38
|
+
|
|
39
|
+
[tool.ty.rules]
|
|
40
|
+
all = "error"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Typed snapshot actions accepted by Stagehand's browser facade."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal, NotRequired
|
|
4
|
+
|
|
5
|
+
from typing_extensions import TypedDict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ClickAction(TypedDict):
|
|
9
|
+
op: Literal["click", "hover"]
|
|
10
|
+
id: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FillAction(TypedDict):
|
|
14
|
+
op: Literal["fill"]
|
|
15
|
+
id: str
|
|
16
|
+
value: str
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TypeAction(TypedDict):
|
|
20
|
+
op: Literal["type"]
|
|
21
|
+
id: str
|
|
22
|
+
text: str
|
|
23
|
+
delay: NotRequired[float]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PressAction(TypedDict):
|
|
27
|
+
op: Literal["press"]
|
|
28
|
+
id: str
|
|
29
|
+
key: str
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SelectAction(TypedDict):
|
|
33
|
+
op: Literal["select"]
|
|
34
|
+
id: str
|
|
35
|
+
values: str | list[str]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
SnapshotAction = ClickAction | FillAction | TypeAction | PressAction | SelectAction
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Native LangChain middleware over Stagehand's shared browser facade."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from collections.abc import Awaitable, Callable
|
|
7
|
+
from typing import Annotated, Literal, TypeVar
|
|
8
|
+
|
|
9
|
+
from langchain.agents.middleware import AgentMiddleware
|
|
10
|
+
from langchain_core.tools import ToolException, tool
|
|
11
|
+
from pydantic import Field
|
|
12
|
+
from stagehand import Stagehand
|
|
13
|
+
from stagehand_middleware.facade import (
|
|
14
|
+
StagehandFacadeExecutionError,
|
|
15
|
+
StagehandFacadeInputError,
|
|
16
|
+
StagehandFacadeTools,
|
|
17
|
+
facade_tools,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .actions import SnapshotAction
|
|
21
|
+
|
|
22
|
+
T = TypeVar("T")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class StagehandMiddleware(AgentMiddleware):
|
|
26
|
+
"""Add browser tools with `middleware=[StagehandMiddleware(stagehand=client)]`."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, *, stagehand: Stagehand) -> None:
|
|
29
|
+
self._stagehand = stagehand
|
|
30
|
+
self._facade = self._open_facade()
|
|
31
|
+
descriptions = {spec["name"]: spec["description"] for spec in facade_tools()}
|
|
32
|
+
|
|
33
|
+
@tool(
|
|
34
|
+
description=descriptions["run"]
|
|
35
|
+
+ (
|
|
36
|
+
" Reuse the initialized browser; do not launch another browser or browser process."
|
|
37
|
+
" Capture a new snapshot after navigation or stale IDs."
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
async def run(
|
|
41
|
+
*, code: str | None = None, actions: list[SnapshotAction] | None = None
|
|
42
|
+
) -> str:
|
|
43
|
+
result = await self._call(lambda facade: facade.run(code=code, actions=actions))
|
|
44
|
+
return result if isinstance(result, str) else json.dumps(result, ensure_ascii=False)
|
|
45
|
+
|
|
46
|
+
@tool(
|
|
47
|
+
description=descriptions["snapshot"]
|
|
48
|
+
+ (" Capture a new snapshot after navigation or stale IDs before taking actions.")
|
|
49
|
+
)
|
|
50
|
+
async def snapshot(*, includeIframes: bool = True) -> str:
|
|
51
|
+
return await self._call(lambda facade: facade.snapshot(include_iframes=includeIframes))
|
|
52
|
+
|
|
53
|
+
@tool(description=descriptions["screenshot"])
|
|
54
|
+
async def screenshot(
|
|
55
|
+
*,
|
|
56
|
+
fullPage: bool | None = None,
|
|
57
|
+
type: Literal["png", "jpeg"] = "png",
|
|
58
|
+
quality: Annotated[float | None, Field(ge=0, le=100)] = None,
|
|
59
|
+
) -> list[dict[str, str]]:
|
|
60
|
+
image = await self._call(
|
|
61
|
+
lambda facade: facade.screenshot(
|
|
62
|
+
full_page=fullPage,
|
|
63
|
+
type=type,
|
|
64
|
+
quality=quality,
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
return [{"type": "image", "base64": image["data"], "mime_type": image["mimeType"]}]
|
|
68
|
+
|
|
69
|
+
self.tools = [run, snapshot, screenshot]
|
|
70
|
+
for browser_tool in self.tools:
|
|
71
|
+
browser_tool.handle_tool_error = True
|
|
72
|
+
browser_tool.handle_validation_error = True
|
|
73
|
+
|
|
74
|
+
def _open_facade(self) -> StagehandFacadeTools:
|
|
75
|
+
# The application owns the client and browser: `browser.close()` in run code
|
|
76
|
+
# leaves both open and swaps in a fresh facade, so later calls still work.
|
|
77
|
+
async def reopen() -> None:
|
|
78
|
+
self._facade = self._open_facade()
|
|
79
|
+
|
|
80
|
+
return StagehandFacadeTools(self._stagehand, on_close_requested=reopen)
|
|
81
|
+
|
|
82
|
+
async def _call(self, operation: Callable[[StagehandFacadeTools], Awaitable[T]]) -> T:
|
|
83
|
+
try:
|
|
84
|
+
return await operation(self._facade)
|
|
85
|
+
except (StagehandFacadeInputError, StagehandFacadeExecutionError) as error:
|
|
86
|
+
raise ToolException(str(error)) from None
|
|
87
|
+
except Exception as error:
|
|
88
|
+
raise ToolException(f"{type(error).__name__}: {error}") from None
|
|
File without changes
|