From ecafbb511dbe9be78e84f4e1d34e1feefa1fce8e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 21:50:13 +0000 Subject: [PATCH 1/3] feat: [CORE-1796][apps/api] Update Node.js SDK --- .stats.yml | 4 ++-- src/browserbase/types/fetch_api_create_response.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 3aabff9..5db2681 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fbrowserbase-b20f9fea14d79990ab1af3d276f931e026cd955ac623ec6ace80b2af90de170f.yml -openapi_spec_hash: 943ff4b3297014503fdc9854544cb9a4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fbrowserbase-d5413d9ed31cc9a28a804736cedc95e8171a9465ed66583ae67f225e9c739ca2.yml +openapi_spec_hash: fafc55851f83f26ddb2554589965e8f5 config_hash: 55c54fdafc9e80be584829b5724b00ab diff --git a/src/browserbase/types/fetch_api_create_response.py b/src/browserbase/types/fetch_api_create_response.py index cfafbba..f97f563 100644 --- a/src/browserbase/types/fetch_api_create_response.py +++ b/src/browserbase/types/fetch_api_create_response.py @@ -10,8 +10,6 @@ class FetchAPICreateResponse(BaseModel): - """Response body for fetch""" - id: str """Unique identifier for the fetch request""" From 55e2d0db2f31b2ca390eaa6855f22de1f1688a88 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:38:41 +0000 Subject: [PATCH 2/3] feat: [GRO-000] docs: Add guide + docs for search api --- .stats.yml | 8 +- api.md | 12 ++ src/browserbase/_client.py | 39 +++- src/browserbase/resources/__init__.py | 14 ++ src/browserbase/resources/search.py | 185 +++++++++++++++++++ src/browserbase/types/__init__.py | 2 + src/browserbase/types/search_web_params.py | 17 ++ src/browserbase/types/search_web_response.py | 46 +++++ tests/api_resources/test_search.py | 102 ++++++++++ 9 files changed, 420 insertions(+), 5 deletions(-) create mode 100644 src/browserbase/resources/search.py create mode 100644 src/browserbase/types/search_web_params.py create mode 100644 src/browserbase/types/search_web_response.py create mode 100644 tests/api_resources/test_search.py diff --git a/.stats.yml b/.stats.yml index 5db2681..28a1de1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fbrowserbase-d5413d9ed31cc9a28a804736cedc95e8171a9465ed66583ae67f225e9c739ca2.yml -openapi_spec_hash: fafc55851f83f26ddb2554589965e8f5 -config_hash: 55c54fdafc9e80be584829b5724b00ab +configured_endpoints: 21 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fbrowserbase-9b1e2a2abf39dd780601935a9a9ee04cb939e2c3ba76627535f625b6aeaf5eb7.yml +openapi_spec_hash: 12fe5f4306c43fdfb394a33f79391a82 +config_hash: cf04ecfb8dad5fbd8b85be25d6e9ec55 diff --git a/api.md b/api.md index ac45583..b6066cb 100644 --- a/api.md +++ b/api.md @@ -53,6 +53,18 @@ Methods: - client.projects.list() -> ProjectListResponse - client.projects.usage(id) -> ProjectUsage +# Search + +Types: + +```python +from browserbase.types import SearchWebResponse +``` + +Methods: + +- client.search.web(\*\*params) -> SearchWebResponse + # Sessions Types: diff --git a/src/browserbase/_client.py b/src/browserbase/_client.py index 70a7b71..d642273 100644 --- a/src/browserbase/_client.py +++ b/src/browserbase/_client.py @@ -31,7 +31,8 @@ ) if TYPE_CHECKING: - from .resources import contexts, projects, sessions, fetch_api, extensions + from .resources import search, contexts, projects, sessions, fetch_api, extensions + from .resources.search import SearchResource, AsyncSearchResource from .resources.contexts import ContextsResource, AsyncContextsResource from .resources.projects import ProjectsResource, AsyncProjectsResource from .resources.fetch_api import FetchAPIResource, AsyncFetchAPIResource @@ -129,6 +130,12 @@ def projects(self) -> ProjectsResource: return ProjectsResource(self) + @cached_property + def search(self) -> SearchResource: + from .resources.search import SearchResource + + return SearchResource(self) + @cached_property def sessions(self) -> SessionsResource: from .resources.sessions import SessionsResource @@ -327,6 +334,12 @@ def projects(self) -> AsyncProjectsResource: return AsyncProjectsResource(self) + @cached_property + def search(self) -> AsyncSearchResource: + from .resources.search import AsyncSearchResource + + return AsyncSearchResource(self) + @cached_property def sessions(self) -> AsyncSessionsResource: from .resources.sessions import AsyncSessionsResource @@ -476,6 +489,12 @@ def projects(self) -> projects.ProjectsResourceWithRawResponse: return ProjectsResourceWithRawResponse(self._client.projects) + @cached_property + def search(self) -> search.SearchResourceWithRawResponse: + from .resources.search import SearchResourceWithRawResponse + + return SearchResourceWithRawResponse(self._client.search) + @cached_property def sessions(self) -> sessions.SessionsResourceWithRawResponse: from .resources.sessions import SessionsResourceWithRawResponse @@ -513,6 +532,12 @@ def projects(self) -> projects.AsyncProjectsResourceWithRawResponse: return AsyncProjectsResourceWithRawResponse(self._client.projects) + @cached_property + def search(self) -> search.AsyncSearchResourceWithRawResponse: + from .resources.search import AsyncSearchResourceWithRawResponse + + return AsyncSearchResourceWithRawResponse(self._client.search) + @cached_property def sessions(self) -> sessions.AsyncSessionsResourceWithRawResponse: from .resources.sessions import AsyncSessionsResourceWithRawResponse @@ -550,6 +575,12 @@ def projects(self) -> projects.ProjectsResourceWithStreamingResponse: return ProjectsResourceWithStreamingResponse(self._client.projects) + @cached_property + def search(self) -> search.SearchResourceWithStreamingResponse: + from .resources.search import SearchResourceWithStreamingResponse + + return SearchResourceWithStreamingResponse(self._client.search) + @cached_property def sessions(self) -> sessions.SessionsResourceWithStreamingResponse: from .resources.sessions import SessionsResourceWithStreamingResponse @@ -587,6 +618,12 @@ def projects(self) -> projects.AsyncProjectsResourceWithStreamingResponse: return AsyncProjectsResourceWithStreamingResponse(self._client.projects) + @cached_property + def search(self) -> search.AsyncSearchResourceWithStreamingResponse: + from .resources.search import AsyncSearchResourceWithStreamingResponse + + return AsyncSearchResourceWithStreamingResponse(self._client.search) + @cached_property def sessions(self) -> sessions.AsyncSessionsResourceWithStreamingResponse: from .resources.sessions import AsyncSessionsResourceWithStreamingResponse diff --git a/src/browserbase/resources/__init__.py b/src/browserbase/resources/__init__.py index f5a2bf0..83a8788 100644 --- a/src/browserbase/resources/__init__.py +++ b/src/browserbase/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .search import ( + SearchResource, + AsyncSearchResource, + SearchResourceWithRawResponse, + AsyncSearchResourceWithRawResponse, + SearchResourceWithStreamingResponse, + AsyncSearchResourceWithStreamingResponse, +) from .contexts import ( ContextsResource, AsyncContextsResource, @@ -66,6 +74,12 @@ "AsyncProjectsResourceWithRawResponse", "ProjectsResourceWithStreamingResponse", "AsyncProjectsResourceWithStreamingResponse", + "SearchResource", + "AsyncSearchResource", + "SearchResourceWithRawResponse", + "AsyncSearchResourceWithRawResponse", + "SearchResourceWithStreamingResponse", + "AsyncSearchResourceWithStreamingResponse", "SessionsResource", "AsyncSessionsResource", "SessionsResourceWithRawResponse", diff --git a/src/browserbase/resources/search.py b/src/browserbase/resources/search.py new file mode 100644 index 0000000..87f34e6 --- /dev/null +++ b/src/browserbase/resources/search.py @@ -0,0 +1,185 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import search_web_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.search_web_response import SearchWebResponse + +__all__ = ["SearchResource", "AsyncSearchResource"] + + +class SearchResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SearchResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/browserbase/sdk-python#accessing-raw-response-data-eg-headers + """ + return SearchResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SearchResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/browserbase/sdk-python#with_streaming_response + """ + return SearchResourceWithStreamingResponse(self) + + def web( + self, + *, + query: str, + num_results: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SearchWebResponse: + """ + Perform a web search and return structured results. + + Args: + query: The search query string + + num_results: Number of results to return (1-25) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/search", + body=maybe_transform( + { + "query": query, + "num_results": num_results, + }, + search_web_params.SearchWebParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SearchWebResponse, + ) + + +class AsyncSearchResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSearchResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/browserbase/sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncSearchResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSearchResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/browserbase/sdk-python#with_streaming_response + """ + return AsyncSearchResourceWithStreamingResponse(self) + + async def web( + self, + *, + query: str, + num_results: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SearchWebResponse: + """ + Perform a web search and return structured results. + + Args: + query: The search query string + + num_results: Number of results to return (1-25) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/search", + body=await async_maybe_transform( + { + "query": query, + "num_results": num_results, + }, + search_web_params.SearchWebParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SearchWebResponse, + ) + + +class SearchResourceWithRawResponse: + def __init__(self, search: SearchResource) -> None: + self._search = search + + self.web = to_raw_response_wrapper( + search.web, + ) + + +class AsyncSearchResourceWithRawResponse: + def __init__(self, search: AsyncSearchResource) -> None: + self._search = search + + self.web = async_to_raw_response_wrapper( + search.web, + ) + + +class SearchResourceWithStreamingResponse: + def __init__(self, search: SearchResource) -> None: + self._search = search + + self.web = to_streamed_response_wrapper( + search.web, + ) + + +class AsyncSearchResourceWithStreamingResponse: + def __init__(self, search: AsyncSearchResource) -> None: + self._search = search + + self.web = async_to_streamed_response_wrapper( + search.web, + ) diff --git a/src/browserbase/types/__init__.py b/src/browserbase/types/__init__.py index 5265990..0a9a3b8 100644 --- a/src/browserbase/types/__init__.py +++ b/src/browserbase/types/__init__.py @@ -7,7 +7,9 @@ from .session import Session as Session from .extension import Extension as Extension from .project_usage import ProjectUsage as ProjectUsage +from .search_web_params import SearchWebParams as SearchWebParams from .session_live_urls import SessionLiveURLs as SessionLiveURLs +from .search_web_response import SearchWebResponse as SearchWebResponse from .session_list_params import SessionListParams as SessionListParams from .context_create_params import ContextCreateParams as ContextCreateParams from .project_list_response import ProjectListResponse as ProjectListResponse diff --git a/src/browserbase/types/search_web_params.py b/src/browserbase/types/search_web_params.py new file mode 100644 index 0000000..68926b5 --- /dev/null +++ b/src/browserbase/types/search_web_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["SearchWebParams"] + + +class SearchWebParams(TypedDict, total=False): + query: Required[str] + """The search query string""" + + num_results: Annotated[int, PropertyInfo(alias="numResults")] + """Number of results to return (1-25)""" diff --git a/src/browserbase/types/search_web_response.py b/src/browserbase/types/search_web_response.py new file mode 100644 index 0000000..0243d22 --- /dev/null +++ b/src/browserbase/types/search_web_response.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["SearchWebResponse", "Result"] + + +class Result(BaseModel): + id: str + """Unique identifier for the result""" + + title: str + """The title of the search result""" + + url: str + """The URL of the search result""" + + author: Optional[str] = None + """Author of the content if available""" + + favicon: Optional[str] = None + """Favicon URL""" + + image: Optional[str] = None + """Image URL if available""" + + published_date: Optional[datetime] = FieldInfo(alias="publishedDate", default=None) + """Publication date in ISO 8601 format""" + + +class SearchWebResponse(BaseModel): + """Response body for web search""" + + query: str + """The search query that was executed""" + + request_id: str = FieldInfo(alias="requestId") + """Unique identifier for the request""" + + results: List[Result] + """List of search results""" diff --git a/tests/api_resources/test_search.py b/tests/api_resources/test_search.py new file mode 100644 index 0000000..fa00b77 --- /dev/null +++ b/tests/api_resources/test_search.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from browserbase import Browserbase, AsyncBrowserbase +from tests.utils import assert_matches_type +from browserbase.types import SearchWebResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSearch: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_web(self, client: Browserbase) -> None: + search = client.search.web( + query="x", + ) + assert_matches_type(SearchWebResponse, search, path=["response"]) + + @parametrize + def test_method_web_with_all_params(self, client: Browserbase) -> None: + search = client.search.web( + query="x", + num_results=1, + ) + assert_matches_type(SearchWebResponse, search, path=["response"]) + + @parametrize + def test_raw_response_web(self, client: Browserbase) -> None: + response = client.search.with_raw_response.web( + query="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + search = response.parse() + assert_matches_type(SearchWebResponse, search, path=["response"]) + + @parametrize + def test_streaming_response_web(self, client: Browserbase) -> None: + with client.search.with_streaming_response.web( + query="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + search = response.parse() + assert_matches_type(SearchWebResponse, search, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncSearch: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_web(self, async_client: AsyncBrowserbase) -> None: + search = await async_client.search.web( + query="x", + ) + assert_matches_type(SearchWebResponse, search, path=["response"]) + + @parametrize + async def test_method_web_with_all_params(self, async_client: AsyncBrowserbase) -> None: + search = await async_client.search.web( + query="x", + num_results=1, + ) + assert_matches_type(SearchWebResponse, search, path=["response"]) + + @parametrize + async def test_raw_response_web(self, async_client: AsyncBrowserbase) -> None: + response = await async_client.search.with_raw_response.web( + query="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + search = await response.parse() + assert_matches_type(SearchWebResponse, search, path=["response"]) + + @parametrize + async def test_streaming_response_web(self, async_client: AsyncBrowserbase) -> None: + async with async_client.search.with_streaming_response.web( + query="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + search = await response.parse() + assert_matches_type(SearchWebResponse, search, path=["response"]) + + assert cast(Any, response.is_closed) is True From 3eb1d5711f924748f313bbddc22714ab239edb61 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:38:59 +0000 Subject: [PATCH 3/3] release: 1.7.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/browserbase/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7deae33..cce9d1c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.6.0" + ".": "1.7.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a482d7b..d6cc7bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.7.0 (2026-03-16) + +Full Changelog: [v1.6.0...v1.7.0](https://github.com/browserbase/sdk-python/compare/v1.6.0...v1.7.0) + +### Features + +* [CORE-1796][apps/api] Update Node.js SDK ([ecafbb5](https://github.com/browserbase/sdk-python/commit/ecafbb511dbe9be78e84f4e1d34e1feefa1fce8e)) +* [GRO-000] docs: Add guide + docs for search api ([55e2d0d](https://github.com/browserbase/sdk-python/commit/55e2d0db2f31b2ca390eaa6855f22de1f1688a88)) + ## 1.6.0 (2026-03-11) Full Changelog: [v1.5.0...v1.6.0](https://github.com/browserbase/sdk-python/compare/v1.5.0...v1.6.0) diff --git a/pyproject.toml b/pyproject.toml index 425bcc4..ad5ff02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "browserbase" -version = "1.6.0" +version = "1.7.0" description = "The official Python library for the Browserbase API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/browserbase/_version.py b/src/browserbase/_version.py index a7f6a7d..1b8456b 100644 --- a/src/browserbase/_version.py +++ b/src/browserbase/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "browserbase" -__version__ = "1.6.0" # x-release-please-version +__version__ = "1.7.0" # x-release-please-version