Tool calling
Let the model call your functions. Works with both the OpenAI SDK and the Anthropic SDK.
Tool calling (function calling) lets the model return structured arguments to invoke functions you declare, instead of answering directly. Your code can run things the model can't — external API lookups, database queries, calculations — and feed the result back. PleumRouter accepts OpenAI-format tools, and when a request is routed to an Anthropic (Claude) model the router automatically translates them to Anthropic's tool format — you don't change the request shape.
Request#
| Parameter | Type | Required | Description |
|---|---|---|---|
| tools | array | Optional | List of functions the model may call. Use OpenAI format [{"type": "function", "function": {"name", "description", "parameters"}}], where parameters is a JSON Schema. |
| tool_choice | string | object | Optional | "auto" (model decides) · "required" (must call a tool) · "none" (no tools), or {"type": "function", "function": {"name": "..."}} to force a specific function. |
| parallel_tool_calls | boolean | Optional | Whether the model may call multiple tools in one response. Applies to OpenAI-compatible providers only; it is not translated for Anthropic. |
Call POST /v1/chat/completions with tools and (optionally) tool_choice in the request body. Authenticate with your plm_ API key via the Authorization: Bearer or x-api-key header.
curl https://apirouter.pleum.ai/v1/chat/completions \
-H "Authorization: Bearer $PLEUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [
{"role": "user", "content": "What is the weather in Seoul?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. Seoul"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}'Response#
When the model decides to call a tool, choices[0].message.content is null and the message.tool_calls array holds the function(s) and arguments. arguments is a JSON string, so parse it before use. In this case finish_reason is "tool_calls". Every chat response also includes the PleumRouter extensions cost (KRW cost, FX rate, markup) and request_id.
{
"id": "chatcmpl-gpt-4o-612ms",
"object": "chat.completion",
"model": "gpt-4o",
"provider": "openai",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Seoul\", \"unit\": \"celsius\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
],
"usage": {
"prompt_tokens": 78,
"completion_tokens": 21,
"total_tokens": 99
},
"cost": {
"usd": 0.000396,
"krw": 1,
"fx_rate": 1525.0,
"markup_rate": 0.0
},
"request_id": "req_01J9X2Qm7..."
}Multi-turn loop#
After receiving tool_calls, run the function(s) in your code, then call again with the same messagesarray extended with the model's assistant message (including tool_calls) and the result as a role: "tool" message. The tool message carries tool_call_id, name, and the result in content. Repeat this loop until the model produces a final answer.
{
"model": "gpt-4o",
"messages": [
{"role": "user", "content": "What is the weather in Seoul?"},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Seoul\", \"unit\": \"celsius\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"name": "get_weather",
"content": "{\"temp\": 21, \"unit\": \"celsius\", \"sky\": \"clear\"}"
}
],
"tools": [
{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}}, "required": ["city"]}}}
]
}parallel_tool_callsis OpenAI-compatible providers only. Anthropic (Claude) models are handled automatically — the router translates the tool format for you, so you don't change the request — but this flag is not forwarded to Anthropic.