Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Embedding Laravel Package

x-laravel/embedding

Laravel package that automatically generates and stores vector embeddings for Eloquent models via laravel/ai. Define named slots, re-embed only changed fields with queued jobs, filter by payload, and run driver-based similarity search with optional reranking.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require x-laravel/embedding laravel/ai
    php artisan migrate
    
  2. Basic Setup: Add the Embeddable trait and define toEmbeddingText() in your Eloquent model:

    use XLaravel\Embedding\Concerns\Embeddable;
    use XLaravel\Embedding\Contracts\HasEmbeddings;
    
    class Post extends Model implements HasEmbeddings
    {
        use Embeddable;
    
        public function toEmbeddingText(): string
        {
            return $this->title . ' ' . $this->body;
        }
    }
    
  3. First Use Case: Trigger embedding generation on a new post:

    $post = Post::create(['title' => 'Laravel Embedding', 'body' => '...']);
    $post->embed(); // Dispatches a queued job to generate embeddings
    

Where to Look First

  • Model Integration: Focus on toEmbeddingText() and $embeddable configuration.
  • Similarity Search: Use similarTo() or similarToText() for basic retrieval.
  • Artisan Commands: Run php artisan embedding:status to verify setup. If your models and embedding tables live on different connections, the status/clean commands handle the split for you (see Gotchas).

Implementation Patterns

Usage Patterns

  1. Single-Slot Models:

    class Post extends Model implements HasEmbeddings
    {
        use Embeddable;
    
        protected array $embeddable = ['title', 'body'];
    
        public function toEmbeddingText(): string
        {
            return $this->title . ' ' . $this->body;
        }
    }
    
    • Trigger: Embeddings auto-generate when title or body changes.
    • Usage: $post->embed() or $post->embedSync().
  2. Multi-Slot Models:

    class Post extends Model implements HasEmbeddings
    {
        use Embeddable;
    
        protected array $embeddable = [
            'title' => ['title'],
            'body'  => ['body'],
            'full'  => ['title', 'body'],
        ];
    
        public function toEmbeddingText(): string|array
        {
            return [
                'title' => $this->title,
                'body'  => $this->body,
                'full'  => $this->title . ' ' . $this->body,
            ];
        }
    }
    
    • Trigger: Only re-embed slots tied to changed fields (e.g., updating title skips body slot).
    • Usage: $post->embed('title') or $post->embeddings() to fetch all slots.
  3. Attribute-Based Configuration:

    use XLaravel\Embedding\Attributes\EmbedOn;
    
    #[EmbedOn(['title', 'body'])]
    class Post extends Model implements HasEmbeddings { ... }
    
    • Use Case: Prefer attributes for dynamic or complex trigger logic.

Workflows

  1. RAG Pipeline:

    • Generate embeddings for documents.
    • Query with similarToText():
      $results = Post::similarToText('Laravel AI', limit: 5);
      
    • Rerank results:
      $reranked = $results->rerankWithScores('Laravel AI performance', take: 3);
      
  2. Bulk Embedding:

    • Use embedding:generate for backfilling:
      php artisan embedding:generate "App\Models\Post" --limit=1000
      
  3. Similarity Search with Filters:

    $results = Post::similarTo($vector, threshold: 0.8)
        ->where('published_at', '>', now()->subYear());
    
  4. Split-Connection / Multi-Database Setups:

    • Keep the model on your primary connection and the embedding tables on a vector-capable connection; the package's comparison commands page through IDs automatically, so no manual chunking is needed.
    • Periodically run the paired status/clean commands to keep both sides honest:
      php artisan embedding:vector:status
      php artisan embedding:payload:status
      php artisan embedding:vector:clean
      php artisan embedding:payload:clean
      

Integration Tips

  • Queue Workers: Ensure queue workers are running for async embedding generation.
  • Driver Selection: Install a database-specific driver (e.g., embedding-pgsql-driver) for native vector search.
  • Pulse Plugin: Add monitoring with:
    composer require x-laravel/embedding-pulse-plugin
    
    Then include Livewire cards in your Pulse dashboard.
  • Separate Connections: Set distinct connections for models vs. embedding tables when using a dedicated vector database; the maintenance commands support this natively.

Gotchas and Tips

Pitfalls

  1. Blocking Operations:

    • Avoid embedSync() in production; use queued jobs (embed()) for scalability.
    • Fix: Monitor queue backlogs with embedding:status.
  2. Slot Mismatches:

    • Deleting a slot from $embeddable won't auto-cleanup old embeddings.
    • Fix: Run php artisan embedding:clean --invalid-slots-only to prune stale slots.
  3. Soft Deletes:

    • Default behavior deletes all embeddings on soft delete. Override with:
    protected bool $keepEmbeddingOnSoftDelete = true;
    
  4. Driver Conflicts:

    • Mixing drivers (e.g., php and pgsql) may cause unexpected behavior.
    • Fix: Explicitly set the driver in config/embedding.php:
    'similarity' => ['driver' => 'pgsql'],
    
  5. Reranking Limits:

    • Reranking APIs (Cohere/Voyage) have rate limits. Batch queries to avoid throttling.
    • Tip: Use take parameter to control batch size:
    ->rerankWithScores('query', take: 10)
    
  6. Model and Embedding Tables on Different Connections (fixed as of v1.0.3):

    • Older versions compared both ID sets in a single IN (...) list, which broke on MySQL once the model exceeded 65,535 IDs: SQLSTATE[HY000]: General error: 1390 Prepared statement contains too many placeholders.
    • v1.0.3 windows the comparison for embeddedCount(), embedding:vector:status, embedding:payload:status, embedding:vector:clean, embedding:payload:clean and embedding:clean; stale payload deletes are now chunked by the connection's IdSetBinder limit.
    • Action: Upgrade if you run split connections over large tables. After upgrading, run the *:status commands once to confirm counts match; no config change is required.

Debugging

  • Failed Jobs: Check embedding.failures Pulse card or queue logs.
  • Embedding Status: Use embedding:status to verify slot coverage:
    php artisan embedding:status "App\Models\Post"
    
  • Drift Between Model and Vector Store: In split-connection setups, inspect each side independently, then reconcile:
    php artisan embedding:vector:status
    php artisan embedding:payload:status
    
    Use embedding:vector:clean / embedding:payload:clean to remove each kind of orphan separately rather than guessing at embedding:clean.
  • Slow Queries: Enable query logging in config/embedding.php:
    'debug' => [
        'log_queries' => true,
    ],
    

Config Quirks

  1. Embedding Disabled:

    • Global suppression with Post::disableEmbedding() may persist across requests.
    • Fix: Re-enable explicitly:
    Post::enableEmbedding();
    
  2. Vector Dimensions:

    • Ensure laravel/ai and embedding models use the same vector dimension (e.g., 1536 for OpenAI).
    • Tip: Validate in config/ai.php:
    'default' => [
        'model' => 'openai',
        'options' => [
            'embedding' => [
                'model' => 'text-embedding-ada-002',
                'dimensions' => 1536,
            ],
        ],
    ],
    
  3. Placeholder Limits on Split Connections:

    • The practical ceiling for a single IN list is driver-specific (65,535 on MySQL). The maintenance commands now window around this, but any custom code you write against both connections should follow the same pattern — chunk IDs through IdSetBinder rather than building one giant list.
  4. Queue Stuck Jobs:

    • Retry failed jobs with:
    php artisan queue:retry all
    

Extension Points

  1. Custom Drivers:

    • Extend XLaravel\Embedding\Contracts\SimilarityDriver for unsupported databases.
    • Register via:
    app(SimilarityManager::class)->extend('custom', fn() => new MyDriver());
    
  2. Event Hooks:

    • Tap into ModelEmbedding/ModelEmbedded events for auditing or side effects:
    Post::onEmbedded(fn($post, $slot) => Log::info("Embedded slot {$slot} for post {$post->id}"));
    
  3. Dynamic Slots:

    • Use toEmbeddingText() to generate slots dynamically (e.g., per-user embeddings):
    public function toEmbeddingText(): array
    {
        return [
            'user_' . $this->user_id => $this->content,
        ];
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource