DocumentationTool calling & JSON
Ask AI

Take the docs with you

Ask ChatGPTOpen a new chat ↗Ask ClaudeOpen a new chat ↗Ask on GeminiCopy prompt, then paste in Gemini ↗View llms.txtDocumentation index ↗

External chats receive links to public docs.

Tool calling & structured output

Tool calling is passed through to the selected model. AtmoRouter does not execute your functions. Your application validates arguments, runs its own function, and returns the result in the next message.

OpenAI function-calling round trip

import json, os
from openai import OpenAI

client = OpenAI(api_key=os.environ["ATMOROUTER_API_KEY"],
                base_url="https://atmorouter.dev/v1")
messages = [{"role": "user", "content": "What is the weather in Paris?"}]
tools = [{"type": "function", "function": {
    "name": "get_weather", "description": "Get weather for a city",
    "parameters": {"type": "object", "properties": {
        "city": {"type": "string"}}, "required": ["city"]}
}}]
first = client.chat.completions.create(model="YOUR_MODEL_ID", messages=messages,
                                      tools=tools, max_tokens=256)
message = first.choices[0].message
messages.append(message.model_dump(exclude_none=True))
for call in message.tool_calls or []:
    if call.function.name != "get_weather":
        raise ValueError("Unexpected tool")
    args = json.loads(call.function.arguments)
    if not isinstance(args.get("city"), str):
        raise ValueError("Invalid city")
    # Replace this demo result with your weather service.
    result = {"city": args["city"], "temperature_c": 18, "demo": True}
    messages.append({"role": "tool", "tool_call_id": call.id,
                     "content": json.dumps(result)})
if message.tool_calls:
    final = client.chat.completions.create(model="YOUR_MODEL_ID", messages=messages,
                                          tools=tools, max_tokens=256)
    print(final.choices[0].message.content)
else:
    print(message.content)

Some models return a normal answer instead of tool calls. Handle both. Streamed tool names and arguments can be split across chunks: collect by tool-call index, wait for completion, then parse JSON and validate it. Never evaluate model-generated code.

JSON and schema output

response = client.chat.completions.create(
    model="YOUR_MODEL_ID",
    messages=[{"role":"user", "content":"Return a JSON object with a city field."}],
    response_format={"type":"json_object"},
    max_tokens=128,
)
value = json.loads(response.choices[0].message.content)
if not isinstance(value.get("city"), str):
    raise ValueError("Unexpected result schema")

response_format, strict schemas, tool choice, vision input, and reasoning controls are model-dependent. A forwarded parameter is not a gateway guarantee. Anthropic tools use the native tools, tool_use, and tool_result shapes instead of OpenAI tool messages.