UniteLabs
How-to

Migrate the Liquid Handling SDK

Breaking changes between Liquid Handling SDK versions and how to update your code.

For breaking changes in unitelabs-sdk, see Migrate the SDK.

Liquid Handling SDK 0.42: lineage is recorded for you

What changed

In unitelabs-liquid-handling 0.41 and earlier, a workflow author had to record lineage with their own Audit.emit calls and write the deck's labware to ctx.state["labware_context"]. In 0.42 and later, the library records tip, liquid, gripper, and labware operations on a liquid handler by default, and describes the deck's labware itself. See Lineage from liquid handling.

unitelabs-liquid-handling 0.42 requires unitelabs-sdk 0.20 or later. The UniteLabs Platform must be at 0.34.0 or later to show the new events correctly.

Upgrade in this order

  1. Make sure your Platform is at 0.34.0 or later.
  2. Update unitelabs-liquid-handling to 0.42.0 or later in each workflow's pyproject.toml.
  3. Remove your liquid-handling lineage code (see Code to change), or switch the library off (see Switch recording off during the transition).
  4. Run the workflow against a mock with Audit.capture and compare the events with an earlier run. See Check the events locally.

Code to change

If your workflow emits its own lineage for liquid-handling operations, each operation is recorded twice after the upgrade. To find this code, search for Audit.emit, labware_context, and flush_workspace. Removing your own lineage events is the recommended upgrade path.

unitelabs-liquid-handling 0.41 and earlier0.42 and later
Audit.emit(...) after a tip, aspirate, dispense, or gripper callRemove it. The library records the operation.
Deck labware written to ctx.state["labware_context"]Remove it. The library describes the deck.
Audit.flush_workspace() after filling labware_contextRemove it. The SDK sends the workspace at each phase boundary.
load_carrier and unload_carrier steps emit one carrier eventThe autoload records one move_labware between the loading tray and the deck. The plates on the carrier keep their history.
Channel IDs channel:<n>Channel IDs channel:<actor>:<module>:<n>
One waste, Deck:<position>, <tool>:parkwaste:<actor>:<name>, deck:<actor>:<position>, park:<actor>:<tool>, and loading_tray:<actor>:<track> for a carrier on the tray

A failed carrier load or unload is still an error event, on move_labware.

Keep Audit.emit calls for devices that are not liquid handlers. See Emit lineage events.

Switch recording off during the transition

Alternatively, switch the library's automatic lineage off for selected operations, or entirely. The switch applies to everything called inside the with block, also inside phases and steps:

from unitelabs.liquid_handling import lineage
from unitelabs.sdk import Operation

# The library skips these operations. Your own events record them.
with lineage.disable(Operation.ASPIRATE, Operation.DISPENSE):
    ...

# The library records nothing.
with lineage.disable():
    ...

UNITELABS_LIQUID_HANDLING_LINEAGE=0 in the environment switches recording off for the whole Python process. Use it for a script, notebook, or test run. For a deployed workflow, use lineage.disable() in code.

lineage.disable and UNITELABS_LIQUID_HANDLING_LINEAGE are for the transition period only. They will be deprecated and removed in a later release. We recommend that you remove your own liquid-handling events and let the library record them.

Code that reads channel IDs

If your code or an integration parses entity IDs, read the channel index from the last part of the ID, or from extras["channel"]. Do not read it from the second part, which is now the actor. The same goes for the deck, the loading tray, park sites and wastes: deck:star:7 is track 7 of the STAR's deck, not labware called deck. Read kind and the parts on each side instead of splitting the ID.

Last updated