Skip to content

feat(termination): add pluggable termination conditions for multi-age… - #118

Open
davidmonterocrespo24 wants to merge 1 commit into
google:mainfrom
davidmonterocrespo24:feat/termination-conditions
Open

feat(termination): add pluggable termination conditions for multi-age…#118
davidmonterocrespo24 wants to merge 1 commit into
google:mainfrom
davidmonterocrespo24:feat/termination-conditions

Conversation

@davidmonterocrespo24

Copy link
Copy Markdown

Summary

Adds a new google.adk_community.termination module with pluggable termination
conditions for use with LoopAgent and Runner-based multi-agent workflows.

This contribution was originally submitted to google/adk-python as
PR #5213 (referencing
issue #5211 and
adk-js#193), and was redirected
here by maintainer @rohityan.

New Module: google.adk_community.termination

Termination Conditions

ClassDescription
MaxIterationsTerminationStops after N events
TextMentionTerminationStops when a keyword appears in event content
TimeoutTerminationStops after a wall-clock duration
TokenUsageTerminationStops when total/prompt/completion token budgets are exceeded
FunctionCallTerminationStops when a specific function response is received
ExternalTerminationStops on external signal via .set()
OrTerminationConditiona | b — stops if either condition is met
AndTerminationConditiona & b — stops only when both are met

All conditions implement:

  • check(events: list[Event]) -> TerminationResult
  • reset() — called automatically at run start, safe to reuse across invocations
  • terminated property

Usage Example

fromgoogle.adk_community.terminationimport (
MaxIterationsTermination,
TextMentionTermination,
)
# Stop after 10 events OR when "DONE" appearscondition=MaxIterationsTermination(10) |TextMentionTermination("DONE")

…nt workflows
Add a new google.adk_community.termination module with pluggable termination
conditions for use with LoopAgent and Runner-based multi-agent workflows.
New termination conditions:
- MaxIterationsTermination: stops after N events
- TextMentionTermination: stops when a keyword appears in event content
- TimeoutTermination: stops after a wall-clock duration
- TokenUsageTermination: stops when token budgets are exceeded
- FunctionCallTermination: stops when a specific function is called
- ExternalTermination: stops on external signal (set() method)
- OrTerminationCondition / AndTerminationCondition composites (via | and &)
All conditions implement check(events), reset(), and the terminated property.
reset() is called automatically at the start of each run so the same instance
can be reused across invocations.
Related: google/adk-python#5211
Related: google/adk-python#5213
Cross-repo: google/adk-js#193
@DeanChensj

Copy link
Copy Markdown
Collaborator

@gemini-cli /review

@github-actions

Copy link
Copy Markdown

🤖 Hi @DeanChensj, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for the PR! The termination conditions are well-implemented and the test coverage is excellent.

I've left a few minor suggestions regarding state consistency in compound conditions and more descriptive termination reasons. Overall, this is a great addition to the community package.

self,
left: TerminationCondition,
right: TerminationCondition,
) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In OrTerminationCondition, if the left condition matches, the right condition is never checked. While this works for the immediate termination, it means the right condition misses these events, which could lead to inconsistent internal state (e.g., token counts or iteration counts) if the condition is reused or part of a more complex logic.

Consider checking both sides even if the first one returns a result:

left_result=awaitself._left.check(events)
right_result=awaitself._right.check(events)
ifleft_result:
self._terminated=Truereturnleft_resultifright_result:
self._terminated=Truereturnright_result


@property
def terminated(self) -> bool:
return self._terminated

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The generic reason "All termination conditions met" loses the specific context of why the underlying conditions terminated. It might be helpful to combine the reasons from both left and right.

Sign up for freeto 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.

2 participants

@davidmonterocrespo24@DeanChensj