Skip to content

fix(flash): stop after consecutive tool-less turns instead of looping - #217

Open
glayyiyi wants to merge 1 commit into
google:mainfrom
glayyiyi:fix/flash-silent-turn-guard
Open

glayyiyi wants to merge 1 commit into
google:mainfrom
glayyiyi:fix/flash-silent-turn-guard

Conversation

@glayyiyi

Copy link
Copy Markdown

Summary

flash.max_turns defaults to 0, which means unbounded. A turn without a tool call is nudged and the loop continues. When the model keeps answering with nothing, an unbounded Flash run calls the model about once a second until the caller gives up. An empty reply (stop_reason refusal, 0 output tokens) is one such case.

I hit this with Claude Opus 5.5 on Amazon Bedrock. One run reached turn 449 before I stopped it, and every turn was a full-context call.

This PR adds flash.max_silent_turns (default 5). After that many consecutive turns without a tool call, the run ends as failed. The explanation names the count, the last stop_reason and the start of the last reply, so the cause is visible in the report. Any tool call resets the counter. Setting it to 0 disables the guard. A single nudged turn still continues exactly as before.

Tests

tests/unit/agents/test_flash_runner_ledger.py adds two tests:

  • three consecutive refusals with max_silent_turns=3 end the run after exactly 3 model calls, and the explanation carries the stop reason;
  • silent turns separated by an action do not trip the guard.

The existing "no-tool-call turn is nudged, not terminated" test still passes. make test, make lint and make typecheck pass.

Related

The refusals themselves came from the Flash prompt wording, which I'm fixing in a separate PR. This guard is still worth having on its own, because any endpoint can degrade to empty replies.

An endpoint that keeps answering with nothing (empty content, stop_reason
refusal) made an unbounded Flash run call the model about once a second
for as long as the caller waited. flash.max_silent_turns (default 5) ends
the run as failed with the last stop reason; 0 disables the guard.
Sign up for free to 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.

1 participant