> ## 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.

# Conversations: Build Multi-Turn Chat with ChappieClient

> ChappieClient maintains a transcript and thread ID across turns automatically, letting you build multi-turn conversations with one persistent instance.

Every `ChappieClient` instance owns a private transcript — an ordered list of messages that grows with each turn. When you call `send(_:)` or `response(_:)`, the client automatically prepends harness context, incorporates any compacted context summary, adds your new message, sends the full transcript to the model, then records the assistant reply. You don't need to track message history yourself. The thread ID that ties the conversation together on the server is also managed automatically and persists for the lifetime of the client instance.

## Sending a Single Message

The simplest way to get a reply is `send(_:)`, which appends one user message to the transcript and returns the model's text response.

```swift theme={null}
let client = Chappie.client()
let reply = try await client.send("Write a welcome message.")
```

Pass a `ChappieInputMessage` directly when you need to use something other than a plain user message, or when you want to set per-message options:

```swift theme={null}
let reply = try await client.send(.user("Quick summary?", fastMode: true))
```

`fastMode: true` sets reasoning effort to `.low`, which reduces latency at the cost of reasoning depth. It is shorthand for setting `ChappieMessageOptions(fastMode: true)`.

## Building Multi-Turn Requests

Use `ChappieResponseRequest` when you need precise control over the transcript, such as when you're replaying a saved conversation or constructing a custom context window.

```swift theme={null}
let request = ChappieResponseRequest(messages: [
    .system("Answer like a concise product assistant."),
    .user("What plan am I on?"),
    .assistant("You are on Plus."),
    .user("What model should I use?")
])
let response = try await client.response(request)
print(response.outputText)
```

<Note>
  When you call `client.response(_:)`, the client records the resulting assistant turn into its internal transcript. Subsequent calls to `send(_:)` will see all previously recorded turns. For most use cases, `response(_:)` is what you want.
</Note>

## Constructing Input Messages

`ChappieInputMessage` provides factory methods for every message role the API accepts:

| Method                                     | Role        | Use it when…                                                                          |
| ------------------------------------------ | ----------- | ------------------------------------------------------------------------------------- |
| `.user(_:)`                                | `user`      | The person is speaking                                                                |
| `.user(_:fastMode:)`                       | `user`      | You want a faster, lower-reasoning reply                                              |
| `.assistant(_:)`                           | `assistant` | You're injecting a prior assistant turn                                               |
| `.system(_:)`                              | `system`    | You need an extra system instruction (folded into harness instructions automatically) |
| `.functionCall(id:callID:name:arguments:)` | `assistant` | You're replaying a tool call from the model                                           |
| `.toolOutput(callID:output:)`              | `user`      | You're providing the result of a tool call                                            |

## Per-Message Response Options

Every user message can carry `ChappieMessageOptions`, which override the client-level defaults for that single turn. The most common use is `ChappieResponseOptions`:

```swift theme={null}
let request = ChappieResponseRequest(
    messages: [
        .user(
            "Compare these options.",
            options: ChappieMessageOptions(
                responseOptions: ChappieResponseOptions(
                    reasoningEffort: .high,
                    serviceTier: .priority,
                    enableSearchTool: true
                )
            )
        )
    ]
)
```

### Reasoning Effort

`ChappieReasoningEffort` controls how deeply the model reasons before responding:

| Value      | When to use                                   |
| ---------- | --------------------------------------------- |
| `.minimal` | Status checks, simple yes/no answers          |
| `.low`     | Fast replies, UI copy, quick summaries        |
| `.medium`  | Default balanced reasoning                    |
| `.high`    | Complex analysis, comparisons, planning tasks |

### Service Tier

`ChappieServiceTier` selects the infrastructure tier for a request:

| Value          | Description                           |
| -------------- | ------------------------------------- |
| `.auto`        | Let the server pick based on load     |
| `.defaultTier` | Standard request handling             |
| `.flex`        | Best-effort, lower-cost tier          |
| `.priority`    | Lowest-latency, highest-priority tier |

### Enabling Web Search

Set `enableSearchTool: true` on a `ChappieResponseOptions` to let the model fetch live web results before responding. The default search tool type is `web_search_preview`.

## Attaching Images

Include image attachments on any user message by passing an array of `ChappieInputAttachment` values:

```swift theme={null}
let message = ChappieInputMessage.user(
    "What's in this image?",
    attachments: [.imageURL("https://example.com/photo.jpg")]
)
let reply = try await client.send(message)
```

You can pass an optional `detail` string to the `.imageURL(_:detail:)` factory if the API endpoint supports detail-level hints.

<Warning>
  Image attachments are only valid on user-role messages. Attaching images to an assistant or system message will throw an encoding error at send time.
</Warning>

## Concurrency and Turn Serialisation

`ChappieClient` serialises turns internally. If you call `send(_:)` or `response(_:)` concurrently on the same instance, the second call waits until the first has finished and its transcript has been recorded. This keeps the transcript consistent without any locking on your side.

However, concurrent turns on the same client **do share the same transcript**. If you need two independent, simultaneous conversations — for example, a background summarisation task running in parallel with a foreground chat — create two separate client instances:

```swift theme={null}
let chatClient = Chappie.client(harness: .default)
let summaryClient = Chappie.client(harness: "Summarise text concisely.")
```

## Managing the Transcript

You can inspect, clear, compact, or resume transcript state at any time using the client's context helpers:

```swift theme={null}
// Read the current context snapshot (messages, thread ID, model)
let snapshot = await client.context()

// Wipe the transcript and start fresh
await client.clearContext()

// Compact a long transcript with an auto-generated summary
try await client.compactContext()

// Resume a saved conversation by thread ID
await client.resumeContext(threadID: "thread-abc123", messages: savedMessages)
```

<Tip>
  Call `compactContext()` when the transcript grows large. The SDK sends the current transcript to the model with a summarisation prompt, replaces all messages with a single summary entry, and preserves the thread ID — so the server-side conversation thread stays intact.
</Tip>
