Input
Inputs are typed parameters you declare on a workflow or phase. They are provided at run time — when a run is started, or when a phase is triggered manually. This lets a single workflow definition handle different samples, concentrations, or experimental conditions without changing the code. This is particularly useful for presenting error recovery options to the human or machine user on the phase level for decision-making.
Workflow inputs
Declare inputs as typed function parameters on the @workflow function:
from unitelabs.sdk.automate import workflow, phase
@workflow(name="ELISA")
async def main_workflow(sample_id: str, concentration: float = 1.0):
plate = await sample_preparation(sample_id)
await detection(plate, min_concentration=concentration)
When the run is started via the platform UI, the operator provides the sample_id. concentration is optional and defaults to 1.0. If triggered through the API, these values are sent as JSON in the request body.
Phase inputs
Phases also accept typed parameters. They receive values passed by the workflow or from the output of a previous phase:
@phase()
async def detection(target: Plate, min_concentration: float):
result = await measure(target)
assert result.concentration >= min_concentration
Reading inputs via the context object
Only workflow inputs are published automatically to the run's context. The moment main_workflow above is called, sample_id and concentration become readable anywhere in the run via get_context().workflow_parameters — without adding them to any phase's or step's own signature:
from unitelabs.sdk import get_context, step
@step()
async def measure(target: Plate):
concentration = get_context().workflow_parameters.get("concentration", 1.0)
...
This is the recommended way to thread run-wide settings — run mode, simulation flags, feature flags — through many phases and steps: declare them once as workflow inputs, read them from context wherever they're needed. Adding a new workflow input later never requires touching an existing phase's or step's signature, since nothing downstream needs to name it as an argument to see it.
Phase inputs, like target and min_concentration above, work differently: they stay ordinary function arguments, passed explicitly by whatever calls the phase, and are not added to workflow_parameters. A phase can still read the parameters its enclosing workflow declared, via the same get_context().workflow_parameters.
workflow_parameters is a read-only view (types.MappingProxyType), populated only from the top-level @workflow call — treat values read from it as immutable. It is only available with SDK >= 0.15.0 or liquid handling SDK >= 0.34.0.Input types
Inputs support standard Python types and are validated before the run starts:
| Type | Example |
|---|---|
str | Sample identifiers, protocol names |
int, float | Volumes, concentrations, durations |
bool | Feature flags, approval gates |
list | Lists of values |
dict | Key-value mappings |
| Complex data types | Nested structures |
| Pydantic models | Structured sample metadata |
Providing inputs via the API
curl -X POST https://api.unitelabs.io/v1/workflows/{workflow_id}/runs \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"inputs": {"sample_id": "S-042", "concentration": 2.5}}'
Related concepts
- Workflow: where inputs are declared
- Human in the Loop: pausing for operator-provided inputs at phase execution time
- Runs: inputs are bound to a specific run and stored with the run record