What this adapter does
Translates Ollama's NDJSON streaming to the universal StreamChunk. Handles message content, tool calls, the final done frame with timing. Implements health check (any HTTP 200 from /api/version) and model listing (/api/tags).
Translation map
| Ollama field | StreamChunk field |
|---|---|
message.content | content |
message.tool_calls | tool_calls (passed through; already parsed dicts) |
done: true | done = True + tokens_per_sec |
message.thinking | thinking — reasoning models fill this and leave content empty on those frames, so carry both |
{"error": …} line | raises OllamaStreamError — a failure after the 200 headers arrives as a stream line, not a status code |
Where to handle errors
Inside the adapter's stream(). Connection refused → OllamaUnavailable (caught by orchestrator and triggers fallback). Read timeout → ModelLoadingTimeout. Malformed JSON line → OllamaProtocolError. A {"error": …} line mid-stream → OllamaStreamError. A stream that ends without a done frame → OllamaProtocolError as well. Each one carries enough context to log meaningfully.
The last two matter more than they look. Ollama sends everything after the response headers as NDJSON lines, including its own failures, so a dead runner is an ordinary line under an HTTP 200 — and an adapter that finishes quietly on one is indistinguishable, to the orchestrator above it, from an adapter that finished the answer. Raising is what lets fallback happen at all. Note what raising cannot undo: chunks already yielded have left the building. That is the problem the fallback lesson picks up.