Lewati ke konten
kenari.

Function calling

Function calling memungkinkan model meminta kodemu melakukan sesuatu yang tidak bisa dilakukannya sendiri, misalnya mencari pesanan atau membaca database. Jelaskan fungsi yang tersedia. Model memilih fungsi dan mengirim argumennya. Jalankan fungsi tersebut, lalu kirim hasilnya kembali agar model bisa menulis jawaban akhir. Fitur ini tersedia di Chat completions, Messages, dan Responses.

Model mendukung function calling jika tool_call bernilai true di GET /v1/models. Endpoint ini publik, jadi kamu bisa memfilternya tanpa key:

Terminal window
curl -s https://kenari.id/v1/models | jq -r '.data[] | select(.tool_call == true) | .id'

kenari tidak memblokir model yang tidak punya flag ini, tetapi model tersebut bisa mengabaikan tools-mu. Contoh di halaman ini memakai step-3-7-flash:free, yang mendukung tools dan gratis dipanggil.

Panggilan tool adalah percakapan pendek: request-mu, panggilan dari model, hasil darimu, lalu jawaban model.

  1. Kirim pesan user dan array tools.
  2. Model mengembalikan pesan assistant dengan tool_calls dan finish_reason: "tool_calls".
  3. Jalankan setiap fungsi. Tambahkan pesan assistant tadi, lalu satu pesan tool untuk setiap panggilan, masing-masing dengan tool_call_id yang sesuai.
  4. Kirim ulang seluruh percakapan. Kali ini model menjawab dengan teks biasa.

Setiap tool punya name, description yang memberi tahu model kapan memakainya, dan JSON Schema untuk parameters. Ini balasan model pada langkah 2:

{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Jakarta\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
}

arguments adalah string berisi JSON, bukan objek. Urai string JSON ini sebelum memakainya. Alur lengkap dengan OpenAI SDK:

import json
import os
from openai import OpenAI
client = OpenAI(
base_url="https://kenari.id/v1",
api_key=os.environ["KENARI_API_KEY"],
)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Ambil cuaca saat ini untuk sebuah kota.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "Nama kota, misalnya Jakarta"}
},
"required": ["city"],
},
},
}
]
def get_weather(city: str) -> dict:
return {"city": city, "temperature_c": 31, "conditions": "berawan"}
messages = [{"role": "user", "content": "Bagaimana cuaca di Jakarta sekarang?"}]
for _ in range(5): # hentikan loop yang berjalan terus
response = client.chat.completions.create(
model="step-3-7-flash:free",
messages=messages,
tools=tools,
)
message = response.choices[0].message
if not message.tool_calls:
print(message.content)
break
messages.append(
{
"role": "assistant",
"content": message.content,
"tool_calls": [call.model_dump() for call in message.tool_calls],
}
)
for call in message.tool_calls:
arguments = json.loads(call.function.arguments)
result = get_weather(**arguments)
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
}
)
else:
print("Berhenti setelah 5 putaran: model masih meminta panggilan tool.")

content pada pesan tool berupa string. Kirim JSON sebagai teks, atau pesan error biasa jika fungsinya gagal, supaya model bisa menanganinya.

Di /v1/messages, sebuah tool berbentuk {name, description, input_schema}. Model mengembalikan blok tool_use dan stop_reason: "tool_use". Kamu menjawab dengan blok tool_result di pesan user berikutnya.

Arahkan Anthropic SDK ke https://kenari.id, tanpa /v1. SDK menambahkan path-nya sendiri.

import json
import os
import anthropic
client = anthropic.Anthropic(
base_url="https://kenari.id",
api_key=os.environ["KENARI_API_KEY"],
)
tools = [
{
"name": "get_weather",
"description": "Ambil cuaca saat ini untuk sebuah kota.",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
]
messages = [{"role": "user", "content": "Bagaimana cuaca di Jakarta sekarang?"}]
for _ in range(5):
response = client.messages.create(
model="step-3-7-flash:free",
max_tokens=2048,
tools=tools,
messages=messages,
)
if response.stop_reason != "tool_use":
print("".join(block.text for block in response.content if block.type == "text"))
break
messages.append({"role": "assistant", "content": response.content})
results = []
for block in response.content:
if block.type == "tool_use":
output = {"city": block.input["city"], "temperature_c": 31}
results.append(
{
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(output),
}
)
messages.append({"role": "user", "content": results})
else:
print("Berhenti setelah 5 putaran: model masih meminta panggilan tool.")

Di /v1/responses, model mengembalikan item function_call dan kamu membalas dengan item function_call_output yang membawa call_id yang sama. Pada Responses, name dan parameters berada langsung di objek tool, yaitu {"type": "function", "name": ..., "parameters": ...}. Lihat Responses.

tool_choice menentukan apakah model memanggil tool serta tool yang harus dipanggil. Tanpa field ini, model yang memutuskan.

Chat completionsMessagesEfek
"auto"{"type": "auto"}Model yang memutuskan.
"required"{"type": "any"}Model harus memanggil minimal satu tool.
{"type": "function", "function": {"name": "get_weather"}}{"type": "tool", "name": "get_weather"}Model harus memanggil tool tersebut.
"none"{"type": "none"}Model menjawab tanpa memanggil tool.

Pilihan paksa tanpa nama ditolak dengan 400. Bentuk yang tidak dikenal ditangani berbeda di tiap format API. Chat completions memperlakukan bentuk yang tidak dikenal, seperti allowed_tools, sebagai "auto". Messages dan Responses menolak tipe tool_choice yang tidak dikenal dengan 400.

Model bisa mengembalikan beberapa panggilan dalam satu balasan: beberapa entri di tool_calls, atau beberapa blok tool_use. Jalankan semuanya, lalu jawab setiap panggilan. Di Chat completions itu berarti satu pesan tool per tool_call_id. Di Messages, semua blok tool_result dikirim dalam satu pesan user.

kenari hanya meneruskan field parallel_tool_calls ke model yang dilayani lewat API gaya OpenAI, dan membuangnya untuk model lain. Field ini juga dibuang setiap kali request mencantumkan server tool kenari:. Jangan mengandalkannya untuk mencegah panggilan paralel. Tulis loop-mu agar bisa menangani satu panggilan maupun banyak.

Dengan "stream": true, sebuah panggilan datang dalam potongan yang harus kamu gabungkan sebelum di-parse. Lihat Streaming untuk field dan event tiap format API.

Tool dengan tipe kenari:<name>, misalnya {"type": "kenari:web_search"}, dijalankan di kenari. Kamu tidak perlu menulis loop: model memanggilnya, kenari menjalankannya lalu melanjutkan jawaban. Kamu bisa mencampur server tools dengan fungsimu sendiri dalam satu array tools. Lihat Server tools.

  • Kirim array tools yang sama di setiap request dalam loop, termasuk request yang mengembalikan jawaban akhir.
  • Definisikan setiap tool yang dijalankan kodemu sebagai tool function. Selain server tool kenari:, tipe tool tanpa definisi fungsi tidak dijalankan kenari, jadi jangan bergantung padanya.
  • Model bisa menghasilkan JSON yang tidak valid di arguments, atau nilai di luar skema-mu. Validasi sebelum menjalankan apa pun yang punya efek samping.
  • Request yang mendeklarasikan tools, atau percakapan yang sudah berisi panggilan dan hasil tool, hanya dikirim ke provider yang mampu membawanya, sehingga tools dan hasilmu tidak hilang diam-diam.