# Chat

> Envoyer un message à un modèle ou à un assistant et obtenir la réponse, en un seul bloc ou en streaming.

Un seul endpoint couvre le chat : vous envoyez un message utilisateur, AYETO exécute le modèle ou l'assistant
(y compris les outils d'IA qu'il utilise, comme la recherche web ou la génération d'images), enregistre les deux
messages dans une conversation et renvoie la réponse. La réponse est renvoyée sous la forme d'un seul
objet message, ou sous la forme d'un [stream](streaming.md) si vous le demandez.

## Envoyer un message

| | |
|---|---|
| Endpoint | `POST /api/v3/chat` |
| Portée | `ayeto.chat` |
| Limite de débit | [par défaut](conventions.md#limites-de-debit) |
| Réponse | [Message](#reponse), ou un [stream](streaming.md) lorsque `stream` vaut `true` |

### Conversations

Chaque message appartient à une conversation, identifiée par `conversation_id`. **C'est vous qui créez
l'id** : générez un UUID (version 4) de votre côté et envoyez-le avec le premier message.
Si aucune conversation avec cet id n'existe encore, AYETO la crée ; si elle existe et appartient
à l'utilisateur de la clé, le message y est ajouté et le modèle voit les messages précédents.

La réponse ne contient pas l'id de la conversation. Si vous omettez `conversation_id`, une
nouvelle conversation est créée pour ce seul message et vous n'avez aucun moyen de la poursuivre :
envoyez donc toujours un id que vous conservez.

Une conversation reste liée à ce avec quoi elle a été créée :

- **Assistant.** Une conversation créée avec `assistant_id` reçoit les réponses de cet
  assistant (ses instructions, ses outils, ses connaissances et son modèle) pendant toute sa durée de vie. Envoyer
  plus tard un autre `assistant_id` ou un `model` ne la fait pas passer à un autre assistant.
  Une conversation créée avec un `model` reste une simple conversation avec un modèle.
- **Organisation.** Une conversation créée dans une organisation y reste. Un message de suivi
  qui ne précise aucune organisation s'exécute dans l'organisation de la conversation ; préciser
  une autre organisation est refusé (voir [Organisations et crédits](#organisations-et-credits)).

Pour lister, lire ou supprimer des conversations, utilisez l'API [Conversations](conversations.md).

### En-têtes de requête

| En-tête | Obligatoire | Description |
|---|---|---|
| `uni-api-key` | oui | Votre [clé API](authentication.md). |
| `Content-Type` | oui | `application/json` |
| `language` | non | Le code de langue de l'utilisateur, par exemple `EN`, `CZ` ou `FR`. La langue est communiquée au modèle avec chaque message, afin qu'il puisse répondre dans cette langue. Par défaut : `EN`. |

### Requête

| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| `message` | chaîne | oui | Le message de l'utilisateur, en Markdown ou en texte brut. Un message peut activer une compétence de l'assistant avec `/skill-slug` (voir ci-dessous). |
| `conversation_id` | UUID | non | Conversation à laquelle ajouter le message ; elle est créée si elle n'existe pas. Générez-le vous-même, voir [Conversations](#conversations). Par défaut : un nouvel id aléatoire. |
| `assistant_id` | UUID | l'un de `assistant_id`, `model` | Assistant avec lequel discuter. Il doit s'agir de votre propre assistant ou d'un assistant partagé avec l'utilisateur de la clé. Lorsqu'il est défini, `model` est ignoré et c'est le modèle de l'assistant qui répond. |
| `model` | chaîne | l'un de `assistant_id`, `model` | Id du modèle avec lequel discuter directement, sans assistant, par exemple `gpt-5-mini`. Les ids sont listés par [Modèles et outils](models-and-tools.md). Obligatoire lorsque `assistant_id` n'est pas défini. |
| `organization_id` | UUID | non | Exécute le message dans cette organisation : les crédits de l'organisation le paient et ses fichiers sont décomptés du stockage de l'organisation. L'utilisateur de la clé doit être administrateur ou membre (pas invité) de l'organisation. Par défaut : l'organisation de l'assistant lorsque l'assistant appartient à une organisation, sinon l'organisation de la conversation lorsque le message poursuit une conversation créée dans une organisation, sinon aucune (compte personnel). |
| `stream` | booléen | non | `true` renvoie la réponse sous forme de [stream](streaming.md) pendant sa génération. Par défaut : `false`. |
| `runner_version` | chaîne | non | Format du stream : `"1"` transmet du texte brut, `"2"` transmet des événements structurés (texte, raisonnement, activité des outils, statut). Utilisé uniquement lorsque `stream` vaut `true`. Par défaut : `"1"`. Voir [Streaming](streaming.md#formats-du-stream-de-chat). |
| `attachments` | tableau de [Pièce jointe](#pieces-jointes) | non | Fichiers envoyés avec le message (documents, images). |
| `max_tokens` | entier | non | Nombre maximal de tokens que le modèle peut générer par appel au modèle. Doit être compris entre 1 et le maximum propre au modèle. Par défaut : la valeur par défaut du modèle. |
| `dynamic_tools` | booléen | non | Conversations avec un modèle uniquement : donne au modèle l'ensemble standard d'outils d'IA (recherche web, génération d'images, rédaction de documents, etc.). Ignoré pour les assistants, dont les outils sont configurés sur l'assistant. Par défaut : `false`. |
| `use_vision` | booléen | non | Permet au modèle de regarder les images jointes (modèles dotés de la vision). Avec `false`, le modèle est informé des images jointes mais ne peut pas les regarder. Par défaut : `true`. |
| `relevant_history` | booléen | non | N'envoie au modèle que la partie de l'historique de la conversation pertinente pour le nouveau message, sélectionnée par un appel supplémentaire à un modèle. S'applique uniquement aux modèles qui le prennent en charge et aux conversations d'au moins 3 messages ; ignoré pour les modèles de raisonnement qui utilisent des outils. Par défaut : `false`. |
| `remove_tool_calls` | booléen | non | Retire les [blocs d'appels d'outils](#appels-doutils-dans-la-reponse) du `content` de la réponse, pour ne garder que le texte propre au modèle. Ne s'applique pas aux streams. Par défaut : `false`. |
| `follow_options` | booléen | non | Demande au modèle de terminer sa réponse, lorsque c'est pertinent, par un bloc de code délimité étiqueté `ayeto-follow-options` : un objet JSON avec une `question` facultative, un `multi_select` facultatif et une liste d'`options` (`label`, `prompt`, `description` facultative) parmi lesquelles l'utilisateur peut choisir son message suivant. Utile uniquement si votre client l'affiche. Par défaut : `false`. |

**Compétences de l'assistant.** Un mot de la forme `/slug` (lettres minuscules, chiffres et
traits d'union, séparé par des espaces) dans `message` active la compétence de l'assistant ayant ce
slug, lorsque l'utilisateur de la clé en est propriétaire ou qu'elle est partagée avec lui et qu'elle appartient au même
contexte d'organisation que la requête. L'instruction et les outils de la compétence s'appliquent à ce
message. Les slugs inconnus sont laissés tels quels en texte brut.

### Pièces jointes

Chaque pièce jointe est un fichier encodé dans le corps de la requête.

| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| `filename` | chaîne | oui | Nom du fichier avec son extension, par exemple `report.pdf`. |
| `data` | chaîne | oui | Contenu encodé en base64 sous forme de data URI, par exemple `data:application/pdf;base64,JVBERi0xLjcK...`. Le base64 brut sans le préfixe `data:...;base64,` est également accepté. Les espaces blancs, comme les sauts de ligne, dans le texte base64 sont ignorés ; tout autre caractère hors de l'alphabet base64, ou un padding incorrect, fait refuser la requête avec `422`. |
| `mime_type` | chaîne | oui | Type MIME, par exemple `application/pdf` ou `image/png`. Pour les images, le type est détecté à partir du contenu (JPEG, PNG, WebP) et corrigé s'il diffère. |

La taille enregistrée est calculée à partir du contenu décodé. Un champ `size` envoyé par d'anciens clients
est ignoré.

Les pièces jointes sont enregistrées comme fichiers de l'utilisateur de la clé (ou de l'organisation, voir
`organization_id`) et conservées avec la conversation ; supprimer la conversation les supprime.

- **Limite de taille.** Chaque fichier est limité par la limite d'upload du déploiement, 50 Mo par défaut.
  Un fichier plus volumineux est refusé avec `422 file is too large`.
- **Quota de stockage.** Les pièces jointes sont décomptées du quota de stockage de l'utilisateur ou de
  l'organisation. Lorsqu'il est plein, la requête échoue avec `422 storage quota exceeded` ou
  `422 organization storage quota exceeded`.
- **Vérifiées en premier, tout ou rien.** Les pièces jointes sont décodées et enregistrées avant tout
  autre élément de la requête. Lorsque l'une d'elles est refusée (base64 invalide, trop volumineuse, au-delà du
  quota de stockage), la requête échoue avec `422`, les pièces jointes de la même requête qui
  avaient déjà été enregistrées sont de nouveau supprimées et la conversation reste en l'état : le message
  utilisateur n'est pas ajouté et aucune nouvelle conversation n'est créée. Corrigez la pièce jointe et renvoyez
  le message avec le même `conversation_id`.
- **Comment le modèle les lit.** Le modèle est informé des fichiers joints et les lit
  avec des outils intégrés : les documents sous forme de texte extrait, les images par la vision (lorsque le
  modèle la prend en charge et que `use_vision` vaut `true` ; les grandes images sont réduites pour le
  modèle). Les pièces jointes ne sont donc utiles qu'avec des modèles qui prennent en charge les outils d'IA. L'extraction
  de texte couvre les formats bureautiques, PDF et texte courants, voir
  [Fichiers et médias](files-and-media.md).

### Réponse

Sans `stream`, la réponse est la réponse de l'assistant au message.

| Champ | Type | Description |
|---|---|---|
| `id` | UUID | Id du message de réponse dans la conversation. |
| `timestamp` | horodatage | Date de création du message de réponse (millisecondes depuis l'epoch). |
| `role` | chaîne | `assistant`. |
| `content` | chaîne | La réponse en Markdown. Contient les [blocs d'appels d'outils](#appels-doutils-dans-la-reponse), sauf si `remove_tool_calls` vaut `true`. |
| `reasoning_content` | chaîne ou null | Le raisonnement du modèle ou le résumé de sa réflexion, pour les modèles qui l'exposent ; vide ou `null` sinon. |
| `model` | chaîne ou null | Id du modèle qui a produit la réponse. Pour un assistant qui utilise un modèle automatique, il s'agit du modèle effectivement choisi pour le message. |
| `attachments` | tableau de [Pièce jointe de la réponse](#pieces-jointes-de-la-reponse) | Fichiers produits pour l'utilisateur pendant la réponse, comme les images générées. |
| `tool_runs` | objet | Réponses des sous-assistants appelés comme outils, voir [Exécutions des sous-assistants](#executions-des-sous-assistants). Objet vide lorsqu'il n'y en a pas. |

### Pièces jointes de la réponse

| Champ | Type | Description |
|---|---|---|
| `filename` | chaîne | Nom du fichier. |
| `content_type` | chaîne | Type MIME, par exemple `image/png`. |
| `file_url` | chaîne | Lien qui télécharge le fichier. Il fonctionne sans clé API : traitez-le donc comme un secret. |
| `name` | chaîne ou null | Nom d'affichage facultatif. |
| `description` | chaîne ou null | Description facultative. |

### Exécutions des sous-assistants

Un assistant peut appeler d'autres assistants comme outils. Ce qu'un tel sous-assistant a répondu est
renvoyé dans `tool_runs`, avec pour clé la position de l'appel d'outil parmi les blocs d'appels
d'outils de `content`, sous forme de chaîne : `"0"` est le premier appel d'outil, `"1"` le deuxième.
Seuls les appels d'outils auxquels un sous-assistant a répondu ont une entrée.

| Champ | Type | Description |
|---|---|---|
| `content` | chaîne | La réponse du sous-assistant, avec ses propres blocs d'appels d'outils. |
| `tool_runs` | objet | Exécutions au sein des propres appels d'outils du sous-assistant, avec la même structure. |

### Appels d'outils dans la réponse

Lorsque le modèle utilise des outils d'IA pendant sa réponse, chaque appel d'outil est écrit dans `content`
sous la forme d'un bloc entre deux marqueurs fixes, suivi de la suite du texte du modèle :

```text
Let me check the current exchange rate.

### calling AI tool ...
### Scraping URL: https://example.com/rates
Looking up today's EUR/CZK rate
Successfully scraped 1 page(s)

***
The current rate is 24.35 CZK per euro.
```

Le marqueur d'ouverture est exactement la chaîne `"\n\n### calling AI tool ...\n"` et le marqueur
de fermeture est `"\n***\n"`. Entre les deux se trouve le texte de progression signalé par l'outil (Markdown).
Définissez `remove_tool_calls` à `true` pour obtenir `content` sans ces blocs, ou utilisez le
[stream d'événements](streaming.md#stream-devenements-version-2) structuré, qui signale l'activité
des outils sous forme d'événements distincts.

### Images et fichiers générés

Les outils qui créent des fichiers pour l'utilisateur (génération d'images, rédaction de documents) signalent le
fichier de deux manières :

- le bloc d'appel d'outil dans `content` contient un lien Markdown, pour une image
  `![image.png](https://ayeto.ai/api/v1/public/file/read?id=...&secret=...)` ;
- les images générées sont listées dans `attachments` de la réponse.

La génération d'images est disponible lorsque l'assistant dispose d'un outil de génération d'images, ou dans une
conversation avec un modèle lorsque `dynamic_tools` vaut `true`. Demandez une image dans `message` ;
il n'existe pas d'option distincte pour les images.

### Organisations et crédits

Chaque appel au modèle est facturé en crédits, à l'utilisateur de la clé ou, lorsque la requête s'exécute dans
une organisation, sur le crédit de l'utilisateur dans cette organisation. Une requête s'exécute dans une
organisation lorsque `organization_id` est défini, lorsque l'assistant appartient à une
organisation, ou lorsqu'elle poursuit une conversation créée dans une organisation.

Une conversation créée dans une organisation y reste. Un message de suivi envoyé sans
`organization_id` (et sans assistant d'une autre organisation) s'exécute dans
l'organisation de la conversation : il y est facturé, ses pièces jointes sont décomptées du stockage de
l'organisation et l'utilisateur de la clé doit toujours en être administrateur ou membre. Un
message de suivi qui précise une autre organisation, directement ou via `assistant_id`, est
refusé avec `422 the conversation belongs to another organization` avant que quoi que ce soit ne soit
enregistré ou facturé. Une conversation créée sans organisation n'est pas soumise à cette règle.

Avant chaque appel au modèle, AYETO estime son coût et refuse l'appel lorsque le solde
ne le couvre pas. Pendant la génération d'une réponse, celle-ci est interrompue lorsque son coût
dépasserait le solde. Dans les deux cas, l'erreur est `422` avec le détail
`not enough user credit` ou `not enough organization credit`. Les appels au modèle déjà
exécutés sont facturés, et une réponse interrompue en cours de route est conservée dans la conversation.

Voir [Compte](account.md) pour lire le solde de crédits.

### Erreurs

Les erreurs sont renvoyées sous la forme `{"detail": "..."}` avec le statut HTTP ci-dessous. Lorsque `stream` vaut
`true`, les erreurs marquées *pendant* surviennent après le début du stream : le statut HTTP est
`200` et l'erreur arrive sous la forme d'un [chunk d'erreur](streaming.md#erreurs-dans-un-stream) avec le
même statut et le même détail. Les erreurs marquées *avant* sont renvoyées comme une réponse d'erreur normale dans
les deux modes.

| Statut | `detail` | Moment | Cause |
|---|---|---|---|
| `401` | `API key is invalid` | avant | Voir [Authentification](authentication.md#erreurs-dauthentification). |
| `403` | `permission denied` | avant | L'utilisateur de la clé n'est pas autorisé à utiliser le chat, ou ne peut pas utiliser l'assistant. |
| `403` | `not shared with user` | avant | L'assistant n'appartient pas à l'utilisateur et n'est pas partagé avec lui. |
| `403` | `You are not allowed to chat in this conversation` | avant | `conversation_id` appartient à un autre utilisateur. |
| `403` | `User is not a member of the organization` | avant | `organization_id` (ou l'organisation de l'assistant, ou l'organisation de la conversation) ne fait pas partie des organisations de l'utilisateur. |
| `403` | `User does not have write permissions in the organization` | avant | L'utilisateur est invité dans l'organisation. |
| `404` | `Assistant not found` | avant | Aucun assistant avec `assistant_id`. |
| `404` | `model not found` | avant | Aucun modèle avec l'id indiqué dans `model` (ou le modèle de l'assistant). |
| `422` | liste d'erreurs de champs | avant | Corps invalide, par exemple ni `assistant_id` ni `model` fourni (`Either assistant_id or model is required`). |
| `422` | `max_tokens must be between 1 and the maximum allowed tokens for the model` | avant | `max_tokens` hors limites. |
| `422` | `the conversation belongs to another organization` | avant | La conversation a été créée dans une organisation et la requête en précise une autre (`organization_id`, ou l'organisation de `assistant_id`). |
| `422` | `invalid base64 data in attachment '<filename>'` | avant | Le `data` d'une pièce jointe n'est pas du base64 valide. |
| `422` | `invalid data URI in attachment '<filename>'` | avant | Le `data` d'une pièce jointe commence par `data:` mais ne contient pas de `,` avant le contenu. |
| `422` | `file is too large` | avant | Une pièce jointe dépasse la limite d'upload. |
| `422` | `storage quota exceeded`, `organization storage quota exceeded` | avant | Une pièce jointe ne tient pas dans le quota de stockage. |
| `422` | `Assistant skill '/<slug>' requires unavailable tools: ...` | avant | La compétence activée a besoin d'un outil qui n'est pas disponible. |
| `422` | `model is deprecated` | avant | Le modèle (ou le modèle de l'assistant) est retiré. |
| `422` | `model is disabled` | avant | Le modèle (ou le modèle de l'assistant) est désactivé par l'administrateur. |
| `422` | `model is not available` | avant | Le modèle (ou le modèle de l'assistant) est un modèle automatique et aucun des modèles parmi lesquels il choisit n'est disponible. |
| `422` | `model does not have the required capability` | pendant | Le modèle n'est pas un modèle de chat. |
| `422` | `not enough user credit`, `not enough organization credit` | pendant | Voir [Organisations et crédits](#organisations-et-credits). |
| `422` | `max iterations reached` | pendant | Le modèle a continué à appeler des outils au-delà du nombre de tours autorisé. |
| `403` | `llm: refusal from provider` | pendant | Le fournisseur du modèle a refusé de répondre. |
| `404` | `organization membership not found` | pendant | L'utilisateur a quitté l'organisation dans laquelle la requête s'exécute. |
| `429` | message de limite de débit | avant | Trop de requêtes, voir [Limites de débit](conventions.md#limites-de-debit). |
| `500` | `llm: error on provider side`, `llm: error in chat stream`, `llm: max token reached` | pendant | Le fournisseur du modèle a échoué, ou la réponse a atteint la limite de tokens en sortie. |
| `529` | `llm: provider overload error` | pendant | Le fournisseur du modèle est surchargé ; réessayez plus tard. |
| `500` | `An error occurred during chat processing` | pendant | Erreur inattendue (sans `stream`). |

Si une erreur interrompt une réponse, la partie générée jusque-là est conservée dans la conversation.

### Exemples

Poser une question à un modèle et poursuivre la même conversation :

```bash
curl -X POST "https://ayeto.ai/api/v3/chat" \
  -H "uni-api-key: $AYETO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "3f6c1a9e-8b2d-4e57-9a1c-2d7e5b8f4a10",
    "model": "gpt-5-mini",
    "message": "Summarize the benefits of unit tests in three bullet points."
  }'
```

```json
{
  "id": "b1e4c7d2-5a3f-4c89-8e6b-0f2a9d1c7e35",
  "timestamp": 1759406412345,
  "role": "assistant",
  "content": "- **Catch regressions early** ...\n- **Document behaviour** ...\n- **Make refactoring safe** ...",
  "reasoning_content": "",
  "model": "gpt-5-mini",
  "attachments": [],
  "tool_runs": {}
}
```

```bash
curl -X POST "https://ayeto.ai/api/v3/chat" \
  -H "uni-api-key: $AYETO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "3f6c1a9e-8b2d-4e57-9a1c-2d7e5b8f4a10",
    "model": "gpt-5-mini",
    "message": "Now give an example for the second point in Python."
  }'
```

Discuter avec un assistant dans une organisation et joindre un document :

```bash
curl -X POST "https://ayeto.ai/api/v3/chat" \
  -H "uni-api-key: $AYETO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "8d2f0b6a-1c4e-4f7a-b3d9-6e5a2c1f0b87",
    "assistant_id": "c7a9e2f1-4b6d-4d38-9f0e-1a2b3c4d5e6f",
    "organization_id": "5e8b1d3c-7a2f-4c6e-8d9b-0a1f2e3d4c5b",
    "message": "List the payment terms in this contract.",
    "remove_tool_calls": true,
    "attachments": [
      {
        "filename": "contract.txt",
        "data": "data:text/plain;base64,UGF5bWVudCBkdWUgd2l0aGluIDMwIGRheXMu",
        "mime_type": "text/plain"
      }
    ]
  }'
```

Générer une image dans une conversation avec un modèle :

```bash
curl -X POST "https://ayeto.ai/api/v3/chat" \
  -H "uni-api-key: $AYETO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "0a7c3e5f-9b1d-4a26-8c4e-7f6d5b3a2c19",
    "model": "gpt-5-mini",
    "dynamic_tools": true,
    "message": "Draw a watercolor lighthouse at sunset."
  }'
```

```json
{
  "id": "e2d4f6a8-0b1c-4d3e-9f5a-7b6c8d9e0f12",
  "timestamp": 1759406530112,
  "role": "assistant",
  "content": "\n\n### calling AI tool ...\n### Generating image\n...\n![image.png](https://ayeto.ai/api/v1/public/file/read?id=9c1e...&secret=4f7a...)\n\n***\nHere is your watercolor lighthouse at sunset.",
  "reasoning_content": "",
  "model": "gpt-5-mini",
  "attachments": [
    {
      "filename": "image.png",
      "content_type": "image/png",
      "file_url": "https://ayeto.ai/api/v1/public/file/read?id=9c1e...&secret=4f7a...",
      "name": null,
      "description": null
    }
  ],
  "tool_runs": {}
}
```

Python (`requests`) :

```python
import base64
import os
import uuid

import requests

API = "https://ayeto.ai/api/v3"
HEADERS = {"uni-api-key": os.environ["AYETO_API_KEY"]}

conversation_id = str(uuid.uuid4())  # keep it to continue the conversation


def chat(message, attachments=None):
    response = requests.post(
        f"{API}/chat",
        headers=HEADERS,
        json={
            "conversation_id": conversation_id,
            "model": "gpt-5-mini",
            "message": message,
            "remove_tool_calls": True,
            "attachments": attachments,
        },
        timeout=600,  # answers that use tools can take minutes
    )
    if response.status_code != 200:
        raise RuntimeError(f"{response.status_code}: {response.json()['detail']}")
    return response.json()


with open("invoice.pdf", "rb") as f:
    pdf = f.read()

answer = chat(
    "What is the total amount of this invoice?",
    attachments=[{
        "filename": "invoice.pdf",
        "data": "data:application/pdf;base64," + base64.b64encode(pdf).decode(),
        "mime_type": "application/pdf",
    }],
)
print(answer["content"])
print(chat("And the due date?")["content"])
```

JavaScript (`fetch`) :

```javascript
const API = "https://ayeto.ai/api/v3";
const conversationId = crypto.randomUUID(); // keep it to continue the conversation

async function chat(message) {
  const response = await fetch(`${API}/chat`, {
    method: "POST",
    headers: {
      "uni-api-key": process.env.AYETO_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      conversation_id: conversationId,
      assistant_id: "c7a9e2f1-4b6d-4d38-9f0e-1a2b3c4d5e6f",
      message,
    }),
  });
  const body = await response.json();
  if (!response.ok) {
    throw new Error(`${response.status}: ${JSON.stringify(body.detail)}`);
  }
  return body;
}

const answer = await chat("Draft a short reply to a customer asking about delivery times.");
console.log(answer.content);
```

Pour recevoir la réponse pendant sa génération, définissez `"stream": true` et lisez la réponse
comme décrit dans [Streaming](streaming.md#lire-un-stream).
