> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbitrage.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool calling

> A complete agent loop with tools, routed and traced through Orbitrage.

Tools work exactly as in the OpenAI SDK — Orbitrage just routes and traces the
calls. Here's a full ReAct loop you can run as-is.

<CodeGroup>
  ```python Python theme={null}
  import os, json, orbitrage
  orbitrage.init(os.environ["ORBITRAGE_API_KEY"], user_id="customer_42")

  from openai import OpenAI
  client = OpenAI()

  tools = [{
      "type": "function",
      "function": {
          "name": "get_weather",
          "description": "Current weather for a city.",
          "parameters": {
              "type": "object",
              "properties": {"city": {"type": "string"}},
              "required": ["city"],
          },
      },
  }]

  def get_weather(city):           # your real implementation
      return f"22°C and sunny in {city}"

  messages = [{"role": "user", "content": "What's the weather in Tokyo?"}]

  while True:
      resp = client.chat.completions.create(model="minimax-m3", messages=messages, tools=tools)
      msg = resp.choices[0].message
      messages.append(msg)
      if not msg.tool_calls:
          print(msg.content)
          break
      for tc in msg.tool_calls:
          args = json.loads(tc.function.arguments)
          result = get_weather(**args)
          messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})
  ```

  ```typescript Node.js theme={null}
  import { orbitrage } from "orbitrage";
  await orbitrage.init({ apiKey: process.env.ORBITRAGE_API_KEY, userId: "customer_42" });

  import OpenAI from "openai";
  const client = new OpenAI();

  const tools = [{
    type: "function",
    function: {
      name: "get_weather",
      description: "Current weather for a city.",
      parameters: {
        type: "object",
        properties: { city: { type: "string" } },
        required: ["city"],
      },
    },
  }];

  const getWeather = (city) => `22°C and sunny in ${city}`;   // your real impl

  const messages = [{ role: "user", content: "What's the weather in Tokyo?" }];

  while (true) {
    const resp = await client.chat.completions.create({ model: "minimax-m3", messages, tools });
    const msg = resp.choices[0].message;
    messages.push(msg);
    if (!msg.tool_calls?.length) { console.log(msg.content); break; }
    for (const tc of msg.tool_calls) {
      const args = JSON.parse(tc.function.arguments);
      messages.push({ role: "tool", tool_call_id: tc.id, content: getWeather(args.city) });
    }
  }
  ```
</CodeGroup>

## Managed tools — let Orbitrage run them

Don't want to wire up a web-search or scrape API at all? Reference a managed tool
by name and Orbitrage runs it server-side with our key, loops the result back to
the model, and returns the final answer — no key, no tool loop on your side. Your
own tools (like `get_weather` above) keep running on your side, in the same call.

<CodeGroup>
  ```python Python theme={null}
  resp = client.chat.completions.create(
      model="minimax-m3",
      messages=[{"role": "user", "content": "Search the web: what's new with MCP? Cite a source."}],
      tools=["tavily_orbitrage"],     # managed — we run it. Just name it.
  )
  print(resp.choices[0].message.content)   # final answer, already includes the search
  ```

  ```typescript Node.js theme={null}
  const resp = await client.chat.completions.create({
    model: "minimax-m3",
    messages: [{ role: "user", content: "Search the web: what's new with MCP? Cite a source." }],
    tools: ["tavily_orbitrage"] as never,   // managed — we run it
  });
  console.log(resp.choices[0].message.content);
  ```
</CodeGroup>

## Mix managed + your own tools

Put both in one `tools` array. Orbitrage runs the managed ones server-side and
hands your own tool calls back to you — in the same request.

```python theme={null}
tools = [
    "calculator_orbitrage",                          # managed — we run it
    "tavily_orbitrage",                              # managed — we run it
    {"type": "function", "function": {               # yours — you run it
        "name": "get_order_status",
        "description": "Look up an order by id.",
        "parameters": {"type": "object",
                       "properties": {"id": {"type": "string"}}, "required": ["id"]}}},
]

messages = [{"role": "user", "content": "Compute 1234*5678, then check order A-1009."}]
while True:
    resp = client.chat.completions.create(model="minimax-m3", messages=messages, tools=tools)
    msg = resp.choices[0].message
    messages.append(msg)
    if not msg.tool_calls:               # managed calls already resolved server-side
        print(msg.content); break
    for tc in msg.tool_calls:            # only YOUR tools land here
        if tc.function.name == "get_order_status":
            messages.append({"role": "tool", "tool_call_id": tc.id, "content": "shipped"})
```

See [Tools Gateway](/concepts/tools-gateway) for the full catalog (web search,
scrape, calculator, datetime, weather, …) and billing. Managed tools also work
with pinned models including `glm-5.2`.

In the dashboard, the run shows each LLM hop, the tool definitions you sent, and
which tools the model called (managed tools are badged **via Orbitrage** with
their cost) — useful for spotting tool-selection drift.

<Tip>
  Routing scores prompts with tools too: a request that offers many tools tends
  to route to a tool-strong model. Pin a model if you need a specific one.
</Tip>
