Eval Output Schema#
There are two distinct eval output schema systems in Langfuse, both of which converge on the same score event format at write-time:
- LLM-as-a-Judge output definition — a persisted, user-configured schema that controls what the LLM must return. Defined in
packages/shared/src/features/evals/outputDefinition.ts. - Code evaluator dispatcher contract — the runtime schema that validates what a user-written Python or TypeScript
evaluatefunction returns. Defined inpackages/shared/src/server/evals/codeEvalDispatcherTypes.ts.
LLM-as-a-Judge Output Definition Schema#
Source: outputDefinition.ts
Persisted schema (PersistedEvalOutputDefinition): a union of a legacy string-field format and the current v2 structured format . V2 schemas are versioned objects with a version: 2 discriminant :
| Schema | dataType |
|---|---|
NumericEvalOutputDefinitionV2Schema | NUMERIC |
BooleanEvalOutputDefinitionV2Schema | BOOLEAN |
CategoricalEvalOutputDefinitionV2Schema | CATEGORICAL |
Each V2 schema has a score field (description + optional categories/shouldAllowMultipleMatches for categorical) and a reasoning field, both with a description string used to populate the LLM prompt .
Runtime flow:
resolvePersistedEvalOutputDefinitionnormalizes legacy and v2 rows intoResolvedEvalOutputDefinition— a flat shape withdataType,reasoningDescription,scoreDescription, and (for categorical)categories+shouldAllowMultipleMatches.buildEvalOutputResultSchema/compilePersistedEvalOutputDefinitionproduce a Zod schema passed to the LLM viawithStructuredOutput(). The schema enforces:- Numeric →
z.number() - Boolean →
z.boolean() - Categorical (single) →
z.enum([...categories]) - Categorical (multi-match) →
z.array(...).min(1)with uniqueness check
- Numeric →
validateEvalOutputResultrunssafeParseagainst the compiled schema and normalizes the raw{ score, reasoning }object into the typedEvalOutputResultunion :{ dataType: "NUMERIC", score: number, reasoning: string }{ dataType: "BOOLEAN", score: boolean, reasoning: string }{ dataType: "CATEGORICAL", matches: string[], reasoning: string }
Code Evaluator Score Contract (CodeEvalScore)#
Source: codeEvalDispatcherTypes.ts
The CodeEvalScoreSchema validates each score object returned by user-written code. It is a union over four dataType variants, plus a no-dataType fallback:
dataType | value type | Notes |
|---|---|---|
NUMERIC | number | |
CATEGORICAL | string | |
BOOLEAN | boolean | 0 | 1 | "true"/"false"/"1"/"0" | Normalized to 0 | 1 on the wire |
TEXT | string (min 1, max TEXT_SCORE_MAX_LENGTH) | Fails fast if over limit |
| (absent) | string | number | Fallback; dataType inferred downstream |
All fields are camelCase in this schema. Shared optional fields: name (required), value (required), dataType (required), comment, configId, metadata.
The full result envelope is { scores: CodeEvalScore[] } (min 1 score), validated by parseDispatchResult, which throws a CodeEvalDispatcherError with code INVALID_RESULT on failure.
Python vs TypeScript Case Conventions#
The user-facing function contract uses different field names depending on the runtime language :
| Field | Python (Score dataclass) | TypeScript (Score type) |
|---|---|---|
| Score data type | data_type | dataType |
| Score config ID | config_id | configId |
| Tool calls in context | ctx.observation.tool_calls | ctx.observation.toolCalls |
| Experiment expected output | ctx.experiment.item_expected_output | ctx.experiment.itemExpectedOutput |
| Experiment metadata | ctx.experiment.item_metadata | ctx.experiment.itemMetadata |
The server-side CodeEvalScoreSchema accepts only camelCase. The case translation from Python snake_case to camelCase happens inside the dispatcher runtime (AWS Lambda handler for Python, or the insecure-local dispatcher for TypeScript). By the time scores reach parseDispatchResult, all field names must be camelCase. A Python evaluator returning { "data_type": "BOOLEAN" } instead of { "dataType": "BOOLEAN" } will fail this validation with an INVALID_RESULT error .
Score Event Write Path#
After dispatch validation, the buildEvalScoreWritePayloads function in evalScoreEvent.ts converts EvalOutputResult or CodeEvalScoreWithName objects into ScoreEventType envelopes. All fields remain camelCase (dataType, traceId, observationId, executionTraceId). These events are consumed by the standard ingestion pipeline where validateAndInflateScore handles configId lookups and final score hydration.
Key Files#
| File | Purpose |
|---|---|
packages/shared/src/features/evals/outputDefinition.ts | LLM evaluator output definition, schema compilation, result validation |
packages/shared/src/server/evals/codeEvalDispatcherTypes.ts | CodeEvalScoreSchema, parseDispatchResult, dispatcher error types |
packages/shared/src/server/evals/codeEvalExecution.ts | runCodeBasedEvaluationDispatch, payload construction, error mapping |
worker/src/features/evaluation/codeBased/executeCodeBasedEvaluation.ts | Worker entry point for code evaluator execution |
worker/src/features/evaluation/evalScoreEvent.ts | Converts eval results to ScoreEventType write payloads |
packages/shared/src/domain/scores.ts | ScoreDataTypeEnum — canonical string values for all data types |
Public docs: Code evaluators — Score fields reference