embedding maintained by x-laravel
Embedding
A Laravel package that automatically generates and stores vector embeddings for Eloquent models using laravel/ai.
How It Works
- Add the
Embeddabletrait to any model — embeddings are generated automatically on save - Only the fields you specify trigger embedding generation
- Embedding generation is handled by a queued job — no blocking
- Similarity search is driver-based: PHP (default), PostgreSQL (pgvector), or MySQL 9 native VECTOR
Requirements
- PHP ^8.3
- Laravel ^12.0 | ^13.0
laravel/ai ^0.6
Installation
composer require x-laravel/embedding
Run the migration:
php artisan migrate
Setup
1. Model
Add the Embeddable trait and implement HasEmbeddings:
use Illuminate\Database\Eloquent\Model;
use XLaravel\Embedding\Concerns\Embeddable;
use XLaravel\Embedding\Contracts\HasEmbeddings;
class Post extends Model implements HasEmbeddings
{
use Embeddable;
protected array $embeddable = ['title', 'body'];
public function toEmbeddingText(): string
{
return $this->title . ' ' . $this->body;
}
}
toEmbeddingText() defines what text is sent to the embedding model. $embeddable defines which fields, when changed, trigger a new embedding.
2. Defining Which Fields Trigger Embedding
Option 1 — $embeddable property:
protected array $embeddable = ['title', 'body']; // [] = never, ['*'] = always
Option 2 — #[EmbedOn] PHP attribute:
use XLaravel\Embedding\Attributes\EmbedOn;
#[EmbedOn('title', 'body')]
class Post extends Model implements HasEmbeddings { ... }
Both approaches merge — you can use either or both.
Usage
Generating embeddings
$post->embed(); // dispatch async job
$post->embedSync(); // generate synchronously
$post->hasEmbedding(): bool
$post->embedding // MorphOne → Embedding model
Suppressing embedding generation
Post::withoutEmbedding(fn() => Post::create([...])); // suppress for closure
Post::disableEmbedding(); // suppress globally
Post::enableEmbedding();
Similarity search
// Find models most similar to a query vector
Post::similarTo($vector, limit: 10);
// Filter by minimum similarity score and Eloquent constraints
Post::similarTo($vector, limit: 10, threshold: 0.8, where: fn($q) => $q->where('status', 'published'));
// Auto-embed a text query, then search
Post::similarToText('web framework', limit: 10);
// Rank an existing collection by similarity to a text or vector
Post::rankByRelevance($posts, 'web framework');
Post::rankByRelevance($posts, $vector);
// Compare two models or a model with a vector
$post->similarityTo($otherPost): float
$post->similarityTo($vector): float
// Find the most similar records to this model, excluding itself
$post->mostSimilar(limit: 5);
All similarity methods set a similarity_score attribute (float) on each returned model.
threshold defaults to 0.0 — pass a value between 0.0 and 1.0 to filter low-scoring results.
Similarity Drivers
The driver is selected automatically based on the database connection:
| Driver | Condition | Computation |
|---|---|---|
php |
Default (SQLite, MySQL 8, etc.) | PHP-side cosine similarity |
pgsql |
PostgreSQL with pgvector | vector <=> operator |
mysql |
MySQL 9+ | VEC_DISTANCE_COSINE |
Override via config or register a custom driver:
// config/embedding.php
'similarity' => ['driver' => 'pgsql'],
// Custom driver
app(SimilarityManager::class)->extend('custom', fn() => new MyDriver());
Model Events
// Static listeners
Post::onEmbedding(fn($post) => ...); // before generation
Post::onEmbedded(fn($post) => ...); // after record saved
// Observer class
class PostObserver
{
public function embedding(Post $post): void { ... }
public function embedded(Post $post): void { ... }
}
Laravel events ModelEmbedding and ModelEmbedded are also fired at the same points.
Soft Delete
By default, deleting a model also deletes its embedding. Set embedding.soft_delete to true to preserve embeddings on soft delete.
Per-model override:
class Post extends Model implements HasEmbeddings
{
use Embeddable, SoftDeletes;
protected bool $keepEmbeddingOnSoftDelete = true;
}
| Event | false (default) |
true |
|---|---|---|
| soft delete | embedding deleted | embedding kept |
| restore | embedding regenerated | unchanged |
| force delete | embedding deleted | embedding deleted |
Artisan Command
php artisan embedding:generate "App\Models\Post" # only missing embeddings
php artisan embedding:generate "App\Models\Post" --all # regenerate all records
php artisan embedding:generate "App\Models\Post" --chunk=500
Database
embeddings
├── id
├── embeddable_type (polymorphic — Post, Article, etc.)
├── embeddable_id
├── vector (json / pgvector / MySQL 9 VECTOR)
├── created_at
└── updated_at
The vector column type is selected automatically based on the database driver at migration time.
Testing
# Build first (once per PHP version)
DOCKER_BUILDKIT=0 docker compose --profile php83 build
# Run tests
docker compose --profile php83 up
docker compose --profile php84 up
docker compose --profile php85 up
License
This package is open-sourced software licensed under the MIT license.