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.
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:
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.
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)
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.
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:
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:
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.
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.
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:
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:
// 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)
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.