eval

package module
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

README

eval

eval is a subject-agnostic quality-evaluation kernel. Evaluator is generic over what it judges, so the same runtime evaluates a generated answer, a retrieval ranking, an agent trajectory, or anything else a caller can type.

It owns datasets, evaluators, reports, suites, experiments, comparison, and aggregation. It does not own dataset persistence, artifact storage, an experiment-tracking service, or a dashboard — those belong to a Host.

Install

go get github.com/Tangerg/scope/eval

Packages

Package Owns
eval The kernel: Evaluator, Metric, Report, Dataset, Experiment, Comparison
judge Model-backed judgment adapted into typed reports
text Generated-text quality metrics
ranking Provider-neutral ranking metrics

A new domain implements Evaluator directly. It does not depend on text-generation or ranking concepts, and it does not add primitives to the root package.

Running an experiment

dataset, err := eval.NewDataset(cases...)
if err != nil {
    return err
}

experiment, err := eval.NewExperiment(eval.ExperimentConfig[Answer]{
    Dataset:   dataset,
    Evaluator: evaluator,
})
if err != nil {
    return err
}

report, err := experiment.Run(ctx)

Concurrency is bounded — DefaultMaxConcurrency unless configured — and the limit is resolved once at construction, never left to the caller to remember.

Reports say only what was measured

A Report carries an optional verdict, a normalized score, a raw measurement, feedback, and child reports, each independently. An evaluator that only produces a measurement or qualitative feedback does not get a pass threshold or a verdict invented for it.

Metric identity includes name, unit, direction, and parameters. Reports with different identities are never aggregated together, so two metrics that happen to share a name but not a unit cannot collapse into one number.

Report.Details is a bounded tree: MaxReportDepth is enforced at every construction, clone, JSON, and summary boundary.

Composing evaluators

  • SuiteEvaluator runs several evaluators and preserves heterogeneous results side by side.
  • CompositeEvaluator aggregates comparable scored verdicts explicitly — weights, required components, and a PassAll / PassAny / PassAtLeast rule, all part of the metric identity.
  • ProjectionEvaluator adapts an aggregate subject down to a narrow evaluator's input.

Failure policy

ErrorCollect records a case failure and continues. ErrorFailFast cancels unscheduled work but never erases case facts that already settled.

Comparison

ExperimentReport.Compare reports exact aggregate deltas. It does not claim statistical significance — that requires a model and enough inputs, and the kernel has neither by default.

See ARCHITECTURE.md for the invariants behind these rules.

Documentation

Overview

Package eval defines a subject-agnostic quality-evaluation kernel. Evaluator is generic over the subject, Metric carries structured identity and measurement semantics, and Report independently represents an optional verdict, normalized quality score, raw numeric measurement, feedback, and child reports. SuiteEvaluator preserves heterogeneous results while CompositeEvaluator explicitly aggregates comparable scored verdicts. Dataset owns case identity, Experiment executes it with bounded concurrency, and ExperimentReport.Compare reports exact aggregate deltas without inventing statistical claims. ProjectionEvaluator adapts aggregate subjects to narrow evaluator inputs.

Domain vocabularies live outside the kernel: judge supplies generic model-backed evaluation, text owns generated-text metrics, and ranking owns provider-neutral ranking metrics. New domains implement Evaluator directly and do not depend on text-generation or ranking concepts.

Index

Examples

Constants

View Source
const DefaultMaxConcurrency = 4
View Source
const MaxReportDepth = 64

MaxReportDepth bounds recursive detail trees at every public trust boundary.

Variables

View Source
var (
	ErrInvalidEvaluatorConfig = errors.New("eval: evaluator configuration is invalid")
	ErrInvalidMetric          = errors.New("eval: invalid metric")
	ErrInvalidScore           = errors.New("eval: invalid score")
	ErrInvalidReport          = errors.New("eval: invalid report")
	ErrInvalidCase            = errors.New("eval: invalid case")
	ErrInvalidDataset         = errors.New("eval: invalid dataset")
	ErrInvalidExperiment      = errors.New("eval: invalid experiment")
	ErrInvalidComparison      = errors.New("eval: invalid comparison")
	ErrCaseNotEvaluated       = errors.New("eval: case was not evaluated")
)

Functions

This section is empty.

Types

type Case

type Case[T any] struct {
	ID       CaseID
	Subject  T
	Metadata metadata.Map
}

Case gives a stable identity to one evaluation subject.

func (Case[T]) Validate

func (c Case[T]) Validate() error

type CaseID

type CaseID string

CaseID is a stable identity within one Dataset.

func (CaseID) String

func (c CaseID) String() string

func (CaseID) Validate

func (c CaseID) Validate() error

type CaseResult

type CaseResult struct {
	ID       CaseID
	Metadata metadata.Map
	Report   Report
	Err      error
}

type Comparison

type Comparison struct {
	Baseline       ExperimentSummary
	Candidate      ExperimentSummary
	EvaluatedDelta int
	PassedDelta    int
	FailedDelta    int
	UnjudgedDelta  int
	ErrorDelta     int
	Metrics        []MetricComparison
}

type Component

type Component[T any] struct {
	Evaluator Evaluator[T]
	Weight    float64
	Required  bool
}

Component assigns score weight and pass criticality to one evaluator. A zero Weight selects 1. Required components must pass independently of the aggregate pass policy.

type CompositeConfig

type CompositeConfig[T any] struct {
	Components     []Component[T]
	PassPolicy     PassPolicy
	MinimumPassed  int
	MaxConcurrency int
}

CompositeConfig defines score aggregation, pass semantics, and bounded concurrency. A zero MaxConcurrency selects DefaultMaxConcurrency.

type CompositeEvaluator

type CompositeEvaluator[T any] struct {
	// contains filtered or unexported fields
}

func NewCompositeEvaluator

func NewCompositeEvaluator[T any](config CompositeConfig[T]) (*CompositeEvaluator[T], error)

func (*CompositeEvaluator[T]) Evaluate

func (c *CompositeEvaluator[T]) Evaluate(ctx context.Context, subject T) (Report, error)

type Dataset

type Dataset[T any] struct {
	// contains filtered or unexported fields
}

Dataset is an immutable ordered set of uniquely identified cases. Evaluators must not mutate subjects; metadata is owned and cloned by the Dataset.

func NewDataset

func NewDataset[T any](cases ...Case[T]) (Dataset[T], error)

func (Dataset[T]) Cases

func (d Dataset[T]) Cases() []Case[T]

Cases returns an owned copy in deterministic declaration order.

func (Dataset[T]) Len

func (d Dataset[T]) Len() int

type Direction

type Direction string

Direction describes how a raw measurement relates to quality. It is kept separate from Score, whose direction is always higher-is-better.

const (
	DirectionUnspecified    Direction = ""
	DirectionHigherIsBetter Direction = "higher_is_better"
	DirectionLowerIsBetter  Direction = "lower_is_better"
)

func (Direction) Validate

func (d Direction) Validate() error

type Distribution

type Distribution struct {
	Count   int
	Mean    float64
	Minimum float64
	P10     float64
	P50     float64
	P90     float64
	Maximum float64
}

Distribution summarizes one homogeneous numeric signal. Count distinguishes an absent distribution from a real distribution whose values are all zero.

type DistributionDelta

type DistributionDelta struct {
	Present bool
	Mean    float64
}

DistributionDelta is candidate mean minus baseline mean. Present is false when either side has no values, so absence cannot be mistaken for zero.

type ErrorPolicy

type ErrorPolicy string
const (
	ErrorCollect  ErrorPolicy = "collect"
	ErrorFailFast ErrorPolicy = "fail_fast"
)

type Evaluator

type Evaluator[T any] interface {
	// Evaluate inspects one subject without mutating it and returns a valid,
	// owned report for the evaluator's metric. Implementations must honor ctx;
	// a non-nil error means the report must not be consumed.
	Evaluate(ctx context.Context, subject T) (Report, error)
}

Evaluator evaluates one subject and returns a valid report.

type EvaluatorFunc

type EvaluatorFunc[T any] func(context.Context, T) (Report, error)

func (EvaluatorFunc[T]) Evaluate

func (e EvaluatorFunc[T]) Evaluate(ctx context.Context, subject T) (Report, error)

type Experiment

type Experiment[T any] struct {
	// contains filtered or unexported fields
}

Experiment is an immutable plan for evaluating one Dataset. It owns bounded scheduling and error semantics, but no persistence, artifacts, or product identity.

func NewExperiment

func NewExperiment[T any](config ExperimentConfig[T]) (Experiment[T], error)

func (Experiment[T]) Run

func (e Experiment[T]) Run(ctx context.Context) (ExperimentReport, error)
Example
package main

import (
	"context"
	"fmt"

	"github.com/Tangerg/scope/eval"
)

func main() {
	metric, err := eval.NewMetric(eval.MetricConfig{
		Namespace: "example",
		Name:      "non_empty",
	})
	if err != nil {
		panic(err)
	}
	evaluator := eval.EvaluatorFunc[string](func(_ context.Context, subject string) (eval.Report, error) {
		return eval.Report{Metric: metric, Verdict: eval.VerdictPass}, nil
	})
	dataset, err := eval.NewDataset(
		eval.Case[string]{ID: "first", Subject: "answer"},
	)
	if err != nil {
		panic(err)
	}
	experiment, err := eval.NewExperiment(eval.ExperimentConfig[string]{
		Dataset: dataset, Evaluator: evaluator,
	})
	if err != nil {
		panic(err)
	}
	report, err := experiment.Run(context.Background())
	if err != nil {
		panic(err)
	}
	summary := report.Summary()

	fmt.Println(summary.Total, summary.Passed)
}
Output:
1 1

type ExperimentConfig

type ExperimentConfig[T any] struct {
	Dataset        Dataset[T]
	Evaluator      Evaluator[T]
	MaxConcurrency int
	ErrorPolicy    ErrorPolicy
}

type ExperimentReport

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

func (ExperimentReport) Cases

func (e ExperimentReport) Cases() []CaseResult

Cases returns owned results in Dataset order.

func (ExperimentReport) Compare

func (e ExperimentReport) Compare(candidate ExperimentReport) (Comparison, error)

Compare keeps the baseline authoritative: only reports over the same ordered Dataset and Metric identities are comparable. Exact deltas avoid inventing statistical significance or a synthetic score across unlike units.

func (ExperimentReport) Summary

func (e ExperimentReport) Summary() ExperimentSummary

Summary returns the owned aggregate calculated from Cases.

type ExperimentSummary

type ExperimentSummary struct {
	Total     int
	Evaluated int
	Passed    int
	Failed    int
	Unjudged  int
	Errors    int
	Metrics   []MetricSummary
}

type Metric

type Metric struct {
	Namespace  string       `json:"namespace,omitzero"`
	Name       MetricName   `json:"name"`
	Unit       string       `json:"unit,omitzero"`
	Direction  Direction    `json:"direction,omitzero"`
	Parameters metadata.Map `json:"parameters,omitzero"`
}

Metric identifies an evaluation without encoding configuration into a string. Parameters holds owned, structured identity for calculation and decision rules. Unit and Direction describe optional raw measurements; normalized scores are always unitless and higher-is-better.

func NewMetric

func NewMetric(config MetricConfig) (Metric, error)

func (Metric) Clone

func (m Metric) Clone() Metric

func (Metric) String

func (m Metric) String() string

func (Metric) Validate

func (m Metric) Validate() error

type MetricComparison

type MetricComparison struct {
	Metric           Metric
	Baseline         MetricSummary
	Candidate        MetricSummary
	EvaluatedDelta   int
	PassedDelta      int
	FailedDelta      int
	UnjudgedDelta    int
	ScoreDelta       DistributionDelta
	MeasurementDelta DistributionDelta
}

type MetricConfig

type MetricConfig struct {
	Namespace  string
	Name       MetricName
	Unit       string
	Direction  Direction
	Parameters metadata.Map
}

type MetricName

type MetricName string

MetricName identifies one quality calculation within a namespace.

const MetricNameComposite MetricName = "composite"
const MetricNameSuite MetricName = "suite"

type MetricSummary

type MetricSummary struct {
	Metric       Metric
	Evaluated    int
	Passed       int
	Failed       int
	Unjudged     int
	Scores       Distribution
	Measurements Distribution
}

MetricSummary keeps score and measurement distributions attached to their full Metric identity so unrelated units, directions, and configurations are never aggregated together. Experiment summarizes both top-level reports and their Details.

type PassPolicy

type PassPolicy string
const (
	PassAll     PassPolicy = "all"
	PassAny     PassPolicy = "any"
	PassAtLeast PassPolicy = "at_least"
)

type Projection

type Projection[T, Subject any] func(T) (Subject, error)

type ProjectionEvaluator

type ProjectionEvaluator[T, Subject any] struct {
	// contains filtered or unexported fields
}

ProjectionEvaluator adapts one aggregate case to the narrower subject a domain evaluator consumes.

func NewProjectionEvaluator

func NewProjectionEvaluator[T, Subject any](
	evaluator Evaluator[Subject],
	projection Projection[T, Subject],
) (*ProjectionEvaluator[T, Subject], error)

func (*ProjectionEvaluator[T, Subject]) Evaluate

func (p *ProjectionEvaluator[T, Subject]) Evaluate(ctx context.Context, value T) (Report, error)

type Report

type Report struct {
	Metric      Metric       `json:"metric"`
	Verdict     Verdict      `json:"verdict,omitzero"`
	Score       *Score       `json:"score,omitzero"`
	Measurement *float64     `json:"measurement,omitzero"`
	Feedback    string       `json:"feedback,omitzero"`
	Metadata    metadata.Map `json:"metadata,omitzero"`
	Details     []Report     `json:"details,omitzero"`
}

Report is one evaluation result. Verdict, normalized Score, and raw Measurement are independent and optional so measurement-only and qualitative evaluations do not need to invent a pass threshold or quality score. Details contains owned child reports instead of convention-based metadata keys.

func (Report) Clone

func (r Report) Clone() (Report, error)

Clone validates the complete detail tree before allocating its detached copy.

func (Report) MarshalJSON

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

func (*Report) UnmarshalJSON

func (r *Report) UnmarshalJSON(data []byte) error

func (Report) Validate

func (r Report) Validate() error

type Score

type Score float64

Score is a normalized quality score in the closed interval [0, 1], where a higher value is always better.

func NewScore

func NewScore(value float64) (Score, error)

func (Score) Float64

func (s Score) Float64() float64

func (Score) Validate

func (s Score) Validate() error

func (Score) Verdict

func (s Score) Verdict(threshold Score) (Verdict, error)

Verdict returns the categorical judgment for a valid threshold.

type SuiteConfig

type SuiteConfig[T any] struct {
	Evaluators     []Evaluator[T]
	MaxConcurrency int
}

SuiteConfig groups heterogeneous evaluators without collapsing their results into one score. A zero MaxConcurrency selects DefaultMaxConcurrency.

type SuiteEvaluator

type SuiteEvaluator[T any] struct {
	// contains filtered or unexported fields
}

SuiteEvaluator preserves every child report and records the ordered child metrics in its own identity. Its verdict fails when any decided child fails, passes when at least one child passes and none fail, and remains unspecified when every child is measurement-only or qualitative.

func NewSuiteEvaluator

func NewSuiteEvaluator[T any](config SuiteConfig[T]) (*SuiteEvaluator[T], error)

func (*SuiteEvaluator[T]) Evaluate

func (s *SuiteEvaluator[T]) Evaluate(ctx context.Context, subject T) (Report, error)

type Verdict

type Verdict string

Verdict is an optional categorical judgment. An unspecified verdict is a valid outcome for measurement-only or qualitative evaluations.

const (
	VerdictUnspecified Verdict = ""
	VerdictPass        Verdict = "pass"
	VerdictFail        Verdict = "fail"
)

func (Verdict) Decided

func (v Verdict) Decided() bool

func (Verdict) Validate

func (v Verdict) Validate() error

Directories

Path Synopsis
Package judge evaluates arbitrary subjects with a structured-output chat model.
Package judge evaluates arbitrary subjects with a structured-output chat model.
Package ranking evaluates ranked outputs against graded relevance judgments.
Package ranking evaluates ranked outputs against graded relevance judgments.
Package text evaluates generated text without imposing one shared sample on metrics with different semantic inputs.
Package text evaluates generated text without imposing one shared sample on metrics with different semantic inputs.
trajectory module

Jump to

Keyboard shortcuts

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