diff --git a/.fernignore b/.fernignore index e666a72..3eb9c64 100644 --- a/.fernignore +++ b/.fernignore @@ -2,6 +2,7 @@ src/scrapybara/client.py src/scrapybara/anthropic/ +src/scrapybara/herd/ src/scrapybara/prompts/ src/scrapybara/tools/ src/scrapybara/types/__init__.py diff --git a/poetry.lock b/poetry.lock index 8c03bbe..3ce8f58 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "annotated-types" @@ -16,13 +16,13 @@ typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} [[package]] name = "anthropic" -version = "0.39.0" +version = "0.47.2" description = "The official Python library for the anthropic API" optional = false python-versions = ">=3.8" files = [ - {file = "anthropic-0.39.0-py3-none-any.whl", hash = "sha256:ea17093ae0ce0e1768b0c46501d6086b5bcd74ff39d68cd2d6396374e9de7c09"}, - {file = "anthropic-0.39.0.tar.gz", hash = "sha256:94671cc80765f9ce693f76d63a97ee9bef4c2d6063c044e983d21a2e262f63ba"}, + {file = "anthropic-0.47.2-py3-none-any.whl", hash = "sha256:61b712a56308fce69f04d92ba0230ab2bc187b5bce17811d400843a8976bb67f"}, + {file = "anthropic-0.47.2.tar.gz", hash = "sha256:452f4ca0c56ffab8b6ce9928bf8470650f88106a7001b250895eb65c54cfa44c"}, ] [package.dependencies] @@ -32,7 +32,7 @@ httpx = ">=0.23.0,<1" jiter = ">=0.4.0,<1" pydantic = ">=1.9.0,<3" sniffio = "*" -typing-extensions = ">=4.7,<5" +typing-extensions = ">=4.10,<5" [package.extras] bedrock = ["boto3 (>=1.28.57)", "botocore (>=1.31.57)"] @@ -62,13 +62,13 @@ trio = ["trio (>=0.26.1)"] [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, - {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, ] [[package]] @@ -780,4 +780,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "8130552a641501ad4537fd8c8eec56deb0b111b9da63b6460974bd12d106ff5a" +content-hash = "afd12abd07590f9897eb7e414c5b497477e5fd6e31bfb4390cac050339717ccd" diff --git a/pyproject.toml b/pyproject.toml index d33b015..32fd0ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "scrapybara" -version = "2.2.7" +version = "2.2.8" description = "" readme = "README.md" authors = [] @@ -32,7 +32,7 @@ Repository = 'https://github.com/scrapybara/scrapybara-python' [tool.poetry.dependencies] python = "^3.8" -anthropic = "^0.39.0" +anthropic = "^0.47.2" httpx = ">=0.21.2" playwright = "^1.48.0" pydantic = ">= 1.9.2" diff --git a/reference.md b/reference.md index a16e22d..a8e0763 100644 --- a/reference.md +++ b/reference.md @@ -938,6 +938,79 @@ client.browser.save_auth( + + + + +
client.browser.modify_auth(...) +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from scrapybara import Scrapybara + +client = Scrapybara( + api_key="YOUR_API_KEY", +) +client.browser.modify_auth( + instance_id="instance_id", + auth_state_id="auth_state_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**instance_id:** `str` + +
+
+ +
+
+ +**auth_state_id:** `str` + +
+
+ +
+
+ +**name:** `typing.Optional[str]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/src/scrapybara/__init__.py b/src/scrapybara/__init__.py index 4921e4c..5311076 100644 --- a/src/scrapybara/__init__.py +++ b/src/scrapybara/__init__.py @@ -17,6 +17,7 @@ InstanceGetStreamUrlResponse, InstanceScreenshotResponse, KernelInfo, + ModifyBrowserAuthResponse, Notebook, NotebookCell, SaveBrowserAuthResponse, @@ -54,6 +55,7 @@ "InstanceGetStreamUrlResponse", "InstanceScreenshotResponse", "KernelInfo", + "ModifyBrowserAuthResponse", "Notebook", "NotebookCell", "SaveBrowserAuthResponse", diff --git a/src/scrapybara/anthropic/__init__.py b/src/scrapybara/anthropic/__init__.py index f5cdafd..847c4d1 100644 --- a/src/scrapybara/anthropic/__init__.py +++ b/src/scrapybara/anthropic/__init__.py @@ -17,10 +17,12 @@ class Anthropic(Model): """Model adapter for Anthropic. Supported models: + - claude-3-7-sonnet-20250219 (1x agent credit if no api_key) + - claude-3-7-sonnet-20250219-thinking (1x agent credit if no api_key) - claude-3-5-sonnet-20241022 (1x agent credit if no api_key) Args: - name: Anthropic model name, defaults to "claude-3-5-sonnet-20241022" + name: Anthropic model name, defaults to "claude-3-7-sonnet-20250219" api_key: Optional Anthropic API key Returns: @@ -31,7 +33,7 @@ class Anthropic(Model): def __init__( self, - name: Optional[str] = "claude-3-5-sonnet-20241022", + name: Optional[str] = "claude-3-7-sonnet-20250219", api_key: Optional[str] = None, ) -> None: super().__init__(provider="anthropic", name=name, api_key=api_key) diff --git a/src/scrapybara/browser/client.py b/src/scrapybara/browser/client.py index a77c77f..b9add3c 100644 --- a/src/scrapybara/browser/client.py +++ b/src/scrapybara/browser/client.py @@ -12,6 +12,7 @@ from ..core.api_error import ApiError from ..types.browser_get_cdp_url_response import BrowserGetCdpUrlResponse from ..types.save_browser_auth_response import SaveBrowserAuthResponse +from ..types.modify_browser_auth_response import ModifyBrowserAuthResponse from ..types.browser_authenticate_response import BrowserAuthenticateResponse from ..types.stop_browser_response import StopBrowserResponse from ..core.client_wrapper import AsyncClientWrapper @@ -198,6 +199,76 @@ def save_auth( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def modify_auth( + self, + instance_id: str, + *, + auth_state_id: str, + name: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ModifyBrowserAuthResponse: + """ + Parameters + ---------- + instance_id : str + + auth_state_id : str + + name : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ModifyBrowserAuthResponse + Successful Response + + Examples + -------- + from scrapybara import Scrapybara + + client = Scrapybara( + api_key="YOUR_API_KEY", + ) + client.browser.modify_auth( + instance_id="instance_id", + auth_state_id="auth_state_id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"v1/instance/{jsonable_encoder(instance_id)}/browser/modify_auth", + method="POST", + params={ + "auth_state_id": auth_state_id, + "name": name, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + ModifyBrowserAuthResponse, + parse_obj_as( + type_=ModifyBrowserAuthResponse, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def authenticate( self, instance_id: str, *, auth_state_id: str, request_options: typing.Optional[RequestOptions] = None ) -> BrowserAuthenticateResponse: @@ -520,6 +591,84 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def modify_auth( + self, + instance_id: str, + *, + auth_state_id: str, + name: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ModifyBrowserAuthResponse: + """ + Parameters + ---------- + instance_id : str + + auth_state_id : str + + name : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ModifyBrowserAuthResponse + Successful Response + + Examples + -------- + import asyncio + + from scrapybara import AsyncScrapybara + + client = AsyncScrapybara( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.browser.modify_auth( + instance_id="instance_id", + auth_state_id="auth_state_id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"v1/instance/{jsonable_encoder(instance_id)}/browser/modify_auth", + method="POST", + params={ + "auth_state_id": auth_state_id, + "name": name, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + ModifyBrowserAuthResponse, + parse_obj_as( + type_=ModifyBrowserAuthResponse, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def authenticate( self, instance_id: str, *, auth_state_id: str, request_options: typing.Optional[RequestOptions] = None ) -> BrowserAuthenticateResponse: diff --git a/src/scrapybara/client.py b/src/scrapybara/client.py index 5b277aa..78f6bc8 100644 --- a/src/scrapybara/client.py +++ b/src/scrapybara/client.py @@ -43,6 +43,7 @@ StartBrowserResponse, StopBrowserResponse, StopInstanceResponse, + ModifyBrowserAuthResponse, ) from .types.act import ( SingleActRequest, @@ -116,6 +117,20 @@ def save_auth( self.instance_id, name=name, request_options=request_options ) + def modify_auth( + self, + *, + auth_state_id: str, + name: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> ModifyBrowserAuthResponse: + return self._client.browser.modify_auth( + self.instance_id, + auth_state_id=auth_state_id, + name=name, + request_options=request_options, + ) + def authenticate( self, *, auth_state_id: str, request_options: Optional[RequestOptions] = None ) -> BrowserAuthenticateResponse: @@ -162,6 +177,20 @@ async def save_auth( self.instance_id, name=name, request_options=request_options ) + async def modify_auth( + self, + *, + auth_state_id: str, + name: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> ModifyBrowserAuthResponse: + return await self._client.browser.modify_auth( + self.instance_id, + auth_state_id=auth_state_id, + name=name, + request_options=request_options, + ) + async def authenticate( self, *, auth_state_id: str, request_options: Optional[RequestOptions] = None ) -> BrowserAuthenticateResponse: @@ -715,6 +744,20 @@ def save_auth( self.id, name=name, request_options=request_options ) + def modify_auth( + self, + *, + auth_state_id: str, + name: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> ModifyBrowserAuthResponse: + return self._client.browser.modify_auth( + self.id, + auth_state_id=auth_state_id, + name=name, + request_options=request_options, + ) + def authenticate( self, *, auth_state_id: str, request_options: Optional[RequestOptions] = None ) -> BrowserAuthenticateResponse: @@ -884,6 +927,20 @@ async def save_auth( self.id, name=name, request_options=request_options ) + async def modify_auth( + self, + *, + auth_state_id: str, + name: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> ModifyBrowserAuthResponse: + return await self._client.browser.modify_auth( + self.id, + auth_state_id=auth_state_id, + name=name, + request_options=request_options, + ) + async def authenticate( self, *, auth_state_id: str, request_options: Optional[RequestOptions] = None ) -> BrowserAuthenticateResponse: diff --git a/src/scrapybara/core/client_wrapper.py b/src/scrapybara/core/client_wrapper.py index ea31b0e..4cdb0d2 100644 --- a/src/scrapybara/core/client_wrapper.py +++ b/src/scrapybara/core/client_wrapper.py @@ -7,9 +7,7 @@ class BaseClientWrapper: - def __init__( - self, *, api_key: str, base_url: str, timeout: typing.Optional[float] = None - ): + def __init__(self, *, api_key: str, base_url: str, timeout: typing.Optional[float] = None): self.api_key = api_key self._base_url = base_url self._timeout = timeout @@ -18,7 +16,7 @@ def get_headers(self) -> typing.Dict[str, str]: headers: typing.Dict[str, str] = { "X-Fern-Language": "Python", "X-Fern-SDK-Name": "scrapybara", - "X-Fern-SDK-Version": "2.2.7", + "X-Fern-SDK-Version": "2.2.8", } headers["x-api-key"] = self.api_key return headers @@ -32,12 +30,7 @@ def get_timeout(self) -> typing.Optional[float]: class SyncClientWrapper(BaseClientWrapper): def __init__( - self, - *, - api_key: str, - base_url: str, - timeout: typing.Optional[float] = None, - httpx_client: httpx.Client + self, *, api_key: str, base_url: str, timeout: typing.Optional[float] = None, httpx_client: httpx.Client ): super().__init__(api_key=api_key, base_url=base_url, timeout=timeout) self.httpx_client = HttpClient( @@ -50,12 +43,7 @@ def __init__( class AsyncClientWrapper(BaseClientWrapper): def __init__( - self, - *, - api_key: str, - base_url: str, - timeout: typing.Optional[float] = None, - httpx_client: httpx.AsyncClient + self, *, api_key: str, base_url: str, timeout: typing.Optional[float] = None, httpx_client: httpx.AsyncClient ): super().__init__(api_key=api_key, base_url=base_url, timeout=timeout) self.httpx_client = AsyncHttpClient( diff --git a/src/scrapybara/types/__init__.py b/src/scrapybara/types/__init__.py index 5c318a2..59c6a92 100644 --- a/src/scrapybara/types/__init__.py +++ b/src/scrapybara/types/__init__.py @@ -14,6 +14,7 @@ from .instance_get_stream_url_response import InstanceGetStreamUrlResponse from .instance_screenshot_response import InstanceScreenshotResponse from .kernel_info import KernelInfo +from .modify_browser_auth_response import ModifyBrowserAuthResponse from .notebook import Notebook from .notebook_cell import NotebookCell from .save_browser_auth_response import SaveBrowserAuthResponse @@ -58,6 +59,7 @@ "InstanceGetStreamUrlResponse", "InstanceScreenshotResponse", "KernelInfo", + "ModifyBrowserAuthResponse", "Notebook", "NotebookCell", "SaveBrowserAuthResponse", diff --git a/src/scrapybara/types/modify_browser_auth_response.py b/src/scrapybara/types/modify_browser_auth_response.py new file mode 100644 index 0000000..84fec1d --- /dev/null +++ b/src/scrapybara/types/modify_browser_auth_response.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class ModifyBrowserAuthResponse(UniversalBaseModel): + status: str + auth_state_id: str + name: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/scrapybara/types/status.py b/src/scrapybara/types/status.py index bdef0a9..3fb3c9c 100644 --- a/src/scrapybara/types/status.py +++ b/src/scrapybara/types/status.py @@ -2,4 +2,4 @@ import typing -Status = typing.Union[typing.Literal["deploying", "running", "terminated", "error"], typing.Any] +Status = typing.Union[typing.Literal["deploying", "running", "paused", "terminated", "error"], typing.Any]