refactor(agent): share the hidden interrupt placeholder row
message_sanitization.close_interrupted_tool_sequence and the turn_api_call repetition-loop exit each spelled the same hidden assistant row literal (content "", display_kind hidden, api_content _INTERRUPTED_PLACEHOLDER), and the conversation loop's redirect built it field by field. Add hidden_interrupt_placeholder_row() to the leaf placeholders module and use it at all three sites so the shape cannot drift between them. Gate rP finding 6 (reuse).
This commit is contained in:
@@ -18,3 +18,9 @@ _INTERRUPTED_PLACEHOLDER = "[interrupt: no assistant output for this turn]"
|
||||
# The spelling shipped before #132949. Models reproduce it verbatim, so rows already carrying it
|
||||
# stay poisoned after the wording change; the replay filters reference this name to retire them.
|
||||
_LEGACY_INTERRUPTED_PLACEHOLDER = "[response interrupted]"
|
||||
|
||||
|
||||
def hidden_interrupt_placeholder_row() -> dict:
|
||||
"""A fresh assistant row that is invisible in transcripts but carries the placeholder as its
|
||||
non-empty ``api_content``, so the pre-call sanitizer does not re-heal it every call (#88955)."""
|
||||
return {"role": "assistant", "content": "", "display_kind": "hidden", "api_content": _INTERRUPTED_PLACEHOLDER}
|
||||
|
||||
@@ -343,15 +343,10 @@ def _apply_active_turn_redirect(agent: Any, messages: List[Dict[str, Any]], text
|
||||
# preserves alternation only — scaffold bytes must never land in it, since api_content
|
||||
# is substituted back into content on replay (#81841).
|
||||
if not (messages and messages[-1].get("role") == "assistant"):
|
||||
placeholder: Dict[str, Any] = {"role": "assistant", "content": visible or ""}
|
||||
if not visible:
|
||||
placeholder["display_kind"] = "hidden"
|
||||
# Hidden row, but a non-empty neutral api_content so the pre-call sanitizer
|
||||
# does not re-heal it every call (#88955). Never _INTERRUPT_SCAFFOLD_MARKER:
|
||||
# as assistant text the model echoes it (#81841).
|
||||
from agent.agent_runtime_helpers_placeholders import _INTERRUPTED_PLACEHOLDER
|
||||
placeholder["api_content"] = _INTERRUPTED_PLACEHOLDER
|
||||
append_message(messages, placeholder)
|
||||
# Hidden row with a neutral api_content (#88955). Never _INTERRUPT_SCAFFOLD_MARKER:
|
||||
# as assistant text the model echoes it (#81841).
|
||||
from agent.agent_runtime_helpers_placeholders import hidden_interrupt_placeholder_row
|
||||
append_message(messages, {"role": "assistant", "content": visible} if visible else hidden_interrupt_placeholder_row())
|
||||
# Transcript shows the user's own words; the provider replays the scaffolded form.
|
||||
append_message(messages, {"role": "user", "content": text, "api_content": correction})
|
||||
|
||||
|
||||
@@ -302,16 +302,11 @@ def close_interrupted_tool_sequence(messages: list, final_response: Any = None)
|
||||
return False
|
||||
text = final_response if isinstance(final_response, str) else ""
|
||||
from agent.message_metadata import append_message
|
||||
from agent.agent_runtime_helpers_placeholders import _INTERRUPTED_PLACEHOLDER
|
||||
from agent.agent_runtime_helpers_placeholders import _INTERRUPTED_PLACEHOLDER, hidden_interrupt_placeholder_row
|
||||
|
||||
stripped = text.strip()
|
||||
if not stripped or stripped == _INTERRUPTED_PLACEHOLDER:
|
||||
append_message(messages, {
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"display_kind": "hidden",
|
||||
"api_content": _INTERRUPTED_PLACEHOLDER,
|
||||
})
|
||||
append_message(messages, hidden_interrupt_placeholder_row())
|
||||
else:
|
||||
append_message(messages, {"role": "assistant", "content": stripped})
|
||||
return True
|
||||
|
||||
@@ -15,7 +15,7 @@ import time
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from agent.error_classifier import FailoverReason
|
||||
from agent.agent_runtime_helpers_placeholders import _INTERRUPTED_PLACEHOLDER
|
||||
from agent.agent_runtime_helpers_placeholders import hidden_interrupt_placeholder_row
|
||||
from agent.message_metadata import append_message
|
||||
from agent.repetition_guard import REPETITION_LOOP_INTERRUPTED, is_runaway_repetition
|
||||
from agent.turn_failure_copy import site_copy, stamp_failure
|
||||
@@ -198,10 +198,7 @@ def handle_api_interrupt(
|
||||
# The interrupted row is replayed next turn; looped bytes there re-seed the loop
|
||||
# (#112764). Same hidden shape as the redirect placeholder: nothing visible in the
|
||||
# transcript, a neutral api_content so the pre-call sanitizer does not re-heal it.
|
||||
append_message(messages, {
|
||||
"role": "assistant", "content": "", "display_kind": "hidden",
|
||||
"api_content": _INTERRUPTED_PLACEHOLDER,
|
||||
})
|
||||
append_message(messages, hidden_interrupt_placeholder_row())
|
||||
final_response = REPETITION_LOOP_INTERRUPTED
|
||||
elif _partial:
|
||||
append_message(messages, {
|
||||
|
||||
Reference in New Issue
Block a user