CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-langfuse--langfuse-java

Java client for the Langfuse API providing access to observability and analytics features for LLM applications

Overview
Eval results
Files

ingestion.mddocs/

Ingestion API

The Ingestion API provides batched ingestion of tracing data for Langfuse. This is the primary API for tracing operations, supporting event deduplication and multiple event types in a single batch. The API handles traces, observations (spans, events, generations), and scores.

Note: The recommended approach for tracing is via OpenTelemetry instrumentation rather than direct Ingestion API usage, as it provides more detailed and standardized tracing without requiring manual batching and update handling.

Capabilities

IngestionClient

Client for batched ingestion of tracing events.

/**
 * Batched ingestion for Langfuse Tracing
 *
 * Features:
 * - Event deduplication using event IDs
 * - Multiple event types in a single batch
 * - Batch size limit: 3.5 MB
 * - Returns 207 Multi-Status with individual error details
 *   instead of 4xx for input errors
 *
 * @param request Batch of ingestion events
 * @param requestOptions Optional request configuration
 * @return Response with success/error details for each event
 */
IngestionResponse batch(IngestionRequest request);
IngestionResponse batch(IngestionRequest request, RequestOptions requestOptions);

Usage Example:

import com.langfuse.client.LangfuseClient;
import com.langfuse.client.resources.ingestion.requests.IngestionRequest;
import com.langfuse.client.resources.ingestion.types.*;
import com.langfuse.client.resources.commons.types.CreateScoreValue;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;

LangfuseClient client = LangfuseClient.builder()
    .url("https://cloud.langfuse.com")
    .credentials("pk-lf-...", "sk-lf-...")
    .build();

// Create a batch of events
// Note: All event types use staged builders requiring id(), timestamp(), body() in that order
IngestionRequest request = IngestionRequest.builder()
    .batch(List.of(
        // Trace event - staged builder: id() -> timestamp() -> body() -> metadata() (optional) -> build()
        IngestionEvent.traceCreate(TraceEvent.builder()
            .id("event-trace-123")  // Event ID for deduplication (required first)
            .timestamp(OffsetDateTime.now().toString())  // Event timestamp as String ISO 8601 (required second)
            .body(TraceBody.builder()  // Event body (required third)
                .id("trace-123")
                .name("User Query")
                .userId("user-456")
                .input(Map.of("query", "What is AI?"))
                .timestamp(OffsetDateTime.now())
                .build())
            .metadata(Map.of("source", "api"))  // Optional event metadata
            .build()),

        // Span event - staged builder: id() -> timestamp() -> body() -> metadata() (optional) -> build()
        IngestionEvent.spanCreate(CreateSpanEvent.builder()
            .id("event-span-123")  // Event ID for deduplication (required first)
            .timestamp(OffsetDateTime.now().plusSeconds(1).toString())  // Event timestamp as String ISO 8601 (required second)
            .body(CreateSpanBody.builder()  // Event body (required third)
                .id("span-123")
                .traceId("trace-123")
                .name("LLM Call")
                .startTime(OffsetDateTime.now().plusSeconds(1))
                .endTime(OffsetDateTime.now().plusSeconds(3))
                .input(Map.of("prompt", "What is AI?"))
                .output(Map.of("response", "AI is..."))
                .build())
            .build()),

        // Score event - staged builder: id() -> timestamp() -> body() -> metadata() (optional) -> build()
        IngestionEvent.scoreCreate(ScoreEvent.builder()
            .id("event-score-123")  // Event ID for deduplication (required first)
            .timestamp(OffsetDateTime.now().plusSeconds(2).toString())  // Event timestamp as String ISO 8601 (required second)
            .body(ScoreBody.builder()  // Score body uses staged builder: name() -> value() -> other fields -> build()
                .name("quality")  // Required first: score name
                .value(CreateScoreValue.of(0.95))  // Required second: score value as CreateScoreValue union
                .id("score-123")  // Optional: score ID (available in _FinalStage)
                .traceId("trace-123")  // Optional: trace ID
                .build())
            .build())
    ))
    .build();

// Send batch
IngestionResponse response = client.ingestion().batch(request);

// Check results
for (IngestionSuccess success : response.getSuccesses()) {
    System.out.println("Success: " + success.getId());
}
for (IngestionError error : response.getErrors()) {
    System.err.println("Error: " + error.getId() + " - " + error.getMessage());
}

Request Types

IngestionRequest

/**
 * Batch ingestion request containing multiple events
 */
public final class IngestionRequest {
    List<IngestionEvent> getBatch();
    Optional<Object> getMetadata();

    static Builder builder();
}

IngestionEvent

Union type representing all possible event types.

/**
 * Union type for ingestion events
 * Supports all event types for tracing
 */
public final class IngestionEvent {
    // Factory methods for creating typed events
    static IngestionEvent traceCreate(TraceEvent value);
    static IngestionEvent scoreCreate(ScoreEvent value);
    static IngestionEvent observationCreate(CreateObservationEvent value);
    static IngestionEvent observationUpdate(UpdateObservationEvent value);
    static IngestionEvent eventCreate(CreateEventEvent value);
    static IngestionEvent spanCreate(CreateSpanEvent value);
    static IngestionEvent spanUpdate(UpdateSpanEvent value);
    static IngestionEvent generationCreate(CreateGenerationEvent value);
    static IngestionEvent generationUpdate(UpdateGenerationEvent value);
    static IngestionEvent sdkLog(SdkLogEvent value);

    // Visitor pattern for type-safe access
    <T> T visit(Visitor<T> visitor);
}

Event Types

TraceEvent

Create or update a trace.

/**
 * Event for creating/updating a trace
 * Type: "trace-create"
 *
 * Staged Builder Pattern (required order):
 * 1. id(String) - Event ID for deduplication
 * 2. timestamp(String) - Event timestamp (ISO 8601 format)
 * 3. body(TraceBody) - Trace data
 * 4. metadata(Object) - Optional SDK metadata
 * 5. build() - Build the event
 */
public final class TraceEvent {
    String getId();  // Event ID for deduplication
    String getTimestamp();  // Event timestamp as String (ISO 8601)
    Optional<Object> getMetadata();  // Optional SDK metadata
    TraceBody getBody();

    static IdStage builder();  // Returns staged builder starting with id()
}

ScoreEvent

Create a score for a trace or observation.

/**
 * Event for creating a score
 * Type: "score-create"
 *
 * Staged Builder Pattern (required order):
 * 1. id(String) - Event ID for deduplication
 * 2. timestamp(String) - Event timestamp (ISO 8601 format)
 * 3. body(ScoreBody) - Score data
 * 4. metadata(Object) - Optional SDK metadata
 * 5. build() - Build the event
 */
public final class ScoreEvent {
    String getId();  // Event ID for deduplication
    String getTimestamp();  // Event timestamp as String (ISO 8601)
    Optional<Object> getMetadata();  // Optional SDK metadata
    ScoreBody getBody();

    static IdStage builder();  // Returns staged builder starting with id()
}

Observation Events

Observations represent spans, events, and generations in traces.

/**
 * Event for creating an observation
 * Type: "observation-create"
 *
 * Staged Builder Pattern (required order):
 * 1. id(String) - Event ID for deduplication
 * 2. timestamp(String) - Event timestamp (ISO 8601 format)
 * 3. body(ObservationBody) - Observation data
 * 4. metadata(Object) - Optional SDK metadata
 * 5. build() - Build the event
 */
public final class CreateObservationEvent {
    String getId();
    String getTimestamp();
    Optional<Object> getMetadata();
    ObservationBody getBody();

    static IdStage builder();
}

/**
 * Event for updating an observation
 * Type: "observation-update"
 *
 * Staged Builder Pattern (required order):
 * 1. id(String) - Event ID for deduplication
 * 2. timestamp(String) - Event timestamp (ISO 8601 format)
 * 3. body(OptionalObservationBody) - Observation data
 * 4. metadata(Object) - Optional SDK metadata
 * 5. build() - Build the event
 */
public final class UpdateObservationEvent {
    String getId();
    String getTimestamp();
    Optional<Object> getMetadata();
    OptionalObservationBody getBody();

    static IdStage builder();
}

Span Events

Spans represent operations or sub-processes within a trace.

/**
 * Event for creating a span
 * Type: "span-create"
 *
 * Staged Builder Pattern (required order):
 * 1. id(String) - Event ID for deduplication
 * 2. timestamp(String) - Event timestamp (ISO 8601 format)
 * 3. body(CreateSpanBody) - Span data
 * 4. metadata(Object) - Optional SDK metadata
 * 5. build() - Build the event
 */
public final class CreateSpanEvent {
    String getId();
    String getTimestamp();
    Optional<Object> getMetadata();
    CreateSpanBody getBody();

    static IdStage builder();
}

/**
 * Event for updating a span
 * Type: "span-update"
 *
 * Staged Builder Pattern (required order):
 * 1. id(String) - Event ID for deduplication
 * 2. timestamp(String) - Event timestamp (ISO 8601 format)
 * 3. body(UpdateSpanBody) - Span data
 * 4. metadata(Object) - Optional SDK metadata
 * 5. build() - Build the event
 */
public final class UpdateSpanEvent {
    String getId();
    String getTimestamp();
    Optional<Object> getMetadata();
    UpdateSpanBody getBody();

    static IdStage builder();
}

Event Events

Events represent point-in-time occurrences within a trace.

/**
 * Event for creating an event
 * Type: "event-create"
 *
 * Staged Builder Pattern (required order):
 * 1. id(String) - Event ID for deduplication
 * 2. timestamp(String) - Event timestamp (ISO 8601 format)
 * 3. body(CreateEventBody) - Event data
 * 4. metadata(Object) - Optional SDK metadata
 * 5. build() - Build the event
 */
public final class CreateEventEvent {
    String getId();
    String getTimestamp();
    Optional<Object> getMetadata();
    CreateEventBody getBody();

    static IdStage builder();
}

Generation Events

Generations represent LLM generation calls within a trace.

/**
 * Event for creating a generation
 * Type: "generation-create"
 *
 * Staged Builder Pattern (required order):
 * 1. id(String) - Event ID for deduplication
 * 2. timestamp(String) - Event timestamp (ISO 8601 format)
 * 3. body(CreateGenerationBody) - Generation data
 * 4. metadata(Object) - Optional SDK metadata
 * 5. build() - Build the event
 */
public final class CreateGenerationEvent {
    String getId();
    String getTimestamp();
    Optional<Object> getMetadata();
    CreateGenerationBody getBody();

    static IdStage builder();
}

/**
 * Event for updating a generation
 * Type: "generation-update"
 *
 * Staged Builder Pattern (required order):
 * 1. id(String) - Event ID for deduplication
 * 2. timestamp(String) - Event timestamp (ISO 8601 format)
 * 3. body(UpdateGenerationBody) - Generation data
 * 4. metadata(Object) - Optional SDK metadata
 * 5. build() - Build the event
 */
public final class UpdateGenerationEvent {
    String getId();
    String getTimestamp();
    Optional<Object> getMetadata();
    UpdateGenerationBody getBody();

    static IdStage builder();
}

SdkLogEvent

SDK log messages for debugging.

/**
 * Event for SDK log messages
 * Type: "sdk-log"
 *
 * Staged Builder Pattern (required order):
 * 1. id(String) - Event ID for deduplication
 * 2. timestamp(String) - Event timestamp (ISO 8601 format)
 * 3. body(SdkLogBody) - Log message data
 * 4. metadata(Object) - Optional SDK metadata
 * 5. build() - Build the event
 */
public final class SdkLogEvent {
    String getId();
    String getTimestamp();
    Optional<Object> getMetadata();
    SdkLogBody getBody();

    static IdStage builder();
}

Event Body Types

TraceBody

/**
 * Trace data for trace events
 */
public final class TraceBody {
    Optional<String> getId();  // Unique trace ID
    Optional<OffsetDateTime> getTimestamp();  // Timestamp as OffsetDateTime
    Optional<String> getName();
    Optional<String> getUserId();
    Optional<Object> getMetadata();
    Optional<String> getRelease();
    Optional<String> getVersion();
    Optional<String> getSessionId();
    Optional<Object> getInput();
    Optional<Object> getOutput();
    Optional<List<String>> getTags();
    Optional<String> getEnvironment();
    Optional<Boolean> getPublic();  // Make trace publicly accessible

    static Builder builder();
}

ScoreBody

/**
 * Score data for score events
 * Uses staged builder: name() -> value() -> optional fields -> build()
 */
public final class ScoreBody {
    Optional<String> getId();  // Unique score ID
    Optional<String> getTraceId();
    String getName();  // Required: score name
    CreateScoreValue getValue();  // Required: Union type (String, Integer, or Double)
    Optional<String> getSessionId();
    Optional<String> getObservationId();
    Optional<String> getDatasetRunId();
    Optional<String> getComment();
    Optional<Object> getMetadata();
    Optional<ScoreDataType> getDataType();  // NUMERIC, CATEGORICAL, BOOLEAN
    Optional<String> getConfigId();
    Optional<String> getEnvironment();

    static NameStage builder();  // Returns staged builder starting with name()
}

ObservationBody

/**
 * Observation data (for spans, events, generations)
 */
public final class ObservationBody {
    Optional<String> getId();  // Unique observation ID
    Optional<String> getTraceId();
    ObservationType getType();  // SPAN, EVENT, GENERATION (required)
    Optional<String> getName();
    Optional<OffsetDateTime> getStartTime();  // Start time as OffsetDateTime
    Optional<OffsetDateTime> getEndTime();  // End time as OffsetDateTime
    Optional<OffsetDateTime> getCompletionStartTime();  // Completion start time as OffsetDateTime
    Optional<String> getModel();
    Optional<Map<String, MapValue>> getModelParameters();
    Optional<Object> getInput();
    Optional<Object> getOutput();
    Optional<Usage> getUsage();  // Token/cost usage (from commons.types)
    Optional<Object> getMetadata();
    Optional<ObservationLevel> getLevel();  // DEBUG, DEFAULT, WARNING, ERROR
    Optional<String> getStatusMessage();
    Optional<String> getParentObservationId();
    Optional<String> getVersion();
    Optional<String> getEnvironment();

    static Builder builder();
}

OptionalObservationBody

/**
 * Observation data with all optional fields (for updates)
 * Contains a subset of ObservationBody fields, all optional
 */
public final class OptionalObservationBody {
    Optional<String> getTraceId();
    Optional<String> getName();
    Optional<OffsetDateTime> getStartTime();  // Start time as OffsetDateTime
    Optional<Object> getMetadata();
    Optional<Object> getInput();
    Optional<Object> getOutput();
    Optional<ObservationLevel> getLevel();
    Optional<String> getStatusMessage();
    Optional<String> getParentObservationId();
    Optional<String> getVersion();
    Optional<String> getEnvironment();

    static Builder builder();
}

Typed Observation Bodies

/**
 * Span-specific body
 * Shares fields with ObservationBody through interface implementation
 */
public final class CreateSpanBody {
    // Has all ObservationBody fields
    static Builder builder();
}

/**
 * Update span body
 * Shares fields with OptionalObservationBody through interface implementation
 */
public final class UpdateSpanBody {
    static Builder builder();
}

/**
 * Event-specific body
 * Shares fields with ObservationBody through interface implementation
 */
public final class CreateEventBody {
    static Builder builder();
}

/**
 * Update event body
 * Shares fields with OptionalObservationBody through interface implementation
 */
public final class UpdateEventBody {
    static Builder builder();
}

/**
 * Generation-specific body
 * Shares fields with ObservationBody through interface implementation
 */
public final class CreateGenerationBody {
    static Builder builder();
}

/**
 * Update generation body
 * Shares fields with OptionalObservationBody through interface implementation
 */
public final class UpdateGenerationBody {
    static Builder builder();
}

SdkLogBody

/**
 * SDK log message data
 */
public final class SdkLogBody {
    String getLog();  // Log message content

    static Builder builder();
}

Usage Types

IngestionUsage

Union type for different usage formats.

/**
 * Union type for usage information
 * Supports OpenAI usage formats and custom usage details
 */
public final class IngestionUsage {
    static IngestionUsage openAiUsage(OpenAiUsage value);
    static IngestionUsage usageDetails(UsageDetails value);

    <T> T visit(Visitor<T> visitor);
}

OpenAiUsage

OpenAI-style usage with token counts.

/**
 * OpenAI usage with token counts
 */
public final class OpenAiUsage {
    Optional<Integer> getPromptTokens();
    Optional<Integer> getCompletionTokens();
    Optional<Integer> getTotalTokens();

    static Builder builder();
}

OpenAiCompletionUsageSchema

/**
 * OpenAI completion usage schema
 */
public final class OpenAiCompletionUsageSchema {
    Optional<Integer> getPromptTokens();
    Optional<Integer> getCompletionTokens();
    Optional<Integer> getTotalTokens();

    static Builder builder();
}

OpenAiResponseUsageSchema

/**
 * Extended OpenAI response usage schema
 */
public final class OpenAiResponseUsageSchema {
    Optional<Integer> getPromptTokens();
    Optional<Integer> getCompletionTokens();
    Optional<Integer> getTotalTokens();
    // Extended fields for detailed token breakdown

    static Builder builder();
}

UsageDetails

/**
 * Union type for detailed usage information
 * Use static factory methods to create instances
 */
public final class UsageDetails {
    // Factory methods for creating UsageDetails
    static UsageDetails of(Map<String, Integer> value);  // Custom usage map
    static UsageDetails of(OpenAiCompletionUsageSchema value);  // OpenAI completion usage
    static UsageDetails of(OpenAiResponseUsageSchema value);  // OpenAI response usage

    // Visitor pattern for type-safe access
    <T> T visit(Visitor<T> visitor);
}

Response Types

IngestionResponse

/**
 * Response from batch ingestion
 * HTTP 207 Multi-Status with individual event results
 */
public final class IngestionResponse {
    List<IngestionSuccess> getSuccesses();
    List<IngestionError> getErrors();

    static Builder builder();
}

IngestionSuccess

/**
 * Successful ingestion of an event
 */
public final class IngestionSuccess {
    String getId();  // Event ID
    int getStatus();  // HTTP status (200, 201, etc.)

    static Builder builder();
}

IngestionError

/**
 * Failed ingestion of an event
 */
public final class IngestionError {
    String getId();  // Event ID
    int getStatus();  // HTTP status (400, 422, etc.)
    String getMessage();  // Error message
    Optional<String> getError();  // Additional error details

    static Builder builder();
}

Enums

ObservationType

/**
 * Type of observation
 */
public enum ObservationType {
    SPAN,
    EVENT,
    GENERATION
}

ObservationLevel

/**
 * Log level for observations
 */
public enum ObservationLevel {
    DEBUG,
    DEFAULT,
    WARNING,
    ERROR
}

ModelUsageUnit

/**
 * Unit for usage measurement
 */
public enum ModelUsageUnit {
    CHARACTERS,
    TOKENS,
    REQUESTS,
    IMAGES,
    SECONDS
}

ScoreDataType

/**
 * Data type for scores
 */
public enum ScoreDataType {
    NUMERIC,
    CATEGORICAL,
    BOOLEAN
}

Complete Tracing Example

import com.langfuse.client.LangfuseClient;
import com.langfuse.client.resources.ingestion.requests.IngestionRequest;
import com.langfuse.client.resources.ingestion.types.*;
import com.langfuse.client.resources.commons.types.CreateScoreValue;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;

public class TracingExample {
    public static void main(String[] args) {
        LangfuseClient client = LangfuseClient.builder()
            .url("https://cloud.langfuse.com")
            .credentials("pk-lf-...", "sk-lf-...")
            .build();

        String traceId = "trace-" + System.currentTimeMillis();
        String spanId = "span-" + System.currentTimeMillis();
        String generationId = "gen-" + System.currentTimeMillis();

        IngestionRequest request = IngestionRequest.builder()
            .batch(List.of(
                // 1. Create trace - Note: Staged builder requires id() -> timestamp() -> body()
                IngestionEvent.traceCreate(TraceEvent.builder()
                    .id("event-" + traceId)  // Event ID for deduplication
                    .timestamp(OffsetDateTime.now().toString())  // Event timestamp as String (ISO 8601)
                    .body(TraceBody.builder()  // Trace body data
                        .id(traceId)
                        .name("Chat Completion")
                        .userId("user-123")
                        .sessionId("session-456")
                        .input(Map.of("message", "Hello, AI!"))
                        .timestamp(OffsetDateTime.now())
                        .tags(List.of("chat", "production"))
                        .build())
                    .build()),

                // 2. Create span for preprocessing - Staged builder: id() -> timestamp() -> body()
                IngestionEvent.spanCreate(CreateSpanEvent.builder()
                    .id("event-" + spanId)  // Event ID for deduplication
                    .timestamp(OffsetDateTime.now().toString())  // Event timestamp as String (ISO 8601)
                    .body(CreateSpanBody.builder()  // Span body data
                        .id(spanId)
                        .traceId(traceId)
                        .name("Preprocess Input")
                        .startTime(OffsetDateTime.now())
                        .endTime(OffsetDateTime.now().plusSeconds(1))
                        .input(Map.of("raw", "Hello, AI!"))
                        .output(Map.of("processed", "hello ai"))
                        .build())
                    .build()),

                // 3. Create generation for LLM call - Staged builder: id() -> timestamp() -> body()
                IngestionEvent.generationCreate(CreateGenerationEvent.builder()
                    .id("event-" + generationId)  // Event ID for deduplication
                    .timestamp(OffsetDateTime.now().plusSeconds(1).toString())  // Event timestamp as String (ISO 8601)
                    .body(CreateGenerationBody.builder()  // Generation body data
                        .id(generationId)
                        .traceId(traceId)
                        .parentObservationId(spanId)
                        .name("OpenAI Chat")
                        .model("gpt-4")
                        .startTime(OffsetDateTime.now().plusSeconds(1))
                        .endTime(OffsetDateTime.now().plusSeconds(3))
                        .input(Map.of("messages", List.of(
                            Map.of("role", "user", "content", "Hello, AI!")
                        )))
                        .output(Map.of("content", "Hello! How can I help you?"))
                        .usage(IngestionUsage.openAiUsage(
                            OpenAiUsage.builder()
                                .promptTokens(10)
                                .completionTokens(8)
                                .totalTokens(18)
                                .build()
                        ))
                        .build())
                    .build()),

                // 4. Add quality score - Staged builder: id() -> timestamp() -> body()
                IngestionEvent.scoreCreate(ScoreEvent.builder()
                    .id("event-score-" + System.currentTimeMillis())  // Event ID for deduplication
                    .timestamp(OffsetDateTime.now().plusSeconds(3).toString())  // Event timestamp as String (ISO 8601)
                    .body(ScoreBody.builder()  // Score body uses staged builder: name() -> value() -> optional fields
                        .name("quality")  // Required first: score name
                        .value(CreateScoreValue.of(0.95))  // Required second: score value
                        .id("score-" + System.currentTimeMillis())  // Optional: available in _FinalStage
                        .traceId(traceId)  // Optional: trace ID
                        .dataType(ScoreDataType.NUMERIC)  // Optional: data type
                        .build())
                    .build())
            ))
            .build();

        // Send batch
        IngestionResponse response = client.ingestion().batch(request);

        // Handle results
        System.out.println("Successes: " + response.getSuccesses().size());
        System.out.println("Errors: " + response.getErrors().size());

        for (IngestionError error : response.getErrors()) {
            System.err.println("Failed event " + error.getId() + ": " + error.getMessage());
        }
    }
}

Best Practices

  1. Use Event IDs for Deduplication: Always provide unique event IDs to enable deduplication on retries
  2. Batch Related Events: Send related events (trace + observations + scores) in a single batch
  3. Handle Partial Failures: Check both successes and errors in the response (207 Multi-Status)
  4. Stay Under Size Limit: Keep batch sizes under 3.5 MB
  5. Use Timestamps: Provide accurate ISO 8601 timestamps for all events
  6. Link Observations: Use traceId and parentObservationId to build trace hierarchy
  7. Prefer OpenTelemetry: For production use, consider OpenTelemetry instrumentation instead

Related Documentation

  • Traces and Observations - Retrieving and managing traces
  • Scores - Score management and configuration
  • Common Types - Shared type definitions

Install with Tessl CLI

npx tessl i tessl/maven-com-langfuse--langfuse-java

docs

client-configuration.md

comments-annotations.md

common-types.md

datasets.md

exceptions.md

health.md

index.md

ingestion.md

media.md

metrics.md

models.md

pagination.md

projects-organizations.md

prompts.md

scim.md

scores.md

sessions.md

traces-observations.md

tile.json