-
Notifications
You must be signed in to change notification settings - Fork 4.6k
fix(sessions): reject resuming a run whose accepted terminal output was not persisted #4698
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
base: main
Are you sure you want to change the base?
Changes from all commits
9d2958d
d9b541f
06ff1f8
71ed7f8
a9de927
aafce1c
ea43537
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 |
|---|---|---|
|
|
@@ -102,6 +102,7 @@ | |
| apply_resumed_conversation_settings, | ||
| attach_usage_to_span, | ||
| get_unsent_tool_call_ids_for_interrupted_state, | ||
| reject_unrecoverable_terminal_state, | ||
| snapshot_usage, | ||
| usage_delta, | ||
| validate_output_guardrails_with_server_managed_conversation, | ||
|
|
@@ -628,6 +629,10 @@ async def _finalize_streamed_final_output( | |
| # Saved as one ordered batch so the session mirrors the model response. Doing it in two | ||
| # halves would both reorder the turn and, because the first save advances the turn's | ||
| # persisted-item count, make the second one a no-op. | ||
| # The output, its guardrails, and its terminal hooks are all complete, so from here until | ||
| # the turn is persisted this run owns a result no resume can reproduce. | ||
| if streamed_result._state is not None: | ||
| streamed_result._state._terminal_unrecoverable = True | ||
|
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.
When a streamed terminal Session append fails, callers can obtain the documented recovery checkpoint via AGENTS.md reference: AGENTS.md:L147-L147 Useful? React with 👍 / 👎. |
||
| if on_persisted_after_guardrails is None: | ||
| await save_items(final_turn_items, response_id, store_setting) | ||
| else: | ||
|
|
@@ -640,6 +645,10 @@ async def _finalize_streamed_final_output( | |
| streamed_result.is_complete = True | ||
| streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) | ||
| return | ||
| # The append and any post-append maintenance both succeeded, so the turn is durable and the | ||
| # state is open again. | ||
| if streamed_result._state is not None: | ||
| streamed_result._state._terminal_unrecoverable = False | ||
|
|
||
| streamed_result.final_output = output | ||
| if on_persisted_after_guardrails is not None: | ||
|
|
@@ -923,6 +932,7 @@ async def start_streaming( | |
| streamed_result._reasoning_item_id_policy = resolved_reasoning_item_id_policy | ||
|
|
||
| if is_resumed_state and run_state is not None: | ||
| reject_unrecoverable_terminal_state(run_state) | ||
| await resume_pending_session_write(run_state, session, wrapper=context_wrapper) | ||
| streamed_result._current_turn_persisted_item_count = ( | ||
| run_state._current_turn_persisted_item_count | ||
|
|
||
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.
For a resumed run that reaches a configured
max_turnshandler, the handler produces a final output, runs end hooks and output guardrails, then its non-streamed save callback still callssave_final_turn_items_after_guardrails(..., run_state=None)atrun.py:1532. Unlike the ordinary terminal branches marked here, an append failure leaves the suppliedRunStateunmarked and retrying it runs the max-turn handler and its hooks again. Arm and clear the same terminal marker around that fallback's post-acceptance persistence path.AGENTS.md reference: AGENTS.md:L147-L147
Useful? React with 👍 / 👎.