---
title: "OpenAIProvider"
description: "Use OpenAI directly, or any OpenAI-compatible endpoint like OpenRouter, Ollama, or Cloudflare AI Gateway via a custom base URL."
canonical_url: "https://kirby.tools/docs/copilot/php-classes/providers/openai"
---

# OpenAIProvider

> Use OpenAI directly, or any OpenAI-compatible endpoint like OpenRouter, Ollama, or Cloudflare AI Gateway via a custom base URL.

`OpenAIProvider` is also the parent class for [`GeminiProvider`](/docs/copilot/php-classes/providers/gemini) and [`MistralProvider`](/docs/copilot/php-classes/providers/mistral) – both inherit retry handling, request shape, and response parsing.

## Construction

```php
public function __construct(
    ProviderConfig $config,
    ClientContract|null $client = null,
    Closure|null $sleep = null,
)
```

<field-group>
<field name="config" type="ProviderConfig">

Resolved per-provider configuration (`apiKey`, `model`, `baseUrl`, plus a passthrough `options` bag).

</field>

<field name="client" type="ClientContract | null">

Inject a custom `OpenAI\Contracts\ClientContract` (e.g. for tests). Defaults to the SDK's `OpenAI::factory()` chain.

</field>

<field name="sleep" type="Closure | null">

Inject a sleep function for retry timing. Defaults to PHP's built-in `sleep(...)`.

</field>
</field-group>

## Methods

### `generateObject`

Builds a `chat.completions` request with `response_format: { type: 'json_schema', strict: true }`, sends it, and returns the decoded JSON object.

```php
$provider->generateObject(
    messages: [
        ['role' => 'system', 'content' => 'Return JSON only.'],
        ['role' => 'user', 'content' => 'Pick three colors.'],
    ],
    schema: [
        'type' => 'object',
        'properties' => ['colors' => ['type' => 'array', 'items' => ['type' => 'string']]],
        'required' => ['colors'],
        'additionalProperties' => false,
    ],
);
```

Throws `ProviderException` when the decoded response is not a JSON object, or when the upstream call returns 4xx/5xx after the retry chain.

### `generateText`

Sends a `chat.completions` request without `response_format` and returns the message content.

```php
$provider->generateText(
    messages: [
        ['role' => 'user', 'content' => 'Describe three primary colors.'],
    ],
);
```

Throws `ProviderException` when the response carries no text content, or when the upstream call returns 4xx/5xx after the retry chain.

## Retry Behavior

The initial request plus up to 3 retries on `RateLimitException` (429), `ServerException` (5xx), `TransporterException` (network), and `ErrorException` with status 429 or ≥500. The wrapper honors `Retry-After`, falling back to `2^attempt` seconds.

After the fourth failed request the wrapper throws `ProviderException` with `reason: 'request failed: <message>'`, `responseExcerpt` (body shortened to 200 chars), `httpCode`, and `previous` set to the original `Throwable`.

## OpenAI-Compatible Endpoints

`OpenAIProvider` doubles as the transport for any OpenAI-compatible API. Configure the base URL and model in `config.php`:

<code-group>

```php [OpenRouter]
'providers' => [
    'openai' => [
        'apiKey' => env('OPENROUTER_API_KEY'),
        'baseUrl' => 'https://openrouter.ai/api/v1',
        'model' => 'anthropic/claude-sonnet-5',
    ],
],
```

```php [Self-hosted (llama.cpp)]
'providers' => [
    'openai' => [
        'apiKey' => 'sk-no-key-required',
        'baseUrl' => 'https://llama.example.com/v1',
        'model' => 'llama-3.2-3b-instruct',
    ],
],
```

```php [Cloudflare AI Gateway]
'providers' => [
    'openai' => [
        'apiKey' => env('OPENAI_API_KEY'),
        'baseUrl' => 'https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/openai',
        'model' => 'gpt-5.6-terra',
    ],
],
```

</code-group>

For Chat Completions vs. Responses API selection, see the [`api` option](/docs/copilot/configuration/global#api).

<warning>

Structured output through OpenAI-compat gateways depends on the gateway's `json_schema` translation. Test before relying on blocks or layout generation through this path.

</warning>

## Provider-Specific Options

Anything in `providers.openai` that isn't `apiKey`, `model`, `baseUrl`, `completionModel`, or `api` lands in `ProviderConfig::$options` and is spread into every request payload:

```php
'providers' => [
    'openai' => [
        'apiKey' => env('OPENAI_API_KEY'),
        'temperature' => 0.7,
        'reasoning_effort' => 'medium',
        'top_p' => 0.9,
    ],
],
```

The plugin doesn't validate option names – anything that doesn't match an OpenAI Chat Completions field is sent as-is and may be rejected upstream.

---

Every page of this site as Markdown: <https://kirby.tools/sitemap.md>
