eval

package module
v0.2.0 Latest Latest
Warning

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

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

README

eval

github.com/looprig/eval is an application-neutral evaluation framework for agentic systems. It runs as ordinary Go code under go test, reusing the standard testing package for execution, comparison, failure reporting, parallelism, and CI, and adds the domain vocabulary testing does not provide: conversations, expectations, operational evidence, evaluators, rubrics, findings, measurements, reports, and sinks.

An agent interaction is a content.AgenticMessages thread from github.com/looprig/core — text, multimodal blocks, tool requests, tool results, errors, and token usage — so input, output, steps, and tool calls are never duplicated into eval-specific string fields.

The root package depends only on core. judge/ and target/inference/ add the github.com/looprig/inference dependency; nothing else does (no Harness, sandbox, OpenTelemetry, or provider SDK reaches a consumer's build graph through eval).

What eval does not do (non-goals)

These are contracts, not omissions:

  • Evals only observe, score, and report. An evaluator reports evidence and assessments; it never authorizes, blocks, rewrites, retries, or otherwise acts on a live session. Deciding what to do with a finding is a separate component's job.
  • Missing evidence is unverified, never a pass. Absent context, an unavailable judge, an unsupported sandbox guarantee, or a model that cannot satisfy the declared schema yields unverified or error — never an inferred passing score.
  • Eval output never touches the Harness journal. The optional read-only Harness adapter snapshots the public conversation at a turn/session boundary and queues evaluation outside the active loop; it does not write the journal or change the active conversation.
  • Eval does not own alert thresholds or delivery, and does not select an OpenTelemetry SDK, exporter, or backend. Reports are data; a downstream system decides whether a measurement warrants an alert.

Deterministic qualification

Write a suite of scenarios, run it through a target with one or more exact.* programmatic evaluators, and gate the report — all inside a normal Go test. exact checks observable facts: required/forbidden output text, required or forbidden tool calls, structured-output conformance, tool-error rate, and latency.

func TestSupportAgentQualification(t *testing.T) {
	t.Parallel()

	suite := eval.Suite{
		Name:     "support-agent",
		Revision: "2026-07-18",
		Scenarios: []eval.Scenario{{
			ID:       "lookup-001",
			Name:     "looks up the account before answering",
			Revision: "1",
			Input:    content.AgenticMessages{ /* user turn(s) */ },
		}},
	}

	report := evaltest.Run(t, suite, target,
		exact.RequiredTool("lookup_account"),
		exact.ForbiddenText("as an AI language model"),
	)

	evaltest.RequirePass(t, report)
}

evaltest.Run executes the suite through eval.Run, presents every scenario and evaluator as a Go subtest, and returns the complete eval.Report for custom assertions. Presentation is informational — gate the report explicitly with evaltest.RequirePass (fails on any non-pass) or evaltest.RequireVerified (fails on error/unverified, tolerates a recorded fail).

For a single case, evaltest.RunScenario(t, scenario, target, evaluators...) wraps one eval.Scenario in a one-scenario suite:

func TestInvoiceAgentDoesNotInventRefund(t *testing.T) {
	t.Parallel()

	scenario := eval.Scenario{
		ID:       "refund-policy-017",
		Name:     "refuses a refund on a non-refundable invoice",
		Revision: "1",
		Input:    content.AgenticMessages{ /* "Refund this non-refundable invoice" */ },
	}

	report := evaltest.RunScenario(t, scenario, agentTarget,
		exact.NoToolCall("issue_refund"),
	)
	evaltest.RequirePass(t, report)
}

The engine runs stages independently: a target error is not reported as a failed quality score, and one evaluator error does not discard a sibling's assessment. eval.RunConfig{} is the deterministic default (one trial, sequential); set Trials, Concurrency, and per-stage timeouts to run a matrix or repeated trials.

The structured judge

For genuinely ambiguous quality — relevance, groundedness, instruction adherence, goal adherence, toxicity — use judge.New. It builds an inference.Request with strict structured output, calls an injected inference.Client, and re-validates the decoded score locally. A model that cannot satisfy the schema produces a typed error/unverified, never a guessed verdict.

// template carries the (structured-output-capable) judge Model and any System
// or sampling defaults; the judge fills Messages (the untrusted conversation)
// and Output (the strict score schema) on each call.
template := inference.Request{Model: model.Model{ /* judge model */ }}

report := evaltest.Run(t, suite, target,
	exact.RequiredTool("lookup_account"),                         // deterministic evidence
	judge.New(rubric.AnswerRelevanceV1, judgeClient, template),   // model judgment
)
evaltest.RequirePass(t, report)

Rubrics (rubric.*) define what good means and are versioned (AnswerRelevanceV1, GroundednessV1, InstructionAdherenceV1, GoalAdherenceV1, ToxicityV1, VulgarityV1, InternetUseAppropriatenessV1; rubric.Catalog() lists them). The judge schema defines the shape of the answer and is separate. Combining an exact.* check with a judge.* score in one suite is the composite pattern: a judge score can never conceal a deterministic failure, because both assessments are retained in the report.

Targets

A target turns a Scenario into an Observation. target/inference's NewTarget(client, template, opts...) drives a scenario's input through an inference.Client and projects the reply into an observation, deriving safe subject/model provenance from the template model. Continuous evaluation already has an observation and constructs a Sample without running a target.

Go test build tags and the test cache

Expensive or networked cases use ordinary Go build tags so they are excluded from the default go test ./...:

  • Tag live/model-backed cases //go:build integration (or qualification) and run them explicitly: go test -tags integration -race ./.... Integration tests live in *_integration_test.go.
  • Unit tests never touch the network. Datasets can be embedded with //go:embed.

Because a live judge or target is nondeterministic, do not let Go's test cache serve a stale pass. Run live/nondeterministic tests with -count=1:

go test -tags integration -race -count=1 ./...

Deterministic golden tests remain cacheable and need no -count=1. Fuzz targets exercise the parsers and codecs (go test -fuzz=FuzzXxx -fuzztime=30s ./path), never a paid judge.

Reports, sinks, and baselines

eval.Run returns a typed eval.Report — per-sample results, assessments, measurements, findings, and provenance. A Sink is the destination contract:

type Sink interface {
	WriteReport(context.Context, Report) error
}

reportjson implements the redacted report/v1 wire form:

  • reportjson.Encode(report) ([]byte, error) / reportjson.Decode(data) (eval.Report, error) — the versioned codec; both directions enforce Report.Validate, and raw conversation text, judge explanations, and secrets are redacted on the wire.
  • reportjson.NewFileSink(dir) — an eval.Sink that writes each report atomically to <dir>/<id>.json, directory-scoped via os.Root so a report ID cannot escape the root.

Compare a candidate report against a stored baseline with compare:

baseline, _ := reportjson.Decode(baselineBytes)
cmp, err := compare.Compare(baseline, candidate)

compare.Compare classifies each case — added, removed, changed, unchanged, errored, unverified, failed, incompatible — and reports per-measurement deltas, rather than comparing averages alone. Comparison is only valid when both reports declare compatible case and evaluator identities.

Building and verifying

Every command runs with GOWORK=off so the module resolves through its own require/replace graph (a parent go.work must not capture it):

GOWORK=off go test -race ./...                 # unit tests, always -race
CGO_ENABLED=0 GOWORK=off go build -trimpath ./...
GOWORK=off make secure                         # fmt-check + vet + staticcheck + gosec + mod verify + govulncheck

The security linters (staticcheck, gosec, govulncheck) are wired as Go tool dependencies in go.mod and are dev/tool-only — they are not linked into the library. The library's only runtime dependencies are github.com/looprig/core (root) and github.com/looprig/inference (judge/ and target/inference/ only).

Documentation

Overview

Package eval is an application-neutral evaluation framework for agentic systems. It runs as ordinary Go code under `go test`, reusing the testing package for execution, comparison, failure reporting, parallelism, and CI, while adding the domain vocabulary the standard library does not provide: conversations, expectations, operational evidence, evaluators, rubrics, findings, measurements, reports, and sinks.

The framework supports two lifecycles over the same observation and assessment contracts. Qualification evaluation runs before deployment against fixtures, golden sets, generated cases, models, agents, HTTP endpoints, or local processes. Continuous evaluation observes completed turns or sessions in production, asynchronously and without altering the active conversation.

Evals only observe, score, report, and propose golden-set candidates. They never authorize, block, rewrite, retry, or otherwise act on a session. Missing evidence and unavailable enforcement are surfaced explicitly as unverified, never as a passing score.

An agent interaction is represented as content.AgenticMessages from github.com/looprig/core, preserving text, multimodal blocks, tool requests, tool results, errors, and usage. The root package depends only on core; judge and inference-backed target packages add the inference dependency.

Index

Constants

View Source
const (
	// MaxFindingMessageBytes bounds a Finding.Message in UTF-8 bytes. The message
	// is evaluator-authored free text: it is bounded here and must be treated as
	// untrusted downstream — in particular it must never be placed in a metric
	// label.
	MaxFindingMessageBytes = 2048
	// MaxAssessmentMeasurements bounds how many measurements one assessment may
	// carry.
	MaxAssessmentMeasurements = 256
	// MaxAssessmentFindings bounds how many findings one assessment may carry.
	MaxAssessmentFindings = 256
	// MaxAssessmentEvidence bounds how many evidence entries one assessment may
	// carry.
	MaxAssessmentEvidence = 1024
)

Byte bound for a Finding.Message and count bounds for an assessment's collections. The message bound keeps an evaluator-authored explanation from ballooning a report; the collection bounds reject absurd inputs.

View Source
const (
	// MaxDescriptionBytes bounds a Descriptor.Description in UTF-8 bytes.
	MaxDescriptionBytes = 1024
	// MaxDescriptorRequires bounds how many required EvidenceKinds one Descriptor
	// may declare.
	MaxDescriptorRequires = 64
)

Byte bound for a Descriptor.Description and count bound for its required evidence kinds. Both reject absurd or hostile inputs before they reach the engine or reports.

View Source
const (
	// MaxExcerptBytes bounds a RedactedExcerpt in UTF-8 bytes.
	MaxExcerptBytes = 512
	// MaxHashBytes bounds a ContentHash in bytes.
	MaxHashBytes = 128
	// MaxIDBytes bounds a free-form correlation identifier in bytes.
	MaxIDBytes = 256
)

Byte bounds for the sensitive-content fields. They cap redacted excerpts and content hashes so a misused field cannot balloon a report or sink. They are byte counts, not rune counts.

View Source
const (
	// MaxFactBytes bounds a single required Fact in UTF-8 bytes.
	MaxFactBytes = 1024
	// MaxActionNameBytes bounds a single forbidden ActionName in UTF-8 bytes.
	MaxActionNameBytes = 256
	// MaxReferenceAnswerBytes bounds a single ReferenceAnswer in UTF-8 bytes.
	MaxReferenceAnswerBytes = 4096

	// MaxRequiredFacts bounds how many required facts one Expectation may carry.
	MaxRequiredFacts = 64
	// MaxForbiddenActions bounds how many forbidden actions one Expectation may
	// carry.
	MaxForbiddenActions = 64
	// MaxExpectedToolCalls bounds how many tool-call expectations one Expectation
	// may carry.
	MaxExpectedToolCalls = 64
	// MaxReferenceAnswers bounds how many reference answers one Expectation may
	// carry.
	MaxReferenceAnswers = 16
)

Byte bounds for the free-form (author-supplied) expectation text fields, and count bounds for the collections. They reject absurd or hostile fixtures before they reach evaluators, reports, or sinks. All are byte or element counts, not rune counts.

View Source
const (
	// MaxAttributeValueBytes bounds a single Attribute value in bytes.
	MaxAttributeValueBytes = 512
	// MaxOperationAttributes bounds how many attributes one Operation may carry.
	// Attributes are a small set of safe key/value facts, not a transcript.
	MaxOperationAttributes = 32
)

Byte bound for free-form correlation identifiers carried on the trace and operations (trace/session/turn/operation IDs). Subject and operation IDs reuse MaxIDBytes from evidence.go.

View Source
const (
	// MaxScenarioLabels bounds how many labels one Scenario may carry.
	MaxScenarioLabels = 64
	// MaxLabelValueBytes bounds a single Label value in UTF-8 bytes.
	MaxLabelValueBytes = 256
	// MaxScenarioInputMessages bounds the length of a Scenario.Input thread. It
	// rejects an absurd fixture; per-message content is core/content's concern.
	MaxScenarioInputMessages = 4096
)

Count bound for a scenario's labels, and byte bound for a label value. Labels are a small set of safe key/value tags, not a payload.

View Source
const (
	// MaxNameBytes bounds a Name in UTF-8 bytes.
	MaxNameBytes = 256
	// MaxRevisionBytes bounds a Revision in UTF-8 bytes.
	MaxRevisionBytes = 256
)

Byte bounds for the identity string types. Identifiers are short by design; these caps reject absurd or hostile inputs before they reach evaluators, reports, or sinks. They are byte counts, not rune counts.

View Source
const MaxReportIDBytes = 1024

MaxReportIDBytes bounds a Report.ID in UTF-8 bytes. The runner derives its ID from the suite identity (Name "@" Revision, each bounded by MaxNameBytes / MaxRevisionBytes), and a caller may overwrite it with a globally unique run ID; this bound is generous enough for both while still rejecting an absurd or hostile value at the untrusted decode boundary.

View Source
const MaxTrials = 1000

MaxTrials bounds RunConfig.Trials. Repeated trials expose per-case flakiness and variance for nondeterministic targets, but a request for an unreasonable number of trials is a configuration mistake, not a workload, and is rejected at preflight. The bound is a guard against accidental fan-out, not a tuning parameter.

Variables

This section is empty.

Functions

This section is empty.

Types

type ActionName

type ActionName string

ActionName names an action a correct interaction must not take, for example "issue_refund". A valid ActionName is non-empty, valid UTF-8, and within MaxActionNameBytes. It is distinct from Name so a forbidden-action list can never be confused with an identifier list.

func (ActionName) Validate

func (a ActionName) Validate() error

Validate reports whether a is a well-formed ActionName.

type Assessment

type Assessment struct {
	Evaluator    Name
	Revision     Revision
	Status       AssessmentStatus
	Measurements []Measurement
	Findings     []Finding
	Evidence     []Evidence
	Duration     time.Duration
}

Assessment is an evaluator's verdict on a sample. Evaluator and Revision echo the descriptor's identity (the evaluator, not the target). Status is the terminal disposition. Measurements and Findings carry the quantitative and qualitative results; Evidence carries the facts they reference; Duration is how long the evaluation took.

func Errored

func Errored(desc Descriptor, findings ...Finding) Assessment

Errored returns an error-status assessment for desc: the evaluator itself failed to reach a verdict (infrastructure, cancellation, malformed judge output). Findings may describe the failure. It carries no measurements — an infrastructure failure is not a quality score. Prefer returning a non-nil error from Evaluate; use Errored when the failure should flow through the report as data rather than abort the run.

func Fail

func Fail(desc Descriptor, findings ...Finding) Assessment

Fail returns a failing quality verdict for desc, explained by the given findings. A fail means the subject fell short — it is not an evaluator error.

func Pass

func Pass(desc Descriptor, measurements ...Measurement) Assessment

Pass returns a passing assessment for desc carrying the given measurements. A pass asserts the subject met the expectation; callers must not attach a high- or critical-severity finding (Validate rejects it).

func Skipped

func Skipped(desc Descriptor) Assessment

Skipped returns a skipped assessment for desc: the evaluator was intentionally not run. It carries no measurements.

func Unverified

func Unverified(desc Descriptor, findings ...Finding) Assessment

Unverified returns an unverified assessment for desc, explained by the given findings. Unverified means no authoritative evidence was available; it is never an inferred pass (design principle #4). It carries no measurements, so an unknown result can never present a numeric score.

func (Assessment) Validate

func (a Assessment) Validate() error

Validate reports whether a is well-formed and internally consistent. See the file comment for the measurement, finding, evidence, dangling-reference, and status-consistency rules it enforces.

type AssessmentStatus

type AssessmentStatus string

AssessmentStatus is the terminal disposition of an assessment. It is a string type so it renders directly in wire envelopes and reports. There is no valid zero value: an unset status must not validate, so missing evidence can never be reported as a pass.

const (
	// StatusPass indicates the expectation was met.
	StatusPass AssessmentStatus = "pass"
	// StatusFail indicates the expectation was not met.
	StatusFail AssessmentStatus = "fail"
	// StatusUnverified indicates no authoritative evidence was available. It is
	// never an inferred pass.
	StatusUnverified AssessmentStatus = "unverified"
	// StatusError indicates the evaluator itself failed to produce a verdict.
	StatusError AssessmentStatus = "error"
	// StatusSkipped indicates the evaluator was intentionally not run.
	StatusSkipped AssessmentStatus = "skipped"
)

func (AssessmentStatus) Validate

func (s AssessmentStatus) Validate() error

Validate reports whether s is a known AssessmentStatus. The zero value is not a member, so an unset status is rejected and can never read as a pass.

type Attribute

type Attribute struct {
	Key   Name
	Value string
}

Attribute is a single safe key/value fact about an operation. Values are bounded and never echoed in diagnostics; callers must not place untrusted content, secrets, or PII here.

func (Attribute) Validate

func (a Attribute) Validate() error

Validate reports whether a is a well-formed attribute.

type ContentHash

type ContentHash string

ContentHash is a safe, correlatable digest (for example "sha256:...") of sensitive content. It is safe to render in reports and sinks. The empty value is valid: hashing is optional.

func (ContentHash) Validate

func (h ContentHash) Validate() error

Validate reports whether h is within bounds and valid UTF-8.

type ConversationExcerpt

type ConversationExcerpt struct {
	MessageIndex int
	Role         content.Role
	Hash         ContentHash
	Redacted     RedactedExcerpt
}

ConversationExcerpt references a message by index and carries a redacted, bounded view of its content plus an optional correlatable hash.

type Descriptor

type Descriptor struct {
	Name        Name
	Revision    Revision
	Method      Method
	Description string
	Requires    []EvidenceKind
}

Descriptor is an evaluator's versioned, self-describing metadata. Name and Revision identify the evaluator (not the target under evaluation); Method is descriptive metadata for filtering, cost accounting, and reporting; Description is a short, optional prose summary; Requires lists the evidence kinds the evaluator needs a sample to carry before it can produce a verdict. When a required kind is absent, the evaluator yields unverified — never pass.

func (Descriptor) CheckRequires

func (d Descriptor) CheckRequires(s Sample) (Assessment, bool)

CheckRequires verifies that s carries every EvidenceKind in d.Requires. When all required kinds are present it returns the zero Assessment and ok=true, so the caller proceeds with evaluation. When one or more required kinds are absent it returns ok=false and an unverified Assessment (built with Unverified) naming the missing kinds in a finding — never a pass. This is the enforcement point for design principle #4: missing required evidence is unknown, and unknown is not pass.

Availability is judged against the sample's observation trace evidence only; the required EvidenceKind constants are safe to render, so the finding message lists them.

func (Descriptor) Validate

func (d Descriptor) Validate() error

Validate reports whether d is a well-formed descriptor: a valid Name and Revision, a known Method, a bounded valid-UTF-8 Description, and a bounded set of valid, non-duplicated required EvidenceKinds. An empty Description is valid.

type DiagnosticEvidence

type DiagnosticEvidence struct {
	Code     Name
	Severity Severity
	Message  RedactedExcerpt
}

DiagnosticEvidence records an evaluator's own diagnostic: a safe code, a severity, and a bounded redacted message. Because a diagnostic may quote a judge or an error, its Message is a RedactedExcerpt — bounded and never raw.

type DuplicateEvaluatorNameError

type DuplicateEvaluatorNameError struct{}

DuplicateEvaluatorNameError reports that Run was given two evaluators sharing the same Descriptor.Name. Within a single run an evaluator name must identify exactly one evaluator (one revision): the report keys a sample's assessments and cross-report comparison keys its cases by evaluator name, so two evaluators under one name would collide — an identical pair corrupts the sample's assessment set, and a same-name/different-revision pair silently loses one revision's identity. It is rejected at preflight before any execution. The offending name is caller-supplied and withheld from the message.

func (*DuplicateEvaluatorNameError) Error

type DuplicateEvidenceError

type DuplicateEvidenceError struct{}

DuplicateEvidenceError reports that an EvidenceID appeared more than once in a trace, which would corrupt evidence reference resolution and comparison. The offending identifier is deliberately withheld from the message: EvidenceIDs are caller-supplied and a hostile value must not leak through a diagnostic.

func (*DuplicateEvidenceError) Error

func (e *DuplicateEvidenceError) Error() string

type DuplicateEvidenceKindError

type DuplicateEvidenceKindError struct{}

DuplicateEvidenceKindError reports that a Descriptor listed the same required EvidenceKind more than once, which would make its requirement set ambiguous. The offending kind is a package constant and therefore safe, but it is withheld to keep the diagnostic vocabulary uniform.

func (*DuplicateEvidenceKindError) Error

type DuplicateFindingError

type DuplicateFindingError struct{}

DuplicateFindingError reports that an Assessment carried two findings with the same Code. Duplicate finding codes are forbidden within a single assessment: a code identifies a distinct check, so a repeat would make the finding set ambiguous. The offending code is caller-supplied and withheld from the message.

func (*DuplicateFindingError) Error

func (e *DuplicateFindingError) Error() string

type DuplicateLabelError

type DuplicateLabelError struct{}

DuplicateLabelError reports that a scenario carried two labels with the same key, which would make the label set ambiguous. The offending key is withheld from the message: label keys are caller-supplied and a hostile value must not leak through a diagnostic.

func (*DuplicateLabelError) Error

func (e *DuplicateLabelError) Error() string

type DuplicateMeasurementError

type DuplicateMeasurementError struct{}

DuplicateMeasurementError reports that an Assessment carried two measurements with the same Name, which would corrupt comparison and aggregation. The offending name is caller-supplied and withheld from the message.

func (*DuplicateMeasurementError) Error

func (e *DuplicateMeasurementError) Error() string

type DuplicateScenarioError

type DuplicateScenarioError struct{}

DuplicateScenarioError reports that a suite carried two scenarios with the same ID, which would make two cases indistinguishable in the report and in baseline comparison. The offending ID is caller-supplied and withheld from the message.

func (*DuplicateScenarioError) Error

func (e *DuplicateScenarioError) Error() string

type ErrorClass

type ErrorClass string

ErrorClass classifies an operation's failure. Unlike the other enums it has a valid zero value: the empty string means "no error classification", which is the correct state for a successful operation. A non-empty value must be a known member.

const (
	// ErrorTimeout: the operation exceeded a deadline.
	ErrorTimeout ErrorClass = "timeout"
	// ErrorCancelled: the operation was cancelled.
	ErrorCancelled ErrorClass = "cancelled"
	// ErrorRateLimited: the operation was rate limited.
	ErrorRateLimited ErrorClass = "rate_limited"
	// ErrorInvalidInput: the operation received invalid input.
	ErrorInvalidInput ErrorClass = "invalid_input"
	// ErrorUnavailable: a dependency was unavailable.
	ErrorUnavailable ErrorClass = "unavailable"
	// ErrorInternal: an internal error occurred.
	ErrorInternal ErrorClass = "internal"
)

func (ErrorClass) Validate

func (c ErrorClass) Validate() error

Validate reports whether c is a known ErrorClass. The empty value is valid and means "no classification".

type Evaluator

type Evaluator interface {
	Descriptor() Descriptor
	Evaluate(context.Context, Sample) (Assessment, error)
}

Evaluator observes a Sample and reports an Assessment. Descriptor returns the evaluator's versioned metadata; Evaluate produces the assessment.

Evaluate's error return is reserved for the evaluator's own failure to reach a verdict (infrastructure, cancellation, malformed judge output). Such a failure must never be encoded as a StatusFail assessment. An implementation may instead return a nil error with an Errored assessment when it wants the failure to flow through the report as data; it must not silently downgrade an infrastructure failure to a quality score.

type EvaluatorRevision

type EvaluatorRevision struct {
	Name     Name
	Revision Revision
}

EvaluatorRevision records one evaluator's identity for provenance.

type Evidence

type Evidence struct {
	ID   EvidenceID
	Kind EvidenceKind

	// Exactly one of the following is non-nil, matching Kind.
	ConversationExcerpt *ConversationExcerpt
	MessageIndex        *MessageIndexRef
	Timing              *TimingEvidence
	Usage               *UsageEvidence
	ToolOperation       *ToolOperationEvidence
	StructuredError     *StructuredOutputError
	StructuredOutput    *StructuredOutput
	Diagnostic          *DiagnosticEvidence
}

Evidence is a tagged union: Kind selects exactly one non-nil payload pointer. Validate enforces that invariant. Representing the union as a struct with one optional pointer per variant keeps every payload strictly typed and the whole value trivially comparable to nil, JSON-encodable, and table-testable.

func (Evidence) Validate

func (e Evidence) Validate() error

Validate reports whether e is a well-formed evidence value: a valid ID, a known Kind, exactly one payload matching that Kind, and a valid payload. It checks that message indexes are non-negative but not their upper bound, which depends on the conversation; Observation.Validate enforces the upper bound.

type EvidenceID

type EvidenceID string

EvidenceID identifies one evidence entry within a trace. IDs must be unique within a trace so references resolve unambiguously. A valid EvidenceID is non-empty, valid UTF-8, and no longer than MaxIDBytes bytes.

func (EvidenceID) Validate

func (id EvidenceID) Validate() error

Validate reports whether id is a well-formed EvidenceID.

type EvidenceKind

type EvidenceKind string

EvidenceKind is the discriminator of the Evidence union. There is no valid zero value: an unset kind does not validate, so evidence with no declared shape can never be silently accepted.

const (
	// EvidenceConversationExcerpt references a message by index and carries a
	// redacted excerpt and/or hash of its content.
	EvidenceConversationExcerpt EvidenceKind = "conversation_excerpt"
	// EvidenceMessageIndex is a bare reference to a message by index.
	EvidenceMessageIndex EvidenceKind = "message_index"
	// EvidenceTiming records how long a step took.
	EvidenceTiming EvidenceKind = "timing"
	// EvidenceUsage records model token usage.
	EvidenceUsage EvidenceKind = "usage"
	// EvidenceToolOperation records a tool invocation as safe metadata: name,
	// hashed/counted arguments, and a result classification.
	EvidenceToolOperation EvidenceKind = "tool_operation"
	// EvidenceStructuredError records a structured-output validation failure as a
	// classification, never as inferred free text.
	EvidenceStructuredError EvidenceKind = "structured_output_error"
	// EvidenceStructuredOutput is the positive signal that the subject produced
	// structured output which validated against its declared schema. It carries
	// only safe schema identity, never the raw model output.
	EvidenceStructuredOutput EvidenceKind = "structured_output"
	// EvidenceDiagnostic records an evaluator's own diagnostic.
	EvidenceDiagnostic EvidenceKind = "evaluator_diagnostic"
)

func (EvidenceKind) Validate

func (k EvidenceKind) Validate() error

Validate reports whether k is a known EvidenceKind. The offending token is withheld from the diagnostic because it may be untrusted.

type EvidencePayloadError

type EvidencePayloadError struct {
	// Reason is one of the payloadReason* constants.
	Reason string
}

EvidencePayloadError reports that an Evidence value violated the tagged-union invariant: it carried no payload, more than one payload, or a payload that did not match its Kind. Reason is drawn only from the fixed vocabulary below, so no untrusted content is ever embedded.

func (*EvidencePayloadError) Error

func (e *EvidencePayloadError) Error() string

type EvidenceRef

type EvidenceRef struct {
	Evidence     EvidenceID
	MessageIndex *int
}

EvidenceRef references evidence by EvidenceID and/or a message index. It is used by Operations (and later Findings) to point at supporting evidence without duplicating it. At least one of the two must be set.

type Expectation

type Expectation struct {
	// RequiredFacts are statements a correct answer must establish or support.
	RequiredFacts []Fact
	// ForbiddenActions name actions a correct interaction must not take.
	ForbiddenActions []ActionName
	// ExpectedToolCalls constrain which tools are invoked and how often.
	ExpectedToolCalls []ToolCallExpectation
	// StructuredOutput, when set, requires a conforming terminal structured
	// output.
	StructuredOutput *StructuredOutputExpectation
	// ReferenceAnswers are author-supplied golden answers for reference-based
	// evaluators.
	ReferenceAnswers []ReferenceAnswer
	// PolicyRef, when set, references an external policy revision this scenario
	// qualifies against.
	PolicyRef Revision
}

Expectation is optional qualification data describing a correct interaction. Every field is independently optional; a wholly-empty Expectation is valid and simply asserts nothing.

func (*Expectation) Validate

func (e *Expectation) Validate() error

Validate reports whether e is well-formed. Each populated collection is size-bounded and each member is validated; a nil or empty Expectation is valid. Diagnostics reference field names and bounds only, never field values.

type Fact

type Fact string

Fact is a single statement a correct interaction must establish or support, for example "the invoice is non-refundable". A valid Fact is non-empty, valid UTF-8, and within MaxFactBytes. It carries author-supplied domain meaning, so it is a named type rather than a bare string.

func (Fact) Validate

func (f Fact) Validate() error

Validate reports whether f is a well-formed Fact. Its diagnostic references only the field name and bound, never the fact text.

type Finding

type Finding struct {
	Code     FindingCode
	Severity Severity
	Message  string
	Evidence []EvidenceRef
}

Finding is one qualitative issue an evaluator reports. Code identifies the check; Severity ranks it; Message is bounded evaluator-authored prose (treated as untrusted downstream); Evidence points at supporting evidence in the enclosing assessment. Findings never carry a raw transcript.

type FindingCode

type FindingCode string

FindingCode is a stable, safe identifier for a distinct check a finding reports on (for example "canary_leak" or "missing_required_evidence"). It is evaluator-defined but must be a well-formed identifier so it can key a finding set and appear safely in reports. A valid FindingCode is non-empty, valid UTF-8, and no longer than MaxNameBytes bytes.

const FindingEvaluatorError FindingCode = "evaluator_error"

This file declares Run: the non-fail-fast execution engine that turns a Suite into a Report. It coordinates the pipeline

Scenario -> Target -> Observation -> Evaluators -> SampleReport

under three invariants:

  • Stage separation. A target failure is recorded on the sample's TargetErr (a typed stage error) and its evaluators are skipped; it never becomes a failed quality assessment and never aborts the run. An evaluator's own failure becomes an error-status assessment beside its siblings; it never becomes a fail and never discards a sibling's completed assessment.
  • Determinism. Trials are expanded into a stable, scenario-major sample order before any work starts, and each sample is written to a fixed slot index — never appended from a goroutine — so the report order is identical whether the run is sequential or concurrent.
  • Bounded, cancellable concurrency. Concurrency is opt-in and capped by a counting semaphore; on context cancellation the runner stops starting new work and returns the partial report plus the context error, retaining every completed sample.

FindingEvaluatorError is the safe code attached to the error-status assessment the runner synthesises when an evaluator returns a non-nil error. The underlying error is never echoed (it may carry untrusted content); the failure is recorded as an evaluator-stage error, not a quality verdict.

const FindingEvaluatorIdentityMismatch FindingCode = "evaluator_identity_mismatch"

FindingEvaluatorIdentityMismatch is the safe code attached to the error-status assessment the runner synthesises when an evaluator returns a nil error and an otherwise-valid Assessment whose Evaluator/Revision identity does not match the evaluator's own descriptor. Assessment.Validate cannot catch this — it has no descriptor to compare against — so the runner enforces it. The masqueraded verdict is discarded (fail-secure): a buggy or hostile evaluator must not be able to stamp its assessment with ANOTHER evaluator's identity and corrupt report provenance and cross-evaluator comparison. The attacker-chosen identity is never echoed (it may be adversarial); the failure is recorded as an evaluator-stage error under the descriptor's true identity, not a quality verdict.

const FindingEvaluatorInvalidAssessment FindingCode = "evaluator_invalid_assessment"

FindingEvaluatorInvalidAssessment is the safe code attached to the error-status assessment the runner synthesises when an evaluator returns a nil error but an Assessment that fails Assessment.Validate. The invalid verdict is discarded (fail-secure): a buggy or hostile evaluator must not place an unvalidated verdict — a zero value, a pass carrying a severe finding, a dangling evidence reference, a mismatched identity — into the report. The validation error is never echoed (it may carry untrusted content); the failure is recorded as an evaluator-stage error, not a quality verdict.

const FindingMissingRequiredEvidence FindingCode = "missing_required_evidence"

FindingMissingRequiredEvidence is the code CheckRequires attaches to the unverified assessment it produces when a required EvidenceKind is absent.

func (FindingCode) Validate

func (c FindingCode) Validate() error

Validate reports whether c is a well-formed FindingCode.

type IndexRangeError

type IndexRangeError struct {
	// Field is the domain field name, e.g. "MessageRange" or
	// "ConversationExcerpt.MessageIndex".
	Field string
	// Index is the offending index or range boundary.
	Index int
	// Len is the length of the conversation the index must fall within.
	Len int
}

IndexRangeError reports that an integer index or range lay outside the conversation it addresses. Field names the offending domain field; Index is the offending index (or range boundary); Len is the conversation length. All three are safe integers/constants — no conversation content is embedded.

func (*IndexRangeError) Error

func (e *IndexRangeError) Error() string

type InvalidEnumError

type InvalidEnumError struct {
	// Enum is the enumerated type name, e.g. "Scope" or "AssessmentStatus".
	Enum string
	// Value is a safe rendering of the invalid value (the numeric ordinal for
	// integer enums), or "" when the offending token is withheld.
	Value string
}

InvalidEnumError reports that a value of an enumerated type was not a known member. Enum is the type name. For integer-backed enums Value holds the underlying number, which is always safe to render. For string-backed enums the offending token is deliberately withheld, because it may originate from untrusted input; Value is left empty and only the type name is reported.

func (*InvalidEnumError) Error

func (e *InvalidEnumError) Error() string

type Label

type Label struct {
	Key   Name
	Value string
}

Label is a typed key/value tag on a scenario, used to group and filter cases (suite, risk tier, capability, and so on). Key carries domain meaning and reuses the Name identity rules; Value is a bounded free-form string. Within a scenario a Key is unique — a label set is a map, not a multimap — so a repeated Key is a duplicate and rejected.

func (Label) Validate

func (l Label) Validate() error

Validate reports whether l is a well-formed label. Its diagnostic references the field name only, never the key or value.

type Measurement

type Measurement struct {
	Name  Name
	Value float64
	Unit  Unit
}

Measurement is one named numeric result an evaluator produced. Value must be finite; Unit names its dimension. Measurement names are unique within an assessment so a report can key on them.

func (Measurement) Validate

func (m Measurement) Validate() error

Validate reports whether m is a well-formed measurement: a valid Name, a finite Value (never NaN or ±Inf), and a known Unit. The value is safe to render, but the offending name is not echoed on failure.

type MessageIndexRef

type MessageIndexRef struct {
	Index int
}

MessageIndexRef is a bare reference to a message by index.

type MessageRange

type MessageRange struct {
	Start int
	Len   int
}

MessageRange addresses a contiguous span [Start, Start+Len) of the conversation. A zero-length range is permitted at any in-bounds start.

type Method

type Method uint8

Method is descriptive metadata describing how an evaluator reaches its assessment. It supports filtering, cost accounting, and reporting.

const (
	// MethodProgrammatic evaluates observable facts deterministically. It is
	// the valid zero value.
	MethodProgrammatic Method = iota
	// MethodModel evaluates genuinely ambiguous meaning with a model judge.
	MethodModel
	// MethodComposite grounds a semantic conclusion in operational facts by
	// combining component evaluators.
	MethodComposite
)

func (Method) Validate

func (m Method) Validate() error

Validate reports whether m is a known Method member.

type Name

type Name string

Name identifies a scenario, evaluator, measurement, or similar domain object. A valid Name is non-empty, valid UTF-8, and no longer than MaxNameBytes bytes.

func (Name) Validate

func (n Name) Validate() error

Validate reports whether n is a well-formed Name.

type NilEvaluatorError

type NilEvaluatorError struct{}

NilEvaluatorError reports that Run was given a nil evaluator in its evaluator list. A nil evaluator cannot describe or evaluate anything, so it is rejected at preflight rather than panicking during execution.

func (*NilEvaluatorError) Error

func (e *NilEvaluatorError) Error() string

type NilTargetError

type NilTargetError struct{}

NilTargetError reports that Run was called with a nil target. A run has no meaning without a target to execute, so this is rejected at preflight.

func (*NilTargetError) Error

func (e *NilTargetError) Error() string

type Observation

type Observation struct {
	Conversation content.AgenticMessages
	Scope        Scope
	Subject      Subject
	Trace        Trace
	Expectation  *Expectation
}

Observation is the canonical record of one evaluated interaction. Conversation is the semantic record; Trace holds the operational facts that are not present in the conversation. A nil/empty Conversation is a valid empty thread, but any message index or range must then be empty too.

Expectation is optional qualification data (see expectation.go). Qualification observations carry it; production observations normally omit it. When present it is validated by Observation.Validate; a nil Expectation is valid.

func (Observation) Validate

func (o Observation) Validate() error

Validate reports whether the observation is well-formed: a valid scope and subject, a trace whose time range, message ranges, evidence, and operation references are all consistent with the conversation, and a valid Expectation when one is present. It is read-only and preserves the order of the conversation, operations, and evidence.

type Operation

type Operation struct {
	ID         string
	ParentID   string
	Kind       OperationKind
	Status     OperationStatus
	StartedAt  time.Time
	EndedAt    time.Time
	Attributes []Attribute
	ErrorClass ErrorClass
	Evidence   []EvidenceRef
}

Operation describes one timed step: an inference call, tool execution, network request, process action, sandbox decision, or other step. It carries typed status, timestamps, parent/child correlation, a small set of safe attributes, an optional error classification, and evidence references. It never carries a transcript: message text lives only in the conversation.

func (Operation) Validate

func (o Operation) Validate() error

Validate reports whether o is well-formed in isolation. It does not resolve evidence references against a trace or conversation; Trace.validate does that.

type OperationKind

type OperationKind string

OperationKind classifies a timed step in a trace. There is no valid zero value: every operation declares what it is.

const (
	// OperationInference is a model inference call.
	OperationInference OperationKind = "inference"
	// OperationTool is a tool execution.
	OperationTool OperationKind = "tool"
	// OperationNetwork is a network request.
	OperationNetwork OperationKind = "network"
	// OperationProcess is a process action.
	OperationProcess OperationKind = "process"
	// OperationSandbox is a sandbox decision.
	OperationSandbox OperationKind = "sandbox"
	// OperationStep is any other timed step.
	OperationStep OperationKind = "step"
)

func (OperationKind) Validate

func (k OperationKind) Validate() error

Validate reports whether k is a known OperationKind.

type OperationStatus

type OperationStatus string

OperationStatus is the terminal disposition of an operation. There is no valid zero value.

const (
	// OperationOK indicates the operation completed successfully.
	OperationOK OperationStatus = "ok"
	// OperationFailed indicates the operation failed.
	OperationFailed OperationStatus = "failed"
	// OperationCancelled indicates the operation was cancelled.
	OperationCancelled OperationStatus = "cancelled"
	// OperationTimedOut indicates the operation exceeded its deadline.
	OperationTimedOut OperationStatus = "timed_out"
)

func (OperationStatus) Validate

func (s OperationStatus) Validate() error

Validate reports whether s is a known OperationStatus.

type Provenance

type Provenance struct {
	Suite      Revision
	Target     Revision
	Evaluators []EvaluatorRevision
}

Provenance records the revisions needed to interpret and reproduce a report: the suite revision, the observed target revision, and each evaluator's identity in the order they were supplied. It is minimal by design; rubric, judge, schema, and policy provenance are layered on later.

type RedactedExcerpt

type RedactedExcerpt string

RedactedExcerpt is a bounded, caller-redacted snippet of otherwise untrusted content. It is the only textual representation of conversation or judge text permitted on evidence; callers are responsible for redacting before construction, and Validate enforces the length bound and UTF-8 validity so a hostile or oversized value cannot pass through. The empty value is valid: a hash or classification may carry the correlation instead.

func (RedactedExcerpt) Validate

func (x RedactedExcerpt) Validate() error

Validate reports whether x is within bounds and valid UTF-8.

type ReferenceAnswer

type ReferenceAnswer string

ReferenceAnswer is an author-supplied golden answer used by evaluators that compare against a reference. A valid ReferenceAnswer is non-empty, valid UTF-8, and within MaxReferenceAnswerBytes.

func (ReferenceAnswer) Validate

func (r ReferenceAnswer) Validate() error

Validate reports whether r is a well-formed ReferenceAnswer.

type Report

type Report struct {
	ID         string
	Suite      Revision
	Target     Revision
	StartedAt  time.Time
	EndedAt    time.Time
	Samples    []SampleReport
	Summary    Summary
	Provenance Provenance
}

Report is the complete result of one Run: the samples in deterministic order, a minimal status summary, and the provenance needed to interpret and compare it. ID is a deterministic identifier derived from the suite (see Suite.reportID); a caller may overwrite it with a globally unique run ID. Suite is the suite revision; Target is the observed target revision, kept distinct from Suite so a report records both what was run and what it ran against.

func Run

func Run(ctx context.Context, cfg RunConfig, suite Suite, target Target, evaluators ...Evaluator) (Report, error)

Run executes suite against target, applying evaluators to every resulting observation, and returns the report. It validates all inputs at preflight and returns the zero Report with a typed error if any input is ill-formed. During execution it never fails fast: target and evaluator failures are recorded as data. It returns a non-nil error only from preflight or from context cancellation; on cancellation the returned Report holds every sample that completed and the error is the context error.

func (Report) Validate

func (r Report) Validate() error

Validate reports whether r satisfies the report-level invariants. It is the whole-report boundary check applied to an untrusted, reconstructed report (the codec calls it after decoding) and is also satisfied by every report the runner itself produces. It enforces, in order:

  • Identity: ID is non-empty, valid UTF-8, and within MaxReportIDBytes; Suite is a required valid revision; Target is valid when present and is present exactly when at least one sample reached the target successfully. An all-target-failed or fully cancelled run legitimately records an empty observed Target revision.
  • Timestamps: when both StartedAt and EndedAt are set, EndedAt is not before StartedAt. Zero timestamps are permitted (the runner may leave them unset).
  • Samples: every ScenarioID is non-empty, valid UTF-8, and within MaxIDBytes; every TrialIndex is non-negative; the (ScenarioID, TrialIndex) identity is unique across samples; and within each sample no two assessments share an evaluator NAME (a name identifies exactly one evaluator, so a repeat is rejected even when the revisions differ). Each contained Assessment is itself valid.
  • Evaluator revision consistency: report-wide, an evaluator name maps to exactly one revision — the same name carrying two different revisions across samples (revision drift) is rejected.
  • Summary: the stored Summary agrees with the samples (recomputed via summarize) — same sample count, target-error count, and per-status tally.
  • Provenance: Suite is required and well-formed; Target is well-formed when present; each evaluator's Name and Revision is required; no evaluator name is repeated; and every successful sample carries exactly the declared evaluator identity set.

Diagnostics never echo a data-supplied value (a scenario ID is untrusted); a structural failure is reported as a ReportValidationError carrying only a fixed-vocabulary reason, and a contained-part failure surfaces that part's own typed, content-free validation error.

type ReportValidationError

type ReportValidationError struct {
	// Reason is one of the reportReason* constants.
	Reason string
}

ReportValidationError reports that a Report failed a report-level invariant (identity, timestamp ordering, trial index, sample or evaluator uniqueness, summary consistency, or observed-target consistency). Reason is drawn only from the fixed vocabulary below, so no untrusted content — in particular a data-supplied scenario ID — is ever embedded; only the class of failure is reported.

func (*ReportValidationError) Error

func (e *ReportValidationError) Error() string

type Revision

type Revision string

Revision identifies a specific version of a named object, such as an evaluator revision, a model revision, or a prompt revision. A valid Revision is non-empty, valid UTF-8, and no longer than MaxRevisionBytes bytes.

func (Revision) Validate

func (r Revision) Validate() error

Validate reports whether r is a well-formed Revision.

type RunConfig

type RunConfig struct {
	// Trials is how many times each scenario is executed. A value of 0 means one
	// trial. Negative or above MaxTrials is rejected at preflight.
	Trials int
	// Concurrency is the maximum number of samples executed simultaneously. A
	// value of 0 (or 1) means sequential execution. Negative is rejected.
	// Concurrency is opt-in: the default never runs targets in parallel.
	Concurrency int
	// TargetTimeout bounds a single target execution. Zero means no per-target
	// timeout (the run context still applies). Negative is rejected.
	TargetTimeout time.Duration
	// EvaluatorTimeout bounds a single evaluator execution. Zero means no
	// per-evaluator timeout. Negative is rejected.
	EvaluatorTimeout time.Duration
	// contains filtered or unexported fields
}

RunConfig shapes one execution: how many trials per scenario, how much target concurrency to allow, and the per-stage timeouts. Its zero value is a valid, conservative default — one trial, sequential, no per-stage timeout — so RunConfig{} runs a suite deterministically.

func (RunConfig) Validate

func (c RunConfig) Validate() error

Validate reports whether c is a well-formed config. It enforces the trial and concurrency bounds and rejects negative timeouts. It does not apply defaults; trials, concurrency, and clock accessors do that.

type Sample

type Sample struct {
	Scenario    *Scenario
	Observation Observation
}

Sample binds a scenario to the observation it produced so an evaluator can assess them together. Scenario is nil for pure continuous observation, where an observation already exists and no target was executed. Observation is always present.

func (Sample) Validate

func (s Sample) Validate() error

Validate reports whether the sample is well-formed. The Observation is always validated. When a Scenario is present it is validated too, and the observation's subject revision must match the target revision the scenario declares (Scenario.Revision): a target that returns an observation for a different revision than the scenario qualifies is a stage error, not a verdict, and must be rejected here rather than silently evaluated. With no Scenario (continuous observation) only the Observation is checked.

type SampleReport

type SampleReport struct {
	ScenarioID  string
	TrialIndex  int
	Observation Observation
	TargetErr   *TargetError
	Assessments []Assessment
}

SampleReport is the result of one (scenario, trial) sample. ScenarioID and TrialIndex are the sample's stable derived identity. Observation is what the target produced (the zero Observation when the target failed). TargetErr is a typed stage error when the target stage failed, and nil when it succeeded — a target failure is recorded here, never as a failed assessment. Assessments holds each evaluator's individual result in evaluator order; a per-evaluator failure appears as an error-status assessment beside its succeeding siblings, never discarding them.

type SampleSubjectMismatchError

type SampleSubjectMismatchError struct{}

SampleSubjectMismatchError reports that a sample's observation described a subject whose revision did not match the target revision the sample's scenario declares. This is a stage error — the target produced an observation for the wrong revision — not a failed assessment. Both revisions are withheld from the message: the subject revision originates with the target and must not leak through a diagnostic.

func (*SampleSubjectMismatchError) Error

type Scenario

type Scenario struct {
	ID          string
	Name        Name
	Revision    Revision
	Input       content.AgenticMessages
	Expectation *Expectation
	Labels      []Label
}

Scenario is an active qualification case: a stable identity, the input thread to drive a target with, optional qualification expectations, and labels. ID is the stable case identity used to reject duplicate scenarios and correlate reports across runs; Name and Revision identify the target revision the scenario qualifies (see Sample.Validate). Input must be non-empty — an empty scenario drives nothing.

func (Scenario) Validate

func (s Scenario) Validate() error

Validate reports whether s is a well-formed scenario: a bounded non-empty ID, a valid Name and Revision, a non-empty and bounded Input thread, unique valid labels, and a valid Expectation when present.

type Scope

type Scope uint8

Scope names the observational granularity an assessment applies to.

const (
	// ScopeCase covers a single qualification case. It is the valid zero value.
	ScopeCase Scope = iota
	// ScopeTurn covers one completed turn.
	ScopeTurn
	// ScopeSession covers a full session.
	ScopeSession
	// ScopeRun covers an entire run of many sessions or cases.
	ScopeRun
)

func (Scope) Validate

func (s Scope) Validate() error

Validate reports whether s is a known Scope member.

type Severity

type Severity string

Severity ranks a Finding. The members are ordered from least to most serious as declared here (info < low < medium < high < critical). It is a string type for direct rendering. There is no valid zero value: an unset severity does not validate.

const (
	// SeverityInfo is informational, not a defect.
	SeverityInfo Severity = "info"
	// SeverityLow is a minor issue.
	SeverityLow Severity = "low"
	// SeverityMedium is a moderate issue.
	SeverityMedium Severity = "medium"
	// SeverityHigh is a serious issue.
	SeverityHigh Severity = "high"
	// SeverityCritical is the most serious issue.
	SeverityCritical Severity = "critical"
)

func (Severity) Validate

func (s Severity) Validate() error

Validate reports whether s is a known Severity. The zero value is not a member.

type Sink

type Sink interface {
	WriteReport(context.Context, Report) error
}

Sink is the destination contract for a completed Report. It is the core reporting seam: the runner (or a continuous-eval loop) hands each Report to a Sink, and a Sink persists, forwards, or exports it. Implementations live outside the root package (a JSON file sink, an OpenTelemetry sink, an in-memory sink) so the root never depends on a storage or wire technology. WriteReport takes a context so a slow or networked sink is cancellable, and returns a typed error the caller can classify. A Sink must treat the Report as untrusted-adjacent: it must not place raw conversation text, judge explanations, or secrets in any external label — redaction is the wire form's responsibility, enforced by the concrete sink.

type StatusConsistencyError

type StatusConsistencyError struct {
	// Status is the assessment's declared status.
	Status AssessmentStatus
	// Reason is one of the statusReason* constants.
	Reason string
}

StatusConsistencyError reports that an Assessment's declared Status was inconsistent with its contents — for example a pass carrying a high/critical-severity finding, or a non-verdict status (unverified, error, skipped) carrying a quality measurement. Status is the assessment's own validated status (a closed enum constant) and Reason is drawn only from the fixed vocabulary below, so no untrusted content is embedded.

func (*StatusConsistencyError) Error

func (e *StatusConsistencyError) Error() string

type StructuredErrorReason

type StructuredErrorReason string

StructuredErrorReason classifies why a structured-output response failed validation. There is no valid zero value: an unclassified failure is rejected so a bare error can never masquerade as a known, benign one.

const (
	// StructuredErrorInvalidJSON: the response was not valid JSON.
	StructuredErrorInvalidJSON StructuredErrorReason = "invalid_json"
	// StructuredErrorSchemaMismatch: the response did not match the schema shape.
	StructuredErrorSchemaMismatch StructuredErrorReason = "schema_mismatch"
	// StructuredErrorMissingField: a required field was absent.
	StructuredErrorMissingField StructuredErrorReason = "missing_field"
	// StructuredErrorOutOfRange: a value fell outside its permitted range.
	StructuredErrorOutOfRange StructuredErrorReason = "out_of_range"
	// StructuredErrorEmptyOutput: the model produced no terminal output.
	StructuredErrorEmptyOutput StructuredErrorReason = "empty_output"
)

func (StructuredErrorReason) Validate

func (r StructuredErrorReason) Validate() error

Validate reports whether r is a known StructuredErrorReason.

type StructuredOutput

type StructuredOutput struct {
	SchemaName     Name
	SchemaRevision Revision
}

StructuredOutput is the positive evidence that the subject produced structured output that validated against its declared schema. It carries only safe schema identity — the schema's name and/or revision — and never the raw model output, so it is a content-free proof of structured-output success. Both identity fields are optional (the presence of this evidence is itself the signal), and each is validated only when supplied.

type StructuredOutputError

type StructuredOutputError struct {
	Schema     Revision
	Reason     StructuredErrorReason
	DetailHash ContentHash
}

StructuredOutputError records a structured-output validation failure as a classification plus an optional correlatable hash of the offending detail. The raw model text is never stored.

type StructuredOutputExpectation

type StructuredOutputExpectation struct {
	Schema Revision
	Strict bool
}

StructuredOutputExpectation asserts that the interaction produces a terminal structured output conforming to a named schema revision. Schema is required (an expectation with no schema asserts nothing and is malformed). Strict mirrors the inference OutputSchema.Strict flag: when true the output must match the schema exactly.

func (StructuredOutputExpectation) Validate

func (s StructuredOutputExpectation) Validate() error

Validate reports whether s is a well-formed structured-output expectation.

type Subject

type Subject struct {
	ID       string
	Kind     SubjectKind
	Name     Name
	Revision Revision
}

Subject identifies what produced the observation. ID is a required correlation identifier; Name and Revision reuse the domain identity types so the same validation rules apply everywhere.

func (Subject) Validate

func (s Subject) Validate() error

Validate reports whether s is a well-formed Subject.

type SubjectKind

type SubjectKind string

SubjectKind names what is under evaluation. There is no valid zero value: a subject must declare what it is, so an unset kind is rejected and cannot be silently treated as, say, a model. The member set covers the Phase-1 targets the design names and nothing more.

const (
	// SubjectModel is a bare inference model.
	SubjectModel SubjectKind = "model"
	// SubjectAgent is an agent entry point.
	SubjectAgent SubjectKind = "agent"
	// SubjectPrompt is a prompt or prompt template.
	SubjectPrompt SubjectKind = "prompt"
	// SubjectHTTPEndpoint is an HTTP service under test.
	SubjectHTTPEndpoint SubjectKind = "http_endpoint"
	// SubjectProcess is a local process under test.
	SubjectProcess SubjectKind = "process"
)

func (SubjectKind) Validate

func (k SubjectKind) Validate() error

Validate reports whether k is a known SubjectKind. The offending token is withheld because it may be untrusted.

type Suite

type Suite struct {
	Name      Name
	Revision  Revision
	Scenarios []Scenario
}

Suite is a named, versioned set of scenarios to run against a single target. Name and Revision identify the suite for provenance and reporting (Revision becomes Report.Suite); Scenarios is the ordered set of cases. Scenario order is preserved in the report, so a suite is reproducible.

func (Suite) Validate

func (s Suite) Validate() error

Validate reports whether s is a well-formed suite: a valid Name and Revision, a non-empty scenario set with no duplicate scenario IDs, and a valid Scenario for every entry. A duplicate ID would make two cases indistinguishable in the report and in baseline comparison, so it is rejected here.

type Summary

type Summary struct {
	Samples      int
	TargetErrors int
	Assessments  map[AssessmentStatus]int
}

Summary is a minimal roll-up of a report: the sample count, how many samples failed at the target stage, and a count of assessments by status. It is kept deliberately small; distribution, quantile, and baseline-comparison reporting are added by a later task and do not belong in the runner.

type Target

type Target interface {
	Name() string
	Observe(context.Context, Scenario) (Observation, error)
}

Target executes a scenario and reports the resulting observation. Name identifies the target for provenance and reporting. Observe runs the scenario and returns its observation; it takes a context so callers can bound execution, and it returns a typed error on failure rather than encoding failure as an observation. A target error is a stage error and must never be reported as a failed assessment.

Observe's Scenario argument is READ-ONLY. The passed Scenario and everything it references — its Input, Labels, and Expectation — share backing with the caller's suite; an implementation MUST NOT mutate them. The runner shallow- copies the Scenario struct header only and relies on this contract in place of a deep copy. A target that needs to derive a modified scenario must copy what it changes.

type TargetError

type TargetError struct {
	// Cause is the underlying failure. It may be a context error, a target's own
	// error, or an observation ValidationError.
	Cause error
}

TargetError reports that a sample's target stage failed: the target returned an error, timed out, was cancelled, or produced an observation that did not validate. It is a stage error, never a failed quality assessment. The wrapped Cause is available via Unwrap so callers can classify the failure (for example errors.Is(err, context.DeadlineExceeded)); the Error() text is fixed and never echoes the cause, which may originate outside the process and carry untrusted content.

func (*TargetError) Error

func (e *TargetError) Error() string

func (*TargetError) Unwrap

func (e *TargetError) Unwrap() error

type TimingEvidence

type TimingEvidence struct {
	Label    Name
	Duration time.Duration
}

TimingEvidence records the duration of a step. Duration is a safe scalar.

type ToolCallExpectation

type ToolCallExpectation struct {
	Tool     Name
	MinCount int
	MaxCount *int
}

ToolCallExpectation asserts that a named tool is invoked a bounded number of times. Tool is required. MinCount is the inclusive lower bound and must not be negative. MaxCount is an optional inclusive upper bound; when set it must be non-negative and no less than MinCount. A nil MaxCount leaves the count unbounded above.

func (ToolCallExpectation) Validate

func (t ToolCallExpectation) Validate() error

Validate reports whether t is a well-formed tool-call expectation.

type ToolOperationEvidence

type ToolOperationEvidence struct {
	ToolName    Name
	ToolUseID   string
	ArgsHash    ContentHash
	ArgsBytes   int
	ResultBytes int
	IsError     bool
}

ToolOperationEvidence records a tool invocation as safe metadata only. Tool arguments and results are sensitive, so they are represented by a hash and byte counts — never stored raw on this value.

type Trace

type Trace struct {
	TraceID       string
	SessionID     string
	TurnID        string
	StartedAt     time.Time
	EndedAt       time.Time
	Model         Revision
	Prompt        Revision
	MessageRanges []MessageRange
	Operations    []Operation
	Evidence      []Evidence
}

Trace holds facts not already present in the conversation: correlation identifiers, timing, the model and prompt revisions in effect, message ranges, operations, and typed evidence.

type Unit

type Unit string

Unit names the dimension of a Measurement.Value. The member set covers the dimensions the Phase-1 evaluators produce and nothing more. There is no valid zero value: every measurement must declare a known unit.

const (
	// UnitCount is a dimensionless whole-number tally.
	UnitCount Unit = "count"
	// UnitRatio is a dimensionless proportion or rate.
	UnitRatio Unit = "ratio"
	// UnitSecond is a duration in seconds.
	UnitSecond Unit = "second"
	// UnitToken is a count of model tokens.
	UnitToken Unit = "token"
	// UnitByte is a size in bytes.
	UnitByte Unit = "byte"
)

func (Unit) Validate

func (u Unit) Validate() error

Validate reports whether u is a known Unit. The zero value is not a member.

type UnknownEvidenceError

type UnknownEvidenceError struct{}

UnknownEvidenceError reports that an EvidenceRef pointed at an EvidenceID that no evidence entry in the trace defines. The dangling identifier is withheld for the same reason as DuplicateEvidenceError.

func (*UnknownEvidenceError) Error

func (e *UnknownEvidenceError) Error() string

type UsageEvidence

type UsageEvidence struct {
	Model Revision
	Usage content.Usage
}

UsageEvidence records model token usage. All fields are safe counts.

type ValidationError

type ValidationError struct {
	// Field is the domain type or field name, e.g. "Name" or "Revision".
	Field string
	// Reason is a bounded, safe explanation, e.g. "must not be empty".
	Reason string
}

ValidationError reports that a domain value failed validation. Field names the domain type or field that failed; Reason is a short, developer-facing explanation drawn only from a fixed vocabulary of package constants and bounds. Neither field ever contains the offending value, so an untrusted or oversized input cannot leak through an error.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Directories

Path Synopsis
Package compare diffs a candidate eval report against a baseline.
Package compare diffs a candidate eval report against a baseline.
Package dataset is the versioned JSONL codec for eval scenarios.
Package dataset is the versioned JSONL codec for eval scenarios.
Package evaltest integrates eval reports with Go's testing package.
Package evaltest integrates eval reports with Go's testing package.
Package exact provides deterministic, programmatic evaluators over the typed core/content conversation and eval evidence.
Package exact provides deterministic, programmatic evaluators over the typed core/content conversation and eval evidence.
Package judge implements the structured-output model judge: an eval.Evaluator that scores a sample's conversation against a rubric by calling an inference.Client with strict structured output.
Package judge implements the structured-output model judge: an eval.Evaluator that scores a sample's conversation against a rubric by calling an inference.Client with strict structured output.
Package reportjson is the versioned, redacted JSON codec for eval reports and a file sink that persists them.
Package reportjson is the versioned, redacted JSON codec for eval reports and a file sink that persists them.
Package rubric declares evaluation rubrics: the trusted definition of what "good" means for a model judge.
Package rubric declares evaluation rubrics: the trusted definition of what "good" means for a model judge.
target
inference
Package inference implements the active-inference eval.Target: it drives a scenario's input thread through an inference.Client and projects the model's reply into an eval.Observation.
Package inference implements the active-inference eval.Target: it drives a scenario's input thread through an inference.Client and projects the model's reply into an eval.Observation.

Jump to

Keyboard shortcuts

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