Skip to content

Commit

Permalink
feat(robot-server): update run creation endpoint to accept runtime pa…
Browse files Browse the repository at this point in the history
…rameter values (#14776)

Adds an optional argument `runTimeParameterValues` to the request body of the POST /runs endpoint to start a run with new runtime parameter values.
  • Loading branch information
jbleon95 authored Apr 3, 2024
1 parent 3b3c8e4 commit 6bf0579
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 14 deletions.
19 changes: 11 additions & 8 deletions api/src/opentrons/protocols/parameters/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,17 @@ def ensure_value_type(
This does not guarantee that the value will be the correct type for the given parameter, only that any data coming
in is in the format that we expect. For now, the only transformation it is doing is converting integers represented
as floating points to integers. If something is labelled as an int but is not actually an integer, that will be
caught when it is attempted to be set as the parameter value and will raise the appropriate error there.
as floating points to integers, and bools represented as 1.0/0.0 to True/False.
If something is labelled as a type but does not get converted here, that will be caught when it is attempted to be
set as the parameter value and will raise the appropriate error there.
"""
validated_value: AllowedTypes
if isinstance(value, float) and parameter_type is int and value.is_integer():
validated_value = int(value)
else:
validated_value = value
validated_value: AllowedTypes = value
if isinstance(value, float):
if parameter_type is bool and (value == 0 or value == 1):
validated_value = bool(value)
elif parameter_type is int and value.is_integer():
validated_value = int(value)
return validated_value


Expand Down Expand Up @@ -163,7 +166,7 @@ def validate_type(value: ParamType, parameter_type: type) -> None:
"""Validate parameter value is the correct type."""
if not isinstance(value, parameter_type):
raise ParameterValueError(
f"Parameter value has type {type(value)} must match type {parameter_type}."
f"Parameter value {value} has type {type(value)}, must match type {parameter_type}."
)


Expand Down
5 changes: 4 additions & 1 deletion api/tests/opentrons/protocols/parameters/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,16 @@ def test_validate_options_raises_name_error() -> None:
(2.0, float, 2.0),
(2.2, float, 2.2),
("3.0", str, "3.0"),
(0.0, bool, False),
(1, bool, True),
(3.0, bool, 3.0),
(True, bool, True),
],
)
def test_ensure_value_type(
value: Union[float, bool, str], param_type: type, result: AllowedTypes
) -> None:
"""It should ensure the correct type is there, converting floats to ints."""
"""It should ensure that if applicable, the value is coerced into the expected type"""
assert result == subject.ensure_value_type(value, param_type)


Expand Down
12 changes: 9 additions & 3 deletions robot-server/robot_server/runs/engine_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@
)

from robot_server.protocols.protocol_store import ProtocolResource
from opentrons.protocol_engine.types import DeckConfigurationType
from opentrons.protocol_engine.types import (
DeckConfigurationType,
RunTimeParamValuesType,
)


class EngineConflictError(RuntimeError):
Expand Down Expand Up @@ -154,14 +157,17 @@ async def create(
deck_configuration: DeckConfigurationType,
notify_publishers: Callable[[], None],
protocol: Optional[ProtocolResource],
run_time_param_values: Optional[RunTimeParamValuesType] = None,
) -> StateSummary:
"""Create and store a ProtocolRunner and ProtocolEngine for a given Run.
Args:
run_id: The run resource the engine is assigned to.
labware_offsets: Labware offsets to create the engine with.
protocol: The protocol to load the runner with, if any.
deck_configuration: A mapping of fixtures to cutout fixtures the deck will be loaded with.
notify_publishers: Utilized by the engine to notify publishers of state changes.
protocol: The protocol to load the runner with, if any.
run_time_param_values: Any runtime parameter values to set.
Returns:
The initial equipment and status summary of the engine.
Expand Down Expand Up @@ -217,7 +223,7 @@ async def create(
# was uploaded before we added stricter validation, and that
# doesn't conform to the new rules.
python_parse_mode=PythonParseMode.ALLOW_LEGACY_METADATA_AND_REQUIREMENTS,
run_time_param_values=None,
run_time_param_values=run_time_param_values,
)
elif isinstance(runner, JsonRunner):
assert (
Expand Down
4 changes: 4 additions & 0 deletions robot-server/robot_server/runs/router/base_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ async def create_run(
"""
protocol_id = request_body.data.protocolId if request_body is not None else None
offsets = request_body.data.labwareOffsets if request_body is not None else []
rtp_values = (
request_body.data.runTimeParameterValues if request_body is not None else None
)
protocol_resource = None

deck_configuration = await deck_configuration_store.get_deck_configuration()
Expand All @@ -185,6 +188,7 @@ async def create_run(
created_at=created_at,
labware_offsets=offsets,
deck_configuration=deck_configuration,
run_time_param_values=rtp_values,
protocol=protocol_resource,
notify_publishers=notify_publishers,
)
Expand Down
6 changes: 6 additions & 0 deletions robot-server/robot_server/runs/run_data_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
CurrentCommand,
Command,
)
from opentrons.protocol_engine.types import RunTimeParamValuesType

from robot_server.protocols.protocol_store import ProtocolResource
from robot_server.service.task_runner import TaskRunner
Expand Down Expand Up @@ -142,6 +143,7 @@ async def create(
created_at: datetime,
labware_offsets: List[LabwareOffsetCreate],
deck_configuration: DeckConfigurationType,
run_time_param_values: Optional[RunTimeParamValuesType],
notify_publishers: Callable[[], None],
protocol: Optional[ProtocolResource],
) -> Union[Run, BadRun]:
Expand All @@ -151,7 +153,10 @@ async def create(
run_id: Identifier to assign the new run.
created_at: Creation datetime.
labware_offsets: Labware offsets to initialize the engine with.
deck_configuration: A mapping of fixtures to cutout fixtures the deck will be loaded with.
notify_publishers: Utilized by the engine to notify publishers of state changes.
run_time_param_values: Any runtime parameter values to set.
protocol: The protocol to load the runner with, if any.
Returns:
The run resource.
Expand All @@ -173,6 +178,7 @@ async def create(
labware_offsets=labware_offsets,
deck_configuration=deck_configuration,
protocol=protocol,
run_time_param_values=run_time_param_values,
notify_publishers=notify_publishers,
)
run_resource = self._run_store.insert(
Expand Down
5 changes: 5 additions & 0 deletions robot-server/robot_server/runs/run_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
Liquid,
CommandNote,
)
from opentrons.protocol_engine.types import RunTimeParamValuesType
from opentrons_shared_data.errors import GeneralError
from robot_server.service.json_api import ResourceModel
from robot_server.errors.error_responses import ErrorDetails
Expand Down Expand Up @@ -212,6 +213,10 @@ class RunCreate(BaseModel):
default_factory=list,
description="Labware offsets to apply as labware are loaded.",
)
runTimeParameterValues: Optional[RunTimeParamValuesType] = Field(
None,
description="Key-value pairs of run-time parameters defined in a protocol.",
)


class RunUpdate(BaseModel):
Expand Down
9 changes: 8 additions & 1 deletion robot-server/tests/runs/router/test_base_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ async def test_create_run(
labware_offsets=[labware_offset_create],
deck_configuration=[],
protocol=None,
run_time_param_values=None,
notify_publishers=mock_notify_publishers,
)
).then_return(expected_response)
Expand Down Expand Up @@ -169,12 +170,17 @@ async def test_create_protocol_run(
labware_offsets=[],
deck_configuration=[],
protocol=protocol_resource,
run_time_param_values={"foo": "bar"},
notify_publishers=mock_notify_publishers,
)
).then_return(expected_response)

result = await create_run(
request_body=RequestModel(data=RunCreate(protocolId="protocol-id")),
request_body=RequestModel(
data=RunCreate(
protocolId="protocol-id", runTimeParameterValues={"foo": "bar"}
)
),
protocol_store=mock_protocol_store,
run_data_manager=mock_run_data_manager,
run_id=run_id,
Expand Down Expand Up @@ -232,6 +238,7 @@ async def test_create_run_conflict(
labware_offsets=[],
deck_configuration=[],
protocol=None,
run_time_param_values=None,
notify_publishers=mock_notify_publishers,
)
).then_raise(EngineConflictError("oh no"))
Expand Down
10 changes: 9 additions & 1 deletion robot-server/tests/runs/test_run_data_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ async def test_create(
labware_offsets=[],
protocol=None,
deck_configuration=[],
run_time_param_values=None,
notify_publishers=mock_notify_publishers,
)
).then_return(engine_state_summary)
Expand All @@ -160,6 +161,7 @@ async def test_create(
labware_offsets=[],
protocol=None,
deck_configuration=[],
run_time_param_values=None,
notify_publishers=mock_notify_publishers,
)

Expand Down Expand Up @@ -187,7 +189,7 @@ async def test_create_with_options(
engine_state_summary: StateSummary,
run_resource: RunResource,
) -> None:
"""It should handle creation with a protocol and labware offsets."""
"""It should handle creation with a protocol, labware offsets and parameters."""
run_id = "hello world"
created_at = datetime(year=2021, month=1, day=1)

Expand All @@ -210,6 +212,7 @@ async def test_create_with_options(
labware_offsets=[labware_offset],
protocol=protocol,
deck_configuration=[],
run_time_param_values={"foo": "bar"},
notify_publishers=mock_notify_publishers,
)
).then_return(engine_state_summary)
Expand All @@ -228,6 +231,7 @@ async def test_create_with_options(
labware_offsets=[labware_offset],
protocol=protocol,
deck_configuration=[],
run_time_param_values={"foo": "bar"},
notify_publishers=mock_notify_publishers,
)

Expand Down Expand Up @@ -263,6 +267,7 @@ async def test_create_engine_error(
labware_offsets=[],
protocol=None,
deck_configuration=[],
run_time_param_values=None,
notify_publishers=mock_notify_publishers,
)
).then_raise(EngineConflictError("oh no"))
Expand All @@ -274,6 +279,7 @@ async def test_create_engine_error(
labware_offsets=[],
protocol=None,
deck_configuration=[],
run_time_param_values=None,
notify_publishers=mock_notify_publishers,
)

Expand Down Expand Up @@ -651,6 +657,7 @@ async def test_create_archives_existing(
labware_offsets=[],
protocol=None,
deck_configuration=[],
run_time_param_values=None,
notify_publishers=mock_notify_publishers,
)
).then_return(engine_state_summary)
Expand All @@ -669,6 +676,7 @@ async def test_create_archives_existing(
labware_offsets=[],
protocol=None,
deck_configuration=[],
run_time_param_values=None,
notify_publishers=mock_notify_publishers,
)

Expand Down

0 comments on commit 6bf0579

Please sign in to comment.