Chat completions
Rute chat: parameter, response, dan penalaran.
Ringkasan
Rute utama NusaRouter. Bentuk request dan responsenya mengikuti SDK OpenAI.
/v1/chat/completionsbutuh API keyIni rute chat bentuk OpenAI. NusaRouter juga punya permukaan Anthropic yang terpisah — Format Anthropic — dengan base URL dan bentuk pesan yang berbeda. Jangan mencampur keduanya: di halaman ini yang berlaku hanya bentuk OpenAI.
Body request
| Field | Tipe | Wajib | Artinya |
|---|---|---|---|
model | string | Wajib | Id model, persis seperti yang ada di katalog. |
messages | array | Wajib | Minimal satu pesan. Bentuknya {role, content, name?, tool_call_id?, tool_calls?}. |
stream | boolean | Opsional | Default false. Lihat Streaming. |
max_tokens | integer | Opsional | Batas token keluaran. Maksimum 1.000.000. |
max_completion_tokens | integer | Opsional | Diterima bersama max_tokens. |
temperature | number | Opsional | 0 sampai 2. |
top_p | number | Opsional | 0 sampai 1. |
stop | string | string[] | Opsional | Satu atau beberapa sekuens penghenti. |
n | integer | Opsional | Hanya 1. Nilai lain ditolak 422. |
tools | array | Opsional | Fungsi yang boleh dipanggil model. Lihat Pemanggilan tool. |
tool_choice | string | object | Opsional | "auto", "required", atau memaksa satu fungsi. Diteruskan apa adanya, dan seberapa tegas ia ditegakkan bergantung modelnya. "none" belum dihormati, modelnya masih bisa memanggil tool. Lihat Pemanggilan tool. |
parallel_tool_calls | boolean | Opsional | Mengizinkan beberapa panggilan tool dalam satu balasan. |
Permukaan Anthropic (POST /v1/messages) memakai nama field Anthropic, bukan field di tabel ini. Lihat Format Anthropic.
role menerima system, developer, user, assistant, atau tool. content berupa string biasa, array berisi bagian bertipe, atau null kalau pesannya hanya membawa tool_calls.
Field tambahan
Field di luar daftar di atas diteruskan ke model, tidak dibuang.
Sebagian field yang umum dipakai sudah kami kenali dan teruskan apa adanya: response_format, seed, presence_penalty, frequency_penalty, logit_bias, logprobs, top_logprobs, user, store, metadata, service_tier, reasoning_effort, modalities, audio, prediction, web_search_options, stream_options, dan pasangan lama functions serta function_call.
Field yang bahkan tidak ada di daftar itu pun tetap diteruskan. Jadi opsi spesifik yang didukung model di balik layar tetap bisa kamu kirim.
Response
{
"id": "chatcmpl-d070b226-…",
"object": "chat.completion",
"created": 1758531600,
"model": "deepseek-flash-v4.1",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Halo! Ada yang bisa saya bantu?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 11,
"total_tokens": 20,
"cached_tokens": 0,
"reasoning_tokens": 0
}
}model di response berisi id yang kamu minta, bukan id internal. NusaRouter tidak pernah membuka rute maupun identitas di balik layar.
Blok usage berisi prompt_tokens, completion_tokens, total_tokens, cached_tokens, dan reasoning_tokens. Kalau model tidak melaporkan pemakaian, blok ini tidak ada di response, bukan berisi nol. Nol berarti “tidak ada yang terpakai”; tidak adanya blok berarti “tidak ada yang dilaporkan”, dan keduanya berbeda.
finish_reason diteruskan apa adanya, jadi jawaban yang terpotong karena batas token (length) tetap bisa dibedakan dari jawaban yang selesai (stop).
Kalau model meminta menjalankan fungsi, message-nya berisi tool_calls, content-nya null, dan finish_reason-nya "tool_calls". Bentuk lengkapnya, beserta cara membalasnya, ada di Pemanggilan tool.
Beberapa field lain ikut muncul kalau modelnya mengirim: refusal saat model menolak menjawab, system_fingerprint, dan service_tier. Field yang tidak dikirim model tidak muncul sama sekali, bukan muncul berisi null. logprobs masuk ke situ: kamu boleh memintanya, tapi NusaRouter tidak pernah mengirimkannya pada model yang aktif sekarang.
Model penalaran
Model yang bernalar mengirim dua hal: proses berpikirnya di reasoning_content, dan jawabannya di content.
Ada lima aturan yang berlaku, dan semuanya demi kamu:
- Kalau model mengirim penalarannya, isinya diteruskan apa adanya di
reasoning_content, tidak kami rangkum dan tidak kami buang. Kamu juga boleh mengirimkannya balik di pesan assistant, dan itu diteruskan utuh ke model. - Yang perlu kamu tahu: pada jawaban biasa,
reasoning_contentsering tidak ada sama sekali. Bukan hilang di sisi kami, tapi memang tidak dikirim modelnya begitu jawabannya sudah jadi. Jadi jangan mengandalkan field ini selalu ada. Yang bisa kamu pegang: kalau ada, isinya utuh. - Kalau model menghabiskan seluruh anggaran tokennya untuk bernalar dan tidak sempat menulis
contentsama sekali, penalarannya juga dikirim sebagaicontent, supaya kamu tidak menerima jawaban kosong. Pada streaming, isi itu ditulis sebelum frame yang membawafinish_reason, karena frame itulah yang jadi tempat klien berhenti membaca. - Pengecualiannya saat model memanggil tool: penalarannya tidak disalin ke
content, dancontent-nya tetapnull. Di situ yang kamu baca memangtool_calls, bukan jawaban, jadi menyalin penalaran kecontentjustru akan mencampur proses berpikir model ke dalam jawaban. Lihat Pemanggilan tool. - Kalau tidak ada
contentmaupunreasoning_content, request itu dianggap gagal. Biayanya ikut aturan kegagalan: token yang sudah diproses tetap ditagih, dan penahananmu dilepas penuh kalau tidak ada pemakaian yang tercatat. Lihat Urutan dan biaya.
Urutan dan biaya
Setiap request menempuh urutan yang sama:
batas laju per menit -> batas tingkat akses -> penahanan saldo -> modelBiaya ditahan sebelum request dikirim ke model, lalu diselesaikan dengan pemakaian sebenarnya dan sisanya dikembalikan. Penahanan itu sebuah estimasi, bukan harga tetap: kalau pemakaian ternyata lebih besar, tagihan menyesuaikan naik sampai maksimum 3× estimasi, dan tidak pernah melebihi saldomu.
Untuk request yang gagal, yang menentukan adalah apakah modelnya sempat bekerja, dan NusaRouter menagihkan hanya pemakaian yang tercatat. Kalau ada pemakaian yang tercatat, berarti tokennya benar-benar diproses, dan itu yang kamu bayar. Kalau tidak ada pemakaian yang tercatat sama sekali, seluruh penahananmu dilepas dan kamu tidak membayar apa pun.
Tarifnya diambil saat request dimulai, jadi perubahan harga di tengah request tidak bisa mengubah biaya request yang sedang jalan.
Troubleshooting
| Gejala | Penyebab | Solusi |
|---|---|---|
422 | Body gagal validasi: model kosong, messages kosong, atau content bukan string. | periksa pesan errornya: field yang bermasalah disebutkan namanya |
404 model_not_found | Id model salah, atau path-nya tidak cocok dengan rute mana pun. | ambil id dari katalog publik atau GET /v1/models |
| Jawaban kosong | Model tidak mengirim content maupun reasoning_content, jadi request itu dianggap gagal dengan 502 empty_completion. Kalau penalarannya ada, penalaran itu yang dikirim sebagai content, bukan jawaban kosong. | cek pesan errornya: kalau menyebut biaya, token yang sudah diproses tetap kamu bayar. Naikkan max_tokens kalau modelnya kehabisan anggaran |
| Parameter yang dikirim tidak berpengaruh | Field diteruskan apa adanya, dan model di baliknya mungkin tidak mendukungnya. | uji lewat playground sebelum memakainya di produksi |
Putaran kedua loop tool gagal 422 | Pesan assistant dikirim ulang dalam bentuk yang sudah diubah: tool_calls-nya dibuang, atau content: null-nya diganti jadi string kosong. | kirim pesan assistant itu apa adanya, lalu tambahkan satu pesan tool dengan tool_call_id yang cocok. Lihat Pemanggilan tool |
502 setelah mengirim tools | Skema parameters tidak valid, jadi modelnya yang menolak. Skema tidak diperiksa di sisi kami. | perbaiki JSON Schema-nya, lalu uji lagi |