How to Use intermediate_messages for Conversation Continuity in aisuite

aisuite automatically accumulates every model and tool message during multi-turn tool execution in the intermediate_messages attribute, allowing you to prepend this history to subsequent requests to maintain conversation state without manual message reconstruction.

The aisuite library from the andrewyng/aisuite repository provides a unified interface for multiple AI providers. When using tool-calling capabilities with the max_turns parameter, the library tracks the complete dialogue history in intermediate_messages, enabling seamless conversation continuity across discrete API calls.

How intermediate_messages Works Internally

The conversation continuity mechanism is implemented in the tool execution loop within aisuite/client.py. When you invoke client.chat.completions.create() with max_turns greater than zero, the Completions._tool_runner method orchestrates the interaction between the model and any provided tools.

The Tool Execution Loop

During each turn of a multi-turn interaction, the _tool_runner method performs the following sequence:

  1. Collects messages – Each turn appends the model's reply to intermediate_messages and, after a tool call, also appends the tool-generated messages.
  2. Snapshots responses – Raw response objects from each turn are accumulated in intermediate_responses.
  3. Attaches history – Before returning the final result, the method populates the response object with the full interaction history:
response.intermediate_responses = intermediate_responses[:-1]   # all but the final turn

response.choices[0].intermediate_messages = intermediate_messages

These two attributes constitute part of the public API and are documented in the quick-start guide at docs/agents-quickstart.md.

What Gets Stored

The intermediate_messages list contains the complete dialogue sequence including:

  • The original user prompt
  • Model-generated assistant messages
  • Tool call requests from the model
  • Tool result messages returned from your functions

Because this collection already contains the complete context, you can resume conversations by simply passing it back to the messages parameter in subsequent calls.

Continuing Conversations with intermediate_messages

To maintain state across multiple API calls, capture the intermediate_messages from the first response and prepend it to new messages in your follow-up request.

Step-by-Step Implementation

from aisuite import Client

# Initialize the client with provider configuration

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

# ----------------------------------------------------------------------

# First request – enable tool execution with max_turns

# ----------------------------------------------------------------------

first_response = client.chat.completions.create(
    model="openai:gpt-4o-mini",
    messages=[{"role": "user", "content": "List the files in the current folder"}],
    max_turns=3,           # Allow the model to call tools up to 3 times

    tools=[my_list_files_tool],
)

# Access the accumulated history

final_answer = first_response.choices[0].message.content
conversation_history = first_response.choices[0].intermediate_messages
print(f"Turns captured: {len(conversation_history)}")

# ----------------------------------------------------------------------

# Continue the conversation – reuse the stored intermediate messages

# ----------------------------------------------------------------------

next_message = {
    "role": "user",
    "content": "Now read the first file you just listed"
}

second_response = client.chat.completions.create(
    model="openai:gpt-4o-mini",
    # Prepend the historic messages so the model sees the full context

    messages=conversation_history + [next_message],
    max_turns=3,
    tools=[my_read_file_tool],
)

print(second_response.choices[0].message.content)  # Answer referencing earlier file list

Checkpointing for Later Resumption

The intermediate_messages list can be serialized and stored (for example, in a database) to resume conversations later:

import json

# Save state

stored_conversation = json.dumps(first_response.choices[0].intermediate_messages)

# Later or in another process: restore and continue

restored_messages = json.loads(stored_conversation)
continuation = client.chat.completions.create(
    model="openai:gpt-4o-mini",
    messages=restored_messages + [{"role": "user", "content": "Summarize what we did"}],
    max_turns=3,
    tools=[my_list_files_tool]
)

Key Implementation Details

The tests/client/test_client.py file contains validation logic confirming that intermediate_messages contains the expected length and content after tool execution. According to the source code in aisuite/client.py (lines 274-351), the _tool_runner method builds the message list incrementally during each turn, ensuring that tool results are automatically formatted according to the provider's expected schema.

When comparing conversation management approaches, using intermediate_messages eliminates the need to manually reconstruct message threads from scratch, while rebuilding from memory requires tracking tool_call_ids and result dictionaries separately.

Summary

  • intermediate_messages captures the complete dialogue including user prompts, model replies, and tool interactions during multi-turn execution.
  • State preservation is achieved by prepending the saved list to the messages parameter in subsequent create() calls.
  • Checkpointing allows you to serialize conversation state and resume later without losing tool execution context.
  • Implementation resides in aisuite/client.py within the Completions._tool_runner method, validated by the test suite at tests/client/test_client.py.

Frequently Asked Questions

What is the difference between intermediate_messages and intermediate_responses?

intermediate_messages contains the formatted message dictionaries (role, content, tool_calls) suitable for sending back to the model, while intermediate_responses contains the raw response objects from the provider. The messages are what you pass to subsequent API calls; the responses contain metadata like token usage and finish reasons from intermediate turns.

Do I need to manually format tool results when using intermediate_messages?

No. When you use max_turns with tool execution, aisuite automatically formats tool results and appends them to intermediate_messages in the correct schema for your provider. You simply pass the entire intermediate_messages list back to the messages parameter without modification.

Can intermediate_messages be used across different providers?

While intermediate_messages maintains a consistent format within aisuite, switching providers between turns is not recommended because different providers may have incompatible tool call schemas. For best results, continue conversations using the same provider that generated the original intermediate_messages.

How does max_turns affect intermediate_messages population?

The max_turns parameter limits how many tool-execution iterations the _tool_runner performs. Each iteration appends to intermediate_messages, so a higher max_turns value results in a longer message list. If max_turns is not set or is zero, no tool execution occurs and intermediate_messages will only contain the initial user message and final response.

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 →