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 Pinecone Store Laravel Package

symfony/ai-pinecone-store

Pinecone vector store integration for Symfony AI Store, enabling upsert, query, and delete of vectors plus serverless index creation through the Pinecone API.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup for Laravel

  1. Install Dependencies:

    composer require symfony/ai symfony/ai-pinecone-store symfony/http-client
    

    Note: If avoiding Symfony AI entirely, use symfony/http-client + Pinecone’s PHP SDK as a lightweight alternative.

  2. Configure Pinecone Credentials: Add to .env:

    PINECONE_API_KEY=your_api_key
    PINECONE_ENV=your_env_region
    PINECONE_INDEX=your_index_name
    
  3. Register the Store in Laravel: Create a service provider (e.g., PineconeServiceProvider) or bind directly in AppServiceProvider:

    use Symfony\AI\PineconeStore;
    use Symfony\Contracts\HttpClient\HttpClientInterface;
    
    public function register()
    {
        $this->app->singleton(PineconeStore::class, function ($app) {
            return new PineconeStore(
                $app->make(HttpClientInterface::class),
                config('services.pinecone.api_key'),
                config('services.pinecone.env'),
                config('services.pinecone.index')
            );
        });
    }
    
  4. First Use Case: Query Vectors

    use Symfony\AI\StoreInterface;
    
    $store = app(StoreInterface::class);
    $results = $store->query(
        vector: $embeddingArray, // Your 1536-dim vector (e.g., from OpenAI)
        limit: 5,
        filter: ['category' => ['$eq' => 'electronics']] // Optional metadata filter
    );
    
  5. Quick Health Check (v0.14+): Use the new count() capability to verify the index is reachable and populated before wiring up RAG flows:

    $store = app(StoreInterface::class);
    $total = $store->count();                 // Total vectors in index
    $docs  = $store->count(['source' => ['$eq' => 'user_guide']]); // Filtered count
    

Implementation Patterns

1. Symfony AI Integration Workflow

  • For Full Symfony AI Adoption: Use the package as a drop-in StoreInterface for AI components like Symfony\AI\Chain or Symfony\AI\Prompt.
    use Symfony\AI\Chain;
    use Symfony\AI\Prompt;
    
    $chain = new Chain(
        new Prompt('...'),
        app(StoreInterface::class) // PineconeStore injected here
    );
    
  • For Laravel-Specific Use: Decouple from Symfony AI by creating a facade or repository:
    class PineconeRepository
    {
        public function __construct(private StoreInterface $store) {}
    
        public function findSimilarProducts($embedding, int $limit): array
        {
            return $this->store->query($embedding, $limit, [
                'product_type' => ['$in' => ['laptop', 'phone']]
            ]);
        }
    }
    

2. Vector Management Patterns

  • Bulk Upserts:
    $vectors = [
        ['id' => 'doc1', 'values' => $embedding1, 'metadata' => ['source' => 'user_guide']],
        ['id' => 'doc2', 'values' => $embedding2, 'metadata' => ['source' => 'api_docs']],
    ];
    $store->upsert($vectors);
    
  • VectorDocumentInterface (v0.14+): The new interface formalizes the "document + vector" pairing so you can upsert and read typed documents without juggling raw arrays.
    use Symfony\AI\VectorDocumentInterface;
    
    class ProductDocument implements VectorDocumentInterface
    {
        public function __construct(
            private string $id,
            private array $values,
            private array $metadata,
        ) {}
    
        public function getId(): string { return $this->id; }
        public function getValues(): array { return $this->values; }
        public function getMetadata(): array { return $this->metadata; }
    }
    
    $store->upsert([
        new ProductDocument('sku-1', $embedding1, ['source' => 'catalog']),
        new ProductDocument('sku-2', $embedding2, ['source' => 'catalog']),
    ]);
    
    This is the cleanest way to normalize upsert payload shape and keep domain objects decoupled from Pinecone’s wire format.
  • Batch Queries: Use query() with includeMetadata: true for RAG pipelines:
    $result = $store->query($queryEmbedding, 3, [], [
        'includeMetadata' => true,
        'includeValues' => false,
    ]);
    // $result['matches'][0]['metadata']['source'] gives context for LLM prompts.
    

3. Hybrid Search Integration

Combine keyword and vector search via metadata filters:

$results = $store->query(
    $embedding,
    10,
    ['price' => ['$gt' => 100]], // Filter by metadata
    ['includeMetadata' => true]
);

4. Counts for Analytics and Pagination (v0.14+)

The new count() capability lets you build UI affordances and guard rails around your index without pulling vectors.

// Dashboard stats
$stats = [
    'total'        => $store->count(),
    'electronics'  => $store->count(['category' => ['$eq' => 'electronics']]),
    'active_docs'  => $store->count(['status' => ['$eq' => 'published']]),
];

// Pagination estimate for a filtered search UI
$matched = $store->count($filter);
$pages   = (int) ceil($matched / $perPage);

Prefer count($filter) over fetching + counting client-side — it’s cheaper and keeps memory flat on large indexes.

5. Error Handling and Retries

Wrap Pinecone operations in Laravel’s try-catch or use Symfony’s HttpClient retries:

try {
    $store->query($embedding, 5);
} catch (\Symfony\Contracts\HttpClient\Exception\ClientException $e) {
    // Handle Pinecone API errors (e.g., rate limits)
    Log::error('Pinecone query failed: ' . $e->getMessage());
}

6. Testing Patterns

  • Mock the StoreInterface:
    $mockStore = Mockery::mock(StoreInterface::class);
    $mockStore->shouldReceive('query')
        ->once()
        ->andReturn(['matches' => []]);
    $mockStore->shouldReceive('count')
        ->andReturn(42); // v0.14+ — stub the new capability too
    
    $this->app->instance(StoreInterface::class, $mockStore);
    
  • Use Pinecone’s Sandbox: Test with a free Pinecone index during development.

Gotchas and Tips

Pitfalls

  1. Symfony AI Overhead:

    • Issue: The package requires symfony/ai (~50MB), which may be unnecessary for simple Pinecone use cases.
    • Fix: Use symfony/http-client directly with Pinecone’s PHP SDK for lightweight integration.
  2. Metadata Filtering Quirks:

    • Issue: Pinecone’s filter syntax (e.g., $eq, $gt) is not documented in the package. Refer to Pinecone’s API docs.
    • Tip: Validate filters with Pinecone’s playground before implementing. The same filter shape is accepted by the new count() method (v0.14+), so a passing filter in query() should behave consistently in count().
  3. Vector Dimension Mismatch:

    • Issue: Pinecone indexes are created with a fixed dimension (e.g., 1536 for OpenAI embeddings). Upserting vectors with mismatched dimensions throws errors.
    • Fix: Check index.describe_index_stats() to verify dimensions. When implementing VectorDocumentInterface, validate getValues() length on construction so bad documents fail fast.
  4. Rate Limiting:

    • Issue: Pinecone’s free tier has strict rate limits (e.g., 1000 queries/day). Unhandled errors may silently fail.
    • Tip: Implement exponential backoff in Laravel’s App\Exceptions\Handler:
      public function render($request, Throwable $exception)
      {
          if ($exception instanceof \Symfony\Contracts\HttpClient\Exception\RateLimitedException) {
              return response()->json(['error' => 'Rate limited'], 429);
          }
          return parent::render($request, $exception);
      }
      
    • Note: count() calls consume the same rate limit budget as query(). Don’t call it on every page load — cache the result.
  5. NullVector Edge Cases:

    • Issue: The package returns NullVector when includeValues: false. This may break Laravel’s type expectations — particularly when deserializing into VectorDocumentInterface implementations that require a non-null values array.
    • Fix: Only implement VectorDocumentInterface on types you intend to write (upsert), or guard reads with a nullable values accessor and normalize in a repository layer:
      $normalized = array_map(function ($match) {
          return $match['metadata'] ?? [];
      }, $result['matches']);
      
  6. Interface Migration Cost (v0.14+):

    • Issue: VectorDocumentInterface is new; existing code that upserts plain arrays still works, but mixing array payloads and interface implementations in the same upsert call can surface inconsistent behavior across store implementations.
    • Tip: Adopt the interface incrementally. Introduce one domain-specific document class, migrate upserts through it, then extend the pattern. Keep raw-array upserts only in legacy/admin scripts.

Debugging Tips

  1. Enable HTTP Client Logging:

    $client = \Symfony\Contracts\HttpClient\HttpClient::create([
        'debug' => true,
    ]);
    

    Logs will appear in Laravel’s storage/logs.

  2. Pinecone API Playground: Test queries manually at Pinecone’s API Playground to isolate issues. Compare the returned totalVectorCount there against $store->count() to confirm the count capability is wired up correctly.

  3. Index Stats: Verify index health with:

    $stats = $store->describeIndex();
    // Check 'dimension', 'status', and 'totalVectorCount'
    

Extension Points

  1. Custom Metadata Handling: Extend the store to transform metadata before/after Pinecone operations. If you’re using VectorDocumentInterface, you can do the same enrichment by decorating the document rather than the store:

    class CustomPineconeStore extends PineconeStore
    {
        public function upsert(array $vectors): void
        {
            $normalized = array_map([$this, 'normalizeMetadata'], $vectors);
            parent::upsert($normalized);
        }
    
        private function normalizeMetadata(array $vector): array
        {
            $vector['metadata']['processed_at'] = now()->toIso8601String();
            return $vector;
        }
    }
    
  2. Caching Layer: Cache frequent queries and counts using Laravel’s cache — counts especially benefit from a short TTL:

    public function query($vector, int $limit, array $filter = [], array $options = []): array
    {
        $cacheKey = md5(serialize([$vector, $limit, $filter]));
        return cache()->remember($cacheKey, now()->addMinutes(5), function () use ($vector, $limit, $filter, $options) {
            return parent::query($vector, $limit, $filter, $options);
        });
    }
    
    public function count(array $filter = []): int
    {
        $cacheKey = 'pinecone.count.' . md5(serialize($filter));
        return cache()->remember($cacheKey, now()->addMinute(), function () use ($filter) {
            return parent::count($filter);
        });
    }
    
  3. Async Operations: Use Laravel Queues for bulk upserts (accepts plain arrays or VectorDocumentInterface instances in v0.14+):

    class UpsertPineconeVectorsJob implements ShouldQueue
    {
        public function handle()
        {
            $store = app(StoreInterface::class);
            $store->upsert($this->vectors);
        }
    }
    

Configuration Quirks

  1. Environment Variables: Ensure Pinecone credentials are in .env and loaded via Laravel’s config:

    'pinecone' => [
        'api_key' => env('PINECONE_API_KEY'),
        'env'     => env('PINECONE_ENV'),
        'index'   => env('PINECONE_INDEX'),
    ],
    

    Avoid calling env() outside config files — config caching (used in production) will return null. Reference config('services.pinecone.*') everywhere else.

  2. Version Pinning: Pin symfony/ai-pinecone-store to ^0.14 if you rely on count() or VectorDocumentInterface. Earlier 0.13 installs will lack both APIs, and StoreInterface compatibility checks may fail silently at runtime rather than at install time.

  3. Interface Compatibility: If your codebase already declares a custom DocumentInterface, verify it doesn’t collide with VectorDocumentInterface before adopting the new type — aliasing in the use statement is often enough to keep both.

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.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log