DokumentasiReferensi API

Pemanggilan tool

Model meminta menjalankan fungsimu, lalu kamu balas hasilnya.

Ringkasan

Kamu kirim daftar fungsi, model memilih satu, kamu jalankan, lalu balas hasilnya. Model tidak menjalankan apa pun sendiri, ia hanya meminta.

POST/v1/chat/completionsbutuh API key

Loop-nya tiga langkah, dan berulang sampai model berhenti minta:

  1. Kirim tools berisi fungsi yang boleh dipanggil, lalu model menjawab dengan tool_calls dan finish_reason: "tool_calls".
  2. Jalankan fungsinya di sisimu, lalu kirim hasilnya sebagai pesan role: "tool".
  3. Ulangi langkah 1. Model akan minta lagi, atau berhenti dengan finish_reason: "stop" dan jawaban akhir di content.

Yang perlu kamu sadari: yang menjalankan fungsinya adalah kodemu. Kami cuma menyampaikan permintaan dan hasilnya. Jadi fungsi yang kamu daftarkan tetap harus kamu validasi sendiri, seperti input dari mana pun.

Mengirim daftar tool

Tambahkan tools ke body request seperti ini:

curl
curl https://api.nusarouter.web.id/v1/chat/completions \
  -H "Authorization: Bearer sk-nusa-..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-flash-v4.1",
    "messages": [{"role": "user", "content": "Cuaca di Jakarta?"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Cuaca sebuah kota",
        "parameters": {
          "type": "object",
          "properties": {"city": {"type": "string"}},
          "required": ["city"]
        }
      }
    }]
  }'
FieldTipeWajibArtinya
toolsarrayOpsionalDaftar fungsi. Kosongkan atau hilangkan kalau tidak ada yang perlu dipanggil.
tool_choicestring | objectOpsional"auto", "required", "none", atau {"type":"function","function":{"name":"…"}} untuk memaksa satu fungsi. Diteruskan apa adanya, tapi seberapa tegas ia ditegakkan bergantung modelnya. Lihat Batasan.
parallel_tool_callsbooleanOpsionalMengizinkan beberapa panggilan dalam satu balasan.

Tiap entri tools berbentuk {type, function}, dengan function berisi name, description, dan parameters.

Bentuk balasannya

Kalau model memilih memanggil fungsi, response-nya seperti ini:

200 application/json
{
  "id": "chatcmpl-bc02dceb-…",
  "object": "chat.completion",
  "created": 1790164911,
  "model": "deepseek-flash-v4.1",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "reasoning_content": "User asks weather in Jakarta, use tool.",
        "tool_calls": [
          {
            "id": "call_00_bIaFbd4YimVPH8K1YYDn2086",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\": \"Jakarta\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "prompt_tokens": 3052,
    "completion_tokens": 71,
    "total_tokens": 3123,
    "cached_tokens": 0,
    "reasoning_tokens": 31
  }
}

Empat hal yang perlu kamu perhatikan:

  • content berisi null, bukan string kosong. Itu penanda bahwa balasan ini adalah permintaan memanggil fungsi, bukan jawaban. Cek tool_calls, bukan content.
  • finish_reason berisi "tool_calls".
  • arguments adalah string berisi JSON, bukan object. Kamu perlu JSON.parse sendiri. Argumen yang tidak bisa diparse berarti model salah menulis, dan itu urusanmu untuk memutuskan mau diapakan.
  • id dipakai untuk memasangkan hasilnya nanti. Simpan apa adanya, jangan diubah.

Membalas dengan hasilnya

Kirim ulang percakapannya, dengan pesan assistant tadi ditambahkan apa adanya, lalu satu pesan tool untuk tiap panggilan.

Contoh lengkapnya, dua putaran:

python
import json

from openai import OpenAI

client = OpenAI(
    base_url="https://api.nusarouter.web.id/v1",
    api_key="sk-nusa-...",
)

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Cuaca sebuah kota",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string", "description": "Nama kota"}},
            "required": ["city"],
        },
    },
}]

messages = [{"role": "user", "content": "Cuaca di Jakarta?"}]

# Putaran 1: model meminta memanggil tool.
res = client.chat.completions.create(
    model="deepseek-flash-v4.1", messages=messages, tools=tools, max_tokens=400,
)
msg = res.choices[0].message

# Balas dengan hasil tool, satu pesan "tool" per panggilan.
messages.append(msg)
for call in msg.tool_calls:
    args = json.loads(call.function.arguments)
    messages.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": json.dumps({"city": args["city"], "temp_c": 31}),
    })

# Putaran 2: model menjawab dengan hasilnya.
res = client.chat.completions.create(
    model="deepseek-flash-v4.1", messages=messages, tools=tools, max_tokens=400,
)
print(res.choices[0].message.content)

Kode itu dijalankan apa adanya dan menghasilkan jawaban akhir di putaran kedua. Fungsi get_weather-nya sendiri tidak ada di contoh, karena di situlah kodemu masuk.

Padanan Anthropic

Permukaan Anthropic (POST /v1/messages) menjalankan loop yang sama, dengan bentuk yang berbeda.

anthropic
{
  "tools": [
    { "name": "get_weather", "input_schema": { "type": "object", "properties": { "city": { "type": "string" } } } }
  ]
}

// balasan model: blok tool_use
{ "role": "assistant", "content": [
  { "type": "tool_use", "id": "toolu_…", "name": "get_weather", "input": { "city": "Jakarta" } }
] }

// balasanmu: pesan user berisi blok tool_result
{ "role": "user", "content": [
  { "type": "tool_result", "tool_use_id": "toolu_…", "content": "31°C" }
] }

Tool dideklarasikan dengan input_schema (bukan parameters), model membalas dengan blok tool_use, dan hasilnya kamu kirim sebagai pesan user berisi blok tool_result yang dipasangkan lewat tool_use_id. tool_choice menerima auto, any, tool, atau none. Bentuk lengkapnya di Format Anthropic.

Kalau pakai streaming

Dengan stream: true, panggilan tool datang bertahap. Bentuknya beda dari balasan biasa, dan bagian ini yang paling mudah salah.

text/event-stream
data: {"choices":[{"delta":{"tool_calls":[{"function":{"arguments":"","name":"get_weather"},"id":"call_00_bJXKcMskleYxzU4rlsAV3026","index":0,"type":"function"}]},"finish_reason":null,"index":0,"logprobs":null}],"created":1790164917,"id":"de4fab0d5f34492fae467e9ef3bd6b4c","model":"deepseek-flash-v4.1","object":"chat.completion.chunk","usage":null}

data: {"choices":[{"delta":{"tool_calls":[{"function":{"arguments":"{"},"index":0}]},"finish_reason":null,"index":0,"logprobs":null}],"created":1790164917,"id":"de4fab0d5f34492fae467e9ef3bd6b4c","model":"deepseek-flash-v4.1","object":"chat.completion.chunk","usage":null}

data: {"choices":[{"delta":{"function_call":{"arguments":"","name":""},"role":"assistant"},"finish_reason":"tool_calls","index":0,"logprobs":null}],"created":1790164917,"id":"de4fab0d5f34492fae467e9ef3bd6b4c","model":"deepseek-flash-v4.1","object":"chat.completion.chunk","usage":{"prompt_tokens":3052,"completion_tokens":49,"total_tokens":3101,"cached_tokens":896,"reasoning_tokens":9}}

data: [DONE]

Kunci membacanya: frame pertama membawa id dan name dengan arguments kosong. Frame-frame setelahnya hanya membawa potongan arguments, dengan index sebagai penanda panggilan mana. Jadi gabungkan potongannya per index, baru parse di akhir. Memparse tiap potongan akan gagal, karena satu potongan bukan JSON yang utuh.

Penanda selesainya tetap data: [DONE], dan frame terakhir membawa finish_reason: "tool_calls" beserta usage yang ditagihkan. Frame terakhir itu juga membawa function_call berisi string kosong, dan itu memang begitu adanya, bukan sesuatu yang perlu kamu pakai.

Batasan

Tiga hal yang perlu kamu tahu sebelum mengandalkan fitur ini:

  • tool_choice: "none" belum dihormati. Nilainya kami terima dan teruskan, tapi model di baliknya masih bisa memanggil fungsi. Kalau kamu benar-benar ingin model menjawab tanpa memanggil apa pun, jangan kirim tools sama sekali.
  • Modelnya harus mendukung. Tidak semua model di katalog bisa memanggil fungsi. Model yang tidak bisa akan menjawab dengan teks biasa seolah tidak ada tools di request, tanpa error. Uji dulu model yang mau kamu pakai.
  • Tidak ada penyimpanan status. Tiap request berdiri sendiri. Riwayat percakapan, termasuk hasil tool sebelumnya, kamu kirim ulang setiap kali.

Lihat katalog publik untuk model yang aktif hari ini, dan Model dan harga untuk cara membacanya.

Troubleshooting

GejalaPenyebabSolusi
Putaran kedua gagal 422Pesan assistant tidak dikirim utuh: tool_calls-nya hilang, atau content: null-nya diganti jadi string kosong.tambahkan pesan assistant apa adanya, jangan diubah bentuknya
422Pesan tool tanpa tool_call_id, atau arguments dikirim sebagai object.tool_call_id wajib ada dan harus cocok dengan id panggilannya; arguments selalu string
502Skema parameters tidak valid, jadi modelnya yang menolak. Skema tidak diperiksa di sisi kami.perbaiki JSON Schema-nya, lalu uji lagi lewat playground
Model menjawab teks biasa, tidak memanggil apa punModel itu tidak mendukung pemanggilan fungsi, atau tidak menganggap perlu memanggil.pakai model lain, atau pertegas description fungsinya
Argumen hasil gabungan gagal diparsePotongan arguments diparse satu per satu, bukan digabung dulu per index.gabungkan semua potongan dengan index yang sama, baru parse sekali