SAGEA LogoDocs & API
Studio

Function calling

Function calling lets a chat model request your code — weather lookups, order databases, refunds — then turn the results into a natural reply.

In this guide

  • Extend Build an agent with robust patterns
  • Run independent tools in parallel for lower latency
  • Control behavior with tool_choice set to auto, any, or none
  • Handle tool errors with re-prompts that keep the chat going

Define two Kathmandu tools

Declare each function as a JSON schema with a name, description, and parameters. This example pairs a Kathmandu weather lookup with an order-status query — one public, one private.

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Current weather for a Nepal city",
            "parameters": {
                "type": "object",
                "required": ["city"],
                "properties": {"city": {"type": "string"}},
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "lookup_order",
            "description": "Delivery status for a SAGEA order ID",
            "parameters": {
                "type": "object",
                "required": ["order_id"],
                "properties": {"order_id": {"type": "string"}},
            },
        },
    },
]
curl -X POST https://api.sagea.space/v1/chat/completions \
  -H "Authorization: Bearer $SAGEA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sage-2-4-actus",
    "tool_choice": "auto",
    "messages": [{"role": "user", "content": "Kathmandu मा अहिले मौसम कस्तो छ? अनि ORD-7712 कहाँ छ?"}],
    "tools": [
      {"type": "function", "function": {"name": "get_weather"}},
      {"type": "function", "function": {"name": "lookup_order"}}
    ]
  }'

Use sage-2-4-actus when calls depend on Nepali nuance or multi-step reasoning. Full tool schemas live in the Chat Completions reference.

Run independent calls in parallel

When the model returns two tool_calls in one turn — weather plus order — execute them concurrently, not sequentially. Append each result as a tool message with the matching tool_call_id, then send everything back for the final answer.

import concurrent.futures, json
 
calls = first.choices[0].message.tool_calls
 
def run(call):
    if call.function.name == "get_weather":
        return {"temp_c": 18, "condition": "हल्का बादल"}
    return {"order_id": "ORD-7712", "status": "Kalanki hub", "eta": "भोलि 11 बजे"}
 
with concurrent.futures.ThreadPoolExecutor() as pool:
    results = list(pool.map(run, calls))
 
followup = [first.choices[0].message]
for call, result in zip(calls, results):
    followup.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": json.dumps(result, ensure_ascii=False),
    })
 
final = client.chat.completions.create(
    model="sage-2-4-actus",
    messages=[
        {"role": "user", "content": "Kathmandu मा अहिले मौसम कस्तो छ? अनि ORD-7712 कहाँ छ?"},
        *followup,
    ],
)

Parallel calls cut p95 latency roughly in half for two-tool turns. Only chain sequentially when the second call needs the first call output.

Steer with tool_choice

tool_choice decides whether the model may, must, or must not call functions.

ValueBehaviorWhen to use
autoModel decides per turnDefault support bots with optional lookups
anyMust call at least one toolGrounded answers — no reply without data
nonePlain chat, tools ignoredCreative writing, greetings, FAQs

Start with auto. Switch to any for flows like refunds or KYC where an ungrounded answer is worse than a slower one. Use none for a fast classification pre-pass on sage-2-5-celer before escalating to a tool-enabled sage-2-4-actus turn.

Recover from tool errors

Tools fail — expired order IDs, timeouts, bad districts. Return the error as tool content, not as an exception, and let the model re-prompt the user gracefully in Nepali.

def lookup_order_safe(order_id):
    try:
        return fetch_order(order_id)
    except OrderNotFound:
        return {"error": "ORDER_NOT_FOUND", "hint": "ORD- prefix सहित ID दिनुहोस्"}
    except UpstreamTimeout:
        return {"error": "RETRYABLE", "hint": "पछि फेरि प्रयास गर्नुहोस्"}
 
# In the follow-up system message:
# "If a tool returns error, explain in Nepali and ask for exactly one correction."

Cap retries at two per tool per turn. On repeated RETRYABLE errors, answer with cached data or escalate to a human instead of looping. For the full starter flow, revisit Build an agent.

Best practices

  • Give every tool a one-line Nepali-aware description with an example input.
  • Validate arguments in code before hitting your database.
  • Keep tool results small — summarize rows, never dump full tables.
  • Log every tool_call_id, latency, and error code for debugging.
  • Test auto vs any on 50 real utterances before locking a default.

What's next

On this page