How max_turns Controls Multi-Turn Tool Calling Loops in aisuite

The max_turns parameter caps the number of LLM-to-tool interaction cycles in aisuite, automatically terminating the loop and returning the final response when the limit is reached.

In the aisuite Python library, max_turns serves as the primary safety mechanism and control knob for autonomous agent behaviors. This parameter determines exactly how many back-and-forth exchanges can occur between the language model and registered tools before the client forcibly terminates the loop, preventing runaway execution while enabling complex multi-step reasoning.

How max_turns Works in Automatic Mode

When you instantiate ChatClient or AsyncChatClient with a max_turns value, aisuite enters automatic tool-execution mode. The client runs an internal loop that manages the entire conversation flow without manual intervention.

The LLM ↔ Tool Interaction Cycle

The automatic loop follows this exact sequence as implemented in aisuite/client.py:

  1. The client sends the current conversation history and tool schema to the LLM provider.
  2. If the LLM returns a tool call (function name and arguments), the client executes the registered function.
  3. The client constructs a tool-result message and feeds it back to the LLM.
  4. Steps 2-3 repeat until either the LLM stops requesting tools or the cycle count reaches max_turns.

Each complete LLM response followed by a tool execution constitutes one "turn" toward the limit.

Loop Termination Behavior

When the interaction counter equals the max_turns value, the client immediately exits the loop. It returns the last LLM message (the one that would have triggered the next tool invocation) without executing any further tools. This hard stop prevents infinite loops in scenarios where the model might otherwise keep requesting tools indefinitely.

Manual Mode: When max_turns Is Absent

Omitting max_turns switches the client to manual mode. In this configuration, as demonstrated in tests/client/test_manual_tool_calling.py, the client does not run the automatic loop. Instead, it forwards the tool schema to the provider exactly once and expects the caller—typically a UI or external orchestrator—to handle tool invocation manually via client.run_tool().

This distinction is deliberate: the absence of max_turns signals that the developer wants full control over the execution flow, while its presence delegates loop management to aisuite.

Validation Rules and Constraints

The client validates max_turns at construction time in aisuite/client.py. The parameter must be an integer greater than or equal to 2. If you provide a value less than 2 or a non-integer, the constructor raises a ValueError. This constraint ensures that every automatic loop includes at least one LLM turn and the possibility of one tool execution.

According to the test suite in tests/client/test_client.py (specifically around line 71), this validation is strictly enforced before any network requests occur.

Practical Configuration Examples

The following patterns demonstrate how to configure max_turns for different operational modes:

Automatic Mode with Safety Cap

Use this configuration for autonomous agents that need to perform research or multi-step data gathering without human intervention:

import aisuite

# Limit to 3 turns: LLM → tool → LLM → tool → final LLM

client = aisuite.ChatClient(
    model="openai:gpt-4o",
    tools=[get_weather, get_current_time],
    max_turns=3,
)

resp = client.chat.completions.create(
    messages=[{"role": "user", "content": "What's the weather and current time?"}]
)

# Returns after at most 3 complete cycles

Manual Mode for UI-Driven Workflows

Use this when your application handles tool execution separately:

import aisuite

# No max_turns → caller must invoke tools manually

client = aisuite.ChatClient(
    model="openai:gpt-4o",
    tools=[get_weather, get_current_time],
)

# Provider receives tool schemas, but execution is external

Implementation Details and Testing

The max_turns logic resides in the ChatClient and AsyncChatClient classes within aisuite/client.py. The implementation maintains an internal counter that increments with each completed tool execution, comparing it against the supplied threshold before initiating subsequent LLM requests.

The test coverage spans multiple files:

Documentation references in guides/openai.md and guides/anthropic.md also illustrate provider-specific usage patterns for this parameter.

Summary

  • max_turns acts as a hard ceiling on LLM-tool interaction cycles, preventing infinite loops while enabling autonomous agent workflows.
  • When provided, the client enters automatic mode, executing tools and feeding results back to the LLM until the limit is reached or the LLM stops calling tools.
  • When omitted, the client operates in manual mode, requiring external callers to handle tool invocation via the exposed API.
  • The parameter requires an integer ≥ 2, validated at construction time in aisuite/client.py.
  • End-to-end tests in the MCP test suite demonstrate practical usage with values ranging from 2 to 30 turns depending on workflow complexity.

Frequently Asked Questions

What happens when the max_turns limit is reached?

When the interaction counter reaches the specified max_turns value, the client returns the last LLM response without executing any pending tool calls. This final message represents the model's output at the moment the safety limit triggered, effectively capping the reasoning depth while preserving the conversation state.

Can max_turns be set to 1?

No. The aisuite client explicitly validates that max_turns must be an integer greater than or equal to 2. Attempting to set max_turns=1 raises a ValueError during client initialization, ensuring that every automatic loop supports at least one complete LLM-to-tool exchange cycle.

How does manual tool calling differ from automatic mode?

In manual mode (when max_turns is omitted), the ChatClient does not run the internal execution loop. It transmits the tool schema to the provider but returns control immediately after the LLM response, expecting the caller to parse any tool calls and invoke client.run_tool() externally. Automatic mode handles this parsing and execution internally, cycling until completion or until hitting the max_turns limit.

Where is max_turns validated in the codebase?

The validation occurs in the ChatClient constructor within aisuite/client.py. The implementation checks that the value is an integer ≥ 2, raising a ValueError otherwise. Unit tests in tests/client/test_client.py (around line 71) verify this behavior, while integration tests throughout the MCP test suite confirm the parameter's effect on loop termination.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →