diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3d2ac0b..10f3091 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0" + ".": "0.2.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 8a02f60..eeba574 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 26 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browser-use%2Fbrowser-use-3a9488448292a0736b08b2d6427e702eaf7106d86345ca2e65b64bed4398b036.yml -openapi_spec_hash: 5ff2781dcc11a0c9aa353f5cb14bcc16 -config_hash: 9d52be5177b2ede4cb0633c04f4cc4ef +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browser-use%2Fbrowser-use-ce018db4d6891d645cfb220c86d17ac1d431e1ba2f604e8015876b17a5a11149.yml +openapi_spec_hash: e9a00924682ab214ca5d8b6b5c84430e +config_hash: dd3e22b635fa0eb9a7c741a8aaca2a7f diff --git a/CHANGELOG.md b/CHANGELOG.md index 91a872a..31ba976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.2.0 (2025-08-19) + +Full Changelog: [v0.1.0...v0.2.0](https://github.com/browser-use/browser-use-python/compare/v0.1.0...v0.2.0) + +### Features + +* **api:** manual updates ([6266282](https://github.com/browser-use/browser-use-python/commit/6266282a615344fdab0737d29adc9124a3bf8b8d)) +* **api:** manual updates ([2d9ba52](https://github.com/browser-use/browser-use-python/commit/2d9ba52b23e53c581360afc655fa8d665a106814)) +* Improve Docs ([6e79b7c](https://github.com/browser-use/browser-use-python/commit/6e79b7c5cfc7cf54f1474521025fa713f200bc3b)) + ## 0.1.0 (2025-08-18) Full Changelog: [v0.0.2...v0.1.0](https://github.com/browser-use/browser-use-python/compare/v0.0.2...v0.1.0) diff --git a/README.md b/README.md index 6a1320b..894765b 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,10 @@ client = BrowserUse( api_key=os.environ.get("BROWSER_USE_API_KEY"), # This is the default and can be omitted ) -me = client.users.me.retrieve() -print(me.additional_credits_balance_usd) +task = client.tasks.create( + task="Search for the top 10 Hacker News posts and return the title and url.", +) +print(task.id) ``` While you can provide an `api_key` keyword argument, @@ -56,8 +58,10 @@ client = AsyncBrowserUse( async def main() -> None: - me = await client.users.me.retrieve() - print(me.additional_credits_balance_usd) + task = await client.tasks.create( + task="Search for the top 10 Hacker News posts and return the title and url.", + ) + print(task.id) asyncio.run(main()) @@ -89,8 +93,10 @@ async def main() -> None: api_key="My API Key", http_client=DefaultAioHttpClient(), ) as client: - me = await client.users.me.retrieve() - print(me.additional_credits_balance_usd) + task = await client.tasks.create( + task="Search for the top 10 Hacker News posts and return the title and url.", + ) + print(task.id) asyncio.run(main()) @@ -137,7 +143,9 @@ from browser_use_sdk import BrowserUse client = BrowserUse() try: - client.users.me.retrieve() + client.tasks.create( + task="Search for the top 10 Hacker News posts and return the title and url.", + ) except browser_use_sdk.APIConnectionError as e: print("The server could not be reached") print(e.__cause__) # an underlying Exception, likely raised within httpx. @@ -180,7 +188,9 @@ client = BrowserUse( ) # Or, configure per-request: -client.with_options(max_retries=5).users.me.retrieve() +client.with_options(max_retries=5).tasks.create( + task="Search for the top 10 Hacker News posts and return the title and url.", +) ``` ### Timeouts @@ -203,7 +213,9 @@ client = BrowserUse( ) # Override per-request: -client.with_options(timeout=5.0).users.me.retrieve() +client.with_options(timeout=5.0).tasks.create( + task="Search for the top 10 Hacker News posts and return the title and url.", +) ``` On timeout, an `APITimeoutError` is thrown. @@ -244,11 +256,13 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from browser_use_sdk import BrowserUse client = BrowserUse() -response = client.users.me.with_raw_response.retrieve() +response = client.tasks.with_raw_response.create( + task="Search for the top 10 Hacker News posts and return the title and url.", +) print(response.headers.get('X-My-Header')) -me = response.parse() # get the object that `users.me.retrieve()` would have returned -print(me.additional_credits_balance_usd) +task = response.parse() # get the object that `tasks.create()` would have returned +print(task.id) ``` These methods return an [`APIResponse`](https://github.com/browser-use/browser-use-python/tree/main/src/browser_use_sdk/_response.py) object. @@ -262,7 +276,9 @@ The above interface eagerly reads the full response body when you make the reque To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. ```python -with client.users.me.with_streaming_response.retrieve() as response: +with client.tasks.with_streaming_response.create( + task="Search for the top 10 Hacker News posts and return the title and url.", +) as response: print(response.headers.get("X-My-Header")) for line in response.iter_lines(): diff --git a/pyproject.toml b/pyproject.toml index d50d931..3617055 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "browser-use-sdk" -version = "0.1.0" +version = "0.2.0" description = "The official Python library for the browser-use API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/browser_use_sdk/_version.py b/src/browser_use_sdk/_version.py index 86ea4c5..052155b 100644 --- a/src/browser_use_sdk/_version.py +++ b/src/browser_use_sdk/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "browser_use_sdk" -__version__ = "0.1.0" # x-release-please-version +__version__ = "0.2.0" # x-release-please-version diff --git a/src/browser_use_sdk/resources/tasks.py b/src/browser_use_sdk/resources/tasks.py index df75c36..fab70a6 100644 --- a/src/browser_use_sdk/resources/tasks.py +++ b/src/browser_use_sdk/resources/tasks.py @@ -69,12 +69,12 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskCreateResponse: """ - Create and start a new AI agent task. + Create and start a new Browser Use Agent task. This is the main endpoint for running AI agents. You can either: - 1. Start a new session with a new task - 2. Add a follow-up task to an existing session + 1. Start a new session with a new task. + 2. Add a follow-up task to an existing session. When starting a new session: @@ -95,9 +95,8 @@ def create( - Browser profiles: Control browser settings and environment (only used for new sessions) - File uploads: Include documents for the agent to work with - - Structured output: Define the format you want results in - - Task metadata: Add custom data for tracking and organization (useful when - using webhooks) + - Structured output: Define the format of the task result + - Task metadata: Add custom data for tracking and organization Args: @@ -564,12 +563,12 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskCreateResponse: """ - Create and start a new AI agent task. + Create and start a new Browser Use Agent task. This is the main endpoint for running AI agents. You can either: - 1. Start a new session with a new task - 2. Add a follow-up task to an existing session + 1. Start a new session with a new task. + 2. Add a follow-up task to an existing session. When starting a new session: @@ -590,9 +589,8 @@ async def create( - Browser profiles: Control browser settings and environment (only used for new sessions) - File uploads: Include documents for the agent to work with - - Structured output: Define the format you want results in - - Task metadata: Add custom data for tracking and organization (useful when - using webhooks) + - Structured output: Define the format of the task result + - Task metadata: Add custom data for tracking and organization Args: diff --git a/tests/test_client.py b/tests/test_client.py index 8f044e4..a3b498a 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -722,20 +722,20 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str @mock.patch("browser_use_sdk._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: BrowserUse) -> None: - respx_mock.get("/users/me").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/tasks").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.users.me.with_streaming_response.retrieve().__enter__() + client.tasks.with_streaming_response.create(task="x").__enter__() assert _get_open_connections(self.client) == 0 @mock.patch("browser_use_sdk._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: BrowserUse) -> None: - respx_mock.get("/users/me").mock(return_value=httpx.Response(500)) + respx_mock.post("/tasks").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.users.me.with_streaming_response.retrieve().__enter__() + client.tasks.with_streaming_response.create(task="x").__enter__() assert _get_open_connections(self.client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -762,9 +762,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/users/me").mock(side_effect=retry_handler) + respx_mock.post("/tasks").mock(side_effect=retry_handler) - response = client.users.me.with_raw_response.retrieve() + response = client.tasks.with_raw_response.create(task="x") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -786,9 +786,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/users/me").mock(side_effect=retry_handler) + respx_mock.post("/tasks").mock(side_effect=retry_handler) - response = client.users.me.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": Omit()}) + response = client.tasks.with_raw_response.create(task="x", extra_headers={"x-stainless-retry-count": Omit()}) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -809,9 +809,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/users/me").mock(side_effect=retry_handler) + respx_mock.post("/tasks").mock(side_effect=retry_handler) - response = client.users.me.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": "42"}) + response = client.tasks.with_raw_response.create(task="x", extra_headers={"x-stainless-retry-count": "42"}) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1539,10 +1539,10 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte async def test_retrying_timeout_errors_doesnt_leak( self, respx_mock: MockRouter, async_client: AsyncBrowserUse ) -> None: - respx_mock.get("/users/me").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/tasks").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.users.me.with_streaming_response.retrieve().__aenter__() + await async_client.tasks.with_streaming_response.create(task="x").__aenter__() assert _get_open_connections(self.client) == 0 @@ -1551,10 +1551,10 @@ async def test_retrying_timeout_errors_doesnt_leak( async def test_retrying_status_errors_doesnt_leak( self, respx_mock: MockRouter, async_client: AsyncBrowserUse ) -> None: - respx_mock.get("/users/me").mock(return_value=httpx.Response(500)) + respx_mock.post("/tasks").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.users.me.with_streaming_response.retrieve().__aenter__() + await async_client.tasks.with_streaming_response.create(task="x").__aenter__() assert _get_open_connections(self.client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1582,9 +1582,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/users/me").mock(side_effect=retry_handler) + respx_mock.post("/tasks").mock(side_effect=retry_handler) - response = await client.users.me.with_raw_response.retrieve() + response = await client.tasks.with_raw_response.create(task="x") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1607,9 +1607,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/users/me").mock(side_effect=retry_handler) + respx_mock.post("/tasks").mock(side_effect=retry_handler) - response = await client.users.me.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": Omit()}) + response = await client.tasks.with_raw_response.create( + task="x", extra_headers={"x-stainless-retry-count": Omit()} + ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1631,9 +1633,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/users/me").mock(side_effect=retry_handler) + respx_mock.post("/tasks").mock(side_effect=retry_handler) - response = await client.users.me.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": "42"}) + response = await client.tasks.with_raw_response.create( + task="x", extra_headers={"x-stainless-retry-count": "42"} + ) assert response.http_request.headers.get("x-stainless-retry-count") == "42"