ineersa / symfony-ai-openai-chatgpt-platform
Single-account Sign in with ChatGPT OAuth and HTTP Responses bridge for Symfony AI.
Package info
github.com/ineersa/symfony-ai-openai-chatgpt-platform
pkg:composer/ineersa/symfony-ai-openai-chatgpt-platform
Requires
- php: >=8.5
- ext-openssl: *
- firebase/php-jwt: ^6.11 || ^7.0
- league/oauth2-client: ^2.8
- psr/log: ^3.0
- symfony/ai-open-responses-platform: ^0.14
- symfony/ai-platform: ^0.14
- symfony/cache: ^8.0
- symfony/clock: ^8.0
- symfony/console: ^8.1
- symfony/filesystem: ^8.0
- symfony/http-client: ^8.0
- symfony/lock: ^8.0
- symfony/process: ^8.0
- symfony/uid: ^8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^13.2
Suggests
- ext-zstd: Lossless level-3 compression of Responses request bodies to mitigate gateway upload-size failures.
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-13 15:53:27 UTC
README
Use your ChatGPT subscription with Symfony AI through Sign in with ChatGPT. The package handles browser login, token refresh, and requests to the OpenAI Responses API using only HTTP and server-sent events (SSE).
Requires PHP 8.5+, Symfony AI 0.14, and a ChatGPT account that permits subscription sharing.
Warning
Install ext-zstd to enable request compression. Without it, uncompressed requests exceeding roughly 1 MiB can fail with HTTP 503 because of an observed OpenAI gateway upload-limit bug, not a documented universal API limit. Compression mitigates this bug but does not guarantee that the model's full context window will fit.
Install the package
composer require ineersa/symfony-ai-openai-chatgpt-platform
Log in and send a message
This basic chatgpt.php example provides a login command and streams a reply to "Hello". Add /var/chatgpt/ to .gitignore before logging in to keep credentials out of version control.
Warning
This package requires a non-empty prompt_cache_key, although the OpenAI API makes it optional. Reuse one key per conversation: the package sends it as prompt_cache_key, session-id, and x-client-request-id to aid routing and cache reuse for identical prompt prefixes, without guaranteeing cache hits.
<?php require __DIR__.'/vendor/autoload.php'; use Symfony\AI\Platform\Bridge\OpenAIChatGPT\Auth\AuthCommand; use Symfony\AI\Platform\Bridge\OpenAIChatGPT\Auth\AuthFileStore; use Symfony\AI\Platform\Bridge\OpenAIChatGPT\Auth\IdTokenVerifier; use Symfony\AI\Platform\Bridge\OpenAIChatGPT\Auth\OAuthConfig; use Symfony\AI\Platform\Bridge\OpenAIChatGPT\Auth\OAuthService; use Symfony\AI\Platform\Bridge\OpenAIChatGPT\Factory; use Symfony\AI\Platform\Bridge\OpenResponses\ModelCatalog; use Symfony\AI\Platform\Bridge\OpenResponses\ResponsesModel; use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Message\Message; use Symfony\AI\Platform\Message\MessageBag; use Symfony\Component\Console\Application; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\Store\FlockStore; // Keep credentials on disk and coordinate token refresh across local processes. $lockDir = __DIR__.'/var/chatgpt/locks'; (new Filesystem())->mkdir($lockDir, 0700); $http = HttpClient::create(); $store = new AuthFileStore( __DIR__.'/var/chatgpt/auth.json', new LockFactory(new FlockStore($lockDir)), ); // Use your application's name for OAuth registration and the Originator header. $config = new OAuthConfig('My CLI'); $oauth = new OAuthService($store, $http, new IdTokenVerifier($http), $config); // Handle login, refresh, and disconnect through Symfony Console. if ('ask' !== ($argv[1] ?? null)) { $console = new Application(); $console->addCommand(new AuthCommand($oauth, $config)); exit($console->run()); } $modelName = $argv[2] ?? throw new InvalidArgumentException('Usage: php chatgpt.php ask MODEL'); $provider = Factory::createProvider($oauth, $http, new ModelCatalog()); // Each ask starts a new conversation. Persist this key with history to continue it. $conversationKey = bin2hex(random_bytes(16)); // Invoke an account-supported model; OAuthService refreshes expired credentials. $result = $provider->invoke( new ResponsesModel($modelName, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]), new MessageBag(Message::ofUser('Hello')), ['prompt_cache_key' => $conversationKey], ); // Print text as SSE events arrive, rather than waiting for the complete response. foreach ($result->asTextStream() as $text) { echo $text; } echo PHP_EOL;
Log in, then stream a reply. Replace YOUR_MODEL with a model available to your account:
php chatgpt.php auth:chatgpt login php chatgpt.php ask YOUR_MODEL
If you signed in without enabling plan usage, run php chatgpt.php auth:chatgpt login --consent. The provider refreshes expired credentials before sending requests.
Documentation
| Guide | Use it to |
|---|---|
| Authentication | Register the command, use manual login, refresh, and disconnect |
| Usage | Configure models, send conversation history, and cancel streams |
| History and reasoning | Preserve message phases, encrypted reasoning, and effort changes |
| API reference | Look up options, request limits, and error types |
| OAuth lifecycle | Understand verification, storage locks, and recovery after refresh |
Develop
Run composer install, then castor test, castor lint, castor phpstan, castor cs-check, and castor composer-validate. Tests use mocked HTTP and synthetic credentials, not a real ChatGPT account.