tourze/open-ai-bundle
A Symfony bundle that wires OpenAI's API into your application through configurable services and dependency injection, giving you a ready-made client for chat, completions, and other OpenAI endpoints without writing boilerplate integration code.
Version 0.0.1 is the initial tagged release and ships with an empty changelog (only a compare link), so there is no documented API surface yet. Treat the source as the spec.
composer require tourze/open-ai-bundle then open vendor/tourze/open-ai-bundle/. Look for, in this order: composer.json (required PHP version, symfony/* vs illuminate/* deps), any *Bundle.php or *ServiceProvider.php, a DependencyInjection/ folder, and the concrete client/service class. That file list tells you more about this release than any README will.-bundle suffix in the tourze/* namespace conventionally means a Symfony bundle (Bundle class + Extension for config loading), not a Laravel package. If there is no Illuminate\Support\ServiceProvider, you will be wiring it up yourself (see Implementation Patterns).Extension class or any Configuration.php), bind it in config/services.php and .env rather than hardcoding: base URL, API key, default model, timeout.Because this is a 0.0.1, pin it ("tourze/open-ai-bundle": "0.0.1") and expect the public API to move.
Adapter/bridge pattern (recommended default). Do not inject the bundle's client directly into controllers or jobs. Define App\Contracts\ChatCompleter with one method, and implement it in App\Services\OpenAi\TourzeChatCompleter that wraps the package. When 0.0.2 renames or restructures things — likely — you edit one class. This also gives you a trivial fake for tests.
Manual container binding for Laravel. If the package exposes a Symfony Extension/Configuration rather than a service provider, skip its container wiring entirely: new the client up in a singleton binding in AppServiceProvider, pulling values from config('services.openai.*'). You get Laravel's config caching, env handling, and lazy resolution for free, and you avoid running two container abstractions against each other.
Queue it, don't inline it. Completions are slow and flaky. Push each request to a job (ShouldQueue, $timeout set generously, $tries = 3 with backoff()), persist prompt/response/usage to a table, and dispatch a domain event on completion. The bundle handles the HTTP call; your job owns retries, idempotency, and cost logging.
Normalize responses at the boundary. Store the raw JSON blob alongside extracted fields (model, prompt_tokens, completion_tokens, finish_reason). If 0.0.2 switches from arrays to DTOs, your extraction layer absorbs it and your stored history stays readable.
Testing pattern. Wrap the underlying transport in Http::fake() where possible — if the client uses Guzzle directly, bind a mock handler via the client's constructor or Http::globalOptions. For unit tests, bind your ChatCompleter contract to an anonymous implementation returning fixtures; never let tests hit the live API, and never assert on exact model output.
-bundle naming is a Symfony tell. Check for Illuminate\Support\ServiceProvider before assuming auto-discovery. If absent, composer require will succeed and nothing will be registered — a silent no-op that is easy to misread as a broken package.vendor/ on every bump; a minor version here can be a rewrite. Do not use ^0.0.1 constraints in a long-lived app.composer update tourze/open-ai-bundle --dry-run, then diff the tag range on GitHub. Since the release notes are a bare compare link, the diff is the release notes.tourze/* ecosystem is composed of many very small split packages; composer why the tree after install to see how much was dragged in. If the dependency set pulls Symfony components into a Laravel app, composer why-not and prefer a lighter official SDK if the integration cost exceeds the benefit.env() at call time. If you bind values in AppServiceProvider, remember php artisan config:cache freezes them — anything read lazily inside the client at request time can go stale. Resolve config in the binding closure, not in the constructor's parent.Http::fake()), a configurable base URI (needed for proxies, Azure-compatible endpoints, and local mocks), and a stream handler. If the package adds these, replace your adapter's internals rather than your contract.How can I help you explore Laravel packages today?