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.

This guide walks you through building a minimal PHP AI SDK provider that uses aisdk/openai-compatible as its wire-format layer. By the end you will have a working model class that can send a chat request, parse a non-streaming response, consume an SSE stream, and generate images — all by delegating the wire format to this package and focusing your provider code only on authentication, endpoint paths, and model catalogs.
Once you have the basics working, see the Building a Provider guide for a full walkthrough including error handling, model catalogs, and publishing your provider package.

Chat Completions

1
Install the packages
2
Add both aisdk/core (portable contracts) and aisdk/openai-compatible (wire adapters) to your provider’s dependencies:
3
composer require aisdk/core aisdk/openai-compatible
4
Your provider’s composer.json should then include:
5
{
    "require": {
        "php": "^8.3",
        "aisdk/core": "^0.2",
        "aisdk/openai-compatible": "*"
    }
}
6
Build the chat request body
7
Use ChatRequestBuilder::build() to convert a portable TextModelRequest into the JSON body for /chat/completions. Pass the model ID, your provider name, the request object, and whether you want streaming:
8
use AiSdk\OpenAICompatible\ChatRequestBuilder;
use AiSdk\Requests\TextModelRequest;
use AiSdk\Message;

$request = new TextModelRequest(
    messages: [Message::user('What is the capital of France?')],
    system: 'You are a helpful assistant.',
);

// stream: false for a regular (non-streaming) completion
$body = ChatRequestBuilder::build(
    modelId: 'gpt-4o',
    providerName: 'openai',
    request: $request,
    stream: false,
);

// $body is now a fully-formed array ready to be JSON-encoded and POSTed.
// e.g. ['model' => 'gpt-4o', 'messages' => [...], 'temperature' => ..., 'stream' => false]
9
ChatRequestBuilder::build() handles system messages, multimodal content, tools, tool choice, structured output (response_format), reasoning effort, stream_options, and provider raw overrides — all from the single portable TextModelRequest object.
10
Send the request
11
Your provider class (extending BaseProvider from aisdk/core) has access to a runner that executes the HTTP call. Pass the built body along with your provider’s base URL and authentication headers:
12
use AiSdk\OpenAICompatible\ChatRequestBuilder;

// Inside your provider's text model class:
$body    = ChatRequestBuilder::build($modelId, $this->providerName(), $request, stream: false);
$payload = $this->runner()->postJson(
    url: 'https://api.openai.com/v1/chat/completions',
    body: $body,
    headers: ['Authorization' => 'Bearer ' . $this->apiKey],
    providerName: $this->providerName(),
);
13
The runner returns the decoded JSON payload as a PHP array, which you pass directly to the parser in the next step.
14
Parse the response
15
ChatResponseParser::parse() converts the raw provider payload into a typed TextModelResponse. Access the generated text, any tool calls, usage statistics, and provider metadata through its methods:
16
use AiSdk\OpenAICompatible\ChatResponseParser;

$response = ChatResponseParser::parse($payload, providerName: 'openai');

echo $response->text();
// "The capital of France is Paris."

echo $response->usage->inputTokens;   // prompt tokens
echo $response->usage->outputTokens;  // completion tokens

// Provider-specific metadata (id, model, system_fingerprint, etc.)
$meta = $response->providerMetadata['openai'];
echo $meta['id'];    // "chatcmpl_..."
echo $meta['model']; // "gpt-4o"
17
Tool calls, if any, are available as typed ToolCallPart objects via $response->toolCalls().
18
Stream with SSE
19
For streaming responses, set stream: true when building the request body and use ChatStreamParser::parse() to process the raw SSE events. Feed each yielded StreamPart into a StreamState instance to accumulate the full response:
20
use AiSdk\OpenAICompatible\ChatRequestBuilder;
use AiSdk\OpenAICompatible\ChatStreamParser;
use AiSdk\Streaming\StreamState;

// Build with stream: true — this adds 'stream_options' => ['include_usage' => true] automatically.
$body = ChatRequestBuilder::build('gpt-4o', 'openai', $request, stream: true);

// Your runner yields raw SSE events: ['event' => ?string, 'data' => string]
$events = $this->runner()->streamJson(
    url: 'https://api.openai.com/v1/chat/completions',
    body: $body,
    headers: ['Authorization' => 'Bearer ' . $this->apiKey],
    providerName: 'openai',
);

$state = new StreamState();

foreach (ChatStreamParser::parse($events, providerName: 'openai') as $part) {
    $state->record($part);
}

// Once the loop completes, $state holds the full accumulated result:
echo $state->text();
// "The capital of France is Paris."

echo $state->usage()->inputTokens;
echo $state->usage()->outputTokens;

// Accumulated tool calls are also available:
foreach ($state->toolCalls() as $call) {
    echo $call->name;      // e.g. "get_weather"
    echo $call->arguments; // fully-assembled argument array
}
21
ChatStreamParser handles text deltas, reasoning deltas, tool-call fragment assembly (keyed by slot index), usage from the final chunk, and provider metadata — yielding typed StreamPart subclasses throughout.

Image Generation

The same pattern applies to image generation endpoints. Use ImageRequestBuilder to build the request body and ImageResponseParser to parse the response.
use AiSdk\OpenAICompatible\ImageRequestBuilder;
use AiSdk\OpenAICompatible\ImageResponseParser;
use AiSdk\Requests\ImageRequest;

// 1. Build the request body from a portable ImageRequest.
$request = new ImageRequest(
    prompt: 'A clean product photo of a white ceramic mug on a marble surface',
    count: 1,
    size: '1024x1024',
);

$body = ImageRequestBuilder::build(
    modelId: 'dall-e-3',
    providerName: 'openai',
    request: $request,
);

// $body includes: model, prompt, n, size, response_format => 'b64_json'

// 2. POST to your provider's image generation endpoint.
$payload = $this->runner()->postJson(
    url: 'https://api.openai.com/v1/images/generations',
    body: $body,
    headers: ['Authorization' => 'Bearer ' . $this->apiKey],
    providerName: 'openai',
);

// 3. Parse the response into a typed ImageResponse.
$response = ImageResponseParser::parse($payload, providerName: 'openai');

$image = $response->first();

echo $image->mimeType;   // "image/png"
echo $image->width;      // 1024
echo $image->height;     // 1024
$rawBytes = $image->bytes(); // decoded binary image data

echo $response->usage->inputTokens;
echo $response->usage->outputTokens;
ImageRequestBuilder also supports aspect-ratio-to-size inference for the standard ratios (1:11024x1024, 16:91536x1024, 9:161024x1536), a configurable size parameter name, and provider raw overrides merged last via providerOptions.

Build docs developers (and LLMs) love