Eval Template Versioning#
The EvalTemplate model supports two distinct versioning tracks depending on whether a template is project-managed (created by users) or Langfuse-managed (predefined, global templates). Both tracks share the same eval_templates table and version Int field, differentiated by whether projectId is set or null.
Database Schema#
The EvalTemplate model enforces a composite unique constraint:
@@unique([projectId, name, version])
This means each (projectId, name, version) triple must be unique. When projectId is null, the row belongs to the global Langfuse-managed pool.
Project Templates: Immutable Version Increment#
When a user creates or updates a project-level eval template, a new immutable version is appended — existing versions are never mutated.
The createTemplate mutation runs inside a transaction:
- Query all existing versions for the same
(projectId, name), ordered byversion DESC. - Pick the highest as
latestTemplate. - Write a new row with
version: (latestTemplate?.version ?? 0) + 1— starting at 1 if none exist .
This also covers cloning a Langfuse-managed template into a project: the clone is written with projectId set to the target project and its version counter starts at 1 .
The allTemplatesForName procedure retrieves the full version history for a template name, returned in descending order, enabling the UI to show version history and allow users to select a specific version.
Managed Evaluators: JSON-Defined Versions#
Langfuse ships 23 built-in evaluators (e.g., Hallucination, Helpfulness, Relevance, RAGAS variants) whose versions are statically declared in worker/src/constants/managed-evaluators.json (imported at line 3 of upsertManagedEvaluators.ts). Each entry in that file includes an explicit version integer field .
The upsertManagedEvaluators function syncs this JSON into the database:
- Validates all entries against
ManagedEvaluatorSchema(Zod) . - Skips rows whose
updatedAtalready matches the JSON'supdated_attimestamp, unlessforce=true. - Upserts by stable
id— theidand timestamps are stored in the JSON to guarantee deterministic results and stable diffs . - Sets
projectId: nullfor all managed templates .
The version field for managed evaluators travels directly from the JSON definition through to the database row — there is no auto-increment. To release a new version of a managed evaluator, a developer increments the version field in the JSON and updates updated_at.
Key Behavioral Differences#
| Project Templates | Managed Evaluators | |
|---|---|---|
projectId | Set (scoped to project) | null (global) |
| Version source | Auto-incremented from DB max | Declared in managed-evaluators.json |
| Mutation model | Immutable append (new row per save) | Upsert in-place by stable id |
| Uniqueness key | (projectId, name, version) | id (fixed in JSON) |
Key Files#
| File | Purpose |
|---|---|
packages/shared/prisma/schema.prisma | EvalTemplate model definition and unique constraint |
web/src/features/evals/server/router.ts | createTemplate mutation with version increment logic |
worker/src/scripts/upsertManagedEvaluators.ts | Sync managed evaluator JSON into DB |
worker/src/constants/managed-evaluators.json | Static definitions for all Langfuse-managed evaluators |