From d67abf1c52a089e192987a261e69219d60514bc3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 00:17:30 +0000 Subject: [PATCH 01/11] chore(package): drop Python 3.8 support From a52802a6d12d96c3bf4bd670e77c9ec50d08b459 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 03:33:41 +0000 Subject: [PATCH 02/11] fix: compat with Python 3.14 --- src/runloop_api_client/_models.py | 11 ++++++++--- tests/test_models.py | 8 ++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/runloop_api_client/_models.py b/src/runloop_api_client/_models.py index 76456a237..58f4c89df 100644 --- a/src/runloop_api_client/_models.py +++ b/src/runloop_api_client/_models.py @@ -2,6 +2,7 @@ import os import inspect +import weakref from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( @@ -573,6 +574,9 @@ class CachedDiscriminatorType(Protocol): __discriminator__: DiscriminatorDetails +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + class DiscriminatorDetails: field_name: str """The name of the discriminator field in the variant class, e.g. @@ -615,8 +619,9 @@ def __init__( def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: - if isinstance(union, CachedDiscriminatorType): - return union.__discriminator__ + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached discriminator_field_name: str | None = None @@ -669,7 +674,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, discriminator_field=discriminator_field_name, discriminator_alias=discriminator_alias, ) - cast(CachedDiscriminatorType, union).__discriminator__ = details + DISCRIMINATOR_CACHE.setdefault(union, details) return details diff --git a/tests/test_models.py b/tests/test_models.py index 87683d2ad..aac33d2e4 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -9,7 +9,7 @@ from runloop_api_client._utils import PropertyInfo from runloop_api_client._compat import PYDANTIC_V1, parse_obj, model_dump, model_json -from runloop_api_client._models import BaseModel, construct_type +from runloop_api_client._models import DISCRIMINATOR_CACHE, BaseModel, construct_type class BasicModel(BaseModel): @@ -809,7 +809,7 @@ class B(BaseModel): UnionType = cast(Any, Union[A, B]) - assert not hasattr(UnionType, "__discriminator__") + assert not DISCRIMINATOR_CACHE.get(UnionType) m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) @@ -818,7 +818,7 @@ class B(BaseModel): assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] - discriminator = UnionType.__discriminator__ + discriminator = DISCRIMINATOR_CACHE.get(UnionType) assert discriminator is not None m = construct_type( @@ -830,7 +830,7 @@ class B(BaseModel): # if the discriminator details object stays the same between invocations then # we hit the cache - assert UnionType.__discriminator__ is discriminator + assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator @pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") From 78449be80b3b3f32c14b3fe12909be1d1ea6c1aa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 21:43:45 +0000 Subject: [PATCH 03/11] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0ffcd7329..a2fbbe5eb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 94 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-91cec4aeec2421487d5eeece4804ae3b8b47e22bdbb9fc7460feb64a8c10e42f.yml -openapi_spec_hash: 3d8d782e2450d46b8ce6573bad488ea1 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-eb33a5a2af905766647a4cf900026240c83d244c67c7a92dc25c453b4e2ca9d6.yml +openapi_spec_hash: ed79fe718b145466c229630c47943a9e config_hash: 95facb8cef59b5a1b05763b871bf6a4b From ee672fdf0881b6dcdf46638821862f27206f8fa9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:48:07 +0000 Subject: [PATCH 04/11] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index a2fbbe5eb..f5090dac7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 94 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-eb33a5a2af905766647a4cf900026240c83d244c67c7a92dc25c453b4e2ca9d6.yml -openapi_spec_hash: ed79fe718b145466c229630c47943a9e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-4f5fe47eea7a16fba1ba473532c7cb9687a113cf97e7b92bac014bca728f9505.yml +openapi_spec_hash: ae55e0436d54e239a548e245fd19a863 config_hash: 95facb8cef59b5a1b05763b871bf6a4b From 50340b20148f72cb645bba0bafde6f902e063425 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 03:28:36 +0000 Subject: [PATCH 05/11] fix(compat): update signatures of `model_dump` and `model_dump_json` for Pydantic v1 --- src/runloop_api_client/_models.py | 41 ++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/runloop_api_client/_models.py b/src/runloop_api_client/_models.py index 58f4c89df..3fcf41040 100644 --- a/src/runloop_api_client/_models.py +++ b/src/runloop_api_client/_models.py @@ -257,15 +257,16 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, - serialize_as_any: bool = False, fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -273,16 +274,24 @@ def model_dump( Args: mode: The mode in which `to_python` should run. - If mode is 'json', the dictionary will only contain JSON serializable types. - If mode is 'python', the dictionary may contain any Python objects. - include: A list of fields to include in the output. - exclude: A list of fields to exclude from the output. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. by_alias: Whether to use the field's alias in the dictionary key if defined. - exclude_unset: Whether to exclude fields that are unset or None from the output. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - round_trip: Whether to enable serialization and deserialization round-trip support. - warnings: Whether to log warnings when invalid fields are encountered. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. Returns: A dictionary representation of the model. @@ -299,6 +308,8 @@ def model_dump( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, @@ -315,15 +326,17 @@ def model_dump_json( self, *, indent: int | None = None, + ensure_ascii: bool = False, include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: @@ -355,6 +368,10 @@ def model_dump_json( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, From ce55350d81f7f5ba3a3aff8faea88c0e1366cea9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:58:23 +0000 Subject: [PATCH 06/11] feat(blueprints): prevent deletion of blueprints with dependent snapshots --- .stats.yml | 8 +- api.md | 15 + src/runloop_api_client/_client.py | 39 +- src/runloop_api_client/pagination.py | 75 ++++ src/runloop_api_client/resources/__init__.py | 14 + src/runloop_api_client/resources/agents.py | 415 ++++++++++++++++++ .../resources/blueprints.py | 14 +- .../resources/devboxes/devboxes.py | 16 +- .../resources/devboxes/disk_snapshots.py | 16 +- src/runloop_api_client/types/__init__.py | 5 + .../types/agent_create_params.py | 18 + .../types/agent_list_params.py | 24 + .../types/agent_list_view.py | 22 + src/runloop_api_client/types/agent_view.py | 22 + .../devbox_list_disk_snapshots_params.py | 3 + .../types/devbox_snapshot_view.py | 3 + .../devboxes/disk_snapshot_list_params.py | 3 + .../types/shared/__init__.py | 1 + .../types/shared/agent_mount_parameters.py | 5 +- .../types/shared/agent_source.py | 62 +++ .../types/shared_params/__init__.py | 1 + .../shared_params/agent_mount_parameters.py | 5 +- .../types/shared_params/agent_source.py | 65 +++ .../devboxes/test_disk_snapshots.py | 2 + tests/api_resources/test_agents.py | 287 ++++++++++++ tests/api_resources/test_devboxes.py | 2 + 26 files changed, 1123 insertions(+), 19 deletions(-) create mode 100644 src/runloop_api_client/resources/agents.py create mode 100644 src/runloop_api_client/types/agent_create_params.py create mode 100644 src/runloop_api_client/types/agent_list_params.py create mode 100644 src/runloop_api_client/types/agent_list_view.py create mode 100644 src/runloop_api_client/types/agent_view.py create mode 100644 src/runloop_api_client/types/shared/agent_source.py create mode 100644 src/runloop_api_client/types/shared_params/agent_source.py create mode 100644 tests/api_resources/test_agents.py diff --git a/.stats.yml b/.stats.yml index f5090dac7..9f4451f29 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 94 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-4f5fe47eea7a16fba1ba473532c7cb9687a113cf97e7b92bac014bca728f9505.yml -openapi_spec_hash: ae55e0436d54e239a548e245fd19a863 -config_hash: 95facb8cef59b5a1b05763b871bf6a4b +configured_endpoints: 97 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-4a14147d9603516ef2245584e4e44af8324cb1cbf130c3718bea39ad15b8084d.yml +openapi_spec_hash: bfeae3e75e496683934b941a19396c4b +config_hash: 2363f563f42501d2b1587a4f64bdccaf diff --git a/api.md b/api.md index deb30848b..a3c529f83 100644 --- a/api.md +++ b/api.md @@ -4,6 +4,7 @@ from runloop_api_client.types import ( AfterIdle, AgentMountParameters, + AgentSource, CodeMountParameters, LaunchParameters, Mount, @@ -47,6 +48,20 @@ Methods: - client.benchmarks.runs.complete(id) -> BenchmarkRunView - client.benchmarks.runs.list_scenario_runs(id, \*\*params) -> SyncBenchmarkRunsCursorIDPage[ScenarioRunView] +# Agents + +Types: + +```python +from runloop_api_client.types import AgentCreateParameters, AgentListView, AgentView +``` + +Methods: + +- client.agents.create(\*\*params) -> AgentView +- client.agents.retrieve(id) -> AgentView +- client.agents.list(\*\*params) -> SyncAgentsCursorIDPage[AgentView] + # Blueprints Types: diff --git a/src/runloop_api_client/_client.py b/src/runloop_api_client/_client.py index bdfccf7e5..f4f6d1535 100644 --- a/src/runloop_api_client/_client.py +++ b/src/runloop_api_client/_client.py @@ -31,7 +31,8 @@ ) if TYPE_CHECKING: - from .resources import objects, secrets, devboxes, scenarios, benchmarks, blueprints, repositories + from .resources import agents, objects, secrets, devboxes, scenarios, benchmarks, blueprints, repositories + from .resources.agents import AgentsResource, AsyncAgentsResource from .resources.objects import ObjectsResource, AsyncObjectsResource from .resources.secrets import SecretsResource, AsyncSecretsResource from .resources.blueprints import BlueprintsResource, AsyncBlueprintsResource @@ -106,6 +107,12 @@ def benchmarks(self) -> BenchmarksResource: return BenchmarksResource(self) + @cached_property + def agents(self) -> AgentsResource: + from .resources.agents import AgentsResource + + return AgentsResource(self) + @cached_property def blueprints(self) -> BlueprintsResource: from .resources.blueprints import BlueprintsResource @@ -318,6 +325,12 @@ def benchmarks(self) -> AsyncBenchmarksResource: return AsyncBenchmarksResource(self) + @cached_property + def agents(self) -> AsyncAgentsResource: + from .resources.agents import AsyncAgentsResource + + return AsyncAgentsResource(self) + @cached_property def blueprints(self) -> AsyncBlueprintsResource: from .resources.blueprints import AsyncBlueprintsResource @@ -479,6 +492,12 @@ def benchmarks(self) -> benchmarks.BenchmarksResourceWithRawResponse: return BenchmarksResourceWithRawResponse(self._client.benchmarks) + @cached_property + def agents(self) -> agents.AgentsResourceWithRawResponse: + from .resources.agents import AgentsResourceWithRawResponse + + return AgentsResourceWithRawResponse(self._client.agents) + @cached_property def blueprints(self) -> blueprints.BlueprintsResourceWithRawResponse: from .resources.blueprints import BlueprintsResourceWithRawResponse @@ -528,6 +547,12 @@ def benchmarks(self) -> benchmarks.AsyncBenchmarksResourceWithRawResponse: return AsyncBenchmarksResourceWithRawResponse(self._client.benchmarks) + @cached_property + def agents(self) -> agents.AsyncAgentsResourceWithRawResponse: + from .resources.agents import AsyncAgentsResourceWithRawResponse + + return AsyncAgentsResourceWithRawResponse(self._client.agents) + @cached_property def blueprints(self) -> blueprints.AsyncBlueprintsResourceWithRawResponse: from .resources.blueprints import AsyncBlueprintsResourceWithRawResponse @@ -577,6 +602,12 @@ def benchmarks(self) -> benchmarks.BenchmarksResourceWithStreamingResponse: return BenchmarksResourceWithStreamingResponse(self._client.benchmarks) + @cached_property + def agents(self) -> agents.AgentsResourceWithStreamingResponse: + from .resources.agents import AgentsResourceWithStreamingResponse + + return AgentsResourceWithStreamingResponse(self._client.agents) + @cached_property def blueprints(self) -> blueprints.BlueprintsResourceWithStreamingResponse: from .resources.blueprints import BlueprintsResourceWithStreamingResponse @@ -626,6 +657,12 @@ def benchmarks(self) -> benchmarks.AsyncBenchmarksResourceWithStreamingResponse: return AsyncBenchmarksResourceWithStreamingResponse(self._client.benchmarks) + @cached_property + def agents(self) -> agents.AsyncAgentsResourceWithStreamingResponse: + from .resources.agents import AsyncAgentsResourceWithStreamingResponse + + return AsyncAgentsResourceWithStreamingResponse(self._client.agents) + @cached_property def blueprints(self) -> blueprints.AsyncBlueprintsResourceWithStreamingResponse: from .resources.blueprints import AsyncBlueprintsResourceWithStreamingResponse diff --git a/src/runloop_api_client/pagination.py b/src/runloop_api_client/pagination.py index 734b6996f..a52e69c24 100644 --- a/src/runloop_api_client/pagination.py +++ b/src/runloop_api_client/pagination.py @@ -16,6 +16,8 @@ "AsyncDiskSnapshotsCursorIDPage", "SyncBenchmarksCursorIDPage", "AsyncBenchmarksCursorIDPage", + "SyncAgentsCursorIDPage", + "AsyncAgentsCursorIDPage", "SyncBenchmarkRunsCursorIDPage", "AsyncBenchmarkRunsCursorIDPage", "SyncScenariosCursorIDPage", @@ -56,6 +58,11 @@ class BenchmarksCursorIDPageItem(Protocol): id: str +@runtime_checkable +class AgentsCursorIDPageItem(Protocol): + id: str + + @runtime_checkable class BenchmarkRunsCursorIDPageItem(Protocol): id: str @@ -421,6 +428,74 @@ def next_page_info(self) -> Optional[PageInfo]: return PageInfo(params={"starting_after": item.id}) +class SyncAgentsCursorIDPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + agents: List[_T] + has_more: Optional[bool] = None + total_count: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + agents = self.agents + if not agents: + return [] + return agents + + @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]: + agents = self.agents + if not agents: + return None + + item = cast(Any, agents[-1]) + if not isinstance(item, AgentsCursorIDPageItem) or item.id is None: # pyright: ignore[reportUnnecessaryComparison] + # TODO emit warning log + return None + + return PageInfo(params={"starting_after": item.id}) + + +class AsyncAgentsCursorIDPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + agents: List[_T] + has_more: Optional[bool] = None + total_count: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + agents = self.agents + if not agents: + return [] + return agents + + @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]: + agents = self.agents + if not agents: + return None + + item = cast(Any, agents[-1]) + if not isinstance(item, AgentsCursorIDPageItem) or item.id is None: # pyright: ignore[reportUnnecessaryComparison] + # TODO emit warning log + return None + + return PageInfo(params={"starting_after": item.id}) + + class SyncBenchmarkRunsCursorIDPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): runs: List[_T] has_more: Optional[bool] = None diff --git a/src/runloop_api_client/resources/__init__.py b/src/runloop_api_client/resources/__init__.py index 72ea92468..adafc4644 100644 --- a/src/runloop_api_client/resources/__init__.py +++ b/src/runloop_api_client/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .agents import ( + AgentsResource, + AsyncAgentsResource, + AgentsResourceWithRawResponse, + AsyncAgentsResourceWithRawResponse, + AgentsResourceWithStreamingResponse, + AsyncAgentsResourceWithStreamingResponse, +) from .objects import ( ObjectsResource, AsyncObjectsResource, @@ -64,6 +72,12 @@ "AsyncBenchmarksResourceWithRawResponse", "BenchmarksResourceWithStreamingResponse", "AsyncBenchmarksResourceWithStreamingResponse", + "AgentsResource", + "AsyncAgentsResource", + "AgentsResourceWithRawResponse", + "AsyncAgentsResourceWithRawResponse", + "AgentsResourceWithStreamingResponse", + "AsyncAgentsResourceWithStreamingResponse", "BlueprintsResource", "AsyncBlueprintsResource", "BlueprintsResourceWithRawResponse", diff --git a/src/runloop_api_client/resources/agents.py b/src/runloop_api_client/resources/agents.py new file mode 100644 index 000000000..6ff202d74 --- /dev/null +++ b/src/runloop_api_client/resources/agents.py @@ -0,0 +1,415 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..types import agent_list_params, agent_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +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 SyncAgentsCursorIDPage, AsyncAgentsCursorIDPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.agent_view import AgentView +from ..types.shared_params.agent_source import AgentSource + +__all__ = ["AgentsResource", "AsyncAgentsResource"] + + +class AgentsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AgentsResourceWithRawResponse: + """ + 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 AgentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AgentsResourceWithStreamingResponse: + """ + 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 AgentsResourceWithStreamingResponse(self) + + def create( + self, + *, + name: str, + source: Optional[AgentSource] | Omit = omit, + # 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, + ) -> AgentView: + """Create a new Agent with a name and optional public visibility. + + The Agent will be + assigned a unique ID. + + Args: + name: The name of the Agent. + + source: The source configuration for the Agent. + + 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 + """ + return self._post( + "/v1/agents", + body=maybe_transform( + { + "name": name, + "source": source, + }, + agent_create_params.AgentCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=AgentView, + ) + + 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, + ) -> AgentView: + """ + Retrieve a specific Agent by its unique identifier. + + 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/agents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentView, + ) + + def list( + self, + *, + is_public: bool | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, + search: str | Omit = omit, + starting_after: str | Omit = omit, + # 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, + ) -> SyncAgentsCursorIDPage[AgentView]: + """ + List all Agents for the authenticated account with pagination support. + + Args: + is_public: Filter agents by public visibility. + + limit: The limit of items to return. Default is 20. + + name: Filter agents by name (partial match supported). + + search: Search by agent ID or name. + + 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/agents", + page=SyncAgentsCursorIDPage[AgentView], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "is_public": is_public, + "limit": limit, + "name": name, + "search": search, + "starting_after": starting_after, + }, + agent_list_params.AgentListParams, + ), + ), + model=AgentView, + ) + + +class AsyncAgentsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAgentsResourceWithRawResponse: + """ + 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 AsyncAgentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAgentsResourceWithStreamingResponse: + """ + 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 AsyncAgentsResourceWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + source: Optional[AgentSource] | Omit = omit, + # 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, + ) -> AgentView: + """Create a new Agent with a name and optional public visibility. + + The Agent will be + assigned a unique ID. + + Args: + name: The name of the Agent. + + source: The source configuration for the Agent. + + 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 + """ + return await self._post( + "/v1/agents", + body=await async_maybe_transform( + { + "name": name, + "source": source, + }, + agent_create_params.AgentCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=AgentView, + ) + + 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, + ) -> AgentView: + """ + Retrieve a specific Agent by its unique identifier. + + 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/agents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentView, + ) + + def list( + self, + *, + is_public: bool | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, + search: str | Omit = omit, + starting_after: str | Omit = omit, + # 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[AgentView, AsyncAgentsCursorIDPage[AgentView]]: + """ + List all Agents for the authenticated account with pagination support. + + Args: + is_public: Filter agents by public visibility. + + limit: The limit of items to return. Default is 20. + + name: Filter agents by name (partial match supported). + + search: Search by agent ID or name. + + 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/agents", + page=AsyncAgentsCursorIDPage[AgentView], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "is_public": is_public, + "limit": limit, + "name": name, + "search": search, + "starting_after": starting_after, + }, + agent_list_params.AgentListParams, + ), + ), + model=AgentView, + ) + + +class AgentsResourceWithRawResponse: + def __init__(self, agents: AgentsResource) -> None: + self._agents = agents + + self.create = to_raw_response_wrapper( + agents.create, + ) + self.retrieve = to_raw_response_wrapper( + agents.retrieve, + ) + self.list = to_raw_response_wrapper( + agents.list, + ) + + +class AsyncAgentsResourceWithRawResponse: + def __init__(self, agents: AsyncAgentsResource) -> None: + self._agents = agents + + self.create = async_to_raw_response_wrapper( + agents.create, + ) + self.retrieve = async_to_raw_response_wrapper( + agents.retrieve, + ) + self.list = async_to_raw_response_wrapper( + agents.list, + ) + + +class AgentsResourceWithStreamingResponse: + def __init__(self, agents: AgentsResource) -> None: + self._agents = agents + + self.create = to_streamed_response_wrapper( + agents.create, + ) + self.retrieve = to_streamed_response_wrapper( + agents.retrieve, + ) + self.list = to_streamed_response_wrapper( + agents.list, + ) + + +class AsyncAgentsResourceWithStreamingResponse: + def __init__(self, agents: AsyncAgentsResource) -> None: + self._agents = agents + + self.create = async_to_streamed_response_wrapper( + agents.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + agents.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + agents.list, + ) diff --git a/src/runloop_api_client/resources/blueprints.py b/src/runloop_api_client/resources/blueprints.py index 1bbd8d4b7..fb0708314 100644 --- a/src/runloop_api_client/resources/blueprints.py +++ b/src/runloop_api_client/resources/blueprints.py @@ -441,8 +441,11 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> object: - """ - Delete a previously created Blueprint. + """Delete a previously created Blueprint. + + If a blueprint has dependent snapshots, + it cannot be deleted. You can find them by querying: GET + /v1/devboxes/disk_snapshots?source_blueprint_id={blueprint_id}. Args: extra_headers: Send extra headers @@ -1067,8 +1070,11 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> object: - """ - Delete a previously created Blueprint. + """Delete a previously created Blueprint. + + If a blueprint has dependent snapshots, + it cannot be deleted. You can find them by querying: GET + /v1/devboxes/disk_snapshots?source_blueprint_id={blueprint_id}. Args: extra_headers: Send extra headers diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py index 9b13767af..b8ddfb976 100644 --- a/src/runloop_api_client/resources/devboxes/devboxes.py +++ b/src/runloop_api_client/resources/devboxes/devboxes.py @@ -1086,6 +1086,7 @@ def list_disk_snapshots( limit: int | Omit = omit, metadata_key: str | Omit = omit, metadata_key_in: str | Omit = omit, + source_blueprint_id: str | Omit = omit, starting_after: str | Omit = omit, # 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. @@ -1095,8 +1096,8 @@ def list_disk_snapshots( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView]: """ - List all snapshots of a Devbox while optionally filtering by Devbox ID and - metadata. + List all snapshots of a Devbox while optionally filtering by Devbox ID, source + Blueprint ID, and metadata. Args: devbox_id: Devbox ID to filter by. @@ -1108,6 +1109,8 @@ def list_disk_snapshots( metadata_key_in: Filter snapshots by metadata key with multiple possible values (OR condition). + source_blueprint_id: Source Blueprint ID to filter snapshots by. + starting_after: Load the next page of data starting after the item with the given ID. extra_headers: Send extra headers @@ -1132,6 +1135,7 @@ def list_disk_snapshots( "limit": limit, "metadata_key": metadata_key, "metadata_key_in": metadata_key_in, + "source_blueprint_id": source_blueprint_id, "starting_after": starting_after, }, devbox_list_disk_snapshots_params.DevboxListDiskSnapshotsParams, @@ -2618,6 +2622,7 @@ def list_disk_snapshots( limit: int | Omit = omit, metadata_key: str | Omit = omit, metadata_key_in: str | Omit = omit, + source_blueprint_id: str | Omit = omit, starting_after: str | Omit = omit, # 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. @@ -2627,8 +2632,8 @@ def list_disk_snapshots( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[DevboxSnapshotView, AsyncDiskSnapshotsCursorIDPage[DevboxSnapshotView]]: """ - List all snapshots of a Devbox while optionally filtering by Devbox ID and - metadata. + List all snapshots of a Devbox while optionally filtering by Devbox ID, source + Blueprint ID, and metadata. Args: devbox_id: Devbox ID to filter by. @@ -2640,6 +2645,8 @@ def list_disk_snapshots( metadata_key_in: Filter snapshots by metadata key with multiple possible values (OR condition). + source_blueprint_id: Source Blueprint ID to filter snapshots by. + starting_after: Load the next page of data starting after the item with the given ID. extra_headers: Send extra headers @@ -2664,6 +2671,7 @@ def list_disk_snapshots( "limit": limit, "metadata_key": metadata_key, "metadata_key_in": metadata_key_in, + "source_blueprint_id": source_blueprint_id, "starting_after": starting_after, }, devbox_list_disk_snapshots_params.DevboxListDiskSnapshotsParams, diff --git a/src/runloop_api_client/resources/devboxes/disk_snapshots.py b/src/runloop_api_client/resources/devboxes/disk_snapshots.py index cf6cb54e8..0e3530374 100644 --- a/src/runloop_api_client/resources/devboxes/disk_snapshots.py +++ b/src/runloop_api_client/resources/devboxes/disk_snapshots.py @@ -114,6 +114,7 @@ def list( limit: int | Omit = omit, metadata_key: str | Omit = omit, metadata_key_in: str | Omit = omit, + source_blueprint_id: str | Omit = omit, starting_after: str | Omit = omit, # 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. @@ -123,8 +124,8 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView]: """ - List all snapshots of a Devbox while optionally filtering by Devbox ID and - metadata. + List all snapshots of a Devbox while optionally filtering by Devbox ID, source + Blueprint ID, and metadata. Args: devbox_id: Devbox ID to filter by. @@ -136,6 +137,8 @@ def list( metadata_key_in: Filter snapshots by metadata key with multiple possible values (OR condition). + source_blueprint_id: Source Blueprint ID to filter snapshots by. + starting_after: Load the next page of data starting after the item with the given ID. extra_headers: Send extra headers @@ -160,6 +163,7 @@ def list( "limit": limit, "metadata_key": metadata_key, "metadata_key_in": metadata_key_in, + "source_blueprint_id": source_blueprint_id, "starting_after": starting_after, }, disk_snapshot_list_params.DiskSnapshotListParams, @@ -361,6 +365,7 @@ def list( limit: int | Omit = omit, metadata_key: str | Omit = omit, metadata_key_in: str | Omit = omit, + source_blueprint_id: str | Omit = omit, starting_after: str | Omit = omit, # 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. @@ -370,8 +375,8 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[DevboxSnapshotView, AsyncDiskSnapshotsCursorIDPage[DevboxSnapshotView]]: """ - List all snapshots of a Devbox while optionally filtering by Devbox ID and - metadata. + List all snapshots of a Devbox while optionally filtering by Devbox ID, source + Blueprint ID, and metadata. Args: devbox_id: Devbox ID to filter by. @@ -383,6 +388,8 @@ def list( metadata_key_in: Filter snapshots by metadata key with multiple possible values (OR condition). + source_blueprint_id: Source Blueprint ID to filter snapshots by. + starting_after: Load the next page of data starting after the item with the given ID. extra_headers: Send extra headers @@ -407,6 +414,7 @@ def list( "limit": limit, "metadata_key": metadata_key, "metadata_key_in": metadata_key_in, + "source_blueprint_id": source_blueprint_id, "starting_after": starting_after, }, disk_snapshot_list_params.DiskSnapshotListParams, diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py index 2330d5227..4d5168b44 100644 --- a/src/runloop_api_client/types/__init__.py +++ b/src/runloop_api_client/types/__init__.py @@ -6,11 +6,13 @@ Mount as Mount, AfterIdle as AfterIdle, RunProfile as RunProfile, + AgentSource as AgentSource, LaunchParameters as LaunchParameters, CodeMountParameters as CodeMountParameters, AgentMountParameters as AgentMountParameters, ObjectMountParameters as ObjectMountParameters, ) +from .agent_view import AgentView as AgentView from .devbox_view import DevboxView as DevboxView from .object_view import ObjectView as ObjectView from .secret_view import SecretView as SecretView @@ -18,17 +20,20 @@ from .scenario_view import ScenarioView as ScenarioView from .benchmark_view import BenchmarkView as BenchmarkView from .blueprint_view import BlueprintView as BlueprintView +from .agent_list_view import AgentListView as AgentListView from .devbox_list_view import DevboxListView as DevboxListView from .object_list_view import ObjectListView as ObjectListView from .scoring_contract import ScoringContract as ScoringContract from .scoring_function import ScoringFunction as ScoringFunction from .secret_list_view import SecretListView as SecretListView +from .agent_list_params import AgentListParams as AgentListParams from .scenario_run_view import ScenarioRunView as ScenarioRunView from .benchmark_run_view import BenchmarkRunView as BenchmarkRunView from .devbox_list_params import DevboxListParams as DevboxListParams from .devbox_tunnel_view import DevboxTunnelView as DevboxTunnelView from .object_list_params import ObjectListParams as ObjectListParams from .secret_list_params import SecretListParams as SecretListParams +from .agent_create_params import AgentCreateParams as AgentCreateParams from .blueprint_build_log import BlueprintBuildLog as BlueprintBuildLog from .blueprint_list_view import BlueprintListView as BlueprintListView from .input_context_param import InputContextParam as InputContextParam diff --git a/src/runloop_api_client/types/agent_create_params.py b/src/runloop_api_client/types/agent_create_params.py new file mode 100644 index 000000000..1a3372e7e --- /dev/null +++ b/src/runloop_api_client/types/agent_create_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 import Optional +from typing_extensions import Required, TypedDict + +from .shared_params.agent_source import AgentSource + +__all__ = ["AgentCreateParams"] + + +class AgentCreateParams(TypedDict, total=False): + name: Required[str] + """The name of the Agent.""" + + source: Optional[AgentSource] + """The source configuration for the Agent.""" diff --git a/src/runloop_api_client/types/agent_list_params.py b/src/runloop_api_client/types/agent_list_params.py new file mode 100644 index 000000000..a3199190b --- /dev/null +++ b/src/runloop_api_client/types/agent_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AgentListParams"] + + +class AgentListParams(TypedDict, total=False): + is_public: bool + """Filter agents by public visibility.""" + + limit: int + """The limit of items to return. Default is 20.""" + + name: str + """Filter agents by name (partial match supported).""" + + search: str + """Search by agent ID or name.""" + + 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/agent_list_view.py b/src/runloop_api_client/types/agent_list_view.py new file mode 100644 index 000000000..c2a7be455 --- /dev/null +++ b/src/runloop_api_client/types/agent_list_view.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .agent_view import AgentView + +__all__ = ["AgentListView"] + + +class AgentListView(BaseModel): + agents: List[AgentView] + """The list of Agents.""" + + has_more: bool + """Whether there are more Agents to fetch.""" + + remaining_count: int + """The count of remaining Agents.""" + + total_count: int + """The total count of Agents.""" diff --git a/src/runloop_api_client/types/agent_view.py b/src/runloop_api_client/types/agent_view.py new file mode 100644 index 000000000..bf2520a49 --- /dev/null +++ b/src/runloop_api_client/types/agent_view.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel +from .shared.agent_source import AgentSource + +__all__ = ["AgentView"] + + +class AgentView(BaseModel): + id: str + """The unique identifier of the Agent.""" + + is_public: bool + """Whether the Agent is publicly accessible.""" + + name: str + """The name of the Agent.""" + + source: Optional[AgentSource] = None + """The source configuration for the Agent.""" diff --git a/src/runloop_api_client/types/devbox_list_disk_snapshots_params.py b/src/runloop_api_client/types/devbox_list_disk_snapshots_params.py index ad476b208..7ffcf5386 100644 --- a/src/runloop_api_client/types/devbox_list_disk_snapshots_params.py +++ b/src/runloop_api_client/types/devbox_list_disk_snapshots_params.py @@ -25,5 +25,8 @@ class DevboxListDiskSnapshotsParams(TypedDict, total=False): metadata_key_in: Annotated[str, PropertyInfo(alias="metadata[key][in]")] """Filter snapshots by metadata key with multiple possible values (OR condition).""" + source_blueprint_id: str + """Source Blueprint ID to filter snapshots by.""" + 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/devbox_snapshot_view.py b/src/runloop_api_client/types/devbox_snapshot_view.py index 82ae90907..477f99d4e 100644 --- a/src/runloop_api_client/types/devbox_snapshot_view.py +++ b/src/runloop_api_client/types/devbox_snapshot_view.py @@ -25,3 +25,6 @@ class DevboxSnapshotView(BaseModel): name: Optional[str] = None """(Optional) The custom name of the snapshot.""" + + source_blueprint_id: Optional[str] = None + """(Optional) The source Blueprint ID this snapshot was created from.""" 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 index e43a55d1d..7b0f3454f 100644 --- a/src/runloop_api_client/types/devboxes/disk_snapshot_list_params.py +++ b/src/runloop_api_client/types/devboxes/disk_snapshot_list_params.py @@ -25,5 +25,8 @@ class DiskSnapshotListParams(TypedDict, total=False): metadata_key_in: Annotated[str, PropertyInfo(alias="metadata[key][in]")] """Filter snapshots by metadata key with multiple possible values (OR condition).""" + source_blueprint_id: str + """Source Blueprint ID to filter snapshots by.""" + 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/shared/__init__.py b/src/runloop_api_client/types/shared/__init__.py index 39a3b079e..696abb529 100644 --- a/src/runloop_api_client/types/shared/__init__.py +++ b/src/runloop_api_client/types/shared/__init__.py @@ -3,6 +3,7 @@ from .mount import Mount as Mount from .after_idle import AfterIdle as AfterIdle from .run_profile import RunProfile as RunProfile +from .agent_source import AgentSource as AgentSource from .launch_parameters import LaunchParameters as LaunchParameters from .code_mount_parameters import CodeMountParameters as CodeMountParameters from .agent_mount_parameters import AgentMountParameters as AgentMountParameters diff --git a/src/runloop_api_client/types/shared/agent_mount_parameters.py b/src/runloop_api_client/types/shared/agent_mount_parameters.py index 5e92a0641..c2b384265 100644 --- a/src/runloop_api_client/types/shared/agent_mount_parameters.py +++ b/src/runloop_api_client/types/shared/agent_mount_parameters.py @@ -15,7 +15,10 @@ class AgentMountParameters(BaseModel): type: Literal["agent_mount"] agent_path: Optional[str] = None - """Optional path to mount the agent on the Devbox. + """Path to mount the agent on the Devbox. Required for git and object agents. Use absolute path (e.g., /home/user/agent) """ + + auth_token: Optional[str] = None + """Optional auth token for private repositories. Only used for git agents.""" diff --git a/src/runloop_api_client/types/shared/agent_source.py b/src/runloop_api_client/types/shared/agent_source.py new file mode 100644 index 000000000..7dc4958d2 --- /dev/null +++ b/src/runloop_api_client/types/shared/agent_source.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["AgentSource", "Git", "Npm", "Object", "Pip"] + + +class Git(BaseModel): + repository: str + """Git repository URL""" + + ref: Optional[str] = None + """Optional Git ref (branch/tag/commit), defaults to main/HEAD""" + + setup: Optional[List[str]] = None + """Setup commands to run after cloning""" + + +class Npm(BaseModel): + package_name: str + """NPM package name""" + + npm_version: Optional[str] = None + """NPM version constraint""" + + registry_url: Optional[str] = None + """NPM registry URL""" + + +class Object(BaseModel): + object_id: str + """Object ID""" + + +class Pip(BaseModel): + package_name: str + """Pip package name""" + + pip_version: Optional[str] = None + """Pip version constraint""" + + registry_url: Optional[str] = None + """Pip registry URL""" + + +class AgentSource(BaseModel): + type: str + """Source type: npm, pip, object, or git""" + + git: Optional[Git] = None + """Git source configuration""" + + npm: Optional[Npm] = None + """NPM source configuration""" + + object: Optional[Object] = None + """Object store source configuration""" + + pip: Optional[Pip] = None + """Pip source configuration""" diff --git a/src/runloop_api_client/types/shared_params/__init__.py b/src/runloop_api_client/types/shared_params/__init__.py index 39a3b079e..696abb529 100644 --- a/src/runloop_api_client/types/shared_params/__init__.py +++ b/src/runloop_api_client/types/shared_params/__init__.py @@ -3,6 +3,7 @@ from .mount import Mount as Mount from .after_idle import AfterIdle as AfterIdle from .run_profile import RunProfile as RunProfile +from .agent_source import AgentSource as AgentSource from .launch_parameters import LaunchParameters as LaunchParameters from .code_mount_parameters import CodeMountParameters as CodeMountParameters from .agent_mount_parameters import AgentMountParameters as AgentMountParameters diff --git a/src/runloop_api_client/types/shared_params/agent_mount_parameters.py b/src/runloop_api_client/types/shared_params/agent_mount_parameters.py index eca7e8cf1..def126043 100644 --- a/src/runloop_api_client/types/shared_params/agent_mount_parameters.py +++ b/src/runloop_api_client/types/shared_params/agent_mount_parameters.py @@ -15,7 +15,10 @@ class AgentMountParameters(TypedDict, total=False): type: Required[Literal["agent_mount"]] agent_path: Optional[str] - """Optional path to mount the agent on the Devbox. + """Path to mount the agent on the Devbox. Required for git and object agents. Use absolute path (e.g., /home/user/agent) """ + + auth_token: Optional[str] + """Optional auth token for private repositories. Only used for git agents.""" diff --git a/src/runloop_api_client/types/shared_params/agent_source.py b/src/runloop_api_client/types/shared_params/agent_source.py new file mode 100644 index 000000000..2c1ce71d3 --- /dev/null +++ b/src/runloop_api_client/types/shared_params/agent_source.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["AgentSource", "Git", "Npm", "Object", "Pip"] + + +class Git(TypedDict, total=False): + repository: Required[str] + """Git repository URL""" + + ref: Optional[str] + """Optional Git ref (branch/tag/commit), defaults to main/HEAD""" + + setup: Optional[SequenceNotStr[str]] + """Setup commands to run after cloning""" + + +class Npm(TypedDict, total=False): + package_name: Required[str] + """NPM package name""" + + npm_version: Optional[str] + """NPM version constraint""" + + registry_url: Optional[str] + """NPM registry URL""" + + +class Object(TypedDict, total=False): + object_id: Required[str] + """Object ID""" + + +class Pip(TypedDict, total=False): + package_name: Required[str] + """Pip package name""" + + pip_version: Optional[str] + """Pip version constraint""" + + registry_url: Optional[str] + """Pip registry URL""" + + +class AgentSource(TypedDict, total=False): + type: Required[str] + """Source type: npm, pip, object, or git""" + + git: Optional[Git] + """Git source configuration""" + + npm: Optional[Npm] + """NPM source configuration""" + + object: Optional[Object] + """Object store source configuration""" + + pip: Optional[Pip] + """Pip source configuration""" diff --git a/tests/api_resources/devboxes/test_disk_snapshots.py b/tests/api_resources/devboxes/test_disk_snapshots.py index d7ae59acd..170e618ba 100644 --- a/tests/api_resources/devboxes/test_disk_snapshots.py +++ b/tests/api_resources/devboxes/test_disk_snapshots.py @@ -84,6 +84,7 @@ def test_method_list_with_all_params(self, client: Runloop) -> None: limit=0, metadata_key="metadata[key]", metadata_key_in="metadata[key][in]", + source_blueprint_id="source_blueprint_id", starting_after="starting_after", ) assert_matches_type(SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], disk_snapshot, path=["response"]) @@ -356,6 +357,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> limit=0, metadata_key="metadata[key]", metadata_key_in="metadata[key][in]", + source_blueprint_id="source_blueprint_id", starting_after="starting_after", ) assert_matches_type(AsyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], disk_snapshot, path=["response"]) diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py new file mode 100644 index 000000000..596338b85 --- /dev/null +++ b/tests/api_resources/test_agents.py @@ -0,0 +1,287 @@ +# 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 AgentView +from runloop_api_client.pagination import SyncAgentsCursorIDPage, AsyncAgentsCursorIDPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAgents: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Runloop) -> None: + agent = client.agents.create( + name="name", + ) + assert_matches_type(AgentView, agent, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Runloop) -> None: + agent = client.agents.create( + name="name", + source={ + "type": "type", + "git": { + "repository": "repository", + "ref": "ref", + "setup": ["string"], + }, + "npm": { + "package_name": "package_name", + "npm_version": "npm_version", + "registry_url": "registry_url", + }, + "object": {"object_id": "object_id"}, + "pip": { + "package_name": "package_name", + "pip_version": "pip_version", + "registry_url": "registry_url", + }, + }, + ) + assert_matches_type(AgentView, agent, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Runloop) -> None: + response = client.agents.with_raw_response.create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentView, agent, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Runloop) -> None: + with client.agents.with_streaming_response.create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentView, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: Runloop) -> None: + agent = client.agents.retrieve( + "id", + ) + assert_matches_type(AgentView, agent, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Runloop) -> None: + response = client.agents.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentView, agent, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Runloop) -> None: + with client.agents.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentView, agent, 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.agents.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: Runloop) -> None: + agent = client.agents.list() + assert_matches_type(SyncAgentsCursorIDPage[AgentView], agent, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Runloop) -> None: + agent = client.agents.list( + is_public=True, + limit=0, + name="name", + search="search", + starting_after="starting_after", + ) + assert_matches_type(SyncAgentsCursorIDPage[AgentView], agent, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Runloop) -> None: + response = client.agents.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(SyncAgentsCursorIDPage[AgentView], agent, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Runloop) -> None: + with client.agents.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(SyncAgentsCursorIDPage[AgentView], agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncAgents: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncRunloop) -> None: + agent = await async_client.agents.create( + name="name", + ) + assert_matches_type(AgentView, agent, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncRunloop) -> None: + agent = await async_client.agents.create( + name="name", + source={ + "type": "type", + "git": { + "repository": "repository", + "ref": "ref", + "setup": ["string"], + }, + "npm": { + "package_name": "package_name", + "npm_version": "npm_version", + "registry_url": "registry_url", + }, + "object": {"object_id": "object_id"}, + "pip": { + "package_name": "package_name", + "pip_version": "pip_version", + "registry_url": "registry_url", + }, + }, + ) + assert_matches_type(AgentView, agent, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncRunloop) -> None: + response = await async_client.agents.with_raw_response.create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentView, agent, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncRunloop) -> None: + async with async_client.agents.with_streaming_response.create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentView, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncRunloop) -> None: + agent = await async_client.agents.retrieve( + "id", + ) + assert_matches_type(AgentView, agent, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncRunloop) -> None: + response = await async_client.agents.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentView, agent, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncRunloop) -> None: + async with async_client.agents.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentView, agent, 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.agents.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncRunloop) -> None: + agent = await async_client.agents.list() + assert_matches_type(AsyncAgentsCursorIDPage[AgentView], agent, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: + agent = await async_client.agents.list( + is_public=True, + limit=0, + name="name", + search="search", + starting_after="starting_after", + ) + assert_matches_type(AsyncAgentsCursorIDPage[AgentView], agent, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncRunloop) -> None: + response = await async_client.agents.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AsyncAgentsCursorIDPage[AgentView], agent, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncRunloop) -> None: + async with async_client.agents.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AsyncAgentsCursorIDPage[AgentView], agent, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_devboxes.py b/tests/api_resources/test_devboxes.py index 98d0a4a7a..ede4c70cf 100644 --- a/tests/api_resources/test_devboxes.py +++ b/tests/api_resources/test_devboxes.py @@ -633,6 +633,7 @@ def test_method_list_disk_snapshots_with_all_params(self, client: Runloop) -> No limit=0, metadata_key="metadata[key]", metadata_key_in="metadata[key][in]", + source_blueprint_id="source_blueprint_id", starting_after="starting_after", ) assert_matches_type(SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], devbox, path=["response"]) @@ -2180,6 +2181,7 @@ async def test_method_list_disk_snapshots_with_all_params(self, async_client: As limit=0, metadata_key="metadata[key]", metadata_key_in="metadata[key][in]", + source_blueprint_id="source_blueprint_id", starting_after="starting_after", ) assert_matches_type(AsyncDiskSnapshotsCursorIDPage[DevboxSnapshotView], devbox, path=["response"]) From 7c265936088c074d00bd3c65b52dde5dcde3ccfb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 02:18:20 +0000 Subject: [PATCH 07/11] fix(snapshot): added "deleted" status to DevboxSnapshotStatus enum \n fix(storage-object): added ObjectState enum, fixed createObject() to appropriately type content_type and state as the respective enums --- .stats.yml | 4 +- src/runloop_api_client/resources/objects.py | 50 +++++++++++-------- .../devbox_snapshot_async_status_view.py | 2 +- .../types/object_create_params.py | 6 +++ .../types/object_list_params.py | 12 ++--- .../types/object_list_public_params.py | 12 ++--- src/runloop_api_client/types/object_view.py | 5 +- tests/api_resources/test_objects.py | 18 ++++--- 8 files changed, 65 insertions(+), 44 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9f4451f29..a331aec18 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-4a14147d9603516ef2245584e4e44af8324cb1cbf130c3718bea39ad15b8084d.yml -openapi_spec_hash: bfeae3e75e496683934b941a19396c4b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-9f8a6310d4d9c36d386f5afe17cfc891d05d5911204574645f9f90d9b6864a00.yml +openapi_spec_hash: 3fc0a807bb8d728abe3b7eaff0b9f7ec config_hash: 2363f563f42501d2b1587a4f64bdccaf diff --git a/src/runloop_api_client/resources/objects.py b/src/runloop_api_client/resources/objects.py index 3f83830e6..4d7d2e0a3 100644 --- a/src/runloop_api_client/resources/objects.py +++ b/src/runloop_api_client/resources/objects.py @@ -52,6 +52,7 @@ def create( content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"], name: str, metadata: Optional[Dict[str, str]] | Omit = omit, + ttl_ms: Optional[int] | Omit = omit, # 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, @@ -72,6 +73,9 @@ def create( metadata: User defined metadata to attach to the object for organization. + ttl_ms: Optional lifetime of the object in milliseconds, after which the object is + automatically deleted. Time starts ticking after the object is created. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -89,6 +93,7 @@ def create( "content_type": content_type, "name": name, "metadata": metadata, + "ttl_ms": ttl_ms, }, object_create_params.ObjectCreateParams, ), @@ -138,12 +143,12 @@ def retrieve( def list( self, *, - content_type: str | Omit = omit, + content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, search: str | Omit = omit, starting_after: str | Omit = omit, - state: str | Omit = omit, + state: Literal["UPLOADING", "READ_ONLY", "DELETED", "ERROR"] | Omit = omit, # 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,17 +160,17 @@ def list( List all Objects for the authenticated account with pagination support. Args: - content_type: Filter objects by content type. + content_type: Filter storage objects by content type. limit: The limit of items to return. Default is 20. - name: Filter objects by name (partial match supported). + name: Filter storage objects by name (partial match supported). search: Search by object ID or name. starting_after: Load the next page of data starting after the item with the given ID. - state: Filter objects by state (UPLOADING, READ_ONLY, DELETED). + state: Filter storage objects by state. extra_headers: Send extra headers @@ -328,12 +333,12 @@ def download( def list_public( self, *, - content_type: str | Omit = omit, + content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, search: str | Omit = omit, starting_after: str | Omit = omit, - state: str | Omit = omit, + state: Literal["UPLOADING", "READ_ONLY", "DELETED", "ERROR"] | Omit = omit, # 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, @@ -345,17 +350,17 @@ def list_public( List all public Objects with pagination support. Args: - content_type: Filter objects by content type. + content_type: Filter storage objects by content type. limit: The limit of items to return. Default is 20. - name: Filter objects by name (partial match supported). + name: Filter storage objects by name (partial match supported). search: Search by object ID or name. starting_after: Load the next page of data starting after the item with the given ID. - state: Filter objects by state (UPLOADING, READ_ONLY, DELETED). + state: Filter storage objects by state. extra_headers: Send extra headers @@ -415,6 +420,7 @@ async def create( content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"], name: str, metadata: Optional[Dict[str, str]] | Omit = omit, + ttl_ms: Optional[int] | Omit = omit, # 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, @@ -435,6 +441,9 @@ async def create( metadata: User defined metadata to attach to the object for organization. + ttl_ms: Optional lifetime of the object in milliseconds, after which the object is + automatically deleted. Time starts ticking after the object is created. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -452,6 +461,7 @@ async def create( "content_type": content_type, "name": name, "metadata": metadata, + "ttl_ms": ttl_ms, }, object_create_params.ObjectCreateParams, ), @@ -501,12 +511,12 @@ async def retrieve( def list( self, *, - content_type: str | Omit = omit, + content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, search: str | Omit = omit, starting_after: str | Omit = omit, - state: str | Omit = omit, + state: Literal["UPLOADING", "READ_ONLY", "DELETED", "ERROR"] | Omit = omit, # 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, @@ -518,17 +528,17 @@ def list( List all Objects for the authenticated account with pagination support. Args: - content_type: Filter objects by content type. + content_type: Filter storage objects by content type. limit: The limit of items to return. Default is 20. - name: Filter objects by name (partial match supported). + name: Filter storage objects by name (partial match supported). search: Search by object ID or name. starting_after: Load the next page of data starting after the item with the given ID. - state: Filter objects by state (UPLOADING, READ_ONLY, DELETED). + state: Filter storage objects by state. extra_headers: Send extra headers @@ -691,12 +701,12 @@ async def download( def list_public( self, *, - content_type: str | Omit = omit, + content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, search: str | Omit = omit, starting_after: str | Omit = omit, - state: str | Omit = omit, + state: Literal["UPLOADING", "READ_ONLY", "DELETED", "ERROR"] | Omit = omit, # 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, @@ -708,17 +718,17 @@ def list_public( List all public Objects with pagination support. Args: - content_type: Filter objects by content type. + content_type: Filter storage objects by content type. limit: The limit of items to return. Default is 20. - name: Filter objects by name (partial match supported). + name: Filter storage objects by name (partial match supported). search: Search by object ID or name. starting_after: Load the next page of data starting after the item with the given ID. - state: Filter objects by state (UPLOADING, READ_ONLY, DELETED). + state: Filter storage objects by state. extra_headers: Send extra headers diff --git a/src/runloop_api_client/types/devboxes/devbox_snapshot_async_status_view.py b/src/runloop_api_client/types/devboxes/devbox_snapshot_async_status_view.py index 2c7ea8eee..11abaa62f 100644 --- a/src/runloop_api_client/types/devboxes/devbox_snapshot_async_status_view.py +++ b/src/runloop_api_client/types/devboxes/devbox_snapshot_async_status_view.py @@ -10,7 +10,7 @@ class DevboxSnapshotAsyncStatusView(BaseModel): - status: Literal["in_progress", "error", "complete"] + status: Literal["in_progress", "error", "complete", "deleted"] """The current status of the snapshot operation.""" error_message: Optional[str] = None diff --git a/src/runloop_api_client/types/object_create_params.py b/src/runloop_api_client/types/object_create_params.py index 99cdff0e7..c48a767f0 100644 --- a/src/runloop_api_client/types/object_create_params.py +++ b/src/runloop_api_client/types/object_create_params.py @@ -22,3 +22,9 @@ class ObjectCreateParams(TypedDict, total=False): metadata: Optional[Dict[str, str]] """User defined metadata to attach to the object for organization.""" + + ttl_ms: Optional[int] + """ + Optional lifetime of the object in milliseconds, after which the object is + automatically deleted. Time starts ticking after the object is created. + """ diff --git a/src/runloop_api_client/types/object_list_params.py b/src/runloop_api_client/types/object_list_params.py index 044cff81a..084fac54d 100644 --- a/src/runloop_api_client/types/object_list_params.py +++ b/src/runloop_api_client/types/object_list_params.py @@ -2,20 +2,20 @@ from __future__ import annotations -from typing_extensions import TypedDict +from typing_extensions import Literal, TypedDict __all__ = ["ObjectListParams"] class ObjectListParams(TypedDict, total=False): - content_type: str - """Filter objects by content type.""" + content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] + """Filter storage objects by content type.""" limit: int """The limit of items to return. Default is 20.""" name: str - """Filter objects by name (partial match supported).""" + """Filter storage objects by name (partial match supported).""" search: str """Search by object ID or name.""" @@ -23,5 +23,5 @@ class ObjectListParams(TypedDict, total=False): starting_after: str """Load the next page of data starting after the item with the given ID.""" - state: str - """Filter objects by state (UPLOADING, READ_ONLY, DELETED).""" + state: Literal["UPLOADING", "READ_ONLY", "DELETED", "ERROR"] + """Filter storage objects by state.""" diff --git a/src/runloop_api_client/types/object_list_public_params.py b/src/runloop_api_client/types/object_list_public_params.py index a69ef53dd..19b18ba49 100644 --- a/src/runloop_api_client/types/object_list_public_params.py +++ b/src/runloop_api_client/types/object_list_public_params.py @@ -2,20 +2,20 @@ from __future__ import annotations -from typing_extensions import TypedDict +from typing_extensions import Literal, TypedDict __all__ = ["ObjectListPublicParams"] class ObjectListPublicParams(TypedDict, total=False): - content_type: str - """Filter objects by content type.""" + content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] + """Filter storage objects by content type.""" limit: int """The limit of items to return. Default is 20.""" name: str - """Filter objects by name (partial match supported).""" + """Filter storage objects by name (partial match supported).""" search: str """Search by object ID or name.""" @@ -23,5 +23,5 @@ class ObjectListPublicParams(TypedDict, total=False): starting_after: str """Load the next page of data starting after the item with the given ID.""" - state: str - """Filter objects by state (UPLOADING, READ_ONLY, DELETED).""" + state: Literal["UPLOADING", "READ_ONLY", "DELETED", "ERROR"] + """Filter storage objects by state.""" diff --git a/src/runloop_api_client/types/object_view.py b/src/runloop_api_client/types/object_view.py index b8a2e0f4c..80aea62ed 100644 --- a/src/runloop_api_client/types/object_view.py +++ b/src/runloop_api_client/types/object_view.py @@ -21,9 +21,12 @@ class ObjectView(BaseModel): name: str """The name of the Object.""" - state: str + state: Literal["UPLOADING", "READ_ONLY", "DELETED", "ERROR"] """The current state of the Object.""" + delete_after_time_ms: Optional[int] = None + """The time after which the Object will be deleted in milliseconds since epoch.""" + size_bytes: Optional[int] = None """The size of the Object content in bytes (null until uploaded).""" diff --git a/tests/api_resources/test_objects.py b/tests/api_resources/test_objects.py index cf5a4cd3b..2593e03bf 100644 --- a/tests/api_resources/test_objects.py +++ b/tests/api_resources/test_objects.py @@ -35,6 +35,7 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: content_type="unspecified", name="name", metadata={"foo": "string"}, + ttl_ms=0, ) assert_matches_type(ObjectView, object_, path=["response"]) @@ -110,12 +111,12 @@ def test_method_list(self, client: Runloop) -> None: @parametrize def test_method_list_with_all_params(self, client: Runloop) -> None: object_ = client.objects.list( - content_type="content_type", + content_type="unspecified", limit=0, name="name", search="search", starting_after="starting_after", - state="state", + state="UPLOADING", ) assert_matches_type(SyncObjectsCursorIDPage[ObjectView], object_, path=["response"]) @@ -269,12 +270,12 @@ def test_method_list_public(self, client: Runloop) -> None: @parametrize def test_method_list_public_with_all_params(self, client: Runloop) -> None: object_ = client.objects.list_public( - content_type="content_type", + content_type="unspecified", limit=0, name="name", search="search", starting_after="starting_after", - state="state", + state="UPLOADING", ) assert_matches_type(SyncObjectsCursorIDPage[ObjectView], object_, path=["response"]) @@ -318,6 +319,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - content_type="unspecified", name="name", metadata={"foo": "string"}, + ttl_ms=0, ) assert_matches_type(ObjectView, object_, path=["response"]) @@ -393,12 +395,12 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: object_ = await async_client.objects.list( - content_type="content_type", + content_type="unspecified", limit=0, name="name", search="search", starting_after="starting_after", - state="state", + state="UPLOADING", ) assert_matches_type(AsyncObjectsCursorIDPage[ObjectView], object_, path=["response"]) @@ -552,12 +554,12 @@ async def test_method_list_public(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_public_with_all_params(self, async_client: AsyncRunloop) -> None: object_ = await async_client.objects.list_public( - content_type="content_type", + content_type="unspecified", limit=0, name="name", search="search", starting_after="starting_after", - state="state", + state="UPLOADING", ) assert_matches_type(AsyncObjectsCursorIDPage[ObjectView], object_, path=["response"]) From f2bc83c126696aea224bb5978294fc3362a94eeb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 01:39:36 +0000 Subject: [PATCH 08/11] feat(object): Added ability to give objects a Time To Live, after which they are automatically deleted.\nfeat(blueprints): Added the ability to attach objects as build contexts that can be referenced in your Dockerfile. --- .stats.yml | 4 +- .../resources/blueprints.py | 44 ++++++++++++ .../types/blueprint_build_parameters.py | 66 +++++++++++++++++- .../types/blueprint_create_params.py | 67 ++++++++++++++++++- .../types/blueprint_preview_params.py | 67 ++++++++++++++++++- src/runloop_api_client/types/devbox_view.py | 3 +- .../types/shared/agent_source.py | 15 ++++- .../types/shared_params/agent_source.py | 15 ++++- tests/api_resources/test_agents.py | 18 +++-- tests/api_resources/test_blueprints.py | 48 +++++++++++++ 10 files changed, 329 insertions(+), 18 deletions(-) diff --git a/.stats.yml b/.stats.yml index a331aec18..2ee72efab 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-9f8a6310d4d9c36d386f5afe17cfc891d05d5911204574645f9f90d9b6864a00.yml -openapi_spec_hash: 3fc0a807bb8d728abe3b7eaff0b9f7ec +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-779f7a8b0be0c3d2d095c6256d35c5dbb4c28d049f3541384c51a8c215e8a87a.yml +openapi_spec_hash: b4ebc80eeaf4e5b3bcd53c30433cf35e config_hash: 2363f563f42501d2b1587a4f64bdccaf diff --git a/src/runloop_api_client/resources/blueprints.py b/src/runloop_api_client/resources/blueprints.py index fb0708314..8f2672da8 100644 --- a/src/runloop_api_client/resources/blueprints.py +++ b/src/runloop_api_client/resources/blueprints.py @@ -131,10 +131,12 @@ def create( base_blueprint_id: Optional[str] | Omit = omit, base_blueprint_name: Optional[str] | Omit = omit, build_args: Optional[Dict[str, str]] | Omit = omit, + build_contexts: Optional[Dict[str, blueprint_create_params.BuildContexts]] | Omit = omit, code_mounts: Optional[Iterable[CodeMountParameters]] | Omit = omit, dockerfile: Optional[str] | Omit = omit, file_mounts: Optional[Dict[str, str]] | Omit = omit, launch_parameters: Optional[LaunchParameters] | Omit = omit, + local_build_context: Optional[blueprint_create_params.LocalBuildContext] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, secrets: Optional[Dict[str, str]] | Omit = omit, services: Optional[Iterable[blueprint_create_params.Service]] | Omit = omit, @@ -167,6 +169,11 @@ def create( build_args: (Optional) Arbitrary Docker build args to pass during build. + build_contexts: (Optional) Map of named Docker build contexts. Keys are context names, values + are typed context definitions (object or http). See Docker buildx additional + contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + code_mounts: A list of code mounts to be included in the Blueprint. dockerfile: Dockerfile contents to be used to build the Blueprint. @@ -175,6 +182,8 @@ def create( launch_parameters: Parameters to configure your Devbox at launch time. + local_build_context: (Optional) Local build context stored in object-storage. + metadata: (Optional) User defined metadata for the Blueprint. secrets: (Optional) Map of mount IDs/environment variable names to secret names. Secrets @@ -210,10 +219,12 @@ def create( "base_blueprint_id": base_blueprint_id, "base_blueprint_name": base_blueprint_name, "build_args": build_args, + "build_contexts": build_contexts, "code_mounts": code_mounts, "dockerfile": dockerfile, "file_mounts": file_mounts, "launch_parameters": launch_parameters, + "local_build_context": local_build_context, "metadata": metadata, "secrets": secrets, "services": services, @@ -638,10 +649,12 @@ def preview( base_blueprint_id: Optional[str] | Omit = omit, base_blueprint_name: Optional[str] | Omit = omit, build_args: Optional[Dict[str, str]] | Omit = omit, + build_contexts: Optional[Dict[str, blueprint_preview_params.BuildContexts]] | Omit = omit, code_mounts: Optional[Iterable[CodeMountParameters]] | Omit = omit, dockerfile: Optional[str] | Omit = omit, file_mounts: Optional[Dict[str, str]] | Omit = omit, launch_parameters: Optional[LaunchParameters] | Omit = omit, + local_build_context: Optional[blueprint_preview_params.LocalBuildContext] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, secrets: Optional[Dict[str, str]] | Omit = omit, services: Optional[Iterable[blueprint_preview_params.Service]] | Omit = omit, @@ -672,6 +685,11 @@ def preview( build_args: (Optional) Arbitrary Docker build args to pass during build. + build_contexts: (Optional) Map of named Docker build contexts. Keys are context names, values + are typed context definitions (object or http). See Docker buildx additional + contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + code_mounts: A list of code mounts to be included in the Blueprint. dockerfile: Dockerfile contents to be used to build the Blueprint. @@ -680,6 +698,8 @@ def preview( launch_parameters: Parameters to configure your Devbox at launch time. + local_build_context: (Optional) Local build context stored in object-storage. + metadata: (Optional) User defined metadata for the Blueprint. secrets: (Optional) Map of mount IDs/environment variable names to secret names. Secrets @@ -711,10 +731,12 @@ def preview( "base_blueprint_id": base_blueprint_id, "base_blueprint_name": base_blueprint_name, "build_args": build_args, + "build_contexts": build_contexts, "code_mounts": code_mounts, "dockerfile": dockerfile, "file_mounts": file_mounts, "launch_parameters": launch_parameters, + "local_build_context": local_build_context, "metadata": metadata, "secrets": secrets, "services": services, @@ -760,10 +782,12 @@ async def create( base_blueprint_id: Optional[str] | Omit = omit, base_blueprint_name: Optional[str] | Omit = omit, build_args: Optional[Dict[str, str]] | Omit = omit, + build_contexts: Optional[Dict[str, blueprint_create_params.BuildContexts]] | Omit = omit, code_mounts: Optional[Iterable[CodeMountParameters]] | Omit = omit, dockerfile: Optional[str] | Omit = omit, file_mounts: Optional[Dict[str, str]] | Omit = omit, launch_parameters: Optional[LaunchParameters] | Omit = omit, + local_build_context: Optional[blueprint_create_params.LocalBuildContext] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, secrets: Optional[Dict[str, str]] | Omit = omit, services: Optional[Iterable[blueprint_create_params.Service]] | Omit = omit, @@ -796,6 +820,11 @@ async def create( build_args: (Optional) Arbitrary Docker build args to pass during build. + build_contexts: (Optional) Map of named Docker build contexts. Keys are context names, values + are typed context definitions (object or http). See Docker buildx additional + contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + code_mounts: A list of code mounts to be included in the Blueprint. dockerfile: Dockerfile contents to be used to build the Blueprint. @@ -804,6 +833,8 @@ async def create( launch_parameters: Parameters to configure your Devbox at launch time. + local_build_context: (Optional) Local build context stored in object-storage. + metadata: (Optional) User defined metadata for the Blueprint. secrets: (Optional) Map of mount IDs/environment variable names to secret names. Secrets @@ -839,10 +870,12 @@ async def create( "base_blueprint_id": base_blueprint_id, "base_blueprint_name": base_blueprint_name, "build_args": build_args, + "build_contexts": build_contexts, "code_mounts": code_mounts, "dockerfile": dockerfile, "file_mounts": file_mounts, "launch_parameters": launch_parameters, + "local_build_context": local_build_context, "metadata": metadata, "secrets": secrets, "services": services, @@ -1267,10 +1300,12 @@ async def preview( base_blueprint_id: Optional[str] | Omit = omit, base_blueprint_name: Optional[str] | Omit = omit, build_args: Optional[Dict[str, str]] | Omit = omit, + build_contexts: Optional[Dict[str, blueprint_preview_params.BuildContexts]] | Omit = omit, code_mounts: Optional[Iterable[CodeMountParameters]] | Omit = omit, dockerfile: Optional[str] | Omit = omit, file_mounts: Optional[Dict[str, str]] | Omit = omit, launch_parameters: Optional[LaunchParameters] | Omit = omit, + local_build_context: Optional[blueprint_preview_params.LocalBuildContext] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, secrets: Optional[Dict[str, str]] | Omit = omit, services: Optional[Iterable[blueprint_preview_params.Service]] | Omit = omit, @@ -1301,6 +1336,11 @@ async def preview( build_args: (Optional) Arbitrary Docker build args to pass during build. + build_contexts: (Optional) Map of named Docker build contexts. Keys are context names, values + are typed context definitions (object or http). See Docker buildx additional + contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + code_mounts: A list of code mounts to be included in the Blueprint. dockerfile: Dockerfile contents to be used to build the Blueprint. @@ -1309,6 +1349,8 @@ async def preview( launch_parameters: Parameters to configure your Devbox at launch time. + local_build_context: (Optional) Local build context stored in object-storage. + metadata: (Optional) User defined metadata for the Blueprint. secrets: (Optional) Map of mount IDs/environment variable names to secret names. Secrets @@ -1340,10 +1382,12 @@ async def preview( "base_blueprint_id": base_blueprint_id, "base_blueprint_name": base_blueprint_name, "build_args": build_args, + "build_contexts": build_contexts, "code_mounts": code_mounts, "dockerfile": dockerfile, "file_mounts": file_mounts, "launch_parameters": launch_parameters, + "local_build_context": local_build_context, "metadata": metadata, "secrets": secrets, "services": services, diff --git a/src/runloop_api_client/types/blueprint_build_parameters.py b/src/runloop_api_client/types/blueprint_build_parameters.py index 63a92f146..25bac19d7 100644 --- a/src/runloop_api_client/types/blueprint_build_parameters.py +++ b/src/runloop_api_client/types/blueprint_build_parameters.py @@ -1,12 +1,65 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, List, Optional +from typing_extensions import Literal from .._models import BaseModel from .shared.launch_parameters import LaunchParameters from .shared.code_mount_parameters import CodeMountParameters -__all__ = ["BlueprintBuildParameters", "Service", "ServiceCredentials"] +__all__ = [ + "BlueprintBuildParameters", + "BuildContexts", + "BuildContextsHTTP", + "BuildContextsObject", + "LocalBuildContext", + "LocalBuildContextHTTP", + "LocalBuildContextObject", + "Service", + "ServiceCredentials", +] + + +class BuildContextsHTTP(BaseModel): + url: str + """HTTP(S) URL to a tarball or directory to use as context.""" + + +class BuildContextsObject(BaseModel): + object_id: str + """Handle for a Runloop stored object to use as context.""" + + +class BuildContexts(BaseModel): + type: Literal["OBJECT", "HTTP"] + """Type of the context. Supported values: object, http""" + + http: Optional[BuildContextsHTTP] = None + """HTTP(S) context parameters.""" + + object: Optional[BuildContextsObject] = None + """Object context parameters (named build context).""" + + +class LocalBuildContextHTTP(BaseModel): + url: str + """HTTP(S) URL to a tarball or directory to use as context.""" + + +class LocalBuildContextObject(BaseModel): + object_id: str + """Handle for a Runloop stored object to use as context.""" + + +class LocalBuildContext(BaseModel): + type: Literal["OBJECT", "HTTP"] + """Type of the context. Supported values: object, http""" + + http: Optional[LocalBuildContextHTTP] = None + """HTTP(S) context parameters.""" + + object: Optional[LocalBuildContextObject] = None + """Object context parameters (named build context).""" class ServiceCredentials(BaseModel): @@ -61,6 +114,14 @@ class BlueprintBuildParameters(BaseModel): build_args: Optional[Dict[str, str]] = None """(Optional) Arbitrary Docker build args to pass during build.""" + build_contexts: Optional[Dict[str, BuildContexts]] = None + """(Optional) Map of named Docker build contexts. + + Keys are context names, values are typed context definitions (object or http). + See Docker buildx additional contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + """ + code_mounts: Optional[List[CodeMountParameters]] = None """A list of code mounts to be included in the Blueprint.""" @@ -73,6 +134,9 @@ class BlueprintBuildParameters(BaseModel): launch_parameters: Optional[LaunchParameters] = None """Parameters to configure your Devbox at launch time.""" + local_build_context: Optional[LocalBuildContext] = None + """(Optional) Local build context stored in object-storage.""" + metadata: Optional[Dict[str, str]] = None """(Optional) User defined metadata for the Blueprint.""" diff --git a/src/runloop_api_client/types/blueprint_create_params.py b/src/runloop_api_client/types/blueprint_create_params.py index 9d0a15848..d2d94a3a8 100644 --- a/src/runloop_api_client/types/blueprint_create_params.py +++ b/src/runloop_api_client/types/blueprint_create_params.py @@ -3,13 +3,23 @@ from __future__ import annotations from typing import Dict, Iterable, Optional -from typing_extensions import Required, TypedDict +from typing_extensions import Literal, Required, TypedDict from .._types import SequenceNotStr from .shared_params.launch_parameters import LaunchParameters from .shared_params.code_mount_parameters import CodeMountParameters -__all__ = ["BlueprintCreateParams", "Service", "ServiceCredentials"] +__all__ = [ + "BlueprintCreateParams", + "BuildContexts", + "BuildContextsHTTP", + "BuildContextsObject", + "LocalBuildContext", + "LocalBuildContextHTTP", + "LocalBuildContextObject", + "Service", + "ServiceCredentials", +] class BlueprintCreateParams(TypedDict, total=False): @@ -33,6 +43,14 @@ class BlueprintCreateParams(TypedDict, total=False): build_args: Optional[Dict[str, str]] """(Optional) Arbitrary Docker build args to pass during build.""" + build_contexts: Optional[Dict[str, BuildContexts]] + """(Optional) Map of named Docker build contexts. + + Keys are context names, values are typed context definitions (object or http). + See Docker buildx additional contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + """ + code_mounts: Optional[Iterable[CodeMountParameters]] """A list of code mounts to be included in the Blueprint.""" @@ -45,6 +63,9 @@ class BlueprintCreateParams(TypedDict, total=False): launch_parameters: Optional[LaunchParameters] """Parameters to configure your Devbox at launch time.""" + local_build_context: Optional[LocalBuildContext] + """(Optional) Local build context stored in object-storage.""" + metadata: Optional[Dict[str, str]] """(Optional) User defined metadata for the Blueprint.""" @@ -67,6 +88,48 @@ class BlueprintCreateParams(TypedDict, total=False): """A list of commands to run to set up your system.""" +class BuildContextsHTTP(TypedDict, total=False): + url: Required[str] + """HTTP(S) URL to a tarball or directory to use as context.""" + + +class BuildContextsObject(TypedDict, total=False): + object_id: Required[str] + """Handle for a Runloop stored object to use as context.""" + + +class BuildContexts(TypedDict, total=False): + type: Required[Literal["OBJECT", "HTTP"]] + """Type of the context. Supported values: object, http""" + + http: Optional[BuildContextsHTTP] + """HTTP(S) context parameters.""" + + object: Optional[BuildContextsObject] + """Object context parameters (named build context).""" + + +class LocalBuildContextHTTP(TypedDict, total=False): + url: Required[str] + """HTTP(S) URL to a tarball or directory to use as context.""" + + +class LocalBuildContextObject(TypedDict, total=False): + object_id: Required[str] + """Handle for a Runloop stored object to use as context.""" + + +class LocalBuildContext(TypedDict, total=False): + type: Required[Literal["OBJECT", "HTTP"]] + """Type of the context. Supported values: object, http""" + + http: Optional[LocalBuildContextHTTP] + """HTTP(S) context parameters.""" + + object: Optional[LocalBuildContextObject] + """Object context parameters (named build context).""" + + class ServiceCredentials(TypedDict, total=False): password: Required[str] """The password of the container service.""" diff --git a/src/runloop_api_client/types/blueprint_preview_params.py b/src/runloop_api_client/types/blueprint_preview_params.py index 5c1e257f2..c58487655 100644 --- a/src/runloop_api_client/types/blueprint_preview_params.py +++ b/src/runloop_api_client/types/blueprint_preview_params.py @@ -3,13 +3,23 @@ from __future__ import annotations from typing import Dict, Iterable, Optional -from typing_extensions import Required, TypedDict +from typing_extensions import Literal, Required, TypedDict from .._types import SequenceNotStr from .shared_params.launch_parameters import LaunchParameters from .shared_params.code_mount_parameters import CodeMountParameters -__all__ = ["BlueprintPreviewParams", "Service", "ServiceCredentials"] +__all__ = [ + "BlueprintPreviewParams", + "BuildContexts", + "BuildContextsHTTP", + "BuildContextsObject", + "LocalBuildContext", + "LocalBuildContextHTTP", + "LocalBuildContextObject", + "Service", + "ServiceCredentials", +] class BlueprintPreviewParams(TypedDict, total=False): @@ -33,6 +43,14 @@ class BlueprintPreviewParams(TypedDict, total=False): build_args: Optional[Dict[str, str]] """(Optional) Arbitrary Docker build args to pass during build.""" + build_contexts: Optional[Dict[str, BuildContexts]] + """(Optional) Map of named Docker build contexts. + + Keys are context names, values are typed context definitions (object or http). + See Docker buildx additional contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + """ + code_mounts: Optional[Iterable[CodeMountParameters]] """A list of code mounts to be included in the Blueprint.""" @@ -45,6 +63,9 @@ class BlueprintPreviewParams(TypedDict, total=False): launch_parameters: Optional[LaunchParameters] """Parameters to configure your Devbox at launch time.""" + local_build_context: Optional[LocalBuildContext] + """(Optional) Local build context stored in object-storage.""" + metadata: Optional[Dict[str, str]] """(Optional) User defined metadata for the Blueprint.""" @@ -67,6 +88,48 @@ class BlueprintPreviewParams(TypedDict, total=False): """A list of commands to run to set up your system.""" +class BuildContextsHTTP(TypedDict, total=False): + url: Required[str] + """HTTP(S) URL to a tarball or directory to use as context.""" + + +class BuildContextsObject(TypedDict, total=False): + object_id: Required[str] + """Handle for a Runloop stored object to use as context.""" + + +class BuildContexts(TypedDict, total=False): + type: Required[Literal["OBJECT", "HTTP"]] + """Type of the context. Supported values: object, http""" + + http: Optional[BuildContextsHTTP] + """HTTP(S) context parameters.""" + + object: Optional[BuildContextsObject] + """Object context parameters (named build context).""" + + +class LocalBuildContextHTTP(TypedDict, total=False): + url: Required[str] + """HTTP(S) URL to a tarball or directory to use as context.""" + + +class LocalBuildContextObject(TypedDict, total=False): + object_id: Required[str] + """Handle for a Runloop stored object to use as context.""" + + +class LocalBuildContext(TypedDict, total=False): + type: Required[Literal["OBJECT", "HTTP"]] + """Type of the context. Supported values: object, http""" + + http: Optional[LocalBuildContextHTTP] + """HTTP(S) context parameters.""" + + object: Optional[LocalBuildContextObject] + """Object context parameters (named build context).""" + + class ServiceCredentials(TypedDict, total=False): password: Required[str] """The password of the container service.""" diff --git a/src/runloop_api_client/types/devbox_view.py b/src/runloop_api_client/types/devbox_view.py index 68240f25d..007af6575 100644 --- a/src/runloop_api_client/types/devbox_view.py +++ b/src/runloop_api_client/types/devbox_view.py @@ -18,7 +18,7 @@ class StateTransition(BaseModel): provisioning: Runloop is allocating and booting the necessary infrastructure resources. initializing: Runloop defined boot scripts are running to enable the environment for interaction. running: The Devbox is ready for interaction. - suspending: The Devbox disk is being snaphsotted and as part of suspension. + suspending: The Devbox disk is being snapshotted as part of suspension. suspended: The Devbox disk is saved and no more active compute is being used for the Devbox. resuming: The Devbox disk is being loaded as part of booting a suspended Devbox. failure: The Devbox failed as part of booting or running user @@ -27,6 +27,7 @@ class StateTransition(BaseModel): """ transition_time_ms: Optional[object] = None + """The time the status change occurred""" class DevboxView(BaseModel): diff --git a/src/runloop_api_client/types/shared/agent_source.py b/src/runloop_api_client/types/shared/agent_source.py index 7dc4958d2..25bcbbc1d 100644 --- a/src/runloop_api_client/types/shared/agent_source.py +++ b/src/runloop_api_client/types/shared/agent_source.py @@ -11,17 +11,20 @@ class Git(BaseModel): repository: str """Git repository URL""" + agent_setup: Optional[List[str]] = None + """Setup commands to run after cloning""" + ref: Optional[str] = None """Optional Git ref (branch/tag/commit), defaults to main/HEAD""" - setup: Optional[List[str]] = None - """Setup commands to run after cloning""" - class Npm(BaseModel): package_name: str """NPM package name""" + agent_setup: Optional[List[str]] = None + """Setup commands to run after installation""" + npm_version: Optional[str] = None """NPM version constraint""" @@ -33,11 +36,17 @@ class Object(BaseModel): object_id: str """Object ID""" + agent_setup: Optional[List[str]] = None + """Setup commands to run after unpacking""" + class Pip(BaseModel): package_name: str """Pip package name""" + agent_setup: Optional[List[str]] = None + """Setup commands to run after installation""" + pip_version: Optional[str] = None """Pip version constraint""" diff --git a/src/runloop_api_client/types/shared_params/agent_source.py b/src/runloop_api_client/types/shared_params/agent_source.py index 2c1ce71d3..9f5a50845 100644 --- a/src/runloop_api_client/types/shared_params/agent_source.py +++ b/src/runloop_api_client/types/shared_params/agent_source.py @@ -14,17 +14,20 @@ class Git(TypedDict, total=False): repository: Required[str] """Git repository URL""" + agent_setup: Optional[SequenceNotStr[str]] + """Setup commands to run after cloning""" + ref: Optional[str] """Optional Git ref (branch/tag/commit), defaults to main/HEAD""" - setup: Optional[SequenceNotStr[str]] - """Setup commands to run after cloning""" - class Npm(TypedDict, total=False): package_name: Required[str] """NPM package name""" + agent_setup: Optional[SequenceNotStr[str]] + """Setup commands to run after installation""" + npm_version: Optional[str] """NPM version constraint""" @@ -36,11 +39,17 @@ class Object(TypedDict, total=False): object_id: Required[str] """Object ID""" + agent_setup: Optional[SequenceNotStr[str]] + """Setup commands to run after unpacking""" + class Pip(TypedDict, total=False): package_name: Required[str] """Pip package name""" + agent_setup: Optional[SequenceNotStr[str]] + """Setup commands to run after installation""" + pip_version: Optional[str] """Pip version constraint""" diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py index 596338b85..6f8096491 100644 --- a/tests/api_resources/test_agents.py +++ b/tests/api_resources/test_agents.py @@ -33,17 +33,22 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: "type": "type", "git": { "repository": "repository", + "agent_setup": ["string"], "ref": "ref", - "setup": ["string"], }, "npm": { "package_name": "package_name", + "agent_setup": ["string"], "npm_version": "npm_version", "registry_url": "registry_url", }, - "object": {"object_id": "object_id"}, + "object": { + "object_id": "object_id", + "agent_setup": ["string"], + }, "pip": { "package_name": "package_name", + "agent_setup": ["string"], "pip_version": "pip_version", "registry_url": "registry_url", }, @@ -170,17 +175,22 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - "type": "type", "git": { "repository": "repository", + "agent_setup": ["string"], "ref": "ref", - "setup": ["string"], }, "npm": { "package_name": "package_name", + "agent_setup": ["string"], "npm_version": "npm_version", "registry_url": "registry_url", }, - "object": {"object_id": "object_id"}, + "object": { + "object_id": "object_id", + "agent_setup": ["string"], + }, "pip": { "package_name": "package_name", + "agent_setup": ["string"], "pip_version": "pip_version", "registry_url": "registry_url", }, diff --git a/tests/api_resources/test_blueprints.py b/tests/api_resources/test_blueprints.py index 9826f0b67..5a28691d0 100644 --- a/tests/api_resources/test_blueprints.py +++ b/tests/api_resources/test_blueprints.py @@ -36,6 +36,13 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: base_blueprint_id="base_blueprint_id", base_blueprint_name="base_blueprint_name", build_args={"foo": "string"}, + build_contexts={ + "foo": { + "type": "OBJECT", + "http": {"url": "url"}, + "object": {"object_id": "object_id"}, + } + }, code_mounts=[ { "repo_name": "repo_name", @@ -65,6 +72,11 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: "username": "username", }, }, + local_build_context={ + "type": "OBJECT", + "http": {"url": "url"}, + "object": {"object_id": "object_id"}, + }, metadata={"foo": "string"}, secrets={"foo": "string"}, services=[ @@ -394,6 +406,13 @@ def test_method_preview_with_all_params(self, client: Runloop) -> None: base_blueprint_id="base_blueprint_id", base_blueprint_name="base_blueprint_name", build_args={"foo": "string"}, + build_contexts={ + "foo": { + "type": "OBJECT", + "http": {"url": "url"}, + "object": {"object_id": "object_id"}, + } + }, code_mounts=[ { "repo_name": "repo_name", @@ -423,6 +442,11 @@ def test_method_preview_with_all_params(self, client: Runloop) -> None: "username": "username", }, }, + local_build_context={ + "type": "OBJECT", + "http": {"url": "url"}, + "object": {"object_id": "object_id"}, + }, metadata={"foo": "string"}, secrets={"foo": "string"}, services=[ @@ -486,6 +510,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - base_blueprint_id="base_blueprint_id", base_blueprint_name="base_blueprint_name", build_args={"foo": "string"}, + build_contexts={ + "foo": { + "type": "OBJECT", + "http": {"url": "url"}, + "object": {"object_id": "object_id"}, + } + }, code_mounts=[ { "repo_name": "repo_name", @@ -515,6 +546,11 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - "username": "username", }, }, + local_build_context={ + "type": "OBJECT", + "http": {"url": "url"}, + "object": {"object_id": "object_id"}, + }, metadata={"foo": "string"}, secrets={"foo": "string"}, services=[ @@ -844,6 +880,13 @@ async def test_method_preview_with_all_params(self, async_client: AsyncRunloop) base_blueprint_id="base_blueprint_id", base_blueprint_name="base_blueprint_name", build_args={"foo": "string"}, + build_contexts={ + "foo": { + "type": "OBJECT", + "http": {"url": "url"}, + "object": {"object_id": "object_id"}, + } + }, code_mounts=[ { "repo_name": "repo_name", @@ -873,6 +916,11 @@ async def test_method_preview_with_all_params(self, async_client: AsyncRunloop) "username": "username", }, }, + local_build_context={ + "type": "OBJECT", + "http": {"url": "url"}, + "object": {"object_id": "object_id"}, + }, metadata={"foo": "string"}, secrets={"foo": "string"}, services=[ From 07a0b8c1825c78b0a6c30c2d374b82aced2f97d5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 00:17:30 +0000 Subject: [PATCH 09/11] chore(package): drop Python 3.8 support From c87b986e558ad3a0cca2ad1609b9833baf86fef8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 00:22:13 +0000 Subject: [PATCH 10/11] feat(blueprints): Cleanup the BuildContext API (#6407)\n\nTest --- .stats.yml | 4 +- .../resources/blueprints.py | 80 +++++++++---------- src/runloop_api_client/types/agent_view.py | 3 + .../types/blueprint_build_parameters.py | 72 ++++------------- .../types/blueprint_create_params.py | 72 ++++------------- .../types/blueprint_preview_params.py | 72 ++++------------- .../types/shared/agent_mount_parameters.py | 11 ++- .../shared_params/agent_mount_parameters.py | 11 ++- tests/api_resources/test_blueprints.py | 80 +++++++++---------- 9 files changed, 150 insertions(+), 255 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2ee72efab..33e553ff2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-779f7a8b0be0c3d2d095c6256d35c5dbb4c28d049f3541384c51a8c215e8a87a.yml -openapi_spec_hash: b4ebc80eeaf4e5b3bcd53c30433cf35e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-5f33221208c1febba343daf570f73a5086f150a9b128df045ebddc3fe2c86607.yml +openapi_spec_hash: 0aea07130ddbe43a665a13a68231e2ca config_hash: 2363f563f42501d2b1587a4f64bdccaf diff --git a/src/runloop_api_client/resources/blueprints.py b/src/runloop_api_client/resources/blueprints.py index 8f2672da8..60d228522 100644 --- a/src/runloop_api_client/resources/blueprints.py +++ b/src/runloop_api_client/resources/blueprints.py @@ -131,13 +131,13 @@ def create( base_blueprint_id: Optional[str] | Omit = omit, base_blueprint_name: Optional[str] | Omit = omit, build_args: Optional[Dict[str, str]] | Omit = omit, - build_contexts: Optional[Dict[str, blueprint_create_params.BuildContexts]] | Omit = omit, + build_context: Optional[blueprint_create_params.BuildContext] | Omit = omit, code_mounts: Optional[Iterable[CodeMountParameters]] | Omit = omit, dockerfile: Optional[str] | Omit = omit, file_mounts: Optional[Dict[str, str]] | Omit = omit, launch_parameters: Optional[LaunchParameters] | Omit = omit, - local_build_context: Optional[blueprint_create_params.LocalBuildContext] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, + named_build_contexts: Optional[Dict[str, blueprint_create_params.NamedBuildContexts]] | Omit = omit, secrets: Optional[Dict[str, str]] | Omit = omit, services: Optional[Iterable[blueprint_create_params.Service]] | Omit = omit, system_setup_commands: Optional[SequenceNotStr[str]] | Omit = omit, @@ -169,10 +169,7 @@ def create( build_args: (Optional) Arbitrary Docker build args to pass during build. - build_contexts: (Optional) Map of named Docker build contexts. Keys are context names, values - are typed context definitions (object or http). See Docker buildx additional - contexts for details: - https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + build_context: A build context backed by an Object. code_mounts: A list of code mounts to be included in the Blueprint. @@ -182,10 +179,13 @@ def create( launch_parameters: Parameters to configure your Devbox at launch time. - local_build_context: (Optional) Local build context stored in object-storage. - metadata: (Optional) User defined metadata for the Blueprint. + named_build_contexts: (Optional) Map of named build contexts to attach to the Blueprint build, where + the keys are the name used when referencing the contexts in a Dockerfile. See + Docker buildx additional contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + secrets: (Optional) Map of mount IDs/environment variable names to secret names. Secrets will be available to commands during the build. Secrets are NOT stored in the blueprint image. Example: {"DB_PASS": "DATABASE_PASSWORD"} makes the secret @@ -219,13 +219,13 @@ def create( "base_blueprint_id": base_blueprint_id, "base_blueprint_name": base_blueprint_name, "build_args": build_args, - "build_contexts": build_contexts, + "build_context": build_context, "code_mounts": code_mounts, "dockerfile": dockerfile, "file_mounts": file_mounts, "launch_parameters": launch_parameters, - "local_build_context": local_build_context, "metadata": metadata, + "named_build_contexts": named_build_contexts, "secrets": secrets, "services": services, "system_setup_commands": system_setup_commands, @@ -649,13 +649,13 @@ def preview( base_blueprint_id: Optional[str] | Omit = omit, base_blueprint_name: Optional[str] | Omit = omit, build_args: Optional[Dict[str, str]] | Omit = omit, - build_contexts: Optional[Dict[str, blueprint_preview_params.BuildContexts]] | Omit = omit, + build_context: Optional[blueprint_preview_params.BuildContext] | Omit = omit, code_mounts: Optional[Iterable[CodeMountParameters]] | Omit = omit, dockerfile: Optional[str] | Omit = omit, file_mounts: Optional[Dict[str, str]] | Omit = omit, launch_parameters: Optional[LaunchParameters] | Omit = omit, - local_build_context: Optional[blueprint_preview_params.LocalBuildContext] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, + named_build_contexts: Optional[Dict[str, blueprint_preview_params.NamedBuildContexts]] | Omit = omit, secrets: Optional[Dict[str, str]] | Omit = omit, services: Optional[Iterable[blueprint_preview_params.Service]] | Omit = omit, system_setup_commands: Optional[SequenceNotStr[str]] | Omit = omit, @@ -685,10 +685,7 @@ def preview( build_args: (Optional) Arbitrary Docker build args to pass during build. - build_contexts: (Optional) Map of named Docker build contexts. Keys are context names, values - are typed context definitions (object or http). See Docker buildx additional - contexts for details: - https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + build_context: A build context backed by an Object. code_mounts: A list of code mounts to be included in the Blueprint. @@ -698,10 +695,13 @@ def preview( launch_parameters: Parameters to configure your Devbox at launch time. - local_build_context: (Optional) Local build context stored in object-storage. - metadata: (Optional) User defined metadata for the Blueprint. + named_build_contexts: (Optional) Map of named build contexts to attach to the Blueprint build, where + the keys are the name used when referencing the contexts in a Dockerfile. See + Docker buildx additional contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + secrets: (Optional) Map of mount IDs/environment variable names to secret names. Secrets will be available to commands during the build. Secrets are NOT stored in the blueprint image. Example: {"DB_PASS": "DATABASE_PASSWORD"} makes the secret @@ -731,13 +731,13 @@ def preview( "base_blueprint_id": base_blueprint_id, "base_blueprint_name": base_blueprint_name, "build_args": build_args, - "build_contexts": build_contexts, + "build_context": build_context, "code_mounts": code_mounts, "dockerfile": dockerfile, "file_mounts": file_mounts, "launch_parameters": launch_parameters, - "local_build_context": local_build_context, "metadata": metadata, + "named_build_contexts": named_build_contexts, "secrets": secrets, "services": services, "system_setup_commands": system_setup_commands, @@ -782,13 +782,13 @@ async def create( base_blueprint_id: Optional[str] | Omit = omit, base_blueprint_name: Optional[str] | Omit = omit, build_args: Optional[Dict[str, str]] | Omit = omit, - build_contexts: Optional[Dict[str, blueprint_create_params.BuildContexts]] | Omit = omit, + build_context: Optional[blueprint_create_params.BuildContext] | Omit = omit, code_mounts: Optional[Iterable[CodeMountParameters]] | Omit = omit, dockerfile: Optional[str] | Omit = omit, file_mounts: Optional[Dict[str, str]] | Omit = omit, launch_parameters: Optional[LaunchParameters] | Omit = omit, - local_build_context: Optional[blueprint_create_params.LocalBuildContext] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, + named_build_contexts: Optional[Dict[str, blueprint_create_params.NamedBuildContexts]] | Omit = omit, secrets: Optional[Dict[str, str]] | Omit = omit, services: Optional[Iterable[blueprint_create_params.Service]] | Omit = omit, system_setup_commands: Optional[SequenceNotStr[str]] | Omit = omit, @@ -820,10 +820,7 @@ async def create( build_args: (Optional) Arbitrary Docker build args to pass during build. - build_contexts: (Optional) Map of named Docker build contexts. Keys are context names, values - are typed context definitions (object or http). See Docker buildx additional - contexts for details: - https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + build_context: A build context backed by an Object. code_mounts: A list of code mounts to be included in the Blueprint. @@ -833,10 +830,13 @@ async def create( launch_parameters: Parameters to configure your Devbox at launch time. - local_build_context: (Optional) Local build context stored in object-storage. - metadata: (Optional) User defined metadata for the Blueprint. + named_build_contexts: (Optional) Map of named build contexts to attach to the Blueprint build, where + the keys are the name used when referencing the contexts in a Dockerfile. See + Docker buildx additional contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + secrets: (Optional) Map of mount IDs/environment variable names to secret names. Secrets will be available to commands during the build. Secrets are NOT stored in the blueprint image. Example: {"DB_PASS": "DATABASE_PASSWORD"} makes the secret @@ -870,13 +870,13 @@ async def create( "base_blueprint_id": base_blueprint_id, "base_blueprint_name": base_blueprint_name, "build_args": build_args, - "build_contexts": build_contexts, + "build_context": build_context, "code_mounts": code_mounts, "dockerfile": dockerfile, "file_mounts": file_mounts, "launch_parameters": launch_parameters, - "local_build_context": local_build_context, "metadata": metadata, + "named_build_contexts": named_build_contexts, "secrets": secrets, "services": services, "system_setup_commands": system_setup_commands, @@ -1300,13 +1300,13 @@ async def preview( base_blueprint_id: Optional[str] | Omit = omit, base_blueprint_name: Optional[str] | Omit = omit, build_args: Optional[Dict[str, str]] | Omit = omit, - build_contexts: Optional[Dict[str, blueprint_preview_params.BuildContexts]] | Omit = omit, + build_context: Optional[blueprint_preview_params.BuildContext] | Omit = omit, code_mounts: Optional[Iterable[CodeMountParameters]] | Omit = omit, dockerfile: Optional[str] | Omit = omit, file_mounts: Optional[Dict[str, str]] | Omit = omit, launch_parameters: Optional[LaunchParameters] | Omit = omit, - local_build_context: Optional[blueprint_preview_params.LocalBuildContext] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, + named_build_contexts: Optional[Dict[str, blueprint_preview_params.NamedBuildContexts]] | Omit = omit, secrets: Optional[Dict[str, str]] | Omit = omit, services: Optional[Iterable[blueprint_preview_params.Service]] | Omit = omit, system_setup_commands: Optional[SequenceNotStr[str]] | Omit = omit, @@ -1336,10 +1336,7 @@ async def preview( build_args: (Optional) Arbitrary Docker build args to pass during build. - build_contexts: (Optional) Map of named Docker build contexts. Keys are context names, values - are typed context definitions (object or http). See Docker buildx additional - contexts for details: - https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + build_context: A build context backed by an Object. code_mounts: A list of code mounts to be included in the Blueprint. @@ -1349,10 +1346,13 @@ async def preview( launch_parameters: Parameters to configure your Devbox at launch time. - local_build_context: (Optional) Local build context stored in object-storage. - metadata: (Optional) User defined metadata for the Blueprint. + named_build_contexts: (Optional) Map of named build contexts to attach to the Blueprint build, where + the keys are the name used when referencing the contexts in a Dockerfile. See + Docker buildx additional contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + secrets: (Optional) Map of mount IDs/environment variable names to secret names. Secrets will be available to commands during the build. Secrets are NOT stored in the blueprint image. Example: {"DB_PASS": "DATABASE_PASSWORD"} makes the secret @@ -1382,13 +1382,13 @@ async def preview( "base_blueprint_id": base_blueprint_id, "base_blueprint_name": base_blueprint_name, "build_args": build_args, - "build_contexts": build_contexts, + "build_context": build_context, "code_mounts": code_mounts, "dockerfile": dockerfile, "file_mounts": file_mounts, "launch_parameters": launch_parameters, - "local_build_context": local_build_context, "metadata": metadata, + "named_build_contexts": named_build_contexts, "secrets": secrets, "services": services, "system_setup_commands": system_setup_commands, diff --git a/src/runloop_api_client/types/agent_view.py b/src/runloop_api_client/types/agent_view.py index bf2520a49..77e56d1b8 100644 --- a/src/runloop_api_client/types/agent_view.py +++ b/src/runloop_api_client/types/agent_view.py @@ -12,6 +12,9 @@ class AgentView(BaseModel): id: str """The unique identifier of the Agent.""" + create_time_ms: int + """The creation time of the Agent (Unix timestamp milliseconds).""" + is_public: bool """Whether the Agent is publicly accessible.""" diff --git a/src/runloop_api_client/types/blueprint_build_parameters.py b/src/runloop_api_client/types/blueprint_build_parameters.py index 25bac19d7..cc86468df 100644 --- a/src/runloop_api_client/types/blueprint_build_parameters.py +++ b/src/runloop_api_client/types/blueprint_build_parameters.py @@ -7,59 +7,21 @@ from .shared.launch_parameters import LaunchParameters from .shared.code_mount_parameters import CodeMountParameters -__all__ = [ - "BlueprintBuildParameters", - "BuildContexts", - "BuildContextsHTTP", - "BuildContextsObject", - "LocalBuildContext", - "LocalBuildContextHTTP", - "LocalBuildContextObject", - "Service", - "ServiceCredentials", -] +__all__ = ["BlueprintBuildParameters", "BuildContext", "NamedBuildContexts", "Service", "ServiceCredentials"] -class BuildContextsHTTP(BaseModel): - url: str - """HTTP(S) URL to a tarball or directory to use as context.""" - - -class BuildContextsObject(BaseModel): +class BuildContext(BaseModel): object_id: str - """Handle for a Runloop stored object to use as context.""" - - -class BuildContexts(BaseModel): - type: Literal["OBJECT", "HTTP"] - """Type of the context. Supported values: object, http""" - - http: Optional[BuildContextsHTTP] = None - """HTTP(S) context parameters.""" + """The ID of an object, whose contents are to be used as a build context.""" - object: Optional[BuildContextsObject] = None - """Object context parameters (named build context).""" + type: Literal["object"] -class LocalBuildContextHTTP(BaseModel): - url: str - """HTTP(S) URL to a tarball or directory to use as context.""" - - -class LocalBuildContextObject(BaseModel): +class NamedBuildContexts(BaseModel): object_id: str - """Handle for a Runloop stored object to use as context.""" - - -class LocalBuildContext(BaseModel): - type: Literal["OBJECT", "HTTP"] - """Type of the context. Supported values: object, http""" + """The ID of an object, whose contents are to be used as a build context.""" - http: Optional[LocalBuildContextHTTP] = None - """HTTP(S) context parameters.""" - - object: Optional[LocalBuildContextObject] = None - """Object context parameters (named build context).""" + type: Literal["object"] class ServiceCredentials(BaseModel): @@ -114,13 +76,8 @@ class BlueprintBuildParameters(BaseModel): build_args: Optional[Dict[str, str]] = None """(Optional) Arbitrary Docker build args to pass during build.""" - build_contexts: Optional[Dict[str, BuildContexts]] = None - """(Optional) Map of named Docker build contexts. - - Keys are context names, values are typed context definitions (object or http). - See Docker buildx additional contexts for details: - https://docs.docker.com/reference/cli/docker/buildx/build/#build-context - """ + build_context: Optional[BuildContext] = None + """A build context backed by an Object.""" code_mounts: Optional[List[CodeMountParameters]] = None """A list of code mounts to be included in the Blueprint.""" @@ -134,12 +91,17 @@ class BlueprintBuildParameters(BaseModel): launch_parameters: Optional[LaunchParameters] = None """Parameters to configure your Devbox at launch time.""" - local_build_context: Optional[LocalBuildContext] = None - """(Optional) Local build context stored in object-storage.""" - metadata: Optional[Dict[str, str]] = None """(Optional) User defined metadata for the Blueprint.""" + named_build_contexts: Optional[Dict[str, NamedBuildContexts]] = None + """ + (Optional) Map of named build contexts to attach to the Blueprint build, where + the keys are the name used when referencing the contexts in a Dockerfile. See + Docker buildx additional contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + """ + secrets: Optional[Dict[str, str]] = None """(Optional) Map of mount IDs/environment variable names to secret names. diff --git a/src/runloop_api_client/types/blueprint_create_params.py b/src/runloop_api_client/types/blueprint_create_params.py index d2d94a3a8..a15e6f470 100644 --- a/src/runloop_api_client/types/blueprint_create_params.py +++ b/src/runloop_api_client/types/blueprint_create_params.py @@ -9,17 +9,7 @@ from .shared_params.launch_parameters import LaunchParameters from .shared_params.code_mount_parameters import CodeMountParameters -__all__ = [ - "BlueprintCreateParams", - "BuildContexts", - "BuildContextsHTTP", - "BuildContextsObject", - "LocalBuildContext", - "LocalBuildContextHTTP", - "LocalBuildContextObject", - "Service", - "ServiceCredentials", -] +__all__ = ["BlueprintCreateParams", "BuildContext", "NamedBuildContexts", "Service", "ServiceCredentials"] class BlueprintCreateParams(TypedDict, total=False): @@ -43,13 +33,8 @@ class BlueprintCreateParams(TypedDict, total=False): build_args: Optional[Dict[str, str]] """(Optional) Arbitrary Docker build args to pass during build.""" - build_contexts: Optional[Dict[str, BuildContexts]] - """(Optional) Map of named Docker build contexts. - - Keys are context names, values are typed context definitions (object or http). - See Docker buildx additional contexts for details: - https://docs.docker.com/reference/cli/docker/buildx/build/#build-context - """ + build_context: Optional[BuildContext] + """A build context backed by an Object.""" code_mounts: Optional[Iterable[CodeMountParameters]] """A list of code mounts to be included in the Blueprint.""" @@ -63,12 +48,17 @@ class BlueprintCreateParams(TypedDict, total=False): launch_parameters: Optional[LaunchParameters] """Parameters to configure your Devbox at launch time.""" - local_build_context: Optional[LocalBuildContext] - """(Optional) Local build context stored in object-storage.""" - metadata: Optional[Dict[str, str]] """(Optional) User defined metadata for the Blueprint.""" + named_build_contexts: Optional[Dict[str, NamedBuildContexts]] + """ + (Optional) Map of named build contexts to attach to the Blueprint build, where + the keys are the name used when referencing the contexts in a Dockerfile. See + Docker buildx additional contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + """ + secrets: Optional[Dict[str, str]] """(Optional) Map of mount IDs/environment variable names to secret names. @@ -88,46 +78,18 @@ class BlueprintCreateParams(TypedDict, total=False): """A list of commands to run to set up your system.""" -class BuildContextsHTTP(TypedDict, total=False): - url: Required[str] - """HTTP(S) URL to a tarball or directory to use as context.""" - - -class BuildContextsObject(TypedDict, total=False): +class BuildContext(TypedDict, total=False): object_id: Required[str] - """Handle for a Runloop stored object to use as context.""" - - -class BuildContexts(TypedDict, total=False): - type: Required[Literal["OBJECT", "HTTP"]] - """Type of the context. Supported values: object, http""" - - http: Optional[BuildContextsHTTP] - """HTTP(S) context parameters.""" - - object: Optional[BuildContextsObject] - """Object context parameters (named build context).""" + """The ID of an object, whose contents are to be used as a build context.""" + type: Required[Literal["object"]] -class LocalBuildContextHTTP(TypedDict, total=False): - url: Required[str] - """HTTP(S) URL to a tarball or directory to use as context.""" - -class LocalBuildContextObject(TypedDict, total=False): +class NamedBuildContexts(TypedDict, total=False): object_id: Required[str] - """Handle for a Runloop stored object to use as context.""" - - -class LocalBuildContext(TypedDict, total=False): - type: Required[Literal["OBJECT", "HTTP"]] - """Type of the context. Supported values: object, http""" - - http: Optional[LocalBuildContextHTTP] - """HTTP(S) context parameters.""" + """The ID of an object, whose contents are to be used as a build context.""" - object: Optional[LocalBuildContextObject] - """Object context parameters (named build context).""" + type: Required[Literal["object"]] class ServiceCredentials(TypedDict, total=False): diff --git a/src/runloop_api_client/types/blueprint_preview_params.py b/src/runloop_api_client/types/blueprint_preview_params.py index c58487655..81244126d 100644 --- a/src/runloop_api_client/types/blueprint_preview_params.py +++ b/src/runloop_api_client/types/blueprint_preview_params.py @@ -9,17 +9,7 @@ from .shared_params.launch_parameters import LaunchParameters from .shared_params.code_mount_parameters import CodeMountParameters -__all__ = [ - "BlueprintPreviewParams", - "BuildContexts", - "BuildContextsHTTP", - "BuildContextsObject", - "LocalBuildContext", - "LocalBuildContextHTTP", - "LocalBuildContextObject", - "Service", - "ServiceCredentials", -] +__all__ = ["BlueprintPreviewParams", "BuildContext", "NamedBuildContexts", "Service", "ServiceCredentials"] class BlueprintPreviewParams(TypedDict, total=False): @@ -43,13 +33,8 @@ class BlueprintPreviewParams(TypedDict, total=False): build_args: Optional[Dict[str, str]] """(Optional) Arbitrary Docker build args to pass during build.""" - build_contexts: Optional[Dict[str, BuildContexts]] - """(Optional) Map of named Docker build contexts. - - Keys are context names, values are typed context definitions (object or http). - See Docker buildx additional contexts for details: - https://docs.docker.com/reference/cli/docker/buildx/build/#build-context - """ + build_context: Optional[BuildContext] + """A build context backed by an Object.""" code_mounts: Optional[Iterable[CodeMountParameters]] """A list of code mounts to be included in the Blueprint.""" @@ -63,12 +48,17 @@ class BlueprintPreviewParams(TypedDict, total=False): launch_parameters: Optional[LaunchParameters] """Parameters to configure your Devbox at launch time.""" - local_build_context: Optional[LocalBuildContext] - """(Optional) Local build context stored in object-storage.""" - metadata: Optional[Dict[str, str]] """(Optional) User defined metadata for the Blueprint.""" + named_build_contexts: Optional[Dict[str, NamedBuildContexts]] + """ + (Optional) Map of named build contexts to attach to the Blueprint build, where + the keys are the name used when referencing the contexts in a Dockerfile. See + Docker buildx additional contexts for details: + https://docs.docker.com/reference/cli/docker/buildx/build/#build-context + """ + secrets: Optional[Dict[str, str]] """(Optional) Map of mount IDs/environment variable names to secret names. @@ -88,46 +78,18 @@ class BlueprintPreviewParams(TypedDict, total=False): """A list of commands to run to set up your system.""" -class BuildContextsHTTP(TypedDict, total=False): - url: Required[str] - """HTTP(S) URL to a tarball or directory to use as context.""" - - -class BuildContextsObject(TypedDict, total=False): +class BuildContext(TypedDict, total=False): object_id: Required[str] - """Handle for a Runloop stored object to use as context.""" - - -class BuildContexts(TypedDict, total=False): - type: Required[Literal["OBJECT", "HTTP"]] - """Type of the context. Supported values: object, http""" - - http: Optional[BuildContextsHTTP] - """HTTP(S) context parameters.""" - - object: Optional[BuildContextsObject] - """Object context parameters (named build context).""" + """The ID of an object, whose contents are to be used as a build context.""" + type: Required[Literal["object"]] -class LocalBuildContextHTTP(TypedDict, total=False): - url: Required[str] - """HTTP(S) URL to a tarball or directory to use as context.""" - -class LocalBuildContextObject(TypedDict, total=False): +class NamedBuildContexts(TypedDict, total=False): object_id: Required[str] - """Handle for a Runloop stored object to use as context.""" - - -class LocalBuildContext(TypedDict, total=False): - type: Required[Literal["OBJECT", "HTTP"]] - """Type of the context. Supported values: object, http""" - - http: Optional[LocalBuildContextHTTP] - """HTTP(S) context parameters.""" + """The ID of an object, whose contents are to be used as a build context.""" - object: Optional[LocalBuildContextObject] - """Object context parameters (named build context).""" + type: Required[Literal["object"]] class ServiceCredentials(TypedDict, total=False): diff --git a/src/runloop_api_client/types/shared/agent_mount_parameters.py b/src/runloop_api_client/types/shared/agent_mount_parameters.py index c2b384265..6351a19f3 100644 --- a/src/runloop_api_client/types/shared/agent_mount_parameters.py +++ b/src/runloop_api_client/types/shared/agent_mount_parameters.py @@ -9,8 +9,15 @@ class AgentMountParameters(BaseModel): - agent_id: str - """The ID of the agent to mount.""" + agent_id: Optional[str] = None + """The ID of the agent to mount. Either agent_id or name must be set.""" + + agent_name: Optional[str] = None + """The name of the agent to mount. + + Returns the most recent agent with a matching name if no agent id string + provided. Either agent id or name must be set + """ type: Literal["agent_mount"] diff --git a/src/runloop_api_client/types/shared_params/agent_mount_parameters.py b/src/runloop_api_client/types/shared_params/agent_mount_parameters.py index def126043..17c18dc32 100644 --- a/src/runloop_api_client/types/shared_params/agent_mount_parameters.py +++ b/src/runloop_api_client/types/shared_params/agent_mount_parameters.py @@ -9,8 +9,15 @@ class AgentMountParameters(TypedDict, total=False): - agent_id: Required[str] - """The ID of the agent to mount.""" + agent_id: Required[Optional[str]] + """The ID of the agent to mount. Either agent_id or name must be set.""" + + agent_name: Required[Optional[str]] + """The name of the agent to mount. + + Returns the most recent agent with a matching name if no agent id string + provided. Either agent id or name must be set + """ type: Required[Literal["agent_mount"]] diff --git a/tests/api_resources/test_blueprints.py b/tests/api_resources/test_blueprints.py index 5a28691d0..030e12112 100644 --- a/tests/api_resources/test_blueprints.py +++ b/tests/api_resources/test_blueprints.py @@ -36,12 +36,9 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: base_blueprint_id="base_blueprint_id", base_blueprint_name="base_blueprint_name", build_args={"foo": "string"}, - build_contexts={ - "foo": { - "type": "OBJECT", - "http": {"url": "url"}, - "object": {"object_id": "object_id"}, - } + build_context={ + "object_id": "object_id", + "type": "object", }, code_mounts=[ { @@ -72,12 +69,13 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: "username": "username", }, }, - local_build_context={ - "type": "OBJECT", - "http": {"url": "url"}, - "object": {"object_id": "object_id"}, - }, metadata={"foo": "string"}, + named_build_contexts={ + "foo": { + "object_id": "object_id", + "type": "object", + } + }, secrets={"foo": "string"}, services=[ { @@ -406,12 +404,9 @@ def test_method_preview_with_all_params(self, client: Runloop) -> None: base_blueprint_id="base_blueprint_id", base_blueprint_name="base_blueprint_name", build_args={"foo": "string"}, - build_contexts={ - "foo": { - "type": "OBJECT", - "http": {"url": "url"}, - "object": {"object_id": "object_id"}, - } + build_context={ + "object_id": "object_id", + "type": "object", }, code_mounts=[ { @@ -442,12 +437,13 @@ def test_method_preview_with_all_params(self, client: Runloop) -> None: "username": "username", }, }, - local_build_context={ - "type": "OBJECT", - "http": {"url": "url"}, - "object": {"object_id": "object_id"}, - }, metadata={"foo": "string"}, + named_build_contexts={ + "foo": { + "object_id": "object_id", + "type": "object", + } + }, secrets={"foo": "string"}, services=[ { @@ -510,12 +506,9 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - base_blueprint_id="base_blueprint_id", base_blueprint_name="base_blueprint_name", build_args={"foo": "string"}, - build_contexts={ - "foo": { - "type": "OBJECT", - "http": {"url": "url"}, - "object": {"object_id": "object_id"}, - } + build_context={ + "object_id": "object_id", + "type": "object", }, code_mounts=[ { @@ -546,12 +539,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - "username": "username", }, }, - local_build_context={ - "type": "OBJECT", - "http": {"url": "url"}, - "object": {"object_id": "object_id"}, - }, metadata={"foo": "string"}, + named_build_contexts={ + "foo": { + "object_id": "object_id", + "type": "object", + } + }, secrets={"foo": "string"}, services=[ { @@ -880,12 +874,9 @@ async def test_method_preview_with_all_params(self, async_client: AsyncRunloop) base_blueprint_id="base_blueprint_id", base_blueprint_name="base_blueprint_name", build_args={"foo": "string"}, - build_contexts={ - "foo": { - "type": "OBJECT", - "http": {"url": "url"}, - "object": {"object_id": "object_id"}, - } + build_context={ + "object_id": "object_id", + "type": "object", }, code_mounts=[ { @@ -916,12 +907,13 @@ async def test_method_preview_with_all_params(self, async_client: AsyncRunloop) "username": "username", }, }, - local_build_context={ - "type": "OBJECT", - "http": {"url": "url"}, - "object": {"object_id": "object_id"}, - }, metadata={"foo": "string"}, + named_build_contexts={ + "foo": { + "object_id": "object_id", + "type": "object", + } + }, secrets={"foo": "string"}, services=[ { From 87abab816e0277e1d2a5b39c36179eb1252b24bf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 00:22:30 +0000 Subject: [PATCH 11/11] release: 0.68.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 23 +++++++++++++++++++++++ pyproject.toml | 2 +- src/runloop_api_client/_version.py | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index db00cae02..0b8a7c68d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.67.0" + ".": "0.68.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f8a9a7ad5..ae56707e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 0.68.0 (2025-11-19) + +Full Changelog: [v0.67.0...v0.68.0](https://github.com/runloopai/api-client-python/compare/v0.67.0...v0.68.0) + +### Features + +* **blueprints:** Cleanup the BuildContext API ([#6407](https://github.com/runloopai/api-client-python/issues/6407))\n\nTest ([c87b986](https://github.com/runloopai/api-client-python/commit/c87b986e558ad3a0cca2ad1609b9833baf86fef8)) +* **blueprints:** prevent deletion of blueprints with dependent snapshots ([ce55350](https://github.com/runloopai/api-client-python/commit/ce55350d81f7f5ba3a3aff8faea88c0e1366cea9)) +* **object:** Added ability to give objects a Time To Live, after which they are automatically deleted.\nfeat(blueprints): Added the ability to attach objects as build contexts that can be referenced in your Dockerfile. ([f2bc83c](https://github.com/runloopai/api-client-python/commit/f2bc83c126696aea224bb5978294fc3362a94eeb)) + + +### Bug Fixes + +* compat with Python 3.14 ([a52802a](https://github.com/runloopai/api-client-python/commit/a52802a6d12d96c3bf4bd670e77c9ec50d08b459)) +* **compat:** update signatures of `model_dump` and `model_dump_json` for Pydantic v1 ([50340b2](https://github.com/runloopai/api-client-python/commit/50340b20148f72cb645bba0bafde6f902e063425)) +* **snapshot:** added "deleted" status to DevboxSnapshotStatus enum \n fix(storage-object): added ObjectState enum, fixed createObject() to appropriately type content_type and state as the respective enums ([7c26593](https://github.com/runloopai/api-client-python/commit/7c265936088c074d00bd3c65b52dde5dcde3ccfb)) + + +### Chores + +* **package:** drop Python 3.8 support ([07a0b8c](https://github.com/runloopai/api-client-python/commit/07a0b8c1825c78b0a6c30c2d374b82aced2f97d5)) +* **package:** drop Python 3.8 support ([d67abf1](https://github.com/runloopai/api-client-python/commit/d67abf1c52a089e192987a261e69219d60514bc3)) + ## 0.67.0 (2025-11-14) Full Changelog: [v0.66.1...v0.67.0](https://github.com/runloopai/api-client-python/compare/v0.66.1...v0.67.0) diff --git a/pyproject.toml b/pyproject.toml index 75e71661e..5b1f6ec4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "runloop_api_client" -version = "0.67.0" +version = "0.68.0" description = "The official Python library for the runloop API" dynamic = ["readme"] license = "MIT" diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py index d5ccb7242..03a1aaf1c 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.67.0" # x-release-please-version +__version__ = "0.68.0" # x-release-please-version