cjmellor / fal-ai-laravel
A Laravel SDK for Fal.ai
Installs: 347
Dependents: 0
Suggesters: 0
Security: 0
Stars: 5
Watchers: 0
Forks: 1
Open Issues: 0
pkg:composer/cjmellor/fal-ai-laravel
Requires
- php: ^8.4
- hosmelq/sse-saloon: ^0.1.0
- illuminate/support: ^12.0
- saloonphp/saloon: ^3.0
Requires (Dev)
- driftingly/rector-laravel: ^2.0
- larastan/larastan: ^3.0
- laravel/pail: ^1.2
- laravel/pint: ^1.0
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-arch: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/phpstan: ^2.0
- rector/rector: ^2.0
- saloonphp/laravel-plugin: ^3.6
This package is auto-updated.
Last update: 2026-01-12 18:51:39 UTC
README
Fal.ai Laravel Package
A Laravel package for integrating with the Fal.ai API, providing a fluent interface for AI model execution with built-in webhook support, streaming, and Platform APIs.
Note
Multi-provider support: This package also includes a Replicate driver for Replicate.com.
Features
- Fluent API for building model requests
- Queue and Sync execution modes
- Real-time streaming with Server-Sent Events (SSE)
- Webhook support with ED25519 signature verification
- Platform APIs for pricing, usage, analytics, and cost estimation
- Multi-provider architecture
Warning
Upgrading from v1.x? Version 2.0 is a complete architectural rewrite with breaking changes. The configuration structure, API methods, and class namespaces have all changed. You must follow the Upgrade Guide to migrate from v1.x to v2.x.
Installation
Install via Composer:
composer require cjmellor/fal-ai-laravel
Publish the configuration:
php artisan vendor:publish --tag=fal-ai-config
Add your API key to .env:
FAL_API_KEY=your_fal_api_key
Basic Usage
use Cjmellor\FalAi\Facades\FalAi; $response = FalAi::model('fal-ai/flux/schnell') ->prompt('A beautiful sunset over mountains') ->imageSize('landscape_4_3') ->run(); $requestId = $response->requestId;
Using a Default Model
Set a default model in your config to omit the model ID:
// config/fal-ai.php 'drivers' => [ 'fal' => [ 'default_model' => 'fal-ai/flux/schnell', ], ], // Usage $response = FalAi::model() ->prompt('A cozy cabin in the woods') ->run();
Queue vs Sync Modes
Queue Mode (Default)
Requests are processed asynchronously. Use webhooks or polling to get results.
$response = FalAi::model('fal-ai/flux/dev') ->prompt('A detailed portrait') ->queue() // Optional - queue is the default ->run(); // Returns immediately with request_id $requestId = $response->requestId;
Best for: Complex generations, batch processing, production workloads.
Sync Mode
Requests block until complete and return the result directly.
$response = FalAi::model('fal-ai/flux/schnell') ->prompt('A quick sketch') ->sync() ->run(); // Returns the complete result $images = $response->json()['images'];
Best for: Simple generations, interactive applications, development.
Warning
Sync mode may timeout for complex requests.
Polling Status & Results
For queued requests, poll for status and retrieve results:
// Check status $status = FalAi::driver('fal')->status($requestId, 'fal-ai/flux/dev'); if ($status->json()['status'] === 'COMPLETED') { // Get the result $result = FalAi::driver('fal')->result($requestId, 'fal-ai/flux/dev'); $images = $result->json()['images']; } // Cancel a queued request FalAi::driver('fal')->cancel($requestId, 'fal-ai/flux/dev');
Response Helpers
$response = FalAi::model('fal-ai/flux/schnell') ->prompt('A fox in a forest') ->run(); $response->requestId; // Request ID $response->statusUrl; // URL to check status $response->responseUrl; // URL to get result $response->cancelUrl; // URL to cancel
Streaming
Stream responses in real-time using Server-Sent Events:
$stream = FalAi::model('fal-ai/flux/schnell') ->prompt('A dancing robot') ->stream(); // Process the stream response $stream->getResponse();
Note
Not all models support streaming. Check model documentation.
Webhook Support
Setting a Webhook URL
Adding a webhook automatically uses queue mode:
$response = FalAi::model('fal-ai/flux/schnell') ->prompt('A beautiful landscape') ->withWebhook('https://yourapp.com/webhooks/fal') ->run();
Important
Webhook URLs must use HTTPS and be publicly accessible.
Built-in Webhook Route
The package provides a pre-configured route at /webhooks/fal:
$response = FalAi::model('fal-ai/flux/schnell') ->withWebhook(url('/webhooks/fal')) ->prompt('Your prompt') ->run();
Custom Webhook Endpoint
Create your own endpoint with the verification middleware:
use Cjmellor\FalAi\Middleware\VerifyFalWebhook; Route::post('/webhooks/fal-custom', function (Request $request) { $payload = $request->json()->all(); if ($payload['status'] === 'OK') { $images = $payload['data']['images']; // Process images } return response()->json(['status' => 'processed']); })->middleware(VerifyFalWebhook::class);
Manual Verification
use Cjmellor\FalAi\Services\WebhookVerifier; use Cjmellor\FalAi\Exceptions\WebhookVerificationException; $verifier = new WebhookVerifier(); try { $verifier->verify($request); // Webhook is valid } catch (WebhookVerificationException $e) { // Verification failed }
Webhook Payload
Success:
{
"request_id": "req_123",
"status": "OK",
"data": {
"images": [{"url": "https://...", "width": 1024, "height": 768}],
"seed": 12345
}
}
Error:
{
"request_id": "req_123",
"status": "ERROR",
"error": {"type": "ValidationError", "message": "Invalid prompt"}
}
Platform APIs
Access Fal.ai Platform APIs for pricing, usage, and analytics.
Pricing
$pricing = FalAi::platform() ->pricing() ->forEndpoints(['fal-ai/flux/dev', 'fal-ai/flux/schnell']) ->get(); $unitPrice = $pricing->getUnitPriceFor('fal-ai/flux/dev');
Cost Estimation
// Estimate by API calls $estimate = FalAi::platform() ->estimateCost() ->historicalApiPrice() ->endpoint('fal-ai/flux/dev', callQuantity: 100) ->estimate(); echo $estimate->totalCost; // e.g., 2.50 // Estimate by billing units $estimate = FalAi::platform() ->estimateCost() ->unitPrice() ->endpoint('fal-ai/flux/dev', unitQuantity: 100) ->estimate();
Usage
$usage = FalAi::platform() ->usage() ->forEndpoint('fal-ai/flux/dev') ->between('2025-01-01T00:00:00Z', '2025-01-31T23:59:59Z') ->timeframe('day') ->withSummary() ->get(); $totalCost = $usage->getTotalCost(); $totalQuantity = $usage->getTotalQuantity();
Analytics
$analytics = FalAi::platform() ->analytics() ->forEndpoint('fal-ai/flux/dev') ->between('2025-01-01', '2025-01-31') ->withAllMetrics() ->get(); $totalRequests = $analytics->getTotalRequests(); $successRate = $analytics->getSuccessRate();
Delete Request Payloads
Remove stored input/output data for a request:
$response = FalAi::platform() ->deleteRequestPayloads('req_123456789') ->delete(); if (!$response->hasErrors()) { echo "Deleted successfully"; }
Fluent API
Dynamic Methods
Method names are converted from camelCase to snake_case:
FalAi::model('fal-ai/flux/schnell') ->prompt('A sunset') // prompt ->imageSize('1024x1024') // image_size ->numInferenceSteps(50) // num_inference_steps ->guidanceScale(7.5) // guidance_scale ->negativePrompt('blurry') // negative_prompt ->numImages(2) // num_images ->seed(12345) // seed ->run();
Bulk Data
$response = FalAi::model('fal-ai/flux/schnell') ->with([ 'prompt' => 'A landscape', 'image_size' => '1024x1024', 'num_images' => 2, ]) ->run();
Immutable Methods
Create new instances without modifying the original:
$base = FalAi::model('fal-ai/flux/schnell') ->imageSize('1024x1024') ->numImages(1); $request1 = $base->promptImmutable('A dragon'); $request2 = $base->promptImmutable('A unicorn'); // $base is unchanged
Conditional Methods
$response = FalAi::model('fal-ai/flux/schnell') ->prompt('A sunset') ->when($highQuality, fn($req) => $req->numInferenceSteps(100)) ->unless($skipSeed, fn($req) => $req->seed(42)) ->run();
Configuration
// config/fal-ai.php return [ 'default' => env('AI_DRIVER', 'fal'), 'drivers' => [ 'fal' => [ 'api_key' => env('FAL_API_KEY'), 'base_url' => env('FAL_BASE_URL', 'https://queue.fal.run'), 'sync_url' => env('FAL_SYNC_URL', 'https://fal.run'), 'platform_base_url' => env('FAL_PLATFORM_URL', 'https://api.fal.ai'), 'default_model' => env('FAL_DEFAULT_MODEL'), 'webhook' => [ 'jwks_cache_ttl' => 86400, 'timestamp_tolerance' => 300, 'verification_timeout' => 10, ], ], ], ];
Error Handling
use Saloon\Exceptions\Request\RequestException; use Cjmellor\FalAi\Exceptions\WebhookVerificationException; try { $response = FalAi::model('fal-ai/flux/schnell') ->prompt('A sunset') ->run(); } catch (RequestException $e) { $status = $e->getResponse()->status(); $body = $e->getResponse()->json(); } catch (WebhookVerificationException $e) { // Webhook verification failed }
Replicate Driver
This package includes a driver for Replicate.com.
Setup
Add your Replicate API key to .env:
REPLICATE_API_KEY=your_replicate_api_key
Usage
use Cjmellor\FalAi\Facades\FalAi as Ai; $response = Ai::driver('replicate') ->model('stability-ai/sdxl') ->prompt('A majestic dragon') ->numOutputs(2) ->run();
Model Format
Replicate models can use two formats:
Official Models:
// Format: owner/model ->model('stability-ai/sdxl')
Custom Models (with specific version):
// Format: owner/model:version ->model('your-username/my-custom-model:da77bc59ee60...')
Note
The :version suffix is only required for custom models. Official Replicate models use just owner/model.
Checking Status
Replicate uses polling for status:
$response = Ai::driver('replicate') ->model('stability-ai/sdxl') ->prompt('A landscape') ->run(); // Poll for completion $status = Ai::driver('replicate')->status($response->id); // Status helpers $status->isRunning(); // starting or processing $status->isSucceeded(); // completed successfully $status->isFailed(); // failed $status->isCanceled(); // canceled $status->isTerminal(); // any final state // Get result when complete if ($status->isSucceeded()) { $output = $status->output; }
Key Differences from Fal
| Feature | Fal.ai | Replicate |
|---|---|---|
| Queue/Sync modes | Yes | No (always async) |
| Streaming | Yes | No (use polling) |
| Platform APIs | Yes | No |
| Webhooks | Yes | Yes |
Replicate Webhooks
$response = Ai::driver('replicate') ->model('stability-ai/sdxl') ->prompt('A sunset') ->withWebhook('https://yourapp.com/webhooks/replicate') ->run();
Built-in route available at /webhooks/replicate.
Configure webhook verification in .env:
REPLICATE_WEBHOOK_SECRET=your_webhook_secret
Testing
composer test
Security
Important
Webhook Verification:
- Fal.ai: ED25519 signatures with JWKS
- Replicate: HMAC-SHA256 signatures
Always use HTTPS for webhook URLs and keep API keys secure.
Contributing
Contributions are welcome! Please submit a Pull Request.
License
MIT License. See LICENSE for details.