diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 52afe059d..fe87cd917 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.42.0" + ".": "0.43.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 9af3f652d..2595a1785 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 91 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-86c4a065b1820b26c7afd40bbc374e87dd5e9c2ffa0abad26e7fd4ebec1cf72b.yml -openapi_spec_hash: 6c6a44548177464033f3c174c89ba06a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-32aaecf1da425c37d534ed04df36003ab9d766a7755cd18f96541929a2a3ea59.yml +openapi_spec_hash: e326c47b99943cbbab473fde3b257221 config_hash: 421e8d0e71c7ef71fdfebede08ea7271 diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b4780ff..8cf3b9669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 0.43.0 (2025-06-14) + +Full Changelog: [v0.42.0...v0.43.0](https://github.com/runloopai/api-client-python/compare/v0.42.0...v0.43.0) + +### Features + +* **api:** api update ([695b29a](https://github.com/runloopai/api-client-python/commit/695b29a97bd2dcad3f90ee377ed0475a84ed1a2b)) + + +### Bug Fixes + +* **client:** correctly parse binary response | stream ([1d815d4](https://github.com/runloopai/api-client-python/commit/1d815d468db24142174fe536c05dcd3ec49ee03f)) + + +### Chores + +* **tests:** run tests in parallel ([fdeee42](https://github.com/runloopai/api-client-python/commit/fdeee42440c5df55af77cbad2423c7faab473dcd)) + ## 0.42.0 (2025-06-11) Full Changelog: [v0.41.0...v0.42.0](https://github.com/runloopai/api-client-python/compare/v0.41.0...v0.42.0) diff --git a/pyproject.toml b/pyproject.toml index c61d56fec..1f083a49a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "runloop_api_client" -version = "0.42.0" +version = "0.43.0" description = "The official Python library for the runloop API" dynamic = ["readme"] license = "MIT" @@ -54,6 +54,7 @@ dev-dependencies = [ "importlib-metadata>=6.7.0", "rich>=13.7.1", "nest_asyncio==1.6.0", + "pytest-xdist>=3.6.1", ] [tool.rye.scripts] @@ -125,7 +126,7 @@ replacement = '[\1](https://github.com/runloopai/api-client-python/tree/main/\g< [tool.pytest.ini_options] testpaths = ["tests"] -addopts = "--tb=short" +addopts = "--tb=short -n auto" xfail_strict = true asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "session" diff --git a/requirements-dev.lock b/requirements-dev.lock index 1e04b71cc..3063be477 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -30,6 +30,8 @@ distro==1.8.0 exceptiongroup==1.2.2 # via anyio # via pytest +execnet==2.1.1 + # via pytest-xdist filelock==3.12.4 # via virtualenv h11==0.14.0 @@ -72,7 +74,9 @@ pygments==2.18.0 pyright==1.1.399 pytest==8.3.3 # via pytest-asyncio + # via pytest-xdist pytest-asyncio==0.24.0 +pytest-xdist==3.7.0 python-dateutil==2.8.2 # via time-machine pytz==2023.3.post1 diff --git a/src/runloop_api_client/_base_client.py b/src/runloop_api_client/_base_client.py index bba4933f3..74864535d 100644 --- a/src/runloop_api_client/_base_client.py +++ b/src/runloop_api_client/_base_client.py @@ -1071,7 +1071,14 @@ def _process_response( ) -> ResponseT: origin = get_origin(cast_to) or cast_to - if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): if not issubclass(origin, APIResponse): raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") @@ -1574,7 +1581,14 @@ async def _process_response( ) -> ResponseT: origin = get_origin(cast_to) or cast_to - if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): if not issubclass(origin, AsyncAPIResponse): raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py index dee24268d..5c05630c7 100644 --- a/src/runloop_api_client/_version.py +++ b/src/runloop_api_client/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "runloop_api_client" -__version__ = "0.42.0" # x-release-please-version +__version__ = "0.43.0" # x-release-please-version diff --git a/src/runloop_api_client/resources/scenarios/scenarios.py b/src/runloop_api_client/resources/scenarios/scenarios.py index 2ff71e2c8..f86bc4873 100644 --- a/src/runloop_api_client/resources/scenarios/scenarios.py +++ b/src/runloop_api_client/resources/scenarios/scenarios.py @@ -256,6 +256,7 @@ def update( def list( self, *, + benchmark_id: str | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, starting_after: str | NotGiven = NOT_GIVEN, @@ -266,12 +267,13 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncScenariosCursorIDPage[ScenarioView]: - """List all Scenarios matching filter. + """ + List all Scenarios matching filter. Args: - limit: The limit of items to return. + benchmark_id: Filter scenarios by benchmark ID. - Default is 20. + limit: The limit of items to return. Default is 20. name: Query for Scenarios with a given name. @@ -295,6 +297,7 @@ def list( timeout=timeout, query=maybe_transform( { + "benchmark_id": benchmark_id, "limit": limit, "name": name, "starting_after": starting_after, @@ -679,6 +682,7 @@ async def update( def list( self, *, + benchmark_id: str | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, starting_after: str | NotGiven = NOT_GIVEN, @@ -689,12 +693,13 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[ScenarioView, AsyncScenariosCursorIDPage[ScenarioView]]: - """List all Scenarios matching filter. + """ + List all Scenarios matching filter. Args: - limit: The limit of items to return. + benchmark_id: Filter scenarios by benchmark ID. - Default is 20. + limit: The limit of items to return. Default is 20. name: Query for Scenarios with a given name. @@ -718,6 +723,7 @@ def list( timeout=timeout, query=maybe_transform( { + "benchmark_id": benchmark_id, "limit": limit, "name": name, "starting_after": starting_after, diff --git a/src/runloop_api_client/types/scenario_list_params.py b/src/runloop_api_client/types/scenario_list_params.py index 26c4b5c79..917da6c94 100644 --- a/src/runloop_api_client/types/scenario_list_params.py +++ b/src/runloop_api_client/types/scenario_list_params.py @@ -8,6 +8,9 @@ class ScenarioListParams(TypedDict, total=False): + benchmark_id: str + """Filter scenarios by benchmark ID.""" + limit: int """The limit of items to return. Default is 20.""" diff --git a/tests/api_resources/test_scenarios.py b/tests/api_resources/test_scenarios.py index 7e77cd2a9..a7fa7a293 100644 --- a/tests/api_resources/test_scenarios.py +++ b/tests/api_resources/test_scenarios.py @@ -339,6 +339,7 @@ def test_method_list(self, client: Runloop) -> None: @parametrize def test_method_list_with_all_params(self, client: Runloop) -> None: scenario = client.scenarios.list( + benchmark_id="benchmark_id", limit=0, name="name", starting_after="starting_after", @@ -762,6 +763,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: scenario = await async_client.scenarios.list( + benchmark_id="benchmark_id", limit=0, name="name", starting_after="starting_after",