Skip to content

Commit cd9adce

Browse files
committed
add preview method
1 parent a099739 commit cd9adce

4 files changed

Lines changed: 132 additions & 73 deletions

File tree

src/runloop_api_client/sdk/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .sync import AgentOps, DevboxOps, ScorerOps, RunloopSDK, ScenarioOps, SnapshotOps, BlueprintOps, StorageObjectOps
99
from .agent import Agent
10+
from ._types import ScenarioPreview
1011
from .async_ import (
1112
AsyncAgentOps,
1213
AsyncDevboxOps,
@@ -75,6 +76,7 @@
7576
"AsyncScenarioRun",
7677
"ScenarioBuilder",
7778
"AsyncScenarioBuilder",
79+
"ScenarioPreview",
7880
"Scorer",
7981
"AsyncScorer",
8082
"Snapshot",

src/runloop_api_client/sdk/_types.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
from typing import Union, Callable, Optional
1+
from typing import Dict, Union, Callable, Optional
22
from typing_extensions import TypedDict
33

44
from .._types import Body, Query, Headers, Timeout, NotGiven
55
from ..lib.polling import PollingConfig
66
from ..types.devboxes import DiskSnapshotListParams, DiskSnapshotUpdateParams
77
from ..types.scenarios import ScorerListParams, ScorerCreateParams, ScorerUpdateParams, ScorerValidateParams
8+
from ..types.input_context import InputContext
9+
from ..types.scenario_view import ScenarioView
10+
from ..types.scoring_contract import ScoringContract
811
from ..types.agent_list_params import AgentListParams
912
from ..types.devbox_list_params import DevboxListParams
1013
from ..types.object_list_params import ObjectListParams
@@ -186,3 +189,27 @@ class SDKScenarioRunAsyncParams(ScenarioStartRunBaseParams, LongRequestOptions):
186189

187190
class SDKScenarioRunParams(ScenarioStartRunBaseParams, LongPollingRequestOptions):
188191
pass
192+
193+
194+
class InputContextPreview(InputContext):
195+
problem_statement: Optional[str] = None # type: ignore[assignment]
196+
"""The problem statement for the Scenario."""
197+
198+
199+
class ScenarioPreview(ScenarioView):
200+
"""Preview of scenario configuration with all fields optional."""
201+
202+
id: Optional[str] = None # type: ignore[assignment]
203+
"""The ID of the Scenario."""
204+
205+
input_context: Optional[InputContextPreview] = None # type: ignore[assignment]
206+
"""The input context for the Scenario."""
207+
208+
metadata: Optional[Dict[str, str]] = None # type: ignore[assignment]
209+
"""User defined metadata to attach to the scenario for organization."""
210+
211+
name: Optional[str] = None # type: ignore[assignment]
212+
"""The name of the Scenario."""
213+
214+
scoring_contract: Optional[ScoringContract] = None # type: ignore[assignment]
215+
"""The scoring contract for the Scenario."""

src/runloop_api_client/sdk/async_scenario_builder.py

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from typing_extensions import Self, Unpack, Literal, override
77

88
from ..types import ScenarioCreateParams, ScenarioEnvironmentParam
9-
from ._types import LongRequestOptions
9+
from ._types import ScenarioPreview, LongRequestOptions
1010
from .._client import AsyncRunloop
1111
from .async_scenario import AsyncScenario
1212
from .async_snapshot import AsyncSnapshot
@@ -387,6 +387,22 @@ def with_validation_type(self, validation_type: Literal["UNSPECIFIED", "FORWARD"
387387
self._validation_type = validation_type
388388
return self
389389

390+
def _build_normalized_scorers(self) -> List[ScoringFunctionParam]:
391+
"""Build normalized scorers list."""
392+
total_weight = sum(s["weight"] for s in self._scorers)
393+
return [{**s, "weight": s["weight"] / total_weight} for s in self._scorers]
394+
395+
def _build_environment_params(self) -> ScenarioEnvironmentParam:
396+
"""Build environment parameters."""
397+
env_params: ScenarioEnvironmentParam = {}
398+
if self._blueprint:
399+
env_params["blueprint_id"] = self._blueprint.id
400+
if self._snapshot:
401+
env_params["snapshot_id"] = self._snapshot.id
402+
if self._working_directory:
403+
env_params["working_directory"] = self._working_directory
404+
return env_params
405+
390406
def _build_params(self) -> ScenarioCreateParams:
391407
"""Build the scenario creation parameters.
392408
@@ -405,51 +421,50 @@ def _build_params(self) -> ScenarioCreateParams:
405421
"Call add_test_command_scorer(), add_bash_script_scorer(), or another scorer method first."
406422
)
407423

408-
# Normalize weights to sum to 1.0
409-
total_weight = sum(s["weight"] for s in self._scorers)
410-
normalized_scorers: List[ScoringFunctionParam] = [
411-
{**s, "weight": s["weight"] / total_weight} for s in self._scorers
412-
]
413-
414-
params: ScenarioCreateParams = {
424+
return {
415425
"name": self._name,
416426
"input_context": {
417427
"problem_statement": self._problem_statement,
428+
"additional_context": self._additional_context,
418429
},
419430
"scoring_contract": {
420-
"scoring_function_parameters": normalized_scorers,
431+
"scoring_function_parameters": self._build_normalized_scorers(),
421432
},
433+
"environment_parameters": self._build_environment_params(),
434+
"metadata": self._metadata,
435+
"reference_output": self._reference_output,
436+
"required_environment_variables": self._required_env_vars,
437+
"required_secret_names": self._required_secrets,
438+
"validation_type": self._validation_type,
422439
}
423440

424-
# Add additional context if set
425-
if self._additional_context is not None:
426-
params["input_context"]["additional_context"] = self._additional_context
441+
def preview(self) -> ScenarioPreview:
442+
"""Preview the scenario configuration without pushing to the platform.
427443
428-
# Build environment parameters if any are set
429-
env_params: ScenarioEnvironmentParam = {}
430-
if self._blueprint:
431-
env_params["blueprint_id"] = self._blueprint.id
432-
if self._snapshot:
433-
env_params["snapshot_id"] = self._snapshot.id
434-
if self._working_directory:
435-
env_params["working_directory"] = self._working_directory
444+
Returns the current configuration state as a ScenarioPreview object.
445+
Does not validate or raise errors for missing required fields.
436446
437-
if env_params:
438-
params["environment_parameters"] = env_params
439-
440-
# Add optional fields
441-
if self._metadata:
442-
params["metadata"] = self._metadata
443-
if self._reference_output:
444-
params["reference_output"] = self._reference_output
445-
if self._required_env_vars:
446-
params["required_environment_variables"] = self._required_env_vars
447-
if self._required_secrets:
448-
params["required_secret_names"] = self._required_secrets
449-
if self._validation_type:
450-
params["validation_type"] = self._validation_type
451-
452-
return params
447+
:return: Preview of the scenario configuration
448+
:rtype: ScenarioPreview
449+
"""
450+
return ScenarioPreview.model_validate(
451+
{
452+
"name": self._name,
453+
"input_context": {
454+
"problem_statement": self._problem_statement,
455+
"additional_context": self._additional_context,
456+
},
457+
"scoring_contract": {
458+
"scoring_function_parameters": self._build_normalized_scorers(),
459+
},
460+
"environment": self._build_environment_params(),
461+
"metadata": self._metadata,
462+
"reference_output": self._reference_output,
463+
"required_environment_variables": self._required_env_vars,
464+
"required_secret_names": self._required_secrets,
465+
"validation_type": self._validation_type,
466+
}
467+
)
453468

454469
async def push(self, **options: Unpack[LongRequestOptions]) -> AsyncScenario:
455470
"""Create the scenario on the platform.

src/runloop_api_client/sdk/scenario_builder.py

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from typing_extensions import Self, Unpack, Literal, override
77

88
from ..types import ScenarioCreateParams, ScenarioEnvironmentParam
9-
from ._types import LongRequestOptions
9+
from ._types import ScenarioPreview, LongRequestOptions
1010
from .._client import Runloop
1111
from .scenario import Scenario
1212
from .snapshot import Snapshot
@@ -387,6 +387,22 @@ def with_validation_type(self, validation_type: Literal["UNSPECIFIED", "FORWARD"
387387
self._validation_type = validation_type
388388
return self
389389

390+
def _build_normalized_scorers(self) -> List[ScoringFunctionParam]:
391+
"""Build normalized scorers list."""
392+
total_weight = sum(s["weight"] for s in self._scorers)
393+
return [{**s, "weight": s["weight"] / total_weight} for s in self._scorers]
394+
395+
def _build_environment_params(self) -> ScenarioEnvironmentParam:
396+
"""Build environment parameters"""
397+
env_params: ScenarioEnvironmentParam = {}
398+
if self._blueprint:
399+
env_params["blueprint_id"] = self._blueprint.id
400+
if self._snapshot:
401+
env_params["snapshot_id"] = self._snapshot.id
402+
if self._working_directory:
403+
env_params["working_directory"] = self._working_directory
404+
return env_params
405+
390406
def _build_params(self) -> ScenarioCreateParams:
391407
"""Build the scenario creation parameters.
392408
@@ -405,51 +421,50 @@ def _build_params(self) -> ScenarioCreateParams:
405421
"Call add_test_command_scorer(), add_bash_script_scorer(), or another scorer method first."
406422
)
407423

408-
# Normalize weights to sum to 1.0
409-
total_weight = sum(s["weight"] for s in self._scorers)
410-
normalized_scorers: List[ScoringFunctionParam] = [
411-
{**s, "weight": s["weight"] / total_weight} for s in self._scorers
412-
]
413-
414-
params: ScenarioCreateParams = {
424+
return {
415425
"name": self._name,
416426
"input_context": {
417427
"problem_statement": self._problem_statement,
428+
"additional_context": self._additional_context,
418429
},
419430
"scoring_contract": {
420-
"scoring_function_parameters": normalized_scorers,
431+
"scoring_function_parameters": self._build_normalized_scorers(),
421432
},
433+
"environment_parameters": self._build_environment_params(),
434+
"metadata": self._metadata,
435+
"reference_output": self._reference_output,
436+
"required_environment_variables": self._required_env_vars,
437+
"required_secret_names": self._required_secrets,
438+
"validation_type": self._validation_type,
422439
}
423440

424-
# Add additional context if set
425-
if self._additional_context is not None:
426-
params["input_context"]["additional_context"] = self._additional_context
441+
def preview(self) -> ScenarioPreview:
442+
"""Preview the scenario configuration without pushing to the platform.
427443
428-
# Build environment parameters if any are set
429-
env_params: ScenarioEnvironmentParam = {}
430-
if self._blueprint:
431-
env_params["blueprint_id"] = self._blueprint.id
432-
if self._snapshot:
433-
env_params["snapshot_id"] = self._snapshot.id
434-
if self._working_directory:
435-
env_params["working_directory"] = self._working_directory
444+
Returns the current configuration state as a ScenarioPreview object.
445+
Does not validate or raise errors for missing required fields.
436446
437-
if env_params:
438-
params["environment_parameters"] = env_params
439-
440-
# Add optional fields
441-
if self._metadata:
442-
params["metadata"] = self._metadata
443-
if self._reference_output:
444-
params["reference_output"] = self._reference_output
445-
if self._required_env_vars:
446-
params["required_environment_variables"] = self._required_env_vars
447-
if self._required_secrets:
448-
params["required_secret_names"] = self._required_secrets
449-
if self._validation_type:
450-
params["validation_type"] = self._validation_type
451-
452-
return params
447+
:return: Preview of the scenario configuration
448+
:rtype: ScenarioPreview
449+
"""
450+
return ScenarioPreview.model_validate(
451+
{
452+
"name": self._name,
453+
"input_context": {
454+
"problem_statement": self._problem_statement,
455+
"additional_context": self._additional_context,
456+
},
457+
"scoring_contract": {
458+
"scoring_function_parameters": self._build_normalized_scorers(),
459+
},
460+
"environment": self._build_environment_params(),
461+
"metadata": self._metadata,
462+
"reference_output": self._reference_output,
463+
"required_environment_variables": self._required_env_vars,
464+
"required_secret_names": self._required_secrets,
465+
"validation_type": self._validation_type,
466+
}
467+
)
453468

454469
def push(self, **options: Unpack[LongRequestOptions]) -> Scenario:
455470
"""Create the scenario on the platform.

0 commit comments

Comments
 (0)