Skip to content

Tracing Langfuse API Reference ​

Complete type definitions for @helix-agents/tracing-langfuse.

Installation ​

bash
npm install @helix-agents/tracing-langfuse @langfuse/tracing @langfuse/otel

Functions ​

createLangfuseHooks ​

Creates Langfuse tracing hooks for use with Helix Agents.

typescript
function createLangfuseHooks<TState = unknown, TOutput = unknown>(
  options?: LangfuseHooksOptions
): LangfuseHooksResult<TState, TOutput>;

Parameters:

  • options - Configuration options (optional, uses env vars if not provided)

Returns: LangfuseHooksResult containing hooks and utility methods

Example:

typescript
import { createLangfuseHooks } from '@helix-agents/tracing-langfuse';

// Basic usage (reads from env vars)
const { hooks, flush, shutdown } = createLangfuseHooks();

// With configuration
const { hooks } = createLangfuseHooks({
  publicKey: 'pk-lf-...',
  secretKey: 'sk-lf-...',
  release: '1.0.0',
  defaultTags: ['production'],
});

createLangfuseSpanProcessor ​

Sends helix's native OpenTelemetry spans to Langfuse (#179). It works with any runtime that reports through telemetry.tracerProvider: runtime-js (JSAgentExecutor) and the Cloudflare Durable Object.

typescript
function createLangfuseSpanProcessor(options?: LangfuseSpanProcessorOptions): SpanProcessor;

interface LangfuseSpanProcessorOptions extends LangfuseSpanProcessorParams {
  /** Tags of every trace a root agent run starts (`langfuse.trace.tags`). */
  tags?: string[];
}

LangfuseSpanProcessorParams are @langfuse/otel's own options, passed through unchanged:

  • publicKey, secretKey, baseUrl;
  • exportMode (batched / immediate), flushAt, flushInterval, timeout;
  • environment, release;
  • mask, shouldExportSpan;
  • additionalHeaders;
  • exporter, a custom SpanExporter.

It returns ONE processor: Langfuse's own LangfuseSpanProcessor, handed a view of each ended span that carries Langfuse's attributes. Register it once on your provider. Its forceFlush / shutdown reach Langfuse's processor, so telemetry.flush flushes it.

typescript
import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
import { createLangfuseSpanProcessor } from '@helix-agents/tracing-langfuse';

const provider = new BasicTracerProvider({
  spanProcessors: [createLangfuseSpanProcessor({ publicKey, secretKey, tags: ['production'] })],
});
const executor = new JSAgentExecutor(store, streams, llm, {
  telemetry: { tracerProvider: provider, flush: () => provider.forceFlush() },
});

The mapping (langfuseAttributesFor). It applies only to helix's spans (instrumentation scope @helix-agents/core); other spans pass through unchanged. It never overwrites a langfuse.* attribute already on the span, so an explicit value of yours wins.

Helix spanLangfuse attributes
invoke_agentlangfuse.observation.type = agent
chattype = generation; model.name from gen_ai.response.model, else gen_ai.request.model; usage_details from gen_ai.usage.input_tokens / output_tokens (JSON)
execute_tooltype = tool
helix.step, helix.tool.wait, helix.do.*none (Langfuse's default span)
every helix spansession.id from helix.session.root_id, else the span's own: one Langfuse session per conversation, sub-agents included
the root invoke_agent (no helix.session.parent_id; helix.session.root_id absent or its own session)langfuse.trace.name from gen_ai.agent.name; langfuse.trace.tags from tags
chat / execute_tool with content capturedlangfuse.observation.input / output from gen_ai.input.messages / output.messages, gen_ai.tool.call.arguments / .result
span status ERRORlangfuse.observation.level = ERROR; status_message from error.type, else the status message

Content reaches Langfuse only when helix recorded it (telemetry.captureContent): the processor never adds any.

A concurrencyGroup call that queued behind its group's previous call has a helix.tool.wait span (with helix.tool.concurrency_group) under its execute_tool span, covering the wait. In Langfuse it is a span inside the tool observation, so the observation's duration (which includes the wait) is explained.

The rewritten session.id and the other attributes exist only in the view handed to Langfuse's processor (withLangfuseAttributes). The span itself is never changed, so another processor on the same provider (your own exporter, say) sees it as helix wrote it. The exception is Langfuse's own onStart, which stamps environment / release on the span when you set them.

Pick one integration per executor.

ExecutorUse
runtime-js (JSAgentExecutor), CF DOcreateLangfuseSpanProcessor on telemetry.tracerProvider
Temporal, DBOS, CF WorkflowscreateLangfuseHooks (they do not report natively yet)

Using both for the same runs reports each run twice, as two traces.

On the Cloudflare Durable Object. Langfuse's processor exports OTLP over fetch, so it runs in workerd. Use exportMode: 'immediate' and the DO's telemetry.flush:

typescript
class MyAgentServer extends DurableObjectAgentBase<Env> {
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env, {
      agents: registry,
      llmAdapter,
      telemetry: (e) => {
        const provider = new BasicTracerProvider({
          spanProcessors: [
            createLangfuseSpanProcessor({
              publicKey: e.LANGFUSE_PUBLIC_KEY,
              secretKey: e.LANGFUSE_SECRET_KEY,
              exportMode: 'immediate',
            }),
          ],
        });
        return { tracerProvider: provider, flush: () => provider.forceFlush() };
      },
    });
  }
}

tracingContext ​

Creates a fluent builder for execution context with tracing metadata.

typescript
function tracingContext(): TracingContextBuilder;

Returns: TracingContextBuilder instance

Example:

typescript
import { tracingContext } from '@helix-agents/tracing-langfuse';

const context = tracingContext()
  .user('user-123')
  .session('session-456')
  .tags('premium')
  .environment('production')
  .build();

await executor.execute(agent, input, context);

createTracingMetadata ​

Creates typed metadata for tracing, filtering out undefined values.

typescript
function createTracingMetadata(metadata: TracingMetadata): Record<string, string>;

Parameters:

  • metadata - Object with typed metadata fields

Returns: Clean Record<string, string> with no undefined values

Example:

typescript
import { createTracingMetadata } from '@helix-agents/tracing-langfuse';

const metadata = createTracingMetadata({
  environment: 'production',
  version: '1.0.0',
  region: 'us-west-2',
  tier: undefined, // Will be filtered out
});

// Result: { environment: 'production', version: '1.0.0', region: 'us-west-2' }

Types ​

LangfuseHooksResult ​

Result returned by createLangfuseHooks().

typescript
interface LangfuseHooksResult<TState = unknown, TOutput = unknown> {
  /** Agent hooks to pass to defineAgent */
  hooks: AgentHooks<TState, TOutput>;

  /** Flush all pending observations to Langfuse */
  flush: () => Promise<void>;

  /** Shutdown the client and flush pending observations */
  shutdown: () => Promise<void>;
}

LangfuseHooksOptions ​

Configuration options for createLangfuseHooks().

typescript
interface LangfuseHooksOptions {
  // ---------------------------------------------------------------------------
  // Core Configuration
  // ---------------------------------------------------------------------------

  /**
   * Langfuse public key.
   * If not provided, reads from LANGFUSE_PUBLIC_KEY env var.
   */
  publicKey?: string;

  /**
   * Langfuse secret key.
   * If not provided, reads from LANGFUSE_SECRET_KEY env var.
   */
  secretKey?: string;

  /**
   * Langfuse base URL.
   * @default "https://cloud.langfuse.com"
   */
  baseUrl?: string;

  // ---------------------------------------------------------------------------
  // OpenTelemetry Configuration
  // ---------------------------------------------------------------------------

  /**
   * Environment label for traces (e.g., 'production', 'staging').
   * Maps to Langfuse's environment attribute.
   */
  environment?: string;

  /**
   * Number of spans to buffer before flushing to Langfuse.
   * @default 512 (Langfuse SDK default)
   */
  flushAt?: number;

  /**
   * Interval in seconds between automatic flushes.
   * @default 5 (Langfuse SDK default)
   */
  flushInterval?: number;

  /**
   * Export mode for the span processor.
   * 'batched' buffers and sends in batches (default, recommended for production).
   * 'immediate' sends each span immediately (useful for debugging/serverless).
   * @default 'batched'
   */
  exportMode?: 'batched' | 'immediate';

  /**
   * Data masking function applied to observation attributes before export.
   * Use to redact PII or sensitive data from input/output/metadata.
   */
  mask?: (params: { data: unknown }) => unknown;

  // ---------------------------------------------------------------------------
  // Trace Hierarchy Options
  // ---------------------------------------------------------------------------

  /**
   * Group observations under step spans.
   * Creates a span per agent step (step-1, step-2, ...) for cleaner trace views.
   * @default true
   */
  groupByStep?: boolean;

  /**
   * Resolve the Langfuse session ID from hook context.
   * By default, sub-agents use their parent's session ID so all traces
   * appear in the same Langfuse Session view.
   * @default (context) => context.parentSessionId ?? context.sessionId
   */
  resolveSessionId?: (context: HookContext) => string;

  // ---------------------------------------------------------------------------
  // Data Capture Options
  // ---------------------------------------------------------------------------

  /**
   * Include agent state snapshots in observations.
   * Warning: State may be large and contain sensitive data.
   * @default false
   */
  includeState?: boolean;

  /**
   * Include message content in generation observations.
   * Matches Langfuse SDK default behavior for eval compatibility.
   * Set to false to redact sensitive conversation data.
   * @default true
   */
  includeMessages?: boolean;

  /**
   * Include tool arguments in observations.
   * @default true
   */
  includeToolArgs?: boolean;

  /**
   * Include tool results in observations.
   * Matches Langfuse SDK default behavior.
   * Set to false to redact potentially large tool outputs.
   * @default true
   */
  includeToolResults?: boolean;

  /**
   * Include LLM input (messages) in generation observations.
   * Required for seeing prompts in Langfuse UI.
   * @default true
   */
  includeGenerationInput?: boolean;

  /**
   * Include LLM output in generation observations.
   * Required for seeing responses in Langfuse UI.
   * @default true
   */
  includeGenerationOutput?: boolean;

  // ---------------------------------------------------------------------------
  // Trace Metadata Defaults
  // ---------------------------------------------------------------------------

  /**
   * Default release/version tag for all traces.
   */
  release?: string;

  /**
   * Default tags applied to all traces.
   * Merged with per-execution tags from ExecuteOptions.
   */
  defaultTags?: string[];

  /**
   * Default metadata applied to all traces.
   * Merged with per-execution metadata from ExecuteOptions.
   */
  defaultMetadata?: Record<string, unknown>;

  // ---------------------------------------------------------------------------
  // Lifecycle Hooks
  // ---------------------------------------------------------------------------

  /**
   * Called when an agent trace is created.
   */
  onAgentTraceCreated?: (params: AgentTraceCreatedParams) => void | Promise<void>;

  /**
   * Called when a generation observation is created.
   */
  onGenerationCreated?: (params: GenerationCreatedParams) => void | Promise<void>;

  /**
   * Called when a tool observation is created.
   */
  onToolCreated?: (params: ToolCreatedParams) => void | Promise<void>;

  /**
   * Called when any observation is about to be ended.
   */
  onObservationEnding?: (params: ObservationEndingParams) => void | Promise<void>;

  /**
   * Custom attribute extractor called for each hook invocation.
   */
  extractAttributes?: (
    context: HookContext
  ) => Record<string, string | number | boolean> | undefined;

  /**
   * Enable debug logging.
   * @default false
   */
  debug?: boolean;
}

TracingMetadata ​

Common metadata fields with type safety.

typescript
interface TracingMetadata {
  /** Application environment (e.g., 'production', 'staging', 'development') */
  environment?: string;

  /** Application version or release tag */
  version?: string;

  /** Service or component name */
  service?: string;

  /** Geographic region */
  region?: string;

  /** User tier or subscription level */
  tier?: string;

  /** Feature flags active for this execution */
  features?: string;

  /** Request source (e.g., 'web', 'mobile', 'api') */
  source?: string;

  /** Allow additional custom fields */
  [key: string]: string | undefined;
}

TracingContextBuilder ​

Fluent builder for creating execution context.

typescript
class TracingContextBuilder {
  /** Set user ID for attribution */
  user(userId: string): this;

  /** Set session ID for conversation grouping */
  session(sessionId: string): this;

  /** Add tags for filtering */
  tags(...tags: string[]): this;

  /** Set environment metadata */
  environment(env: 'production' | 'staging' | 'development'): this;

  /** Set version metadata */
  version(version: string): this;

  /** Set custom metadata (key-value or object) */
  metadata(key: string, value: string): this;
  metadata(obj: TracingMetadata): this;

  /** Build the execution options */
  build(): Pick<ExecuteOptions, 'userId' | 'sessionId' | 'tags' | 'metadata'>;
}

Lifecycle Hook Parameters ​

AgentTraceCreatedParams ​

typescript
interface AgentTraceCreatedParams {
  /** Deterministic trace ID for this run */
  traceId: string;

  /** Session ID (primary identifier) */
  sessionId: string;

  /** Agent name */
  agentName: string;

  /** User ID if provided */
  userId?: string;

  /** Tags from options merged with defaults */
  tags: string[];

  /** Metadata from options merged with defaults */
  metadata: Record<string, unknown>;

  /** Hook context */
  hookContext: HookContext;

  /** Update the trace with additional attributes */
  updateTrace: (updates: TraceUpdateParams) => void;
}

GenerationCreatedParams ​

typescript
interface GenerationCreatedParams {
  /** Generation observation ID */
  observationId: string;

  /** Model identifier */
  model?: string;

  /** Model parameters */
  modelParameters?: Record<string, unknown>;

  /** Input messages if includeGenerationInput is true */
  input?: unknown;

  /** Hook context */
  hookContext: HookContext;

  /** No-op in current architecture. Use extractAttributes for per-observation metadata. */
  updateGeneration: (updates: GenerationUpdateParams) => void;
}

ToolCreatedParams ​

typescript
interface ToolCreatedParams {
  /** Tool observation ID */
  observationId: string;

  /** Tool name */
  toolName: string;

  /** Tool call ID */
  toolCallId: string;

  /** Tool arguments if includeToolArgs is true */
  arguments?: unknown;

  /** Hook context */
  hookContext: HookContext;

  /** No-op in current architecture. Use extractAttributes for per-observation metadata. */
  updateTool: (updates: ToolUpdateParams) => void;
}

ObservationEndingParams ​

typescript
interface ObservationEndingParams {
  /** Observation type */
  type: 'agent' | 'generation' | 'tool' | 'span';

  /** Observation ID */
  observationId: string;

  /** Duration in milliseconds */
  durationMs: number;

  /** Whether the observation succeeded */
  success: boolean;

  /** Error if failed */
  error?: Error;

  /** Hook context */
  hookContext: HookContext;
}

Update Parameter Types ​

TraceUpdateParams ​

typescript
interface TraceUpdateParams {
  /** User ID for the trace */
  userId?: string;

  /** Session ID for grouping traces */
  sessionId?: string;

  /** Tags for filtering */
  tags?: string[];

  /** Additional metadata */
  metadata?: Record<string, unknown>;

  /** Release/version identifier */
  release?: string;
}

GenerationUpdateParams ​

typescript
interface GenerationUpdateParams {
  /** Model identifier */
  model?: string;

  /** Model parameters */
  modelParameters?: LangfuseModelParameters;

  /** Additional metadata */
  metadata?: unknown;
}

ToolUpdateParams ​

typescript
interface ToolUpdateParams {
  /** Additional metadata */
  metadata?: Record<string, unknown>;
}

LangfuseObservationType ​

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

Behavior Notes ​

Trace Update Consolidation ​

Trace-level attributes (name, input, output, userId, sessionId, tags) are set only on the root span of each agent trace via updateTrace. Child observations (generations, tool spans, step spans) do not redundantly call updateTrace — they inherit trace context from the root. This reduces the number of API calls to Langfuse and produces cleaner trace data.

onAgentComplete and finishWith Tools ​

The onAgentComplete hook fires for both completion paths (__finish__ and finishWith tools). For finishWith tools, the runtime promotes the tool output to agent output before firing the hook, so the output field in the completion payload always reflects the final agent output.

Sub-Agent Trace Spans ​

Sub-agent onAgentComplete hooks fire independently from the parent, ensuring each sub-agent produces its own root trace span in Langfuse. The sub-agent's stream is not closed (the parent owns the stream), but the trace data is flushed.


Token Usage Mapping ​

The Langfuse integration maps framework token fields to Langfuse's v4 usageDetails format on each generation observation. This includes standard tokens, reasoning tokens, and prompt cache tokens:

Framework fieldLangfuse usageDetails fieldDescription
promptTokensinputInput tokens
completionTokensoutputOutput tokens
totalTokenstotalTotal tokens
reasoningTokensreasoning_tokensReasoning/thinking tokens
cachedTokenscache_read_input_tokensTokens served from cache
cacheWriteTokenscache_creation_input_tokensTokens written to cache

Cache token fields are only populated when prompt caching is active (set via cache strategy in llmConfig). When present, Langfuse uses these fields for accurate cost calculation — cache read tokens are typically 90% cheaper than regular input tokens.


Environment Variables ​

VariableDescriptionRequired
LANGFUSE_PUBLIC_KEYLangfuse public keyYes (or pass in options)
LANGFUSE_SECRET_KEYLangfuse secret keyYes (or pass in options)
LANGFUSE_BASEURLLangfuse API URLNo (defaults to cloud.langfuse.com)

See Also ​

Released under the MIT License.