> ## 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.

# Penggunaan token

Setiap respons LLM membawa objek `usage` yang melaporkan token yang dikonsumsi oleh permintaan tersebut. Sunra melaporkan penggunaan dalam semantik endpoint yang Anda panggil: respons Messages mengikuti kontrak Anthropic, dan respons Chat Completions atau Responses mengikuti kontrak OpenAI.

Bentuk-bentuk ini berbeda, dan itu disengaja. SDK OpenAI yang membaca respons Chat Completions mengasumsikan semantik OpenAI, dan SDK Anthropic yang membaca respons Messages mengasumsikan semantik Anthropic. Setiap endpoint menghormati apa yang dijanjikan oleh spesifikasinya sendiri alih-alih dipaksa masuk ke dalam satu bentuk bersama.

| Endpoint               | Field jumlah prompt | Apakah token cache termasuk di dalamnya?                           |
| ---------------------- | ------------------- | ------------------------------------------------------------------ |
| `/v1/messages`         | `input_tokens`      | Tidak — ketiga bucket input saling eksklusif                       |
| `/v1/chat/completions` | `prompt_tokens`     | Ya — token cache adalah subset, dirinci di `prompt_tokens_details` |
| `/v1/responses`        | `input_tokens`      | Ya — token cache adalah subset, dirinci di `input_tokens_details`  |

<Warning>
  `/v1/messages` dan `/v1/responses` sama-sama menggunakan nama field `input_tokens`, dan artinya tidak sama pada keduanya. Pada Messages, itu hanya input baru. Pada Responses, itu adalah keseluruhan prompt, termasuk token cache.
</Warning>

## Messages

Pada `/v1/messages`, ketiga bucket input **saling eksklusif**. Tidak ada token yang dihitung dua kali, sehingga total prompt adalah jumlah dari ketiganya:

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

<ResponseField name="input_tokens" type="integer">
  Hanya token input baru. Tidak termasuk kedua bucket cache.
</ResponseField>

<ResponseField name="cache_creation_input_tokens" type="integer">
  Token yang ditulis ke cache oleh permintaan ini.
</ResponseField>

<ResponseField name="cache_read_input_tokens" type="integer">
  Token yang disajikan ke permintaan ini dari cache.
</ResponseField>

<ResponseField name="output_tokens" type="integer">
  Token yang dihasilkan oleh model.
</ResponseField>

<ResponseField name="total_tokens" type="integer">
  Ketiga bucket input ditambah `output_tokens`. Ada pada respons non-streaming.
</ResponseField>

Bucket yang tidak ada atau bernilai `null` dihitung sebagai nol.

### Contoh perhitungan

Prefix 19.000 token yang sama dikirim dua kali ke `claude-opus-4-8`. Permintaan pertama menulis cache; permintaan kedua membacanya.

```json Permintaan dingin (penulisan 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 Permintaan hangat (pembacaan 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` tetap 8 pada kedua permintaan, karena 8 adalah input yang benar-benar baru setiap kali. Prefix 19.349 token berpindah dari bucket pembuatan cache ke bucket pembacaan cache. Kedua permintaan sama-sama berjumlah total 19.359 token tetapi biayanya tidak sama: penulisan cache dan pembacaan cache dikenakan tarif per-token masing-masing, itulah sebabnya keduanya dilaporkan sebagai bucket terpisah.

## Chat Completions dan Responses

Endpoint-endpoint ini melaporkan semantik OpenAI, tanpa perubahan. Jumlah prompt adalah **keseluruhan** prompt, dan token cache adalah **subset** darinya yang dilaporkan secara terpisah.

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

Menjumlahkan `prompt_tokens` dan `cached_tokens` menghasilkan penghitungan ganda. Untuk mendapatkan input baru pada endpoint ini, kurangkan:

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

Aturan yang sama berlaku untuk `/v1/responses` dengan `input_tokens` dan `input_tokens_details.cached_tokens`.

## Penanda `sunra_usage_semantics`

Respons yang telah dinormalisasi oleh Sunra membawa sebuah penanda di dalam `usage`:

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

Penanda ini adalah sebuah janji tentang **respons**, bukan tentang event atau field mana pun secara individual. Pada respons tanpa streaming, penanda muncul di objek `usage` root; pada respons yang di-stream, penanda muncul di `message_start`, yaitu event yang membawa bucket input. Ketika penanda hadir dengan nilai ini, jaminan pada halaman ini berlaku: ketiga bucket input saling eksklusif, dan `total_tokens` — jika ada — adalah jumlah ketiganya ditambah `output_tokens`.

**Ketiadaannya juga merupakan sebuah janji.** Sunra hanya menormalisasi bentuk respons yang telah diukurnya. Selebihnya, respons diteruskan dari provider upstream tanpa disentuh dan dibiarkan tanpa penanda, dan respons tanpa penanda adalah respons yang bucket-bucketnya tidak dijamin oleh Sunra.

Lakukan pemeriksaan terhadap penanda alih-alih memeriksa angka-angkanya untuk menebak konvensi mana yang diikuti sebuah respons. Menebak dari bentuk respons adalah hal yang ingin digantikan oleh field ini — heuristik seperti "bucket-bucket ini pasti eksklusif karena jumlahnya lebih besar dari jumlah prompt" dipenuhi oleh lebih dari satu konvensi dan pada akhirnya akan membaca sebuah respons secara keliru.

```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
```

Hanya respons `/v1/messages` yang pernah membawa penanda ini. Chat Completions dan Responses mengikuti spesifikasi OpenAI dan tidak diberi penanda.

Nilainya memiliki versi. Perubahan yang merusak pada makna field-field ini akan dirilis dengan nilai baru, sehingga pemeriksaan kesamaan terhadap `anthropic.exclusive.v1` tidak akan diam-diam mulai membaca kontrak yang berbeda.

## Streaming

Pada permintaan Messages yang di-stream, penggunaan tiba melalui dua event. `message_start` membawa sisi input dan penanda. `message_delta` terakhir hanya membawa `output_tokens`. Lakukan pemeriksaan terhadap penanda di `message_start`, lalu gabungkan kedua event seperti biasa — menerapkan usage dari event yang lebih akhir di atas event yang lebih awal — untuk memperoleh nilai-nilai yang didokumentasikan di atas.

`total_tokens` dihilangkan dari respons yang di-stream. Tidak ada satu event pun yang mengetahui sisi input dan sisi output sekaligus, sehingga total apa pun yang dihitung di tengah stream akan salah. Jumlahkan sendiri bucket-bucket tersebut setelah stream selesai.

## Migrasi dari perilaku sebelumnya

Sebelumnya Sunra meneruskan objek `usage` dari upstream secara verbatim pada `/v1/messages`. Lapisan penerjemahan di depan sebagian provider melipat token pembuatan cache **ke dalam** `input_tokens`, sehingga pemanggil yang mengikuti kontrak Anthropic dan menjumlahkan ketiga bucket menghitung token pembuatan cache dua kali.

* **Jika Anda menjumlahkan ketiga bucket**, seperti yang dijelaskan oleh kontrak Anthropic, sekarang Anda sudah benar. Tidak ada perubahan yang diperlukan di sisi Anda.
* **Jika Anda melakukan kompensasi untuk perilaku lama** — mengurangkan sendiri `cache_creation_input_tokens` dari `input_tokens`, atau merekayasa balik pelipatan itu dengan cara lain — **hentikan**. Koreksi tersebut kini mengurangkan token yang memang sudah dikecualikan dan akan membuat input Anda terlaporkan lebih rendah dari yang sebenarnya.
* **Jika Anda membaca `total_tokens`**, field ini tetap melaporkan hitungan penuh: input baru, kedua bucket cache, dan output.

Kendalikan perubahan ini berdasarkan penanda, bukan berdasarkan tanggal deploy, sehingga jalur kode yang sama tetap benar baik untuk respons yang bertanda maupun yang tidak bertanda.
