symfony/ai-postgres-store
Symfony AI Store integration for PostgreSQL using pgvector. Store and query embeddings with Postgres vector/halfvec types, distance operators, and indexing options. Links to pgvector docs plus Symfony AI contribution and issue resources.
Install Dependencies
Add to composer.json:
{
"require": {
"symfony/ai": "^0.8",
"symfony/ai-postgres-store": "^0.8",
"doctrine/dbal": "^3.6"
}
}
Run composer install.
Enable pgvector in PostgreSQL Execute in your PostgreSQL client:
CREATE EXTENSION vector;
Configure Laravel
Add to config/database.php under your PostgreSQL connection:
'postgres' => [
'schema' => 'public',
'pgvector' => [
'dimensions' => 1536, // Adjust based on your embedding model (e.g., OpenAI's ada-002)
],
],
First Use Case: Basic Vector Storage Create a service to interact with the store:
use Symfony\Component\AI\Store\PostgresStore;
use Doctrine\DBAL\Connection;
class VectorStoreService {
public function __construct(private PostgresStore $store) {}
public function addEmbedding(array $embedding, array $metadata) {
return $this->store->add($embedding, $metadata);
}
public function findNearest(array $embedding, int $limit = 5) {
return $this->store->nearest($embedding, $limit);
}
}
Bind the service in a Laravel provider:
$this->app->singleton(VectorStoreService::class, function ($app) {
$connection = $app->make(Connection::class);
return new VectorStoreService(
new PostgresStore($connection->getWrappedConnection(), 'public')
);
});
Create a Migration for Vector Table
Schema::connection('pgsql')->create('vector_embeddings', function (Blueprint $table) {
$table->id();
$table->vector('embedding', 1536); // Adjust dimensions
$table->json('metadata')->nullable();
$table->timestamps();
});
Run Migrations
php artisan migrate
// Add a vector embedding
$store->add([0.1, 0.2, ...], ['product_id' => 123, 'category' => 'electronics']);
// Find nearest neighbors
$neighbors = $store->nearest([0.15, 0.25, ...], 5);
// Remove a vector by ID
$store->remove(1);
// Combine vector similarity with metadata filtering
$results = $store->search(
[0.1, 0.2, ...],
10,
[
'filter' => [
'category' => 'electronics',
'price_gt' => 100,
],
'text_search' => 'wireless headphones' // PostgreSQL full-text search
]
);
// Add multiple embeddings in a batch
$batch = [
['embedding' => [0.1, 0.2, ...], 'metadata' => ['id' => 1]],
['embedding' => [0.3, 0.4, ...], 'metadata' => ['id' => 2]],
];
$store->addBatch($batch);
Use a custom accessor to fetch embeddings for Eloquent models:
// In your Product model
public function getEmbeddingAttribute() {
return $this->vectorEmbeddings()->value('embedding');
}
public function vectorEmbeddings() {
return $this->hasOne(VectorEmbedding::class);
}
Wrap the Symfony store in a Laravel service for easier testing and dependency injection:
class VectorStoreService {
public function __construct(private PostgresStore $store) {}
public function search(array $embedding, int $limit, array $filters = []) {
return $this->store->search($embedding, $limit, $filters);
}
}
For complex queries, use Doctrine DBAL to bypass Laravel’s Eloquent limitations:
use Doctrine\DBAL\Connection;
class VectorQueryBuilder {
public function __construct(private Connection $connection) {}
public function findSimilarProducts(array $embedding, int $limit) {
$sql = "
SELECT *, embedding <=> :embedding AS distance
FROM vector_embeddings
WHERE embedding <=> :embedding < 0.5
ORDER BY distance
LIMIT :limit
";
return $this->connection->fetchAllAssociative($sql, [
'embedding' => $embedding,
'limit' => $limit,
]);
}
}
Cache results of expensive vector searches using Laravel’s cache:
public function cachedNearest(array $embedding, int $limit, string $cacheKey) {
return Cache::remember($cacheKey, now()->addHours(1), function () use ($embedding, $limit) {
return $this->store->nearest($embedding, $limit);
});
}
Trigger actions when vectors are added or removed:
// In EventServiceProvider
protected $listen = [
VectorAdded::class => [
UpdateSearchIndex::class,
],
VectorRemoved::class => [
CleanupRecommendations::class,
],
];
ERROR: function vector(...) not found.CREATE EXTENSION vector; is run in PostgreSQL. Verify with:
SELECT * FROM pg_extension WHERE extname = 'vector';
ERROR: dimension mismatch when adding vectors.dimensions option in your Laravel config to match your embeddings (e.g., 1536 for OpenAI’s text-embedding-ada-002).vector columns natively.$results = DB::connection('pgsql')->select(
"SELECT *, embedding <=> :embedding AS distance FROM vector_embeddings ORDER BY distance LIMIT 10",
['embedding' => $yourEmbedding]
);
composer.json:
"require": {
"symfony/ai": "^0.8",
"symfony/ai-postgres-store": "^0.8",
"symfony/http-client": "^6.0",
"symfony/options-resolver": "^6.0"
}
category).halfvec type for 8-bit precision (halves memory usageHow can I help you explore Laravel packages today?