Skip to content

Commit f6f10b9

Browse files
committed
Accept TypedDict/dict params across managers (keep Pydantic compat)
1 parent 9ed3288 commit f6f10b9

38 files changed

Lines changed: 1609 additions & 226 deletions

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,39 @@ def main():
107107
main()
108108
```
109109

110+
### Passing parameters
111+
112+
Every manager method that takes a params object accepts **either** a plain
113+
dictionary (recommended — you get editor autocomplete via the `*Dict`
114+
`TypedDict` types) **or** the existing Pydantic params class. Both produce
115+
identical requests, so existing code keeps working after upgrading.
116+
117+
```python
118+
from hyperbrowser import Hyperbrowser
119+
from hyperbrowser.models import StartScrapeJobParams # still supported
120+
121+
client = Hyperbrowser(api_key="test-key")
122+
123+
# New: plain dict with snake_case keys (autocompletes against StartScrapeJobParamsDict)
124+
client.scrape.start_and_wait(
125+
{
126+
"url": "https://example.com",
127+
"scrape_options": {"formats": ["markdown"], "only_main_content": True},
128+
}
129+
)
130+
131+
# Existing: Pydantic params object — unchanged
132+
client.scrape.start_and_wait(
133+
StartScrapeJobParams(
134+
url="https://example.com",
135+
scrape_options={"formats": ["markdown"], "only_main_content": True},
136+
)
137+
)
138+
```
139+
140+
For extraction and structured web-fetch outputs, the `schema` field still
141+
accepts a Pydantic model class or a raw JSON schema dict, exactly as before.
142+
110143
## Sandboxes
111144

112145
The sync and async clients expose the same sandbox APIs through `client.sandboxes`.

hyperbrowser/client/managers/async_manager/agents/browser_use.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import asyncio
2+
from typing import Union
3+
24
import jsonref
35

46
from hyperbrowser.exceptions import HyperbrowserError
7+
from .....models.params import coerce_to_model, StartBrowserUseTaskParamsDict
58

69
from .....models import (
710
POLLING_ATTEMPTS,
@@ -18,8 +21,9 @@ def __init__(self, client):
1821
self._client = client
1922

2023
async def start(
21-
self, params: StartBrowserUseTaskParams
24+
self, params: Union[StartBrowserUseTaskParams, StartBrowserUseTaskParamsDict]
2225
) -> StartBrowserUseTaskResponse:
26+
params = coerce_to_model(StartBrowserUseTaskParams, params)
2327
if params.output_model_schema:
2428
if hasattr(params.output_model_schema, "model_json_schema"):
2529
params.output_model_schema = jsonref.replace_refs(
@@ -52,7 +56,7 @@ async def stop(self, job_id: str) -> BasicResponse:
5256
return BasicResponse(**response.data)
5357

5458
async def start_and_wait(
55-
self, params: StartBrowserUseTaskParams
59+
self, params: Union[StartBrowserUseTaskParams, StartBrowserUseTaskParamsDict]
5660
) -> BrowserUseTaskResponse:
5761
job_start_resp = await self.start(params)
5862
job_id = job_start_resp.job_id

hyperbrowser/client/managers/async_manager/agents/claude_computer_use.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import asyncio
2+
from typing import Union
3+
4+
from .....models.params import coerce_to_model, StartClaudeComputerUseTaskParamsDict
25

36
from hyperbrowser.exceptions import HyperbrowserError
47

@@ -17,11 +20,16 @@ def __init__(self, client):
1720
self._client = client
1821

1922
async def start(
20-
self, params: StartClaudeComputerUseTaskParams
23+
self,
24+
params: Union[
25+
StartClaudeComputerUseTaskParams, StartClaudeComputerUseTaskParamsDict
26+
],
2127
) -> StartClaudeComputerUseTaskResponse:
2228
response = await self._client.transport.post(
2329
self._client._build_url("/task/claude-computer-use"),
24-
data=params.model_dump(exclude_none=True, by_alias=True),
30+
data=coerce_to_model(StartClaudeComputerUseTaskParams, params).model_dump(
31+
exclude_none=True, by_alias=True
32+
),
2533
)
2634
return StartClaudeComputerUseTaskResponse(**response.data)
2735

@@ -44,7 +52,10 @@ async def stop(self, job_id: str) -> BasicResponse:
4452
return BasicResponse(**response.data)
4553

4654
async def start_and_wait(
47-
self, params: StartClaudeComputerUseTaskParams
55+
self,
56+
params: Union[
57+
StartClaudeComputerUseTaskParams, StartClaudeComputerUseTaskParamsDict
58+
],
4859
) -> ClaudeComputerUseTaskResponse:
4960
job_start_resp = await self.start(params)
5061
job_id = job_start_resp.job_id

hyperbrowser/client/managers/async_manager/agents/cua.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import asyncio
2+
from typing import Union
3+
4+
from .....models.params import coerce_to_model, StartCuaTaskParamsDict
25

36
from hyperbrowser.exceptions import HyperbrowserError
47

@@ -16,10 +19,14 @@ class CuaManager:
1619
def __init__(self, client):
1720
self._client = client
1821

19-
async def start(self, params: StartCuaTaskParams) -> StartCuaTaskResponse:
22+
async def start(
23+
self, params: Union[StartCuaTaskParams, StartCuaTaskParamsDict]
24+
) -> StartCuaTaskResponse:
2025
response = await self._client.transport.post(
2126
self._client._build_url("/task/cua"),
22-
data=params.model_dump(exclude_none=True, by_alias=True),
27+
data=coerce_to_model(StartCuaTaskParams, params).model_dump(
28+
exclude_none=True, by_alias=True
29+
),
2330
)
2431
return StartCuaTaskResponse(**response.data)
2532

@@ -41,7 +48,9 @@ async def stop(self, job_id: str) -> BasicResponse:
4148
)
4249
return BasicResponse(**response.data)
4350

44-
async def start_and_wait(self, params: StartCuaTaskParams) -> CuaTaskResponse:
51+
async def start_and_wait(
52+
self, params: Union[StartCuaTaskParams, StartCuaTaskParamsDict]
53+
) -> CuaTaskResponse:
4554
job_start_resp = await self.start(params)
4655
job_id = job_start_resp.job_id
4756
if not job_id:

hyperbrowser/client/managers/async_manager/agents/gemini_computer_use.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import asyncio
2+
from typing import Union
3+
4+
from .....models.params import coerce_to_model, StartGeminiComputerUseTaskParamsDict
25

36
from hyperbrowser.exceptions import HyperbrowserError
47

@@ -17,11 +20,16 @@ def __init__(self, client):
1720
self._client = client
1821

1922
async def start(
20-
self, params: StartGeminiComputerUseTaskParams
23+
self,
24+
params: Union[
25+
StartGeminiComputerUseTaskParams, StartGeminiComputerUseTaskParamsDict
26+
],
2127
) -> StartGeminiComputerUseTaskResponse:
2228
response = await self._client.transport.post(
2329
self._client._build_url("/task/gemini-computer-use"),
24-
data=params.model_dump(exclude_none=True, by_alias=True),
30+
data=coerce_to_model(StartGeminiComputerUseTaskParams, params).model_dump(
31+
exclude_none=True, by_alias=True
32+
),
2533
)
2634
return StartGeminiComputerUseTaskResponse(**response.data)
2735

@@ -44,7 +52,10 @@ async def stop(self, job_id: str) -> BasicResponse:
4452
return BasicResponse(**response.data)
4553

4654
async def start_and_wait(
47-
self, params: StartGeminiComputerUseTaskParams
55+
self,
56+
params: Union[
57+
StartGeminiComputerUseTaskParams, StartGeminiComputerUseTaskParamsDict
58+
],
4859
) -> GeminiComputerUseTaskResponse:
4960
job_start_resp = await self.start(params)
5061
job_id = job_start_resp.job_id

hyperbrowser/client/managers/async_manager/agents/grok_computer_use.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import asyncio
2+
from typing import Union
3+
4+
from .....models.params import coerce_to_model, StartGrokComputerUseTaskParamsDict
25

36
from hyperbrowser.exceptions import HyperbrowserError
47

@@ -17,11 +20,16 @@ def __init__(self, client):
1720
self._client = client
1821

1922
async def start(
20-
self, params: StartGrokComputerUseTaskParams
23+
self,
24+
params: Union[
25+
StartGrokComputerUseTaskParams, StartGrokComputerUseTaskParamsDict
26+
],
2127
) -> StartGrokComputerUseTaskResponse:
2228
response = await self._client.transport.post(
2329
self._client._build_url("/task/grok-computer-use"),
24-
data=params.model_dump(exclude_none=True, by_alias=True),
30+
data=coerce_to_model(StartGrokComputerUseTaskParams, params).model_dump(
31+
exclude_none=True, by_alias=True
32+
),
2533
)
2634
return StartGrokComputerUseTaskResponse(**response.data)
2735

@@ -44,7 +52,10 @@ async def stop(self, job_id: str) -> BasicResponse:
4452
return BasicResponse(**response.data)
4553

4654
async def start_and_wait(
47-
self, params: StartGrokComputerUseTaskParams
55+
self,
56+
params: Union[
57+
StartGrokComputerUseTaskParams, StartGrokComputerUseTaskParamsDict
58+
],
4859
) -> GrokComputerUseTaskResponse:
4960
job_start_resp = await self.start(params)
5061
job_id = job_start_resp.job_id

hyperbrowser/client/managers/async_manager/agents/hyper_agent.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import asyncio
2+
from typing import Union
3+
4+
from .....models.params import coerce_to_model, StartHyperAgentTaskParamsDict
25

36
from hyperbrowser.exceptions import HyperbrowserError
47

@@ -17,11 +20,13 @@ def __init__(self, client):
1720
self._client = client
1821

1922
async def start(
20-
self, params: StartHyperAgentTaskParams
23+
self, params: Union[StartHyperAgentTaskParams, StartHyperAgentTaskParamsDict]
2124
) -> StartHyperAgentTaskResponse:
2225
response = await self._client.transport.post(
2326
self._client._build_url("/task/hyper-agent"),
24-
data=params.model_dump(exclude_none=True, by_alias=True),
27+
data=coerce_to_model(StartHyperAgentTaskParams, params).model_dump(
28+
exclude_none=True, by_alias=True
29+
),
2530
)
2631
return StartHyperAgentTaskResponse(**response.data)
2732

@@ -44,7 +49,7 @@ async def stop(self, job_id: str) -> BasicResponse:
4449
return BasicResponse(**response.data)
4550

4651
async def start_and_wait(
47-
self, params: StartHyperAgentTaskParams
52+
self, params: Union[StartHyperAgentTaskParams, StartHyperAgentTaskParamsDict]
4853
) -> HyperAgentTaskResponse:
4954
job_start_resp = await self.start(params)
5055
job_id = job_start_resp.job_id

hyperbrowser/client/managers/async_manager/crawl.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import asyncio
2+
from typing import Union
23

34
from hyperbrowser.models.consts import POLLING_ATTEMPTS
5+
from ....models.params import (
6+
coerce_to_model,
7+
GetCrawlJobParamsDict,
8+
StartCrawlJobParamsDict,
9+
)
410
from ....models.crawl import (
511
CrawlJobResponse,
612
CrawlJobStatus,
@@ -16,7 +22,10 @@ class CrawlManager:
1622
def __init__(self, client):
1723
self._client = client
1824

19-
async def start(self, params: StartCrawlJobParams) -> StartCrawlJobResponse:
25+
async def start(
26+
self, params: Union[StartCrawlJobParams, StartCrawlJobParamsDict]
27+
) -> StartCrawlJobResponse:
28+
params = coerce_to_model(StartCrawlJobParams, params)
2029
response = await self._client.transport.post(
2130
self._client._build_url("/crawl"),
2231
data=params.model_dump(exclude_none=True, by_alias=True),
@@ -30,16 +39,21 @@ async def get_status(self, job_id: str) -> CrawlJobStatusResponse:
3039
return CrawlJobStatusResponse(**response.data)
3140

3241
async def get(
33-
self, job_id: str, params: GetCrawlJobParams = GetCrawlJobParams()
42+
self,
43+
job_id: str,
44+
params: Union[GetCrawlJobParams, GetCrawlJobParamsDict] = GetCrawlJobParams(),
3445
) -> CrawlJobResponse:
46+
params = coerce_to_model(GetCrawlJobParams, params)
3547
response = await self._client.transport.get(
3648
self._client._build_url(f"/crawl/{job_id}"),
3749
params=params.model_dump(exclude_none=True, by_alias=True),
3850
)
3951
return CrawlJobResponse(**response.data)
4052

4153
async def start_and_wait(
42-
self, params: StartCrawlJobParams, return_all_pages: bool = True
54+
self,
55+
params: Union[StartCrawlJobParams, StartCrawlJobParamsDict],
56+
return_all_pages: bool = True,
4357
) -> CrawlJobResponse:
4458
job_start_resp = await self.start(params)
4559
job_id = job_start_resp.job_id

hyperbrowser/client/managers/async_manager/extension.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import os
2-
from typing import List
2+
from typing import List, Union
33

44
from hyperbrowser.exceptions import HyperbrowserError
5+
from hyperbrowser.models.params import coerce_to_model, CreateExtensionParamsDict
56
from hyperbrowser.models.extension import CreateExtensionParams, ExtensionResponse
67

78

89
class ExtensionManager:
910
def __init__(self, client):
1011
self._client = client
1112

12-
async def create(self, params: CreateExtensionParams) -> ExtensionResponse:
13+
async def create(
14+
self, params: Union[CreateExtensionParams, CreateExtensionParamsDict]
15+
) -> ExtensionResponse:
16+
params = coerce_to_model(CreateExtensionParams, params)
1317
file_path = params.file_path
1418
params.file_path = None
1519

hyperbrowser/client/managers/async_manager/extract.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import asyncio
2+
from typing import Union
3+
24
from hyperbrowser.exceptions import HyperbrowserError
35
from hyperbrowser.models.consts import POLLING_ATTEMPTS
6+
from hyperbrowser.models.params import coerce_to_model, StartExtractJobParamsDict
47
from hyperbrowser.models.extract import (
58
ExtractJobResponse,
69
ExtractJobStatusResponse,
@@ -14,7 +17,10 @@ class ExtractManager:
1417
def __init__(self, client):
1518
self._client = client
1619

17-
async def start(self, params: StartExtractJobParams) -> StartExtractJobResponse:
20+
async def start(
21+
self, params: Union[StartExtractJobParams, StartExtractJobParamsDict]
22+
) -> StartExtractJobResponse:
23+
params = coerce_to_model(StartExtractJobParams, params)
1824
if not params.schema_ and not params.prompt:
1925
raise HyperbrowserError("Either schema or prompt must be provided")
2026
if params.schema_:
@@ -41,7 +47,9 @@ async def get(self, job_id: str) -> ExtractJobResponse:
4147
)
4248
return ExtractJobResponse(**response.data)
4349

44-
async def start_and_wait(self, params: StartExtractJobParams) -> ExtractJobResponse:
50+
async def start_and_wait(
51+
self, params: Union[StartExtractJobParams, StartExtractJobParamsDict]
52+
) -> ExtractJobResponse:
4553
job_start_resp = await self.start(params)
4654
job_id = job_start_resp.job_id
4755
if not job_id:

0 commit comments

Comments
 (0)