Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/phpaisdk/openai-compatible/llms.txt

Use this file to discover all available pages before exploring further.

ChatMessageConverter transforms portable AiSdk\Message objects — including plain text, tool result, assistant tool-call, and multimodal messages — into the messages array required by the OpenAI /chat/completions endpoint. It is called internally by ChatRequestBuilder::build() and never needs to be instantiated directly; all methods are static.

Namespace

AiSdk\OpenAICompatible\Converters\ChatMessageConverter

Methods

convert()

public static function convert(array $messages, ?string $system = null): array<int, array<string, mixed>>
Converts an array of Message objects into the OpenAI wire-format messages array. When $system is provided and non-empty, a {"role": "system", "content": "..."} entry is prepended as the first element.
messages
array<int, Message>
required
An ordered array of AiSdk\Message objects representing the conversation history. At least one message is required by the API.
system
string|null
An optional system prompt. When non-null and non-empty, it is prepended to the output as {"role": "system", "content": $system} before all other messages.
Returns array<int, array<string, mixed>> — the fully serialized messages array ready for inclusion in a /chat/completions request body.

Role handling

Tool messages (Message::ROLE_TOOL)

Tool result messages are serialized with the tool role and carry the call identifier and function name alongside the plain-text output:
{
  "role": "tool",
  "tool_call_id": "<id>",
  "name": "<function name>",
  "content": "<text output>"
}

Assistant messages with tool calls

When an assistant Message carries one or more ToolCall objects, the converter emits an assistant entry that includes a tool_calls array. The content field is null when the assistant produced no accompanying text:
{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    {
      "id": "call_1",
      "type": "function",
      "function": {
        "name": "weather",
        "arguments": "{\"city\":\"Lahore\"}"
      }
    }
  ]
}
arguments is always JSON-encoded from the ToolCall::$arguments array.

All other messages (single-text shorthand)

For user, assistant, and other roles whose content resolves to a single text part, the converter applies a flattening shorthand — rather than wrapping the content in a one-element array, it inlines the string directly:
{ "role": "user", "content": "Hi" }
When a message contains multiple content parts (or a non-text single part), the full parts array is used instead:
{
  "role": "user",
  "content": [
    { "type": "text", "text": "Describe these inputs." },
    { "type": "image_url", "image_url": { "url": "https://example.com/photo.png" } }
  ]
}

Content type mappings

Each AiSdk\Content part on a message is converted to an OpenAI content-part object according to its type:
Content typeOpenAI part structure
Content::TYPE_TEXT{"type": "text", "text": "<string>"}
Content::TYPE_IMAGE (URL source){"type": "image_url", "image_url": {"url": "<url>"}}
Content::TYPE_IMAGE (base64 source){"type": "image_url", "image_url": {"url": "<data URI>"}}
Content::TYPE_AUDIO{"type": "input_audio", "input_audio": {"data": "<base64>", "format": "mp3|wav|ogg"}}
Content::TYPE_FILE{"type": "file", "file": {"filename": "<name>", "file_data": "<url or data URI>"}}
Passing a Content type not listed above (e.g. a custom type) throws AiSdk\Exceptions\InvalidArgumentException with a message identifying the unsupported type.

Audio format mapping

The format field of input_audio parts is derived from the content’s MIME type:
MIME typeformat value
audio/mpeg, audio/mp3mp3
audio/wav, audio/x-wavwav
audio/oggogg
anything else / nullwav (default)

Example

The following example — drawn directly from the test suite — demonstrates all four multimodal content types in a single user message:
use AiSdk\Content;
use AiSdk\InputEncoding;
use AiSdk\Message;
use AiSdk\OpenAICompatible\ChatRequestBuilder;
use AiSdk\Requests\TextModelRequest;

$request = new TextModelRequest(
    messages: [
        Message::user([
            Content::text('Describe these inputs.'),
            Content::image('https://example.com/photo.png'),
            Content::audio('UklGRg==', mimeType: 'audio/wav', encoding: InputEncoding::Base64),
            Content::file('JVBERi0=', mimeType: 'application/pdf', filename: 'report.pdf', encoding: InputEncoding::Base64),
        ]),
    ],
);

$body = ChatRequestBuilder::build('gpt-4o-audio-preview', 'openai', $request, false);

// $body['messages'][0]['content'] =>
// [
//   ['type' => 'text',        'text'        => 'Describe these inputs.'],
//   ['type' => 'image_url',   'image_url'   => ['url' => 'https://example.com/photo.png']],
//   ['type' => 'input_audio', 'input_audio' => ['data' => 'UklGRg==', 'format' => 'wav']],
//   ['type' => 'file',        'file'        => ['filename' => 'report.pdf', 'file_data' => 'data:application/pdf;base64,JVBERi0=']],
// ]
Tool result and assistant tool-call round-trip:
use AiSdk\Message;
use AiSdk\ToolCall;
use AiSdk\OpenAICompatible\ChatRequestBuilder;
use AiSdk\Requests\TextModelRequest;

$request = new TextModelRequest(
    messages: [
        // Assistant emits a tool call
        Message::assistant('', toolCalls: [
            new ToolCall('call_1', 'weather', ['city' => 'Lahore']),
        ]),
        // Tool result returned to the model
        Message::tool(toolCallId: 'call_1', output: 'Sunny', name: 'weather'),
    ],
);

$body = ChatRequestBuilder::build('gpt-4o', 'openai', $request, false);

// $body['messages'][0] =>
// [
//   'role'       => 'assistant',
//   'content'    => null,
//   'tool_calls' => [
//     ['id' => 'call_1', 'type' => 'function', 'function' => ['name' => 'weather', 'arguments' => '{"city":"Lahore"}']],
//   ],
// ]

// $body['messages'][1] =>
// [
//   'role'         => 'tool',
//   'tool_call_id' => 'call_1',
//   'name'         => 'weather',
//   'content'      => 'Sunny',
// ]

Build docs developers (and LLMs) love