Documentation
¶
Overview ¶
Package aicheck evaluates a flow's AI checks: the `expect:` block of request and graphql steps (a JSON schema, latency, time-to-first-token and token budgets, a G-Eval rubric judge). It runs after a flow's steps finish, so checks never add to step or flow timings. Ported from the Stresseur CLI's stress/internal/expect with its behavior; see docs/specs/AI_CHECKS.md.
Index ¶
- Constants
- Variables
- func Borderline(score, minScore float64) bool
- func CacheKey(cfg JudgeConfig, j *JudgeSpec, input, output string) string
- func Clip(s string) string
- func DecodeExpect(n *yaml.Node) (mexpect.Expect, error)
- func DecodeQuality(n *yaml.Node) (*mexpect.Quality, error)
- func FoldFileSettings(c *mexpect.Checks, flowID idwrap.IDWrap) (mexpect.FlowSettings, bool)
- func FormatMS(v float64) string
- func JSONShape(v any) (map[string]any, bool)
- func KeyEnvironment(dotenvPath string) map[string]string
- func KeyEnvs(cfg JudgeConfig) []string
- func NoKeyReason(cfg JudgeConfig) string
- func ParseDotEnv(b []byte) map[string]string
- func ParseRate(s string) (float64, error)
- func ParseVerdict(text string) (int, string, error)
- func Percentile(values []float64, p float64) float64
- func Present(n yaml.Node) bool
- func Render(tmpl, step string, run Run) (out string, missing string)
- func Resolve(path, step string, run Run) (any, bool)
- func RunLines(step string, spec *Spec, results []Result) []string
- func StatsLine(s Stats) string
- func StrictDecode(n *yaml.Node, out any) error
- func SummaryLines(step string, sums []Summary) []string
- func Text(v any) string
- func ValidateJudge(c JudgeConfig) error
- func WeightedScore(tokens []openAILogprob) *float64
- func Worst(statuses ...string) string
- type APIError
- type Cache
- type CacheEntry
- type Client
- type Evaluator
- type File
- type Flow
- type FlowResult
- type JudgeConfig
- type JudgeSpec
- type Prompt
- type Quality
- type Result
- type Run
- type Sample
- type Spec
- type Stats
- type StepRun
- type Summary
- type Verdict
Constants ¶
const ( KindSchema = "schema" KindLatency = "latency" KindTTFT = "ttft" KindTokens = "tokens" KindJudge = "judge" )
Check kinds, in the order they are evaluated and printed.
const ( ProviderAnthropic = "anthropic" ProviderOpenAI = "openai" )
Providers.
const ( DefaultSchemaAt = "response.body" DefaultUsage = "response.body.usage" // StreamUsage is the usage a streamed response reported; the fallback when usage: is unset. StreamUsage = "response.usage" )
Default paths of an expect: block.
const ( StatusPassed = "passed" StatusSkipped = "skipped" StatusWarn = "warn" StatusFailed = "failed" )
Statuses, worst last.
const DefaultCachePath = ".devtools/judge-cache.json"
DefaultCachePath is the judge cache, relative to the working directory. The key is the Stresseur CLI's, so both can share one file (Stresseur keeps .stresseur/judge-cache.json).
const MaxCacheEntries = 5000
MaxCacheEntries bounds the cache file; the oldest entries go first.
const MaxReason = 300
MaxReason caps a reason, in characters.
const ReportOnlyHint = "Checks report only: add quality: { fail_below: 90% } to the flow file to fail on them."
ReportOnlyHint is printed once when checks failed but no quality.fail_below makes them count.
Variables ¶
var DefaultBaseURLs = map[string]string{ ProviderAnthropic: "https://api.anthropic.com", ProviderOpenAI: "https://api.openai.com/v1", }
DefaultBaseURLs are the provider APIs.
var DefaultModels = map[string]string{ ProviderAnthropic: "claude-haiku-4-5-20251001", ProviderOpenAI: "gpt-4.1-mini", }
DefaultModels is the judge model when the config names none.
var Kinds = []string{KindSchema, KindLatency, KindTTFT, KindTokens, KindJudge}
Kinds is every check kind in order.
Functions ¶
func Borderline ¶
Borderline: a whole score at min_score or one below, where one sample decides the verdict.
func CacheKey ¶
func CacheKey(cfg JudgeConfig, j *JudgeSpec, input, output string) string
CacheKey identifies a verdict: same judge, same rubric, same data → same verdict.
func DecodeExpect ¶
DecodeExpect strictly decodes an expect: block. It is validated by CompileSpec.
func DecodeQuality ¶
DecodeQuality strictly decodes and validates a quality: block.
func FoldFileSettings ¶
FoldFileSettings is a flow's settings with the file's judge: and quality: folded in, as a workspace keeps them per flow: the judge merged key by key, the flow's quality: over the file's. ok is false when there is nothing to keep.
func JSONShape ¶
JSONShape turns a node's output into plain JSON values (float64 numbers, map[string]any), the shape a JSON report holds, so checks behave the same on engine values and on reports.
func KeyEnvironment ¶
KeyEnvironment is where judge keys are read from: the dotenv file at dotenvPath (when it exists), overridden by the process environment.
func KeyEnvs ¶
func KeyEnvs(cfg JudgeConfig) []string
KeyEnvs are the variables a judge key is read from, in order.
func NoKeyReason ¶
func NoKeyReason(cfg JudgeConfig) string
NoKeyReason is why judge checks were skipped without a key.
func ParseDotEnv ¶
ParseDotEnv reads KEY=VALUE lines: `export ` prefixes, # comments and quoted values are accepted; anything else is skipped.
func ParseVerdict ¶
ParseVerdict reads {"reason": "...", "score": n} from a reply (the first JSON object in it).
func Percentile ¶
Percentile is the nearest-rank percentile of values (p in (0, 100]).
func Render ¶
Render fills every {{ path }} of a template. missing is the first path that did not resolve.
func Resolve ¶
Resolve reads a path in a run: response.… / request.… (the current step) or Step.response.… / Step.request.… (another step).
func RunLines ¶
RunLines are one run's check lines (`flow run`):
✓ AskAssistant latency 812 ms ≤ 4.0 s ✗ AskAssistant judge 3 (min 4): States a refund policy not in the docs.
func StatsLine ¶
StatsLine is the judge's work: "Judge: 4 calls, 8 cached, 1.1 s". "" when nothing was judged.
func StrictDecode ¶
StrictDecode decodes a YAML node into out, rejecting unknown keys.
func SummaryLines ¶
SummaryLines are a step's check lines over many runs (`ci`):
✓ AskAssistant schema 12 of 12 ✓ AskAssistant latency 12 of 12, p95 1.8 s ≤ 4.0 s ✗ AskAssistant judge 9 of 12, score 3.8 (min 4): States a refund policy not in the docs.
func WeightedScore ¶
func WeightedScore(tokens []openAILogprob) *float64
WeightedScore is G-Eval's probability-weighted score: at the score digit (the first 1–5 token after "score"), Σ p(s)·s over the 1–5 alternatives, renormalized. Nil when not found.
Types ¶
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache is the verdict cache, kept in a JSON file. Safe for concurrent use.
func OpenCache ¶
OpenCache reads the cache file; a missing or unreadable file is an empty cache. path "" keeps it in memory only.
type CacheEntry ¶
type CacheEntry struct {
Score float64 `json:"score"`
Reason string `json:"reason"`
Model string `json:"model"`
At string `json:"at"`
}
CacheEntry is one stored verdict.
type Client ¶
type Client struct {
Config JudgeConfig
APIKey string
HTTP *http.Client
// Backoff is the wait before retry n (1-based); exponential from 1 s when nil.
Backoff func(n int) time.Duration
// contains filtered or unexported fields
}
Client calls one provider.
type Evaluator ¶
type Evaluator struct {
Env map[string]string
HTTP *http.Client
Cache *Cache
Concurrency int
Backoff func(n int) time.Duration
Stats Stats
// contains filtered or unexported fields
}
Evaluator runs the checks of flows after their timed runs. One per command.
func (*Evaluator) EvaluateFlow ¶
EvaluateFlow runs every check of a flow over its measured runs: the deterministic ones inline, the judge calls in parallel (Concurrency at a time), through the cache.
type File ¶
File is a flow file's checks, by flow name.
func Parse ¶
Parse reads the checks of a flow file on its own (dir resolves schema file paths). A file without any expect: block parses to a File without checks; a malformed one is an error naming the step.
type Flow ¶
type Flow struct {
Name string
// Iterations is the flow's iterations: (for `stress ci`); 0 when unset.
Iterations int
Judge JudgeConfig // merged and defaulted
// AutoProvider is true when no provider, model, base_url or api_key_env was set:
// the judge then follows the keys present (see JudgeFor).
AutoProvider bool
Quality Quality // merged
// Steps are the steps with an expect: block, in file order.
Steps []string
Specs map[string]*Spec
}
Flow is one flow's checks.
type FlowResult ¶
FlowResult is a flow's checks: per run (parallel to the runs given), per step, the results; per step the summaries.
type JudgeConfig ¶
type JudgeConfig = mexpect.JudgeConfig
JudgeConfig is the judge: which model, where, with which key.
func DecodeJudge ¶
func DecodeJudge(n *yaml.Node) (*JudgeConfig, error)
DecodeJudge strictly decodes and validates a judge: block.
func JudgeDefaults ¶
func JudgeDefaults(c JudgeConfig) JudgeConfig
JudgeDefaults fills the provider's defaults.
func JudgeFor ¶
func JudgeFor(fl *Flow, env map[string]string) JudgeConfig
JudgeFor is a flow's judge for an environment. A flow that sets none of provider, model, base_url and api_key_env judges with OpenAI when OPENAI_API_KEY is the only key set; otherwise its configured (or default Anthropic) judge.
func MergeJudge ¶
func MergeJudge(base JudgeConfig, over JudgeConfig) JudgeConfig
MergeJudge applies a flow's judge over the file's, key by key.
type Prompt ¶
Prompt is one judge request.
func BuildPrompt ¶
BuildPrompt is the G-Eval prompt: criteria, fixed evaluation steps, a 1–5 rubric, then the input and output as delimited data.
type Quality ¶
type Quality struct {
// FailBelow (0–1): a check passing in a smaller share of runs fails the command. Nil: report only.
FailBelow *float64
}
Quality is the pass-rate gate.
type Result ¶
type Result struct {
Kind string `json:"kind"`
Passed bool `json:"passed"`
Score *float64 `json:"score"`
Reason string `json:"reason"`
JudgeModel *string `json:"judge_model"`
Cached bool `json:"cached"`
Value *float64 `json:"value"`
Limit *float64 `json:"limit"`
// Skipped: the judge could not judge (no key, provider error). Not a pass or a failure.
Skipped bool `json:"skipped,omitempty"`
// Judge accounting: calls made (cache hits: 0) and their tokens.
Calls int `json:"judge_calls,omitempty"`
InputTokens int `json:"judge_input_tokens,omitempty"`
OutputTokens int `json:"judge_output_tokens,omitempty"`
}
Result is one check in one run: the engine's report steps[].checks[].
type Sample ¶
type Sample struct {
Score int
Reason string
// Weighted is the logprob-weighted score (G-Eval), when the provider returned logprobs.
Weighted *float64
InputTokens int
OutputTokens int
}
Sample is one judge answer.
type Spec ¶
type Spec struct {
Schema *jsonschema.Schema
SchemaAt string
MaxLatencyMS *float64
MaxTTFTMS *float64
Usage string
// UsageSet: the step names its usage path, so there is no fallback to the stream's.
UsageSet bool
MaxInputTokens *float64
MaxOutputTokens *float64
MaxTotalTokens *float64
Judge *JudgeSpec
}
Spec is one step's compiled expect: block.
func CompileSpec ¶
CompileSpec validates an expect: block and compiles its schema. dir resolves a schema path; with dir "" a schema path is checked only for being a string, not read.
type Stats ¶
type Stats struct {
Calls int // API calls (borderline samples included)
Judged int // verdicts that needed calls
Cached int // verdicts from the cache or an identical output this run
Skipped int
InputTokens int
OutputTokens int
// Latencies are each verdict's wall time (its calls, samples in parallel).
Latencies []time.Duration
// Elapsed is the wall time of the judging phases.
Elapsed time.Duration
// NoKey are the reasons judge checks were skipped for a missing key, once each.
NoKey []string
}
Stats is the judge's work over a command.
type StepRun ¶
type StepRun struct {
// Ran: the engine executed it (Success or Failure).
Ran bool
DurationMS *float64
// Data is the node's output: {"request": {...}, "response": {...}}.
Data map[string]any
}
StepRun is one step in one run, as the engine executed it.
type Summary ¶
type Summary struct {
Kind string `json:"kind"`
Runs int `json:"runs"`
Passed int `json:"passed"`
Skipped int `json:"skipped"`
PassRate *float64 `json:"pass_rate"`
Status string `json:"status"`
FailBelow *float64 `json:"fail_below"`
Threshold *float64 `json:"threshold"`
MeanScore *float64 `json:"mean_score"`
JudgeModel *string `json:"judge_model"`
Criteria *string `json:"criteria"`
Reason string `json:"reason"`
JudgeCalls int `json:"judge_calls"`
JudgeInputTokens int `json:"judge_input_tokens"`
JudgeOutputTokens int `json:"judge_output_tokens"`
JudgeCached int `json:"-"`
// Values are the measured latency or token values, for the console's p95.
Values []float64 `json:"-"`
}
Summary is one check over a step's measured runs (ingest-v1 steps[].checks[]).