Skip to content

Commit 19eb010

Browse files
committed
scenario wrapper
1 parent adf48af commit 19eb010

15 files changed

Lines changed: 1745 additions & 1 deletion

src/runloop_api_client/sdk/__init__.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,32 @@
55

66
from __future__ import annotations
77

8-
from .sync import DevboxOps, ScorerOps, RunloopSDK, SnapshotOps, BlueprintOps, StorageObjectOps
8+
from .sync import DevboxOps, ScorerOps, RunloopSDK, ScenarioOps, SnapshotOps, BlueprintOps, StorageObjectOps
99
from .async_ import (
1010
AsyncDevboxOps,
1111
AsyncScorerOps,
1212
AsyncRunloopSDK,
13+
AsyncScenarioOps,
1314
AsyncSnapshotOps,
1415
AsyncBlueprintOps,
1516
AsyncStorageObjectOps,
1617
)
1718
from .devbox import Devbox, NamedShell
1819
from .scorer import Scorer
20+
from .scenario import Scenario
1921
from .snapshot import Snapshot
2022
from .blueprint import Blueprint
2123
from .execution import Execution
2224
from .async_devbox import AsyncDevbox, AsyncNamedShell
2325
from .async_scorer import AsyncScorer
26+
from .scenario_run import ScenarioRun
27+
from .async_scenario import AsyncScenario
2428
from .async_snapshot import AsyncSnapshot
2529
from .storage_object import StorageObject
2630
from .async_blueprint import AsyncBlueprint
2731
from .async_execution import AsyncExecution
2832
from .execution_result import ExecutionResult
33+
from .async_scenario_run import AsyncScenarioRun
2934
from .async_storage_object import AsyncStorageObject
3035
from .async_execution_result import AsyncExecutionResult
3136

@@ -38,6 +43,8 @@
3843
"AsyncDevboxOps",
3944
"BlueprintOps",
4045
"AsyncBlueprintOps",
46+
"ScenarioOps",
47+
"AsyncScenarioOps",
4148
"ScorerOps",
4249
"AsyncScorerOps",
4350
"SnapshotOps",
@@ -53,6 +60,10 @@
5360
"AsyncExecutionResult",
5461
"Blueprint",
5562
"AsyncBlueprint",
63+
"Scenario",
64+
"AsyncScenario",
65+
"ScenarioRun",
66+
"AsyncScenarioRun",
5667
"Scorer",
5768
"AsyncScorer",
5869
"Snapshot",

src/runloop_api_client/sdk/_types.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from ..types.object_list_params import ObjectListParams
1010
from ..types.devbox_create_params import DevboxCreateParams, DevboxBaseCreateParams
1111
from ..types.object_create_params import ObjectCreateParams
12+
from ..types.scenario_list_params import ScenarioListParams
1213
from ..types.blueprint_list_params import BlueprintListParams
1314
from ..types.object_download_params import ObjectDownloadParams
1415
from ..types.blueprint_create_params import BlueprintCreateParams
@@ -157,3 +158,23 @@ class SDKScorerUpdateParams(ScorerUpdateParams, LongRequestOptions):
157158

158159
class SDKScorerValidateParams(ScorerValidateParams, LongRequestOptions):
159160
pass
161+
162+
163+
class SDKScenarioListParams(ScenarioListParams, BaseRequestOptions):
164+
pass
165+
166+
167+
class SDKScenarioRunParams(TypedDict, total=False):
168+
"""Parameters for starting a scenario run (excludes scenario_id which is set automatically)."""
169+
170+
benchmark_run_id: Optional[str]
171+
"""Benchmark to associate the run."""
172+
173+
metadata: Optional[dict[str, str]]
174+
"""User defined metadata to attach to the run for organization."""
175+
176+
run_name: Optional[str]
177+
"""Display name of the run."""
178+
179+
polling_config: Optional[PollingConfig]
180+
"""Configuration for polling behavior (used by run_and_await_env_ready)."""

src/runloop_api_client/sdk/async_.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
SDKScorerListParams,
2020
SDKDevboxCreateParams,
2121
SDKObjectCreateParams,
22+
SDKScenarioListParams,
2223
SDKScorerCreateParams,
2324
SDKBlueprintListParams,
2425
SDKBlueprintCreateParams,
@@ -30,6 +31,7 @@
3031
from ._helpers import detect_content_type
3132
from .async_devbox import AsyncDevbox
3233
from .async_scorer import AsyncScorer
34+
from .async_scenario import AsyncScenario
3335
from .async_snapshot import AsyncSnapshot
3436
from .async_blueprint import AsyncBlueprint
3537
from .async_storage_object import AsyncStorageObject
@@ -542,6 +544,45 @@ async def list(self, **params: Unpack[SDKScorerListParams]) -> list[AsyncScorer]
542544
return [AsyncScorer(self._client, item.id) async for item in page]
543545

544546

547+
class AsyncScenarioOps:
548+
"""Manage scenarios (async). Access via ``runloop.scenario``.
549+
550+
Example:
551+
>>> runloop = AsyncRunloopSDK()
552+
>>> scenario = runloop.scenario.from_id("scn-xxx")
553+
>>> run = await scenario.run()
554+
>>> scenarios = await runloop.scenario.list()
555+
"""
556+
557+
def __init__(self, client: AsyncRunloop) -> None:
558+
"""Initialize AsyncScenarioOps.
559+
560+
:param client: AsyncRunloop client instance
561+
:type client: AsyncRunloop
562+
"""
563+
self._client = client
564+
565+
def from_id(self, scenario_id: str) -> AsyncScenario:
566+
"""Get an AsyncScenario instance for an existing scenario ID.
567+
568+
:param scenario_id: ID of the scenario
569+
:type scenario_id: str
570+
:return: AsyncScenario instance for the given ID
571+
:rtype: AsyncScenario
572+
"""
573+
return AsyncScenario(self._client, scenario_id)
574+
575+
async def list(self, **params: Unpack[SDKScenarioListParams]) -> list[AsyncScenario]:
576+
"""List all scenarios, optionally filtered by parameters.
577+
578+
:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKScenarioListParams` for available parameters
579+
:return: List of scenarios
580+
:rtype: list[AsyncScenario]
581+
"""
582+
page = await self._client.scenarios.list(**params)
583+
return [AsyncScenario(self._client, item.id) async for item in page]
584+
585+
545586
class AsyncRunloopSDK:
546587
"""High-level asynchronous entry point for the Runloop SDK.
547588
@@ -555,6 +596,8 @@ class AsyncRunloopSDK:
555596
:vartype devbox: AsyncDevboxOps
556597
:ivar blueprint: High-level async interface for blueprint management
557598
:vartype blueprint: AsyncBlueprintOps
599+
:ivar scenario: High-level async interface for scenario management
600+
:vartype scenario: AsyncScenarioOps
558601
:ivar scorer: High-level async interface for scorer management
559602
:vartype scorer: AsyncScorerOps
560603
:ivar snapshot: High-level async interface for snapshot management
@@ -573,6 +616,7 @@ class AsyncRunloopSDK:
573616
api: AsyncRunloop
574617
devbox: AsyncDevboxOps
575618
blueprint: AsyncBlueprintOps
619+
scenario: AsyncScenarioOps
576620
scorer: AsyncScorerOps
577621
snapshot: AsyncSnapshotOps
578622
storage_object: AsyncStorageObjectOps
@@ -617,6 +661,7 @@ def __init__(
617661

618662
self.devbox = AsyncDevboxOps(self.api)
619663
self.blueprint = AsyncBlueprintOps(self.api)
664+
self.scenario = AsyncScenarioOps(self.api)
620665
self.scorer = AsyncScorerOps(self.api)
621666
self.snapshot = AsyncSnapshotOps(self.api)
622667
self.storage_object = AsyncStorageObjectOps(self.api)
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""AsyncScenario resource class for asynchronous operations."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Dict, Optional
6+
from typing_extensions import Unpack, override
7+
8+
from ..types import ScenarioView
9+
from ._types import BaseRequestOptions, LongRequestOptions, SDKScenarioRunParams
10+
from .._client import AsyncRunloop
11+
from .async_scenario_run import AsyncScenarioRun
12+
13+
14+
class AsyncScenario:
15+
"""Async wrapper around a scenario resource.
16+
17+
Provides async methods for retrieving scenario details, updating the scenario,
18+
and starting scenario runs.
19+
20+
Example:
21+
>>> scenario = sdk.scenario.from_id("scn-xxx")
22+
>>> info = await scenario.get_info()
23+
>>> run = await scenario.run(run_name="test-run")
24+
>>> devbox = run.devbox
25+
"""
26+
27+
def __init__(self, client: AsyncRunloop, scenario_id: str) -> None:
28+
"""Initialize the wrapper.
29+
30+
:param client: Generated AsyncRunloop client
31+
:type client: AsyncRunloop
32+
:param scenario_id: Scenario ID returned by the API
33+
:type scenario_id: str
34+
"""
35+
self._client = client
36+
self._id = scenario_id
37+
38+
@override
39+
def __repr__(self) -> str:
40+
return f"<AsyncScenario id={self._id!r}>"
41+
42+
@property
43+
def id(self) -> str:
44+
"""Return the scenario ID.
45+
46+
:return: Unique scenario ID
47+
:rtype: str
48+
"""
49+
return self._id
50+
51+
async def get_info(
52+
self,
53+
**options: Unpack[BaseRequestOptions],
54+
) -> ScenarioView:
55+
"""Retrieve current scenario details.
56+
57+
:param options: Optional request configuration
58+
:return: Current scenario info
59+
:rtype: ScenarioView
60+
"""
61+
return await self._client.scenarios.retrieve(
62+
self._id,
63+
**options,
64+
)
65+
66+
async def update(
67+
self,
68+
*,
69+
name: Optional[str] = None,
70+
metadata: Optional[Dict[str, str]] = None,
71+
**options: Unpack[LongRequestOptions],
72+
) -> ScenarioView:
73+
"""Update the scenario.
74+
75+
Only provided fields will be updated.
76+
77+
:param name: New name for the scenario
78+
:type name: Optional[str]
79+
:param metadata: New metadata for the scenario
80+
:type metadata: Optional[Dict[str, str]]
81+
:param options: Optional long-running request configuration
82+
:return: Updated scenario info
83+
:rtype: ScenarioView
84+
"""
85+
return await self._client.scenarios.update(
86+
self._id,
87+
name=name,
88+
metadata=metadata,
89+
**options,
90+
)
91+
92+
async def run(
93+
self,
94+
**params: Unpack[SDKScenarioRunParams],
95+
) -> AsyncScenarioRun:
96+
"""Start a new scenario run.
97+
98+
Creates a new scenario run and returns a wrapper for managing it.
99+
The underlying devbox may still be starting; call await_env_ready()
100+
on the returned AsyncScenarioRun to wait for it to be ready.
101+
102+
:param params: See SDKScenarioRunParams for available parameters
103+
:return: Wrapper for the new scenario run
104+
:rtype: AsyncScenarioRun
105+
"""
106+
run_view = await self._client.scenarios.start_run(
107+
scenario_id=self._id,
108+
**params,
109+
)
110+
return AsyncScenarioRun(self._client, run_view.id, run_view.devbox_id)
111+
112+
async def run_and_await_env_ready(
113+
self,
114+
**params: Unpack[SDKScenarioRunParams],
115+
) -> AsyncScenarioRun:
116+
"""Start a new scenario run and wait for environment to be ready.
117+
118+
Convenience method that starts a run and waits for the devbox to be ready.
119+
120+
:param params: See SDKScenarioRunParams for available parameters
121+
:return: Wrapper for the scenario run with ready environment
122+
:rtype: AsyncScenarioRun
123+
"""
124+
run_view = await self._client.scenarios.start_run_and_await_env_ready(
125+
scenario_id=self._id,
126+
**params,
127+
)
128+
return AsyncScenarioRun(self._client, run_view.id, run_view.devbox_id)
129+

0 commit comments

Comments
 (0)