Skip to content

feat(api): add keep_last_tip argument to liquid class transfer methods #18693

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 20, 2025

Conversation

jbleon95
Copy link
Contributor

Overview

Closes AUTH-1996.

This PR adds a new argument keep_last_tip to three liquid class transfer functions (transfer_with_liquid_class, consolidate_with_liquid_class, and distribute_with_liquid_class). If this is set to True, the last (or only tip for never and once) will not be dropped at the end of the high level transfer/consolidate/distribute. Otherwise, the last tip will be dropped, even if the tip policy is never.

In order to maintain existing behavior, this new argument will actually default to None to represent it not being set. If the value is None, we will default to keep_last_tip=False for all tip policies EXCEPT never, which will default to True. This preserves existing behavior and ensures that if not set, it continues to work as expect and if set, works in a logical and orthogonal way.

Test Plan and Hands on Testing

Unit tests and integration tests should cover for any regressions, and the following protocol should work as described in the comments

requirements = {
	"robotType": "Flex",
	"apiLevel": "2.24"
}

metadata = {
    "protocolName":'Liquid Class keep last tip',
    'author':'Jeremy'
}

def run(protocol_context):
	tiprack = protocol_context.load_labware("opentrons_flex_96_tiprack_1000ul", "C2")
	trash = protocol_context.load_trash_bin('A3')
	pipette_1k = protocol_context.load_instrument("flex_1channel_1000", "right", tip_racks=[tiprack])
	nest_plate = protocol_context.load_labware("nest_96_wellplate_2ml_deep", "D1")
	arma_plate = protocol_context.load_labware("armadillo_96_wellplate_200ul_pcr_full_skirt", "D3")

	water_class = protocol_context.get_liquid_class("water")

	pipette_1k.pick_up_tip()
	
	# Tip should stay on at the end, default behavior
	pipette_1k.transfer_with_liquid_class(
		liquid_class=water_class,
		volume=200,
		source=nest_plate['A1'],
		dest=arma_plate['A1'],
		new_tip="never",
		trash_location=trash,
	)
	# Tip should be dropped at the end
	pipette_1k.transfer_with_liquid_class(
		liquid_class=water_class,
		volume=200,
		source=nest_plate['A1'],
		dest=arma_plate['A1'],
		new_tip="never",
		trash_location=trash,
		keep_last_tip=False,
	)

	# Tip should be dropped at the end, default behavior
	pipette_1k.transfer_with_liquid_class(
		liquid_class=water_class,
		volume=200,
		source=nest_plate['A1'],
		dest=arma_plate['A1'],
		new_tip="once",
		trash_location=trash,
	)
	# Tip should be kept at the end
	pipette_1k.transfer_with_liquid_class(
		liquid_class=water_class,
		volume=200,
		source=nest_plate['A1'],
		dest=arma_plate['A1'],
		new_tip="once",
		trash_location=trash,
		keep_last_tip=True,
	)
	pipette_1k.drop_tip()

Changelog

  • add keep_last_tip to transfer_with_liquid_class, consolidate_with_liquid_class, and distribute_with_liquid_class to control whether the last (or only) tip used is dropped at the end

Review requests

Mostly name and doc string related suggestions.

Risk assessment

Low-medium, theres changes to all three liquid class functions but the scope is pretty small and the changes overall do not complicate the code.

@jbleon95 jbleon95 requested review from sanni-t, ddcc4 and jerader June 20, 2025 15:31
@jbleon95 jbleon95 requested a review from a team as a code owner June 20, 2025 15:31
"""Resolve the liquid class transfer argument `keep_last_tip`
If set to a boolean value, maintains that setting. Otherwise, default to
`True` if tip policy is `NEVER`, otherwise default to `False`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to mention what the return value of this function means -- i.e., if this function returns True, we are really keeping the last tip.

False
if is_last_step and new_tip == TransferTipPolicyV2.NEVER
else True
False if is_last_step and keep_last_tip else True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is saying we never want to end the whole transfer with an air gap in the tip?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, because we want the tip to be empty if we are keeping it

@@ -1205,6 +1205,7 @@ def transfer_with_liquid_class( # noqa: C901
starting_tip: Optional[WellCore],
trash_location: Union[Location, TrashBin, WasteChute],
return_tip: bool,
keep_last_tip: bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This keep_last_tip is after you've resolved it, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, at this point it will either reflect the default or the user's intentions

Copy link
Contributor

@ecormany ecormany left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting some changes to docstrings (which are :meta private: in this branch, but will become public by the release of 8.5).

Also, regarding docstrings and versioning: is this param retconned into API level 2.23 along with the various _with_liquid_classes, or should we call it out as added in 2.24?

Comment on lines 1837 to 1839
:param keep_last_tip: If set to ``True``, do not drop or return the last tip used in the transfer. If set to
``False``, the last tip will be dropped or returned. If not set, default depends on tip policy chosen. A
tip policy of ``"never"`` will default to ``True``, all other tip policies will default to ``False``.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prose suggestions below. Highest priority change is to use the term new_tip (the public parameter name) instead of "tip policy" (an internal implementation name).

Suggested change
:param keep_last_tip: If set to ``True``, do not drop or return the last tip used in the transfer. If set to
``False``, the last tip will be dropped or returned. If not set, default depends on tip policy chosen. A
tip policy of ``"never"`` will default to ``True``, all other tip policies will default to ``False``.
:param keep_last_tip: When ``True``, the pipette keeps the last tip used in the transfer attached. When
``False``, the last tip will be dropped or returned. If not set, behavior depends on the value of ``new_tip``.
``new_tip="never"`` keeps the tip, and all other values of ``new_tip`` drop or return the tip.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And replicate any changes here in the other two functions, modulo action name (transfer/consolidate/distribute).

Copy link
Collaborator

@jerader jerader left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested with a protocol I made in PD with 3 steps: transfer, consolidate, and distribute. each with a new_tip:"once" and keep_last_tip=True with drop_tip()s in between and passed analysis 🚀

@jbleon95 jbleon95 requested a review from ecormany June 20, 2025 18:58
Copy link
Contributor

@ecormany ecormany left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New param docstrings look good. Broader things (unhiding docstrings, choosing a version decorator) can be done elsewhere.

@jbleon95 jbleon95 merged commit 6c65dac into chore_release-8.5.0 Jun 20, 2025
24 checks passed
@jbleon95 jbleon95 deleted the keep_tip_transfer_arg branch June 20, 2025 19:54
@ddcc4
Copy link
Contributor

ddcc4 commented Jun 20, 2025

OK, implementation looks fine.

The issue of the public state vs internal state not knowing that the tip is still attached is a separate bug that we can fix afterwards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants