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:
- Anthropic:
AnthropicMessageConverterinaisuite/providers/anthropic_provider.py - Google:
GoogleMessageConverterinaisuite/providers/google_provider.py - Groq:
GroqMessageConverterinaisuite/providers/groq_provider.py(inherits base directly)
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 convertstoolrole touserwith a special"tool_result"content block - Google Vertex AI: Constructs
Contentobjects withrole="user"orrole="model", creatingPartobjects withfunction_callfields 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:
- Copies assistant text into
choices[0].message.content - Detects tool calls and builds
ChatCompletionMessageToolCallobjects - Extracts token usage into
CompletionUsageobjects
Provider-specific detection logic varies:
- OpenAI: Passes through unchanged (native format)
- Anthropic: Inspects content blocks for
tool_usetypes and constructs OpenAI-style tool call dictionaries - Google: Examines the first
Partof the candidate forfunction_callpresence
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:
aisuite/providers/message_converter.py— BaseOpenAICompliantMessageConverterclassaisuite/providers/anthropic_provider.py—AnthropicMessageConverterimplementationaisuite/providers/google_provider.py—GoogleMessageConverterwith Vertex AI supportaisuite/providers/groq_provider.py—GroqMessageConverter(OpenAI-compliant inheritance)aisuite/providers/openai_provider.py— Direct base converter usageaisuite/framework/message.py— InternalMessagemodel definitionaisuite/framework/chat_completion_response.py— Unified response schema
Summary
- aisuite uses a unified internal
Messagemodel to abstract provider differences - Provider-specific converters (e.g.,
AnthropicMessageConverter,GoogleMessageConverter) handle translation inaisuite/providers/ - Request conversion includes role mapping (Anthropic tool→user, Google Content objects) and message normalization via
convert_request - Response normalization produces OpenAI-compatible
ChatCompletionResponseobjects regardless of source API - Streaming support is achieved through
convert_stream_eventmethods that yield standardizedChatCompletionChunkobjects
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →