> ## Documentation Index
> Fetch the complete documentation index at: https://www.adaline.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Span

# Span Class

The `Span` class represents a specific operation within a trace. Spans are the building blocks of observability, tracking individual steps like LLM calls, tool executions, database queries, or any custom operation in your LLM pipeline.

## Overview

A span captures:

* **Operation details**: name, timing, status
* **Content**: input/output data for different span types (Model, Tool, Retrieval, etc.)
* **Metadata**: tags, attributes for filtering and analysis
* **Hierarchy**: parent-child relationships for nested operations
* **Evaluation**: optional evaluator execution

Common span types:

* **Model** - LLM inference calls
* **ModelStream** - Streaming LLM responses
* **Tool** - Function/API executions
* **Retrieval** - RAG and vector searches
* **Embeddings** - Embedding generation
* **Function** - Custom application logic
* **Guardrail** - Safety/compliance checks
* **Other** - Any custom operation

## Creation

Create spans from a trace or parent span:

```typescript theme={null}
// From trace
const span = trace.logSpan({
  name: 'LLM Call',
  tags: ['llm']
});

// From parent span (nested)
const childSpan = parentSpan.logSpan({
  name: 'Tool Execution',
  tags: ['tool']
});
```

## Properties

### span

```typescript theme={null}
span: CreateLogSpanRequest
```

The underlying span request object containing all span data.

**Structure:**

```typescript theme={null}
{
  projectId: string;
  span: {
    name: string;
    status: SpanStatus;
    referenceId: string;
    traceId?: string;
    traceReferenceId: string;
    parentReferenceId?: string;
    sessionId?: string;
    promptId?: string;
    deploymentId?: string;
    runEvaluation?: boolean;
    tags?: string[];
    attributes?: Record<string, string | number | boolean>;
    content: LogSpanContent;
    startedAt: number;
    endedAt: number;
  };
}
```

## Methods

### logSpan()

Create a nested child span under this span.

```typescript theme={null}
logSpan(options: LogSpanOptions): Span
```

This allows you to create hierarchical span relationships like:

```text theme={null}
Trace
  └─ Parent Span
      ├─ Child Span 1
      └─ Child Span 2
          └─ Grandchild Span
```

#### Parameters

<ParamField path="options" type="object" required>
  Same parameters as `Trace.logSpan()`. See [Trace.logSpan()](/docs/reference/sdk/v2/typescript/classes/trace#logspan) for details.
</ParamField>

#### Returns

<ResponseField name="span" type="Span">
  A new child Span instance linked to this parent span.
</ResponseField>

#### Example

```typescript theme={null}
const parentSpan = trace.logSpan({ name: 'RAG Pipeline' });

// Create child spans
const embeddingSpan = parentSpan.logSpan({
  name: 'Generate Embedding',
  tags: ['embedding']
});
embeddingSpan.end();

const retrievalSpan = parentSpan.logSpan({
  name: 'Vector Search',
  tags: ['retrieval']
});
retrievalSpan.end();

parentSpan.end();
```

### update()

Update span metadata and content.

```typescript theme={null}
update(updates: SpanUpdates): this
```

#### Parameters

<ParamField path="updates" type="object" required>
  <Expandable title="properties">
    <ParamField path="name" type="string" optional>
      Update the span name.
    </ParamField>

    <ParamField path="status" type="SpanStatus" optional>
      Update [SpanStatus](/docs/reference/sdk/v2/typescript/types/SpanStatus): `'success' | 'failure' | 'aborted' | 'cancelled' | 'unknown'`
    </ParamField>

    <ParamField path="content" type="LogSpanContent" optional>
      Update the span [LogSpanContent](/docs/reference/sdk/v2/typescript/types/LogSpanContent) (input/output data).
    </ParamField>

    <ParamField path="runEvaluation" type="boolean" optional>
      Update whether to run evaluators.
    </ParamField>

    <ParamField path="tags" type="string[]" optional>
      Update tags (replaces existing).
    </ParamField>

    <ParamField path="attributes" type="Record<string, string | number | boolean>" optional>
      Update attributes (merges with existing).
    </ParamField>
  </Expandable>
</ParamField>

#### Returns

Returns `this` for method chaining.

#### Examples

<CodeGroup>
  ```typescript Update Status theme={null}
  const span = trace.logSpan({ name: 'API Call' });

  try {
    await callAPI();
    span.update({ status: 'success' });
  } catch (error) {
    span.update({ status: 'failure' });
  }

  span.end();
  ```

  ```typescript Update Content theme={null}
  const span = trace.logSpan({ name: 'LLM Call' });

  const gatewayResponse = await gateway.completeChat({ model, messages, tools });

  span.update({
    status: 'success',
    content: {
      type: 'Model',
      provider: 'openai',
      model: 'gpt-4o',
      input: JSON.stringify(gatewayResponse.provider.request),
      output: JSON.stringify(gatewayResponse.provider.response),
      cost: 0.002
    }
  });

  span.end();
  ```

  ```typescript Method Chaining theme={null}
  trace.logSpan({ name: 'Quick Op' })
    .update({ status: 'unknown' })
    .update({ tags: ['fast'] })
    .update({ status: 'success' })
    .end();
  ```
</CodeGroup>

### end()

Mark the span as complete and ready to be flushed.

```typescript theme={null}
end(): string | undefined
```

#### Behavior

1. Sets `endedAt` timestamp
2. Marks the span as `ready` in the monitor's buffer
3. Recursively ends all child spans
4. Returns the span's reference ID

<Warning>
  Always call `end()` on your spans! Spans that are never ended will never be flushed to the API.
</Warning>

#### Returns

<ResponseField name="referenceId" type="string | undefined">
  The span's reference ID for correlation.
</ResponseField>

#### Example

```typescript theme={null}
const span = trace.logSpan({ name: 'Operation' });

try {
  await doWork();
  span.update({ status: 'success' });
} finally {
  span.end(); // Always called
}
```

## Span Content Types

Different content types for different operations. See [LogSpanContent Type](/docs/reference/sdk/v2/typescript/types/LogSpanContent#span-content-types) for complete documentation.

### Model (LLM Inference)

For standard LLM completions ([LogSpanModelContent](/docs/reference/sdk/v2/typescript/types/LogSpanModelContent)).

```typescript theme={null}
span.update({
  content: {
    type: 'Model',
    provider: 'openai',           // Provider name
    model: 'gpt-4o',              // Model identifier
    variables: {                   // Optional prompt variables
      userName: 'John',
      context: 'support'
    },
    cost: 0.002,                  // Optional cost in USD
    input: JSON.stringify([       // Input messages
      { role: 'user', content: 'Hello' }
    ]),
    output: JSON.stringify({      // Output message
      role: 'assistant',
      content: 'Hi there!'
    }),
    expected: JSON.stringify({    // Optional expected output for evaluation
      role: 'assistant',
      content: 'Hello! How can I help?'
    })
  }
});
```

<Note>
  The `variables` field is typed as `LogSpanVariable | null` in the SDK. [`LogSpanVariable`](/docs/reference/sdk/v2/typescript/types/LogSpanVariable) is a record type, so you can pass a plain object as shown above.
</Note>

### ModelStream (Streaming LLM)

For streaming LLM responses.

```typescript theme={null}
let chunks = '';
for await (const chunk of stream) {
  chunks += chunk;
}

span.update({
  content: {
    type: 'ModelStream',
    provider: 'anthropic',
    model: 'claude-3-opus',
    input: JSON.stringify(messages),
    output: chunks,                        // Raw stream chunks
    aggregateOutput: JSON.stringify({      // Final aggregated response
      role: 'assistant',
      content: fullResponse
    }),
    cost: 0.005
  }
});
```

### Tool (Function/API Call)

For tool executions and API calls.

```typescript theme={null}
span.update({
  content: {
    type: 'Tool',
    input: JSON.stringify({
      function: 'getWeather',
      city: 'San Francisco'
    }),
    output: JSON.stringify({
      temperature: 72,
      conditions: 'sunny',
      humidity: 65
    })
  }
});
```

### Retrieval (RAG/Vector Search)

For document retrieval and vector searches.

```typescript theme={null}
span.update({
  content: {
    type: 'Retrieval',
    input: JSON.stringify({
      query: 'What is machine learning?',
      topK: 5,
      filters: { category: 'AI' }
    }),
    output: JSON.stringify({
      documents: [
        { id: 'doc1', score: 0.95, text: '...' },
        { id: 'doc2', score: 0.89, text: '...' }
      ]
    })
  }
});
```

### Embeddings

For embedding generation.

```typescript theme={null}
span.update({
  content: {
    type: 'Embeddings',
    input: JSON.stringify({
      texts: ['text1', 'text2', 'text3'],
      model: 'text-embedding-3-large'
    }),
    output: JSON.stringify({
      embeddings: [[0.1, 0.2, ...], [0.3, 0.4, ...]],
      dimensions: 3072
    })
  }
});
```

### Function (Custom Logic)

For custom application logic.

```typescript theme={null}
span.update({
  content: {
    type: 'Function',
    input: JSON.stringify({
      operation: 'processData',
      params: { id: 123 }
    }),
    output: JSON.stringify({
      result: 'success',
      itemsProcessed: 42
    })
  }
});
```

### Guardrail (Safety Check)

For safety and compliance checks.

```typescript theme={null}
span.update({
  content: {
    type: 'Guardrail',
    input: JSON.stringify({
      text: 'User input to check...',
      checks: ['toxicity', 'pii', 'harm']
    }),
    output: JSON.stringify({
      safe: true,
      scores: {
        toxicity: 0.05,
        pii: 0.01,
        harm: 0.02
      }
    })
  }
});
```

### Other (Custom)

For any other operation type.

```typescript theme={null}
span.update({
  content: {
    type: 'Other',
    input: JSON.stringify({ custom: 'input' }),
    output: JSON.stringify({ custom: 'output' })
  }
});
```

## Complete Examples

### Example 1: LLM Call with Deployment

```typescript theme={null}
import { Adaline } from '@adaline/client';
import { Gateway } from '@adaline/gateway';
import { OpenAI } from '@adaline/openai';

const adaline = new Adaline();
const gateway = new Gateway();
const openaiProvider = new OpenAI();
const monitor = adaline.initMonitor({ projectId: 'my-project' });

async function generateResponse(userId: string, message: string) {
  // Get deployment
  const deployment = await adaline.getLatestDeployment({
    promptId: 'chat-prompt',
    deploymentEnvironmentId: 'environment_abc123'
  });

  const trace = monitor.logTrace({
    name: 'Chat Completion',
    sessionId: userId
  });

  const span = trace.logSpan({
    name: 'LLM Completion',
    promptId: deployment.promptId,
    deploymentId: deployment.id,
    runEvaluation: true,
    tags: ['llm', 'openai']
  });

  try {
    const model = openaiProvider.chatModel({
      modelName: deployment.prompt.config.model,
      apiKey: process.env.OPENAI_API_KEY!
    });

    const gatewayResponse = await gateway.completeChat({
      model,
      config: deployment.prompt.config.settings,
      messages: [
        ...deployment.prompt.messages,
        { role: 'user', content: [{ modality: 'text', value: message }] }
      ],
      tools: deployment.prompt.tools
    });

    const reply = String(gatewayResponse.response.messages[0].content[0].value);

    span.update({
      status: 'success',
      content: {
        type: 'Model',
        provider: deployment.prompt.config.providerName,
        model: deployment.prompt.config.model,
        input: JSON.stringify(gatewayResponse.provider.request),
        output: JSON.stringify(gatewayResponse.provider.response)
      },
    });

    return reply;

  } catch (error) {
    span.update({
      status: 'failure',
      attributes: {
        error: error instanceof Error ? error.message : String(error)
      }
    });
    throw error;

  } finally {
    span.end();
    trace.end();
  }
}
```

### Example 2: Nested RAG Pipeline

```typescript theme={null}
import { ChromaClient } from 'chromadb';

const chroma = new ChromaClient();

async function ragPipeline(question: string) {
  const trace = monitor.logTrace({
    name: 'RAG Pipeline',
    tags: ['rag']
  });

  const pipelineSpan = trace.logSpan({
    name: 'RAG Workflow',
    tags: ['pipeline']
  });

  try {
    // Step 1: Embedding
    const embedSpan = pipelineSpan.logSpan({
      name: 'Generate Embedding',
      tags: ['embedding']
    });

    const embedResponse = await openai.embeddings.create({
      model: 'text-embedding-3-large',
      input: question
    });
    const embedding = embedResponse.data[0].embedding;

    embedSpan.update({
      status: 'success',
      content: {
        type: 'Embeddings',
        input: JSON.stringify({ text: question }),
        output: JSON.stringify({
          dimensions: embedding.length,
          model: 'text-embedding-3-large'
        })
      }
    });
    embedSpan.end();

    // Step 2: Retrieval
    const retrievalSpan = pipelineSpan.logSpan({
      name: 'Vector Search',
      tags: ['retrieval', 'chromadb']
    });

    const collection = await chroma.getCollection({ name: 'docs' });
    const results = await collection.query({
      queryEmbeddings: [embedding],
      nResults: 5
    });

    retrievalSpan.update({
      status: 'success',
      content: {
        type: 'Retrieval',
        input: JSON.stringify({
          query: question,
          collection: 'docs',
          topK: 5
        }),
        output: JSON.stringify({
          documentIds: results.ids[0],
          distances: results.distances?.[0],
          documents: results.documents[0]
        })
      },
      attributes: {
        documentsFound: results.ids[0].length
      }
    });
    retrievalSpan.end();

    // Step 3: LLM Generation
    const llmSpan = pipelineSpan.logSpan({
      name: 'Generate Answer',
      runEvaluation: true,
      tags: ['llm', 'answer']
    });

    const context = results.documents[0].join('\n\n');

    const llmModel = openaiProvider.chatModel({
      modelName: 'gpt-4o',
      apiKey: process.env.OPENAI_API_KEY!
    });

    const gatewayResponse = await gateway.completeChat({
      model: llmModel,
      messages: [
        { role: 'system', content: [{ modality: 'text', value: 'Answer based on the provided context.' }] },
        { role: 'user', content: [{ modality: 'text', value: `Context:\n${context}\n\nQuestion: ${question}` }] }
      ]
    });

    const answer = String(gatewayResponse.response.messages[0].content[0].value);

    llmSpan.update({
      status: 'success',
      content: {
        type: 'Model',
        provider: 'openai',
        model: 'gpt-4o',
        input: JSON.stringify(gatewayResponse.provider.request),
        output: JSON.stringify(gatewayResponse.provider.response)
      },
      attributes: {
        contextLength: context.length
      }
    });
    llmSpan.end();

    pipelineSpan.update({ status: 'success' });
    pipelineSpan.end();

    trace.update({ status: 'success' });

    return answer;

  } catch (error) {
    pipelineSpan.update({ status: 'failure' });
    trace.update({ status: 'failure' });
    throw error;

  } finally {
    trace.end();
  }
}
```

### Example 3: Guardrail Check

```typescript theme={null}
async function checkContent(text: string) {
  const trace = monitor.logTrace({ name: 'Content Check' });

  const guardrailSpan = trace.logSpan({
    name: 'Safety Guardrail',
    tags: ['guardrail', 'safety']
  });

  try {
    // Call safety API
    const response = await fetch('https://api.safety.com/v1/check', {
      method: 'POST',
      body: JSON.stringify({ text })
    });
    const result = await response.json();

    const isSafe = result.toxicity < 0.1 && result.harm < 0.1;

    guardrailSpan.update({
      status: 'success',
      content: {
        type: 'Guardrail',
        input: JSON.stringify({ text }),
        output: JSON.stringify({
          safe: isSafe,
          scores: {
            toxicity: result.toxicity,
            harm: result.harm,
            pii: result.pii
          }
        })
      },
      attributes: {
        textLength: text.length,
        safe: isSafe
      }
    });

    guardrailSpan.end();
    trace.end();

    return isSafe;

  } catch (error) {
    guardrailSpan.update({ status: 'failure' });
    guardrailSpan.end();
    trace.end();
    throw error;
  }
}
```

## Type Definitions

```typescript theme={null}
type SpanStatus = 
  | 'success'
  | 'failure'
  | 'aborted'
  | 'cancelled'
  | 'unknown';

type LogAttributesValue = string | number | boolean;

interface LogSpanOptions {
  name: string;
  status?: SpanStatus;
  referenceId?: string;
  promptId?: string;
  deploymentId?: string | null;
  runEvaluation?: boolean;
  tags?: string[];
  attributes?: Record<string, LogAttributesValue>;
  content?: LogSpanContent;
}

interface SpanUpdates {
  name?: string;
  status?: SpanStatus;
  content?: LogSpanContent;
  runEvaluation?: boolean;
  tags?: string[];
  attributes?: Record<string, LogAttributesValue>;
}

type LogSpanContent =
  | LogSpanModelContent
  | LogSpanModelStreamContent
  | LogSpanEmbeddingsContent
  | LogSpanFunctionContent
  | LogSpanToolContent
  | LogSpanGuardrailContent
  | LogSpanRetrievalContent
  | LogSpanOtherContent;
```

## Best Practices

### 1. Use Appropriate Content Types

```typescript theme={null}
// ✅ Good: Correct content type for operation
const llmSpan = trace.logSpan({ name: 'LLM' });
llmSpan.update({
  content: {
    type: 'Model',
    provider: 'openai',
    model: 'gpt-4o',
    input: '...',
    output: '...'
  }
});

const toolSpan = trace.logSpan({ name: 'API' });
toolSpan.update({
  content: {
    type: 'Tool',
    input: '...',
    output: '...'
  }
});
```

### 2. Always End Spans

```typescript theme={null}
// ✅ Good: Always end
const span = trace.logSpan({ name: 'Op' });
try {
  await work();
} finally {
  span.end(); // Always called
}
```

### 3. Use Attributes and Tags for Filtering

```typescript theme={null}
// ✅ Good: Descriptive tags
const span = trace.logSpan({
  name: 'OpenAI Call',
  tags: [
    'llm',
    'openai',
    'gpt-4o',
    'production',
    'premium-tier'
  ],
  attributes: {
    userId: 'user-123',
    region: 'us-east-1',
    cached: false,
  }
});
```
