feat(agent): add concurrent_invocation_mode parameter#1707
feat(agent): add concurrent_invocation_mode parameter#1707
Conversation
Add a new parameter to Agent.__init__ that controls concurrent invocation behavior: - 'throw' (default): Raises ConcurrencyException if concurrent invocation is attempted, maintaining existing behavior - 'unsafe_reentrant': Skips lock acquisition entirely, allowing concurrent invocations (restores pre-locking behavior) This enables power users to re-invoke the same agent without exceptions when needed, while preserving safe defaults for typical usage. Resolves #1702
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
Assessment: Approve Clean implementation that provides an escape hatch for power users who need concurrent agent invocations while maintaining safe defaults. Review Summary
Note: This PR introduces a new public API parameter. Per the API Bar Raising guidelines, moderate changes like this benefit from informal API review. Consider adding the |
Address PR feedback: - Update existing exception tests to explicitly pass concurrent_invocation_mode='throw' - Remove redundant test_agent_concurrent_invocation_mode_throw_raises_exception test - Rely on test_agent_concurrent_invocation_mode_default_is_throw for default verification
Address PR feedback: - Add explicit warning about unsafe_reentrant making no guarantees - Remove parenthetical about restoring pre-lock behavior - Update both type definition and Agent.__init__ docstrings for consistency
|
Thanks for the documentation update! The explicit warnings about |
src/strands/agent/agent.py
Outdated
| "Agent is already processing a request. Concurrent invocations are not supported." | ||
| ) | ||
| lock_acquired = False | ||
| if self._concurrent_invocation_mode == "throw": |
There was a problem hiding this comment.
Nit: Let's use enums for the concurrent mode values
There was a problem hiding this comment.
This has been addressed in the latest commit - ConcurrentInvocationMode is now a str Enum that supports both enum values (ConcurrentInvocationMode.THROW) and string values ("throw").
|
Re-review after enum refactor The switch from ✅ Enum benefits: Better IDE autocompletion, self-documenting, can add methods later if needed The implementation looks good. Ready to merge once the design question about public vs private instance variables is resolved (I've added context in my reply to that thread). |
| def invoke(): | ||
| try: | ||
| result = agent("test") | ||
| with lock: |
There was a problem hiding this comment.
Nit: I don't think this lock is necessary. Even if the GIL was disabled, list append is thread safe.
Motivation
Users who need to re-trigger an agent during invocation (e.g., to provide feedback out-of-band) are currently blocked by
ConcurrencyExceptionintroduced in #1453. While concurrent invocation detection is valuable for safety, power users requested an escape hatch to restore the previous behavior when they need concurrent re-invocations.Resolves #1702
Public API Changes
Agent.__init__now accepts aconcurrent_invocation_modeparameter:The parameter accepts:
"throw"(default): Maintains existing behavior - raisesConcurrencyExceptionon concurrent invocation attempts"unsafe_reentrant": Skips lock acquisition entirely, allowing concurrent invocationsThe new type
ConcurrentInvocationModeis available viastrands.types.agent.ConcurrentInvocationModefor type annotations.Use Cases
"interrupt"mode to cleanly interrupt agents with new user messages