From 9bb3abf4b274d9e75940cc859eafd0f4f37027b8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 22:04:46 +0000 Subject: [PATCH 1/5] feat(api): api update --- .stats.yml | 6 +- api.md | 2 + .../resources/scenarios/scenarios.py | 10 +-- src/runloop_api_client/types/__init__.py | 2 + .../types/input_context_update_param.py | 67 +++++++++++++++++++ .../types/scenario_update_params.py | 8 +-- .../types/scoring_contract_update_param.py | 15 +++++ tests/api_resources/test_scenarios.py | 50 +++++++++++++- 8 files changed, 147 insertions(+), 13 deletions(-) create mode 100644 src/runloop_api_client/types/input_context_update_param.py create mode 100644 src/runloop_api_client/types/scoring_contract_update_param.py diff --git a/.stats.yml b/.stats.yml index f27792d6d..e8a6c9e17 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-ecb3d41adaf06e76fd95f11d6da77c7aa0119387a3f372e736edd1579ec2aa03.yml -openapi_spec_hash: 2671664b7d6b0107a6402746033a65ac -config_hash: c4d0f5cf7262a18f9254da07d289f3ec +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-04db114d63ee85c22cfe1523fc2422a2e19b7cb7b93cbee97936a1e1a8997a89.yml +openapi_spec_hash: c3d50a0138a3a9c35f0a02292f3788a3 +config_hash: 55cf3b0829d3d91846df7c4cfab7db0d diff --git a/api.md b/api.md index 20cfc67ff..1ec5c2e25 100644 --- a/api.md +++ b/api.md @@ -264,6 +264,7 @@ Types: ```python from runloop_api_client.types import ( InputContext, + InputContextUpdate, ScenarioCreateParameters, ScenarioEnvironment, ScenarioRunListView, @@ -272,6 +273,7 @@ from runloop_api_client.types import ( ScenarioView, ScoringContract, ScoringContractResultView, + ScoringContractUpdate, ScoringFunction, ScoringFunctionResultView, StartScenarioRunParameters, diff --git a/src/runloop_api_client/resources/scenarios/scenarios.py b/src/runloop_api_client/resources/scenarios/scenarios.py index 07511e4aa..258b8d352 100644 --- a/src/runloop_api_client/resources/scenarios/scenarios.py +++ b/src/runloop_api_client/resources/scenarios/scenarios.py @@ -46,7 +46,9 @@ from ...types.scenario_run_view import ScenarioRunView from ...types.input_context_param import InputContextParam from ...types.scoring_contract_param import ScoringContractParam +from ...types.input_context_update_param import InputContextUpdateParam from ...types.scenario_environment_param import ScenarioEnvironmentParam +from ...types.scoring_contract_update_param import ScoringContractUpdateParam __all__ = ["ScenariosResource", "AsyncScenariosResource"] @@ -186,11 +188,11 @@ def update( id: str, *, environment_parameters: Optional[ScenarioEnvironmentParam] | NotGiven = NOT_GIVEN, - input_context: Optional[InputContextParam] | NotGiven = NOT_GIVEN, + input_context: Optional[InputContextUpdateParam] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, reference_output: Optional[str] | NotGiven = NOT_GIVEN, - scoring_contract: Optional[ScoringContractParam] | NotGiven = NOT_GIVEN, + scoring_contract: Optional[ScoringContractUpdateParam] | NotGiven = NOT_GIVEN, # 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, @@ -613,11 +615,11 @@ async def update( id: str, *, environment_parameters: Optional[ScenarioEnvironmentParam] | NotGiven = NOT_GIVEN, - input_context: Optional[InputContextParam] | NotGiven = NOT_GIVEN, + input_context: Optional[InputContextUpdateParam] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, reference_output: Optional[str] | NotGiven = NOT_GIVEN, - scoring_contract: Optional[ScoringContractParam] | NotGiven = NOT_GIVEN, + scoring_contract: Optional[ScoringContractUpdateParam] | NotGiven = NOT_GIVEN, # 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, diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py index 12c611295..e21c31852 100644 --- a/src/runloop_api_client/types/__init__.py +++ b/src/runloop_api_client/types/__init__.py @@ -54,6 +54,7 @@ from .benchmark_start_run_params import BenchmarkStartRunParams as BenchmarkStartRunParams from .blueprint_build_parameters import BlueprintBuildParameters as BlueprintBuildParameters from .devbox_execute_sync_params import DevboxExecuteSyncParams as DevboxExecuteSyncParams +from .input_context_update_param import InputContextUpdateParam as InputContextUpdateParam from .repository_connection_view import RepositoryConnectionView as RepositoryConnectionView from .scenario_environment_param import ScenarioEnvironmentParam as ScenarioEnvironmentParam from .devbox_create_tunnel_params import DevboxCreateTunnelParams as DevboxCreateTunnelParams @@ -69,6 +70,7 @@ from .scoring_function_result_view import ScoringFunctionResultView as ScoringFunctionResultView from .repository_inspection_details import RepositoryInspectionDetails as RepositoryInspectionDetails from .scenario_definition_list_view import ScenarioDefinitionListView as ScenarioDefinitionListView +from .scoring_contract_update_param import ScoringContractUpdateParam as ScoringContractUpdateParam from .blueprint_build_logs_list_view import BlueprintBuildLogsListView as BlueprintBuildLogsListView from .devbox_create_ssh_key_response import DevboxCreateSSHKeyResponse as DevboxCreateSSHKeyResponse from .repository_connection_list_view import RepositoryConnectionListView as RepositoryConnectionListView diff --git a/src/runloop_api_client/types/input_context_update_param.py b/src/runloop_api_client/types/input_context_update_param.py new file mode 100644 index 000000000..90685312a --- /dev/null +++ b/src/runloop_api_client/types/input_context_update_param.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["InputContextUpdateParam", "AdditionalContext"] + + +class AdditionalContext(TypedDict, total=False): + array: Required[bool] + + big_decimal: Required[Annotated[bool, PropertyInfo(alias="bigDecimal")]] + + big_integer: Required[Annotated[bool, PropertyInfo(alias="bigInteger")]] + + binary: Required[bool] + + boolean: Required[bool] + + container_node: Required[Annotated[bool, PropertyInfo(alias="containerNode")]] + + double: Required[bool] + + empty: Required[bool] + + float: Required[bool] + + floating_point_number: Required[Annotated[bool, PropertyInfo(alias="floatingPointNumber")]] + + int: Required[bool] + + integral_number: Required[Annotated[bool, PropertyInfo(alias="integralNumber")]] + + long: Required[bool] + + missing_node: Required[Annotated[bool, PropertyInfo(alias="missingNode")]] + + null: Required[bool] + + number: Required[bool] + + object: Required[bool] + + pojo: Required[bool] + + short: Required[bool] + + textual: Required[bool] + + value_node: Required[Annotated[bool, PropertyInfo(alias="valueNode")]] + + node_type: Annotated[ + Literal["ARRAY", "BINARY", "BOOLEAN", "MISSING", "NULL", "NUMBER", "OBJECT", "POJO", "STRING"], + PropertyInfo(alias="nodeType"), + ] + + +class InputContextUpdateParam(TypedDict, total=False): + additional_context: Optional[AdditionalContext] + """Additional JSON structured input context.""" + + problem_statement: Optional[str] + """The problem statement for the Scenario.""" diff --git a/src/runloop_api_client/types/scenario_update_params.py b/src/runloop_api_client/types/scenario_update_params.py index e2be5fac1..1bc7524ca 100644 --- a/src/runloop_api_client/types/scenario_update_params.py +++ b/src/runloop_api_client/types/scenario_update_params.py @@ -5,9 +5,9 @@ from typing import Dict, Optional from typing_extensions import TypedDict -from .input_context_param import InputContextParam -from .scoring_contract_param import ScoringContractParam +from .input_context_update_param import InputContextUpdateParam from .scenario_environment_param import ScenarioEnvironmentParam +from .scoring_contract_update_param import ScoringContractUpdateParam __all__ = ["ScenarioUpdateParams"] @@ -16,7 +16,7 @@ class ScenarioUpdateParams(TypedDict, total=False): environment_parameters: Optional[ScenarioEnvironmentParam] """The Environment in which the Scenario will run.""" - input_context: Optional[InputContextParam] + input_context: Optional[InputContextUpdateParam] """The input context for the Scenario.""" metadata: Optional[Dict[str, str]] @@ -32,5 +32,5 @@ class ScenarioUpdateParams(TypedDict, total=False): apply to the environment. """ - scoring_contract: Optional[ScoringContractParam] + scoring_contract: Optional[ScoringContractUpdateParam] """The scoring contract for the Scenario.""" diff --git a/src/runloop_api_client/types/scoring_contract_update_param.py b/src/runloop_api_client/types/scoring_contract_update_param.py new file mode 100644 index 000000000..ce2c85289 --- /dev/null +++ b/src/runloop_api_client/types/scoring_contract_update_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import TypedDict + +from .scoring_function_param import ScoringFunctionParam + +__all__ = ["ScoringContractUpdateParam"] + + +class ScoringContractUpdateParam(TypedDict, total=False): + scoring_function_parameters: Optional[Iterable[ScoringFunctionParam]] + """A list of scoring functions used to evaluate the Scenario.""" diff --git a/tests/api_resources/test_scenarios.py b/tests/api_resources/test_scenarios.py index c4243a16a..9e9b8e3f6 100644 --- a/tests/api_resources/test_scenarios.py +++ b/tests/api_resources/test_scenarios.py @@ -217,8 +217,31 @@ def test_method_update_with_all_params(self, client: Runloop) -> None: "working_directory": "working_directory", }, input_context={ + "additional_context": { + "array": True, + "big_decimal": True, + "big_integer": True, + "binary": True, + "boolean": True, + "container_node": True, + "double": True, + "empty": True, + "float": True, + "floating_point_number": True, + "int": True, + "integral_number": True, + "long": True, + "missing_node": True, + "null": True, + "number": True, + "object": True, + "pojo": True, + "short": True, + "textual": True, + "value_node": True, + "node_type": "ARRAY", + }, "problem_statement": "problem_statement", - "additional_context": {}, }, metadata={"foo": "string"}, name="name", @@ -583,8 +606,31 @@ async def test_method_update_with_all_params(self, async_client: AsyncRunloop) - "working_directory": "working_directory", }, input_context={ + "additional_context": { + "array": True, + "big_decimal": True, + "big_integer": True, + "binary": True, + "boolean": True, + "container_node": True, + "double": True, + "empty": True, + "float": True, + "floating_point_number": True, + "int": True, + "integral_number": True, + "long": True, + "missing_node": True, + "null": True, + "number": True, + "object": True, + "pojo": True, + "short": True, + "textual": True, + "value_node": True, + "node_type": "ARRAY", + }, "problem_statement": "problem_statement", - "additional_context": {}, }, metadata={"foo": "string"}, name="name", From 3f896cf907ef15fc2f0d01f681844eef0bca9479 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 22:26:41 +0000 Subject: [PATCH 2/5] feat(api): api update --- .stats.yml | 4 +- .../types/input_context_update_param.py | 57 +------------------ tests/api_resources/test_scenarios.py | 50 +--------------- 3 files changed, 7 insertions(+), 104 deletions(-) diff --git a/.stats.yml b/.stats.yml index e8a6c9e17..27297fbd4 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-04db114d63ee85c22cfe1523fc2422a2e19b7cb7b93cbee97936a1e1a8997a89.yml -openapi_spec_hash: c3d50a0138a3a9c35f0a02292f3788a3 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-1d4b018cbfb409baf83725737f00789a9ddbbdbdbe12db1ac7a1fd2cf9c453f0.yml +openapi_spec_hash: 1c533f386f6d3d3802d353cc6f1df547 config_hash: 55cf3b0829d3d91846df7c4cfab7db0d diff --git a/src/runloop_api_client/types/input_context_update_param.py b/src/runloop_api_client/types/input_context_update_param.py index 90685312a..30580cf9a 100644 --- a/src/runloop_api_client/types/input_context_update_param.py +++ b/src/runloop_api_client/types/input_context_update_param.py @@ -3,64 +3,13 @@ from __future__ import annotations from typing import Optional -from typing_extensions import Literal, Required, Annotated, TypedDict +from typing_extensions import TypedDict -from .._utils import PropertyInfo - -__all__ = ["InputContextUpdateParam", "AdditionalContext"] - - -class AdditionalContext(TypedDict, total=False): - array: Required[bool] - - big_decimal: Required[Annotated[bool, PropertyInfo(alias="bigDecimal")]] - - big_integer: Required[Annotated[bool, PropertyInfo(alias="bigInteger")]] - - binary: Required[bool] - - boolean: Required[bool] - - container_node: Required[Annotated[bool, PropertyInfo(alias="containerNode")]] - - double: Required[bool] - - empty: Required[bool] - - float: Required[bool] - - floating_point_number: Required[Annotated[bool, PropertyInfo(alias="floatingPointNumber")]] - - int: Required[bool] - - integral_number: Required[Annotated[bool, PropertyInfo(alias="integralNumber")]] - - long: Required[bool] - - missing_node: Required[Annotated[bool, PropertyInfo(alias="missingNode")]] - - null: Required[bool] - - number: Required[bool] - - object: Required[bool] - - pojo: Required[bool] - - short: Required[bool] - - textual: Required[bool] - - value_node: Required[Annotated[bool, PropertyInfo(alias="valueNode")]] - - node_type: Annotated[ - Literal["ARRAY", "BINARY", "BOOLEAN", "MISSING", "NULL", "NUMBER", "OBJECT", "POJO", "STRING"], - PropertyInfo(alias="nodeType"), - ] +__all__ = ["InputContextUpdateParam"] class InputContextUpdateParam(TypedDict, total=False): - additional_context: Optional[AdditionalContext] + additional_context: Optional[object] """Additional JSON structured input context.""" problem_statement: Optional[str] diff --git a/tests/api_resources/test_scenarios.py b/tests/api_resources/test_scenarios.py index 9e9b8e3f6..170a63476 100644 --- a/tests/api_resources/test_scenarios.py +++ b/tests/api_resources/test_scenarios.py @@ -217,30 +217,7 @@ def test_method_update_with_all_params(self, client: Runloop) -> None: "working_directory": "working_directory", }, input_context={ - "additional_context": { - "array": True, - "big_decimal": True, - "big_integer": True, - "binary": True, - "boolean": True, - "container_node": True, - "double": True, - "empty": True, - "float": True, - "floating_point_number": True, - "int": True, - "integral_number": True, - "long": True, - "missing_node": True, - "null": True, - "number": True, - "object": True, - "pojo": True, - "short": True, - "textual": True, - "value_node": True, - "node_type": "ARRAY", - }, + "additional_context": {}, "problem_statement": "problem_statement", }, metadata={"foo": "string"}, @@ -606,30 +583,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncRunloop) - "working_directory": "working_directory", }, input_context={ - "additional_context": { - "array": True, - "big_decimal": True, - "big_integer": True, - "binary": True, - "boolean": True, - "container_node": True, - "double": True, - "empty": True, - "float": True, - "floating_point_number": True, - "int": True, - "integral_number": True, - "long": True, - "missing_node": True, - "null": True, - "number": True, - "object": True, - "pojo": True, - "short": True, - "textual": True, - "value_node": True, - "node_type": "ARRAY", - }, + "additional_context": {}, "problem_statement": "problem_statement", }, metadata={"foo": "string"}, From 27007c0a1500e80cc9ac93cb634021a7cfb569e6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 04:13:55 +0000 Subject: [PATCH 3/5] chore(tests): skip some failing tests on the latest python versions --- tests/test_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index 60cefa491..8f3f70514 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -194,6 +194,7 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self) -> None: options = FinalRequestOptions(method="get", url="/foo") @@ -1049,6 +1050,7 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self) -> None: options = FinalRequestOptions(method="get", url="/foo") From 382b9018d26ce11e12d7143a84b80147be63ac2e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 16:30:40 +0000 Subject: [PATCH 4/5] feat(api): api update --- .stats.yml | 4 +- api.md | 1 + .../resources/scenarios/runs.py | 108 ++++++++++++++++ tests/api_resources/scenarios/test_runs.py | 120 ++++++++++++++++++ 4 files changed, 231 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 27297fbd4..a615cde74 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 91 +configured_endpoints: 92 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-1d4b018cbfb409baf83725737f00789a9ddbbdbdbe12db1ac7a1fd2cf9c453f0.yml openapi_spec_hash: 1c533f386f6d3d3802d353cc6f1df547 -config_hash: 55cf3b0829d3d91846df7c4cfab7db0d +config_hash: 33b544375e4a932cbb2890f79a9aa040 diff --git a/api.md b/api.md index 1ec5c2e25..84d42980a 100644 --- a/api.md +++ b/api.md @@ -297,6 +297,7 @@ Methods: - client.scenarios.runs.list(\*\*params) -> SyncBenchmarkRunsCursorIDPage[ScenarioRunView] - client.scenarios.runs.cancel(id) -> ScenarioRunView - client.scenarios.runs.complete(id) -> ScenarioRunView +- client.scenarios.runs.download_logs(id) -> BinaryAPIResponse - client.scenarios.runs.score(id) -> ScenarioRunView ## Scorers diff --git a/src/runloop_api_client/resources/scenarios/runs.py b/src/runloop_api_client/resources/scenarios/runs.py index 1b277d52a..029c16c1b 100644 --- a/src/runloop_api_client/resources/scenarios/runs.py +++ b/src/runloop_api_client/resources/scenarios/runs.py @@ -9,10 +9,18 @@ from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( + BinaryAPIResponse, + AsyncBinaryAPIResponse, + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, + to_custom_raw_response_wrapper, async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_raw_response_wrapper, + async_to_custom_streamed_response_wrapper, ) from ...pagination import SyncBenchmarkRunsCursorIDPage, AsyncBenchmarkRunsCursorIDPage from ..._exceptions import RunloopError @@ -213,6 +221,48 @@ def complete( cast_to=ScenarioRunView, ) + def download_logs( + self, + id: str, + *, + # 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, + idempotency_key: str | None = None, + ) -> BinaryAPIResponse: + """ + Download a zip file containing all logs for a Scenario run from the associated + devbox. + + Args: + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "application/zip", **(extra_headers or {})} + return self._post( + f"/v1/scenarios/runs/{id}/download_logs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=BinaryAPIResponse, + ) + def score( self, id: str, @@ -583,6 +633,48 @@ async def complete( cast_to=ScenarioRunView, ) + async def download_logs( + self, + id: str, + *, + # 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, + idempotency_key: str | None = None, + ) -> AsyncBinaryAPIResponse: + """ + Download a zip file containing all logs for a Scenario run from the associated + devbox. + + Args: + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "application/zip", **(extra_headers or {})} + return await self._post( + f"/v1/scenarios/runs/{id}/download_logs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=AsyncBinaryAPIResponse, + ) + async def score( self, id: str, @@ -782,6 +874,10 @@ def __init__(self, runs: RunsResource) -> None: self.complete = to_raw_response_wrapper( runs.complete, ) + self.download_logs = to_custom_raw_response_wrapper( + runs.download_logs, + BinaryAPIResponse, + ) self.score = to_raw_response_wrapper( runs.score, ) @@ -803,6 +899,10 @@ def __init__(self, runs: AsyncRunsResource) -> None: self.complete = async_to_raw_response_wrapper( runs.complete, ) + self.download_logs = async_to_custom_raw_response_wrapper( + runs.download_logs, + AsyncBinaryAPIResponse, + ) self.score = async_to_raw_response_wrapper( runs.score, ) @@ -824,6 +924,10 @@ def __init__(self, runs: RunsResource) -> None: self.complete = to_streamed_response_wrapper( runs.complete, ) + self.download_logs = to_custom_streamed_response_wrapper( + runs.download_logs, + StreamedBinaryAPIResponse, + ) self.score = to_streamed_response_wrapper( runs.score, ) @@ -845,6 +949,10 @@ def __init__(self, runs: AsyncRunsResource) -> None: self.complete = async_to_streamed_response_wrapper( runs.complete, ) + self.download_logs = async_to_custom_streamed_response_wrapper( + runs.download_logs, + AsyncStreamedBinaryAPIResponse, + ) self.score = async_to_streamed_response_wrapper( runs.score, ) diff --git a/tests/api_resources/scenarios/test_runs.py b/tests/api_resources/scenarios/test_runs.py index 57eacb2b5..7b981e9bb 100644 --- a/tests/api_resources/scenarios/test_runs.py +++ b/tests/api_resources/scenarios/test_runs.py @@ -5,11 +5,19 @@ import os from typing import Any, cast +import httpx import pytest +from respx import MockRouter from tests.utils import assert_matches_type from runloop_api_client import Runloop, AsyncRunloop from runloop_api_client.types import ScenarioRunView +from runloop_api_client._response import ( + BinaryAPIResponse, + AsyncBinaryAPIResponse, + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, +) from runloop_api_client.pagination import SyncBenchmarkRunsCursorIDPage, AsyncBenchmarkRunsCursorIDPage base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -166,6 +174,62 @@ def test_path_params_complete(self, client: Runloop) -> None: "", ) + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_download_logs(self, client: Runloop, respx_mock: MockRouter) -> None: + respx_mock.post("/v1/scenarios/runs/id/download_logs").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + run = client.scenarios.runs.download_logs( + "id", + ) + assert run.is_closed + assert run.json() == {"foo": "bar"} + assert cast(Any, run.is_closed) is True + assert isinstance(run, BinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_raw_response_download_logs(self, client: Runloop, respx_mock: MockRouter) -> None: + respx_mock.post("/v1/scenarios/runs/id/download_logs").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + + run = client.scenarios.runs.with_raw_response.download_logs( + "id", + ) + + assert run.is_closed is True + assert run.http_request.headers.get("X-Stainless-Lang") == "python" + assert run.json() == {"foo": "bar"} + assert isinstance(run, BinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_download_logs(self, client: Runloop, respx_mock: MockRouter) -> None: + respx_mock.post("/v1/scenarios/runs/id/download_logs").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + with client.scenarios.runs.with_streaming_response.download_logs( + "id", + ) as run: + assert not run.is_closed + assert run.http_request.headers.get("X-Stainless-Lang") == "python" + + assert run.json() == {"foo": "bar"} + assert cast(Any, run.is_closed) is True + assert isinstance(run, StreamedBinaryAPIResponse) + + assert cast(Any, run.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_path_params_download_logs(self, client: Runloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.scenarios.runs.with_raw_response.download_logs( + "", + ) + @parametrize def test_method_score(self, client: Runloop) -> None: run = client.scenarios.runs.score( @@ -358,6 +422,62 @@ async def test_path_params_complete(self, async_client: AsyncRunloop) -> None: "", ) + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_download_logs(self, async_client: AsyncRunloop, respx_mock: MockRouter) -> None: + respx_mock.post("/v1/scenarios/runs/id/download_logs").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + run = await async_client.scenarios.runs.download_logs( + "id", + ) + assert run.is_closed + assert await run.json() == {"foo": "bar"} + assert cast(Any, run.is_closed) is True + assert isinstance(run, AsyncBinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_download_logs(self, async_client: AsyncRunloop, respx_mock: MockRouter) -> None: + respx_mock.post("/v1/scenarios/runs/id/download_logs").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + + run = await async_client.scenarios.runs.with_raw_response.download_logs( + "id", + ) + + assert run.is_closed is True + assert run.http_request.headers.get("X-Stainless-Lang") == "python" + assert await run.json() == {"foo": "bar"} + assert isinstance(run, AsyncBinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_download_logs(self, async_client: AsyncRunloop, respx_mock: MockRouter) -> None: + respx_mock.post("/v1/scenarios/runs/id/download_logs").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + async with async_client.scenarios.runs.with_streaming_response.download_logs( + "id", + ) as run: + assert not run.is_closed + assert run.http_request.headers.get("X-Stainless-Lang") == "python" + + assert await run.json() == {"foo": "bar"} + assert cast(Any, run.is_closed) is True + assert isinstance(run, AsyncStreamedBinaryAPIResponse) + + assert cast(Any, run.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_path_params_download_logs(self, async_client: AsyncRunloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.scenarios.runs.with_raw_response.download_logs( + "", + ) + @parametrize async def test_method_score(self, async_client: AsyncRunloop) -> None: run = await async_client.scenarios.runs.score( From 1b7350db731870ca8ada027b6f30a9ea37a5c1bb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 16:31:00 +0000 Subject: [PATCH 5/5] release: 0.45.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/runloop_api_client/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cc51f6f8e..fc0d7ff8b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.44.0" + ".": "0.45.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f0586117..5eb9442bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 0.45.0 (2025-06-24) + +Full Changelog: [v0.44.0...v0.45.0](https://github.com/runloopai/api-client-python/compare/v0.44.0...v0.45.0) + +### Features + +* **api:** api update ([382b901](https://github.com/runloopai/api-client-python/commit/382b9018d26ce11e12d7143a84b80147be63ac2e)) +* **api:** api update ([3f896cf](https://github.com/runloopai/api-client-python/commit/3f896cf907ef15fc2f0d01f681844eef0bca9479)) +* **api:** api update ([9bb3abf](https://github.com/runloopai/api-client-python/commit/9bb3abf4b274d9e75940cc859eafd0f4f37027b8)) + + +### Chores + +* **tests:** skip some failing tests on the latest python versions ([27007c0](https://github.com/runloopai/api-client-python/commit/27007c0a1500e80cc9ac93cb634021a7cfb569e6)) + ## 0.44.0 (2025-06-21) Full Changelog: [v0.43.0...v0.44.0](https://github.com/runloopai/api-client-python/compare/v0.43.0...v0.44.0) diff --git a/pyproject.toml b/pyproject.toml index 541db7250..33cf23a70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "runloop_api_client" -version = "0.44.0" +version = "0.45.0" description = "The official Python library for the runloop API" dynamic = ["readme"] license = "MIT" diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py index 9382b6e18..0bac585fa 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.44.0" # x-release-please-version +__version__ = "0.45.0" # x-release-please-version