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

# Utilisation des tokens

Chaque réponse LLM comporte un objet `usage` qui indique les tokens consommés par la requête. Sunra rapporte l'utilisation selon la sémantique de l'endpoint que vous avez appelé : une réponse Messages suit le contrat Anthropic, et une réponse Chat Completions ou Responses suit le contrat OpenAI.

Ces structures diffèrent, délibérément. Un SDK OpenAI qui lit une réponse Chat Completions suppose une sémantique OpenAI, et un SDK Anthropic qui lit une réponse Messages suppose une sémantique Anthropic. Chaque endpoint respecte ce que sa propre spécification promet plutôt que d'être forcé dans une structure unique et partagée.

| Endpoint               | Champ de comptage du prompt | Les tokens en cache y sont-ils inclus ?                                                   |
| ---------------------- | --------------------------- | ----------------------------------------------------------------------------------------- |
| `/v1/messages`         | `input_tokens`              | Non — les trois buckets d'entrée sont mutuellement exclusifs                              |
| `/v1/chat/completions` | `prompt_tokens`             | Oui — les tokens en cache en sont un sous-ensemble, détaillé dans `prompt_tokens_details` |
| `/v1/responses`        | `input_tokens`              | Oui — les tokens en cache en sont un sous-ensemble, détaillé dans `input_tokens_details`  |

<Warning>
  `/v1/messages` et `/v1/responses` utilisent tous deux le nom de champ `input_tokens`, et il ne signifie pas la même chose dans les deux cas. Sur Messages, il s'agit uniquement de l'entrée fraîche. Sur Responses, il s'agit de l'intégralité du prompt, tokens en cache inclus.
</Warning>

## Messages

Sur `/v1/messages`, les trois buckets d'entrée sont **mutuellement exclusifs**. Aucun token n'est compté deux fois, le total du prompt est donc leur somme :

```
total prompt = input_tokens + cache_creation_input_tokens + cache_read_input_tokens
```

<ResponseField name="input_tokens" type="integer">
  Uniquement les tokens d'entrée frais. Exclut les deux buckets de cache.
</ResponseField>

<ResponseField name="cache_creation_input_tokens" type="integer">
  Tokens écrits dans le cache par cette requête.
</ResponseField>

<ResponseField name="cache_read_input_tokens" type="integer">
  Tokens servis à cette requête depuis le cache.
</ResponseField>

<ResponseField name="output_tokens" type="integer">
  Tokens générés par le modèle.
</ResponseField>

<ResponseField name="total_tokens" type="integer">
  Les trois buckets d'entrée plus `output_tokens`. Présent sur les réponses non-streaming.
</ResponseField>

Un bucket absent ou `null` compte pour zéro.

### Exemple détaillé

Le même préfixe de 19 000 tokens envoyé deux fois vers `claude-opus-4-8`. La première requête écrit le cache ; la seconde le lit.

```json Requête à froid (écriture dans le cache) theme={null}
{
  "usage": {
    "input_tokens": 8,
    "cache_creation_input_tokens": 19349,
    "cache_read_input_tokens": 0,
    "output_tokens": 2,
    "total_tokens": 19359,
    "sunra_usage_semantics": "anthropic.exclusive.v1"
  }
}
```

```json Requête à chaud (lecture depuis le cache) theme={null}
{
  "usage": {
    "input_tokens": 8,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 19349,
    "output_tokens": 2,
    "total_tokens": 19359,
    "sunra_usage_semantics": "anthropic.exclusive.v1"
  }
}
```

`input_tokens` reste à 8 pour les deux requêtes, car 8 correspond à l'entrée réellement nouvelle à chaque fois. Le préfixe de 19 349 tokens passe du bucket de création au bucket de lecture. Les deux requêtes totalisent les mêmes 19 359 tokens mais ne coûtent pas la même chose : les écritures de cache et les lectures de cache sont facturées à leurs propres tarifs par token, ce qui explique qu'elles soient rapportées comme des buckets distincts.

## Chat Completions et Responses

Ces endpoints rapportent la sémantique OpenAI, inchangée. Le comptage du prompt correspond au prompt **entier**, et les tokens en cache en sont un **sous-ensemble** rapporté séparément.

```json /v1/chat/completions theme={null}
{
  "usage": {
    "prompt_tokens": 19357,
    "completion_tokens": 2,
    "total_tokens": 19359,
    "prompt_tokens_details": {
      "cached_tokens": 19349
    }
  }
}
```

Additionner `prompt_tokens` et `cached_tokens` revient à compter deux fois. Pour obtenir l'entrée fraîche sur ces endpoints, soustrayez :

```
fresh input = prompt_tokens - prompt_tokens_details.cached_tokens
```

La même règle s'applique à `/v1/responses` avec `input_tokens` et `input_tokens_details.cached_tokens`.

## Le marqueur `sunra_usage_semantics`

Les réponses que Sunra a normalisées portent un marqueur à l'intérieur de `usage` :

```json theme={null}
"sunra_usage_semantics": "anthropic.exclusive.v1"
```

Le marqueur est une promesse portant sur la **réponse**, et non sur un événement ou un champ particulier. Sur une réponse sans streaming, il apparaît dans l'objet `usage` racine ; sur une réponse en streaming, il apparaît dans `message_start`, l'événement qui porte les buckets d'entrée. Lorsqu'il est présent avec cette valeur, les garanties de cette page s'appliquent : les trois buckets d'entrée sont mutuellement exclusifs, et `total_tokens` — lorsqu'il est présent — est leur somme plus `output_tokens`.

**Son absence est également une promesse.** Sunra ne normalise que les structures de réponse qu'il a mesurées. Tout le reste est transmis tel quel depuis le fournisseur en amont et laissé sans marqueur, et une réponse sans le marqueur est une réponse pour laquelle Sunra ne se porte pas garant des buckets.

Appuyez-vous sur le marqueur plutôt que d'inspecter les nombres pour deviner quelle convention suit une réponse. La détection par la forme est précisément ce que ce champ existe pour remplacer — une heuristique telle que « les buckets doivent être exclusifs puisque leur somme dépasse le comptage du prompt » est satisfaite par plus d'une convention et finira par lire une réponse de travers.

```python theme={null}
usage = response["usage"]

if usage.get("sunra_usage_semantics") == "anthropic.exclusive.v1":
    total_prompt = (
        usage["input_tokens"]
        + usage.get("cache_creation_input_tokens", 0)
        + usage.get("cache_read_input_tokens", 0)
    )
else:
    # Not normalized by Sunra. Treat the upstream shape as unverified
    # and consult that provider's own documentation.
    total_prompt = None
```

Seules les réponses de `/v1/messages` portent le marqueur. Chat Completions et Responses suivent la spécification OpenAI et ne sont pas marquées.

La valeur est versionnée. Un changement cassant de la signification de ces champs est livré sous une nouvelle valeur, de sorte qu'un test d'égalité avec `anthropic.exclusive.v1` ne se mettra pas silencieusement à lire un contrat différent.

## Streaming

Sur une requête Messages en streaming, l'utilisation arrive à travers deux événements. `message_start` porte le côté entrée et le marqueur. Le `message_delta` terminal ne porte que `output_tokens`. Appuyez-vous sur le marqueur de `message_start`, puis fusionnez les deux événements comme d'habitude — appliquer l'utilisation de l'événement le plus tardif par-dessus celle du plus ancien — pour obtenir les valeurs documentées ci-dessus.

`total_tokens` est omis des réponses en streaming. Aucun événement isolé ne connaît à la fois le côté entrée et le côté sortie, donc tout total calculé en cours de stream serait faux. Additionnez vous-même les buckets une fois le stream terminé.

## Migration depuis le comportement précédent

Sunra transmettait auparavant tel quel l'objet `usage` en amont sur `/v1/messages`. La couche de traduction placée devant certains fournisseurs replie les tokens de création de cache **dans** `input_tokens`, de sorte qu'un appelant qui suivait le contrat Anthropic et additionnait les trois buckets comptait deux fois les tokens de création de cache.

* **Si vous additionnez les trois buckets**, comme le décrit le contrat Anthropic, vous êtes désormais dans le vrai. Aucun changement n'est requis de votre côté.
* **Si vous compensiez l'ancien comportement** — en soustrayant vous-même `cache_creation_input_tokens` de `input_tokens`, ou en rétro-concevant le repli d'une autre manière — **arrêtez**. Cette correction soustrait maintenant des tokens qui étaient déjà exclus et sous-estimera votre entrée.
* **Si vous lisez `total_tokens`**, il continue de rapporter le compte complet : l'entrée fraîche, les deux buckets de cache et la sortie.

Conditionnez le changement au marqueur plutôt qu'à une date de déploiement, afin que le même chemin de code soit correct aussi bien face aux réponses marquées que non marquées.
