Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.abv.dev/llms.txt

Use this file to discover all available pages before exploring further.

How Metadata Works

Metadata is arbitrary structured JSON data attached to traces or observations. Unlike tags (simple string labels), metadata is key-value data that enables precise filtering and rich analysis.

Understand Metadata Structure

Metadata is a JSON object with string keys and arbitrary values (strings, numbers, booleans, nested objects, arrays).Example metadata:
{
  "tenant_id": "acme-corp",
  "feature": "document-summarization",
  "experiment_version": "v2.3",
  "model_config": {
    "temperature": 0.7,
    "max_tokens": 1000
  },
  "user_tier": "enterprise",
  "region": "us-west-2"
}
Key properties:
  • Top-level keys: Identify dimensions (tenant, feature, version, etc.)
  • Nested objects: Group related data (model config, user details)
  • Merged on update: Adding new keys preserves existing metadata
  • Queryable: Filter traces using SQL-like queries on metadata fields
Avoid overwriting the same top-level key multiple times within a single trace. Metadata updates merge based on top-level keys, and rewriting a key produces undefined behavior. Add keys incrementally instead.

Add Metadata to Traces

Attach metadata when creating traces or update it dynamically as your code executes.Python (decorator pattern):
from abvdev import observe, ABV

abv = ABV(api_key="sk-abv-...", host="https://app.abv.dev")

@observe()
def process_request(user_id, tenant_id):
    # Add metadata to the current trace
    abv.update_current_trace(
        metadata={
            "tenant_id": tenant_id,
            "feature": "query-processing",
            "version": "1.2.3"
        }
    )

    # Process the request
    result = handle_query(user_id)
    return result

process_request(user_id="user-456", tenant_id="acme-corp")
Python (manual span creation):
with abv.start_as_current_span(name="process-order") as root_span:
    # Add metadata to the trace
    root_span.update_trace(metadata={"order_id": "order-123", "region": "us-west"})

    # Add metadata to the current span
    root_span.update(metadata={"stage": "validation"})

    # Create child span with metadata
    with root_span.start_as_current_generation(
        name="generate-summary",
        model="gpt-4o",
        metadata={"temperature": 0.7, "max_tokens": 500}
    ) as gen:
        # Update metadata later if needed
        gen.update(metadata={"completion_reason": "stop"})
JavaScript/TypeScript:
import { startActiveObservation, updateActiveTrace } from '@abvdev/tracing';

await startActiveObservation('process-request', async (span) => {
  // Add metadata to the trace
  updateActiveTrace({
    metadata: {
      tenant_id: 'acme-corp',
      feature: 'summarization',
      version: '1.2.3'
    }
  });

  // Add metadata to the observation
  span.update({
    metadata: { stage: 'processing' }
  });

  // Process request...
});

Update Metadata Incrementally

Metadata updates merge based on top-level keys. You can add new keys throughout the trace lifecycle without overwriting existing data.Example: Incremental updates
with abv.start_as_current_span(name="workflow") as span:
    # First update: Add initial context
    span.update_trace(metadata={"status": "started", "user_id": "user-123"})

    # Second update: Add more context (merges with existing)
    span.update_trace(metadata={"feature": "summarization", "model": "gpt-4"})

    # Third update: Add error details if something fails
    try:
        result = process_data()
    except Exception as e:
        span.update_trace(metadata={"error_type": "timeout", "error_message": str(e)})
        raise

# Final metadata: {"status": "started", "user_id": "user-123", "feature": "summarization", "model": "gpt-4", "error_type": "timeout", "error_message": "..."}
Do not write the same top-level key multiple times:
# BAD: Overwrites "status" key unpredictably
span.update_trace(metadata={"status": "started"})
span.update_trace(metadata={"status": "processing"})  # Undefined behavior
span.update_trace(metadata={"status": "completed"})   # Undefined behavior
Instead, use different keys or nested objects:
# GOOD: Different keys for each stage
span.update_trace(metadata={"stage_1": "started"})
span.update_trace(metadata={"stage_2": "processing"})
span.update_trace(metadata={"stage_3": "completed"})

Query Traces by Metadata

In the ABV Dashboard, filter traces using SQL-like queries on metadata fields.Query examples:
  • metadata.tenant_id = "acme-corp" → All traces for tenant “acme-corp”
  • metadata.feature = "summarization" → All traces for the summarization feature
  • metadata.version = "v2.3" AND environment = "production" → Traces for specific version in production
  • metadata.user_tier = "enterprise" → Traces for enterprise users only
  • metadata.model_config.temperature > 0.5 → Traces with high temperature settings
Dashboard workflow:
  1. Navigate to the Traces view
  2. Click “Add Filter”
  3. Select “Metadata” and enter your query
  4. Results update instantly
Export filtered traces:
  • Export to CSV for analysis
  • Create datasets for evaluations
  • Generate reports for stakeholders

Aggregate Metrics by Metadata

Use metadata dimensions to aggregate costs, latency, and quality metrics for business analysis.Example analyses:
  • Cost by tenant: Group traces by metadata.tenant_id to calculate per-tenant LLM costs
  • Latency by feature: Filter by metadata.feature to identify slow features
  • Quality by experiment: Compare metadata.experiment_version to measure A/B test results
  • Error rate by region: Analyze metadata.region to detect regional issues
Dashboard aggregations:
  • Navigate to Metrics → Custom Dashboards
  • Create charts grouped by metadata fields
  • Track trends over time for specific dimensions
  • Set alerts based on metadata filters (e.g., “alert when enterprise tier costs exceed threshold”)

Why Use Metadata?

Attach tenant_id to isolate costs, performance, and errors by customer.
abv.update_current_trace(metadata={"tenant_id": tenant_id, "subscription_tier": "enterprise"})
Filter by metadata.tenant_id to calculate per-tenant costs or detect tenant-specific errors.
Tag experiment variants to compare costs, latency, and quality.
abv.update_current_trace(metadata={"experiment_variant": "v2", "experiment_id": "summarization-test"})
Compare metrics by variant to make data-driven rollout decisions.
Attach deployment version to correlate issues with releases.
abv.update_current_trace(metadata={"deployment_version": "v1.2.3", "git_commit_sha": "abc123"})
Filter by version to compare error rates and identify regressions.
Track model configurations to compare cost, quality, and latency.
abv.update_current_trace(metadata={"model_provider": "openai", "prompt_version": "v3"})
Filter by metadata.model_provider to quantify tradeoffs and optimize selection.
Segment traces by user attributes to analyze costs by cohort.
abv.update_current_trace(metadata={"subscription_tier": "enterprise", "region": "us-west"})
Group by tier or region to optimize model selection and justify pricing.
Categorize errors for faster debugging.
abv.update_current_trace(metadata={"error_type": "timeout", "timeout_seconds": 30})
Filter by metadata.error_type to identify patterns and set alerts.

Implementation Guide

Use the @observe() decorator to automatically trace functions. Update metadata with abv.update_current_trace() and abv.update_current_span().Setup:
pip install abvdev
Basic usage:
from abvdev import observe, ABV

abv = ABV(api_key="sk-abv-...", host="https://app.abv.dev")

@observe()
def process_data(tenant_id, feature_name):
    # Add metadata to the trace
    abv.update_current_trace(
        metadata={
            "tenant_id": tenant_id,
            "feature": feature_name,
            "version": "1.2.3"
        }
    )

    # Add metadata to the current span
    abv.update_current_span(
        metadata={"processing_stage": "initial"}
    )

    # Process data...
    return "Processing complete"

process_data(tenant_id="acme-corp", feature_name="summarization")
Incremental updates:
@observe()
def complex_workflow(data):
    # Initial metadata
    abv.update_current_trace(metadata={"status": "started"})

    step1_result = process_step1(data)
    abv.update_current_trace(metadata={"step1_duration_ms": step1_result.duration})

    step2_result = process_step2(step1_result)
    abv.update_current_trace(metadata={"step2_duration_ms": step2_result.duration})

    # Final metadata
    abv.update_current_trace(metadata={"status": "completed"})
    return step2_result
Create spans manually and attach metadata at the trace or span level.Trace-level metadata:
from abvdev import ABV

abv = ABV(api_key="sk-abv-...", host="https://app.abv.dev")

# Add metadata to the trace
with abv.start_as_current_span(name="process-request") as root_span:
    # Set trace metadata
    root_span.update_trace(metadata={
        "request_id": "req_12345",
        "tenant_id": "acme-corp",
        "feature": "document-processing"
    })

    # Set span metadata
    root_span.update(metadata={"stage": "parsing"})

    # Your code here
    result = process_document()
Span-level metadata:
with abv.start_as_current_span(name="workflow") as root_span:
    root_span.update_trace(metadata={"workflow_id": "wf-123"})

    # Child span with its own metadata
    with root_span.start_as_current_generation(
        name="generate-summary",
        model="gpt-4o",
        metadata={"temperature": 0.7, "max_tokens": 1000}
    ) as gen:
        # Update generation metadata
        gen.update(metadata={"completion_reason": "stop", "tokens_used": 850})
Use the @abvdev/tracing package to add metadata to traces and observations.Setup:
npm install @abvdev/tracing @abvdev/otel @opentelemetry/sdk-node dotenv
Configuration (instrumentation.ts):
import dotenv from 'dotenv';
dotenv.config();

import { NodeSDK } from '@opentelemetry/sdk-node';
import { ABVSpanProcessor } from '@abvdev/otel';

const sdk = new NodeSDK({
  spanProcessors: [
    new ABVSpanProcessor({
      apiKey: process.env.ABV_API_KEY,
      baseUrl: process.env.ABV_BASE_URL,
      exportMode: 'immediate',
      flushAt: 1,
      flushInterval: 1
    })
  ]
});

sdk.start();
Add metadata:
import './instrumentation';  // Must be first import
import { startActiveObservation, updateActiveTrace } from '@abvdev/tracing';

async function main() {
  await startActiveObservation('process-request', async (span) => {
    // Add trace metadata
    updateActiveTrace({
      metadata: {
        tenant_id: 'acme-corp',
        feature: 'summarization',
        version: '1.2.3'
      }
    });

    // Add span metadata
    span.update({
      metadata: { stage: 'processing' }
    });

    // Process request...
  });
}

main();
Wrap existing functions with automatic tracing and metadata updates.Example:
import './instrumentation';
import { observe, updateActiveTrace } from '@abvdev/tracing';

// Original function
async function fetchData(source: string, tenantId: string) {
  // Add metadata
  updateActiveTrace({
    metadata: {
      tenant_id: tenantId,
      data_source: source,
      timestamp: new Date().toISOString()
    }
  });

  // Fetch data
  const response = await fetch(source);
  return await response.json();
}

// Wrap with observe
const tracedFetchData = observe(fetchData, {
  name: 'fetch-data-operation'
});

// Use traced version
async function main() {
  const result = await tracedFetchData('https://api.example.com/data', 'acme-corp');
}

main();
Create spans manually with metadata attached.Example:
import './instrumentation';
import { startObservation } from '@abvdev/tracing';

const span = startObservation('manual-operation', {
  input: { query: 'Process this data' },
  metadata: {
    tenant_id: 'acme-corp',
    feature: 'data-processing',
    priority: 'high'
  }
});

// Update trace metadata
span.updateTrace({
  metadata: {
    workflow_id: 'wf-123',
    user_id: 'user-456'
  }
});

// Update span metadata
span.update({
  metadata: { processing_stage: 'validation' }
});

span.end();

Related Features

Tags

Add simple string labels to traces for quick categorization and filtering

Sessions

Group related traces by user journey or job to see end-to-end workflows

Environments

Separate development, staging, and production traces for clean comparisons

User Tracking

Link traces to user accounts for faster debugging and GDPR-compliant handling