langfuse

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 20, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

README

go-langfuse

go-langfuse is an independent, observation-first community Langfuse client for Go, built on the official OpenTelemetry Go SDK: tracing over OTLP/HTTP protobuf (Langfuse ingestion version 4), all ten observation types, request-scoped trace identity, scores for evaluations and user feedback, and strict content-privacy controls. Prompt management, datasets, and administrative APIs are out of scope; use the Langfuse REST API for those. go-langfuse is not affiliated with or endorsed by Langfuse.

Observation types are first-class: alongside span, generation, and event, go-langfuse supports the typed observations Langfuse introduced in 2025 — agent, tool, chain, retriever, evaluator, embedding, and guardrail — with the same semantics as as_type in the Python SDK (v3.3+) and asType in the JS/TS SDK (v4+). These types give traces clearer, filterable structure in the Langfuse UI.

go-langfuse follows semantic versioning. Until v1.0, minor releases may contain documented breaking changes; patch releases are always backward compatible.

Install

go get github.com/fgn/go-langfuse

Set the project credentials from Langfuse -> Settings -> API Keys:

export LANGFUSE_PUBLIC_KEY=pk-lf-...
export LANGFUSE_SECRET_KEY=sk-lf-...
export LANGFUSE_BASE_URL=https://cloud.langfuse.com

LANGFUSE_BASE_URL may point at Langfuse Cloud or a self-hosted instance as a host root, /api/public/otel, or the full traces endpoint. Path-prefixed reverse-proxy base URLs are not supported in v0.1.

Quickstart

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/fgn/go-langfuse"
)

func main() {
	if err := run(context.Background()); err != nil {
		log.Fatal(err)
	}
}

func run(ctx context.Context) error {
	lf, err := langfuse.New(ctx, langfuse.ConfigFromEnv())
	if err != nil {
		return fmt.Errorf("create Langfuse client: %w", err)
	}
	defer func() {
		shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		_ = lf.Shutdown(shutdownCtx)
	}()

	ctx = lf.WithTraceAttributes(ctx, langfuse.TraceAttributes{
		Name: "chat-turn", UserID: "user-123", SessionID: "conversation-456",
		Tags: []string{"chat"},
	})

	question := "What is context in Go?"
	rootCtx, root := lf.StartObservation(ctx, "chat-turn", langfuse.TypeAgent,
		langfuse.ObservationAttributes{Input: question})
	defer root.End()

	messages := []string{question}
	generationCtx, generation := lf.StartObservation(rootCtx, "generate-answer",
		langfuse.TypeGeneration, langfuse.ObservationAttributes{
			Model: "gemini-2.5-flash", Input: messages,
		})
	defer generation.End()

	answer, usage, err := callModel(generationCtx, messages)
	if err != nil {
		generation.RecordError(err)
		root.RecordError(err)
		return err
	}

	generation.Update(langfuse.ObservationAttributes{Output: answer, Usage: &usage})
	root.Update(langfuse.ObservationAttributes{Output: answer})
	return nil
}

// Replace this stub with a provider SDK call and pass ctx to that SDK.
func callModel(ctx context.Context, messages []string) (string, langfuse.Usage, error) {
	if err := ctx.Err(); err != nil {
		return "", langfuse.Usage{}, err
	}
	return "Context carries cancellation and request-scoped values.", langfuse.Usage{
		InputTokens: int64(len(messages) * 6), OutputTokens: 8,
	}, nil
}

Runnable examples: quickstart, streaming, existing OpenTelemetry provider, and short-lived jobs, events, masking, disabled mode, and flushing.

Three rules prevent most tracing mistakes:

  1. Pass the context returned by StartObservation to child work, and keep parent contexts in distinct variables as the example does with rootCtx and generationCtx. Go context is the parent-child relationship; there is no package-global current observation.
  2. End an observation only after its work is complete. For streaming model calls, consume the stream before ending the generation.
  3. End observations before flushing or shutting down, and use a fresh timeout context rather than a canceled request context for lifecycle calls.

Observations

Every operation uses the same API and differs only by its observation type: span, generation, event, embedding, agent, tool, chain, retriever, evaluator, or guardrail — the full set defined by the current Langfuse platform (observation types). When the work fits one function, prefer Observe: the callback receives the child context, the observation always ends (a panic is marked as a payload-free failure before it propagates), and a returned error is recorded and passed through unchanged:

err := lf.Observe(parentCtx, "retrieve-documents", langfuse.TypeRetriever,
	langfuse.ObservationAttributes{Input: query},
	func(ctx context.Context, observation *langfuse.Observation) error {
		documents, err := retrieve(ctx, query)
		if err != nil {
			return err // recorded via RecordError automatically
		}
		observation.Update(langfuse.ObservationAttributes{Output: documents})
		return nil
	})

Use StartObservation directly when an observation's lifetime cannot be scoped to one function, as the quickstart does. Event records a point-in-time event, Update merges non-zero fields, and RecordError marks an observation failed without ending it. Semantics and limits are detailed in the reference.

Scores

RecordScore submits evaluations and user feedback through the Langfuse REST scores endpoint. A score is validated synchronously — every returned error means the score was not accepted — and then delivered asynchronously with bounded retry using the same backoff defaults as observation export, so a Langfuse blip neither blocks the request path nor loses feedback to one failed attempt. Flush and Shutdown drain accepted scores; a delivery that outlives the retry budget is dropped with a payload-free diagnostic through the OpenTelemetry error handler. When ID is empty the SDK generates one, keeping retried deliveries idempotent. A disabled client is a no-op.

rating := float64(feedback.Rating)
err := lf.RecordScore(ctx, langfuse.Score{
	ID:           "feedback-" + feedback.ID, // idempotent upsert key
	Name:         "user-feedback",
	SessionID:    "conversation-456",        // or TraceID / TraceID+ObservationID
	NumericValue: &rating,
	Comment:      feedback.Text,
})

Provider modes

The default mode creates an isolated SDK tracer provider that exports only observations created through this client:

lf, err := langfuse.New(ctx, langfuse.ConfigFromEnv())

If the application already owns an *sdktrace.TracerProvider, attach the client as another processor; a smart filter then also exports third-party AI spans (gen_ai.* attributes and known LLM instrumentation scopes):

cfg := langfuse.ConfigFromEnv()
cfg.TracerProvider = existingProvider
lf, err := langfuse.New(ctx, cfg)
Behavior Isolated provider Borrowed provider
Provider owner SDK client Application
SDK observations Exported Exported
Selected third-party AI spans Not observed Exported by the SDK's smart filter
Sampler and resource Always-sampled; SDK-owned resource Existing provider remains authoritative
Span limits Fixed SDK-safe limits; ambient OTEL_SPAN_* ignored Caller limits remain authoritative
Client.Shutdown Stops owned provider resources Stops and unregisters only the SDK's processor
Global OTel provider Never replaced Never replaced

Neither mode ever changes the global OpenTelemetry provider. Borrowed-mode lifecycle, annotation visibility, and the one-client-per-provider rule are covered in the existing OpenTelemetry guide.

Content and sensitive data

The SDK never inspects function arguments, HTTP bodies, or model clients; it exports only fields explicitly supplied by the caller. LANGFUSE_CONTENT_CAPTURE_ENABLED=false drops SDK-supplied input and output, and Config.Mask transforms input, output, and metadata before export. Identifiers, model data, status messages, error text, and third-party spans sit outside both controls; the exact boundary and a masker example are in the privacy guide.

Documentation

Development

task ci

This checks formatting and module tidiness, runs static analysis, compiles the examples and README quickstart, and runs the test, fuzz-smoke, and vulnerability suites. Run task format to apply source and module formatting.

The module language version is Go 1.25; go.mod records the suggested patched toolchain. Tests never require Langfuse credentials. Release steps are documented in RELEASING.md.

Documentation

Overview

Package langfuse provides observation-centric Langfuse tracing on top of OpenTelemetry.

It exports OTLP/HTTP protobuf traces to Langfuse and can either own an isolated tracer provider or attach a Langfuse processor to an existing OpenTelemetry SDK tracer provider through Config. The package never changes global OpenTelemetry state.

Construct a Client with New, typically from ConfigFromEnv, stamp request-scoped identity with Client.WithTraceAttributes, and record work with Client.Observe:

lf, err := langfuse.New(ctx, langfuse.ConfigFromEnv())
if err != nil {
	return err
}
defer lf.Shutdown(shutdownCtx)

err = lf.Observe(ctx, "answer-question", langfuse.TypeGeneration,
	langfuse.ObservationAttributes{Input: question},
	func(ctx context.Context, o *langfuse.Observation) error {
		answer, err := callModel(ctx, question)
		o.Update(langfuse.ObservationAttributes{Output: answer})
		return err
	})

Client.StartObservation is the lower-level pair for lifetimes that span functions; the context it returns carries the parent-child relationship, so observations started from it nest under their parent. Client.RecordScore submits evaluations and user feedback through the Langfuse REST scores endpoint, and Client.Flush and Client.Shutdown control the export lifecycle. A nil or disabled Client and the zero Observation are safe no-ops.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

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

Client owns all Langfuse exporter, processor, and lifecycle state. Its zero value is a safe no-op.

func New

func New(ctx context.Context, cfg Config) (*Client, error)

New constructs a client and validates configuration without performing network I/O. A disabled configuration bypasses all other validation.

Example
package main

import (
	"context"
	"log"
	"time"

	"github.com/fgn/go-langfuse"
)

func main() {
	ctx := context.Background()
	lf, err := langfuse.New(ctx, langfuse.Config{
		BaseURL:     "https://cloud.langfuse.com",
		PublicKey:   "pk-lf-...",
		SecretKey:   "sk-lf-...",
		Environment: "production",
	})
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		_ = lf.Shutdown(shutdownCtx)
	}()

	_, observation := lf.StartObservation(ctx, "nightly-import", langfuse.TypeSpan,
		langfuse.ObservationAttributes{Input: "42 records"})
	observation.End()
}

func (*Client) Event

func (c *Client) Event(ctx context.Context, name string, values ObservationAttributes)

Event records an instantaneous event observation.

func (*Client) Flush

func (c *Client) Flush(ctx context.Context) error

Flush exports all ended observations and delivers every score accepted before the call; scores recorded after Flush begins do not extend the wait. A score mid-retry holds Flush until it is delivered or dropped, bounded by ctx.

Example
package main

import (
	"context"
	"log"
	"time"

	"github.com/fgn/go-langfuse"
)

func main() {
	ctx := context.Background()
	lf, err := langfuse.New(ctx, langfuse.ConfigFromEnv())
	if err != nil {
		log.Fatal(err)
	}

	lf.Event(ctx, "job-completed", langfuse.ObservationAttributes{
		Metadata: map[string]any{"records": 42},
	})

	// Short-lived jobs and serverless handlers flush before returning so
	// batched observations are exported while the client stays usable.
	flushCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := lf.Flush(flushCtx); err != nil {
		log.Printf("flush: %v", err)
	}
}

func (*Client) Observe

func (c *Client) Observe(
	ctx context.Context,
	name string,
	observationType ObservationType,
	values ObservationAttributes,
	fn func(ctx context.Context, observation *Observation) error,
) error

Observe starts an observation, runs fn with the child context and the observation handle, and always ends the observation, including when fn panics. A non-nil error returned by fn is recorded through Observation.RecordError and returned unchanged, so fn does not need to record it itself. When fn panics, the observation is marked failed with the payload-free status "panic" — the panic value is never captured — and the panic propagates. On a nil, disabled, or stopped client fn still runs, receiving ctx unchanged and a no-op handle. A nil fn reports a diagnostic and starts no observation.

Example
package main

import (
	"context"
	"log"

	"github.com/fgn/go-langfuse"
)

func main() {
	ctx := context.Background()
	lf, err := langfuse.New(ctx, langfuse.ConfigFromEnv())
	if err != nil {
		log.Fatal(err)
	}

	err = lf.Observe(ctx, "retrieve-documents", langfuse.TypeRetriever,
		langfuse.ObservationAttributes{Input: "safe api design"},
		func(ctx context.Context, observation *langfuse.Observation) error {
			// Child observations started from ctx nest under this one. The
			// observation always ends, a returned error is recorded on it,
			// and a panic is marked as a failure before it propagates.
			documents, err := retrieve(ctx, "safe api design")
			if err != nil {
				return err
			}
			observation.Update(langfuse.ObservationAttributes{Output: documents})
			return nil
		})
	if err != nil {
		log.Printf("retrieve: %v", err)
	}
}

func retrieve(ctx context.Context, query string) ([]string, error) {
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	return []string{"doc-" + query}, nil
}

func (*Client) RecordScore

func (c *Client) RecordScore(ctx context.Context, score Score) error

RecordScore submits one score through the Langfuse REST scores endpoint using the client's credentials and environment. The score is validated synchronously — every returned error marks a score that was not accepted — and then queued for asynchronous delivery with bounded retry (network errors and HTTP 408, 429, and 5xx responses, using the same backoff defaults as observation export), so transport failures never reach the caller: after the retry budget they are reported as payload-free OpenTelemetry diagnostics and the score is dropped. Client.Flush and Client.Shutdown drain accepted scores. When the queue is full a score is dropped with a diagnostic unless Config.BlockOnQueueFull waits for space, bounded by ctx. A disabled client returns nil without sending, and a shut-down client returns an error. The complete serialized score is limited to 128 KiB.

Example
package main

import (
	"context"
	"log"

	"github.com/fgn/go-langfuse"
)

func main() {
	ctx := context.Background()
	lf, err := langfuse.New(ctx, langfuse.ConfigFromEnv())
	if err != nil {
		log.Fatal(err)
	}

	rating := 4.0
	err = lf.RecordScore(ctx, langfuse.Score{
		ID:           "feedback-42", // idempotent upsert key
		Name:         "user-feedback",
		SessionID:    "conversation-456",
		NumericValue: &rating,
		Comment:      "clear and concise answer",
	})
	if err != nil {
		log.Printf("record score: %v", err)
	}
}

func (*Client) Shutdown

func (c *Client) Shutdown(ctx context.Context) error

Shutdown permanently stops this Client after draining queued observations and scores, bounded by ctx. In borrowed mode it shuts down the Langfuse processor using ctx before unregistering it; the provider and all unrelated processors remain owned by the application.

func (*Client) StartObservation

func (c *Client) StartObservation(
	ctx context.Context,
	name string,
	observationType ObservationType,
	values ObservationAttributes,
) (context.Context, *Observation)

StartObservation starts an observation. Child work must use the returned context to preserve the parent-child relationship.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/fgn/go-langfuse"
)

func main() {
	ctx := context.Background()
	lf, err := langfuse.New(ctx, langfuse.ConfigFromEnv())
	if err != nil {
		log.Fatal(err)
	}

	// StartObservation suits lifetimes that span functions, such as ending a
	// generation only after a stream is fully consumed.
	generationCtx, generation := lf.StartObservation(ctx, "generate-answer",
		langfuse.TypeGeneration, langfuse.ObservationAttributes{
			Model: "gemini-2.5-flash",
			Input: "What is context in Go?",
		})
	defer generation.End()

	answer, usage, err := callModel(generationCtx, "What is context in Go?")
	if err != nil {
		generation.RecordError(err)
		return
	}
	generation.Update(langfuse.ObservationAttributes{Output: answer, Usage: &usage})
}

func callModel(ctx context.Context, question string) (string, langfuse.Usage, error) {
	if err := ctx.Err(); err != nil {
		return "", langfuse.Usage{}, err
	}
	return fmt.Sprintf("answer to %q", question), langfuse.Usage{
		InputTokens:  6,
		OutputTokens: 8,
	}, nil
}

func (*Client) WithTraceAttributes

func (c *Client) WithTraceAttributes(ctx context.Context, values TraceAttributes) context.Context

WithTraceAttributes returns a context that propagates request-scoped trace fields to observations subsequently started from it. If an SDK observation belonging to this Client is active, it is updated immediately as well.

Example
package main

import (
	"context"
	"log"

	"github.com/fgn/go-langfuse"
)

func main() {
	ctx := context.Background()
	lf, err := langfuse.New(ctx, langfuse.ConfigFromEnv())
	if err != nil {
		log.Fatal(err)
	}

	// Stamp request-scoped identity once; observations started from the
	// returned context share the same trace name, user, session, and tags.
	ctx = lf.WithTraceAttributes(ctx, langfuse.TraceAttributes{
		Name:      "chat-turn",
		UserID:    "user-123",
		SessionID: "conversation-456",
		Tags:      []string{"chat"},
	})

	observationCtx, observation := lf.StartObservation(ctx, "chat-turn",
		langfuse.TypeAgent, langfuse.ObservationAttributes{})
	defer observation.End()
	_ = observationCtx
}

type Config

type Config struct {
	// BaseURL is the Langfuse host or OTLP traces endpoint. It defaults to
	// https://cloud.langfuse.com.
	BaseURL string
	// PublicKey identifies the Langfuse project.
	PublicKey string
	// SecretKey authenticates writes to the Langfuse project.
	SecretKey string

	// Environment and Release are stamped on every span observed by the
	// Langfuse processor.
	Environment string
	Release     string

	// ServiceName overrides service.name on an SDK-owned provider. When empty,
	// the standard OpenTelemetry resource.Default value is retained, including
	// OTEL_SERVICE_NAME. It is ignored when TracerProvider is set.
	ServiceName string

	// TracerProvider attaches the Langfuse processor to a caller-owned provider. The client
	// never replaces the global provider or shuts this provider down.
	TracerProvider *sdktrace.TracerProvider

	// MaxQueueSize bounds how many ended export-eligible spans the client
	// buffers in memory while waiting for batch export. Zero selects the
	// default of 2048; negative values are a validation error in [New].
	MaxQueueSize int

	// BlockOnQueueFull makes ending an exported observation, and recording a
	// score, wait for buffer space instead of dropping when the
	// corresponding queue is full. A sustained export outage can then stall
	// goroutines that end observations or record scores, so it defaults to
	// false: on a full queue new work is dropped with a diagnostic, matching
	// OpenTelemetry defaults.
	BlockOnQueueFull bool

	// Disabled makes the complete client a safe no-op.
	Disabled bool

	// DisableContentCapture removes Input and Output supplied through
	// ObservationAttributes. It does not remove content emitted by third-party
	// OpenTelemetry instrumentation. Identifiers, metadata, model data, and
	// usage are still recorded.
	DisableContentCapture bool

	// Mask applies only to Input, Output, and Metadata supplied through this
	// Client. Each metadata map is passed as one complete value and must remain
	// a map[string]any to be retained. It does not process identifiers, model
	// fields, StatusMessage, [Observation.RecordError] text, or third-party
	// spans and events.
	// Mask may be called concurrently and therefore must be concurrency-safe.
	// A panic is recovered and the affected value is omitted.
	Mask func(value any) any
	// contains filtered or unexported fields
}

Config configures a Langfuse client.

func ConfigFromEnv

func ConfigFromEnv() Config

ConfigFromEnv returns configuration from the seven documented LANGFUSE_* variables. It intentionally does not read the deprecated LANGFUSE_HOST.

Example
package main

import (
	"context"
	"log"
	"time"

	"github.com/fgn/go-langfuse"
)

func main() {
	// ConfigFromEnv reads LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY,
	// LANGFUSE_BASE_URL, LANGFUSE_TRACING_ENVIRONMENT, LANGFUSE_RELEASE,
	// LANGFUSE_TRACING_ENABLED, and LANGFUSE_CONTENT_CAPTURE_ENABLED.
	lf, err := langfuse.New(context.Background(), langfuse.ConfigFromEnv())
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		_ = lf.Shutdown(shutdownCtx)
	}()
}

type Level

type Level string

Level is the Langfuse severity of an observation.

const (
	LevelDefault Level = "DEFAULT"
	LevelDebug   Level = "DEBUG"
	LevelWarning Level = "WARNING"
	LevelError   Level = "ERROR"
)

type Observation

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

Observation is a concurrency-safe handle around one OpenTelemetry span. Its zero value is a safe no-op.

func (*Observation) End

func (o *Observation) End()

End ends the observation exactly once.

func (*Observation) ID

func (o *Observation) ID() string

ID returns the lowercase 16-character OpenTelemetry span ID, or an empty string for a no-op observation.

func (*Observation) RecordError

func (o *Observation) RecordError(err error)

RecordError records an exception and marks the observation as failed. It does not end the observation. At most eight exception events are retained; later calls are omitted with one diagnostic. The error text is explicitly supplied content and is not processed by Config.Mask. Invalid UTF-8 or text over 64 KiB is replaced by the payload-free string "error".

func (*Observation) TraceID

func (o *Observation) TraceID() string

TraceID returns the lowercase 32-character OpenTelemetry trace ID, or an empty string for a no-op observation.

func (*Observation) Update

func (o *Observation) Update(values ObservationAttributes)

Update merges non-zero fields into an active observation. Structured model, usage, and cost maps replace their complete serialized attributes when set; metadata merges by top-level key. StartTime cannot be changed after start and is ignored with a diagnostic.

type ObservationAttributes

type ObservationAttributes struct {
	// Input and Output are explicit content. They are never inferred by the
	// SDK, are subject to Config.DisableContentCapture, and pass through Mask.
	Input  any
	Output any
	// Metadata merges by top-level key across Update calls and passes through
	// Mask. Empty maps are ignored and cannot clear earlier metadata. At most 32
	// distinct keys are retained over an observation's lifetime; keys are
	// limited to 200 bytes.
	Metadata map[string]any
	Level    Level
	// StatusMessage is explicit telemetry content, is limited to 16 KiB, and
	// does not pass through Mask. Sanitize it before calling the SDK.
	StatusMessage string
	// Version overrides a propagated TraceAttributes.Version on this
	// observation only.
	Version string

	// The remaining fields are honored only by generation and embedding
	// observations, except Prompt, which Langfuse links only on generations.
	Model               string
	ModelParameters     map[string]any
	Usage               *Usage
	CostDetails         map[string]float64
	Prompt              *PromptRef
	CompletionStartTime time.Time

	// StartTime is honored only by StartObservation; Event and Update ignore
	// it with a diagnostic.
	StartTime time.Time
}

ObservationAttributes contains fields that can be set when an observation starts or in a later Update. Zero values are ignored and updates never clear an already recorded value. Each serialized structured value is limited to 1 MiB and the observation-level payload attributes are limited to 2 MiB in aggregate over the observation's current values. Separately bounded trace/client propagation and exception events sit outside that aggregate. Oversized fields are omitted with a payload-free OpenTelemetry diagnostic.

type ObservationType

type ObservationType string

ObservationType identifies how Langfuse presents an observation.

const (
	TypeSpan       ObservationType = "span"
	TypeGeneration ObservationType = "generation"
	TypeEvent      ObservationType = "event"
	TypeEmbedding  ObservationType = "embedding"
	TypeAgent      ObservationType = "agent"
	TypeTool       ObservationType = "tool"
	TypeChain      ObservationType = "chain"
	TypeRetriever  ObservationType = "retriever"
	TypeEvaluator  ObservationType = "evaluator"
	TypeGuardrail  ObservationType = "guardrail"
)

type PromptRef

type PromptRef struct {
	Name    string
	Version int
}

PromptRef links an observation to a versioned Langfuse prompt.

type Score

type Score struct {
	// ID makes submissions idempotent: Langfuse upserts scores by ID.
	// Optional; the SDK generates a random ID when empty so retried
	// deliveries cannot create duplicates.
	ID string
	// Name identifies the score series, for example "user-feedback".
	// Required; at most 200 characters.
	Name string
	// TraceID, SessionID, and ObservationID select the score target. At
	// least one of TraceID or SessionID is required, and ObservationID
	// additionally requires TraceID.
	TraceID       string
	SessionID     string
	ObservationID string
	// Exactly one of NumericValue or StringValue must be set. Boolean
	// scores use NumericValue 0 or 1 with ScoreTypeBoolean.
	NumericValue *float64
	StringValue  *string
	// DataType is optional; when empty, Langfuse infers NUMERIC or
	// CATEGORICAL from the value type.
	DataType ScoreDataType
	// Comment is explicit content supplied by the caller. It is not
	// processed by Config.Mask; sanitize it before calling the SDK.
	Comment string
	// Metadata is serialized as one JSON value.
	Metadata map[string]any
}

Score is one evaluation or feedback value attached to a trace, a session, or an observation. Scores are submitted through the Langfuse REST API rather than the OpenTelemetry trace pipeline.

type ScoreDataType

type ScoreDataType string

ScoreDataType identifies how Langfuse stores and aggregates a score value.

const (
	ScoreTypeBoolean     ScoreDataType = "BOOLEAN"
	ScoreTypeCategorical ScoreDataType = "CATEGORICAL"
	ScoreTypeCorrection  ScoreDataType = "CORRECTION"
	ScoreTypeNumeric     ScoreDataType = "NUMERIC"
	ScoreTypeText        ScoreDataType = "TEXT"
)

type TraceAttributes

type TraceAttributes struct {
	Name      string
	UserID    string
	SessionID string
	// Tags merge in caller order, de-duplicate, and retain at most 64 values
	// and 16 KiB of UTF-8 data over one trace context.
	Tags []string
	// Metadata merges by top-level key and retains at most 32 distinct keys in
	// one trace context so required observation fields remain within default
	// OpenTelemetry span limits. Keys are limited to 200 bytes.
	Metadata map[string]any
	Version  string
}

TraceAttributes are request-scoped fields copied to an active SDK observation and to spans subsequently started from the returned context.

type Usage

type Usage struct {
	InputTokens              int64
	OutputTokens             int64
	CacheReadInputTokens     int64
	CacheCreationInputTokens int64
	ReasoningOutputTokens    int64

	// Details contains additional provider-specific subsets. Keys prefixed
	// input_ or output_ are subtracted from the corresponding base bucket.
	// Such buckets must be mutually exclusive, must not overlap the typed
	// cache/reasoning fields, and must be subsets of their inclusive total. The
	// canonical input, output, total, cache, and reasoning keys are reserved.
	// At most 64 provider-specific detail buckets are retained, with keys
	// limited to 200 bytes.
	Details map[string]int64
}

Usage records inclusive provider token counts. InputTokens includes cache tokens, and OutputTokens includes reasoning tokens. The SDK normalizes these values to Langfuse's exclusive usage_details representation while total remains the checked sum of the inclusive input and output totals.

Directories

Path Synopsis
contrib
googlegenai module
openai module
examples
existingotel command
quickstart command
shortlived command
streaming command
internal
attributes
Package attributes owns Langfuse's OpenTelemetry attribute contract.
Package attributes owns Langfuse's OpenTelemetry attribute contract.
diagnostic
Package diagnostic reports payload-free SDK diagnostics through OpenTelemetry.
Package diagnostic reports payload-free SDK diagnostics through OpenTelemetry.
otlpreceiver
Package otlpreceiver provides a local OTLP/HTTP protobuf receiver for tests.
Package otlpreceiver provides a local OTLP/HTTP protobuf receiver for tests.
processor
Package processor contains the Langfuse OpenTelemetry span processor.
Package processor contains the Langfuse OpenTelemetry span processor.

Jump to

Keyboard shortcuts

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