symfony/ai-supabase-store
Supabase vector store integration for Symfony AI Store using PostgreSQL pgvector. Connect your Symfony AI apps to Supabase vector columns and the match_documents RPC for similarity search, with links to Supabase docs and Symfony AI contribution/resources.
Install the Package Add the package to your Laravel project via Composer:
composer require symfony/ai-supabase-store
For Laravel, ensure you have the Symfony Bridge installed (if not using Symfony directly):
composer require symfony/http-client symfony/options-resolver
Configure Supabase Connection
Publish the package’s configuration (if available) or manually set up Supabase credentials in .env:
SUPABASE_URL=your-supabase-url
SUPABASE_KEY=your-supabase-key
SUPABASE_VECTOR_TABLE=your_vector_table
Set Up Supabase Table
Create a table in Supabase with a vector column (pgvector extension required):
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536), -- Adjust dimension to your embedding size
metadata JSONB
);
Enable the match_documents RPC function (refer to Supabase’s pgvector guide).
First Use Case: Store and Query Embeddings Use the store in a Laravel service or controller:
use Symfony\Component\AI\Store\StoreInterface;
use Symfony\Component\AI\Store\SupabaseStore;
// Initialize the store (Laravel-specific adaptation)
$store = new SupabaseStore(
new \Symfony\Component\AI\Store\SupabaseClient(
env('SUPABASE_URL'),
env('SUPABASE_KEY')
),
env('SUPABASE_VECTOR_TABLE')
);
// Store an embedding
$store->add([
'id' => 'doc1',
'embedding' => $embeddingArray, // Your 1536-dim vector
'metadata' => ['source' => 'user_upload', 'tags' => ['ai', 'laravel']],
]);
// Query similar embeddings
$results = $store->query($queryEmbedding, limit: 5, filter: ['source' => 'user_upload']);
RAG Pipeline Integration
Combine with Laravel AI tools (e.g., symfonycasts/laravel-ai) for Retrieval-Augmented Generation:
// Retrieve context for LLM prompt
$context = $store->query($userQueryEmbedding, limit: 3);
$prompt = "Answer based on: " . implode("\n", $context) . "\n\nUser: {$userQuery}";
// Use Laravel AI to generate response
$response = app(\SymfonyCast\Laravel\Ai\Services\OpenAI::class)->complete($prompt);
Semantic Search Replace keyword search with vector similarity:
// Generate embedding for search query (e.g., using `symfonycasts/laravel-ai`)
$queryEmbedding = app(\SymfonyCast\Laravel\Ai\Services\OpenAI::class)->embed("Laravel AI packages");
// Fetch top 5 similar documents
$results = $store->query($queryEmbedding, limit: 5);
Hybrid Filtering Combine metadata filters with vector search:
$results = $store->query(
$embedding,
filter: ['tags' => ['ai'], 'source' => 'documentation'],
limit: 10
);
Laravel Service Container Bind the store to Laravel’s container for dependency injection:
// In AppServiceProvider
$this->app->singleton(StoreInterface::class, function ($app) {
return new SupabaseStore(
new \Symfony\Component\AI\Store\SupabaseClient(
env('SUPABASE_URL'),
env('SUPABASE_KEY')
),
env('SUPABASE_VECTOR_TABLE')
);
});
Batch Operations
Use Laravel’s DB::transaction for atomic batch inserts:
DB::transaction(function () use ($store, $embeddings) {
foreach ($embeddings as $embedding) {
$store->add($embedding);
}
});
Caching Layer Cache frequent queries (e.g., with Laravel Cache):
$cacheKey = "search:{$userQueryHash}";
$results = cache()->remember($cacheKey, now()->addMinutes(5), function () use ($store, $embedding) {
return $store->query($embedding, limit: 5);
});
Event-Driven Updates Trigger updates via Laravel events (e.g., after document creation):
// In DocumentCreated event listener
$store->add([
'id' => $document->id,
'embedding' => $document->embedding,
'metadata' => $document->metadata,
]);
Supabase RPC Limitations
match_documents RPC has default limits (e.g., 100MB payload). Exceeding these may fail silently or return partial results.Embedding Dimension Mismatch
vector(N) column. Mismatches cause errors or corrupt data.DB_LOG_QUERIES=true
Filter Syntax Quirks
filter parameter uses SQL-like syntax but is passed as a JSON object. Nested filters may not work as expected.['tags' => ['ai']] instead of nested arrays.Connection Timeouts
ini_set('default_socket_timeout', 60);
Laravel-Symfony DI Conflicts
StoreInterface may conflict with Laravel’s autowiring. Explicitly type-hint the interface:
public function __construct(private StoreInterface $store) {}
Enable Supabase Logging Add this to your Supabase client initialization:
$client = new \Symfony\Component\AI\Store\SupabaseClient(
env('SUPABASE_URL'),
env('SUPABASE_KEY'),
['log' => true] // Enable RPC logging
);
Check RPC Execution
Verify the match_documents RPC is working in Supabase’s SQL editor:
SELECT match_documents(
'documents',
'[1.2, 3.4, ...]', -- Your embedding vector
'cosine_distance',
'id, content, metadata',
5,
'{}' -- Filters as JSON
);
Laravel Query Logging Enable logging for Supabase queries:
\DB::enableQueryLog();
$results = $store->query($embedding);
\Log::debug(\DB::getQueryLog());
Custom Distance Metrics
Extend the store to support non-default distance metrics (e.g., euclidean):
class CustomSupabaseStore extends SupabaseStore {
public function query($embedding, array $options = []) {
$options['distance'] = $options['distance'] ?? 'euclidean';
return parent::query($embedding, $options);
}
}
Fallback to REST API Override RPC calls for better error handling:
class ResilientSupabaseStore extends SupabaseStore {
protected function callRpc($rpcName, array $args) {
try {
return parent::callRpc($rpcName, $args);
} catch (\Exception $e) {
// Fallback to REST API
return $this->callSupabaseApi($rpcName, $args);
}
}
}
Laravel Model Integration Create a model wrapper for seamless ORM usage:
class Document extends Model {
public function embeddings() {
return $this->hasMany(DocumentEmbedding::class);
}
public function addToVectorStore() {
$store = app(StoreInterface::class);
$store->add([
'id' => $this->id,
'embedding' => $this->embeddings->first()->vector,
'metadata' => $this->toArray(),
]);
}
}
Async Processing Use Laravel Queues for non-blocking operations:
class AddEmbed
How can I help you explore Laravel packages today?