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

Ai Vektor Store Laravel Package

symfony/ai-vektor-store

Vektor vector store integration for Symfony AI Store, connecting your Symfony AI applications to the Vektor vector database for storage and retrieval.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install Dependencies:

    composer require symfony/ai-vektor-store
    

    Ensure your Laravel app uses PHP 8.2+ and has either Redis or PostgreSQL with pgvector installed.

  2. Configure Vektor: Add to config/services.php:

    'vektor' => [
        'dsn' => env('VEKTOR_DSN', 'redis://127.0.0.1:6379'),
        'collection' => 'default',
    ],
    
  3. Register the Store (unchanged interface, updated bridge class):

    use Symfony\AI\Store\StoreInterface;
    use Symfony\AI\Store\Bridge\Vektor\Store;
    
    class VektorServiceProvider extends ServiceProvider {
        public function register() {
            $this->app->singleton(StoreInterface::class, function ($app) {
                return new Store(
                    $app['config']['services.vektor.dsn'],
                    $app['config']['services.vektor.collection']
                );
            });
        }
    }
    
  4. First Use Case — index and query, with the new count() guard (v0.14):

    use Symfony\AI\Store\StoreInterface;
    use Symfony\AI\Store\Document\VectorDocument;
    use Symfony\AI\Platform\Vector\Vector;
    
    class SearchController extends Controller {
        public function __construct(private StoreInterface $store) {}
    
        public function search(string $query) {
            // New in v0.14: cheap sanity check before hitting the vector index
            if ($this->store->count() === 0) {
                return response()->json(['error' => 'No documents indexed'], 404);
            }
    
            $vector  = app(VectorizerInterface::class)->vectorize($query);
            $results = iterator_to_array(
                $this->store->query(new Vector($vector), ['limit' => 3])
            );
    
            return response()->json($results);
        }
    }
    

Where to Look First

  • Symfony AI Store Documentation for the StoreInterface methods (add, remove, query, and now count).
  • Vektor Bridge README for backend-specific tuning (Redis/PostgreSQL) and the v0.14 changelog.
  • Symfony\AI\Store\Document\VectorDocumentInterface — new in v0.14; the type you should code against for anything that flows through add().

Implementation Patterns

Core Workflows

1. Embedding and Storage (now typed against VectorDocumentInterface)

  • Pattern: Build VectorDocument instances and pass them to add(); use a deterministic ID for idempotency at the application layer.
    use Symfony\AI\Store\Document\VectorDocument;
    use Symfony\AI\Store\Document\Metadata;
    use Symfony\AI\Platform\Vector\Vector;
    
    $doc = new VectorDocument(
        id: 'doc_'.$model->id,
        vector: new Vector($embedding),
        metadata: new Metadata($model->toArray()),
    );
    $store->add($doc);
    
  • Laravel Integration: Attach to model observers or events:
    // app/Observers/DocumentObserver.php
    class DocumentObserver {
        public function saved(Document $document) {
            app(StoreInterface::class)->add(new VectorDocument(
                id: 'doc_'.$document->id,
                vector: new Vector($this->embed($document->content)),
                metadata: new Metadata($document->toArray()),
            ));
        }
    
        public function deleted(Document $document) {
            app(StoreInterface::class)->remove('doc_'.$document->id);
        }
    }
    

2. Collection Size / Freshness Checks (v0.14 count())

  • Pattern: Use count() for pagination, dashboards, or as an index-readiness gate before running retrieval.
    $total = $store->count();
    $stale = $store->count(['filter' => ['indexed_at' => ['$lt' => now()->subDay()->toIso8601String()]]]);
    
  • Use in a health route:
    Route::get('/health/vektor', fn () => response()->json([
        'documents' => app(StoreInterface::class)->count(),
    ]));
    

3. Hybrid Search with Scout

  • Pattern: Extend Laravel Scout's Engine to delegate vector queries. Since v0.14, query() returns VectorDocumentInterface instances; map them to your Eloquent models via id.
    // app/Scout/Engines/VektorEngine.php
    class VektorEngine extends Engine {
        public function search($query) {
            $vector  = app(VectorizerInterface::class)->vectorize($query);
            $results = app(StoreInterface::class)->query(new Vector($vector));
            return $this->mapResults(iterator_to_array($results));
        }
    }
    

4. Batch Operations

  • Pattern: Stream documents into a queue job; batch add() calls because Vektor backends (Redis/PG) reward batched writes.
    class EmbedDocuments implements ShouldQueue {
        public function handle(StoreInterface $store) {
            Document::query()->chunkById(200, function ($chunk) use ($store) {
                $docs = $chunk->map(fn ($doc) => new VectorDocument(
                    id: 'doc_'.$doc->id,
                    vector: new Vector($this->embed($doc->content)),
                    metadata: new Metadata($doc->toArray()),
                ))->all();
                $store->add(...$docs);
            });
        }
    }
    

5. RAG Pipeline

  • Pattern: Retrieve and feed context to an LLM; use count() to short-circuit when the collection is empty.
    $vector  = app(VectorizerInterface::class)->vectorize($query);
    $context = collect(iterator_to_array($this->store->query(new Vector($vector), ['limit' => 2])))
        ->map(fn (VectorDocumentInterface $r) => $r->getMetadata()->get('content'))
        ->filter()
        ->implode("\n");
    
    $prompt = "Context: {$context}\nQuestion: {$query}";
    $response = $llm->complete($prompt);
    

Integration Tips

  • Environment Variables:
    VEKTOR_DSN=postgres://user:pass@localhost/vektor_db
    
  • Payload Filtering: Keep metadata flat; many Vektor backends only support single-level filters in query()/count() options.
  • Error Handling: Wrap count() on the health route too — a Redis hiccup will 500 your /health endpoint otherwise.

Gotchas and Tips

Pitfalls

  1. VectorDocumentInterface is not a class you instantiate:

    • Issue: v0.14 exposes the interface; the concrete VectorDocument remains the value-object you build.
    • Fix: Type-hint against VectorDocumentInterface in method signatures (so future releases can swap implementations), but construct new VectorDocument(...) when writing.
  2. count() is not free on Redis:

    • Issue: Some Vektor backends implement count() as a full scan; calling it inside a loop or per-request can be surprisingly expensive.
    • Fix: Cache the result — Cache::remember('vektor.count', 60, fn () => $store->count()); — and avoid it in hot paths.
  3. upsert no longer exists as a store method:

    • Issue: Older snippets use $store->upsert(...). The stable API is add() + deterministic IDs (or remove() then add() for a true replace).
    • Fix: Use deterministic IDs at the application layer and re-add(); check the bridge README for backend-specific merge semantics.
  4. Vector Dimension Mismatch:

    • Issue: Embeddings from different models (e.g. text-embedding-3-small vs. all-MiniLM-L6-v2) have incompatible dimensions.
    • Fix: Lock the embedding model per collection; assert dimensions in a unit test before shipping.
  5. Laravel Cache vs. Vektor on the same Redis instance:

    • Issue: Shared Redis DB causes eviction pressure and stale vectors.
    • Fix: Use a dedicated DB index:
      VEKTOR_DSN=redis://127.0.0.1:6379/1
      
  6. PostgreSQL pgvector Setup:

    • Issue: Missing extension or wrong dimension in schema.
    • Fix:
      CREATE EXTENSION IF NOT EXISTS vector;
      -- match your model's dimension exactly
      CREATE TABLE vectors (id TEXT PRIMARY KEY, embedding vector(1536));
      

Debugging

  • Count-based smoke test:
    php artisan tinker
    >>> app(Symfony\AI\Store\StoreInterface::class)->count()
    
    If this returns 0 after a seeded run, the issue is almost always a write path (missing add() or a failing observer), not the query.
  • Enable bridge logging:
    'vektor' => [
        'dsn'   => env('VEKTOR_DSN'),
        'debug' => env('APP_DEBUG', false),
    ],
    
  • Debugbar tab for Vektor metrics:
    Debugbar::addCollector(new class implements DataCollectorInterface {
        public function collect() {
            return ['documents' => app(StoreInterface::class)->count()];
        }
    });
    

Extension Points

  1. Custom VectorDocumentInterface implementation: Since v0.14 formally introduces the interface, you can implement domain-specific document types (e.g. one that derives metadata from an Eloquent model lazily) and pass them to add() if the bridge accepts the interface rather than the concrete class.

    class ModelLinkedDocument implements VectorDocumentInterface {
        public function __construct(private Model $model, private array $vector) {}
        public function getId(): string { return 'doc_'.$this->model->getKey(); }
        public function getVector(): array { return $this->vector; }
        public function getMetadata(): array { return $this->model->toArray(); }
    }
    
  2. Laravel Facade for cleaner call sites:

    // app/Facades/Vektor.php
    use Illuminate\Support\Facades\Facade;
    
    class Vektor extends Facade {
        protected static function getFacadeAccessor() {
            return \Symfony\AI\Store\StoreInterface::class;
        }
    }
    // Usage: Vektor::count();
    
  3. Decorator around the store to add caching for count() and instrumentation:

    class CachedStore implements StoreInterface {
        public function __construct(private StoreInterface $inner) {}
        public function add(VectorDocument ...$documents): void { $this->inner->add(...$documents); Cache::forget('vektor.count'); }
        public function count(array $options = []): int { return Cache::remember('vektor.count', 60, fn () => $this->inner->count($options)); }
        // delegate query()/remove()…
    }
    
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin