Skip to content

feat(api): update via SDK Studio #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 28, 2024
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
2 changes: 1 addition & 1 deletion .stats.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
configured_endpoints: 18
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fbrowserbase-60444f8b1aa1aa8dbec1e9f11e929c2b7ac27470764ef5f1796134fc27f3381c.yml
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fbrowserbase-9f93c744538f57747ea1385817e21b40c318b65ebc155dca8950268beb280bc9.yml
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ from browserbase import Browserbase
client = Browserbase(
# This is the default and can be omitted
api_key=os.environ.get("BROWSERBASE_API_KEY"),
# or 'production' | 'local'; defaults to "production".
environment="development",
)

context = client.contexts.create(
Expand All @@ -58,6 +60,8 @@ from browserbase import AsyncBrowserbase
client = AsyncBrowserbase(
# This is the default and can be omitted
api_key=os.environ.get("BROWSERBASE_API_KEY"),
# or 'production' | 'local'; defaults to "production".
environment="development",
)


Expand Down
21 changes: 9 additions & 12 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ anyio==4.4.0
# via httpx
argcomplete==3.1.2
# via nox
attrs==23.1.0
# via pytest
certifi==2023.7.22
# via httpcore
# via httpx
Expand All @@ -28,8 +26,9 @@ distlib==0.3.7
# via virtualenv
distro==1.8.0
# via browserbase
exceptiongroup==1.1.3
exceptiongroup==1.2.2
# via anyio
# via pytest
filelock==3.12.4
# via virtualenv
h11==0.14.0
Expand Down Expand Up @@ -60,20 +59,18 @@ packaging==23.2
# via pytest
platformdirs==3.11.0
# via virtualenv
pluggy==1.3.0
# via pytest
py==1.11.0
pluggy==1.5.0
# via pytest
pydantic==2.7.1
pydantic==2.9.2
# via browserbase
pydantic-core==2.18.2
pydantic-core==2.23.4
# via pydantic
pygments==2.18.0
# via rich
pyright==1.1.380
pytest==7.1.1
pytest==8.3.3
# via pytest-asyncio
pytest-asyncio==0.21.1
pytest-asyncio==0.24.0
python-dateutil==2.8.2
# via time-machine
pytz==2023.3.post1
Expand All @@ -90,10 +87,10 @@ sniffio==1.3.0
# via browserbase
# via httpx
time-machine==2.9.0
tomli==2.0.1
tomli==2.0.2
# via mypy
# via pytest
typing-extensions==4.8.0
typing-extensions==4.12.2
# via anyio
# via browserbase
# via mypy
Expand Down
8 changes: 4 additions & 4 deletions requirements.lock
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ certifi==2023.7.22
# via httpx
distro==1.8.0
# via browserbase
exceptiongroup==1.1.3
exceptiongroup==1.2.2
# via anyio
h11==0.14.0
# via httpcore
Expand All @@ -30,15 +30,15 @@ httpx==0.25.2
idna==3.4
# via anyio
# via httpx
pydantic==2.7.1
pydantic==2.9.2
# via browserbase
pydantic-core==2.18.2
pydantic-core==2.23.4
# via pydantic
sniffio==1.3.0
# via anyio
# via browserbase
# via httpx
typing-extensions==4.8.0
typing-extensions==4.12.2
# via anyio
# via browserbase
# via pydantic
Expand Down
2 changes: 2 additions & 0 deletions src/browserbase/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ._types import NOT_GIVEN, NoneType, NotGiven, Transport, ProxiesTypes
from ._utils import file_from_path
from ._client import (
ENVIRONMENTS,
Client,
Stream,
Timeout,
Expand Down Expand Up @@ -68,6 +69,7 @@
"AsyncStream",
"Browserbase",
"AsyncBrowserbase",
"ENVIRONMENTS",
"file_from_path",
"BaseModel",
"DEFAULT_TIMEOUT",
Expand Down
83 changes: 71 additions & 12 deletions src/browserbase/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from __future__ import annotations

import os
from typing import Any, Union, Mapping
from typing_extensions import Self, override
from typing import Any, Dict, Union, Mapping, cast
from typing_extensions import Self, Literal, override

import httpx

Expand Down Expand Up @@ -33,6 +33,7 @@
)

__all__ = [
"ENVIRONMENTS",
"Timeout",
"Transport",
"ProxiesTypes",
Expand All @@ -44,6 +45,12 @@
"AsyncClient",
]

ENVIRONMENTS: Dict[str, str] = {
"production": "https://api.browserbase.com",
"development": "https://api.dev.browserbase.com",
"local": "http://api.localhost",
}


class Browserbase(SyncAPIClient):
contexts: resources.ContextsResource
Expand All @@ -56,11 +63,14 @@ class Browserbase(SyncAPIClient):
# client options
api_key: str

_environment: Literal["production", "development", "local"] | NotGiven

def __init__(
self,
*,
api_key: str | None = None,
base_url: str | httpx.URL | None = None,
environment: Literal["production", "development", "local"] | NotGiven = NOT_GIVEN,
base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
Expand Down Expand Up @@ -91,10 +101,31 @@ def __init__(
)
self.api_key = api_key

if base_url is None:
base_url = os.environ.get("BROWSERBASE_BASE_URL")
if base_url is None:
base_url = f"https://api.dev.browserbase.com"
self._environment = environment

base_url_env = os.environ.get("BROWSERBASE_BASE_URL")
if is_given(base_url) and base_url is not None:
# cast required because mypy doesn't understand the type narrowing
base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast]
elif is_given(environment):
if base_url_env and base_url is not None:
raise ValueError(
"Ambiguous URL; The `BROWSERBASE_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None",
)

try:
base_url = ENVIRONMENTS[environment]
except KeyError as exc:
raise ValueError(f"Unknown environment: {environment}") from exc
elif base_url_env is not None:
base_url = base_url_env
else:
self._environment = environment = "production"

try:
base_url = ENVIRONMENTS[environment]
except KeyError as exc:
raise ValueError(f"Unknown environment: {environment}") from exc

super().__init__(
version=__version__,
Expand Down Expand Up @@ -138,6 +169,7 @@ def copy(
self,
*,
api_key: str | None = None,
environment: Literal["production", "development", "local"] | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
http_client: httpx.Client | None = None,
Expand Down Expand Up @@ -173,6 +205,7 @@ def copy(
return self.__class__(
api_key=api_key or self.api_key,
base_url=base_url or self.base_url,
environment=environment or self._environment,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
http_client=http_client,
max_retries=max_retries if is_given(max_retries) else self.max_retries,
Expand Down Expand Up @@ -230,11 +263,14 @@ class AsyncBrowserbase(AsyncAPIClient):
# client options
api_key: str

_environment: Literal["production", "development", "local"] | NotGiven

def __init__(
self,
*,
api_key: str | None = None,
base_url: str | httpx.URL | None = None,
environment: Literal["production", "development", "local"] | NotGiven = NOT_GIVEN,
base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
Expand Down Expand Up @@ -265,10 +301,31 @@ def __init__(
)
self.api_key = api_key

if base_url is None:
base_url = os.environ.get("BROWSERBASE_BASE_URL")
if base_url is None:
base_url = f"https://api.dev.browserbase.com"
self._environment = environment

base_url_env = os.environ.get("BROWSERBASE_BASE_URL")
if is_given(base_url) and base_url is not None:
# cast required because mypy doesn't understand the type narrowing
base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast]
elif is_given(environment):
if base_url_env and base_url is not None:
raise ValueError(
"Ambiguous URL; The `BROWSERBASE_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None",
)

try:
base_url = ENVIRONMENTS[environment]
except KeyError as exc:
raise ValueError(f"Unknown environment: {environment}") from exc
elif base_url_env is not None:
base_url = base_url_env
else:
self._environment = environment = "production"

try:
base_url = ENVIRONMENTS[environment]
except KeyError as exc:
raise ValueError(f"Unknown environment: {environment}") from exc

super().__init__(
version=__version__,
Expand Down Expand Up @@ -312,6 +369,7 @@ def copy(
self,
*,
api_key: str | None = None,
environment: Literal["production", "development", "local"] | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
http_client: httpx.AsyncClient | None = None,
Expand Down Expand Up @@ -347,6 +405,7 @@ def copy(
return self.__class__(
api_key=api_key or self.api_key,
base_url=base_url or self.base_url,
environment=environment or self._environment,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
http_client=http_client,
max_retries=max_retries if is_given(max_retries) else self.max_retries,
Expand Down
2 changes: 1 addition & 1 deletion src/browserbase/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str:
def model_dump(
model: pydantic.BaseModel,
*,
exclude: IncEx = None,
exclude: IncEx | None = None,
exclude_unset: bool = False,
exclude_defaults: bool = False,
warnings: bool = True,
Expand Down
10 changes: 5 additions & 5 deletions src/browserbase/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def __str__(self) -> str:
# Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836.
@classmethod
@override
def construct(
def construct( # pyright: ignore[reportIncompatibleMethodOverride]
cls: Type[ModelT],
_fields_set: set[str] | None = None,
**values: object,
Expand Down Expand Up @@ -248,8 +248,8 @@ def model_dump(
self,
*,
mode: Literal["json", "python"] | str = "python",
include: IncEx = None,
exclude: IncEx = None,
include: IncEx | None = None,
exclude: IncEx | None = None,
by_alias: bool = False,
exclude_unset: bool = False,
exclude_defaults: bool = False,
Expand Down Expand Up @@ -303,8 +303,8 @@ def model_dump_json(
self,
*,
indent: int | None = None,
include: IncEx = None,
exclude: IncEx = None,
include: IncEx | None = None,
exclude: IncEx | None = None,
by_alias: bool = False,
exclude_unset: bool = False,
exclude_defaults: bool = False,
Expand Down
6 changes: 4 additions & 2 deletions src/browserbase/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
Optional,
Sequence,
)
from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable
from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable

import httpx
import pydantic
Expand Down Expand Up @@ -193,7 +193,9 @@ def get(self, __key: str) -> str | None: ...

# Note: copied from Pydantic
# https://github.com/pydantic/pydantic/blob/32ea570bf96e84234d2992e1ddf40ab8a565925a/pydantic/main.py#L49
IncEx: TypeAlias = "set[int] | set[str] | dict[int, Any] | dict[str, Any] | None"
IncEx: TypeAlias = Union[
Set[int], Set[str], Mapping[int, Union["IncEx", Literal[True]]], Mapping[str, Union["IncEx", Literal[True]]]
]

PostParser = Callable[[Any], Any]

Expand Down
14 changes: 8 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from __future__ import annotations

import os
import asyncio
import logging
from typing import TYPE_CHECKING, Iterator, AsyncIterator

import pytest
from pytest_asyncio import is_async_test

from browserbase import Browserbase, AsyncBrowserbase

Expand All @@ -17,11 +17,13 @@
logging.getLogger("browserbase").setLevel(logging.DEBUG)


@pytest.fixture(scope="session")
def event_loop() -> Iterator[asyncio.AbstractEventLoop]:
loop = asyncio.new_event_loop()
yield loop
loop.close()
# automatically add `pytest.mark.asyncio()` to all of our async tests
# so we don't have to add that boilerplate everywhere
def pytest_collection_modifyitems(items: list[pytest.Function]) -> None:
pytest_asyncio_tests = (item for item in items if is_async_test(item))
session_scope_marker = pytest.mark.asyncio(loop_scope="session")
for async_test in pytest_asyncio_tests:
async_test.add_marker(session_scope_marker, append=False)


base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
Expand Down
Loading