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.

ChatRequestBuilder is a stateless, final utility class in the aisdk/openai-compatible package that converts a portable TextModelRequest object into a raw associative array ready to send as the JSON body of an OpenAI-compatible /chat/completions HTTP request. It is called by every provider adapter that speaks the OpenAI wire format — you will typically invoke it inside your own provider’s doChat() method rather than calling it directly from application code.

Namespace

AiSdk\OpenAICompatible\ChatRequestBuilder

Method: build()

public static function build(
    string $modelId,
    string $providerName,
    TextModelRequest $request,
    bool $stream
): array
Serializes every field of $request that has an OpenAI-compatible wire representation and returns the complete request body as a PHP array. The returned array can be passed directly to a JSON-encoding HTTP client.

Parameters

modelId
string
required
The model identifier that will be placed verbatim in the model field of the request body, e.g. "gpt-4o" or "o3".
providerName
string
required
The provider namespace string, e.g. "openai". Used exclusively to read $request->providerOptionsFor($providerName)['raw'] when merging raw override keys. It must match the key your application uses when constructing providerOptions.
request
TextModelRequest
required
The portable, provider-agnostic request object. ChatRequestBuilder reads the following properties:
PropertyWire field
$request->messagesmessages (via ChatMessageConverter)
$request->systemPrepended as a system role message
$request->temperaturetemperature
$request->maxTokensmax_tokens
$request->topPtop_p (omitted when null)
$request->toolstools + tool_choice (omitted when empty)
$request->toolChoicetool_choice
$request->outputresponse_format (see below)
$request->reasoningreasoning_effort (see below)
$request->providerOptionsFor($providerName)['raw']merged last via array_replace()
stream
bool
required
When true, the body will include "stream": true and "stream_options": {"include_usage": true}. When false, "stream": false is set and stream_options is omitted entirely.

Return value

Returns array<string, mixed>. The shape of the returned array depends on which optional features are active:
// Guaranteed keys — always present
[
    'model'       => 'gpt-4o',
    'messages'    => [/* converted message objects */],
    'temperature' => 0.7,
    'stream'      => false,
    'max_tokens'  => 1024,

    // Conditional — present only when the corresponding feature is used
    'top_p'             => 0.9,           // when $request->topP !== null
    'tools'             => [/* … */],     // when $request->tools !== []
    'tool_choice'       => 'auto',        // when $request->tools !== []
    'response_format'   => [/* … */],     // when $request->output is set
    'reasoning_effort'  => 'high',        // when Reasoning::effort() is used
    'stream_options'    => ['include_usage' => true], // when $stream === true
]

Usage example

use AiSdk\Message;
use AiSdk\OpenAICompatible\ChatRequestBuilder;
use AiSdk\Requests\TextModelRequest;

$request = new TextModelRequest(
    messages: [Message::user('What is the capital of France?')],
    system: 'Answer concisely.',
    temperature: 0.3,
    maxTokens: 256,
);

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

// $body is ready to JSON-encode and POST to /chat/completions

response_format logic

The response_format key is set only when $request->output is an Output instance. Two cases are handled: Structured output with a named schema — when $request->output->kind === Output::KIND_OBJECT and $request->output->schema is an AiSdk\Schema instance, the builder emits the json_schema variant with strict: true:
'response_format' => [
    'type' => 'json_schema',
    'json_schema' => [
        'name'   => 'response',       // or $output->schema->name() when set
        'strict' => true,
        'schema' => $output->schema->jsonSchema(),
    ],
]
Generic JSON object — when $request->output->kind === Output::KIND_OBJECT but no AiSdk\Schema is attached, the builder emits the simpler variant:
'response_format' => ['type' => 'json_object']
Any other Output::kind value results in response_format being omitted entirely.

Reasoning behavior

OpenAI-compatible adapters support a single reasoning path: normalized effort levels.
Token budget reasoning (Reasoning::budget(int $tokens)) is not supported and will always throw. Use Reasoning::effort() or the raw escape hatch instead.
Reasoning factoryBehaviour
Reasoning::effort('low' | 'medium' | 'high')Sets reasoning_effort in the request body
Reasoning::budget(int $tokens)Throws AiSdk\Exceptions\InvalidArgumentException
use AiSdk\OpenAICompatible\ChatRequestBuilder;
use AiSdk\Reasoning;

$body = ChatRequestBuilder::build(
    modelId: 'o3',
    providerName: 'openai',
    request: new TextModelRequest(
        messages: [Message::user('Solve this step by step.')],
        reasoning: Reasoning::effort('high'),
    ),
    stream: false,
);

// $body['reasoning_effort'] === 'high'

Raw escape hatch

When you need to send a field that the portable API does not yet model — such as seed, a custom logit_bias, or a preview parameter — you can pass it through providerOptions[$providerName]['raw']. This array is merged into the body last using array_replace(), so it can overwrite any key the builder already set.
use AiSdk\OpenAICompatible\ChatRequestBuilder;

$body = ChatRequestBuilder::build(
    modelId: 'gpt-4o',
    providerName: 'openai',
    request: new TextModelRequest(
        messages: [Message::user('Hi')],
        providerOptions: [
            'openai' => [
                'raw' => [
                    'temperature' => 0.1,   // overrides $request->temperature
                    'seed'        => 42,    // new key not in the portable API
                ],
            ],
        ],
    ),
    stream: false,
);

// $body['temperature'] === 0.1
// $body['seed']        === 42
Keys supplied via raw bypass all validation. If you override a key like model or messages here, the builder will honour it without warning.

Build docs developers (and LLMs) love