# How to Get Tool Invocation Requests from an aisuite Response

> Learn how to get tool invocation requests from an aisuite response. Extract tool call names and arguments or use max_turns for automatic resolution.

- Repository: [Andrew Ng/aisuite](https://github.com/andrewyng/aisuite)
- Tags: how-to-guide
- Published: 2026-06-16

---

**aisuite exposes tool invocation requests through the `tool_calls` attribute on `response.choices[0].message`, which contains a list of `ToolCall` objects with `name` and `arguments` fields that you can extract manually or let the client resolve automatically via the `max_turns` parameter.**

When building agentic workflows with **andrewyng/aisuite**, you need to intercept **tool invocation requests** that the model generates before they execute. The library surfaces these requests from the underlying provider's response in a unified format, letting you handle them manually or delegate execution to the built-in multi-turn handler.

## Where Tool Calls Live in the Response Object

The aisuite client extracts raw tool-call information exactly as the underlying provider supplies it. When a model emits a function call, the provider places a list of `ToolCall` objects on the message located at `response.choices[0].message`.

In [`aisuite/client.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/client.py) (lines 309–317), the completion flow handles the response by exposing the `tool_calls` attribute on the assistant message. This attribute is not added by aisuite itself but is surfaced directly from the provider's response structure, ensuring compatibility with OpenAI, Anthropic, Google, and other supported backends.

## Extracting Tool Invocation Requests Manually

To inspect tool calls without automatic execution, omit the `max_turns` parameter when calling `client.chat.completions.create()`. After the call returns, access the tool invocation requests using `getattr()` with a safe fallback:

```python
from aisuite.client import Client
from aisuite.utils.tools import Tools

client = Client(provider_configs={"openai": {"api_key": "YOUR_KEY"}})

def get_weather(location: str) -> str:
    return f"The weather in {location} is sunny."

tools = Tools([get_weather])

response = client.chat.completions.create(
    model="openai:gpt-4o-mini",
    messages=[{"role": "user", "content": "What's the weather in Paris?"}],
    tools=tools,
    max_turns=None,  # Disable automatic execution

)

tool_calls = getattr(response.choices[0].message, "tool_calls", None)

if tool_calls:
    for call in tool_calls:
        print(f"Tool name: {call.name}")
        print(f"Arguments: {call.arguments}")  # JSON string

        print(f"Call ID: {getattr(call, 'id', None)}")

```

Each `ToolCall` object contains:
- **`name`**: The function name to invoke
- **`arguments`**: A JSON string containing the parameters
- **`id`**: An optional identifier for tracking the call

## Automatic Multi-Turn Handling

If you enable `max_turns` (for example, `max_turns=5`), the client enters a loop that automatically executes tools and returns the final response after resolution. In this mode, `response.choices[0].message.tool_calls` will be empty because all invocations have been processed.

However, you can still inspect the intermediate tool calls. According to the implementation in [`aisuite/client.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/client.py) (lines 319–327), the client stores intermediate responses in `response.intermediate_responses` and the final message list in `response.choices[0].intermediate_messages`:

```python
response = client.chat.completions.create(
    model="openai:gpt-4o-mini",
    messages=[{"role": "user", "content": "What's the weather in Paris?"}],
    tools=tools,
    max_turns=3,  # Allow up to 3 tool turns

)

# Final answer after automatic execution

print(response.choices[0].message.content)

# Inspect the tool calls that were automatically handled

for intermediate in response.intermediate_responses:
    calls = getattr(intermediate.choices[0].message, "tool_calls", [])
    for call in calls:
        print(f"Executed: {call.name} with {call.arguments}")

```

## Complete Working Example

This example demonstrates both manual extraction and the `Tools` helper registration:

```python
from aisuite.client import Client
from aisuite.utils.tools import Tools

# Initialize client

client = Client(provider_configs={"openai": {"api_key": "YOUR_KEY"}})

# Define tool schema

def calculate_sum(a: int, b: int) -> int:
    """Add two numbers together."""
    return a + b

tools = Tools([calculate_sum])

# Manual mode - inspect tool invocation requests

response = client.chat.completions.create(
    model="openai:gpt-4o-mini",
    messages=[{"role": "user", "content": "Calculate 5 plus 3"}],
    tools=tools,
)

message = response.choices[0].message
tool_calls = getattr(message, "tool_calls", None)

if tool_calls:
    for tc in tool_calls:
        print(f"Requested: {tc.name}")
        print(f"Params: {tc.arguments}")

```

The `Tools` helper (located in [`aisuite/utils/tools.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/utils/tools.py)) automatically converts Python callables into OpenAI-compatible function schemas, which the client injects into the request when you pass the `tools` parameter.

## Summary

- **aisuite surfaces tool calls** through `response.choices[0].message.tool_calls` as a list of `ToolCall` objects.
- **Manual extraction** requires using `getattr(message, "tool_calls", None)` to safely access the attribute, which contains `name`, `arguments`, and optional `id` fields.
- **Automatic execution** via `max_turns` resolves tool calls internally, leaving the final response with empty `tool_calls` but preserving history in `intermediate_responses`.
- **Provider agnostic**: The extraction pattern works across all supported providers (OpenAI, Anthropic, Google, Ollama) because aisuite normalizes the response structure in [`aisuite/client.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/client.py).

## Frequently Asked Questions

### How do I check if a response contains tool calls before accessing them?

Use `getattr(response.choices[0].message, "tool_calls", None)` rather than direct attribute access. This pattern safely returns `None` if the model did not generate any tool invocation requests, preventing AttributeError exceptions when the attribute is missing.

### What is the difference between using max_turns and manual tool handling?

When you set `max_turns` to a positive integer (like `max_turns=5`), the client automatically executes tool calls and feeds results back to the model until reaching the limit or completion. When `max_turns` is `None`, the client returns immediately after the first model response, allowing you to inspect `tool_calls` and handle execution manually.

### Where are intermediate tool calls stored when using max_turns?

According to the source code in [`aisuite/client.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/client.py) (lines 319–327), intermediate tool invocations are stored in `response.intermediate_responses`, while the accumulated message history is available in `response.choices[0].intermediate_messages`. This allows you to audit the full conversation flow even after the client resolves all tool calls.

### Does aisuite modify the tool call format from the provider?

No, aisuite preserves the raw tool-call information exactly as the underlying provider supplies it. The `ToolCall` objects appear on the message with the same structure OpenAI uses (`name`, `arguments` as JSON string, and `id`), ensuring compatibility across different model providers in the unified response format.