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