Skip to lesson
supraj.dev THE ENGINEERING HANDBOOKS
LEARN / BUILD / VERIFY2026 edition · checked 06 Oct

CHAPTER 09 / 30 · Design reliable tools

Stream progress without inventing completion

Consume text and lifecycle events while preserving the distinction between partial output and a final result.

4 min read + practiceWorked exerciseInterview practice

The mechanism

Streaming changes when information reaches the client. It does not change what counts as successful execution. A text chunk can arrive before a tool runs, before the final answer is complete, or before an error terminates the request. The interface must represent progress, partial output and terminal state separately.

ParcelOps might display “Looking up incident” when a tool call begins, then show a factual summary after the lookup returns. It must not display “Incident resolved” just because those words appeared in a partial model response. A final business status should come from the verified result contract, not a substring match on streamed text.

SDK event stream
Event adapter
UI progress + partial text
Terminal result verification

A worked streaming consumer

Optional inference. Reuse the explicitly configured agent from chapter 3 with callback_handler=None. This fragment prints text deltas; production code also needs terminal-event and error handling appropriate to its transport.

import asyncio

async def display_response():
    chunks = []
    async for event in agent.stream_async(
        "Explain the synthetic INC-104 record without changing it."
    ):
        if "data" in event:
            text = event["data"]
            chunks.append(text)
            print(text, end="", flush=True)
    print()
    return "".join(chunks)

# Run only after configuring the approved model provider.
# asyncio.run(display_response())

The returned string is accumulated display text. It is not a replacement for the SDK’s final structured result or stop reason. Event formats differ between the Python and TypeScript SDKs; a JavaScript consumer should follow its typed event reference rather than copying Python dictionary keys.

Build an adapter between SDK events and your public UI protocol. That keeps SDK-specific fields out of frontend business logic and gives you one place to redact sensitive arguments. A tool invocation may contain private record identifiers, so “show all raw events” is not an acceptable default debug interface.

Practice: replay a broken stream

Offline. Create a fixture sequence with started, two text fragments, tool_started, tool_failed and failed. These are your application event names, not claimed SDK names. Replay them into a small state table. The UI should retain partial text with a visible incomplete label, stop any spinner and offer a bounded recovery action.

Then replay a client disconnect after the first fragment. Decide whether the server cancels work, continues a tracked job or waits for a reconnect. Expected observation: transport disconnection and business cancellation are separate events. If the backend keeps running, the user needs a request ID and a way to inspect its eventual outcome.

Add backpressure to the thought experiment: the model produces chunks faster than the browser consumes them. Bound the buffer and decide which progress events may be coalesced. Never drop the terminal state or authoritative outcome simply to keep the animation smooth.

Troubleshooting and trade-offs

Duplicate text often comes from both a callback handler and a stream consumer printing the same data. Missing terminal state can leave the UI permanently “working.” Proxy buffering can make a correct backend stream appear as one delayed response; inspect the complete HTTP path before changing SDK logic.

Streaming improves perceived responsiveness but adds lifecycle complexity. A batch endpoint may be simpler for a short structured task. Choose based on the user experience and operational requirements, then measure time to first useful information as well as total completion time.

Interview practice

Why should the UI have a separate completion state?

A partial text stream can fail or be cancelled after displaying plausible content. Completion must be tied to a terminal result and verified business outcome, not to visible prose.

What should happen when the browser disconnects?

An explicit policy should cancel the invocation or continue a tracked job with a recoverable request ID. Socket closure alone does not guarantee tool work stopped or that effects were rolled back.

Completion check

Replay success, failure and disconnect fixtures. Explain which displayed fields are provisional and which are authoritative. Verify that raw tool arguments are not automatically exposed to the browser.

Sources and version notes

Checked 6 October 2026. Python examples target strands-agents==1.58.0 unless labelled otherwise. Live documentation can change; compare your installed version before adapting an example.

YOUR NEXT STEP

Make the understanding yours.

Use the completion check above. Mark this chapter when you can explain the mechanism and its limits.

Self-assessed reading progress. This does not certify that a lab ran or a system is secure.