npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@traceai/pinecone

v0.1.0

Published

OpenTelemetry instrumentation for Pinecone vector database

Readme

@traceai/pinecone

OpenTelemetry instrumentation for Pinecone - the serverless vector database for high-performance AI applications.

Overview

This package provides automatic tracing for Pinecone operations in Node.js applications, enabling full observability of your vector database interactions in production AI systems, RAG pipelines, and semantic search applications.

Installation

npm install @traceai/pinecone
# or
yarn add @traceai/pinecone
# or
pnpm add @traceai/pinecone

Quick Start

Basic Setup

import { PineconeInstrumentation } from "@traceai/pinecone";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { registerInstrumentations } from "@opentelemetry/instrumentation";

// Set up OpenTelemetry
const provider = new NodeTracerProvider();
provider.addSpanProcessor(
  new SimpleSpanProcessor(
    new OTLPTraceExporter({
      url: "https://api.futureagi.com/v1/traces",
      headers: {
        "Authorization": `Bearer ${process.env.FI_API_KEY}`,
        "FI-Project-Name": process.env.FI_PROJECT_NAME,
      },
    })
  )
);
provider.register();

// Register Pinecone instrumentation
registerInstrumentations({
  instrumentations: [
    new PineconeInstrumentation(),
  ],
});

// Now use Pinecone as normal - all operations are traced
import { Pinecone } from "@pinecone-database/pinecone";

const pinecone = new Pinecone({ apiKey: process.env.PINECONE_API_KEY });
const index = pinecone.index("my-index");

// This operation will be automatically traced
const results = await index.query({
  vector: [0.1, 0.2, 0.3, /* ... */],
  topK: 10,
  includeMetadata: true,
});

Manual Instrumentation

import { PineconeInstrumentation } from "@traceai/pinecone";
import * as pinecone from "@pinecone-database/pinecone";

const instrumentation = new PineconeInstrumentation();
instrumentation.manuallyInstrument(pinecone);

Configuration Options

interface PineconeInstrumentationConfig {
  // Whether to capture query vectors in spans (may be large)
  captureQueryVectors?: boolean;

  // Whether to capture result vectors in spans (may be large)
  captureResultVectors?: boolean;
}

// Example with all options
const instrumentation = new PineconeInstrumentation({
  instrumentationConfig: {
    captureQueryVectors: false,  // Recommended for production
    captureResultVectors: false,
  },
  traceConfig: {
    maskInputs: false,
    maskOutputs: false,
  },
});

Traced Operations

| Operation | Span Name | Description | |-----------|-----------|-------------| | query | pinecone query | Semantic similarity search | | upsert | pinecone upsert | Insert or update vectors | | fetch | pinecone fetch | Retrieve vectors by ID | | update | pinecone update | Update vector metadata | | deleteOne | pinecone delete | Delete single vector | | deleteMany | pinecone delete_many | Delete multiple vectors | | deleteAll | pinecone delete_all | Delete all vectors in namespace | | listPaginated | pinecone list | List vectors with pagination | | describeIndexStats | pinecone describe_stats | Get index statistics |

Span Attributes

Each span includes these semantic convention attributes:

| Attribute | Description | Example | |-----------|-------------|---------| | db.system | Database system identifier | pinecone | | db.operation.name | Operation being performed | query, upsert | | db.vector.index.name | Index name | my-index | | db.vector.namespace | Namespace (if used) | documents | | db.vector.query.top_k | Number of results requested | 10 | | db.vector.query.filter | Query filter (JSON) | {"category": "docs"} | | db.vector.query.include_metadata | Whether metadata included | true | | db.vector.query.include_vectors | Whether vectors included | false | | db.vector.results.count | Number of results returned | 10 | | db.vector.upsert.count | Number of vectors upserted | 100 | | db.vector.upsert.dimensions | Vector dimensions | 1536 | | db.vector.delete.count | Number of vectors deleted | 50 | | db.vector.delete.all | Whether all deleted | true | | db.vector.index.dimensions | Index dimensions | 1536 | | fi.span.kind | TraceAI span kind | VECTOR_DB |

Real-World Use Cases

1. Production RAG with Serverless Pinecone

High-scale retrieval-augmented generation:

import { Pinecone } from "@pinecone-database/pinecone";
import OpenAI from "openai";

const pinecone = new Pinecone();
const openai = new OpenAI();

const index = pinecone.index("knowledge-base");

// Ingest documents with namespace isolation
async function ingestDocuments(documents: Document[], namespace: string) {
  const batchSize = 100;

  for (let i = 0; i < documents.length; i += batchSize) {
    const batch = documents.slice(i, i + batchSize);

    // Generate embeddings
    const embeddings = await openai.embeddings.create({
      model: "text-embedding-3-large",
      input: batch.map(d => d.content),
      dimensions: 1536,
    });

    // Upsert to Pinecone (traced automatically)
    await index.namespace(namespace).upsert(
      batch.map((doc, j) => ({
        id: doc.id,
        values: embeddings.data[j].embedding,
        metadata: {
          title: doc.title,
          source: doc.source,
          created_at: doc.createdAt,
          chunk_index: doc.chunkIndex,
        },
      }))
    );
  }
}

// RAG query with hybrid search (traced automatically)
async function ragQuery(question: string, namespace: string, filters?: object) {
  // Generate query embedding
  const queryEmbedding = await openai.embeddings.create({
    model: "text-embedding-3-large",
    input: question,
    dimensions: 1536,
  });

  // Semantic search with metadata filtering
  const results = await index.namespace(namespace).query({
    vector: queryEmbedding.data[0].embedding,
    topK: 5,
    includeMetadata: true,
    filter: filters,
  });

  // Build context from results
  const context = results.matches
    .map(m => m.metadata?.content)
    .filter(Boolean)
    .join("\n\n");

  // Generate response
  const response = await openai.chat.completions.create({
    model: "gpt-4-turbo",
    messages: [
      {
        role: "system",
        content: `Answer based on this context:\n${context}\n\nIf the context doesn't contain relevant information, say so.`,
      },
      { role: "user", content: question },
    ],
  });

  return {
    answer: response.choices[0].message.content,
    sources: results.matches.map(m => ({
      id: m.id,
      score: m.score,
      title: m.metadata?.title,
    })),
  };
}

2. Multi-Tenant SaaS Application

Isolate customer data using namespaces:

class MultiTenantVectorStore {
  private index: any;

  constructor(indexName: string) {
    const pinecone = new Pinecone();
    this.index = pinecone.index(indexName);
  }

  // Store customer data in isolated namespace (traced)
  async storeForTenant(tenantId: string, documents: any[]) {
    const ns = this.index.namespace(`tenant_${tenantId}`);

    await ns.upsert(documents.map(doc => ({
      id: `${tenantId}_${doc.id}`,
      values: doc.embedding,
      metadata: {
        tenant_id: tenantId,
        ...doc.metadata,
      },
    })));
  }

  // Query within tenant namespace only (traced)
  async queryForTenant(tenantId: string, queryVector: number[], options: any = {}) {
    const ns = this.index.namespace(`tenant_${tenantId}`);

    return await ns.query({
      vector: queryVector,
      topK: options.topK || 10,
      includeMetadata: true,
      filter: options.filter,
    });
  }

  // Get tenant usage statistics (traced)
  async getTenantStats(tenantId: string) {
    const stats = await this.index.describeIndexStats();
    const tenantNamespace = `tenant_${tenantId}`;

    return {
      vectorCount: stats.namespaces[tenantNamespace]?.recordCount || 0,
      totalIndexVectors: stats.totalRecordCount,
    };
  }

  // Clean up tenant data on deletion (traced)
  async deleteTenant(tenantId: string) {
    const ns = this.index.namespace(`tenant_${tenantId}`);
    await ns.deleteAll();
  }
}

// Usage
const vectorStore = new MultiTenantVectorStore("saas-production");

// Store data for tenant
await vectorStore.storeForTenant("acme-corp", customerDocuments);

// Query within tenant isolation
const results = await vectorStore.queryForTenant("acme-corp", queryVector, {
  topK: 5,
  filter: { document_type: "contract" },
});

3. Real-Time Recommendation Engine

Product recommendations with hybrid filtering:

class RecommendationEngine {
  private index: any;

  constructor() {
    const pinecone = new Pinecone();
    this.index = pinecone.index("product-embeddings");
  }

  // Index product catalog (traced)
  async indexProducts(products: Product[]) {
    const batches = this.chunk(products, 100);

    for (const batch of batches) {
      await this.index.upsert(
        batch.map(p => ({
          id: p.id,
          values: p.embedding,
          metadata: {
            name: p.name,
            category: p.category,
            brand: p.brand,
            price: p.price,
            rating: p.rating,
            in_stock: p.inStock,
            tags: p.tags,
          },
        }))
      );
    }
  }

  // Find similar products (traced)
  async findSimilar(productId: string, options: RecommendationOptions = {}) {
    // Fetch the source product embedding
    const { records } = await this.index.fetch([productId]);
    const sourceProduct = records[productId];

    if (!sourceProduct) {
      throw new Error(`Product ${productId} not found`);
    }

    // Query for similar products with filters
    const results = await this.index.query({
      vector: sourceProduct.values,
      topK: options.limit || 10,
      includeMetadata: true,
      filter: {
        $and: [
          // Exclude the source product
          { id: { $ne: productId } },
          // Only in-stock items
          { in_stock: { $eq: true } },
          // Optional category filter
          ...(options.sameCategory
            ? [{ category: { $eq: sourceProduct.metadata?.category } }]
            : []),
          // Price range filter
          ...(options.maxPrice
            ? [{ price: { $lte: options.maxPrice } }]
            : []),
        ],
      },
    });

    return results.matches.map(m => ({
      id: m.id,
      score: m.score,
      ...m.metadata,
    }));
  }

  // Personalized recommendations (traced)
  async getPersonalized(userEmbedding: number[], preferences: UserPreferences) {
    const results = await this.index.query({
      vector: userEmbedding,
      topK: 20,
      includeMetadata: true,
      filter: {
        $and: [
          { in_stock: { $eq: true } },
          { category: { $in: preferences.favoriteCategories } },
          { brand: { $in: preferences.preferredBrands } },
          { price: { $lte: preferences.maxBudget } },
          { rating: { $gte: 4.0 } },
        ],
      },
    });

    return results.matches;
  }

  private chunk<T>(array: T[], size: number): T[][] {
    return Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>
      array.slice(i * size, i * size + size)
    );
  }
}

4. Semantic Code Search

Search code repositories by natural language:

class CodeSearchEngine {
  private index: any;

  constructor() {
    const pinecone = new Pinecone();
    this.index = pinecone.index("code-embeddings");
  }

  // Index code files (traced)
  async indexRepository(repoId: string, files: CodeFile[]) {
    const ns = this.index.namespace(`repo_${repoId}`);

    // Chunk files into smaller pieces
    const chunks = files.flatMap(file =>
      this.chunkCode(file).map((chunk, i) => ({
        id: `${file.path}_chunk_${i}`,
        values: chunk.embedding,
        metadata: {
          repo_id: repoId,
          file_path: file.path,
          language: file.language,
          start_line: chunk.startLine,
          end_line: chunk.endLine,
          content: chunk.content,
          functions: chunk.functions,
        },
      }))
    );

    // Batch upsert
    for (let i = 0; i < chunks.length; i += 100) {
      await ns.upsert(chunks.slice(i, i + 100));
    }
  }

  // Natural language code search (traced)
  async search(repoId: string, query: string, options: SearchOptions = {}) {
    const ns = this.index.namespace(`repo_${repoId}`);
    const queryEmbedding = await this.getEmbedding(query);

    const filter: any = {};
    if (options.language) {
      filter.language = { $eq: options.language };
    }
    if (options.filePath) {
      filter.file_path = { $eq: options.filePath };
    }

    const results = await ns.query({
      vector: queryEmbedding,
      topK: options.limit || 10,
      includeMetadata: true,
      filter: Object.keys(filter).length > 0 ? filter : undefined,
    });

    return results.matches.map(m => ({
      filePath: m.metadata?.file_path,
      language: m.metadata?.language,
      content: m.metadata?.content,
      startLine: m.metadata?.start_line,
      endLine: m.metadata?.end_line,
      score: m.score,
    }));
  }

  // Find similar code patterns (traced)
  async findSimilarCode(codeSnippet: string, repoId: string) {
    const ns = this.index.namespace(`repo_${repoId}`);
    const codeEmbedding = await this.getEmbedding(codeSnippet);

    const results = await ns.query({
      vector: codeEmbedding,
      topK: 5,
      includeMetadata: true,
    });

    return results.matches;
  }

  private chunkCode(file: CodeFile) {
    // Implementation for chunking code files
    return [];
  }

  private async getEmbedding(text: string): Promise<number[]> {
    // Implementation for getting embeddings
    return [];
  }
}

5. Anomaly Detection Pipeline

Detect anomalies using vector similarity:

class AnomalyDetector {
  private index: any;
  private threshold: number;

  constructor(threshold = 0.7) {
    const pinecone = new Pinecone();
    this.index = pinecone.index("behavior-patterns");
    this.threshold = threshold;
  }

  // Index normal behavior patterns (traced)
  async indexNormalPatterns(patterns: BehaviorPattern[]) {
    await this.index.namespace("normal").upsert(
      patterns.map(p => ({
        id: p.id,
        values: p.embedding,
        metadata: {
          pattern_type: p.type,
          frequency: p.frequency,
          timestamp: p.timestamp,
        },
      }))
    );
  }

  // Check if behavior is anomalous (traced)
  async detectAnomaly(behaviorEmbedding: number[]): Promise<AnomalyResult> {
    const results = await this.index.namespace("normal").query({
      vector: behaviorEmbedding,
      topK: 5,
      includeMetadata: true,
    });

    // If no close matches, it's anomalous
    const maxSimilarity = results.matches[0]?.score || 0;
    const isAnomalous = maxSimilarity < this.threshold;

    return {
      isAnomalous,
      confidence: isAnomalous ? 1 - maxSimilarity : maxSimilarity,
      closestPatterns: results.matches.slice(0, 3),
    };
  }

  // Batch anomaly detection (traced)
  async detectAnomaliesBatch(behaviors: number[][]): Promise<AnomalyResult[]> {
    return Promise.all(
      behaviors.map(b => this.detectAnomaly(b))
    );
  }
}

Performance Tips

Batch Operations

For high-throughput scenarios, batch your upserts:

async function batchUpsert(index: any, vectors: any[], batchSize = 100) {
  for (let i = 0; i < vectors.length; i += batchSize) {
    const batch = vectors.slice(i, i + batchSize);
    await index.upsert(batch);  // Each batch creates a trace span
  }
}

Use Namespaces for Isolation

Namespaces provide logical separation and can improve query performance:

// Separate by data type
const docsIndex = index.namespace("documents");
const productsIndex = index.namespace("products");

// Or by tenant
const tenantIndex = index.namespace(`tenant_${tenantId}`);

Environment Variables

| Variable | Description | |----------|-------------| | PINECONE_API_KEY | Your Pinecone API key | | FI_API_KEY | Your Future AGI API key | | FI_PROJECT_NAME | Your project name |

Troubleshooting

Connection Issues

Ensure your Pinecone API key is valid and the index exists:

const pinecone = new Pinecone({ apiKey: process.env.PINECONE_API_KEY });
const indexes = await pinecone.listIndexes();
console.log("Available indexes:", indexes);

Large Span Sizes

Disable vector capture for production:

new PineconeInstrumentation({
  instrumentationConfig: {
    captureQueryVectors: false,
    captureResultVectors: false,
  },
});

Development

# Build
pnpm build

# Run tests
pnpm test

# Type check
pnpm tsc --noEmit

License

Apache-2.0

Links