How to Format Messages for aisuite Chat Completions API

The aisuite Chat Completions API accepts a list of Message objects (or equivalent dictionaries) containing role, content, and optional tool call fields, which the library converts to provider-specific formats via OpenAICompliantMessageConverter.

Formatting messages correctly for the andrewyng/aisuite library ensures seamless interoperability across multiple LLM providers. The Chat Completions endpoint expects structured conversation data that mirrors the OpenAI Chat API specification, using specific data models defined in the framework to represent chat turns, tool calls, and function parameters.

Core Data Models

The Message Class

At the heart of aisuite's message formatting is the Message class located in aisuite/framework/message.py. This model represents a single turn in the conversation and contains the following fields:

  • role: Required. Must be one of "system", "user", "assistant", or "tool"
  • content: Required. The text content of the message
  • tool_calls: Optional. A list of ChatCompletionMessageToolCall objects when the assistant invokes functions
  • reasoning_content: Optional. Stores internal reasoning or "thinking" content that can be displayed separately from the main response
  • refusal: Optional. Indicates if the model refused to generate content

Tool Call and Function Models

For function calling workflows, aisuite uses two additional models defined in the same file:

  • ChatCompletionMessageToolCall: Represents a specific tool invocation with fields for id, type (always "function"), and function
  • Function: Contains the name of the function being called and its arguments as a JSON-serializable string

Message Format Rules

When assembling your conversation history, adhere to these structural requirements:

  1. Chronological ordering – Messages must appear in sequence, starting with the system prompt (if used), followed by alternating user and assistant turns
  2. Role restrictions – Only four roles are supported: "system", "user", "assistant", and "tool"
  3. Tool call syntax – Assistant messages that trigger functions must include a tool_calls list containing ChatCompletionMessageToolCall objects
  4. Tool result formatting – Responses from executed functions must be sent back as messages with role="tool" and content containing the stringified result
  5. Reasoning handling – Place internal reasoning chains in reasoning_content; the converter automatically strips the refusal field when processing requests

How Message Conversion Works

Before sending to the underlying provider, Chat.completions.create() forwards your message list to OpenAICompliantMessageConverter.convert_request in aisuite/providers/message_converter.py. This converter transforms Message objects (or plain dictionaries) into the exact JSON structure required by the specific LLM provider, handling role mappings and parameter normalization automatically.

Code Examples

Basic User-Assistant Exchange

from aisuite.framework.message import Message

messages = [
    Message(role="system", content="You are a helpful assistant."),
    Message(role="user", content="What is the capital of France?"),
]

response = client.chat.completions.create(
    model="openai:gpt-4o-mini",
    messages=messages,
)
print(response.choices[0].message.content)   # → Paris

Function Calling Workflow

from aisuite.framework.message import Message, ChatCompletionMessageToolCall, Function

# 1️⃣ Assistant asks the model to run a tool

assistant_msg = Message(
    role="assistant",
    content="",
    tool_calls=[
        ChatCompletionMessageToolCall(
            id="tool-1",
            function=Function(name="search_web", arguments='{"query":"latest AI news"}')
        )
    ],
)

messages = [
    Message(role="system", content="You can search the web for up‑to‑date info."),
    Message(role="user", content="Give me the latest AI headlines."),
    assistant_msg,
]

# 2️⃣ Send to the model – provider will see the tool spec in the payload

response = client.chat.completions.create(
    model="openai:gpt-4o-mini",
    messages=messages,
    tools=[  # declare the available tool for the provider

        {
            "type": "function",
            "function": {
                "name": "search_web",
                "description": "Search the web and return results.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {"type": "string", "description": "Search query"}
                    },
                    "required": ["query"]
                },
            },
        }
    ],
)

# 3️⃣ The provider returns a tool call – extract it:

tool_call = response.choices[0].message.tool_calls[0]

# Execute the real tool (here we just mock a result)

tool_result = {"results": ["AI‑generated art beats humans", "New GPT‑5 rumors"]}

# 4️⃣ Feed the tool result back as a new message

tool_msg = Message(
    role="tool",
    content=str(tool_result),   # content must be JSON‑serialisable string

    tool_calls=[tool_call]      # keep the original call id for correlation

)

messages.append(tool_msg)

# 5️⃣ Continue the conversation

final = client.chat.completions.create(
    model="openai:gpt-4o-mini",
    messages=messages,
)
print(final.choices[0].message.content)   # assistant now replies using the web data

Handling Reasoning Content

msg = Message(
    role="assistant",
    content="The current year is 2026.",
    reasoning_content="I need to check my knowledge cutoff and calculate from there."
)

Summary

  • Use the Message class from aisuite/framework/message.py to ensure type safety and proper structure
  • Supported roles are strictly limited to "system", "user", "assistant", and "tool"
  • Tool calls require ChatCompletionMessageToolCall and Function objects with proper ID correlation
  • Plain dictionaries work interchangeably with Message objects due to the OpenAICompliantMessageConverter
  • Tool results must be sent back with role="tool" and stringified content to continue the function calling loop

Frequently Asked Questions

What roles are supported in aisuite Message objects?

The role field accepts four values: "system" for initial instructions, "user" for human inputs, "assistant" for model responses, and "tool" for function execution results. These align with the OpenAI Chat API specification and are validated during conversion.

Can I use plain Python dictionaries instead of Message objects?

Yes. The OpenAICompliantMessageConverter.convert_request method in aisuite/providers/message_converter.py handles both Message instances and raw dictionaries, converting them to the provider's required JSON format before transmission.

How do I handle tool results in the conversation flow?

After executing a function, append a new Message with role="tool" to your messages list. Set content to the stringified JSON result and include the original tool_calls data to maintain correlation with the assistant's request, as shown in the function calling example.

Where does aisuite handle message format conversion?

Conversion logic resides in aisuite/providers/message_converter.py, specifically within the OpenAICompliantMessageConverter class. This utility strips unsupported fields like refusal and optionally converts tool results to strings when tool_results_as_strings is enabled, ensuring compatibility across different LLM providers.

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 →