Skip to content

Commit 5e07fee

Browse files
committed
scenario wrapper
1 parent a8bf27f commit 5e07fee

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,30 +5,35 @@
55

66
from __future__ import annotations
77

8-
from .sync import AgentOps, DevboxOps, ScorerOps, RunloopSDK, SnapshotOps, BlueprintOps, StorageObjectOps
8+
from .sync import AgentOps, DevboxOps, ScorerOps, RunloopSDK, ScenarioOps, SnapshotOps, BlueprintOps, StorageObjectOps
99
from .agent import Agent
1010
from .async_ import (
1111
AsyncAgentOps,
1212
AsyncDevboxOps,
1313
AsyncScorerOps,
1414
AsyncRunloopSDK,
15+
AsyncScenarioOps,
1516
AsyncSnapshotOps,
1617
AsyncBlueprintOps,
1718
AsyncStorageObjectOps,
1819
)
1920
from .devbox import Devbox, NamedShell
2021
from .scorer import Scorer
22+
from .scenario import Scenario
2123
from .snapshot import Snapshot
2224
from .blueprint import Blueprint
2325
from .execution import Execution
2426
from .async_agent import AsyncAgent
2527
from .async_devbox import AsyncDevbox, AsyncNamedShell
2628
from .async_scorer import AsyncScorer
29+
from .scenario_run import ScenarioRun
30+
from .async_scenario import AsyncScenario
2731
from .async_snapshot import AsyncSnapshot
2832
from .storage_object import StorageObject
2933
from .async_blueprint import AsyncBlueprint
3034
from .async_execution import AsyncExecution
3135
from .execution_result import ExecutionResult
36+
from .async_scenario_run import AsyncScenarioRun
3237
from .async_storage_object import AsyncStorageObject
3338
from .async_execution_result import AsyncExecutionResult
3439

@@ -43,6 +48,8 @@
4348
"AsyncDevboxOps",
4449
"BlueprintOps",
4550
"AsyncBlueprintOps",
51+
"ScenarioOps",
52+
"AsyncScenarioOps",
4653
"ScorerOps",
4754
"AsyncScorerOps",
4855
"SnapshotOps",
@@ -60,6 +67,10 @@
6067
"AsyncExecutionResult",
6168
"Blueprint",
6269
"AsyncBlueprint",
70+
"Scenario",
71+
"AsyncScenario",
72+
"ScenarioRun",
73+
"AsyncScenarioRun",
6374
"Scorer",
6475
"AsyncScorer",
6576
"Snapshot",

src/runloop_api_client/sdk/_types.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from ..types.agent_create_params import AgentCreateParams
1212
from ..types.devbox_create_params import DevboxCreateParams, DevboxBaseCreateParams
1313
from ..types.object_create_params import ObjectCreateParams
14+
from ..types.scenario_list_params import ScenarioListParams
1415
from ..types.blueprint_list_params import BlueprintListParams
1516
from ..types.object_download_params import ObjectDownloadParams
1617
from ..types.blueprint_create_params import BlueprintCreateParams
@@ -167,3 +168,23 @@ class SDKAgentCreateParams(AgentCreateParams, LongRequestOptions):
167168

168169
class SDKAgentListParams(AgentListParams, BaseRequestOptions):
169170
pass
171+
172+
173+
class SDKScenarioListParams(ScenarioListParams, BaseRequestOptions):
174+
pass
175+
176+
177+
class SDKScenarioRunParams(TypedDict, total=False):
178+
"""Parameters for starting a scenario run (excludes scenario_id which is set automatically)."""
179+
180+
benchmark_run_id: Optional[str]
181+
"""Benchmark to associate the run."""
182+
183+
metadata: Optional[dict[str, str]]
184+
"""User defined metadata to attach to the run for organization."""
185+
186+
run_name: Optional[str]
187+
"""Display name of the run."""
188+
189+
polling_config: Optional[PollingConfig]
190+
"""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
@@ -21,6 +21,7 @@
2121
SDKAgentCreateParams,
2222
SDKDevboxCreateParams,
2323
SDKObjectCreateParams,
24+
SDKScenarioListParams,
2425
SDKScorerCreateParams,
2526
SDKBlueprintListParams,
2627
SDKBlueprintCreateParams,
@@ -33,6 +34,7 @@
3334
from .async_agent import AsyncAgent
3435
from .async_devbox import AsyncDevbox
3536
from .async_scorer import AsyncScorer
37+
from .async_scenario import AsyncScenario
3638
from .async_snapshot import AsyncSnapshot
3739
from .async_blueprint import AsyncBlueprint
3840
from .async_storage_object import AsyncStorageObject
@@ -761,6 +763,45 @@ async def list(
761763
return [AsyncAgent(self._client, item.id, item) for item in page.agents]
762764

763765

766+
class AsyncScenarioOps:
767+
"""Manage scenarios (async). Access via ``runloop.scenario``.
768+
769+
Example:
770+
>>> runloop = AsyncRunloopSDK()
771+
>>> scenario = runloop.scenario.from_id("scn-xxx")
772+
>>> run = await scenario.run()
773+
>>> scenarios = await runloop.scenario.list()
774+
"""
775+
776+
def __init__(self, client: AsyncRunloop) -> None:
777+
"""Initialize AsyncScenarioOps.
778+
779+
:param client: AsyncRunloop client instance
780+
:type client: AsyncRunloop
781+
"""
782+
self._client = client
783+
784+
def from_id(self, scenario_id: str) -> AsyncScenario:
785+
"""Get an AsyncScenario instance for an existing scenario ID.
786+
787+
:param scenario_id: ID of the scenario
788+
:type scenario_id: str
789+
:return: AsyncScenario instance for the given ID
790+
:rtype: AsyncScenario
791+
"""
792+
return AsyncScenario(self._client, scenario_id)
793+
794+
async def list(self, **params: Unpack[SDKScenarioListParams]) -> list[AsyncScenario]:
795+
"""List all scenarios, optionally filtered by parameters.
796+
797+
:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKScenarioListParams` for available parameters
798+
:return: List of scenarios
799+
:rtype: list[AsyncScenario]
800+
"""
801+
page = await self._client.scenarios.list(**params)
802+
return [AsyncScenario(self._client, item.id) async for item in page]
803+
804+
764805
class AsyncRunloopSDK:
765806
"""High-level asynchronous entry point for the Runloop SDK.
766807
@@ -776,6 +817,8 @@ class AsyncRunloopSDK:
776817
:vartype devbox: AsyncDevboxOps
777818
:ivar blueprint: High-level async interface for blueprint management
778819
:vartype blueprint: AsyncBlueprintOps
820+
:ivar scenario: High-level async interface for scenario management
821+
:vartype scenario: AsyncScenarioOps
779822
:ivar scorer: High-level async interface for scorer management
780823
:vartype scorer: AsyncScorerOps
781824
:ivar snapshot: High-level async interface for snapshot management
@@ -795,6 +838,7 @@ class AsyncRunloopSDK:
795838
agent: AsyncAgentOps
796839
devbox: AsyncDevboxOps
797840
blueprint: AsyncBlueprintOps
841+
scenario: AsyncScenarioOps
798842
scorer: AsyncScorerOps
799843
snapshot: AsyncSnapshotOps
800844
storage_object: AsyncStorageObjectOps
@@ -840,6 +884,7 @@ def __init__(
840884
self.agent = AsyncAgentOps(self.api)
841885
self.devbox = AsyncDevboxOps(self.api)
842886
self.blueprint = AsyncBlueprintOps(self.api)
887+
self.scenario = AsyncScenarioOps(self.api)
843888
self.scorer = AsyncScorerOps(self.api)
844889
self.snapshot = AsyncSnapshotOps(self.api)
845890
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)