Skip to content

FirestoreTraceStore

FirestoreTraceStore is the production-grade Firestore implementation of TraceStore. It provides span write-batching, configurable collection prefixes, automatic Date/Timestamp conversion, and depth-safe serialization for Firestore's 20-level nesting limit.

Import

import { FirestoreTraceStore } from "@modernpath/agent-framework";

Optional Dependency

The @google-cloud/firestore package is loaded lazily. It is only required when FirestoreTraceStore is instantiated. Consumers that use InMemoryTraceStore do not need the Firestore dependency.

Constructor

new FirestoreTraceStore(config: FirestoreTraceStoreConfig)

FirestoreTraceStoreConfig

interface FirestoreTraceStoreConfig extends TraceStoreConfig {
  projectId?: string;
  collectionPrefix?: string;
  firestore?: any;
  spanBatchSize?: number;
  spanFlushIntervalMs?: number;
}
Property Type Default Description
captureContent boolean -- Required. Store full prompts/responses in spans.
defaultTags string[] -- Tags automatically added to every new trace.
projectId string -- GCP project ID for Firestore client construction. Ignored if firestore is provided.
collectionPrefix string "" Prefix for all collection names (e.g. "prod_" produces "prod_traces", "prod_sessions").
firestore any -- Pre-constructed Firestore instance. Takes priority over projectId.
spanBatchSize number 10 Flush pending spans when this many are buffered per trace.
spanFlushIntervalMs number 1000 Flush pending spans after this many milliseconds of inactivity.

Firestore Collection Schema

{prefix}traces/{traceId}                        -- Trace document
{prefix}traces/{traceId}/spans/{spanId}          -- Span subcollection
{prefix}sessions/{sessionId}                     -- Session document
{prefix}config_snapshots/{snapshotId}            -- ConfigSnapshot document

For the listTraces and listSessions queries with filters, create composite indexes on:

  • traces: (agentName, startedAt DESC), (sessionId, startedAt DESC), (userId, startedAt DESC), (status, startedAt DESC)
  • sessions: (userId, updatedAt DESC)

Span Write-Batching

Spans are buffered in memory and flushed to Firestore in batches to reduce write operations:

  1. Each addSpan() call adds the span to a per-trace buffer
  2. When the buffer reaches spanBatchSize, it flushes to Firestore as a batch write
  3. A timer flushes remaining spans after spanFlushIntervalMs of inactivity
  4. endTrace() flushes all pending spans for that trace before finalizing

This batching reduces Firestore write operations by up to 10x for agents that produce many spans per execution.

Serialization

The store handles Firestore-specific serialization:

  • Date objects are converted to Firestore Timestamps on write and back to Date on read
  • Deep nesting (> 15 levels) is automatically collapsed to JSON strings to respect Firestore's 20-level nesting limit
  • Undefined values are stripped (Firestore rejects them by default)
  • Internal objects like __traceContext are excluded from serialization

Debugging

Enable debug logging by setting the DEBUG environment variable:

DEBUG=firestore-trace-store node server.js

Error logs are always emitted regardless of the DEBUG setting, since they indicate potential data loss.

Code Example

import { FirestoreTraceStore } from "@modernpath/agent-framework";

// Production setup with GCP project
const traceStore = new FirestoreTraceStore({
  projectId: "my-gcp-project",
  captureContent: false, // Production: metadata only
  collectionPrefix: "prod_",
  defaultTags: ["production", "v2"],
  spanBatchSize: 10,
  spanFlushIntervalMs: 1000,
});

// Or with a pre-constructed Firestore instance
import { Firestore } from "@google-cloud/firestore";

const db = new Firestore({
  projectId: "my-gcp-project",
  ignoreUndefinedProperties: true,
});

const traceStore2 = new FirestoreTraceStore({
  firestore: db,
  captureContent: true, // Staging: full content for eval
  collectionPrefix: "staging_",
});

Multi-Environment Configuration

function createTraceStore(env: "production" | "staging" | "test") {
  if (env === "test") {
    return new InMemoryTraceStore({ captureContent: true });
  }

  return new FirestoreTraceStore({
    projectId: process.env.GCP_PROJECT_ID,
    captureContent: env === "staging",
    collectionPrefix: `${env}_`,
    defaultTags: [env],
  });
}