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

# Conversations

> Multi-turn chat with message history. The foundation of any chatbot or assistant.

A conversation is just a list of messages. You keep adding to it, send the whole list every time, and the model has full context of everything that came before.

## The message shape

Every message has a `role` and `content`.

| Role        | What it is                                                                        |
| ----------- | --------------------------------------------------------------------------------- |
| `system`    | Instructions for the model. Usually one message at the start of the conversation. |
| `user`      | What the user typed.                                                              |
| `assistant` | What the model said. You append these from previous responses.                    |
| `tool`      | The result of a tool call (when using [Tool calling](/build/gateway/tools)).      |

## A working multi-turn

<CodeGroup>
  ```python Python theme={null}
  import os
  from openai import OpenAI

  client = OpenAI(
      base_url="https://api.opper.ai/v3/compat",
      api_key=os.environ["OPPER_API_KEY"],
  )

  messages = [
      {"role": "system", "content": "You are a concise assistant."},
      {"role": "user", "content": "What's a vector?"},
  ]

  # Turn 1
  r = client.chat.completions.create(model="openai/gpt-5-mini", messages=messages)
  reply = r.choices[0].message.content
  messages.append({"role": "assistant", "content": reply})
  print("Assistant:", reply)

  # Turn 2 — the model remembers turn 1 because we sent the whole list
  messages.append({"role": "user", "content": "Give me an example in 3D."})
  r = client.chat.completions.create(model="openai/gpt-5-mini", messages=messages)
  print("Assistant:", r.choices[0].message.content)
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
      baseURL: "https://api.opper.ai/v3/compat",
      apiKey: process.env.OPPER_API_KEY!,
  });

  const messages: any[] = [
      { role: "system", content: "You are a concise assistant." },
      { role: "user", content: "What's a vector?" },
  ];

  let r = await client.chat.completions.create({ model: "openai/gpt-5-mini", messages });
  const reply = r.choices[0].message.content;
  messages.push({ role: "assistant", content: reply });
  console.log("Assistant:", reply);

  messages.push({ role: "user", content: "Give me an example in 3D." });
  r = await client.chat.completions.create({ model: "openai/gpt-5-mini", messages });
  console.log("Assistant:", r.choices[0].message.content);
  ```
</CodeGroup>

## System prompt

The first `system` message sets the model's persona and ground rules. It stays in the conversation, and you only write it once.

```python Python theme={null}
{"role": "system", "content": "You are a customer support agent for Acme Corp. Always cite a ticket number when referring to a past issue."}
```

Keep it short. Long system prompts cost more on every turn and tend to be ignored.

## Stop reasons

The response tells you why the model stopped:

| `finish_reason`  | Meaning                                                                |
| ---------------- | ---------------------------------------------------------------------- |
| `stop`           | Model finished its answer naturally.                                   |
| `length`         | Hit `max_tokens`. Response may be truncated.                           |
| `tool_calls`     | Model wants to call a tool (see [Tool calling](/build/gateway/tools)). |
| `content_filter` | A [Guard](/control-plane/guard) rule blocked or modified the output.   |

## Keeping history under control

Messages add up fast. A few options:

* **Trim** older turns when the conversation gets long. Keep the system message and recent N turns.
* **Summarize** older turns into a single assistant message ("earlier you discussed: X, Y, Z").
* **Use a separate [structured output](/build/gateway/structured-output) call** to extract just the bits worth remembering, then drop the rest.

## What's next

<CardGroup cols={2}>
  <Card title="Tool calling" icon="screwdriver-wrench" href="/build/gateway/tools">
    Let the model invoke your tools mid-conversation.
  </Card>

  <Card title="Streaming" icon="bolt-lightning" href="/build/gateway/streaming">
    Stream the response token-by-token.
  </Card>

  <Card title="Vision & PDFs" icon="image" href="/build/multimodal/vision-pdfs">
    Send images and documents as message content.
  </Card>

  <Card title="Drop-in SDKs" icon="plug" href="/build/gateway/drop-in-sdks">
    The auth and base URL setup.
  </Card>
</CardGroup>
