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_retrynanobot/providers/base.py::_run_with_retrynanobot/agent/loop.py::_cancel_active_tasksnanobot/agent/loop.py::_dispatchnanobot/agent/runner.py::runnanobot/api/server.py::handle_chat_completionstests/providers/test_provider_retry.pytests/agent/test_stop_preserves_context.pytests/test_api_stream.py
Predict before running
Complete one row for each case:
| Case | Retry? | 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" `
-vvSet breakpoints where:
chat_stream_with_retry()setshas_streamed_content;_run_with_retry()evaluatesshould_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 -vvObserve:
- how the active task is canceled;
- where
CancelledErroris 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 -vvObserve:
- the
on_streamandon_stream_endcallbacks passed toprocess_direct(); - how a delta becomes an SSE chunk;
- the final response and
[DONE]; - how an exception or timeout closes the stream.
Diagnostic table
| Symptom | First observation point | Do not start with |
|---|---|---|
| Long wait before any output | Provider retry metadata and on_retry_wait | Restarting the entire Gateway |
| Output restarts after stopping midway | stream ID/segment and stall timeout | Assuming the frontend rendered twice |
A later Turn sees tool results after /stop | checkpoint restore | Declaring the Session corrupted |
| SSE is disconnected but backend work continues | whether request cancellation reached the task | Deleting 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
- Why can a stream stall be a special recovery exception after an ordinary failure?
- Why should
CancelledErrorgenerally not be converted to a plain error string? - Does a closed SSE connection necessarily mean the Provider task was canceled?
- What is the fundamental difference between checkpoint persistence and rollback of an external side effect?
Next: Write a Workspace Skill