Reproduce streaming, cancellation, and retry

Exercise objective

Use deterministic tests to reproduce stream segmentation, context preservation after cancellation, and Provider retry. Learn to distinguish three superficially similar forms of an “interrupted response.”

Prerequisites

  • Understand asyncio cancellation.
  • Read the chapters on Provider retry and Runtime Events.
  • This exercise does not require a DeepSeek key.

Source to read

  • nanobot/providers/base.py::chat_stream_with_retry
  • nanobot/providers/base.py::_run_with_retry
  • nanobot/agent/loop.py::_cancel_active_tasks
  • nanobot/agent/loop.py::_dispatch
  • nanobot/agent/runner.py::run
  • nanobot/api/server.py::handle_chat_completions
  • tests/providers/test_provider_retry.py
  • tests/agent/test_stop_preserves_context.py
  • tests/test_api_stream.py

Predict before running

Complete one row for each case:

CaseRetry?Preserve partial text?Save a checkpoint?
Provider returns 429 before the first delta
An ordinary error occurs after stream output
The user invokes /stop

Exercise A: streaming retry boundary

Run:

Set-Location "example\nanobot\code-EN"
python -m pytest tests\providers\test_provider_retry.py `
  -k "does_not_retry_after_emitting_content or retries_timeout_in_new_stream_segment" `
  -vv

Set breakpoints where:

  • chat_stream_with_retry() sets has_streamed_content;
  • _run_with_retry() evaluates should_retry_guard;
  • a new stream segment begins.

Expected observations

  • After content becomes externally visible, an ordinary failure cannot be retried invisibly by default.
  • A stream-stall timeout is an explicitly handled recovery exception.
  • When the entry point supports segments, it ends the old segment before starting a new one.
  • Retry policy depends both on the error type and on externally visible output that has already occurred.

Exercise B: context after cancellation

Run:

python -m pytest tests\agent\test_stop_preserves_context.py -vv

Observe:

  • how the active task is canceled;
  • where CancelledError is caught;
  • how the runtime checkpoint is restored;
  • how the pending user Turn is cleaned up;
  • which intermediate messages are persisted to the Session.

Expected observations

Cancellation does not erase the entire Turn. Completed tool and assistant intermediate state is preserved where possible so that the next Turn can understand what already happened.

Exercise C: API SSE

Run:

python -m pytest tests\test_api_stream.py -vv

Observe:

  • the on_stream and on_stream_end callbacks passed to process_direct();
  • how a delta becomes an SSE chunk;
  • the final response and [DONE];
  • how an exception or timeout closes the stream.

Diagnostic table

SymptomFirst observation pointDo not start with
Long wait before any outputProvider retry metadata and on_retry_waitRestarting the entire Gateway
Output restarts after stopping midwaystream ID/segment and stall timeoutAssuming the frontend rendered twice
A later Turn sees tool results after /stopcheckpoint restoreDeclaring the Session corrupted
SSE is disconnected but backend work continueswhether request cancellation reached the taskDeleting the Session

Completion criteria

  • All three test groups run, or any environmental blocker is recorded precisely.
  • Draw separate call chains for retry, cancellation, and SSE.
  • Correct the prediction table.
  • Identify at least one reason an externally visible operation cannot be replayed safely.

Verification

Keep code-EN read-only. Use IDE single-stepping or pytest --pdb to inspect the existing fake-response order and assertions. If you need to change the response order, copy the smallest fake case to $nanobotLab or this learning library’s example area. Do not edit the source repository’s tests, and do not change production retry policy merely to make the experiment pass.

Review questions

  1. Why can a stream stall be a special recovery exception after an ordinary failure?
  2. Why should CancelledError generally not be converted to a plain error string?
  3. Does a closed SSE connection necessarily mean the Provider task was canceled?
  4. What is the fundamental difference between checkpoint persistence and rollback of an external side effect?

Next: Write a Workspace Skill