chore(generation): track CTX_DIAG per attempt not per round

Retry attempts were previously conflated with the initial call,
making prompt_tokens and headroom look cumulative and useless for
diagnosing the silent-round behavior. Move start-of-attempt captures
inside the retry loop and emit attempt_start / attempt_end lines so
each attempt's numbers stand alone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-15 20:39:49 -04:00
parent b6165e56e5
commit 1261e93ede
+23 -21
View File
@@ -410,18 +410,20 @@ async def run_generation(
buf.append_event("status", {"status": "Generating response..." if _round == 0 else "Composing response..."})
t_stream = time.monotonic()
round_content_start = len(buf.content_so_far)
round_tokens_start = timing.get("output_tokens") or 0
round_prompt_tokens_start = timing.get("prompt_tokens") or 0
approx_msg_chars = sum(len(str(m.get("content", ""))) for m in messages)
effective_think = think
retried_with_think = False
logger.info(
"CTX_DIAG round_start conv=%d round=%d num_ctx=%d msgs=%d approx_chars=%d think=%s",
conv_id, _round, num_ctx, len(messages), approx_msg_chars, effective_think,
)
attempt = 0
while True:
attempt_content_start = len(buf.content_so_far)
attempt_output_tokens_start = timing.get("output_tokens") or 0
attempt_prompt_tokens_start = timing.get("prompt_tokens") or 0
attempt_tool_calls_start = len(round_tool_calls)
logger.info(
"CTX_DIAG attempt_start conv=%d round=%d attempt=%d num_ctx=%d msgs=%d approx_chars=%d think=%s",
conv_id, _round, attempt, num_ctx, len(messages), approx_msg_chars, effective_think,
)
async for chunk in _stream_with_retry(messages, model, tools, effective_think, num_ctx=num_ctx):
if buf.cancel_event.is_set():
cancelled = True
@@ -523,24 +525,23 @@ async def run_generation(
if cancelled:
break
round_content_added = len(buf.content_so_far) - round_content_start
round_tokens_added = (timing.get("output_tokens") or 0) - round_tokens_start
round_prompt_tokens = (timing.get("prompt_tokens") or 0) - round_prompt_tokens_start
headroom = num_ctx - round_prompt_tokens if round_prompt_tokens else None
attempt_content_added = len(buf.content_so_far) - attempt_content_start
attempt_tokens_added = (timing.get("output_tokens") or 0) - attempt_output_tokens_start
attempt_prompt_tokens = (timing.get("prompt_tokens") or 0) - attempt_prompt_tokens_start
attempt_tool_calls_added = len(round_tool_calls) - attempt_tool_calls_start
headroom = num_ctx - attempt_prompt_tokens if attempt_prompt_tokens else None
is_silent = (
not round_tool_calls
and round_content_added == 0
and round_tokens_added > 0
attempt_tool_calls_added == 0
and attempt_content_added == 0
and attempt_tokens_added > 0
)
logger.info(
"CTX_DIAG round_end conv=%d round=%d think=%s prompt_tokens=%d output_tokens=%d headroom=%s content_added=%d tool_calls=%d silent=%s",
conv_id, _round, effective_think, round_prompt_tokens, round_tokens_added,
headroom, round_content_added, len(round_tool_calls), is_silent,
"CTX_DIAG attempt_end conv=%d round=%d attempt=%d think=%s prompt_tokens=%d output_tokens=%d headroom=%s content_added=%d tool_calls=%d silent=%s",
conv_id, _round, attempt, effective_think, attempt_prompt_tokens, attempt_tokens_added,
headroom, attempt_content_added, attempt_tool_calls_added, is_silent,
)
if (
not round_tool_calls
and round_content_added == 0
and round_tokens_added > 0
is_silent
and not effective_think
and not retried_with_think
):
@@ -549,11 +550,12 @@ async def run_generation(
# think=True — reasoning mode produces more reliable output.
logger.warning(
"Silent round %d for conv %d (tokens=%d); retrying with think=True",
_round, conv_id, round_tokens_added,
_round, conv_id, attempt_tokens_added,
)
buf.append_event("status", {"status": "Retrying with reasoning enabled..."})
effective_think = True
retried_with_think = True
attempt += 1
continue
break