langfuse

package
v0.8.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2026 License: Apache-2.0, MIT Imports: 23 Imported by: 0

Documentation

Overview

Package langfuse implements WeKnora's Langfuse/LiteFuse LLM tracing client on top of the OpenTelemetry Go SDK. It records chat/embedding/rerank/VLM/ASR generations plus the surrounding agent/HTTP/asynq spans as OTLP/HTTP spans to a Langfuse v3+ or LiteFuse backend (POST /api/public/otel/v1/traces).

The integration is fully opt-in: when disabled (the default), every public entry point is a cheap no-op, so callers can wire them unconditionally. A W3C traceparent propagated from upstream callers (e.g. sop3) is inherited so WeKnora's trace shares the upstream trace id — correlating runs across services in the LiteFuse UI.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AsynqMiddleware

func AsynqMiddleware() asynq.MiddlewareFunc

AsynqMiddleware is the worker-side counterpart of GinMiddleware. It:

  1. Extracts the W3C traceparent stamped onto the task payload by InjectTracing and resumes the originating trace (so the Langfuse UI stitches the HTTP request and the async processing into one tree). For scheduled jobs with no upstream trace it opens a standalone trace named after the task type.

  2. Opens a SPAN around the handler execution so every child generation (embedding / VLM / chat / rerank / ASR) auto-attaches to it.

  3. Enriches the span with asynq's own metadata: task id, queue, retry count, payload size.

When the manager is disabled it degrades to a pass-through; failure of the Langfuse path never blocks task execution. Register once via mux.Use.

func AttachTraceparent

func AttachTraceparent(ctx context.Context, traceparent string) context.Context

AttachTraceparent resumes the originating trace from a W3C traceparent previously captured by TraceparentFromContext / InjectTracing. When ctx already has a *Trace (same-request background work), it is left unchanged so we do not replace a live local parent with a remote one.

This is the in-process counterpart of AsynqMiddleware's extract path: a later HTTP request (e.g. POST .../suggestions) has no GinMiddleware trace, and without this StartGeneration would auto-create an orphan root named after the LLM call.

func GinMiddleware

func GinMiddleware() gin.HandlerFunc

GinMiddleware returns a Gin handler that opens a Langfuse trace for each incoming request that hits a traced path. The trace is auto-finished when the handler chain returns; individual LLM calls inside the handler attach their generations to this trace via the request context.

Only paths matching shouldTrace are traced — static assets, health checks and polling endpoints are noisy and uninteresting.

func InjectTracing

func InjectTracing(ctx context.Context, carrier types.LangfuseTracingCarrier)

InjectTracing stamps the current W3C traceparent (plus a best-effort user/session label) from ctx onto the given payload, provided the payload embeds types.TracingContext (and thus implements LangfuseTracingCarrier).

The traceparent is produced by propagator.Inject from the active OTel span context (the HTTP root span opened by GinMiddleware). The asynq worker re-extracts it so worker-side spans are children of the same trace — giving LiteFuse one stitched tree across the HTTP request and the async processing. This also makes a sop3 run's traceparent propagate through to any asynq jobs WeKnora enqueues while serving sop3's agent-chat call.

Safe to call unconditionally: when Langfuse is disabled or no span is present on ctx, it writes a zero-valued TracingContext — which round-trips through JSON as absent fields and costs nothing.

func SummarizeMemoryRecallOutput

func SummarizeMemoryRecallOutput(meta map[string]interface{}, items []*types.MemoryItem) map[string]interface{}

SummarizeMemoryRecallOutput builds Langfuse output for a memory.recall span.

func SummarizePassagePreviews

func SummarizePassagePreviews(
	candidates []*types.SearchResult,
	passages []string,
	limit int,
) []map[string]interface{}

SummarizePassagePreviews builds rerank passage previews aligned with candidates.

func SummarizeRankScores

func SummarizeRankScores(
	results []map[string]interface{},
	limit int,
) []map[string]interface{}

SummarizeRankScores builds rerank model score rows for Langfuse output.

func SummarizeRetrievalContextOutput

func SummarizeRetrievalContextOutput(
	background string,
	interests, documents []string,
	items []*types.MemoryItem,
) map[string]interface{}

SummarizeRetrievalContextOutput builds Langfuse output for retrieval conditioning.

func SummarizeRetrieveOutput

func SummarizeRetrieveOutput(results []*types.RetrieveResult) map[string]interface{}

SummarizeRetrieveOutput builds Langfuse output for the retrieve span.

func SummarizeSearchResults

func SummarizeSearchResults(results []*types.SearchResult, limit int) map[string]interface{}

SummarizeSearchResults builds a compact ranked preview for rerank spans.

func TraceparentFromContext

func TraceparentFromContext(ctx context.Context) string

TraceparentFromContext returns the W3C traceparent for the active span, or empty when Langfuse is disabled or ctx carries no span. Used to stamp the originating chat trace onto derived work (follow-up suggestions) that may run after the HTTP handler returns, or on a later request.

func TruncateRunes

func TruncateRunes(s string, maxRunes int) string

TruncateRunes shortens s to at most maxRunes runes for trace payloads.

Types

type Config

type Config struct {
	// Enabled is the master switch. If false the entire package is a no-op.
	Enabled bool
	// Host is the Langfuse base URL, e.g. https://cloud.langfuse.com or
	// https://us.cloud.langfuse.com or a self-hosted address.
	Host string
	// PublicKey / SecretKey are the project credentials used for Basic Auth.
	PublicKey string
	SecretKey string
	// FlushAt flushes the queued events once the buffer reaches this size.
	FlushAt int
	// FlushInterval is the maximum time between automatic flushes.
	FlushInterval time.Duration
	// QueueSize bounds the in-memory buffer to avoid unbounded growth if the
	// Langfuse endpoint is unreachable.
	QueueSize int
	// RequestTimeout is the HTTP timeout for a single ingestion batch.
	RequestTimeout time.Duration
	// Release / Environment are attached to every trace for filtering in the
	// Langfuse UI (e.g. release="v0.4.2", environment="production").
	Release     string
	Environment string
	// SampleRate (0..1) controls trace sampling. 0 means "use 1.0".
	SampleRate float64
	// Debug enables verbose logging of batch send errors.
	Debug bool
	// contains filtered or unexported fields
}

Config holds the runtime configuration for the Langfuse client.

In practice users enable Langfuse purely through environment variables — Host / PublicKey / SecretKey — which matches every other Langfuse SDK and keeps WeKnora's YAML config free of secrets.

func LoadConfigFromEnv

func LoadConfigFromEnv() Config

LoadConfigFromEnv builds a Config by reading the LANGFUSE_* environment variables, mirroring the official Python / JS SDK conventions.

func (Config) Validate

func (c Config) Validate() error

Validate verifies required fields are present when Langfuse is enabled.

type Generation

type Generation struct {
	ID string
	// contains filtered or unexported fields
}

Generation represents a single model invocation (LLM / embedding / VLM / ASR).

func (*Generation) Finish

func (g *Generation) Finish(output interface{}, usage *TokenUsage, err error)

Finish updates a generation with its final output, token usage and any error. A non-nil err marks the observation as ERROR.

func (*Generation) MarkCompletionStart

func (g *Generation) MarkCompletionStart(t time.Time)

MarkCompletionStart records the time at which the first token was received in a streaming generation. Langfuse surfaces this as time-to-first-token.

type GenerationOptions

type GenerationOptions struct {
	Name            string
	Model           string
	Input           interface{}
	Metadata        map[string]interface{}
	ModelParameters map[string]interface{}
}

GenerationOptions configures a new generation observation.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager is the public façade of the langfuse package. A singleton is installed via Init(); callers should treat a nil *Manager as "disabled" and still invoke methods — every public method tolerates a nil receiver.

Internally the manager owns an OpenTelemetry TracerProvider backed by an OTLP/HTTP exporter pointing at the Langfuse v3+ / LiteFuse OTel endpoint. The handles (*Trace / *Span / *Generation) wrap OTel spans; spans are buffered by the BatchSpanProcessor and exported complete on End, so there is no per-flush-batch duplication of root spans (the bug the legacy hand-rolled translator had on long traces spanning multiple flushes).

func GetManager

func GetManager() *Manager

GetManager returns the installed singleton, or nil if Init has not been called. Callers must tolerate a nil return.

func Init

func Init(cfg Config) (*Manager, error)

Init builds a Manager from cfg and installs it as the package-wide singleton. When cfg.Enabled is false this returns a disabled manager that behaves as a no-op for every public method.

func (*Manager) Enabled

func (m *Manager) Enabled() bool

Enabled reports whether the manager would actually emit spans.

func (*Manager) ResumeTrace

func (m *Manager) ResumeTrace(ctx context.Context, traceID, parentSpanID string) (context.Context, *Trace)

ResumeTrace reconstructs a *Trace handle from an externally-provided W3C trace id (and optional parent span id), without creating a new root span — the originating process (e.g. an HTTP request that already opened a trace) owns the root. Used to graft async work onto an existing trace: it sets a remote SpanContext on ctx so any child span/generation started under it inherits the upstream trace id. When traceID is empty the returned *Trace is nil, signalling the caller should fall back to StartTrace.

func (*Manager) Shutdown

func (m *Manager) Shutdown(ctx context.Context) error

Shutdown flushes pending spans and releases the exporter. Safe to call multiple times.

func (*Manager) StartChildSpan

func (m *Manager) StartChildSpan(ctx context.Context, opts SpanOptions) (context.Context, *Span)

StartChildSpan records a low-level operation only when its caller is already traced. Polling and housekeeping must not create a new trace for every RPC; their operation-level caller owns the trace boundary instead.

func (*Manager) StartGeneration

func (m *Manager) StartGeneration(ctx context.Context, opts GenerationOptions) (context.Context, *Generation)

StartGeneration opens a generation observation under the trace carried by ctx (or a newly auto-created trace). If a parent span is present on ctx, the generation attaches under it via the OTel span context.

func (*Manager) StartSpan

func (m *Manager) StartSpan(ctx context.Context, opts SpanOptions) (context.Context, *Span)

StartSpan opens a child span under the trace/span carried by ctx. When no trace is present, OTel creates a fresh root (mirroring StartGeneration's auto-trace behaviour). Returns a ctx whose active span is this span.

func (*Manager) StartTrace

func (m *Manager) StartTrace(ctx context.Context, opts TraceOptions) (context.Context, *Trace)

StartTrace opens a root span. When ctx carries a remote SpanContext (from a W3C traceparent extracted by GinMiddleware), the root span inherits the upstream trace id — this is what makes a sop3 run and its WeKnora call land under the same trace in LiteFuse. The returned *Trace is non-nil even when disabled (methods are no-ops), so callers don't need nil checks.

func (*Manager) Tracer

func (m *Manager) Tracer() trace.Tracer

Tracer exposes the OTel tracer so middleware can create spans directly when needed (e.g. extracting a remote traceparent). Returns a no-op tracer when disabled.

type Span

type Span struct {
	ID string
	// contains filtered or unexported fields
}

Span represents a logical unit of work that isn't itself an LLM call — for example an asynq task execution, a pipeline stage, or a document-processing step. Generations and nested spans attach as children via the OTel span context (parenting is automatic through trace.SpanFromContext).

func (*Span) Finish

func (s *Span) Finish(output interface{}, metadata map[string]interface{}, err error)

Finish updates a span with its final output, extra metadata and any error. A non-nil err marks the span as ERROR. Finish-time metadata is merged on top of the metadata set at StartSpan (finish keys win) rather than discarded, so fields only known at completion (outcome, duration_ms, tool_calls, …) are reported. If this span implicitly opened a root trace, that root is ended last so it is exported.

type SpanOptions

type SpanOptions struct {
	Name     string
	Input    interface{}
	Metadata map[string]interface{}
}

SpanOptions configures a new SPAN observation.

type TokenUsage

type TokenUsage struct {
	Input      int    `json:"input,omitempty"`
	Output     int    `json:"output,omitempty"`
	Total      int    `json:"total,omitempty"`
	CacheRead  int    `json:"cache_read_input_tokens,omitempty"`
	CacheWrite int    `json:"cache_creation_input_tokens,omitempty"`
	CacheMiss  int    `json:"cache_miss_input_tokens,omitempty"`
	Unit       string `json:"unit,omitempty"`
}

TokenUsage captures the input/output/total token counts reported by the underlying model, in Langfuse's canonical schema. Set as the value of the langfuse.observation.usage_details span attribute (JSON-serialized).

type Trace

type Trace struct {
	ID string
	// contains filtered or unexported fields
}

Trace represents an active root observation. A Trace is conceptually one "request" (e.g. a chat turn). Generations and spans attached to it roll up as children in the Langfuse UI. It wraps an OpenTelemetry root span; its ID is the OTel trace id (W3C 32-hex), which — when the request carried a traceparent header — is the upstream caller's trace id (sop3 correlation).

func TraceFromContext

func TraceFromContext(ctx context.Context) (*Trace, bool)

TraceFromContext is the public accessor used by HTTP middlewares and handlers that want to set the trace input/output on the active trace.

func (*Trace) Finish

func (t *Trace) Finish(output interface{}, metadata map[string]interface{})

Finish updates the trace with its final output and merges any finish-time metadata into the metadata set at StartTrace. Safe to call on a disabled trace (no-op). Finish keys are merged on top of the open-time correlation fields (request_id, http.method, etc.) rather than overwriting them, so both the open's correlation and the finish outcome survive.

type TraceOptions

type TraceOptions struct {
	Name        string
	UserID      string
	SessionID   string
	Input       interface{}
	Metadata    map[string]interface{}
	Tags        []string
	Environment string
	Release     string
}

TraceOptions configures a new trace.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL