Skip to content

v0.5.0 — streaming RES_CHUNK responses

Choose a tag to compare

@bene-art bene-art released this 03 May 19:53
f739a4d

Summary

Closes #11. PACT now supports streaming responses: handlers `yield` instead of `return`, dispatcher signs each yielded payload as a `RES_CHUNK`, HTTP layer streams them as NDJSON over chunked transfer encoding.

This was the last open issue. Zero open issues, zero documented xfails.

Quick example

```python

Server side

@agent.handle("ask_llm_stream")
def ask_llm_stream(payload):
for token in ollama_stream(payload["prompt"]):
yield {"chunk": token}

Client side

req = build_req(..., stream=True)
for chunk in send_message_streaming(url, req):
print(chunk["payload"]["chunk"], end="")
```

Wire format additions

`PACTMessage` gains three new optional fields:

Field Where Purpose
`stream: bool` REQ Opt-in to streaming response
`chunk_seq: int` RES_CHUNK Monotonic sequence (0, 1, 2, ...)
`chunk_final: bool` RES_CHUNK Marks the terminal chunk

Each chunk is a fully-formed signed PACTMessage with `type="RES_CHUNK"`. Tampering with any field — including chunk_seq or chunk_final — invalidates that chunk's signature without affecting others.

Idempotency under streaming

Cached value is now polymorphic:

  • Dict → one-shot replay (existing)
  • List of chunk dicts → streaming replay (new)

A retried streaming REQ returns the cached chunks WITHOUT re-running the handler. Test: `test_streaming_replay_returns_same_chunks`.

Receipts

One receipt per stream, written after the final chunk. `refs` include the original REQ id plus every chunk id. If the consumer disconnects mid-stream, the receipt records `outcome="failed"` with whatever chunks did make it.

Test results

v0.4.0 v0.5.0
Tests passing 135 140
Documented xfails 0 0
Open issues 1 0
CI 3.11 / 3.12 / 3.13 green 3.11 / 3.12 / 3.13 green

5 new tests in `tests/integration/test_streaming.py`:

  • Round-trip stream
  • Per-chunk signature verification
  • Tampered chunk detection
  • Idempotent replay returns same chunks
  • One-shot handler under stream=true still works

No breaking changes

Streaming is opt-in on both sides:

  • Client requests it via `stream=True` on `build_req`
  • Server delivers it when the handler yields (generator return)
  • Either side opting out → falls back to one-shot RES (existing behavior)

Stats arc — full session

The complete v0.1.3 → v0.5.0 progression in two days:

v0.1.3 (case study era) v0.5.0
Tests passing 118 + 1 xfail 140
Critical auth bypasses 3 0
Durability gaps 1 0
Wire-level delegation broken works
Streaming not supported supported
Documented xfails 1 0
Open issues 7 0

Install

```bash
pip install --upgrade pact-passport
```

What's next

The actionable v0.x backlog is empty. Possible next moves:

  • Patrick integration — wire the local agent stack onto PACT (was out of scope; now safe and unblocked)
  • Mini-cluster integration — replace the Mac→NUC SSH path with PACT for the actual Monte Carlo flow
  • Spec doc audit — `spec/PACT_v1.md` was written for v0.1; needs updating to reflect v0.2-v0.5 additions (identity_doc, cap_envelope, RES_CHUNK)