symfony/ai-vektor-store
Symfony AI Store integration for the Vektor vector database. Use Vektor as a vector store backend in Symfony AI apps to store, index, and query embeddings for retrieval and semantic search. Links to Vektor docs and Symfony AI contribution resources.
Install Dependencies:
composer require symfony/ai centamiv/vektor
Ensure your Laravel app uses PHP 8.2+ and has either Redis or PostgreSQL with pgvector installed.
Configure Vektor:
Add to config/services.php:
'vektor' => [
'dsn' => env('VEKTOR_DSN', 'redis://127.0.0.1:6379'),
'collection' => 'default',
],
Register the Store:
Create a service provider (app/Providers/VektorServiceProvider.php):
use Symfony\Component\AI\Store\VectorStoreInterface;
use Symfony\Component\AI\Store\VektorStore;
class VektorServiceProvider extends ServiceProvider {
public function register() {
$this->app->singleton(VectorStoreInterface::class, function ($app) {
return new VektorStore($app['config']['services.vektor']);
});
}
}
First Use Case: Embed and search a query in a Laravel controller:
use Symfony\Component\AI\Store\VectorStoreInterface;
class SearchController extends Controller {
public function __construct(private VectorStoreInterface $store) {}
public function semanticSearch(string $query) {
$embedding = $this->store->embed($query);
$results = $this->store->search($embedding, limit: 3);
return response()->json($results);
}
}
embed, search, upsert).config/services.php for environment variable overrides (e.g., VEKTOR_DSN).upsert for idempotent writes (avoid duplicates):
$this->store->upsert(
id: 'doc_123',
vector: $embedding,
payload: ['title' => 'Laravel AI', 'content' => '...']
);
// app/Observers/DocumentObserver.php
class DocumentObserver {
public function saved(Document $document) {
$embedding = $this->generateEmbedding($document->content);
app(VectorStoreInterface::class)->upsert(
id: 'doc_'.$document->id,
vector: $embedding,
payload: $document->toArray()
);
}
}
Engine to delegate vector queries:
// app/Scout/Engines/VektorEngine.php
use Symfony\Component\AI\Store\VectorStoreInterface;
class VektorEngine extends Engine {
public function search($query) {
$embedding = app(VectorStoreInterface::class)->embed($query);
$results = app(VectorStoreInterface::class)->search($embedding);
return $this->mapResults($results);
}
}
config/scout.php:
'engine' => \App\Scout\Engines\VektorEngine::class,
// app/Jobs/EmbedDocuments.php
use Symfony\Component\AI\Store\VectorStoreInterface;
class EmbedDocuments implements ShouldQueue {
public function handle() {
$documents = Document::all();
$store = app(VectorStoreInterface::class);
foreach ($documents as $doc) {
$store->upsert('doc_'.$doc->id, $this->embed($doc->content), $doc->toArray());
}
}
}
dispatch() or dispatchSync().$query = "Explain Laravel AI";
$embedding = $this->store->embed($query);
$context = $this->store->search($embedding, limit: 2)->map(fn($r) => $r['payload']['content']);
$prompt = "Context: ".implode("\n", $context)."\nQuestion: ".$query;
$response = $llm->complete($prompt);
.env:
VEKTOR_DSN=postgres://user:pass@localhost/vektor_db
payload metadata for Laravel-specific fields (e.g., created_at):
$this->store->upsert('doc_1', $vector, [
'title' => 'Post',
'user_id' => auth()->id(),
'published_at' => now()->toDateTimeString(),
]);
try-catch:
try {
$results = $this->store->search($embedding);
} catch (\RuntimeException $e) {
Log::error("Vektor search failed: ".$e->getMessage());
return response()->json(['error' => 'Service unavailable'], 503);
}
Redis vs. PostgreSQL Conflicts:
predis/predis may conflict with Vektor’s Redis client.$client = new \Vektor\Client(new \Predis\Client($dsn));
return new VektorStore($client, $collection);
Vector Dimension Mismatch:
text-embedding-ada-002 vs. all-MiniLM-L6-v2) have incompatible dimensions.$embedding = array_map('floatval', $rawEmbedding); // Ensure float[]
Laravel Caching Interference:
VEKTOR_DSN=redis://127.0.0.1:6379/1 # Database 1 for Vektor
PostgreSQL pgvector Setup:
pgvector extension or incorrect schema.CREATE EXTENSION vector;
CREATE TABLE vectors (id TEXT PRIMARY KEY, embedding vector(1536));
Symfony Component Collisions:
symfony/http-client may conflict with Symfony AI’s dependencies.Http facade or alias Symfony’s client:
$this->app->alias(\Symfony\Component\HttpClient\HttpClient::class, \Illuminate\Support\Facades\Http::class);
config/services.php:
'vektor' => [
'dsn' => env('VEKTOR_DSN'),
'debug' => env('APP_DEBUG', false), // Enable Vektor logs in debug mode
],
// app/Providers/AppServiceProvider.php
use Barryvdh\Debugbar\Debugbar;
public function boot() {
Debugbar::addCollector(function() {
return [
'Vektor' => [
'Collections' => app(\Vektor\Client::class)->listCollections(),
],
];
});
}
Custom Payload Serialization: Override how Laravel models are serialized to Vektor payloads:
// app/Services/VektorPayloadSerializer.php
class VektorPayloadSerializer {
public function serialize($model) {
return array_merge(
$model->toArray(),
['serialized_at' => now()->toIso8601String()]
);
}
}
Inject into the store:
$store = new VektorStore($client, $collection, $serializer);
Laravel Facade: Create a facade for cleaner syntax:
// app/Facades/Vektor.php
use Illuminate\Support\Facades\Facade;
class V
How can I help you explore Laravel packages today?