Documentation Index
Fetch the complete documentation index at: https://mintlify.com/phpaisdk/openrouter/llms.txt
Use this file to discover all available pages before exploring further.
The PHP AI SDK provides a dependency injection mechanism via the AiSdk\Support\Sdk class. By swapping the real HTTP client for a fake one at test time, you can exercise your OpenRouter-based code end-to-end — including request building, response parsing, and metadata extraction — without making any real API calls or spending credits.
The FakeHttpClient pattern
FakeHttpClient implements PSR-18’s Psr\Http\Client\ClientInterface. It accepts a canned HTTP response at construction time and records the last request it received so that you can make assertions on what was sent.
use AiSdk\OpenRouter\Tests\Fakes\FakeHttpClient;
// Construct with: HTTP status code, response body string, optional content-type
$client = new FakeHttpClient(
status: 200,
body: json_encode([/* ... */]),
contentType: 'application/json', // default
);
After a request has been made, use these two properties for assertions:
$client->lastRequest — a Psr\Http\Message\RequestInterface containing the full request: URI, headers, and body.
$client->sentBody() — decodes the request body from JSON and returns it as array<string, mixed>, making it easy to assert individual fields.
// Assert the Authorization header
$client->lastRequest->getHeaderLine('Authorization'); // 'Bearer or-...'
// Assert the request path
$client->lastRequest->getUri()->getPath(); // '/api/v1/chat/completions'
// Assert a field in the JSON body
$body = $client->sentBody();
$body['model']; // 'openai/gpt-4o'
$body['stream']; // false
$body['messages'][0]['role']; // 'user'
Wiring up the fake client
Pass the FakeHttpClient instance to Generate::configure() wrapped in an AiSdk\Support\Sdk value object. You also need to supply a PSR-17 request factory and stream factory — the Psr17Factory from nyholm/psr7 covers both interfaces.
use AiSdk\Generate;
use AiSdk\Support\Sdk;
use AiSdk\OpenRouter\Tests\Fakes\FakeHttpClient;
use Nyholm\Psr7\Factory\Psr17Factory;
$client = new FakeHttpClient(200, json_encode([/* ... */]));
$factory = new Psr17Factory;
Generate::configure(new Sdk(
httpClient: $client,
requestFactory: $factory,
streamFactory: $factory,
));
After calling Generate::configure(), every subsequent call to Generate::text(), Generate::image(), or any other generation helper will route its HTTP request through $client instead of making a real network call.
Full test example
The following test is drawn directly from the SDK’s own test suite and covers an end-to-end text generation flow — from configuring a fake client to asserting both the parsed response and the outgoing request payload.
use AiSdk\Capability;
use AiSdk\Generate;
use AiSdk\OpenRouter;
use AiSdk\OpenRouter\Tests\Fakes\FakeHttpClient;
use AiSdk\Support\Sdk;
use Nyholm\Psr7\Factory\Psr17Factory;
// 1. Build a fake HTTP client that returns a canned chat completion response.
$client = new FakeHttpClient(200, json_encode([
'id' => 'chatcmpl_openrouter',
'object' => 'chat.completion',
'created' => 1710000000,
'model' => 'openai/gpt-4o',
'choices' => [
[
'index' => 0,
'message' => ['content' => 'Hello from OpenRouter'],
'finish_reason' => 'stop',
],
],
'usage' => ['prompt_tokens' => 9, 'completion_tokens' => 3],
]));
// 2. Wire the fake client into the SDK's HTTP layer.
$factory = new Psr17Factory;
Generate::configure(new Sdk(
httpClient: $client,
requestFactory: $factory,
streamFactory: $factory,
));
// 3. Initialise OpenRouter with an API key and optional headers.
OpenRouter::create([
'apiKey' => 'or-test',
'headers' => ['HTTP-Referer' => 'https://example.com'],
]);
// 4. Run the generation.
$result = Generate::text('Hi')
->model(OpenRouter::model('openai/gpt-4o'))
->run();
// 5. Assert the parsed response values.
assert($result->text === 'Hello from OpenRouter');
assert($result->usage->inputTokens === 9);
assert($result->providerMetadata['openrouter']['id'] === 'chatcmpl_openrouter');
assert($result->providerMetadata['openrouter']['model'] === 'openai/gpt-4o');
// 6. Assert the outgoing request body.
$body = $client->sentBody();
assert($body['model'] === 'openai/gpt-4o');
assert($body['messages'][0]['role'] === 'user');
assert($body['stream'] === false);
// 7. Assert the outgoing request metadata.
assert($client->lastRequest->getUri()->getPath() === '/api/v1/chat/completions');
assert($client->lastRequest->getHeaderLine('Authorization') === 'Bearer or-test');
assert($client->lastRequest->getHeaderLine('HTTP-Referer') === 'https://example.com');
In a Pest or PHPUnit test this translates to fluent expect() or $this->assertSame() calls respectively.
Resetting state between tests
Both Generate and OpenRouter store their configuration as static singletons. Always tear them down in an afterEach (Pest) or tearDown (PHPUnit) hook so that one test’s configuration does not bleed into the next.
// Pest
afterEach(function () {
Generate::reset();
OpenRouter::reset();
});
// PHPUnit
protected function tearDown(): void
{
Generate::reset();
OpenRouter::reset();
}
Calling reset() on both classes sets their internal $default singleton back to null, so the next test always starts from a clean state.
Add nyholm/psr7 to your dev dependencies to get the Psr17Factory used in the examples above:composer require --dev nyholm/psr7
nyholm/psr7 implements both PSR-7 (HTTP messages) and PSR-17 (HTTP factories), so a single package satisfies both constructor parameters of AiSdk\Support\Sdk.