symfony/ai-gemini-platform
Google Gemini platform bridge for Symfony AI, letting your PHP apps call Gemini's generateContent and streaming endpoints plus batchEmbedContents embeddings through a consistent Symfony AI interface.
Architecture fit. This is a bridge, not a framework: it implements Symfony AI's Platform contract against Gemini's generateContent and batchEmbedContents endpoints. For a Laravel codebase that is a favorable shape — the integration surface is one interface plus a factory, and Symfony components are already transitive dependencies of Laravel. The strategic asset is not the Gemini call; it's that Gemini sits behind a provider abstraction, so a second provider is a config change rather than a rewrite. The risk is the opposite direction: if the concrete Platform/ModelClient classes leak into controllers, jobs, and Blade views, you've paid for portability and not received it.
Integration feasibility. Mechanically low-friction: Composer install, an API key in config/services.php, a service provider binding the platform to the container. There is no Laravel-native package or facade — you write the wiring yourself, which is roughly half a day for someone comfortable with Laravel's container and roughly a week for someone who isn't. Two real dependencies to verify early: the symfony/ai-platform version constraint (the bridge tracks the monorepo's release cadence, currently ~v0.14) and the PHP floor (the changelog carries an 8.3 polyfill for json_validate(), so confirm your runtime). Streaming requires an SSE-tolerant path end to end — PHP-FPM worker occupancy, proxy buffering, and any API gateway in front of the app are all part of the integration, not just the client.
Technical risk. The honest headline: five stars, zero dependents, pre-1.0 semver. There is effectively no production battle-testing outside Symfony's own work. That is offset — partially — by the maintainers being recognizable Symfony core contributors and by the code living in the symfony/ai monorepo with a shared issue tracker and PR flow. The changelog is the strongest positive signal available here: it is dense with small, specific correctness fixes (multi-part streaming crashes, empty tool-call roundtrips, $schema stripping from tool parameters, protobuf wrapping of function_response, SSE framing to avoid partial-JSON parsing). That pattern indicates an actively exercised bridge rather than an abandoned one. Secondary risks: Gemini API drift against a model catalogue that refreshes on an automated release cadence, no async story (background work is yours to queue), and no vendor SLA anywhere in the chain.
Key questions to resolve before committing.
symfony/ai-platform constraint does this release pin, and does it collide with anything already in composer.lock?Stack fit. Laravel + Symfony HTTP client + Symfony AI is a well-trodden combination; the marginal dependency cost is low because Symfony's HTTP client and polyfills are already present in most Laravel installs. The friction is not technical, it's architectural discipline: Laravel developers will instinctively reach for a facade, and there isn't one. Plan on a config/ai.php, a GeminiServiceProvider, and — critically — an internal AiClient interface that the rest of the application depends on. The bridge is an implementation detail behind that interface, not an ambient capability.
Migration path. Treat this as a strangler migration, not a greenfield adoption:
Compatibility. Pin exactly (0.14.x) and commit the lockfile; do not use a caret range. Read changelogs before every bump — several recent minors carry behavioral fixes (SSE framing, delta semantics, AssistantMessage rework) that will surface as test failures or silent output changes. Model IDs are catalog-managed and refresh automatically in releases; hardcoded model strings in application code will rot, so put them in config with an env override. Verify EMBEDDINGS capability flags on embedding models before wiring search — a historical bug in this exact area (missing capability declaration) is the kind of thing that fails at runtime, not at boot.
Sequencing.
AiClient contract, Gemini implementation, a fake for tests, a single smoke test per capability.TokenUsage from day one; it carries the model identifier and is the only cheap path to per-feature cost attribution later.Maintenance. Budget a recurring, if modest, tax: monthly changelog review, quarterly dependency bumps, and vigilance on model catalogue changes that can silently alter cost or capability. That cadence is predictable, which is worth more than the absolute size of the effort — it removes the unplanned "our model IDs are deprecated" ticket from mid-sprint. The larger maintenance hazard is abandonment: with five stars and zero dependents, if upstream stalls, your options are fork-and-maintain or re-implement behind the same internal interface. The interface is what makes the second option survivable; without it, you inherit a dead dependency in domain code.
Support. MIT, community-maintained, issue tracker inside the symfony/ai monorepo. There is no vendor to escalate to, no SLA, and no security-review channel beyond upstream PRs. If procurement or a customer security questionnaire requires vendor-backed assurances, this package cannot supply them — Google's API uptime is the only contractual guarantee in the chain. Escalation path for a blocking bug is "open an upstream issue and contribute a fix," which means the team needs at least one person capable of reading the bridge's source and reading a raw Gemini error payload.
Scaling. Calls are synchronous HTTP; there is no built-in async or batching orchestration. At meaningful volume, throughput becomes a queueing and concurrency design problem — Laravel queues with Horizon, separate worker pools per workload class, and timeouts tuned per endpoint. Three ceilings will be hit in this order: provider rate limits (surfaced as RateLimitExceededException), context-size limits (ExceedContextSizeException), and PHP-FPM worker exhaustion from long-lived streaming connections. The first two need backoff and prompt-trimming strategies; the third needs a streaming-aware infrastructure decision (dedicated pool, or a proxy that doesn't buffer SSE). Embeddings should be issued as explicit batches — batch sizing is a throughput/cost lever you control, not something the bridge optimizes for you.
Failure modes to design for.
ServerException; needs bounded retries with jitter, not retry-until-success.ModelNotFoundException; alert on it distinctly, since it usually means a config or catalogue drift rather than a user error.ExceedContextSizeException at request time — truncation and chunking policy belong in the application, upstream of the client.Ramp-up. The learning curve is not Gemini, it's Symfony AI's abstractions: Platform, ModelClient, message types, DeltaInterface deltas, capability flags, and the exception hierarchy. Expect one to two weeks for a mid-level Laravel developer to be productive, longer if they've never debugged an HTTP-level integration. Documentation is thin — the README points to Google's API reference and the Symfony AI docs, and the real knowledge lives in the changelog and the source. Mitigate by writing a short internal runbook covering container wiring, the model config, how to read a raw Gemini error, and where token usage is persisted. That runbook will pay for itself the first time someone debugs a streaming failure at 2 a.m. without it.
How can I help you explore Laravel packages today?