Skip to content

Report why a streamed generation stopped - #610

Closed
pcbeingused333 wants to merge 2 commits into
activeagents:mainfrom
pcbeingused333:fix/stream-finish-reason
Closed

pcbeingused333 wants to merge 2 commits into
activeagents:mainfrom
pcbeingused333:fix/stream-finish-reason

Conversation

@pcbeingused333

Copy link
Copy Markdown
Contributor

response.finish_reason (and its stop_reason alias) reads the provider's response body: choices[0].finish_reason for OpenAI Chat, stop_reason for Anthropic. A streamed generation has no response body (process_prompt_finished runs without one), so finish_reason was nil for every streamed Chat and Anthropic generation, and so was the finish_reason of the prompt.active_agent instrumentation event and the finish= part of the log line. A caller can't tell a complete answer from one cut off at max_tokens (length) or a tool call turn.

This change records it from the stream: the Chat provider takes the chunk's finish_reason, the Anthropic provider the message_delta's stop_reason. The provider hands it to the response as streamed_finish_reason, and finish_reason falls back to it when there is no raw_response. Non-streamed responses read the body as before. With tool calling the response reports the last turn's reason.

Tests:

  • test/providers/open_ai/chat/streaming_finish_reason_test.rb and test/providers/anthropic/streaming_finish_reason_test.rb: a streamed generation over the real wire format (WebMock SSE) reports stop/length (Chat) and end_turn/max_tokens (Anthropic). Both fail on main with nil.
  • test/providers/** and test/integration/** (openai/anthropic gems, rails8 gemfile, dummy API keys for VCR): 1340 runs, 0 failures. The 6 errors (RubyLLMIntegrationTest, SolidAgentCompatibilityTest) happen on main too and don't involve this code. Rubocop is clean, and there's a CHANGELOG entry.

Not in this change: the OpenAI Responses provider streams too, but reports its stop reason in status/incomplete_details, a different vocabulary; it still returns nil and is left for a follow-up.

This change was made with Claude Code; the tests above were run on the branch.

🤖 Generated with Claude Code

A streamed generation has no response body, so response.finish_reason (and its
stop_reason alias) was always nil for streamed Chat and Anthropic generations,
and so was the finish_reason of the prompt.active_agent instrumentation event.
Record it from the stream (the chunk's finish_reason for Chat, the
message_delta's stop_reason for Anthropic) and let the response fall back to it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Copy link
Copy Markdown
Contributor

Closing as superseded: #613 carries this change (the streamed finish_reason for Chat and Anthropic, with the same tests) plus the Responses side, and is on main through #622 for 1.9.3. Thanks for the fix.


Generated by Claude Code

yorzi pushed a commit to yorzi/activeagent that referenced this pull request Oct 22, 2026
…eneration stopped

Carries activeagents#610 (a streamed generation reports why it stopped). Resolved on
main: the changelog bullets join the Fixed section under Unreleased.
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.

2 participants