eval

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 14, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package eval provides an evaluation harness for LoopKit loops. It runs a dataset of test cases through a loop definition, scores the outputs, computes aggregates, and integrates with go test via RunSuite.

Usage:

suite := eval.Suite{
    Name:        "my-suite",
    Dataset:     eval.Dataset{...},
    Run:         myRunFunc,
    Scorers:     []eval.Scorer{eval.ExactMatch, eval.RubricFunc(...)},
    Parallelism: 4,
}
report, err := suite.Execute(ctx)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func WriteBaseline

func WriteBaseline(path string, report Report) error

WriteBaseline writes a report to path as the new baseline. Call this after a successful gate pass to update the baseline.

Types

type Aggregate

type Aggregate struct {
	// ScorerName is the name of the scorer.
	ScorerName string `json:"scorer_name"`
	// Mean is the average score across all cases (excluding errored cases).
	Mean float64 `json:"mean"`
	// PassRate is the fraction of cases with score >= 0.5 (by default).
	PassRate float64 `json:"pass_rate"`
	// NumCases is the total number of cases evaluated.
	NumCases int `json:"num_cases"`
	// NumErrors is the number of cases that errored during scoring.
	NumErrors int `json:"num_errors"`
}

Aggregate contains per-scorer aggregate statistics.

type Case

type Case struct {
	// ID is the unique identifier for this case.
	ID string `json:"id"`
	// Input is the input to the loop (arbitrary JSON-serializable value).
	Input any `json:"input"`
	// Expect is the expected output (arbitrary JSON-serializable value).
	Expect any `json:"expect"`
	// Tags are optional labels for filtering.
	Tags []string `json:"tags,omitempty"`
}

Case is a single evaluation case.

type CaseResult

type CaseResult struct {
	// CaseID is the case identifier.
	CaseID string `json:"case_id"`
	// Scores maps scorer name to score value.
	Scores map[string]Score `json:"scores"`
	// Error is non-empty if the case run failed.
	Error string `json:"error,omitempty"`
}

CaseResult is the per-case evaluation result.

type Dataset

type Dataset []Case

Dataset is a collection of evaluation cases.

func LoadJSONL

func LoadJSONL(path string) (Dataset, error)

LoadJSONL loads a dataset from a JSONL file (one JSON object per line). Each line must be a valid Case JSON.

type ExactMatchScorer

type ExactMatchScorer struct{}

ExactMatchScorer scores 1.0 if the actual output exactly equals the expected output (by JSON round-trip equality), and 0.0 otherwise.

func (ExactMatchScorer) Name

func (ExactMatchScorer) Name() string

func (ExactMatchScorer) Score

type Gate

type Gate struct {
	// MinMean is the minimum acceptable mean score per scorer (0..1).
	// If any scorer's mean falls below this threshold, the test fails.
	// Zero means no minimum.
	MinMean float64
	// MaxRegression is the maximum allowed mean score drop vs baseline (0..1).
	// If any scorer regresses by more than this amount, the test fails.
	// Zero means any regression fails.
	MaxRegression float64
	// Baseline is a path to a baseline report JSON file (from a previous run).
	// If empty, regression checking is disabled.
	Baseline string
}

Gate defines the pass/fail criteria for an eval suite in go test.

type JudgeRunFunc

type JudgeRunFunc func(ctx context.Context, rubric string, c Case, run ScoredRun) (Score, error)

JudgeRunFunc is the function that runs the LLM judge for a single case. It receives the judge context (case + run output) and returns a score in [0, 1]. Implementations typically run a LoopKit loop with the rubric as system prompt.

type LLMJudgeScorer

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

LLMJudgeScorer runs a judge model as a LoopKit loop to score each case. The judge runs with its own budget; judge overspend causes score=0 (not panic). The judge's provider and rubric prompt are configured at construction time.

func (*LLMJudgeScorer) Name

func (j *LLMJudgeScorer) Name() string

func (*LLMJudgeScorer) Score

func (j *LLMJudgeScorer) Score(ctx context.Context, c Case, run ScoredRun) (Score, error)

type RegressionCase

type RegressionCase struct {
	CaseID     string  `json:"case_id"`
	ScorerName string  `json:"scorer_name"`
	OldScore   float64 `json:"old_score"`
	NewScore   float64 `json:"new_score"`
	Delta      float64 `json:"delta"` // new - old (negative = regression)
}

RegressionCase is a case that regressed between old and new reports.

type RegressionReport

type RegressionReport struct {
	// Regressions is the list of cases that regressed (new score < old score).
	Regressions []RegressionCase `json:"regressions"`
	// Improvements is the list of cases that improved (new score > old score).
	Improvements []RegressionCase `json:"improvements"`
	// MeanDelta maps scorer name to mean score delta (new - old).
	MeanDelta map[string]float64 `json:"mean_delta"`
}

RegressionReport compares two reports and identifies regressions.

func Compare

func Compare(old, new Report) RegressionReport

Compare computes the regression between old and new reports.

type Report

type Report struct {
	// SuiteName is the suite's name.
	SuiteName string `json:"suite_name"`
	// Cases contains per-case results, sorted by CaseID.
	Cases []CaseResult `json:"cases"`
	// Aggregates contains per-scorer aggregate statistics.
	Aggregates []Aggregate `json:"aggregates"`
	// TotalDuration is how long the suite took.
	TotalDuration time.Duration `json:"total_duration_ns"`
	// GeneratedAt is the timestamp of this report.
	GeneratedAt time.Time `json:"generated_at"`
}

Report is the result of executing a Suite.

func ReportFromJSON

func ReportFromJSON(data []byte) (Report, error)

ReportFromJSON deserializes a Report from JSON bytes.

func RunSuite

func RunSuite(t *testing.T, suite Suite, gate Gate) Report

RunSuite executes the suite and applies the gate, calling t.Fatal on breach. If baseline is configured and exists, it also checks for regressions. The new report is written to t.TempDir()/report.json for CI artifact capture.

func (Report) MarshalJSON

func (r Report) MarshalJSON() ([]byte, error)

MarshalJSON provides a stable JSON schema for Report.

type RubricFunc

type RubricFunc func(c Case, run ScoredRun) Score

RubricFunc creates a Scorer from a simple scoring function. The function receives the case and run result and returns a Score.

type RunFunc

type RunFunc func(ctx context.Context, c Case) (output any, err error)

RunFunc is the function that runs a single case and returns its output.

type Score

type Score struct {
	// Value is in [0, 1].
	Value float64
	// Label is a human-readable description of the score.
	Label string
	// Details is optional additional detail.
	Details string
}

Score is the result of a scorer for one case. 0=worst, 1=best.

type ScoredRun

type ScoredRun struct {
	// CaseID is the case that was run.
	CaseID string
	// ActualOutput is what the loop returned (JSON-serializable).
	ActualOutput any
	// ActualJSON is the JSON-encoded actual output.
	ActualJSON json.RawMessage
	// Error is non-nil if the run itself failed (not a scoring failure).
	Error error
}

ScoredRun is the result of running a single case.

type Scorer

type Scorer interface {
	// Name returns the scorer's identifier.
	Name() string
	// Score evaluates one case. Returns Score and any error.
	// A scorer error causes the case to receive score 0 with error details.
	Score(ctx context.Context, c Case, run ScoredRun) (Score, error)
}

Scorer evaluates a single case's output.

var ExactMatch Scorer = ExactMatchScorer{}

ExactMatch is a pre-built ExactMatchScorer ready for use.

var Structural Scorer = StructuralScorer{}

Structural is a pre-built StructuralScorer ready for use.

func NewLLMJudge

func NewLLMJudge(name, rubric string, judgeFunc JudgeRunFunc) Scorer

NewLLMJudge creates an LLMJudgeScorer. name is the scorer name. rubric is the system prompt for the judge. judgeFunc is the implementation that runs the judge model (allows dogfooding).

func NewRubricFunc

func NewRubricFunc(name string, fn RubricFunc) Scorer

NewRubricFunc creates a named Scorer from a function.

type StructuralScorer

type StructuralScorer struct{}

StructuralScorer scores 1.0 if all expected fields are present in the actual output (subset match — actual may have extra fields). Uses JSON structural comparison.

func (StructuralScorer) Name

func (StructuralScorer) Name() string

func (StructuralScorer) Score

type Suite

type Suite struct {
	// Name is the suite's identifier.
	Name string
	// Dataset is the set of cases to evaluate.
	Dataset Dataset
	// Run is the function that runs each case.
	Run RunFunc
	// Scorers is the set of scorers to apply to each case's output.
	Scorers []Scorer
	// Parallelism controls how many cases run concurrently. 0 or 1 = sequential.
	Parallelism int
}

Suite is a named collection of evaluation cases and scorers.

func (Suite) Execute

func (s Suite) Execute(ctx context.Context) (Report, error)

Execute runs all cases in the suite, scores them, and returns a Report.

Jump to

Keyboard shortcuts

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