> ## 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.

# Metadata

> Attach structured context to traces for precise filtering, analysis, and correlation with your business data

# 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.

<Steps>
  <Step title="Understand Metadata Structure" icon="brackets-curly">
    Metadata is a JSON object with string keys and arbitrary values (strings, numbers, booleans, nested objects, arrays).

    **Example metadata:**

    ```json theme={null}
    {
      "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

    <Warning>
      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.
    </Warning>
  </Step>

  <Step title="Add Metadata to Traces" icon="tag">
    Attach metadata when creating traces or update it dynamically as your code executes.

    **Python (decorator pattern):**

    ```python theme={null}
    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):**

    ```python theme={null}
    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:**

    ```typescript theme={null}
    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...
    });
    ```
  </Step>

  <Step title="Update Metadata Incrementally" icon="plus">
    Metadata updates **merge** based on top-level keys. You can add new keys throughout the trace lifecycle without overwriting existing data.

    **Example: Incremental updates**

    ```python theme={null}
    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": "..."}
    ```

    <Warning>
      **Do not** write the same top-level key multiple times:

      ```python theme={null}
      # 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:

      ```python theme={null}
      # 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"})
      ```
    </Warning>
  </Step>

  <Step title="Query Traces by Metadata" icon="magnifying-glass">
    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
  </Step>

  <Step title="Aggregate Metrics by Metadata" icon="chart-line">
    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")
  </Step>
</Steps>

# Why Use Metadata?

<AccordionGroup>
  <Accordion title="Multi-Tenant Applications: Track Costs and Performance Per Customer" icon="users">
    Attach `tenant_id` to isolate costs, performance, and errors by customer.

    ```python theme={null}
    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.
  </Accordion>

  <Accordion title="Feature Flagging: Measure Impact of New Features" icon="flag">
    Tag experiment variants to compare costs, latency, and quality.

    ```python theme={null}
    abv.update_current_trace(metadata={"experiment_variant": "v2", "experiment_id": "summarization-test"})
    ```

    Compare metrics by variant to make data-driven rollout decisions.
  </Accordion>

  <Accordion title="Deployment Tracking: Correlate Issues with Code Versions" icon="code-branch">
    Attach deployment version to correlate issues with releases.

    ```python theme={null}
    abv.update_current_trace(metadata={"deployment_version": "v1.2.3", "git_commit_sha": "abc123"})
    ```

    Filter by version to compare error rates and identify regressions.
  </Accordion>

  <Accordion title="Model and Prompt Versioning: Compare Configurations" icon="sliders">
    Track model configurations to compare cost, quality, and latency.

    ```python theme={null}
    abv.update_current_trace(metadata={"model_provider": "openai", "prompt_version": "v3"})
    ```

    Filter by `metadata.model_provider` to quantify tradeoffs and optimize selection.
  </Accordion>

  <Accordion title="User Segmentation: Analyze by Subscription Tier or Region" icon="users-viewfinder">
    Segment traces by user attributes to analyze costs by cohort.

    ```python theme={null}
    abv.update_current_trace(metadata={"subscription_tier": "enterprise", "region": "us-west"})
    ```

    Group by tier or region to optimize model selection and justify pricing.
  </Accordion>

  <Accordion title="Error Tracking: Categorize and Debug Failures" icon="triangle-exclamation">
    Categorize errors for faster debugging.

    ```python theme={null}
    abv.update_current_trace(metadata={"error_type": "timeout", "timeout_seconds": 30})
    ```

    Filter by `metadata.error_type` to identify patterns and set alerts.
  </Accordion>
</AccordionGroup>

# Implementation Guide

<AccordionGroup>
  <Accordion title="Python: Using the @observe() Decorator" icon="python">
    Use the `@observe()` decorator to automatically trace functions. Update metadata with `abv.update_current_trace()` and `abv.update_current_span()`.

    **Setup:**

    ```bash theme={null}
    pip install abvdev
    ```

    **Basic usage:**

    ```python theme={null}
    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:**

    ```python theme={null}
    @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
    ```
  </Accordion>

  <Accordion title="Python: Manual Span Creation" icon="python">
    Create spans manually and attach metadata at the trace or span level.

    **Trace-level metadata:**

    ```python theme={null}
    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:**

    ```python theme={null}
    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})
    ```
  </Accordion>

  <Accordion title="JavaScript/TypeScript: Context Managers" icon="js">
    Use the `@abvdev/tracing` package to add metadata to traces and observations.

    **Setup:**

    ```bash theme={null}
    npm install @abvdev/tracing @abvdev/otel @opentelemetry/sdk-node dotenv
    ```

    **Configuration (instrumentation.ts):**

    ```typescript theme={null}
    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:**

    ```typescript theme={null}
    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();
    ```
  </Accordion>

  <Accordion title="JavaScript/TypeScript: observe Wrapper" icon="js">
    Wrap existing functions with automatic tracing and metadata updates.

    **Example:**

    ```typescript theme={null}
    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();
    ```
  </Accordion>

  <Accordion title="JavaScript/TypeScript: Manual Span Creation" icon="js">
    Create spans manually with metadata attached.

    **Example:**

    ```typescript theme={null}
    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();
    ```
  </Accordion>
</AccordionGroup>

# Related Features

<CardGroup cols={2}>
  <Card title="Tags" icon="tags" href="/developer/basic-features/tags">
    Add simple string labels to traces for quick categorization and filtering
  </Card>

  <Card title="Sessions" icon="diagram-project" href="/developer/basic-features/sessions">
    Group related traces by user journey or job to see end-to-end workflows
  </Card>

  <Card title="Environments" icon="layer-group" href="/developer/basic-features/environments">
    Separate development, staging, and production traces for clean comparisons
  </Card>

  <Card title="User Tracking" icon="user" href="/developer/basic-features/user-tracking">
    Link traces to user accounts for faster debugging and GDPR-compliant handling
  </Card>
</CardGroup>
