Observation Data Loading#
Overview#
Observation data loading in Langfuse refers to the server-side mechanism for fetching observation records (spans, generations, events) from ClickHouse. The central optimization is conditional field exclusion: large fields (input, output, metadata, and tool-related fields) are omitted from the SQL SELECT by default and only included when explicitly requested. This directly reduces query payload size, CPU cost, and memory pressure for high-observation-count traces.
Core Query Function: getObservationsForTrace#
The primary function for trace-scoped observation loading is getObservationsForTrace in packages/shared/src/server/repositories/observations.ts.
Signature:
type GetObservationsForTraceOpts<IncludeIO extends boolean> = {
traceId: string;
projectId: string;
timestamp?: Date;
includeIO?: IncludeIO; // defaults to false
preferredClickhouseService?: PreferredClickhouseService;
};
The includeIO flag uses a generic type parameter (IncludeIO extends boolean) for type-safe conditional field inclusion. When false (the default), the SQL SELECT clause omits input, output, metadata, tool_definitions, tool_calls, and tool_call_names entirely — not at the application layer, but at the ClickHouse query level :
${includeIO === true ? "input, output, metadata," : ""}
...
${includeIO === true ? "tool_definitions, tool_calls, tool_call_names," : ""}
Payload Size Guard#
Even when includeIO: true, the function enforces a byte-level limit after fetching. It accumulates the string lengths of input, output, and metadata values across all returned observations and throws if the total exceeds env.LANGFUSE_API_TRACE_OBSERVATIONS_SIZE_LIMIT_BYTES . This is a secondary defense against NextJS's ~5 MB response size limit.
Usage at the tRPC Layer#
The tRPC traces.byIdWithObservationsAndScores procedure calls getObservationsForTrace with includeIO: false . The observations are returned to the frontend without I/O — the UI fetches individual observation I/O separately on demand. This keeps the initial trace load fast even for large traces.
Related Query Functions#
The same conditional-exclusion pattern appears across several observation repository functions :
| Function | IO Flag | Default |
|---|---|---|
getObservationsForTrace | includeIO | false |
getObservationForTraceIdByName | fetchWithInputOutput | false |
getObservationById | fetchWithInputOutput | false |
getObservationsById | fetchWithInputOutput | false |
Trace Download: Adaptive Field Inclusion (PR #13033)#
PR #13033 (feat: Add new trace download endpoint) introduces a two-level adaptive strategy for the /api/traces/[traceId]/download endpoint:
-
Observation-count threshold: Defined as
TRACE_DOWNLOAD_OMIT_LARGE_FIELDS_THRESHOLD = 350inweb/src/features/traces/shared/traceDownloadConfig.ts. Traces with ≥ 350 observations fetch withselectIOAndMetadata: falseandselectToolData: false; smaller traces fetch with both flagstrue. -
Byte-level validation: The download builder iterates observations and sums
input/output/metadatabyte lengths. If the total exceedsenv.LANGFUSE_API_TRACE_OBSERVATIONS_SIZE_LIMIT_BYTES, it throws aTraceDownloadTooLargeErrorwith the actual and limit sizes.
The response omits the large fields from the export object when omitLargeFields = true, using a spread:
...(!omitLargeFields ? { toolDefinitions, toolCalls, input, output, metadata } : {})
Observations v2 API: Field Group System#
The newer GET /api/public/v2/observations endpoint (ClickHouse-backed, feature-flagged via LANGFUSE_ENABLE_EVENTS_TABLE_V2_APIS) uses a more granular field group system instead of a single boolean :
- The
fieldsquery parameter accepts comma-separated group names (core,basic,io,metadata,model,usage,prompt,metrics,time). - Omitting
fieldsdefaults to["core", "basic"]— no I/O or metadata loaded. - When
ioormetadataare requested, the query switches to a CTE-based split strategy: filtering/ordering on the lightweightevents_coretable first, then fetching I/O only for matched rows fromevents_full. - The
OBSERVATION_FIELD_GROUPSconstant inpackages/shared/src/server/repositories/events.tsis the authoritative list of valid group names.
Key Source Files#
| File | Purpose |
|---|---|
packages/shared/src/server/repositories/observations.ts | Core observation query functions — getObservationsForTrace, getObservationById, etc. |
web/src/server/api/routers/traces.ts | tRPC trace router — calls getObservationsForTrace with includeIO: false |
web/src/features/traces/server/buildTraceExport.ts | Download endpoint logic — observation-count threshold and IO omission |
web/src/features/traces/shared/traceDownloadConfig.ts | TRACE_DOWNLOAD_OMIT_LARGE_FIELDS_THRESHOLD = 350 constant |
packages/shared/src/server/repositories/events.ts | v2 API observation queries — OBSERVATION_FIELD_GROUPS, split CTE logic |