Skip to content

Commit b00a7c1

Browse files
feat: add helpers for blueprint and scenario run creation
1 parent 66af1cc commit b00a7c1

File tree

2 files changed

+321
-0
lines changed

2 files changed

+321
-0
lines changed

src/runloop_api_client/resources/blueprints.py

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
async_to_raw_response_wrapper,
2121
async_to_streamed_response_wrapper,
2222
)
23+
from .._exceptions import RunloopError
24+
from ..lib.polling import PollingConfig, poll_until
25+
from ..lib.polling_async import async_poll_until
2326
from ..pagination import SyncBlueprintsCursorIDPage, AsyncBlueprintsCursorIDPage
2427
from .._base_client import AsyncPaginator, make_request_options
2528
from ..types.blueprint_view import BlueprintView
@@ -154,6 +157,119 @@ def retrieve(
154157
cast_to=BlueprintView,
155158
)
156159

160+
def await_build_complete(
161+
self,
162+
id: str,
163+
*,
164+
polling_config: PollingConfig | None = None,
165+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
166+
# The extra values given here take precedence over values defined on the client or passed to this method.
167+
extra_headers: Headers | None = None,
168+
extra_query: Query | None = None,
169+
extra_body: Body | None = None,
170+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
171+
) -> BlueprintView:
172+
"""Wait for a blueprint to finish building.
173+
174+
Args:
175+
id: The ID of the blueprint to wait for
176+
polling_config: Optional polling configuration
177+
extra_headers: Send extra headers
178+
extra_query: Add additional query parameters to the request
179+
extra_body: Add additional JSON properties to the request
180+
timeout: Override the client-level default timeout for this request, in seconds
181+
182+
Returns:
183+
The blueprint in built state
184+
185+
Raises:
186+
PollingTimeout: If polling times out before blueprint is built
187+
RunloopError: If blueprint enters a non-built terminal state
188+
"""
189+
def retrieve_blueprint() -> BlueprintView:
190+
return self.retrieve(
191+
id,
192+
extra_headers=extra_headers,
193+
extra_query=extra_query,
194+
extra_body=extra_body,
195+
timeout=timeout
196+
)
197+
198+
def is_done_building(blueprint: BlueprintView) -> bool:
199+
return blueprint.status not in ["building", "provisioning"]
200+
201+
blueprint = poll_until(retrieve_blueprint, is_done_building, polling_config)
202+
203+
if blueprint.status != "build_complete":
204+
raise RunloopError(
205+
f"Blueprint entered non-built terminal state: {blueprint.status}"
206+
)
207+
208+
return blueprint
209+
210+
def create_and_await_build_complete(
211+
self,
212+
*,
213+
name: str,
214+
code_mounts: Optional[Iterable[CodeMountParameters]] | NotGiven = NOT_GIVEN,
215+
dockerfile: Optional[str] | NotGiven = NOT_GIVEN,
216+
file_mounts: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,
217+
launch_parameters: Optional[LaunchParameters] | NotGiven = NOT_GIVEN,
218+
system_setup_commands: Optional[List[str]] | NotGiven = NOT_GIVEN,
219+
polling_config: PollingConfig | None = None,
220+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
221+
# The extra values given here take precedence over values defined on the client or passed to this method.
222+
extra_headers: Headers | None = None,
223+
extra_query: Query | None = None,
224+
extra_body: Body | None = None,
225+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
226+
idempotency_key: str | None = None,
227+
) -> BlueprintView:
228+
"""Create a new Blueprint and wait for it to finish building.
229+
230+
Args:
231+
dockerfile: The Dockerfile contents to use for building the Blueprint
232+
file_mounts: Files to mount into the Blueprint
233+
launch_parameters: Launch parameters for Devboxes created from this Blueprint
234+
name: Name for the Blueprint
235+
system_setup_commands: Commands to run during Blueprint build
236+
polling_config: Optional polling configuration
237+
extra_headers: Send extra headers
238+
extra_query: Add additional query parameters to the request
239+
extra_body: Add additional JSON properties to the request
240+
timeout: Override the client-level default timeout for this request, in seconds
241+
idempotency_key: Specify a custom idempotency key for this request
242+
243+
Returns:
244+
The built blueprint
245+
246+
Raises:
247+
PollingTimeout: If polling times out before blueprint is built
248+
RunloopError: If blueprint enters a non-built terminal state
249+
"""
250+
blueprint = self.create(
251+
name=name,
252+
dockerfile=dockerfile,
253+
code_mounts=code_mounts,
254+
file_mounts=file_mounts,
255+
launch_parameters=launch_parameters,
256+
system_setup_commands=system_setup_commands,
257+
extra_headers=extra_headers,
258+
extra_query=extra_query,
259+
extra_body=extra_body,
260+
timeout=timeout,
261+
idempotency_key=idempotency_key,
262+
)
263+
264+
return self.await_build_complete(
265+
blueprint.id,
266+
polling_config=polling_config,
267+
extra_headers=extra_headers,
268+
extra_query=extra_query,
269+
extra_body=extra_body,
270+
timeout=timeout,
271+
)
272+
157273
def list(
158274
self,
159275
*,
@@ -429,6 +545,119 @@ async def retrieve(
429545
),
430546
cast_to=BlueprintView,
431547
)
548+
549+
async def await_build_complete(
550+
self,
551+
id: str,
552+
*,
553+
polling_config: PollingConfig | None = None,
554+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
555+
# The extra values given here take precedence over values defined on the client or passed to this method.
556+
extra_headers: Headers | None = None,
557+
extra_query: Query | None = None,
558+
extra_body: Body | None = None,
559+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
560+
) -> BlueprintView:
561+
"""Wait for a blueprint to finish building.
562+
563+
Args:
564+
id: The ID of the blueprint to wait for
565+
polling_config: Optional polling configuration
566+
extra_headers: Send extra headers
567+
extra_query: Add additional query parameters to the request
568+
extra_body: Add additional JSON properties to the request
569+
timeout: Override the client-level default timeout for this request, in seconds
570+
571+
Returns:
572+
The blueprint in built state
573+
574+
Raises:
575+
PollingTimeout: If polling times out before blueprint is built
576+
RunloopError: If blueprint enters a non-built terminal state
577+
"""
578+
async def retrieve_blueprint() -> BlueprintView:
579+
return await self.retrieve(
580+
id,
581+
extra_headers=extra_headers,
582+
extra_query=extra_query,
583+
extra_body=extra_body,
584+
timeout=timeout
585+
)
586+
587+
def is_done_building(blueprint: BlueprintView) -> bool:
588+
return blueprint.status not in ["building", "provisioning"]
589+
590+
blueprint = await async_poll_until(retrieve_blueprint, is_done_building, polling_config)
591+
592+
if blueprint.status != "build_complete":
593+
raise RunloopError(
594+
f"Blueprint entered non-built terminal state: {blueprint.status}"
595+
)
596+
597+
return blueprint
598+
599+
async def create_and_await_build_complete(
600+
self,
601+
*,
602+
name: str,
603+
code_mounts: Optional[Iterable[CodeMountParameters]] | NotGiven = NOT_GIVEN,
604+
dockerfile: Optional[str] | NotGiven = NOT_GIVEN,
605+
file_mounts: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,
606+
launch_parameters: Optional[LaunchParameters] | NotGiven = NOT_GIVEN,
607+
system_setup_commands: Optional[List[str]] | NotGiven = NOT_GIVEN,
608+
polling_config: PollingConfig | None = None,
609+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
610+
# The extra values given here take precedence over values defined on the client or passed to this method.
611+
extra_headers: Headers | None = None,
612+
extra_query: Query | None = None,
613+
extra_body: Body | None = None,
614+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
615+
idempotency_key: str | None = None,
616+
) -> BlueprintView:
617+
"""Create a new Blueprint and wait for it to finish building.
618+
619+
Args:
620+
dockerfile: The Dockerfile contents to use for building the Blueprint
621+
file_mounts: Files to mount into the Blueprint
622+
launch_parameters: Launch parameters for Devboxes created from this Blueprint
623+
name: Name for the Blueprint
624+
system_setup_commands: Commands to run during Blueprint build
625+
polling_config: Optional polling configuration
626+
extra_headers: Send extra headers
627+
extra_query: Add additional query parameters to the request
628+
extra_body: Add additional JSON properties to the request
629+
timeout: Override the client-level default timeout for this request, in seconds
630+
idempotency_key: Specify a custom idempotency key for this request
631+
632+
Returns:
633+
The built blueprint
634+
635+
Raises:
636+
PollingTimeout: If polling times out before blueprint is built
637+
RunloopError: If blueprint enters a non-built terminal state
638+
"""
639+
blueprint = await self.create(
640+
name=name,
641+
dockerfile=dockerfile,
642+
code_mounts=code_mounts,
643+
file_mounts=file_mounts,
644+
launch_parameters=launch_parameters,
645+
system_setup_commands=system_setup_commands,
646+
extra_headers=extra_headers,
647+
extra_query=extra_query,
648+
extra_body=extra_body,
649+
timeout=timeout,
650+
idempotency_key=idempotency_key,
651+
)
652+
653+
return await self.await_build_complete(
654+
blueprint.id,
655+
polling_config=polling_config,
656+
extra_headers=extra_headers,
657+
extra_query=extra_query,
658+
extra_body=extra_body,
659+
timeout=timeout,
660+
)
432661

433662
def list(
434663
self,

src/runloop_api_client/resources/scenarios/scenarios.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
maybe_transform,
2626
async_maybe_transform,
2727
)
28+
from ...lib.polling import PollingConfig
2829
from ..._compat import cached_property
2930
from ..._resource import SyncAPIResource, AsyncAPIResource
3031
from ..._response import (
@@ -317,6 +318,63 @@ def start_run(
317318
cast_to=ScenarioRunView,
318319
)
319320

321+
def start_run_and_await_env_ready(
322+
self,
323+
*,
324+
scenario_id: str,
325+
benchmark_run_id: Optional[str] | NotGiven = NOT_GIVEN,
326+
run_name: Optional[str] | NotGiven = NOT_GIVEN,
327+
polling_config: PollingConfig | None = None,
328+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
329+
# The extra values given here take precedence over values defined on the client or passed to this method.
330+
extra_headers: Headers | None = None,
331+
extra_query: Query | None = None,
332+
extra_body: Body | None = None,
333+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
334+
idempotency_key: str | None = None,
335+
) -> ScenarioRunView:
336+
"""Start a new ScenarioRun and wait for its environment to be ready.
337+
338+
Args:
339+
scenario_id: ID of the Scenario to run
340+
benchmark_run_id: Benchmark to associate the run
341+
run_name: Display name of the run
342+
polling_config: Optional polling configuration
343+
extra_headers: Send extra headers
344+
extra_query: Add additional query parameters to the request
345+
extra_body: Add additional JSON properties to the request
346+
timeout: Override the client-level default timeout for this request, in seconds
347+
idempotency_key: Specify a custom idempotency key for this request
348+
349+
Returns:
350+
The scenario run in running state
351+
352+
Raises:
353+
PollingTimeout: If polling times out before environment is ready
354+
RunloopError: If environment enters a non-running terminal state
355+
"""
356+
run = self.start_run(
357+
scenario_id=scenario_id,
358+
benchmark_run_id=benchmark_run_id,
359+
run_name=run_name,
360+
extra_headers=extra_headers,
361+
extra_query=extra_query,
362+
extra_body=extra_body,
363+
timeout=timeout,
364+
idempotency_key=idempotency_key,
365+
)
366+
367+
self._client.devboxes.await_running(
368+
run.devbox_id,
369+
polling_config=polling_config,
370+
extra_headers=extra_headers,
371+
extra_query=extra_query,
372+
extra_body=extra_body,
373+
timeout=timeout,
374+
)
375+
376+
return run
377+
320378

321379
class AsyncScenariosResource(AsyncAPIResource):
322380
@cached_property
@@ -591,6 +649,40 @@ async def start_run(
591649
cast_to=ScenarioRunView,
592650
)
593651

652+
async def start_run_and_await_env_ready(
653+
self,
654+
scenario_id: str,
655+
benchmark_run_id: Optional[str] | NotGiven = NOT_GIVEN,
656+
run_name: Optional[str] | NotGiven = NOT_GIVEN,
657+
polling_config: PollingConfig | None = None,
658+
) -> ScenarioRunView:
659+
"""Start a new ScenarioRun and wait for its environment to be ready.
660+
661+
Args:
662+
scenario_id: ID of the Scenario to run
663+
benchmark_run_id: Benchmark to associate the run
664+
run_name: Display name of the run
665+
polling_config: Optional polling configuration
666+
667+
Returns:
668+
The scenario run in running state
669+
670+
Raises:
671+
PollingTimeout: If polling times out before environment is ready
672+
RunloopError: If environment enters a non-running terminal state
673+
"""
674+
run = await self.start_run(
675+
scenario_id=scenario_id,
676+
benchmark_run_id=benchmark_run_id,
677+
run_name=run_name,
678+
)
679+
680+
await self._client.devboxes.await_running(
681+
run.devbox_id,
682+
polling_config=polling_config,
683+
)
684+
685+
return run
594686

595687
class ScenariosResourceWithRawResponse:
596688
def __init__(self, scenarios: ScenariosResource) -> None:

0 commit comments

Comments
 (0)