Skip to content

fix: emit complete signal to callback handler when text block stops#1890

Open
giulio-leone wants to merge 1 commit intostrands-agents:mainfrom
giulio-leone:fix/callback-complete-signal
Open

fix: emit complete signal to callback handler when text block stops#1890
giulio-leone wants to merge 1 commit intostrands-agents:mainfrom
giulio-leone:fix/callback-complete-signal

Conversation

@giulio-leone
Copy link
Contributor

Issue

Closes #826

Problem

PrintingCallbackHandler checks kwargs.get('complete', False) to determine whether to print a trailing newline after text output, but no event in the streaming pipeline ever sets complete=True. This causes text output to never have proper line termination.

Root Cause

The streaming pipeline emits TextStreamEvent({'data': text, 'delta': delta}) for each text chunk but never signals completion. When contentBlockStop arrives, handle_content_block_stop() clears the accumulated text state—but yields no event to notify the callback handler that text streaming is done.

Meanwhile, EventLoopStopEvent and ModelStopReason both have is_callback_event = False, so they never reach the callback handler either.

Solution

Before calling handle_content_block_stop(), check if text was being accumulated (state['text'] is truthy). If so, yield a ModelStreamEvent({'complete': True}) after the stop handler runs.

This event:

  • Has is_callback_event = True (non-empty dict → len(self.keys()) > 0)
  • Does NOT trigger prepare() invocation_state merge (no 'delta' key in the dict)
  • Is only emitted for text content blocks—not for toolUse or reasoningContent blocks

The fix is minimal (3 lines) and does not alter any existing event payloads or control flow.

Testing

  • 4 new tests covering the complete signal behavior:
    • test_complete_event_emitted_for_text_block — verifies exactly 1 complete event after text stop
    • test_no_complete_event_for_tool_use_block — verifies no complete event for tool_use
    • test_no_complete_event_for_reasoning_block — verifies no complete event for reasoning
    • test_complete_event_with_multiple_text_blocks — verifies 1 complete event per text block
  • All 1870 existing tests pass with updated expected event lists
  • Updated tests: test_process_stream, test_stream_messages, test_agent__call__callback, test_stream_e2e_*

Changes

File Change
src/strands/event_loop/streaming.py +3 lines: check had_text, yield ModelStreamEvent({'complete': True})
tests/strands/event_loop/test_streaming.py +124 lines: 4 new tests + updated expected events
tests/strands/agent/test_agent.py +1 line: updated callback expectations
tests/strands/agent/hooks/test_agent_events.py +6 lines: updated E2E expected events

By issuing a ticket, you agree to abide by the Contributing Guidelines.

… stops

When PrintingCallbackHandler checks kwargs.get('complete', False) to
determine whether to print a trailing newline, no event in the streaming
pipeline ever sets complete=True. This causes text output to never have
proper line termination.

Root cause: TextStreamEvent emits {data, delta} but never signals
completion. contentBlockStop triggers handle_content_block_stop() which
clears the text state, but no event is yielded to notify the callback.

Fix: Before calling handle_content_block_stop(), check if text was being
accumulated (state['text'] is truthy). If so, yield a
ModelStreamEvent({'complete': True}) after the stop handler runs. This
event reaches the callback handler via is_callback_event (non-empty dict)
and enables PrintingCallbackHandler to print the trailing newline.

The complete signal is only emitted for text content blocks, not for
toolUse or reasoningContent blocks, matching the callback handler's
expected behavior.

Closes strands-agents#826
@giulio-leone
Copy link
Contributor Author

Friendly ping — emits the complete signal to the callback handler when a text content block stops, fixing missing completion callbacks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Callback handler doesn't correctly supply the "complete" arg

1 participant