How aisuite Handles Message Format Conversion Between Diverse LLM Providers

aisuite normalizes message format conversion by using a unified internal Message model and provider-specific converter classes that translate requests and responses between OpenAI-compatible schemas and vendor-specific formats like Anthropic's content blocks or Google's Vertex AI Content objects.

aisuite is an open-source Python library developed by Andrew Ng's team to simplify integration with multiple large language model providers. The framework's architecture centers on message format conversion logic that abstracts away API differences, allowing developers to use a single internal representation while the library handles provider-specific payload transformations.

Message Format Conversion Architecture

The foundation of aisuite's interoperability lies in its two-layer conversion system defined in aisuite.framework.message.Message and implemented across provider-specific modules.

The Base Converter Pattern

At the core is OpenAICompliantMessageConverter located in aisuite/providers/message_converter.py. This base class provides OpenAI-compatible defaults for tool-result handling, usage extraction, and response shaping. Each provider extends this base:

Provider classes like OpenaiProvider or AnthropicProvider instantiate these converters and delegate all message-format work to them:


# Simplified flow from OpenaiProvider

transformed = self.transformer.convert_request(messages)
response = self.client.chat.completions.create(model=model, messages=transformed, **kwargs)

Request-Side Conversion

When sending messages to an LLM, convert_request performs two critical transformations: normalization and role mapping.

Message Normalization and Role Mapping

The conversion pipeline first ensures every message element is a plain dictionary via model_dump(mode="json"). Then it maps roles according to provider requirements:

  • OpenAI-compatible providers (OpenAI, Groq): Preserve native roles (user, assistant, tool)
  • Anthropic: Maps user → user, assistant → assistant, but converts tool role to user with a special "tool_result" content block
  • Google Vertex AI: Constructs Content objects with role="user" or role="model", creating Part objects with function_call fields for tool interactions

Tool-Result Handling

For tool results (role=tool), the base OpenAICompliantMessageConverter supports tool_results_as_strings=True to coerce content to strings. Provider-specific converters like Anthropic's embed these in vendor-specific structures such as "tool_result" blocks, while Google wraps them in function_call parts.

Response-Side Normalization

Each converter implements convert_response to transform vendor-specific outputs into aisuite's unified ChatCompletionResponse.

Building the Standardized Response

The method creates a fresh ChatCompletionResponse matching OpenAI's schema structure:

  1. Copies assistant text into choices[0].message.content
  2. Detects tool calls and builds ChatCompletionMessageToolCall objects
  3. Extracts token usage into CompletionUsage objects

Provider-specific detection logic varies:

  • OpenAI: Passes through unchanged (native format)
  • Anthropic: Inspects content blocks for tool_use types and constructs OpenAI-style tool call dictionaries
  • Google: Examines the first Part of the candidate for function_call presence

Streaming Support

For streaming implementations, converters provide convert_stream_event methods that translate vendor-specific streaming events into ChatCompletionChunk objects with unified ChoiceDelta structures. This allows downstream code to consume streaming tokens, tool-call deltas, and partial transcriptions without provider-specific parsing logic.

Practical Usage Example

The following demonstrates how the same internal Message list works across different providers without format modification:

from aisuite import aisuite
from aisuite.providers import AnthropicProvider, OpenaiProvider

# Build chat history using the internal Message model

messages = [
    aisuite.framework.message.Message(role="user", content="What's the weather in Paris?"),
]

# Anthropic provider auto-converts the request

anthropic = AnthropicProvider(api_key="…")
anthropic_response = anthropic.chat_completions_create(
    model="claude-3-5-sonnet-20240620", messages=messages
)
print(anthropic_response.choices[0].message.content)

# Switch to OpenAI with identical message format

openai = OpenaiProvider(api_key="…")
openai_response = openai.chat_completions_create(
    model="gpt-4o-mini", messages=messages
)
print(openai_response.choices[0].message.content)

Key Implementation Files

The message format conversion system spans these critical files in the andrewyng/aisuite repository:

Summary

  • aisuite uses a unified internal Message model to abstract provider differences
  • Provider-specific converters (e.g., AnthropicMessageConverter, GoogleMessageConverter) handle translation in aisuite/providers/
  • Request conversion includes role mapping (Anthropic tool→user, Google Content objects) and message normalization via convert_request
  • Response normalization produces OpenAI-compatible ChatCompletionResponse objects regardless of source API
  • Streaming support is achieved through convert_stream_event methods that yield standardized ChatCompletionChunk objects

Frequently Asked Questions

How does aisuite handle different role naming conventions between providers?

aisuite's converter classes map internal roles to provider-specific strings during request transformation. For example, AnthropicMessageConverter maps the internal tool role to user with a "tool_result" content type, while GoogleMessageConverter translates roles to "user" or "model" for Vertex AI. OpenAI-compatible providers use the internal roles directly without modification.

Can I use the same message history with multiple providers in aisuite?

Yes. By instantiating different providers (e.g., AnthropicProvider, OpenaiProvider) with the same messages list built from aisuite.framework.message.Message objects, aisuite automatically handles the format conversion. Each provider's convert_request method transforms the unified model into the vendor-specific payload required by that API.

What happens to tool calls in responses from non-OpenAI providers?

Converters inspect provider-specific response structures and normalize them into OpenAI-compatible ChatCompletionMessageToolCall objects. Anthropic's converter checks for tool_use content blocks, while Google's converter looks for function_call in response parts. Both return a standardized ChatCompletionResponse with tool_calls populated in the expected format.

How does aisuite manage streaming responses across different APIs?

Each provider implements a convert_stream_event method that translates vendor-specific streaming events (SSE chunks, Vertex AI streaming objects, etc.) into ChatCompletionChunk objects containing ChoiceDelta data. This ensures that consuming code can process streaming tokens, tool-call deltas, and completion events through a single interface regardless of the underlying LLM provider.

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 →