diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index cb9d2541c..7f3f5c846 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.22.0"
+ ".": "0.23.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index abd9dea86..fb43e6c65 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,2 +1,2 @@
-configured_endpoints: 73
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-a6925284ea7678ed801dbaf6b5c60676b97ab7a16191c1b2ff8ef6a468e89f3b.yml
+configured_endpoints: 77
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-a3d91c690527ff6a9040ade46943ba56916987f1f7d1fb45a9974546770ffe97.yml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3108be617..835e12e70 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,26 @@
# Changelog
+## 0.23.0 (2025-02-11)
+
+Full Changelog: [v0.22.0...v0.23.0](https://github.com/runloopai/api-client-python/compare/v0.22.0...v0.23.0)
+
+### Features
+
+* **api:** api update ([#540](https://github.com/runloopai/api-client-python/issues/540)) ([0f1ce28](https://github.com/runloopai/api-client-python/commit/0f1ce28e9137b52265666131ee0c7561e92cefd9))
+* **client:** send `X-Stainless-Read-Timeout` header ([#536](https://github.com/runloopai/api-client-python/issues/536)) ([996da95](https://github.com/runloopai/api-client-python/commit/996da9596984be3962a3f1f81753c0617676988b))
+
+
+### Bug Fixes
+
+* **api:** remove recursive model for now ([#534](https://github.com/runloopai/api-client-python/issues/534)) ([748bd3d](https://github.com/runloopai/api-client-python/commit/748bd3d1c63587094c0c7ead108d670740052a0b))
+
+
+### Chores
+
+* **internal:** codegen related update ([#538](https://github.com/runloopai/api-client-python/issues/538)) ([b538d7c](https://github.com/runloopai/api-client-python/commit/b538d7c842fa8f2ebf5bcb61b9184d9171283c36))
+* **internal:** fix type traversing dictionary params ([#537](https://github.com/runloopai/api-client-python/issues/537)) ([d6afc80](https://github.com/runloopai/api-client-python/commit/d6afc8051e30d19b2f011f3bcad85676d96c4a48))
+* **internal:** minor type handling changes ([#539](https://github.com/runloopai/api-client-python/issues/539)) ([ecd4acd](https://github.com/runloopai/api-client-python/commit/ecd4acd4ac7e950ad5757a4f6adc08971135f7d3))
+
## 0.22.0 (2025-02-04)
Full Changelog: [v0.21.0...v0.22.0](https://github.com/runloopai/api-client-python/compare/v0.21.0...v0.22.0)
diff --git a/README.md b/README.md
index 5954a9576..f480d2d45 100644
--- a/README.md
+++ b/README.md
@@ -179,7 +179,7 @@ Error codes are as follows:
### Retries
-Certain errors are automatically retried 2 times by default, with a short exponential backoff.
+Certain errors are automatically retried 0 times by default, with a short exponential backoff.
Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
429 Rate Limit, and >=500 Internal errors are all retried by default.
diff --git a/api.md b/api.md
index 0595502d6..90c237349 100644
--- a/api.md
+++ b/api.md
@@ -83,6 +83,7 @@ Methods:
- client.devboxes.create(\*\*params) -> DevboxView
- client.devboxes.retrieve(id) -> DevboxView
+- client.devboxes.update(id, \*\*params) -> DevboxView
- client.devboxes.list(\*\*params) -> SyncDevboxesCursorIDPage[DevboxView]
- client.devboxes.create_ssh_key(id) -> DevboxCreateSSHKeyResponse
- client.devboxes.create_tunnel(id, \*\*params) -> DevboxTunnelView
@@ -101,6 +102,20 @@ Methods:
- client.devboxes.upload_file(id, \*\*params) -> object
- client.devboxes.write_file_contents(id, \*\*params) -> DevboxExecutionDetailView
+## DiskSnapshots
+
+Types:
+
+```python
+from runloop_api_client.types.devboxes import DiskSnapshotDeleteResponse
+```
+
+Methods:
+
+- client.devboxes.disk_snapshots.update(id, \*\*params) -> DevboxSnapshotView
+- client.devboxes.disk_snapshots.list(\*\*params) -> SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView]
+- client.devboxes.disk_snapshots.delete(id) -> object
+
## Browsers
Types:
@@ -112,6 +127,7 @@ from runloop_api_client.types.devboxes import BrowserView
Methods:
- client.devboxes.browsers.create(\*\*params) -> BrowserView
+- client.devboxes.browsers.retrieve(id) -> BrowserView
## Computers
@@ -129,6 +145,7 @@ from runloop_api_client.types.devboxes import (
Methods:
- client.devboxes.computers.create(\*\*params) -> ComputerView
+- client.devboxes.computers.retrieve(id) -> ComputerView
- client.devboxes.computers.keyboard_interaction(id, \*\*params) -> ComputerKeyboardInteractionResponse
- client.devboxes.computers.mouse_interaction(id, \*\*params) -> ComputerMouseInteractionResponse
- client.devboxes.computers.screen_interaction(id, \*\*params) -> ComputerScreenInteractionResponse
diff --git a/pyproject.toml b/pyproject.toml
index 369c275c3..0d8b9c708 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "runloop_api_client"
-version = "0.22.0"
+version = "0.23.0"
description = "The official Python library for the runloop API"
dynamic = ["readme"]
license = "MIT"
diff --git a/src/runloop_api_client/_base_client.py b/src/runloop_api_client/_base_client.py
index bd82fb93e..22e3bc630 100644
--- a/src/runloop_api_client/_base_client.py
+++ b/src/runloop_api_client/_base_client.py
@@ -418,10 +418,17 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0
if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers:
headers[idempotency_header] = options.idempotency_key or self._idempotency_key()
- # Don't set the retry count header if it was already set or removed by the caller. We check
+ # Don't set these headers if they were already set or removed by the caller. We check
# `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case.
- if "x-stainless-retry-count" not in (header.lower() for header in custom_headers):
+ lower_custom_headers = [header.lower() for header in custom_headers]
+ if "x-stainless-retry-count" not in lower_custom_headers:
headers["x-stainless-retry-count"] = str(retries_taken)
+ if "x-stainless-read-timeout" not in lower_custom_headers:
+ timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout
+ if isinstance(timeout, Timeout):
+ timeout = timeout.read
+ if timeout is not None:
+ headers["x-stainless-read-timeout"] = str(timeout)
return headers
diff --git a/src/runloop_api_client/_constants.py b/src/runloop_api_client/_constants.py
index 6ddf2c717..6be0f5e04 100644
--- a/src/runloop_api_client/_constants.py
+++ b/src/runloop_api_client/_constants.py
@@ -7,8 +7,8 @@
# default timeout is 1 minute
DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0)
-DEFAULT_MAX_RETRIES = 2
+DEFAULT_MAX_RETRIES = 0
DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20)
-INITIAL_RETRY_DELAY = 0.5
-MAX_RETRY_DELAY = 8.0
+INITIAL_RETRY_DELAY = 1.0
+MAX_RETRY_DELAY = 10.0
diff --git a/src/runloop_api_client/_models.py b/src/runloop_api_client/_models.py
index 12c34b7d1..c4401ff86 100644
--- a/src/runloop_api_client/_models.py
+++ b/src/runloop_api_client/_models.py
@@ -426,10 +426,16 @@ def construct_type(*, value: object, type_: object) -> object:
If the given value does not match the expected type then it is returned as-is.
"""
+
+ # store a reference to the original type we were given before we extract any inner
+ # types so that we can properly resolve forward references in `TypeAliasType` annotations
+ original_type = None
+
# we allow `object` as the input type because otherwise, passing things like
# `Literal['value']` will be reported as a type error by type checkers
type_ = cast("type[object]", type_)
if is_type_alias_type(type_):
+ original_type = type_ # type: ignore[unreachable]
type_ = type_.__value__ # type: ignore[unreachable]
# unwrap `Annotated[T, ...]` -> `T`
@@ -446,7 +452,7 @@ def construct_type(*, value: object, type_: object) -> object:
if is_union(origin):
try:
- return validate_type(type_=cast("type[object]", type_), value=value)
+ return validate_type(type_=cast("type[object]", original_type or type_), value=value)
except Exception:
pass
diff --git a/src/runloop_api_client/_utils/_transform.py b/src/runloop_api_client/_utils/_transform.py
index a6b62cad0..18afd9d8b 100644
--- a/src/runloop_api_client/_utils/_transform.py
+++ b/src/runloop_api_client/_utils/_transform.py
@@ -25,7 +25,7 @@
is_annotated_type,
strip_annotated_type,
)
-from .._compat import model_dump, is_typeddict
+from .._compat import get_origin, model_dump, is_typeddict
_T = TypeVar("_T")
@@ -164,9 +164,14 @@ def _transform_recursive(
inner_type = annotation
stripped_type = strip_annotated_type(inner_type)
+ origin = get_origin(stripped_type) or stripped_type
if is_typeddict(stripped_type) and is_mapping(data):
return _transform_typeddict(data, stripped_type)
+ if origin == dict and is_mapping(data):
+ items_type = get_args(stripped_type)[1]
+ return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
+
if (
# List[T]
(is_list_type(stripped_type) and is_list(data))
@@ -307,9 +312,14 @@ async def _async_transform_recursive(
inner_type = annotation
stripped_type = strip_annotated_type(inner_type)
+ origin = get_origin(stripped_type) or stripped_type
if is_typeddict(stripped_type) and is_mapping(data):
return await _async_transform_typeddict(data, stripped_type)
+ if origin == dict and is_mapping(data):
+ items_type = get_args(stripped_type)[1]
+ return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
+
if (
# List[T]
(is_list_type(stripped_type) and is_list(data))
diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py
index c524a520b..83f733319 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.22.0" # x-release-please-version
+__version__ = "0.23.0" # x-release-please-version
diff --git a/src/runloop_api_client/pagination.py b/src/runloop_api_client/pagination.py
index f2b918ab9..eb5d5653f 100644
--- a/src/runloop_api_client/pagination.py
+++ b/src/runloop_api_client/pagination.py
@@ -86,6 +86,14 @@ def _get_page_items(self) -> List[_T]:
return []
return blueprints
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
blueprints = self.blueprints
@@ -112,6 +120,14 @@ def _get_page_items(self) -> List[_T]:
return []
return blueprints
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
blueprints = self.blueprints
@@ -138,6 +154,14 @@ def _get_page_items(self) -> List[_T]:
return []
return devboxes
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
devboxes = self.devboxes
@@ -164,6 +188,14 @@ def _get_page_items(self) -> List[_T]:
return []
return devboxes
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
devboxes = self.devboxes
@@ -190,6 +222,14 @@ def _get_page_items(self) -> List[_T]:
return []
return repositories
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
repositories = self.repositories
@@ -216,6 +256,14 @@ def _get_page_items(self) -> List[_T]:
return []
return repositories
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
repositories = self.repositories
@@ -242,6 +290,14 @@ def _get_page_items(self) -> List[_T]:
return []
return snapshots
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
snapshots = self.snapshots
@@ -268,6 +324,14 @@ def _get_page_items(self) -> List[_T]:
return []
return snapshots
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
snapshots = self.snapshots
@@ -294,6 +358,14 @@ def _get_page_items(self) -> List[_T]:
return []
return benchmarks
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
benchmarks = self.benchmarks
@@ -320,6 +392,14 @@ def _get_page_items(self) -> List[_T]:
return []
return benchmarks
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
benchmarks = self.benchmarks
@@ -346,6 +426,14 @@ def _get_page_items(self) -> List[_T]:
return []
return runs
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
runs = self.runs
@@ -372,6 +460,14 @@ def _get_page_items(self) -> List[_T]:
return []
return runs
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
runs = self.runs
@@ -398,6 +494,14 @@ def _get_page_items(self) -> List[_T]:
return []
return scenarios
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
scenarios = self.scenarios
@@ -424,6 +528,14 @@ def _get_page_items(self) -> List[_T]:
return []
return scenarios
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
scenarios = self.scenarios
@@ -450,6 +562,14 @@ def _get_page_items(self) -> List[_T]:
return []
return runs
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
runs = self.runs
@@ -476,6 +596,14 @@ def _get_page_items(self) -> List[_T]:
return []
return runs
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
runs = self.runs
@@ -502,6 +630,14 @@ def _get_page_items(self) -> List[_T]:
return []
return scorers
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
scorers = self.scorers
@@ -528,6 +664,14 @@ def _get_page_items(self) -> List[_T]:
return []
return scorers
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
@override
def next_page_info(self) -> Optional[PageInfo]:
scorers = self.scorers
diff --git a/src/runloop_api_client/resources/devboxes/__init__.py b/src/runloop_api_client/resources/devboxes/__init__.py
index 1e09245d6..acdd589f0 100644
--- a/src/runloop_api_client/resources/devboxes/__init__.py
+++ b/src/runloop_api_client/resources/devboxes/__init__.py
@@ -48,8 +48,22 @@
ExecutionsResourceWithStreamingResponse,
AsyncExecutionsResourceWithStreamingResponse,
)
+from .disk_snapshots import (
+ DiskSnapshotsResource,
+ AsyncDiskSnapshotsResource,
+ DiskSnapshotsResourceWithRawResponse,
+ AsyncDiskSnapshotsResourceWithRawResponse,
+ DiskSnapshotsResourceWithStreamingResponse,
+ AsyncDiskSnapshotsResourceWithStreamingResponse,
+)
__all__ = [
+ "DiskSnapshotsResource",
+ "AsyncDiskSnapshotsResource",
+ "DiskSnapshotsResourceWithRawResponse",
+ "AsyncDiskSnapshotsResourceWithRawResponse",
+ "DiskSnapshotsResourceWithStreamingResponse",
+ "AsyncDiskSnapshotsResourceWithStreamingResponse",
"BrowsersResource",
"AsyncBrowsersResource",
"BrowsersResourceWithRawResponse",
diff --git a/src/runloop_api_client/resources/devboxes/browsers.py b/src/runloop_api_client/resources/devboxes/browsers.py
index d59cbcd4d..799b58aef 100644
--- a/src/runloop_api_client/resources/devboxes/browsers.py
+++ b/src/runloop_api_client/resources/devboxes/browsers.py
@@ -90,6 +90,39 @@ def create(
cast_to=BrowserView,
)
+ def retrieve(
+ 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,
+ ) -> BrowserView:
+ """
+ Get Browser Details.
+
+ 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
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/v1/devboxes/browsers/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserView,
+ )
+
class AsyncBrowsersResource(AsyncAPIResource):
@cached_property
@@ -155,6 +188,39 @@ async def create(
cast_to=BrowserView,
)
+ async def retrieve(
+ 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,
+ ) -> BrowserView:
+ """
+ Get Browser Details.
+
+ 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
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/v1/devboxes/browsers/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserView,
+ )
+
class BrowsersResourceWithRawResponse:
def __init__(self, browsers: BrowsersResource) -> None:
@@ -163,6 +229,9 @@ def __init__(self, browsers: BrowsersResource) -> None:
self.create = to_raw_response_wrapper(
browsers.create,
)
+ self.retrieve = to_raw_response_wrapper(
+ browsers.retrieve,
+ )
class AsyncBrowsersResourceWithRawResponse:
@@ -172,6 +241,9 @@ def __init__(self, browsers: AsyncBrowsersResource) -> None:
self.create = async_to_raw_response_wrapper(
browsers.create,
)
+ self.retrieve = async_to_raw_response_wrapper(
+ browsers.retrieve,
+ )
class BrowsersResourceWithStreamingResponse:
@@ -181,6 +253,9 @@ def __init__(self, browsers: BrowsersResource) -> None:
self.create = to_streamed_response_wrapper(
browsers.create,
)
+ self.retrieve = to_streamed_response_wrapper(
+ browsers.retrieve,
+ )
class AsyncBrowsersResourceWithStreamingResponse:
@@ -190,3 +265,6 @@ def __init__(self, browsers: AsyncBrowsersResource) -> None:
self.create = async_to_streamed_response_wrapper(
browsers.create,
)
+ self.retrieve = async_to_streamed_response_wrapper(
+ browsers.retrieve,
+ )
diff --git a/src/runloop_api_client/resources/devboxes/computers.py b/src/runloop_api_client/resources/devboxes/computers.py
index b34697390..fea63c395 100644
--- a/src/runloop_api_client/resources/devboxes/computers.py
+++ b/src/runloop_api_client/resources/devboxes/computers.py
@@ -110,6 +110,39 @@ def create(
cast_to=ComputerView,
)
+ def retrieve(
+ 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,
+ ) -> ComputerView:
+ """
+ Get Computer Details.
+
+ 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
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/v1/devboxes/computers/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ComputerView,
+ )
+
def keyboard_interaction(
self,
id: str,
@@ -343,6 +376,39 @@ async def create(
cast_to=ComputerView,
)
+ async def retrieve(
+ 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,
+ ) -> ComputerView:
+ """
+ Get Computer Details.
+
+ 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
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/v1/devboxes/computers/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ComputerView,
+ )
+
async def keyboard_interaction(
self,
id: str,
@@ -508,6 +574,9 @@ def __init__(self, computers: ComputersResource) -> None:
self.create = to_raw_response_wrapper(
computers.create,
)
+ self.retrieve = to_raw_response_wrapper(
+ computers.retrieve,
+ )
self.keyboard_interaction = to_raw_response_wrapper(
computers.keyboard_interaction,
)
@@ -526,6 +595,9 @@ def __init__(self, computers: AsyncComputersResource) -> None:
self.create = async_to_raw_response_wrapper(
computers.create,
)
+ self.retrieve = async_to_raw_response_wrapper(
+ computers.retrieve,
+ )
self.keyboard_interaction = async_to_raw_response_wrapper(
computers.keyboard_interaction,
)
@@ -544,6 +616,9 @@ def __init__(self, computers: ComputersResource) -> None:
self.create = to_streamed_response_wrapper(
computers.create,
)
+ self.retrieve = to_streamed_response_wrapper(
+ computers.retrieve,
+ )
self.keyboard_interaction = to_streamed_response_wrapper(
computers.keyboard_interaction,
)
@@ -562,6 +637,9 @@ def __init__(self, computers: AsyncComputersResource) -> None:
self.create = async_to_streamed_response_wrapper(
computers.create,
)
+ self.retrieve = async_to_streamed_response_wrapper(
+ computers.retrieve,
+ )
self.keyboard_interaction = async_to_streamed_response_wrapper(
computers.keyboard_interaction,
)
diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py
index 0fc5187ff..9a81bac09 100644
--- a/src/runloop_api_client/resources/devboxes/devboxes.py
+++ b/src/runloop_api_client/resources/devboxes/devboxes.py
@@ -26,6 +26,7 @@
from ...types import (
devbox_list_params,
devbox_create_params,
+ devbox_update_params,
devbox_upload_file_params,
devbox_execute_sync_params,
devbox_create_tunnel_params,
@@ -92,8 +93,16 @@
)
from ..._exceptions import RunloopError
from ...lib.polling import PollingConfig, poll_until
-from ..._base_client import AsyncPaginator, make_request_options
from ...lib.polling_async import async_poll_until
+from ..._base_client import AsyncPaginator, make_request_options
+from .disk_snapshots import (
+ DiskSnapshotsResource,
+ AsyncDiskSnapshotsResource,
+ DiskSnapshotsResourceWithRawResponse,
+ AsyncDiskSnapshotsResourceWithRawResponse,
+ DiskSnapshotsResourceWithStreamingResponse,
+ AsyncDiskSnapshotsResourceWithStreamingResponse,
+)
from ...types.devbox_view import DevboxView
from ...types.devbox_tunnel_view import DevboxTunnelView
from ...types.devbox_snapshot_view import DevboxSnapshotView
@@ -108,6 +117,10 @@
DEVBOX_BOOTING_STATES = frozenset(('provisioning', 'initializing'))
class DevboxesResource(SyncAPIResource):
+ @cached_property
+ def disk_snapshots(self) -> DiskSnapshotsResource:
+ return DiskSnapshotsResource(self._client)
+
@cached_property
def browsers(self) -> BrowsersResource:
return BrowsersResource(self._client)
@@ -279,6 +292,60 @@ def retrieve(
cast_to=DevboxView,
)
+ def update(
+ self,
+ id: str,
+ *,
+ metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,
+ name: Optional[str] | 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,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ idempotency_key: str | None = None,
+ ) -> DevboxView:
+ """
+ Updates a devbox by doing a complete update the existing name,metadata fields.
+ It does not patch partial values.
+
+ Args:
+ metadata: User defined metadata to attach to the devbox for organization.
+
+ name: (Optional) A user specified name to give the Devbox.
+
+ 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}")
+ return self._post(
+ f"/v1/devboxes/{id}",
+ body=maybe_transform(
+ {
+ "metadata": metadata,
+ "name": name,
+ },
+ devbox_update_params.DevboxUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=DevboxView,
+ )
+
def await_running(
self,
id: str,
@@ -1239,6 +1306,10 @@ def write_file_contents(
class AsyncDevboxesResource(AsyncAPIResource):
+ @cached_property
+ def disk_snapshots(self) -> AsyncDiskSnapshotsResource:
+ return AsyncDiskSnapshotsResource(self._client)
+
@cached_property
def browsers(self) -> AsyncBrowsersResource:
return AsyncBrowsersResource(self._client)
@@ -1532,8 +1603,62 @@ def is_done_booting(devbox: DevboxView) -> bool:
)
return devbox
+
+ async def update(
+ self,
+ id: str,
+ *,
+ metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,
+ name: Optional[str] | 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,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ idempotency_key: str | None = None,
+ ) -> DevboxView:
+ """
+ Updates a devbox by doing a complete update the existing name,metadata fields.
+ It does not patch partial values.
+
+ Args:
+ metadata: User defined metadata to attach to the devbox for organization.
+
+ name: (Optional) A user specified name to give the Devbox.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
- async def list(
+ 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}")
+ return await self._post(
+ f"/v1/devboxes/{id}",
+ body=await async_maybe_transform(
+ {
+ "metadata": metadata,
+ "name": name,
+ },
+ devbox_update_params.DevboxUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=DevboxView,
+ )
+
+ def list(
self,
*,
limit: int | NotGiven = NOT_GIVEN,
@@ -2376,6 +2501,9 @@ def __init__(self, devboxes: DevboxesResource) -> None:
self.retrieve = to_raw_response_wrapper(
devboxes.retrieve,
)
+ self.update = to_raw_response_wrapper(
+ devboxes.update,
+ )
self.list = to_raw_response_wrapper(
devboxes.list,
)
@@ -2429,6 +2557,10 @@ def __init__(self, devboxes: DevboxesResource) -> None:
devboxes.write_file_contents,
)
+ @cached_property
+ def disk_snapshots(self) -> DiskSnapshotsResourceWithRawResponse:
+ return DiskSnapshotsResourceWithRawResponse(self._devboxes.disk_snapshots)
+
@cached_property
def browsers(self) -> BrowsersResourceWithRawResponse:
return BrowsersResourceWithRawResponse(self._devboxes.browsers)
@@ -2460,6 +2592,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
self.retrieve = async_to_raw_response_wrapper(
devboxes.retrieve,
)
+ self.update = async_to_raw_response_wrapper(
+ devboxes.update,
+ )
self.list = async_to_raw_response_wrapper(
devboxes.list,
)
@@ -2513,6 +2648,10 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
devboxes.write_file_contents,
)
+ @cached_property
+ def disk_snapshots(self) -> AsyncDiskSnapshotsResourceWithRawResponse:
+ return AsyncDiskSnapshotsResourceWithRawResponse(self._devboxes.disk_snapshots)
+
@cached_property
def browsers(self) -> AsyncBrowsersResourceWithRawResponse:
return AsyncBrowsersResourceWithRawResponse(self._devboxes.browsers)
@@ -2544,6 +2683,9 @@ def __init__(self, devboxes: DevboxesResource) -> None:
self.retrieve = to_streamed_response_wrapper(
devboxes.retrieve,
)
+ self.update = to_streamed_response_wrapper(
+ devboxes.update,
+ )
self.list = to_streamed_response_wrapper(
devboxes.list,
)
@@ -2597,6 +2739,10 @@ def __init__(self, devboxes: DevboxesResource) -> None:
devboxes.write_file_contents,
)
+ @cached_property
+ def disk_snapshots(self) -> DiskSnapshotsResourceWithStreamingResponse:
+ return DiskSnapshotsResourceWithStreamingResponse(self._devboxes.disk_snapshots)
+
@cached_property
def browsers(self) -> BrowsersResourceWithStreamingResponse:
return BrowsersResourceWithStreamingResponse(self._devboxes.browsers)
@@ -2628,6 +2774,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
self.retrieve = async_to_streamed_response_wrapper(
devboxes.retrieve,
)
+ self.update = async_to_streamed_response_wrapper(
+ devboxes.update,
+ )
self.list = async_to_streamed_response_wrapper(
devboxes.list,
)
@@ -2681,6 +2830,10 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
devboxes.write_file_contents,
)
+ @cached_property
+ def disk_snapshots(self) -> AsyncDiskSnapshotsResourceWithStreamingResponse:
+ return AsyncDiskSnapshotsResourceWithStreamingResponse(self._devboxes.disk_snapshots)
+
@cached_property
def browsers(self) -> AsyncBrowsersResourceWithStreamingResponse:
return AsyncBrowsersResourceWithStreamingResponse(self._devboxes.browsers)
diff --git a/src/runloop_api_client/resources/devboxes/disk_snapshots.py b/src/runloop_api_client/resources/devboxes/disk_snapshots.py
new file mode 100644
index 000000000..9d23416f1
--- /dev/null
+++ b/src/runloop_api_client/resources/devboxes/disk_snapshots.py
@@ -0,0 +1,421 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+
+import httpx
+
+from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..._utils import (
+ maybe_transform,
+ async_maybe_transform,
+)
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...pagination import SyncDiskSnapshotsCursorIDPage, AsyncDiskSnapshotsCursorIDPage
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.devboxes import disk_snapshot_list_params, disk_snapshot_update_params
+from ...types.devbox_snapshot_view import DevboxSnapshotView
+
+__all__ = ["DiskSnapshotsResource", "AsyncDiskSnapshotsResource"]
+
+
+class DiskSnapshotsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> DiskSnapshotsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/runloopai/api-client-python#accessing-raw-response-data-eg-headers
+ """
+ return DiskSnapshotsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> DiskSnapshotsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/runloopai/api-client-python#with_streaming_response
+ """
+ return DiskSnapshotsResourceWithStreamingResponse(self)
+
+ def update(
+ self,
+ id: str,
+ *,
+ metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,
+ name: Optional[str] | 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,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ idempotency_key: str | None = None,
+ ) -> DevboxSnapshotView:
+ """Updates disk snapshot metadata via update vs patch.
+
+ The entire metadata will be
+ replaced.
+
+ Args:
+ metadata: (Optional) Metadata used to describe the snapshot
+
+ name: (Optional) A user specified name to give the snapshot
+
+ 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}")
+ return self._post(
+ f"/v1/devboxes/disk_snapshots/{id}",
+ body=maybe_transform(
+ {
+ "metadata": metadata,
+ "name": name,
+ },
+ disk_snapshot_update_params.DiskSnapshotUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=DevboxSnapshotView,
+ )
+
+ def list(
+ self,
+ *,
+ devbox_id: str | NotGiven = NOT_GIVEN,
+ limit: int | NotGiven = NOT_GIVEN,
+ starting_after: str | 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,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView]:
+ """
+ List all snapshots of a Devbox while optionally filtering by Devbox ID.
+
+ Args:
+ devbox_id: Devbox ID to filter by.
+
+ limit: The limit of items to return. Default is 20.
+
+ starting_after: Load the next page of data starting after the item with the given ID.
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/devboxes/disk_snapshots",
+ page=SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "devbox_id": devbox_id,
+ "limit": limit,
+ "starting_after": starting_after,
+ },
+ disk_snapshot_list_params.DiskSnapshotListParams,
+ ),
+ ),
+ model=DevboxSnapshotView,
+ )
+
+ def delete(
+ 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,
+ ) -> object:
+ """
+ Delete a previously taken disk snapshot of a 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}")
+ return self._post(
+ f"/v1/devboxes/disk_snapshots/{id}/delete",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=object,
+ )
+
+
+class AsyncDiskSnapshotsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncDiskSnapshotsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/runloopai/api-client-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncDiskSnapshotsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncDiskSnapshotsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/runloopai/api-client-python#with_streaming_response
+ """
+ return AsyncDiskSnapshotsResourceWithStreamingResponse(self)
+
+ async def update(
+ self,
+ id: str,
+ *,
+ metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,
+ name: Optional[str] | 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,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ idempotency_key: str | None = None,
+ ) -> DevboxSnapshotView:
+ """Updates disk snapshot metadata via update vs patch.
+
+ The entire metadata will be
+ replaced.
+
+ Args:
+ metadata: (Optional) Metadata used to describe the snapshot
+
+ name: (Optional) A user specified name to give the snapshot
+
+ 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}")
+ return await self._post(
+ f"/v1/devboxes/disk_snapshots/{id}",
+ body=await async_maybe_transform(
+ {
+ "metadata": metadata,
+ "name": name,
+ },
+ disk_snapshot_update_params.DiskSnapshotUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=DevboxSnapshotView,
+ )
+
+ def list(
+ self,
+ *,
+ devbox_id: str | NotGiven = NOT_GIVEN,
+ limit: int | NotGiven = NOT_GIVEN,
+ starting_after: str | 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,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> AsyncPaginator[DevboxSnapshotView, AsyncDiskSnapshotsCursorIDPage[DevboxSnapshotView]]:
+ """
+ List all snapshots of a Devbox while optionally filtering by Devbox ID.
+
+ Args:
+ devbox_id: Devbox ID to filter by.
+
+ limit: The limit of items to return. Default is 20.
+
+ starting_after: Load the next page of data starting after the item with the given ID.
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/devboxes/disk_snapshots",
+ page=AsyncDiskSnapshotsCursorIDPage[DevboxSnapshotView],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "devbox_id": devbox_id,
+ "limit": limit,
+ "starting_after": starting_after,
+ },
+ disk_snapshot_list_params.DiskSnapshotListParams,
+ ),
+ ),
+ model=DevboxSnapshotView,
+ )
+
+ async def delete(
+ 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,
+ ) -> object:
+ """
+ Delete a previously taken disk snapshot of a 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}")
+ return await self._post(
+ f"/v1/devboxes/disk_snapshots/{id}/delete",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=object,
+ )
+
+
+class DiskSnapshotsResourceWithRawResponse:
+ def __init__(self, disk_snapshots: DiskSnapshotsResource) -> None:
+ self._disk_snapshots = disk_snapshots
+
+ self.update = to_raw_response_wrapper(
+ disk_snapshots.update,
+ )
+ self.list = to_raw_response_wrapper(
+ disk_snapshots.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ disk_snapshots.delete,
+ )
+
+
+class AsyncDiskSnapshotsResourceWithRawResponse:
+ def __init__(self, disk_snapshots: AsyncDiskSnapshotsResource) -> None:
+ self._disk_snapshots = disk_snapshots
+
+ self.update = async_to_raw_response_wrapper(
+ disk_snapshots.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ disk_snapshots.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ disk_snapshots.delete,
+ )
+
+
+class DiskSnapshotsResourceWithStreamingResponse:
+ def __init__(self, disk_snapshots: DiskSnapshotsResource) -> None:
+ self._disk_snapshots = disk_snapshots
+
+ self.update = to_streamed_response_wrapper(
+ disk_snapshots.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ disk_snapshots.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ disk_snapshots.delete,
+ )
+
+
+class AsyncDiskSnapshotsResourceWithStreamingResponse:
+ def __init__(self, disk_snapshots: AsyncDiskSnapshotsResource) -> None:
+ self._disk_snapshots = disk_snapshots
+
+ self.update = async_to_streamed_response_wrapper(
+ disk_snapshots.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ disk_snapshots.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ disk_snapshots.delete,
+ )
diff --git a/src/runloop_api_client/resources/scenarios/scorers.py b/src/runloop_api_client/resources/scenarios/scorers.py
index 5d2ee5b58..77f9393d6 100644
--- a/src/runloop_api_client/resources/scenarios/scorers.py
+++ b/src/runloop_api_client/resources/scenarios/scorers.py
@@ -54,7 +54,7 @@ def create(
self,
*,
bash_script: str,
- name: str,
+ type: 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,
@@ -70,7 +70,7 @@ def create(
bash_script: Bash script for the custom scorer taking context as a json object
$RL_TEST_CONTEXT.
- name: Name of the custom scorer.
+ type: Name of the type of custom scorer.
extra_headers: Send extra headers
@@ -87,7 +87,7 @@ def create(
body=maybe_transform(
{
"bash_script": bash_script,
- "name": name,
+ "type": type,
},
scorer_create_params.ScorerCreateParams,
),
@@ -139,7 +139,7 @@ def update(
id: str,
*,
bash_script: str,
- name: str,
+ type: 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,
@@ -155,7 +155,7 @@ def update(
bash_script: Bash script for the custom scorer taking context as a json object
$RL_TEST_CONTEXT.
- name: Name of the custom scorer.
+ type: Name of the type of custom scorer.
extra_headers: Send extra headers
@@ -174,7 +174,7 @@ def update(
body=maybe_transform(
{
"bash_script": bash_script,
- "name": name,
+ "type": type,
},
scorer_update_params.ScorerUpdateParams,
),
@@ -313,7 +313,7 @@ async def create(
self,
*,
bash_script: str,
- name: str,
+ type: 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,
@@ -329,7 +329,7 @@ async def create(
bash_script: Bash script for the custom scorer taking context as a json object
$RL_TEST_CONTEXT.
- name: Name of the custom scorer.
+ type: Name of the type of custom scorer.
extra_headers: Send extra headers
@@ -346,7 +346,7 @@ async def create(
body=await async_maybe_transform(
{
"bash_script": bash_script,
- "name": name,
+ "type": type,
},
scorer_create_params.ScorerCreateParams,
),
@@ -398,7 +398,7 @@ async def update(
id: str,
*,
bash_script: str,
- name: str,
+ type: 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,
@@ -414,7 +414,7 @@ async def update(
bash_script: Bash script for the custom scorer taking context as a json object
$RL_TEST_CONTEXT.
- name: Name of the custom scorer.
+ type: Name of the type of custom scorer.
extra_headers: Send extra headers
@@ -433,7 +433,7 @@ async def update(
body=await async_maybe_transform(
{
"bash_script": bash_script,
- "name": name,
+ "type": type,
},
scorer_update_params.ScorerUpdateParams,
),
diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py
index f8e0dbe8d..62b6d30e3 100644
--- a/src/runloop_api_client/types/__init__.py
+++ b/src/runloop_api_client/types/__init__.py
@@ -26,6 +26,7 @@
from .input_context_param import InputContextParam as InputContextParam
from .devbox_create_params import DevboxCreateParams as DevboxCreateParams
from .devbox_snapshot_view import DevboxSnapshotView as DevboxSnapshotView
+from .devbox_update_params import DevboxUpdateParams as DevboxUpdateParams
from .scenario_environment import ScenarioEnvironment as ScenarioEnvironment
from .scenario_list_params import ScenarioListParams as ScenarioListParams
from .benchmark_list_params import BenchmarkListParams as BenchmarkListParams
diff --git a/src/runloop_api_client/types/devbox_update_params.py b/src/runloop_api_client/types/devbox_update_params.py
new file mode 100644
index 000000000..2fbdae310
--- /dev/null
+++ b/src/runloop_api_client/types/devbox_update_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import TypedDict
+
+__all__ = ["DevboxUpdateParams"]
+
+
+class DevboxUpdateParams(TypedDict, total=False):
+ metadata: Optional[Dict[str, str]]
+ """User defined metadata to attach to the devbox for organization."""
+
+ name: Optional[str]
+ """(Optional) A user specified name to give the Devbox."""
diff --git a/src/runloop_api_client/types/devboxes/__init__.py b/src/runloop_api_client/types/devboxes/__init__.py
index 6ec9bf85f..ebfdc7800 100644
--- a/src/runloop_api_client/types/devboxes/__init__.py
+++ b/src/runloop_api_client/types/devboxes/__init__.py
@@ -61,10 +61,12 @@
from .file_definition_response import FileDefinitionResponse as FileDefinitionResponse
from .base_workspace_edit_param import BaseWorkspaceEditParam as BaseWorkspaceEditParam
from .code_action_context_param import CodeActionContextParam as CodeActionContextParam
+from .disk_snapshot_list_params import DiskSnapshotListParams as DiskSnapshotListParams
from .execution_retrieve_params import ExecutionRetrieveParams as ExecutionRetrieveParams
from .base_parameter_information import BaseParameterInformation as BaseParameterInformation
from .code_segment_info_response import CodeSegmentInfoResponse as CodeSegmentInfoResponse
from .lsp_file_definition_params import LspFileDefinitionParams as LspFileDefinitionParams
+from .disk_snapshot_update_params import DiskSnapshotUpdateParams as DiskSnapshotUpdateParams
from .lsp_document_symbols_params import LspDocumentSymbolsParams as LspDocumentSymbolsParams
from .lsp_apply_code_action_params import LspApplyCodeActionParams as LspApplyCodeActionParams
from .execution_execute_sync_params import ExecutionExecuteSyncParams as ExecutionExecuteSyncParams
diff --git a/src/runloop_api_client/types/devboxes/browser_view.py b/src/runloop_api_client/types/devboxes/browser_view.py
index ef9303f0f..84341e301 100644
--- a/src/runloop_api_client/types/devboxes/browser_view.py
+++ b/src/runloop_api_client/types/devboxes/browser_view.py
@@ -20,5 +20,7 @@ class BrowserView(BaseModel):
live_view_url: str
"""
The url to view the browser window and enable user interactions via their own
- browser.
+ browser. You can control the interactivity of the browser by adding or removing
+ 'view_only' query parameter. view_only=1 will allow interaction and view_only=0
+ will disable interaction.
"""
diff --git a/src/runloop_api_client/types/devboxes/computer_view.py b/src/runloop_api_client/types/devboxes/computer_view.py
index 858aa27a2..f401845cc 100644
--- a/src/runloop_api_client/types/devboxes/computer_view.py
+++ b/src/runloop_api_client/types/devboxes/computer_view.py
@@ -12,4 +12,9 @@ class ComputerView(BaseModel):
"""The underlying devbox the computer setup is running on."""
live_screen_url: str
- """The http tunnel to connect and view the live screen of the computer."""
+ """The http tunnel to connect and view the live screen of the computer.
+
+ You can control the interactivity of the browser by adding or removing
+ 'view_only' query parameter. view_only=1 will allow interaction and view_only=0
+ will disable interaction.
+ """
diff --git a/src/runloop_api_client/types/devboxes/disk_snapshot_list_params.py b/src/runloop_api_client/types/devboxes/disk_snapshot_list_params.py
new file mode 100644
index 000000000..82cd12731
--- /dev/null
+++ b/src/runloop_api_client/types/devboxes/disk_snapshot_list_params.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["DiskSnapshotListParams"]
+
+
+class DiskSnapshotListParams(TypedDict, total=False):
+ devbox_id: str
+ """Devbox ID to filter by."""
+
+ limit: int
+ """The limit of items to return. Default is 20."""
+
+ starting_after: str
+ """Load the next page of data starting after the item with the given ID."""
diff --git a/src/runloop_api_client/types/devboxes/disk_snapshot_update_params.py b/src/runloop_api_client/types/devboxes/disk_snapshot_update_params.py
new file mode 100644
index 000000000..6b4d276c5
--- /dev/null
+++ b/src/runloop_api_client/types/devboxes/disk_snapshot_update_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import TypedDict
+
+__all__ = ["DiskSnapshotUpdateParams"]
+
+
+class DiskSnapshotUpdateParams(TypedDict, total=False):
+ metadata: Optional[Dict[str, str]]
+ """(Optional) Metadata used to describe the snapshot"""
+
+ name: Optional[str]
+ """(Optional) A user specified name to give the snapshot"""
diff --git a/src/runloop_api_client/types/scenarios/scorer_create_params.py b/src/runloop_api_client/types/scenarios/scorer_create_params.py
index 8261dff54..4ba0d6d4d 100644
--- a/src/runloop_api_client/types/scenarios/scorer_create_params.py
+++ b/src/runloop_api_client/types/scenarios/scorer_create_params.py
@@ -14,5 +14,5 @@ class ScorerCreateParams(TypedDict, total=False):
$RL_TEST_CONTEXT.
"""
- name: Required[str]
- """Name of the custom scorer."""
+ type: Required[str]
+ """Name of the type of custom scorer."""
diff --git a/src/runloop_api_client/types/scenarios/scorer_create_response.py b/src/runloop_api_client/types/scenarios/scorer_create_response.py
index 8c9adf293..49b614e0d 100644
--- a/src/runloop_api_client/types/scenarios/scorer_create_response.py
+++ b/src/runloop_api_client/types/scenarios/scorer_create_response.py
@@ -13,5 +13,5 @@ class ScorerCreateResponse(BaseModel):
bash_script: str
"""Bash script that takes in $RL_TEST_CONTEXT as env variable and runs scoring."""
- name: str
- """Name of the scenario scorer."""
+ type: str
+ """Name of the type of scenario scorer."""
diff --git a/src/runloop_api_client/types/scenarios/scorer_list_response.py b/src/runloop_api_client/types/scenarios/scorer_list_response.py
index 59f7adc40..d24bfd87e 100644
--- a/src/runloop_api_client/types/scenarios/scorer_list_response.py
+++ b/src/runloop_api_client/types/scenarios/scorer_list_response.py
@@ -13,5 +13,5 @@ class ScorerListResponse(BaseModel):
bash_script: str
"""Bash script that takes in $RL_TEST_CONTEXT as env variable and runs scoring."""
- name: str
- """Name of the scenario scorer."""
+ type: str
+ """Name of the type of scenario scorer."""
diff --git a/src/runloop_api_client/types/scenarios/scorer_retrieve_response.py b/src/runloop_api_client/types/scenarios/scorer_retrieve_response.py
index 25ab5b1d6..d72dd5778 100644
--- a/src/runloop_api_client/types/scenarios/scorer_retrieve_response.py
+++ b/src/runloop_api_client/types/scenarios/scorer_retrieve_response.py
@@ -13,5 +13,5 @@ class ScorerRetrieveResponse(BaseModel):
bash_script: str
"""Bash script that takes in $RL_TEST_CONTEXT as env variable and runs scoring."""
- name: str
- """Name of the scenario scorer."""
+ type: str
+ """Name of the type of scenario scorer."""
diff --git a/src/runloop_api_client/types/scenarios/scorer_update_params.py b/src/runloop_api_client/types/scenarios/scorer_update_params.py
index bff2e81c0..3637f4b05 100644
--- a/src/runloop_api_client/types/scenarios/scorer_update_params.py
+++ b/src/runloop_api_client/types/scenarios/scorer_update_params.py
@@ -14,5 +14,5 @@ class ScorerUpdateParams(TypedDict, total=False):
$RL_TEST_CONTEXT.
"""
- name: Required[str]
- """Name of the custom scorer."""
+ type: Required[str]
+ """Name of the type of custom scorer."""
diff --git a/src/runloop_api_client/types/scenarios/scorer_update_response.py b/src/runloop_api_client/types/scenarios/scorer_update_response.py
index 72c6dce78..ef77f37e6 100644
--- a/src/runloop_api_client/types/scenarios/scorer_update_response.py
+++ b/src/runloop_api_client/types/scenarios/scorer_update_response.py
@@ -13,5 +13,5 @@ class ScorerUpdateResponse(BaseModel):
bash_script: str
"""Bash script that takes in $RL_TEST_CONTEXT as env variable and runs scoring."""
- name: str
- """Name of the scenario scorer."""
+ type: str
+ """Name of the type of scenario scorer."""
diff --git a/src/runloop_api_client/types/scoring_function.py b/src/runloop_api_client/types/scoring_function.py
index 2ed26f677..a5adc2b75 100644
--- a/src/runloop_api_client/types/scoring_function.py
+++ b/src/runloop_api_client/types/scoring_function.py
@@ -11,6 +11,9 @@ class ScoringFunction(BaseModel):
name: str
"""Name of scoring function."""
+ type: str
+ """Type of the scoring function. Defaults to bash script."""
+
weight: float
"""Wight to apply to scoring function score.
@@ -23,3 +26,9 @@ class ScoringFunction(BaseModel):
score to standard out. Score should be an integer between 0 and 100, and look
like "score=[0..100].
"""
+
+ scorer_params: Optional[object] = None
+ """
+ Additional JSON structured context to pass to the scoring function if using
+ custom scorer.
+ """
diff --git a/src/runloop_api_client/types/scoring_function_param.py b/src/runloop_api_client/types/scoring_function_param.py
index ba42923cc..1f101bad2 100644
--- a/src/runloop_api_client/types/scoring_function_param.py
+++ b/src/runloop_api_client/types/scoring_function_param.py
@@ -12,6 +12,9 @@ class ScoringFunctionParam(TypedDict, total=False):
name: Required[str]
"""Name of scoring function."""
+ type: Required[str]
+ """Type of the scoring function. Defaults to bash script."""
+
weight: Required[float]
"""Wight to apply to scoring function score.
@@ -24,3 +27,9 @@ class ScoringFunctionParam(TypedDict, total=False):
score to standard out. Score should be an integer between 0 and 100, and look
like "score=[0..100].
"""
+
+ scorer_params: Optional[object]
+ """
+ Additional JSON structured context to pass to the scoring function if using
+ custom scorer.
+ """
diff --git a/tests/api_resources/devboxes/test_browsers.py b/tests/api_resources/devboxes/test_browsers.py
index 2bdbd823a..5fa04afc0 100644
--- a/tests/api_resources/devboxes/test_browsers.py
+++ b/tests/api_resources/devboxes/test_browsers.py
@@ -49,6 +49,44 @@ def test_streaming_response_create(self, client: Runloop) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_method_retrieve(self, client: Runloop) -> None:
+ browser = client.devboxes.browsers.retrieve(
+ "id",
+ )
+ assert_matches_type(BrowserView, browser, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Runloop) -> None:
+ response = client.devboxes.browsers.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser = response.parse()
+ assert_matches_type(BrowserView, browser, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Runloop) -> None:
+ with client.devboxes.browsers.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser = response.parse()
+ assert_matches_type(BrowserView, browser, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Runloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.devboxes.browsers.with_raw_response.retrieve(
+ "",
+ )
+
class TestAsyncBrowsers:
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
@@ -84,3 +122,41 @@ async def test_streaming_response_create(self, async_client: AsyncRunloop) -> No
assert_matches_type(BrowserView, browser, path=["response"])
assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncRunloop) -> None:
+ browser = await async_client.devboxes.browsers.retrieve(
+ "id",
+ )
+ assert_matches_type(BrowserView, browser, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.devboxes.browsers.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser = await response.parse()
+ assert_matches_type(BrowserView, browser, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncRunloop) -> None:
+ async with async_client.devboxes.browsers.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser = await response.parse()
+ assert_matches_type(BrowserView, browser, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncRunloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.devboxes.browsers.with_raw_response.retrieve(
+ "",
+ )
diff --git a/tests/api_resources/devboxes/test_computers.py b/tests/api_resources/devboxes/test_computers.py
index a388926b6..8671cf96c 100644
--- a/tests/api_resources/devboxes/test_computers.py
+++ b/tests/api_resources/devboxes/test_computers.py
@@ -58,6 +58,44 @@ def test_streaming_response_create(self, client: Runloop) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_method_retrieve(self, client: Runloop) -> None:
+ computer = client.devboxes.computers.retrieve(
+ "id",
+ )
+ assert_matches_type(ComputerView, computer, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Runloop) -> None:
+ response = client.devboxes.computers.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = response.parse()
+ assert_matches_type(ComputerView, computer, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Runloop) -> None:
+ with client.devboxes.computers.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = response.parse()
+ assert_matches_type(ComputerView, computer, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Runloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.devboxes.computers.with_raw_response.retrieve(
+ "",
+ )
+
@parametrize
def test_method_keyboard_interaction(self, client: Runloop) -> None:
computer = client.devboxes.computers.keyboard_interaction(
@@ -245,6 +283,44 @@ async def test_streaming_response_create(self, async_client: AsyncRunloop) -> No
assert cast(Any, response.is_closed) is True
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncRunloop) -> None:
+ computer = await async_client.devboxes.computers.retrieve(
+ "id",
+ )
+ assert_matches_type(ComputerView, computer, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.devboxes.computers.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = await response.parse()
+ assert_matches_type(ComputerView, computer, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncRunloop) -> None:
+ async with async_client.devboxes.computers.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = await response.parse()
+ assert_matches_type(ComputerView, computer, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncRunloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.devboxes.computers.with_raw_response.retrieve(
+ "",
+ )
+
@parametrize
async def test_method_keyboard_interaction(self, async_client: AsyncRunloop) -> None:
computer = await async_client.devboxes.computers.keyboard_interaction(
diff --git a/tests/api_resources/devboxes/test_disk_snapshots.py b/tests/api_resources/devboxes/test_disk_snapshots.py
new file mode 100644
index 000000000..dbbefa079
--- /dev/null
+++ b/tests/api_resources/devboxes/test_disk_snapshots.py
@@ -0,0 +1,261 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from runloop_api_client import Runloop, AsyncRunloop
+from runloop_api_client.types import DevboxSnapshotView
+from runloop_api_client.pagination import SyncDiskSnapshotsCursorIDPage, AsyncDiskSnapshotsCursorIDPage
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestDiskSnapshots:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_update(self, client: Runloop) -> None:
+ disk_snapshot = client.devboxes.disk_snapshots.update(
+ id="id",
+ )
+ assert_matches_type(DevboxSnapshotView, disk_snapshot, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Runloop) -> None:
+ disk_snapshot = client.devboxes.disk_snapshots.update(
+ id="id",
+ metadata={"foo": "string"},
+ name="name",
+ )
+ assert_matches_type(DevboxSnapshotView, disk_snapshot, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Runloop) -> None:
+ response = client.devboxes.disk_snapshots.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ disk_snapshot = response.parse()
+ assert_matches_type(DevboxSnapshotView, disk_snapshot, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Runloop) -> None:
+ with client.devboxes.disk_snapshots.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ disk_snapshot = response.parse()
+ assert_matches_type(DevboxSnapshotView, disk_snapshot, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Runloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.devboxes.disk_snapshots.with_raw_response.update(
+ id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Runloop) -> None:
+ disk_snapshot = client.devboxes.disk_snapshots.list()
+ assert_matches_type(SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], disk_snapshot, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Runloop) -> None:
+ disk_snapshot = client.devboxes.disk_snapshots.list(
+ devbox_id="devbox_id",
+ limit=0,
+ starting_after="starting_after",
+ )
+ assert_matches_type(SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], disk_snapshot, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Runloop) -> None:
+ response = client.devboxes.disk_snapshots.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ disk_snapshot = response.parse()
+ assert_matches_type(SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], disk_snapshot, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Runloop) -> None:
+ with client.devboxes.disk_snapshots.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ disk_snapshot = response.parse()
+ assert_matches_type(SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], disk_snapshot, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: Runloop) -> None:
+ disk_snapshot = client.devboxes.disk_snapshots.delete(
+ "id",
+ )
+ assert_matches_type(object, disk_snapshot, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Runloop) -> None:
+ response = client.devboxes.disk_snapshots.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ disk_snapshot = response.parse()
+ assert_matches_type(object, disk_snapshot, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Runloop) -> None:
+ with client.devboxes.disk_snapshots.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ disk_snapshot = response.parse()
+ assert_matches_type(object, disk_snapshot, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Runloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.devboxes.disk_snapshots.with_raw_response.delete(
+ "",
+ )
+
+
+class TestAsyncDiskSnapshots:
+ parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncRunloop) -> None:
+ disk_snapshot = await async_client.devboxes.disk_snapshots.update(
+ id="id",
+ )
+ assert_matches_type(DevboxSnapshotView, disk_snapshot, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncRunloop) -> None:
+ disk_snapshot = await async_client.devboxes.disk_snapshots.update(
+ id="id",
+ metadata={"foo": "string"},
+ name="name",
+ )
+ assert_matches_type(DevboxSnapshotView, disk_snapshot, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.devboxes.disk_snapshots.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ disk_snapshot = await response.parse()
+ assert_matches_type(DevboxSnapshotView, disk_snapshot, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncRunloop) -> None:
+ async with async_client.devboxes.disk_snapshots.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ disk_snapshot = await response.parse()
+ assert_matches_type(DevboxSnapshotView, disk_snapshot, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncRunloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.devboxes.disk_snapshots.with_raw_response.update(
+ id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncRunloop) -> None:
+ disk_snapshot = await async_client.devboxes.disk_snapshots.list()
+ assert_matches_type(AsyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], disk_snapshot, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None:
+ disk_snapshot = await async_client.devboxes.disk_snapshots.list(
+ devbox_id="devbox_id",
+ limit=0,
+ starting_after="starting_after",
+ )
+ assert_matches_type(AsyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], disk_snapshot, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.devboxes.disk_snapshots.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ disk_snapshot = await response.parse()
+ assert_matches_type(AsyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], disk_snapshot, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncRunloop) -> None:
+ async with async_client.devboxes.disk_snapshots.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ disk_snapshot = await response.parse()
+ assert_matches_type(AsyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], disk_snapshot, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncRunloop) -> None:
+ disk_snapshot = await async_client.devboxes.disk_snapshots.delete(
+ "id",
+ )
+ assert_matches_type(object, disk_snapshot, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.devboxes.disk_snapshots.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ disk_snapshot = await response.parse()
+ assert_matches_type(object, disk_snapshot, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncRunloop) -> None:
+ async with async_client.devboxes.disk_snapshots.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ disk_snapshot = await response.parse()
+ assert_matches_type(object, disk_snapshot, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncRunloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.devboxes.disk_snapshots.with_raw_response.delete(
+ "",
+ )
diff --git a/tests/api_resources/scenarios/test_scorers.py b/tests/api_resources/scenarios/test_scorers.py
index aa0d641e3..f3cc6cb50 100644
--- a/tests/api_resources/scenarios/test_scorers.py
+++ b/tests/api_resources/scenarios/test_scorers.py
@@ -28,7 +28,7 @@ class TestScorers:
def test_method_create(self, client: Runloop) -> None:
scorer = client.scenarios.scorers.create(
bash_script="bash_script",
- name="name",
+ type="type",
)
assert_matches_type(ScorerCreateResponse, scorer, path=["response"])
@@ -36,7 +36,7 @@ def test_method_create(self, client: Runloop) -> None:
def test_raw_response_create(self, client: Runloop) -> None:
response = client.scenarios.scorers.with_raw_response.create(
bash_script="bash_script",
- name="name",
+ type="type",
)
assert response.is_closed is True
@@ -48,7 +48,7 @@ def test_raw_response_create(self, client: Runloop) -> None:
def test_streaming_response_create(self, client: Runloop) -> None:
with client.scenarios.scorers.with_streaming_response.create(
bash_script="bash_script",
- name="name",
+ type="type",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -101,7 +101,7 @@ def test_method_update(self, client: Runloop) -> None:
scorer = client.scenarios.scorers.update(
id="id",
bash_script="bash_script",
- name="name",
+ type="type",
)
assert_matches_type(ScorerUpdateResponse, scorer, path=["response"])
@@ -110,7 +110,7 @@ def test_raw_response_update(self, client: Runloop) -> None:
response = client.scenarios.scorers.with_raw_response.update(
id="id",
bash_script="bash_script",
- name="name",
+ type="type",
)
assert response.is_closed is True
@@ -123,7 +123,7 @@ def test_streaming_response_update(self, client: Runloop) -> None:
with client.scenarios.scorers.with_streaming_response.update(
id="id",
bash_script="bash_script",
- name="name",
+ type="type",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -139,7 +139,7 @@ def test_path_params_update(self, client: Runloop) -> None:
client.scenarios.scorers.with_raw_response.update(
id="",
bash_script="bash_script",
- name="name",
+ type="type",
)
@parametrize
@@ -238,7 +238,7 @@ class TestAsyncScorers:
async def test_method_create(self, async_client: AsyncRunloop) -> None:
scorer = await async_client.scenarios.scorers.create(
bash_script="bash_script",
- name="name",
+ type="type",
)
assert_matches_type(ScorerCreateResponse, scorer, path=["response"])
@@ -246,7 +246,7 @@ async def test_method_create(self, async_client: AsyncRunloop) -> None:
async def test_raw_response_create(self, async_client: AsyncRunloop) -> None:
response = await async_client.scenarios.scorers.with_raw_response.create(
bash_script="bash_script",
- name="name",
+ type="type",
)
assert response.is_closed is True
@@ -258,7 +258,7 @@ async def test_raw_response_create(self, async_client: AsyncRunloop) -> None:
async def test_streaming_response_create(self, async_client: AsyncRunloop) -> None:
async with async_client.scenarios.scorers.with_streaming_response.create(
bash_script="bash_script",
- name="name",
+ type="type",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -311,7 +311,7 @@ async def test_method_update(self, async_client: AsyncRunloop) -> None:
scorer = await async_client.scenarios.scorers.update(
id="id",
bash_script="bash_script",
- name="name",
+ type="type",
)
assert_matches_type(ScorerUpdateResponse, scorer, path=["response"])
@@ -320,7 +320,7 @@ async def test_raw_response_update(self, async_client: AsyncRunloop) -> None:
response = await async_client.scenarios.scorers.with_raw_response.update(
id="id",
bash_script="bash_script",
- name="name",
+ type="type",
)
assert response.is_closed is True
@@ -333,7 +333,7 @@ async def test_streaming_response_update(self, async_client: AsyncRunloop) -> No
async with async_client.scenarios.scorers.with_streaming_response.update(
id="id",
bash_script="bash_script",
- name="name",
+ type="type",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -349,7 +349,7 @@ async def test_path_params_update(self, async_client: AsyncRunloop) -> None:
await async_client.scenarios.scorers.with_raw_response.update(
id="",
bash_script="bash_script",
- name="name",
+ type="type",
)
@parametrize
diff --git a/tests/api_resources/test_devboxes.py b/tests/api_resources/test_devboxes.py
index 843c1b21d..e3faa298a 100644
--- a/tests/api_resources/test_devboxes.py
+++ b/tests/api_resources/test_devboxes.py
@@ -134,6 +134,53 @@ def test_path_params_retrieve(self, client: Runloop) -> None:
"",
)
+ @parametrize
+ def test_method_update(self, client: Runloop) -> None:
+ devbox = client.devboxes.update(
+ id="id",
+ )
+ assert_matches_type(DevboxView, devbox, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Runloop) -> None:
+ devbox = client.devboxes.update(
+ id="id",
+ metadata={"foo": "string"},
+ name="name",
+ )
+ assert_matches_type(DevboxView, devbox, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Runloop) -> None:
+ response = client.devboxes.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ devbox = response.parse()
+ assert_matches_type(DevboxView, devbox, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Runloop) -> None:
+ with client.devboxes.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ devbox = response.parse()
+ assert_matches_type(DevboxView, devbox, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Runloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.devboxes.with_raw_response.update(
+ id="",
+ )
+
@parametrize
def test_method_list(self, client: Runloop) -> None:
devbox = client.devboxes.list()
@@ -960,6 +1007,53 @@ async def test_path_params_retrieve(self, async_client: AsyncRunloop) -> None:
"",
)
+ @parametrize
+ async def test_method_update(self, async_client: AsyncRunloop) -> None:
+ devbox = await async_client.devboxes.update(
+ id="id",
+ )
+ assert_matches_type(DevboxView, devbox, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncRunloop) -> None:
+ devbox = await async_client.devboxes.update(
+ id="id",
+ metadata={"foo": "string"},
+ name="name",
+ )
+ assert_matches_type(DevboxView, devbox, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.devboxes.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ devbox = await response.parse()
+ assert_matches_type(DevboxView, devbox, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncRunloop) -> None:
+ async with async_client.devboxes.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ devbox = await response.parse()
+ assert_matches_type(DevboxView, devbox, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncRunloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.devboxes.with_raw_response.update(
+ id="",
+ )
+
@parametrize
async def test_method_list(self, async_client: AsyncRunloop) -> None:
devbox = await async_client.devboxes.list()
diff --git a/tests/api_resources/test_scenarios.py b/tests/api_resources/test_scenarios.py
index 64ae41c99..78b3cebc2 100644
--- a/tests/api_resources/test_scenarios.py
+++ b/tests/api_resources/test_scenarios.py
@@ -30,6 +30,7 @@ def test_method_create(self, client: Runloop) -> None:
"scoring_function_parameters": [
{
"name": "name",
+ "type": "type",
"weight": 0,
}
]
@@ -49,8 +50,10 @@ def test_method_create_with_all_params(self, client: Runloop) -> None:
"scoring_function_parameters": [
{
"name": "name",
+ "type": "type",
"weight": 0,
"bash_script": "bash_script",
+ "scorer_params": {},
}
]
},
@@ -71,6 +74,7 @@ def test_raw_response_create(self, client: Runloop) -> None:
"scoring_function_parameters": [
{
"name": "name",
+ "type": "type",
"weight": 0,
}
]
@@ -91,6 +95,7 @@ def test_streaming_response_create(self, client: Runloop) -> None:
"scoring_function_parameters": [
{
"name": "name",
+ "type": "type",
"weight": 0,
}
]
@@ -263,6 +268,7 @@ async def test_method_create(self, async_client: AsyncRunloop) -> None:
"scoring_function_parameters": [
{
"name": "name",
+ "type": "type",
"weight": 0,
}
]
@@ -282,8 +288,10 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) -
"scoring_function_parameters": [
{
"name": "name",
+ "type": "type",
"weight": 0,
"bash_script": "bash_script",
+ "scorer_params": {},
}
]
},
@@ -304,6 +312,7 @@ async def test_raw_response_create(self, async_client: AsyncRunloop) -> None:
"scoring_function_parameters": [
{
"name": "name",
+ "type": "type",
"weight": 0,
}
]
@@ -324,6 +333,7 @@ async def test_streaming_response_create(self, async_client: AsyncRunloop) -> No
"scoring_function_parameters": [
{
"name": "name",
+ "type": "type",
"weight": 0,
}
]
diff --git a/tests/test_client.py b/tests/test_client.py
index 81c247f78..4e2d14a39 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -92,7 +92,7 @@ def test_copy_default_options(self) -> None:
# options that have a default are overridden correctly
copied = self.client.copy(max_retries=7)
assert copied.max_retries == 7
- assert self.client.max_retries == 2
+ assert self.client.max_retries == 0
copied2 = copied.copy(max_retries=6)
assert copied2.max_retries == 6
@@ -738,21 +738,21 @@ class Model(BaseModel):
"remaining_retries,retry_after,timeout",
[
[3, "20", 20],
- [3, "0", 0.5],
- [3, "-10", 0.5],
+ [3, "0", 1],
+ [3, "-10", 1],
[3, "60", 60],
- [3, "61", 0.5],
+ [3, "61", 1],
[3, "Fri, 29 Sep 2023 16:26:57 GMT", 20],
- [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5],
- [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5],
+ [3, "Fri, 29 Sep 2023 16:26:37 GMT", 1],
+ [3, "Fri, 29 Sep 2023 16:26:27 GMT", 1],
[3, "Fri, 29 Sep 2023 16:27:37 GMT", 60],
- [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5],
- [3, "99999999999999999999999999999999999", 0.5],
- [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5],
- [3, "", 0.5],
- [2, "", 0.5 * 2.0],
- [1, "", 0.5 * 4.0],
- [-1100, "", 8], # test large number potentially overflowing
+ [3, "Fri, 29 Sep 2023 16:27:38 GMT", 1],
+ [3, "99999999999999999999999999999999999", 1],
+ [3, "Zun, 29 Sep 2023 16:26:27 GMT", 1],
+ [3, "", 1],
+ [2, "", 1 * 2.0],
+ [1, "", 1 * 4.0],
+ [-1100, "", 10], # test large number potentially overflowing
],
)
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
@@ -762,7 +762,7 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str
headers = httpx.Headers({"retry-after": retry_after})
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
- assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType]
+ assert calculated == pytest.approx(timeout, 1 * 0.875) # pyright: ignore[reportUnknownMemberType]
@mock.patch("runloop_api_client._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
@@ -909,7 +909,7 @@ def test_copy_default_options(self) -> None:
# options that have a default are overridden correctly
copied = self.client.copy(max_retries=7)
assert copied.max_retries == 7
- assert self.client.max_retries == 2
+ assert self.client.max_retries == 0
copied2 = copied.copy(max_retries=6)
assert copied2.max_retries == 6
@@ -1558,21 +1558,21 @@ class Model(BaseModel):
"remaining_retries,retry_after,timeout",
[
[3, "20", 20],
- [3, "0", 0.5],
- [3, "-10", 0.5],
+ [3, "0", 1],
+ [3, "-10", 1],
[3, "60", 60],
- [3, "61", 0.5],
+ [3, "61", 1],
[3, "Fri, 29 Sep 2023 16:26:57 GMT", 20],
- [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5],
- [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5],
+ [3, "Fri, 29 Sep 2023 16:26:37 GMT", 1],
+ [3, "Fri, 29 Sep 2023 16:26:27 GMT", 1],
[3, "Fri, 29 Sep 2023 16:27:37 GMT", 60],
- [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5],
- [3, "99999999999999999999999999999999999", 0.5],
- [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5],
- [3, "", 0.5],
- [2, "", 0.5 * 2.0],
- [1, "", 0.5 * 4.0],
- [-1100, "", 8], # test large number potentially overflowing
+ [3, "Fri, 29 Sep 2023 16:27:38 GMT", 1],
+ [3, "99999999999999999999999999999999999", 1],
+ [3, "Zun, 29 Sep 2023 16:26:27 GMT", 1],
+ [3, "", 1],
+ [2, "", 1 * 2.0],
+ [1, "", 1 * 4.0],
+ [-1100, "", 10], # test large number potentially overflowing
],
)
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
@@ -1583,7 +1583,7 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte
headers = httpx.Headers({"retry-after": retry_after})
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
- assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType]
+ assert calculated == pytest.approx(timeout, 1 * 0.875) # pyright: ignore[reportUnknownMemberType]
@mock.patch("runloop_api_client._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
diff --git a/tests/test_transform.py b/tests/test_transform.py
index 839b59a76..f30ab41ee 100644
--- a/tests/test_transform.py
+++ b/tests/test_transform.py
@@ -2,7 +2,7 @@
import io
import pathlib
-from typing import Any, List, Union, TypeVar, Iterable, Optional, cast
+from typing import Any, Dict, List, Union, TypeVar, Iterable, Optional, cast
from datetime import date, datetime
from typing_extensions import Required, Annotated, TypedDict
@@ -388,6 +388,15 @@ def my_iter() -> Iterable[Baz8]:
}
+@parametrize
+@pytest.mark.asyncio
+async def test_dictionary_items(use_async: bool) -> None:
+ class DictItems(TypedDict):
+ foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")]
+
+ assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}}
+
+
class TypedDictIterableUnionStr(TypedDict):
foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")]