ianrodrigues / laravel-fal-ai
fal.ai driver for the Laravel AI SDK. Ships image generation today; audio, video, and transcription handlers can be added as fal expands.
Requires
- php: ^8.3
- illuminate/contracts: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- laravel/ai: ^0.7
Requires (Dev)
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-laravel: ^4.1
This package is auto-updated.
Last update: 2026-05-27 12:58:37 UTC
README
Introduction
laravel-fal-ai is a third-party fal.ai driver for the official Laravel AI SDK. It plugs a fal provider into the SDK's manager so you may generate content with fal models through the same fluent API you use for OpenAI, Gemini, and the other bundled providers.
The package ships image generation today with two built-in handlers:
nano-banana-2/edit— prompt + reference images → edited image.birefnet— single image → transparent background-removed cutout (defaults to v2; passbirefnet/v1to opt into the legacy model).
Its architecture is capability-scoped, so support for audio, video, and transcription can be added under parallel namespaces as fal expands.
Installation
You may install the package via Composer:
composer require ianrodrigues/laravel-fal-ai
The service provider is registered automatically through Laravel's package discovery. No manual registration is required.
Requirements
- PHP 8.3 or higher
- Laravel 12 or 13
laravel/ai^0.7
Configuration
Set your fal API key in your application's .env file. You may grab a key from the fal dashboard:
FAL_KEY=your-fal-api-key
Next, register the driver in your config/ai.php providers array:
'providers' => [ // ... 'fal' => [ 'driver' => 'fal', 'key' => env('FAL_KEY'), ], ],
If you would like to customize the package's defaults — timeouts, polling cadence, model handler registration — you may publish the configuration file:
php artisan vendor:publish --tag=fal-ai-config
This will place a fal-ai.php file in your config directory. The available options are documented in the Configuration Reference section.
Image Generation
The driver supports two usage surfaces. The first matches the upstream Laravel AI builder and is recommended when the SDK's standard options are all you need. The second is a fluent builder that exposes fal-specific knobs the upstream SDK does not yet surface for image generation.
Standard Laravel AI Builder
use Laravel\Ai\Files\Image as ImageFile; use Laravel\Ai\Image; $response = Image::of('change the sky to a sunset') ->attachments([ImageFile::fromUrl('https://example.com/photo.jpg')]) ->square() ->quality('high') ->generate(provider: 'fal', model: 'nano-banana-2/edit'); file_put_contents('out.png', base64_decode($response->images->first()->image));
The SDK's size and quality arguments are translated to fal's vocabulary:
| SDK value | fal field | fal value |
|---|---|---|
->square() / 1:1 |
aspect_ratio |
1:1 |
->portrait() / 2:3 |
aspect_ratio |
2:3 |
->landscape() / 3:2 |
aspect_ratio |
3:2 |
quality('low') |
resolution |
0.5K |
quality('medium') |
resolution |
1K |
quality('high') |
resolution |
2K |
Fluent fal Builder
When you need a fal-specific option (seed, num_images, safety_tolerance, and so on), use the Fal facade. It wraps the standard Image::of(...) builder and accepts every method the SDK exposes, plus methods for each fal parameter:
use IanRodrigues\FalAi\Facades\Fal; use Laravel\Ai\Files\Image as ImageFile; $response = Fal::image('change the sky to a sunset') ->attachments([ImageFile::fromUrl('https://example.com/photo.jpg')]) ->seed(42) ->numImages(2) ->aspectRatio('16:9') ->safetyTolerance(4) ->outputFormat('webp') ->generate(model: 'nano-banana-2/edit');
The following fal-only methods are available:
| Method | fal parameter |
|---|---|
numImages(int) |
num_images |
seed(int) |
seed |
aspectRatio(string) |
aspect_ratio |
resolution(string) |
resolution |
safetyTolerance(int) |
safety_tolerance |
outputFormat(string) |
output_format |
systemPrompt(string) |
system_prompt |
thinkingLevel(string) |
thinking_level |
enableWebSearch(bool) |
enable_web_search |
limitGenerations(int) |
limit_generations |
For any option not covered by a dedicated method, you may use option(string, mixed) or pass an array via options(array).
Background Removal (BiRefNet)
The package also handles fal-ai/birefnet, which removes the background of a single attached image and returns a transparent PNG. v2 is used by default; pass birefnet/v1 to opt into the legacy model.
use Laravel\Ai\Files\Image as ImageFile; use Laravel\Ai\Image; $response = Image::of('remove background') ->attachments([ImageFile::fromPath('/home/laravel/character.png')]) ->generate(provider: 'fal', model: 'birefnet'); file_put_contents('cutout.png', base64_decode($response->images->first()->image));
A prompt string is required by the SDK but ignored by BiRefNet — use any placeholder. Per-call options the handler passes through to fal: model (Swin variant), output_format (png | webp), sync_mode.
Attachments
The driver accepts every attachment type the Laravel AI SDK exposes:
| Source | Behavior |
|---|---|
ImageFile::fromUrl($url) |
URL is forwarded to fal verbatim. |
ImageFile::fromPath($path) |
Bytes are read from disk and uploaded to fal storage. |
ImageFile::fromBase64($data, $mime) |
Decoded and uploaded to fal storage. |
ImageFile::fromStorage($path, $disk) |
Pulled from the disk and uploaded to fal storage. |
ImageFile::fromUpload($uploadedFile) |
Read from the request and uploaded to fal storage. |
When an upload is required, the package uses fal's storage upload endpoint and substitutes the returned URL into the request.
Extending With New Models
To support a fal model the package does not ship a handler for, implement the IanRodrigues\FalAi\Image\ModelHandler interface:
namespace App\AiModels; use IanRodrigues\FalAi\Image\ModelHandler; use Illuminate\Support\Collection; use Laravel\Ai\Responses\Data\GeneratedImage; use Laravel\Ai\Responses\Data\Meta; use Laravel\Ai\Responses\Data\Usage; use Laravel\Ai\Responses\ImageResponse; class FluxPro implements ModelHandler { public function supports(string $model): bool { return $model === 'flux-pro' || $model === 'fal-ai/flux-pro'; } public function endpoint(string $model): string { return 'fal-ai/flux-pro'; } public function buildPayload(string $prompt, array $imageUrls, ?string $size, ?string $quality, array $requestOptions, array $providerConfig): array { return array_filter([ 'prompt' => $prompt, 'image_size' => $size, ...$requestOptions, ]); } public function parseResponse(array $json, string $model): ImageResponse { $images = Collection::make($json['images'] ?? []) ->map(fn (array $image) => new GeneratedImage( $image['_b64'] ?? $image['url'] ?? '', $image['content_type'] ?? 'image/png', )); return new ImageResponse($images, new Usage, new Meta('fal', $model)); } public function requiresAttachments(): bool { return false; } }
Register the handler in config/fal-ai.php:
'image' => [ 'models' => [ \IanRodrigues\FalAi\Image\NanoBananaTwoEdit::class, \App\AiModels\FluxPro::class, ], ],
Handlers are resolved in reverse registration order, so application-level handlers take precedence over the defaults the package ships.
When fal-ai.fetch_images is enabled, the gateway downloads each result URL before invoking parseResponse. The downloaded bytes are attached to each image entry under a _b64 key alongside the original url and content_type fields. Your handler should prefer _b64 when present, as shown above.
Events
The driver dispatches the SDK's own image events, so Event::fake and listener registration behave exactly as they do for the bundled providers:
use Laravel\Ai\Events\GeneratingImage; use Laravel\Ai\Events\ImageGenerated; Event::fake([GeneratingImage::class, ImageGenerated::class]); // ... perform a generation ... Event::assertDispatched(ImageGenerated::class);
Exceptions
All exceptions raised by the driver extend IanRodrigues\FalAi\Exceptions\FalException:
| Exception | Raised when |
|---|---|
FalRequestException |
fal returned a non-2xx response, or a terminal failure status. |
FalQueueTimeoutException |
A queued job did not complete before queue.timeout elapsed. |
UnknownModelException |
No handler is registered for the requested model. |
MissingAttachmentsException |
A model that requires attachments was invoked without any. |
You may catch FalException to handle all driver-originated failures uniformly.
Configuration Reference
The full list of options available in config/fal-ai.php:
| Key | Type | Default | Description |
|---|---|---|---|
key |
string | env('FAL_KEY') |
Your fal API key. |
base_url |
string | https://queue.fal.run |
fal's queue endpoint. |
sync_base_url |
string | https://fal.run |
fal's synchronous endpoint. Reserved for future use. |
storage_upload_url |
string | https://rest.alpha.fal.ai/storage/upload/initiate |
Initiates a two-step upload: returns a presigned URL the package then PUTs the bytes to. |
request_timeout |
int | 30 |
Per-request timeout in seconds. |
connect_timeout |
int | 10 |
Connection timeout in seconds. |
image_download_timeout |
int | 30 |
Timeout for downloading result images when fetch_images is enabled. |
queue.initial_delay_ms |
int | 1000 |
Delay before the first status poll. |
queue.max_delay_ms |
int | 5000 |
Upper bound on the exponential polling backoff. |
queue.timeout |
int | 120 |
Maximum total seconds to wait for a job to complete. |
fetch_images |
bool | true |
When true, result image URLs are downloaded and inlined as base64. Set false to keep URLs. |
image.models |
array | [NanoBananaTwoEdit::class] |
Ordered list of registered ModelHandler classes. |
Roadmap
The package's gateway implements fal's queue, polling, and storage-upload primitives in a capability-agnostic way. Audio, video, and transcription support will be added as fal expands its catalog under parallel namespaces (IanRodrigues\FalAi\Audio, IanRodrigues\FalAi\Video, and so on) following the same ModelHandler registry pattern documented above.
Testing
The package ships with a Pest test suite:
composer test
To run Pint against the codebase:
composer lint
License
laravel-fal-ai is open-sourced software licensed under the MIT license.