CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-langfuse--tracing

Langfuse instrumentation methods based on OpenTelemetry

Overview
Eval results
Files

manual-observations.mddocs/

Manual Observations

Manual observation creation with startObservation() provides explicit control over the lifecycle of observations in your Langfuse traces. This approach gives you fine-grained control over when observations start and end, making it ideal for complex workflows where automatic lifecycle management isn't suitable.

Core Function

startObservation

Creates and starts a new Langfuse observation with automatic TypeScript type inference based on observation type.

/**
 * Creates and starts a new Langfuse observation with automatic TypeScript type inference.
 *
 * @param name - Descriptive name for the observation (e.g., 'openai-gpt-4', 'vector-search')
 * @param attributes - Type-specific attributes (input, output, metadata, etc.)
 * @param options - Configuration options including observation type and timing
 * @returns Strongly-typed observation object based on `asType` parameter
 */
function startObservation(
  name: string,
  attributes?: LangfuseObservationAttributes,
  options?: StartObservationOpts
): LangfuseObservation;

// Type-specific overloads for automatic type inference
function startObservation(
  name: string,
  attributes: LangfuseGenerationAttributes,
  options: { asType: "generation" }
): LangfuseGeneration;

function startObservation(
  name: string,
  attributes: LangfuseEventAttributes,
  options: { asType: "event" }
): LangfuseEvent;

function startObservation(
  name: string,
  attributes: LangfuseAgentAttributes,
  options: { asType: "agent" }
): LangfuseAgent;

// ... additional overloads for tool, chain, retriever, evaluator, guardrail, embedding

// Options interface
interface StartObservationOpts {
  /** Type of observation to create. Defaults to 'span' */
  asType?: LangfuseObservationType;
  /** Custom start time for the observation */
  startTime?: Date;
  /** Parent span context to attach this observation to */
  parentSpanContext?: SpanContext;
}

type LangfuseObservationType =
  | "span"
  | "generation"
  | "event"
  | "embedding"
  | "agent"
  | "tool"
  | "chain"
  | "retriever"
  | "evaluator"
  | "guardrail";

Observation Types

Span (Default)

General-purpose observation for tracking operations, functions, and workflows.

import { startObservation } from '@langfuse/tracing';

// Default span creation
const span = startObservation('user-authentication', {
  input: { username: 'john_doe', method: 'oauth' },
  metadata: { provider: 'google' }
});

try {
  const user = await authenticateUser(credentials);
  span.update({
    output: { userId: user.id, success: true }
  });
} catch (error) {
  span.update({
    level: 'ERROR',
    statusMessage: error.message,
    output: { success: false, error: error.message }
  });
} finally {
  span.end();
}

Generation

Specialized observation for LLM calls and AI model interactions.

const generation = startObservation('openai-gpt-4', {
  input: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'Explain quantum computing' }
  ],
  model: 'gpt-4-turbo',
  modelParameters: {
    temperature: 0.7,
    maxTokens: 500
  }
}, { asType: 'generation' });

const response = await openai.chat.completions.create({
  model: 'gpt-4-turbo',
  messages: generation.attributes.input,
  temperature: 0.7,
  max_tokens: 500
});

generation.update({
  output: response.choices[0].message,
  usageDetails: {
    promptTokens: response.usage.prompt_tokens,
    completionTokens: response.usage.completion_tokens,
    totalTokens: response.usage.total_tokens
  },
  costDetails: {
    totalCost: 0.025,
    currency: 'USD'
  }
});

generation.end();

Agent

Observation for AI agent workflows with tool usage and autonomous operations.

const agent = startObservation('research-agent', {
  input: {
    task: 'Research renewable energy trends',
    tools: ['web-search', 'summarizer'],
    maxIterations: 3
  },
  metadata: {
    model: 'gpt-4',
    strategy: 'react'
  }
}, { asType: 'agent' });

// Agent performs multiple operations
const searchResults = await performWebSearch('renewable energy 2024');
const summary = await summarizeResults(searchResults);

agent.update({
  output: {
    completed: true,
    toolsUsed: ['web-search', 'summarizer'],
    iterationsRequired: 2,
    finalResult: summary
  },
  metadata: {
    efficiency: 0.85,
    qualityScore: 0.92
  }
});

agent.end();

Tool

Observation for individual tool calls and external API interactions.

const tool = startObservation('web-search', {
  input: {
    query: 'latest AI developments',
    maxResults: 10
  },
  metadata: {
    provider: 'google-api',
    timeout: 5000
  }
}, { asType: 'tool' });

try {
  const results = await webSearch('latest AI developments');

  tool.update({
    output: {
      results: results,
      count: results.length,
      relevanceScore: 0.89
    },
    metadata: {
      latency: 1200,
      cacheHit: false
    }
  });
} catch (error) {
  tool.update({
    level: 'ERROR',
    statusMessage: 'Search failed',
    output: { error: error.message }
  });
} finally {
  tool.end();
}

Chain

Observation for structured multi-step workflows and process chains.

const chain = startObservation('rag-pipeline', {
  input: {
    query: 'What is renewable energy?',
    steps: ['retrieval', 'generation']
  },
  metadata: {
    vectorDb: 'pinecone',
    model: 'gpt-4'
  }
}, { asType: 'chain' });

// Execute pipeline steps
const docs = await retrieveDocuments('renewable energy');
const response = await generateResponse(query, docs);

chain.update({
  output: {
    finalResponse: response,
    stepsCompleted: 2,
    documentsUsed: docs.length,
    pipelineEfficiency: 0.87
  }
});

chain.end();

Retriever

Observation for document retrieval and search operations.

const retriever = startObservation('vector-search', {
  input: {
    query: 'machine learning applications',
    topK: 10,
    similarityThreshold: 0.7
  },
  metadata: {
    vectorDB: 'pinecone',
    embeddingModel: 'text-embedding-ada-002',
    similarity: 'cosine'
  }
}, { asType: 'retriever' });

const results = await vectorDB.search({
  query: 'machine learning applications',
  topK: 10
});

retriever.update({
  output: {
    documents: results,
    count: results.length,
    avgSimilarity: 0.89
  },
  metadata: {
    searchLatency: 150,
    cacheHit: false
  }
});

retriever.end();

Evaluator

Observation for quality assessment and evaluation operations.

const evaluator = startObservation('response-quality-eval', {
  input: {
    response: 'Machine learning is a subset of artificial intelligence...',
    reference: 'Expected high-quality explanation',
    criteria: ['accuracy', 'completeness', 'clarity']
  },
  metadata: {
    evaluator: 'custom-bert-scorer',
    threshold: 0.8
  }
}, { asType: 'evaluator' });

const evaluation = await evaluateResponse({
  response: inputText,
  criteria: ['accuracy', 'completeness', 'clarity']
});

evaluator.update({
  output: {
    overallScore: 0.87,
    criteriaScores: {
      accuracy: 0.92,
      completeness: 0.85,
      clarity: 0.90
    },
    passed: true,
    grade: 'excellent'
  }
});

evaluator.end();

Guardrail

Observation for safety checks and compliance enforcement.

const guardrail = startObservation('content-safety-check', {
  input: {
    content: userMessage,
    policies: ['no-toxicity', 'no-hate-speech', 'no-pii'],
    strictMode: true
  },
  metadata: {
    guardrailVersion: 'v2.1',
    confidence: 0.95
  }
}, { asType: 'guardrail' });

const safetyCheck = await checkContentSafety({
  text: userMessage,
  policies: ['no-toxicity', 'no-hate-speech']
});

guardrail.update({
  output: {
    safe: safetyCheck.safe,
    riskScore: 0.15,
    violations: [],
    action: 'allow'
  }
});

guardrail.end();

Embedding

Observation for text embedding and vector generation operations.

const embedding = startObservation('text-embedder', {
  input: {
    texts: [
      'Machine learning is a subset of AI',
      'Deep learning uses neural networks'
    ],
    batchSize: 2
  },
  model: 'text-embedding-ada-002',
  metadata: {
    dimensions: 1536,
    normalization: 'l2'
  }
}, { asType: 'embedding' });

const embedResult = await generateEmbeddings({
  texts: embedding.attributes.input.texts,
  model: 'text-embedding-ada-002'
});

embedding.update({
  output: {
    embeddings: embedResult.vectors,
    count: embedResult.vectors.length,
    dimensions: 1536
  },
  usageDetails: {
    totalTokens: embedResult.tokenCount
  },
  metadata: {
    processingTime: 340
  }
});

embedding.end();

Event

Observation for point-in-time occurrences or log entries (automatically ended).

// Events are automatically ended at creation
const event = startObservation('user-login', {
  input: {
    userId: '123',
    method: 'oauth',
    timestamp: new Date().toISOString()
  },
  level: 'DEFAULT',
  metadata: {
    ip: '192.168.1.1',
    userAgent: 'Chrome/120.0',
    sessionId: 'sess_456'
  }
}, { asType: 'event' });

// No need to call event.end() - events are automatically ended

Nested Observations

All observation types support creating child observations to build hierarchical traces.

// Parent observation
const workflow = startObservation('ai-pipeline', {
  input: { query: 'Explain quantum computing' }
});

// Create child retriever
const retrieval = workflow.startObservation('document-search', {
  input: { query: 'quantum computing', topK: 5 }
}, { asType: 'retriever' });

const docs = await searchDocuments('quantum computing', 5);
retrieval.update({ output: { documents: docs, count: docs.length } });
retrieval.end();

// Create child generation
const generation = workflow.startObservation('response-generation', {
  input: { query: 'Explain quantum computing', context: docs },
  model: 'gpt-4'
}, { asType: 'generation' });

const response = await llm.generate({ prompt, context: docs });
generation.update({
  output: response,
  usageDetails: { totalTokens: 500 }
});
generation.end();

// Update parent with final results
workflow.update({
  output: {
    finalResponse: response,
    childOperations: 2,
    success: true
  }
});
workflow.end();

Observation Class Methods

All observation classes share common methods for lifecycle management and updates.

update()

Updates the observation with new attributes. Returns the observation for method chaining.

// For span observations
update(attributes: LangfuseSpanAttributes): LangfuseSpan;

// For generation observations
update(attributes: LangfuseGenerationAttributes): LangfuseGeneration;

// Each observation type has its corresponding update signature

Example:

span.update({
  output: { result: 'success' },
  level: 'DEFAULT',
  metadata: { duration: 150 }
});

end()

Marks the observation as complete with optional end timestamp.

/**
 * Ends the observation, marking it as complete.
 *
 * @param endTime - Optional end time, defaults to current time
 */
end(endTime?: Date | number): void;

Example:

const span = startObservation('operation');
// ... perform operation
span.end(); // End with current timestamp

// Or specify custom end time
const customEndTime = new Date();
span.end(customEndTime);

updateTrace()

Updates the parent trace with trace-level attributes from within an observation.

/**
 * Updates the parent trace with new attributes.
 *
 * @param attributes - Trace attributes to set
 * @returns This observation for method chaining
 */
updateTrace(attributes: LangfuseTraceAttributes): LangfuseObservation;

interface LangfuseTraceAttributes {
  /** Human-readable name for the trace */
  name?: string;
  /** Identifier for the user associated with this trace */
  userId?: string;
  /** Session identifier for grouping related traces */
  sessionId?: string;
  /** Version identifier for the code/application */
  version?: string;
  /** Release identifier for deployment tracking */
  release?: string;
  /** Input data that initiated the trace */
  input?: unknown;
  /** Final output data from the trace */
  output?: unknown;
  /** Additional metadata for the trace */
  metadata?: unknown;
  /** Tags for categorizing and filtering traces */
  tags?: string[];
  /** Whether this trace should be publicly visible */
  public?: boolean;
  /** Environment where the trace was captured */
  environment?: string;
}

Example:

const observation = startObservation('api-request');

observation.updateTrace({
  name: 'user-checkout',
  userId: 'user-123',
  sessionId: 'session-456',
  tags: ['checkout', 'payment'],
  metadata: { version: '2.1.0' }
});

observation.end();

startObservation()

Creates a new child observation within this observation's context.

/**
 * Creates a new child observation within this observation's context.
 *
 * @param name - Descriptive name for the child observation
 * @param attributes - Type-specific attributes
 * @param options - Configuration including observation type
 * @returns Strongly-typed observation instance based on `asType`
 */
startObservation(
  name: string,
  attributes?: LangfuseObservationAttributes,
  options?: { asType?: LangfuseObservationType }
): LangfuseObservation;

Example:

const parent = startObservation('workflow');

// Create child with default type (span)
const child1 = parent.startObservation('step-1', {
  input: { data: 'value' }
});

// Create child with specific type
const child2 = parent.startObservation('llm-call', {
  model: 'gpt-4',
  input: 'prompt'
}, { asType: 'generation' });

child1.end();
child2.end();
parent.end();

Common Observation Attributes

All observation types support these base attributes:

interface LangfuseSpanAttributes {
  /** Input data for the operation being tracked */
  input?: unknown;
  /** Output data from the operation */
  output?: unknown;
  /** Additional metadata as key-value pairs */
  metadata?: Record<string, unknown>;
  /** Severity level of the observation */
  level?: "DEBUG" | "DEFAULT" | "WARNING" | "ERROR";
  /** Human-readable status message */
  statusMessage?: string;
  /** Version identifier for the code/model being tracked */
  version?: string;
  /** Environment where the operation is running */
  environment?: string;
}

Generation-Specific Attributes

Generation and embedding observations support additional LLM-specific attributes:

interface LangfuseGenerationAttributes extends LangfuseSpanAttributes {
  /** Timestamp when the model started generating completion */
  completionStartTime?: Date;
  /** Name of the language model used */
  model?: string;
  /** Parameters passed to the model */
  modelParameters?: {
    [key: string]: string | number;
  };
  /** Token usage and other model-specific usage metrics */
  usageDetails?: {
    [key: string]: number;
  } | OpenAiUsage;
  /** Cost breakdown for the generation */
  costDetails?: {
    [key: string]: number;
  };
  /** Information about the prompt used from Langfuse prompt management */
  prompt?: {
    name: string;
    version: number;
    isFallback: boolean;
  };
}

interface OpenAiUsage {
  promptTokens?: number;
  completionTokens?: number;
  totalTokens?: number;
}

Options Configuration

Start Time

Specify a custom start time for backdating observations:

const observation = startObservation('operation', {
  input: { data: 'value' }
}, {
  startTime: new Date('2024-01-01T10:00:00Z')
});

Parent Context

Manually specify parent span context for custom hierarchies:

import { startObservation } from '@langfuse/tracing';

const parent = startObservation('parent');
const parentContext = parent.otelSpan.spanContext();

// Create sibling observation by using same parent context
const child = startObservation('child', {
  input: { step: 'processing' }
}, {
  parentSpanContext: parentContext
});

child.end();
parent.end();

Best Practices

Always End Observations

Manual observations require explicit end() calls. Use try-finally blocks to ensure proper cleanup:

const observation = startObservation('operation');

try {
  // Perform operation
  const result = await performWork();
  observation.update({ output: result });
} catch (error) {
  observation.update({
    level: 'ERROR',
    statusMessage: error.message
  });
  throw error;
} finally {
  observation.end(); // Always end the observation
}

Progressive Updates

Update observations progressively as information becomes available:

const generation = startObservation('llm-call', {
  model: 'gpt-4',
  modelParameters: { temperature: 0.7 }
}, { asType: 'generation' });

// Set input
generation.update({
  input: [{ role: 'user', content: 'Hello' }]
});

// Call API
const response = await llm.call();

// Update with output
generation.update({
  output: response.message
});

// Add usage details
generation.update({
  usageDetails: response.usage
});

generation.end();

Meaningful Names

Use descriptive names that indicate the operation's purpose:

// Good: Specific and descriptive
const retrieval = startObservation('pinecone-vector-search', {}, { asType: 'retriever' });

// Avoid: Generic and unclear
const span = startObservation('search');

Structured Metadata

Use consistent metadata structure for better analytics:

const observation = startObservation('api-call', {
  input: request,
  metadata: {
    service: 'payment-gateway',
    version: '2.1.0',
    environment: 'production',
    region: 'us-east-1'
  }
});

Install with Tessl CLI

npx tessl i tessl/npm-langfuse--tracing

docs

active-observations.md

attribute-creation.md

context-management.md

index.md

manual-observations.md

observation-types.md

observe-decorator.md

otel-span-attributes.md

trace-id-generation.md

tracer-provider.md

tile.json