> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chappiesdk.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Streaming Real-Time Model Responses with Chappie SDK

> Chappie streams model output token-by-token over WebSocket with automatic fallback to HTTP/SSE, exposing each chunk as a typed Swift AsyncSequence event.

Streaming lets your UI update as the model generates text, rather than waiting for the full response. Chappie's streaming layer is transport-transparent: it prefers a WebSocket connection for lower overhead, but automatically falls back to HTTP Server-Sent Events (SSE) if WebSocket fails after retries. Your event-handling code stays identical regardless of which transport is active. The model output, tool calls, usage snapshots, and lifecycle events all arrive as strongly-typed `ChappieStreamEvent` values over an `AsyncThrowingStream`.

## Basic Streaming

Call `client.stream(_:)` with a prompt string and iterate the resulting `AsyncThrowingStream` with `for try await`:

```swift theme={null}
let client = Chappie.client()
for try await event in client.stream("Write a welcome message.") {
    switch event {
    case .textDelta(let text):
        print(text, terminator: "")
    case .usageLimits(let snapshot):
        // Update usage UI
        break
    case .completed:
        break
    default:
        break
    }
}
```

You can also pass a `ChappieInputMessage` directly, or a full `ChappieResponseRequest` via `streamResponse(_:)`, when you need control over message role, fast mode, or per-request options.

## Stream Events

Every value your `for try await` loop receives is a `ChappieStreamEvent`. Handle each case you care about and ignore the rest with `default`:

<AccordionGroup>
  <Accordion title=".textDelta(String)">
    An incremental text chunk from the model. Append each delta to a `String` buffer to build the full response. Deltas arrive in order and are never empty.
  </Accordion>

  <Accordion title=".usageLimits(ChappieUsageLimitSnapshot)">
    A mid-stream usage snapshot. The snapshot may include a `primary` window (`usedPercent`, `windowMinutes`, `resetsAt`) and a `secondary` window. Use it to drive a live usage indicator without polling the rate-limits endpoint.
  </Accordion>

  <Accordion title=".toolCallRequested(ChappieToolExecutionRequest)">
    The model has decided to call a host tool and the SDK is about to execute it. The event carries the `ChappieToolExecutionRequest`, which contains the tool name, arguments, and the policy applied (automatic, ask, or deny). You can use this event to show a "thinking…" indicator.
  </Accordion>

  <Accordion title=".toolCallCompleted(ChappieToolExecutionResult)">
    The host tool finished executing. The result contains the tool call details, execution status (`completed`, `failed`, `denied`, `cancelled`), the output string, and timing. The SDK automatically feeds the output back to the model as a follow-up turn.
  </Accordion>

  <Accordion title=".approvalRequested(ChappieApprovalRequest)">
    A tool whose policy is `.ask` is waiting for explicit user approval. Present a consent prompt; call the `toolApprovalHandler` closure you registered on the client with `.approved()` or `.denied()` to continue.
  </Accordion>

  <Accordion title=".completed">
    The model has finished generating for this turn. The stream will end cleanly after this event. Use it to hide loading spinners or finalize your text buffer.
  </Accordion>

  <Accordion title=".raw(type:payload:)">
    The raw SSE event type and payload string before any parsing. Useful for debugging unexpected server events or building advanced integrations that need access to fields the SDK does not yet surface.
  </Accordion>
</AccordionGroup>

## Cancelling a Stream with `ChappieStreamHandle`

When you need to cancel a stream from outside the iteration loop — for example, when the user taps a stop button — use `streamHandle(_:)` instead of `stream(_:)`. It returns a `ChappieStreamHandle` whose `cancel()` method immediately stops the underlying transport and finishes the event stream.

```swift theme={null}
let handle = client.streamHandle("Draft a reply.")

Task {
    for try await event in handle.events {
        if case .textDelta(let text) = event {
            // append text to your UI buffer
        }
    }
}

// Later, when the user taps Stop:
handle.cancel()
```

You can also cancel via Swift structured concurrency by wrapping the iteration in a `Task` and calling `cancel()` on it:

```swift theme={null}
let streamTask = Task {
    for try await event in client.stream("Write a welcome message.") {
        // handle events
    }
}

// Cancel from anywhere:
streamTask.cancel()
```

Both approaches cooperate with Swift's cooperative cancellation model — the loop exits cleanly without throwing an error on cancellation.

## Transport Behaviour

### WebSocket with Automatic Fallback

By default, Chappie opens a WebSocket connection for every streaming turn. If the WebSocket fails, the SDK retries before falling back to HTTP/SSE for the remainder of the session. Once HTTP/SSE is active, all subsequent turns in that client instance use HTTP/SSE — WebSocket is not re-attempted mid-session.

### Replay Divergence Protection

If a reconnection attempt results in the server sending a different continuation than what was already delivered to your UI, the SDK throws `ChappieClientError.streamingTransportReplayDiverged`.

<Note>
  If your error handler catches `.streamingTransportReplayDiverged`, the associated `String` value contains the text that was already delivered to your UI before the divergence. You can use it to display a partial response or prompt the user to retry.
</Note>

### Forcing HTTP/SSE Only

If your environment blocks WebSocket connections or you want deterministic transport for testing, opt into HTTP/SSE exclusively:

```swift theme={null}
let client = Chappie.client(
    streamingTransportPolicy: .httpSSEOnly
)
```

You can also build a custom policy:

```swift theme={null}
let policy = ChappieStreamingTransportPolicy(
    prefersWebSocket: true,
    maxWebSocketReconnectionAttempts: 3,
    fallsBackToHTTPSSE: true
)
let client = Chappie.client(streamingTransportPolicy: policy)
```

## Streaming With a Full Request

When you need to customise reasoning effort, service tier, or search tool access on a streaming turn, build a `ChappieResponseRequest` and call `streamResponse(_:)`:

```swift theme={null}
let request = ChappieResponseRequest(
    messages: [
        .user(
            "Summarise today's news.",
            options: ChappieMessageOptions(
                responseOptions: ChappieResponseOptions(
                    enableSearchTool: true,
                    reasoningEffort: .medium
                )
            )
        )
    ]
)

for try await event in client.streamResponse(request) {
    if case .textDelta(let chunk) = event {
        // update UI
    }
}
```
