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

# Chat

> Multi-turn conversations with message history

Pass an array of messages instead of a string. Same method.

```ts theme={null}
const reply = await RunAnywhere.llm.generate([
  { role: 'system', content: 'You are a concise assistant.' },
  { role: 'user', content: 'What is on-device inference?' },
  { role: 'assistant', content: 'Running the model on your own hardware.' },
  { role: 'user', content: 'Why does that matter?' },
])
```

## How messages are split

The array is not sent verbatim:

* System turns become `options.systemPrompt`.
* The **trailing user turn** becomes the prompt.
* Everything between travels as history.

A conversation with no user turn throws.

## Streaming a conversation

```ts theme={null}
for await (const event of RunAnywhere.llm.generateStream(messages)) {
  if (event.type === 'textDelta') reply += event.text
}
```

## Keeping a transcript

```ts theme={null}
import type { ChatMessage } from '@runanywhere/electron'

const messages: ChatMessage[] = [{ role: 'system', content: 'You are a helpful assistant.' }]

async function send(text: string): Promise<void> {
  messages.push({ role: 'user', content: text })

  let reply = ''
  for await (const event of RunAnywhere.llm.generateStream(messages)) {
    if (event.type === 'textDelta') reply += event.text
  }

  messages.push({ role: 'assistant', content: reply })
}
```

## Persisting across launches

A desktop app is expected to remember. Conversations are plain values, so writing them as JSON
under the app's user-data directory is enough. Persist the array, not SDK state: models reload
from disk on the next launch, and history is all you need to carry.

## Context length

Trim before the conversation outgrows the model's window, keeping the system message:

```ts theme={null}
if (messages.length > 21) {
  messages.splice(1, messages.length - 21)
}
```

A desktop can afford a larger `contextLength` at load time, which pushes this problem further
out but does not remove it.
