-
Notifications
You must be signed in to change notification settings - Fork 3.7k
feat(lifecycle): add on_turn_start / on_turn_end hooks with TurnControl #2911
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -820,6 +820,29 @@ async def _save_stream_items_without_count( | |
| streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) | ||
| break | ||
|
|
||
| run_hook_control, agent_hook_control = await asyncio.gather( | ||
| hooks.on_turn_start(context_wrapper, current_agent, current_turn), | ||
| ( | ||
| current_agent.hooks.on_turn_start( | ||
| context_wrapper, current_agent, current_turn | ||
| ) | ||
| if current_agent.hooks | ||
| else _coro.noop_coroutine() | ||
| ), | ||
| ) | ||
| if run_hook_control == "stop" or agent_hook_control == "stop": | ||
| logger.debug( | ||
| "Turn %s: on_turn_start hook requested stop; halting run.", | ||
| current_turn, | ||
| ) | ||
| streamed_result._max_turns_handled = True | ||
| streamed_result.current_turn = current_turn | ||
| if run_state is not None: | ||
| run_state._current_turn = current_turn | ||
| run_state._current_step = None | ||
| streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) | ||
| break | ||
|
|
||
| if current_turn == 1: | ||
| all_input_guardrails = starting_agent.input_guardrails + ( | ||
| run_config.input_guardrails or [] | ||
|
|
@@ -909,6 +932,17 @@ async def _save_stream_items_without_count( | |
| tool_use_tracker | ||
| ) | ||
|
|
||
| await asyncio.gather( | ||
| hooks.on_turn_end(context_wrapper, current_agent, current_turn), | ||
| ( | ||
| current_agent.hooks.on_turn_end( | ||
| context_wrapper, current_agent, current_turn | ||
|
Comment on lines
+935
to
+939
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The streaming loop has the same premature Useful? React with 👍 / 👎. |
||
| ) | ||
| if current_agent.hooks | ||
| else _coro.noop_coroutine() | ||
| ), | ||
| ) | ||
|
|
||
| streamed_result.raw_responses = streamed_result.raw_responses + [ | ||
| turn_result.model_response | ||
| ] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the sync runner,
on_turn_endis fired immediately afterrun_single_turnreturns, even whennext_stepisNextStepInterruption(tool approval pending). In that path the turn has not actually finished—resolve_interrupted_turncontinues the same turn later—so this reports a false turn completion and the hook is never re-fired at the real end. Any hook that compacts state, records per-turn metrics, or enforces turn-level cancellation will run too early for approval-based workflows.Useful? React with 👍 / 👎.