Six merges landed in laravel/ai across image generation, provider consistency, and correctness fixes. Three of them tighten up the image generation surface, one fixes a data-mapping gap in text generation, and two close sharp edges that were producing confusing failures in production.
@billyfranklim1 merged PR #333, adding a new OpenRouterImageGateway that routes image generation requests through OpenRouter's /api/v1/chat/completions endpoint using the modalities: ["image", "text"] parameter.
Before this PR, calling Image::of()->generate(provider: Lab::OpenRouter) threw a LogicException because OpenRouterProvider only implemented EmbeddingProvider and TextProvider. Now it implements ImageProvider as well, with google/gemini-2.5-flash-image as the default model.
1use Laravel\Ai\Image;2use Laravel\Ai\Enums\Lab;3 4$response = Image::of('A sunset over mountains')5 ->landscape()6 ->generate(provider: Lab::OpenRouter, model: 'google/gemini-2.5-flash-image');7 8$image = $response->firstImage();9$binary = $image->content();
The gateway parses base64 data URI responses and maps them into GeneratedImage instances. Aspect ratio flows through to image_config.aspect_ratio on the request. This fixes issue #330.
@morcken merged PR #282, fixing a silent drop in GeminiProvider::defaultImageOptions(). Previously, only 1:1, 2:3, and 3:2 were forwarded to Gemini as aspect_ratio. Any other value, including ratios the Gemini API actively supports like 3:4, was converted to null and discarded.
The fix mirrors what OpenAI already does: any non-null size value is passed through as aspect_ratio. The three convenience mappings are preserved.
1use Laravel\Ai\Enums\Lab;2use Laravel\Ai\Image;3 4return Image::of($prompt)5 ->quality('medium')6 ->size('3:4')7 ->timeout(60)8 ->generate(provider: Lab::Gemini);
This is backward compatible. No method signatures changed.
@maherelgamil merged PR #482, fixing a data discard in both OpenAiGateway::generateImage() and GeminiGateway::generateImage(). Both were constructing new Usage(0, 0) and throwing away the token counts the providers actually returned. This fixes issue #457.
gpt-image-1 returns usage.input_tokens and usage.output_tokens. Gemini's image generateContent returns the same usageMetadata block as text generation. Both are now mapped through into ImageResponse->usage.
xAI and Bedrock use per-image billing and do not return token usage, so those gateways are left intentionally unchanged. Any team tracking generation costs against OpenAI or Gemini image endpoints will now get accurate numbers from $response->usage.
@mohali-id merged PR #461, enhancing assistant message mapping and extending tool call attributes to capture richer metadata. The change addresses issue #271 and has been tested against Azure OpenAI with the GPT-5.3-Codex model.
@Button99 merged PR #345, correcting a contract mismatch in the text generation flow. Conversational::messages() declares a return type of iterable, but both the prompt and stream paths were treating the return value as an array and appending with $messages[].
This works when the concrete implementation happens to return an array, but breaks for any other iterable type. The fix handles all iterable types correctly rather than relying on the underlying type to be an array.
ai.default@JoshSalway merged PR #327, adding a guard against a config shape that was producing a completely misleading error. When config('ai.default') was set to an array like ['text' => 'anthropic'] instead of a plain string, the SDK threw "Instance driver [text] is not supported". That message gives no indication the config itself is the problem.
The published config/ai.php has always expected a string ('default' => 'openai'), but the array shape appeared in enough user setups that the obscure error was worth addressing directly. The fix detects the array case and throws a message that points straight at the misconfiguration. This fixes issue #289.
If you enjoyed this article, please consider supporting our work for as low as $5 / month.
Sponsor
Written by
Writing and maintaining @LaravelMagazine. Host of "The Laravel Magazine Podcast". Pronouns: vi/vim.