symfony/ai-voyage-platform
Symfony AI bridge for Voyage AI: integrate Voyage text and multimodal embeddings into Symfony apps. Provides a platform connector to call Voyage APIs and use embedding models for semantic search, RAG, and vector workflows.
Install Dependencies:
composer require symfony/ai symfony/ai-voyage-platform symfony/http-client
For Laravel-only stacks, add a Symfony-compatible container (e.g., php-di/php-di).
Configure API Key:
Add Voyage’s API key to config/services.php:
'voyage' => [
'api_key' => env('VOYAGE_API_KEY'),
],
First Use Case: Generate embeddings for a text string:
use Symfony\AI\Voyage\VoyageEmbeddingModel;
$model = new VoyageEmbeddingModel('text-embedding-v1');
$embeddings = $model->generate(['Your text here']);
Laravel Integration: Bind the model in a service provider:
$this->app->singleton(VoyageEmbeddingModel::class, function ($app) {
return new VoyageEmbeddingModel('text-embedding-v1', $app['voyage.client']);
});
text-embedding-v1, multimodal-embedding-v1).// Laravel Service
public function generateEmbeddings(string $text): array
{
$model = app(VoyageEmbeddingModel::class);
return $model->generate([$text]);
}
// Route models based on configuration
$modelName = config('ai.voyage.model');
$model = new VoyageEmbeddingModel($modelName);
// Process multiple texts in one API call
$embeddings = $model->generate([
'Text 1',
'Text 2',
// ...
]);
// For images/text combinations
$model = new VoyageEmbeddingModel('multimodal-embedding-v1');
$embeddings = $model->generate([
['text' => 'Describe this image:', 'image_url' => 'https://example.com/image.jpg'],
]);
// Store embeddings in Meilisearch/TypeORM
$embeddings = $this->generateEmbeddings($text);
$vectorDB->addDocument($text, $embeddings);
$queryEmbeddings = $this->generateEmbeddings($userQuery);
$results = $vectorDB->search($queryEmbeddings, limit: 5);
$userEmbedding = $this->generateEmbeddings($userProfile);
$itemEmbeddings = $this->generateEmbeddings($items);
$scores = $this->computeSimilarity($userEmbedding, $itemEmbeddings);
Caching: Cache embeddings in Redis to reduce API calls:
$cacheKey = "embedding:{$text}";
$embeddings = cache()->remember($cacheKey, now()->addHours(1), function() use ($text) {
return $this->generateEmbeddings($text);
});
Error Handling: Wrap API calls in try-catch:
try {
$embeddings = $model->generate([$text]);
} catch (\Symfony\Contracts\HttpClient\Exception\ClientException $e) {
log::error("Voyage API error: " . $e->getMessage());
return fallbackEmbeddings();
}
Laravel Facade:
// app/Facades/VoyageFacade.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class VoyageFacade extends Facade {
protected static function getFacadeAccessor() { return 'voyage.embedding'; }
}
Bind in AppServiceProvider:
$this->app->bind('voyage.embedding', function ($app) {
return new VoyageEmbeddingModel(config('ai.voyage.model'), $app['voyage.client']);
});
Usage:
$embeddings = VoyageFacade::generate(['text']);
Symfony DI Conflicts:
php-di/php-di as a neutral container or wrap Symfony services in Laravel bindings.API Rate Limits:
use Symfony\Contracts\HttpClient\HttpClientInterface;
$client = HttpClient::create([
'timeout' => 30,
'base_uri' => 'https://api.voyage.ai',
'options' => [
'headers' => ['Authorization' => 'Bearer ' . config('services.voyage.api_key')],
'retries' => 3,
],
]);
Model Name Mismatches:
text-embedding-v1) differ from OpenAI’s. Double-check the Voyage docs.Multimodal Input Format:
// Correct
$model->generate([['text' => 'Describe this:', 'image_url' => '...']]);
// Incorrect (will fail)
$model->generate(['Describe this:', 'image_url']);
Laravel’s env() vs. Symfony Config:
Container uses parameter_bag, while Laravel uses env(). Bridge them:
$container->setParameter('voyage.api_key', env('VOYAGE_API_KEY'));
Enable Symfony Debug Mode:
$client = HttpClient::create([
'debug' => true,
]);
Check logs for HTTP errors.
Validate Inputs:
$text = trim($text);
if (empty($text)) throw new \InvalidArgumentException("Text cannot be empty");
Check API Responses:
try {
$response = $client->request('POST', '/v1/embeddings', [
'json' => ['input' => [$text]],
]);
$data = $response->toArray();
if (isset($data['error'])) {
throw new \RuntimeException($data['error']['message']);
}
} catch (\Throwable $e) {
log::error("Voyage API failed: " . $e->getMessage());
}
Dynamic Model Routing:
Configure in config/ai.php:
'voyage' => [
'model' => env('VOYAGE_MODEL', 'text-embedding-v1'),
'timeout' => 30,
],
HTTP Client Overrides: Customize the client in a service provider:
$this->app->singleton(HttpClientInterface::class, function ($app) {
return HttpClient::create([
'base_uri' => 'https://api.voyage.ai',
'auth_bearer' => config('services.voyage.api_key'),
'timeout' => config('ai.voyage.timeout'),
]);
});
Custom Providers:
Extend the Provider abstraction to support multiple services:
namespace App\AI\Providers;
use Symfony\AI\Provider\EmbeddingProviderInterface;
class CustomVoyageProvider implements EmbeddingProviderInterface {
public function generate(array $inputs): array {
// Custom logic (e.g., retry, caching)
return $model->generate($inputs);
}
}
Laravel Events: Dispatch events for embedding generation:
event(new EmbeddingsGenerated($text, $embeddings));
Model Factories: Create a factory for different embedding models
How can I help you explore Laravel packages today?