PHPackages                             aisdk/core - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. aisdk/core

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

aisdk/core
==========

Framework-agnostic PHP AI SDK.

v0.8.0(1mo ago)2989↓50%20MITPHPPHP ^8.3CI passing

Since Jun 30Pushed 4w agoCompare

[ Source](https://github.com/phpaisdk/core)[ Packagist](https://packagist.org/packages/aisdk/core)[ Docs](https://github.com/phpaisdk/core)[ RSS](/packages/aisdk-core/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (10)Dependencies (25)Versions (13)Used By (20)

aisdk/core
==========

[](#aisdkcore)

[![GitHub Workflow Status](https://camo.githubusercontent.com/438bcfd5be52a2f011e756fc1bec6edcc11916dcb8bb7e541426e3aaa9b8f72e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f706870616973646b2f636f72652f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d5465737473)](https://github.com/phpaisdk/core/actions)[![Total Downloads](https://camo.githubusercontent.com/b5c1e0040aca1f8590c325cbf3645b9f2848b5df118db67552b83c30d6f4840a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f616973646b2f636f7265)](https://packagist.org/packages/aisdk/core)[![Latest Version](https://camo.githubusercontent.com/e7abe335a80042c0fcf9285ec08f3051cca2324d354b89a46f1a0504470ba788/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616973646b2f636f7265)](https://packagist.org/packages/aisdk/core)[![License](https://camo.githubusercontent.com/6126567f30265912cdb515bf802fb1ed33d5a338a2bf68cad93421044c976259/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f616973646b2f636f7265)](https://packagist.org/packages/aisdk/core)[![Why PHP in 2026](https://camo.githubusercontent.com/d2b9305e630caf7daae4ca023de39ece0b885c37461629d42961533f00868b76/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5768795f5048502d696e5f323032362d3741383645383f7374796c653d666c61742d737175617265266c6162656c436f6c6f723d313831383162)](https://whyphp.dev)

---

Framework-agnostic PHP AI SDK core: contracts, fluent API, value objects, streaming, tools, structured output, and PSR integration.

Installation
------------

[](#installation)

```
composer require aisdk/core
```

Basic Usage
-----------

[](#basic-usage)

```
use AiSdk\Generate;
use AiSdk\OpenAI;

$result = Generate::text()
    ->model(OpenAI::model('gpt-4o'))
    ->instructions('Write short, clear answers.')
    ->prompt('Explain closures in PHP.')
    ->run();

echo $result->text;
```

Default models allow terse call sites:

```
Generate::model(OpenAI::model('gpt-4o'));

$result = Generate::text('Explain closures in PHP.')->run();
```

For long-running workers, concurrent applications, or dependency-injected code, use an instance runtime so configuration is not stored in static process state:

```
use AiSdk\OpenAI\OpenAIOptions;
use AiSdk\OpenAI\OpenAIProvider;
use AiSdk\Support\SdkFactory;

$runtime = (new SdkFactory)
    ->withConnectTimeout(10)
    ->withTimeout(120)
    ->make();

$provider = new OpenAIProvider(OpenAIOptions::fromArray([
    'apiKey' => $_ENV['OPENAI_API_KEY'],
    'sdk' => $runtime,
]));

$sdk = $runtime->withDefaultTextModel($provider->model('gpt-4o'));
$result = $sdk->text('Explain closures in PHP.')->run();
```

The static `Generate` and provider entry points remain convenient for scripts and request-scoped applications; the instance API is the safer choice when one PHP process serves multiple independent workloads.

Structured Output
-----------------

[](#structured-output)

```
use AiSdk\Generate;
use AiSdk\OpenAI;
use AiSdk\Schema;

$result = Generate::text()
    ->model(OpenAI::model('gpt-4o'))
    ->prompt('Extract the city and country from: Lahore, Pakistan.')
    ->output(Schema::object(
        name: 'address',
        description: 'The city and country extracted from the prompt.',
        properties: [
            Schema::string(name: 'city')->required(),
            Schema::string(name: 'country')->required(),
        ],
    ))
    ->run();

echo $result->output['city']; // "Lahore"
```

Tools
-----

[](#tools)

```
use AiSdk\Generate;
use AiSdk\OpenAI;
use AiSdk\Schema;
use AiSdk\Tool;

$weather = Tool::make('weather', 'Get current weather')
    ->input(Schema::string(name: 'city')->required())
    ->run(fn (string $city): string => "Sunny in {$city}");

$result = Generate::text()
    ->model(OpenAI::model('gpt-4o'))
    ->prompt('What is the weather in Lahore?')
    ->tool($weather)
    ->run();
```

Class-based tools:

```
final class WeatherTool extends Tool
{
    public function __construct()
    {
        $this->as('weather')
            ->for('Get current weather')
            ->input(Schema::string(name: 'city')->required());
    }

    public function __invoke(string $city): string
    {
        return "Sunny in {$city}";
    }
}
```

Streaming
---------

[](#streaming)

```
use AiSdk\Generate;
use AiSdk\OpenAI;

$stream = Generate::text('Tell me a story.')
    ->model(OpenAI::model('gpt-4o'))
    ->stream();

foreach ($stream->chunks() as $chunk) {
    echo $chunk;
}

$result = $stream->run();
```

Stream hooks:

```
$stream->onChunk(fn (string $text) => log($text))
    ->onFinish(fn (TextResult $result) => log($result->usage))
    ->onError(fn (\Throwable $e) => log($e));
```

Image Generation
----------------

[](#image-generation)

```
use AiSdk\Generate;
use AiSdk\OpenAI;

$result = Generate::image()
    ->model(OpenAI::model('gpt-image-1'))
    ->prompt('A clean app icon for a PHP AI SDK')
    ->size('1024x1024')
    ->run();

$result->output->save(__DIR__.'/icon.png');
```

Generate multiple images or use portable image options:

```
$result = Generate::image('Minimal line art of Lahore Fort')
    ->model(OpenAI::model('gpt-image-1'))
    ->count(2)
    ->aspectRatio('1:1')
    ->run();

foreach ($result->images as $index => $image) {
    $image->save(__DIR__."/image-{$index}.png");
}
```

Speech Generation
-----------------

[](#speech-generation)

```
use AiSdk\Generate;
use AiSdk\OpenAI;

$result = Generate::speech()
    ->model(OpenAI::model('gpt-4o-mini-tts'))
    ->input('Welcome to the PHP AI SDK.')
    ->voice('coral')
    ->format('mp3')
    ->run();

$result->output->save(__DIR__.'/welcome.mp3');
```

The portable speech surface supports `input()`, `voice()`, `format()`, and provider-specific options:

```
$result = Generate::speech('Read this in a warm tone.')
    ->model(OpenAI::model('gpt-4o-mini-tts'))
    ->providerOptions('openai', [
        'instructions' => 'Speak clearly and warmly.',
    ])
    ->run();
```

Embeddings
----------

[](#embeddings)

Generate one vector or a batch of vectors through the same text-only API:

```
use AiSdk\Generate;
use AiSdk\OpenAI;

$result = Generate::embedding([
        'First document',
        'Second document',
    ])
    ->model(OpenAI::model('text-embedding-3-small'))
    ->dimensions(512)
    ->run();

$firstVector = $result->output->vector;
$allVectors = $result->embeddings;
```

`dimensions()` is the only portable embedding option. Provider-specific fields such as retrieval task type or truncation can be sent with `providerOptions()` using the provider package's documented field names.

Transcription
-------------

[](#transcription)

```
use AiSdk\Content;
use AiSdk\Generate;
use AiSdk\OpenAI;

$result = Generate::transcription(Content::audio(__DIR__.'/meeting.mp3'))
    ->model(OpenAI::model('gpt-4o-transcribe'))
    ->run();

echo $result->output->text;
```

The portable transcription surface accepts typed audio content. Language hints, diarization, timestamp detail, and other provider-specific controls remain available through `providerOptions()`.

Video Generation
----------------

[](#video-generation)

Video providers expose asynchronous jobs through one portable API. `run()` waits for completion; `job()` starts the operation and returns its job descriptor.

```
$result = Generate::video('A cinematic product reveal')
    ->model($provider->model('video-model-id'))
    ->aspectRatio('16:9')
    ->resolution('1280x720')
    ->duration(8)
    ->run(timeout: 600);

$job = Generate::videoJob('A glass PHP logo rotating in space')
    ->model($provider->model('video-model-id'))
    ->job();
```

Live Voice, Transcription, and Translation
------------------------------------------

[](#live-voice-transcription-and-translation)

`AiSdk\Live` is part of core. Provider packages prepare their endpoint, authentication, messages, and normalized events; the application supplies a transport that implements core's two small transport contracts.

```
use AiSdk\Live;
use AiSdk\Live\AudioDelta;
use AiSdk\Live\TranscriptCompleted;
use AiSdk\OpenAI;

$session = Live::voice()
    ->model(OpenAI::model('gpt-realtime-2.1'))
    ->instructions('Answer briefly and clearly.')
    ->voice('marin')
    ->inputAudioFormat('pcm16')
    ->outputAudioFormat('pcm16')
    ->connect($applicationTransport);

$session->sendAudio($pcmBytes);
$session->commitAudio();

foreach ($session->events() as $event) {
    if ($event instanceof AudioDelta) {
        $speaker->write($event->bytes);
    }

    if ($event instanceof TranscriptCompleted) {
        echo $event->text;
    }
}
```

The session API is transport-independent:

```
$session->sendAudio($bytes);
$session->sendText('Hello');
$session->commitAudio();
$session->clearAudio();
$session->requestResponse();
$session->cancelResponse();
$session->sendToolResult($callId, $result);
$events = $session->events();
$session->close();
```

Use separate builders so an operation only exposes portable fields that apply to it:

```
$captions = Live::transcribe()
    ->model($provider->model('live-transcription-model'))
    ->language('en')
    ->audioFormat('pcm16')
    ->connect($applicationTransport);

$translator = Live::translate()
    ->model($provider->model('live-translation-model'))
    ->from('en')
    ->to('es')
    ->inputAudioFormat('pcm16')
    ->outputAudioFormat('pcm16')
    ->connect($applicationTransport);
```

`providerOptions('provider-name', [...])` remains the escape hatch for provider-only session fields. Proxy, TLS, timeout, buffer, and frame-limit configuration belongs to the transport instead.

### Ready-made transports

[](#ready-made-transports)

The optional `aisdk/transport` package supplies WebSocket and bidirectional HTTP/2 implementations:

```
composer require aisdk/transport
```

```
use AiSdk\Transport;

$session = Live::voice()
    ->model(OpenAI::model('gpt-realtime-2.1'))
    ->connect(Transport::auto());
```

`Transport::auto()` chooses from the endpoint prepared by the provider. Use `Transport::webSocket()` or `Transport::http2()` when the application should explicitly restrict the network protocol.

### Core without `aisdk/transport`

[](#core-without-aisdktransport)

Core has no Amp or WebSocket dependency. Install any networking library and implement `AiSdk\Live\Contracts\TransportInterface` plus `TransportConnectionInterface`. A complete Amp implementation is included in [`examples/AppWebSocketTransport.php`](examples/AppWebSocketTransport.php):

```
composer require amphp/websocket-client:^2.0
```

```
use App\Ai\AppWebSocketTransport;

$session = Live::voice()
    ->model(OpenAI::model('gpt-realtime-2.1'))
    ->connect(new AppWebSocketTransport());
```

Provider adapters only exchange `TransportFrame::text()` and `TransportFrame::binary()` with the transport. A custom transport must not interpret provider event names.

`events()` waits for incoming network data. For continuous, full-duplex audio, run the microphone sender and event consumer concurrently using the async runtime chosen by your application. Core itself does not expose Amp, Revolt, or another event-loop type; the ready-made transport README includes an Amp example.

### Tools and normalized events

[](#tools-and-normalized-events)

Voice tools use the same core `Tool` objects as text generation. A registered tool with a handler is executed automatically. The `ToolCallEvent` is still emitted for observability, followed by an automatic `ToolResultEvent`. Unknown or handler-less tools remain manual:

```
foreach ($session->events() as $event) {
    if ($event instanceof \AiSdk\Live\ToolCallEvent && $event->name === 'approval') {
        $session->sendToolResult($event->callId, ['approved' => true]);
    }
}
```

Portable events cover audio and text deltas, append-only transcript deltas, replaceable transcript updates, completed transcripts, speech boundaries, interruptions, tool calls and results, usage, response completion, errors, and closure. Unknown provider messages are preserved as `ProviderEvent` rather than discarded.

### WebRTC and provider-hosted calls

[](#webrtc-and-provider-hosted-calls)

WebRTC and SIP are connection topologies, not PHP media transports. A browser uses its native `RTCPeerConnection`; PHP creates a short-lived client secret or exchanges an SDP offer through the provider adapter:

```
$secret = Live::voice()
    ->model(OpenAI::model('gpt-realtime-2.1'))
    ->voice('marin')
    ->clientSecret();

$answer = Live::voice()
    ->model(OpenAI::model('gpt-realtime-2.1'))
    ->webRtc($offerSdp);

return ['sdp' => $answer->sdp, 'call_id' => $answer->callId];
```

For an incoming provider-hosted call, first verify the webhook with the provider package, then control the call through core:

```
$call = Live::voice()
    ->model(OpenAI::model('gpt-realtime-2.1'))
    ->instructions('You are the support desk.')
    ->call($verifiedWebhook['data']['call_id']);

$call->accept();
$controlSession = $call->connect($applicationTransport);

// Read normalized events and answer tool calls over the control connection.
foreach ($controlSession->events() as $event) {
    // ...
}

$call->hangup();
```

That control connection is sometimes called a sideband connection: the provider continues carrying WebRTC or SIP media, while the backend attaches a WebSocket to the same call ID to update the session, observe events, and handle tools. Providers expose lifecycle methods only where their official protocol supports them.

Model IDs and Capabilities
--------------------------

[](#model-ids-and-capabilities)

Model IDs are opaque provider values. Packages do not ship model inventories, so new, private, routed, deployed, and locally installed models can be used without waiting for an SDK release:

```
$result = Generate::text('Hello')
    ->model(OpenAI::model('your-model-id'))
    ->run();
```

`Provider::model()` is the only public provider model selector. The operation resolves that opaque reference to its internal text, image, speech, transcription, embedding, video, or Live adapter when the request executes.

Adapter capabilities are internal implementation details. Before sending a request, the SDK verifies that the selected provider adapter can serialize the requested features. The provider remains the authority on whether the exact model accepts those features. Models never need to be registered with the SDK.

Providers whose available models depend on the current account, region, or local runtime may implement `AvailableModelsProviderInterface` and, where supported, `ModelInspectionProviderInterface`. Provider packages should query an authoritative runtime endpoint instead of presenting a bundled list as live availability. For example, Ollama exposes `Ollama::availableModels()` for its local registry and `Ollama::inspectModel()` for informational model details.

Supported Capabilities
----------------------

[](#supported-capabilities)

- Text generation (`Generate::text()`)
- Streaming text generation (`->stream()`)
- Image generation (`Generate::image()`)
- Speech generation (`Generate::speech()`)
- Text embeddings (`Generate::embedding()`)
- Audio transcription (`Generate::transcription()`)
- Video generation and asynchronous jobs (`Generate::video()`, `Generate::videoJob()`)
- Live voice, transcription, and translation (`Live::voice()`, `Live::transcribe()`, `Live::translate()`)
- Tool calling (`->tool(...)`)
- Structured output (`->output(...)`)
- Provider options passthrough (`->providerOptions(...)`)
- Normalized provider errors

Testing
-------

[](#testing)

```
composer test
```

Core ships with fakes and assertions for deterministic testing:

```
use AiSdk\Testing\Fakes\FakeTextModel;

$fake = FakeTextModel::text('Hello!');

$result = Generate::text('Hi')
    ->model($fake)
    ->run();

expect($result->text)->toBe('Hello!');
```

Embedding tests can use `AiSdk\Testing\Fakes\FakeEmbeddingModel` in the same way. Transcription tests can use `AiSdk\Testing\Fakes\FakeTranscriptionModel`.

Documentation
-------------

[](#documentation)

- [PHP AI SDK documentation](https://phpaisdk.com/docs)
- [Core documentation](https://phpaisdk.com/docs)

Community
---------

[](#community)

- [Contributing](https://github.com/phpaisdk/.github/blob/main/CONTRIBUTING.md)
- [Support](https://github.com/phpaisdk/.github/blob/main/SUPPORT.md)
- For private security reports, email .

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance94

Actively maintained with recent releases

Popularity23

Limited adoption so far

Community20

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

12

Last Release

31d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ef29d1962da41e7526023f215cf844494e209b2fd163742084cb919a3b508021?d=identicon)[aisdk](/maintainers/aisdk)

---

Top Contributors

[![RanaMoizHaider](https://avatars.githubusercontent.com/u/58527494?v=4)](https://github.com/RanaMoizHaider "RanaMoizHaider (21 commits)")

---

Tags

aiopenaillmanthropicaisdk

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/aisdk-core/health.svg)

```
[![Health](https://phpackages.com/badges/aisdk-core/health.svg)](https://phpackages.com/packages/aisdk-core)
```

###  Alternatives

[tempest/framework

The PHP framework that gets out of your way.

2.3k37.6k20](/packages/tempest-framework)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

86337.5k](/packages/flow-php-flow)[cakephp/cakephp

The CakePHP framework

8.9k20.0M1.8k](/packages/cakephp-cakephp)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

762297.9k52](/packages/civicrm-civicrm-core)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)[drupal/core-recommended

Locked core dependencies; require this project INSTEAD OF drupal/core.

6943.5M448](/packages/drupal-core-recommended)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
