symfony/ai-pinecone-store
Symfony AI Store integration for Pinecone vector databases. Upsert, query, and delete embeddings, and work with Pinecone serverless indexes using Pinecone’s data/control plane APIs. Links to official Pinecone docs and Symfony AI contribution resources.
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.
Configure Pinecone Credentials:
Add to .env:
PINECONE_API_KEY=your_api_key
PINECONE_ENV=your_env_region
PINECONE_INDEX=your_index_name
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')
);
});
}
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
);
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
);
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']]
]);
}
}
$vectors = [
['id' => 'doc1', 'values' => $embedding1, 'metadata' => ['source' => 'user_guide']],
['id' => 'doc2', 'values' => $embedding2, 'metadata' => ['source' => 'api_docs']],
];
$store->upsert($vectors);
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.
Combine keyword and vector search via metadata filters:
$results = $store->query(
$embedding,
10,
['price' => ['$gt' => 100]], // Filter by metadata
['includeMetadata' => true]
);
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());
}
$mockStore = Mockery::mock(StoreInterface::class);
$mockStore->shouldReceive('query')
->once()
->andReturn(['matches' => []]);
$this->app->instance(StoreInterface::class, $mockStore);
Symfony AI Overhead:
symfony/ai (~50MB), which may be unnecessary for simple Pinecone use cases.symfony/http-client directly with Pinecone’s PHP SDK for lightweight integration.Metadata Filtering Quirks:
$eq, $gt) is not documented in the package. Refer to Pinecone’s API docs.Vector Dimension Mismatch:
index.describe_index_stats() to verify dimensions.Rate Limiting:
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);
}
NullVector Edge Cases:
NullVector when includeValues: false. This may break Laravel’s type expectations.$normalized = array_map(function ($match) {
return $match['metadata'] ?? [];
}, $result['matches']);
Enable HTTP Client Logging:
$client = \Symfony\Contracts\HttpClient\HttpClient::create([
'debug' => true,
]);
Logs will appear in Laravel’s storage/logs.
Pinecone API Playground: Test queries manually at Pinecone’s API Playground to isolate issues.
Index Stats: Verify index health with:
$stats = $store->describeIndex();
// Check 'dimension', 'status', and 'totalVectorCount'
Custom Metadata Handling: Extend the store to transform metadata before/after Pinecone operations:
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;
}
}
Caching Layer: Cache frequent queries using Laravel’s cache:
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);
});
}
Async Operations: Use Laravel Queues for bulk upserts:
class UpsertPineconeVectorsJob implements ShouldQueue
{
public function handle()
{
$store = app(StoreInterface::class);
$store->upsert($this->vectors);
}
}
.env and loaded via Laravel’s config:
'pinecone' => [
'api_key' => env('PINECONE_API_KEY'),
'env' => env('PINEC
How can I help you explore Laravel packages today?