# Durable streaming

> Resume an interrupted stream from the last received event.

Source: https://maincode.com/docs/python-client-sdk-durable-streaming
Section: Client SDK · Matilda documentation

---

Durable streaming lets a client disconnect mid-stream and resume from where it left off. The server buffers events in a Redis stream, keyed by a `stream_id` advertised at stream start.

## Durable streaming lifecycle

1. Start a stream — `chat.stream()` emits a `ResponseCreated` event with a `stream_id`.
2. Persist the `stream_id` and `conversation_id` immediately.
3. If disconnected, call `chat.active_stream(conversation_id)` to check if the stream is still live.
4. Call `chat.resume(stream_id, last_event_id=...)` to replay buffered events from `last_event_id` onwards.

## `chat.resume(stream_id, *, last_event_id=None, ...)`

Resumes a previously detached stream by replaying buffered events from `last_event_id`. Returns an async generator of `ChatEvent`. Raises `MatildaAPIError` (404) when the stream buffer has expired.

```python
async for event in client.chat.resume(saved_stream_id, last_event_id=saved_last_event_id):
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)
```

| Field | Type | Description |
| - | - | - |
| `stream_id` | `str` | The stream ID from response.created. |
| `last_event_id` | `str \| None` | The last Redis stream entry ID received. Omit to replay from the start. |
| `access_token` | `str \| None` | Override access token. |
| `fingerprint` | `str \| None` | Device fingerprint. |
| `stall_timeout_ms` | `int \| None` | SSE stall watchdog timeout. Defaults to 45\_000. |

## `chat.active_stream(conversation_id)`

Checks whether a conversation has an active stream.

```python
result = await client.chat.active_stream("conv-123")
# {"stream_id": "abc-123" | None, "status": "active" | "done" | "error" | None}
```

Returns a `dict`:

```python
# ActiveStreamLookup:
{"stream_id": "abc-123" or None, "status": "active" | "done" | "error" | None}
```

## `chat.notify_on_completion(stream_id, enabled=True)`

Request a push notification when a backgrounded stream completes.

```python
await client.chat.notify_on_completion(stream_id, enabled=True)
```

| Field | Type | Description |
| - | - | - |
| `stream_id` | `str` | The stream to watch. |
| `enabled` | `bool` | Enable or disable the notification. Defaults to True. |

Returns a `dict` (e.g. `{"status": ...}`).

## Full resume example

```python title="resume.py"
import asyncio
import os

from matilda_client import MatildaClient


async def main():
    async with MatildaClient(token=os.environ["MATILDA_ACCESS_TOKEN"]) as client:
        stream_id = None
        last_event_id = None

        # Start streaming
        async for event in client.chat.stream(
            input="Write a long essay about Australia.",
            conversation_id="conv-123",
        ):
            if event.type == "response.created":
                stream_id = event.stream_id
            if event.type == "response.cursor":
                last_event_id = event.last_event_id
            if event.type == "response.output_text.delta":
                print(event.delta, end="", flush=True)

        # Later — check if the stream is still active, then resume
        active = await client.chat.active_stream("conv-123")
        if active["status"] == "active" and stream_id:
            print("\n--- Resuming ---")
            async for event in client.chat.resume(stream_id, last_event_id=last_event_id):
                if event.type == "response.output_text.delta":
                    print(event.delta, end="", flush=True)


asyncio.run(main())
```
