Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ai Open Router Platform Laravel Package

symfony/ai-open-router-platform

Symfony AI bridge for the OpenRouter platform. Provides integration for chat completions (including streaming), model listing, and rerank requests via OpenRouter’s API, enabling Symfony apps to access multiple LLM providers through a single gateway.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Package:

    composer require symfony/ai-open-router-platform
    

    Optional: If using Symfony’s AiClient abstractions, also install:

    composer require symfony/ai
    
  2. Configure API Key: Add to .env:

    OPENROUTER_API_KEY=your_api_key_here
    

    Load in config/services.php:

    'openrouter' => [
        'api_key' => env('OPENROUTER_API_KEY'),
        'default_model' => 'openrouter/free', // or 'openrouter/mistral-latest'
    ],
    
  3. First Use Case: Chat Completion

    use Symfony\Component\AI\OpenRouter\Client;
    use Symfony\Component\AI\Message\ChatMessage;
    
    $client = new Client(
        new \Symfony\Component\HttpClient\HttpClient(),
        config('services.openrouter.api_key')
    );
    
    $response = $client->chat([
        new ChatMessage('Hello, how are you?', 'user'),
    ]);
    
    echo $response->getContent();
    

Where to Look First


Implementation Patterns

Core Workflows

1. Chat Completions (Synchronous)

// Using Symfony Client
$client = app(Client::class);
$response = $client->chat([
    new ChatMessage('Explain Laravel dependency injection', 'user'),
], config('services.openrouter.default_model'));

// Using Laravel HTTP (direct)
$response = Http::withHeaders([
    'Authorization' => 'Bearer ' . config('services.openrouter.api_key'),
])->post('https://openrouter.ai/api/v1/chat/completions', [
    'model' => config('services.openrouter.default_model'),
    'messages' => [['role' => 'user', 'content' => 'Explain Laravel DI']],
]);

2. Streaming Responses

// Symfony Client (streaming)
$stream = $client->streamChat([
    new ChatMessage('Stream this response', 'user'),
]);

foreach ($stream as $chunk) {
    echo $chunk->getContent() . "\n";
}

// Laravel + Symfony Streaming (custom event handler)
$stream = $client->streamChat([new ChatMessage('Stream to Laravel')]);
$stream->onChunk(function ($chunk) {
    event(new OpenRouterChunkReceived($chunk->getContent()));
});

3. Model Routing (Multi-Provider)

// Define providers in config/services.php
'openrouter' => [
    'providers' => [
        'free' => 'openrouter/free',
        'pro' => 'openrouter/mistral-latest',
    ],
    'default' => 'free',
],

// Dynamic routing in service
$model = config('services.openrouter.providers.' . request()->input('model_type', config('services.openrouter.default')));
$response = $client->chat([new ChatMessage('Dynamic model!')], $model);

4. Reranking for Search

$client = app(Client::class);
$results = $client->rerank(
    'Find the best match for "Laravel AI"',
    ['Laravel is a PHP framework', 'AI is artificial intelligence'],
    config('services.openrouter.default_model')
);

Integration Tips

Laravel Service Container

Register the client in AppServiceProvider:

public function register()
{
    $this->app->singleton(Client::class, function ($app) {
        return new Client(
            new \Symfony\Component\HttpClient\HttpClient(),
            $app['config']['services.openrouter.api_key']
        );
    });
}

Facade for Ergonomics

// app/Facades/OpenRouter.php
namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class OpenRouter extends Facade
{
    protected static function getFacadeAccessor() { return 'openrouter.client'; }
}

// Register in AppServiceProvider
$this->app->bind('openrouter.client', function ($app) {
    return new \Symfony\Component\AI\OpenRouter\Client(
        new \Symfony\Component\HttpClient\HttpClient(),
        $app['config']['services.openrouter.api_key']
    );
});

Usage:

use App\Facades\OpenRouter;

$response = OpenRouter::chat([new ChatMessage('Hello')]);

Error Handling

Wrap calls in a try-catch for OpenRouter-specific errors:

try {
    $response = $client->chat([new ChatMessage('Test')]);
} catch (\Symfony\Component\AI\Exception\AiException $e) {
    Log::error('OpenRouter error: ' . $e->getMessage());
    return response()->json(['error' => 'AI service unavailable'], 503);
}

Rate Limiting

Use Laravel’s throttle middleware for API calls:

Route::middleware(['throttle:10,1'])->group(function () {
    Route::post('/ai/chat', [AIController::class, 'chat']);
});

Gotchas and Tips

Pitfalls

  1. Symfony Dependency Overhead

    • Issue: Pulling in symfony/ai adds ~50KB and tightens coupling to Symfony.
    • Fix: Use only symfony/ai-open-router-platform and Laravel’s HTTP client.
  2. Streaming Quirks

    • Issue: Symfony’s StreamingResponse doesn’t natively integrate with Laravel’s event system.
    • Fix: Use a custom event listener:
      $stream = $client->streamChat([new ChatMessage('Stream')]);
      $stream->onChunk(function ($chunk) {
          event(new AiStreamChunk($chunk->getContent()));
      });
      
  3. Model Availability

    • Issue: OpenRouter’s free-tier models (openrouter/free) may have usage limits.
    • Fix: Check OpenRouter’s model docs and implement fallback logic:
      $model = config('services.openrouter.default_model');
      if (!OpenRouter::modelExists($model)) {
          $model = 'openrouter/mistral-latest';
      }
      
  4. Authentication Leaks

    • Issue: Hardcoding API keys in config or client initialization.
    • Fix: Use Laravel’s .env and validate in bootstrap/app.php:
      if (!env('OPENROUTER_API_KEY')) {
          throw new \RuntimeException('OpenRouter API key not set.');
      }
      
  5. Response Parsing

    • Issue: OpenRouter’s API may return non-standard JSON (e.g., nested choices).
    • Fix: Normalize responses in a service layer:
      $response = $client->chat([new ChatMessage('Parse this')]);
      $content = collect($response->getContent())->first()['message']['content'];
      

Debugging Tips

  1. Enable HTTP Logging Configure Symfony’s HTTP client to log requests:

    $client = new Client(
        \Symfony\Component\HttpClient\HttpClient::create([
            'debug' => true,
        ]),
        config('services.openrouter.api_key')
    );
    
  2. Validate API Key Test connectivity with a simple request:

    $response = Http::get('https://openrouter.ai/api/v1/models', [
        'headers' => ['Authorization' => 'Bearer ' . config('services.openrouter.api_key')],
    ]);
    
  3. Token Limits OpenRouter enforces token limits per request. Validate input:

    $messages = [new ChatMessage('Long message...')];
    $tokenCount = $client->estimateTokenCount($messages);
    if ($tokenCount > 4096) { // OpenRouter’s max for free tier
        throw new \RuntimeException('Message exceeds token limit.');
    }
    

Extension Points

  1. Custom Providers Extend the Provider abstraction to support other APIs:

    namespace App\Providers;
    
    use Symfony\Component\AI\Provider\ProviderInterface;
    
    class CustomProvider implements ProviderInterface
    {
        public function getModel(string $modelName): string
        {
            return match ($modelName) {
                'custom' => 'openrouter/mistral-latest',
                default => $modelName,
            };
        }
    }
    
  2. Laravel Events for Streaming Dispatch events for each chunk:

    $stream = $client->streamChat([new ChatMessage('Event-driven')]);
    $stream->onChunk(function ($chunk) {
        event(new AiStreamEvent($chunk->getContent()));
    });
    
  3. Caching Responses Cache frequent queries (e.g., model listings):

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor