Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ collides with different provenance is refused instead of silently replaced.
The report lists every run and aggregates repeated points across runs with
mean ± spread.

## Spec Provenance

Create specs with `sweep plan`; machine-specific runtime choices are flags
(`--vllm-command`, `--gpu-memory-utilization`, `--kv-cache-memory-bytes`),
and deliberate ladder caps are declared trims with reasons
(`--trim 64k=8:'12 GiB KV budget'`) that render in reports like adaptive
skips. Generated specs carry a verified `generator` stamp: reports label
runs "Generated default sweep" only while the spec's content hash still
matches, and anything hand-written or edited shows as "Custom grid".

## Context Semantics

Every workload must declare what its context number means:
Expand Down
29 changes: 29 additions & 0 deletions docs/2026-07-02-default-inference-sweep.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,32 @@ whose results already completed.

Do not call a sweep complete until all completed, skipped, and failed cases are
recorded with enough metadata to reproduce the run.

## Spec Provenance

Create sweep specs only through the generator; never hand-write or
post-edit the JSON:

```sh
localperf sweep plan \
--model <model-id> \
--vllm-command /path/to/runtime/bin/vllm \
--gpu-memory-utilization 0.4 \
--kv-cache-memory-bytes 12884901888 \
--trim 64k=8:'12 GiB KV budget' \
--out spec.json
```

Machine-specific runtime choices (vllm path, GPU memory, KV budget, extra
serve args) are generator flags, so nothing forces editing the output. A
deliberate concurrency cap is a declared trim: `--trim <context>=<max>:<reason>`
removes the higher points from the grid, records the decision in the spec,
and reports render the trimmed points like adaptive skips — with the reason,
never as silent holes.

Generated specs carry a `generator` stamp (tool, intent, content hash).
`bench run` verifies the hash and prints the provenance; reports and the
viewer label the run "Generated default sweep" only when the hash still
matches. A spec without a stamp, or edited after generation, runs fine but
is labeled "Custom grid" — the label is verified from the stored spec bytes,
never taken on trust.
4 changes: 2 additions & 2 deletions docs/2026-07-05-report-integrity-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ date: 2026-07-05

# Report Integrity Plan

Date: 2026-07-05. Status: PR 1 (streaming TTFT) and PR 2 (rendering +
legacy cutover) implemented; PR 3 (provenance) in progress.
Date: 2026-07-05. Status: implemented (PR 1 streaming TTFT, PR 2
rendering + legacy cutover, PR 3 spec provenance).

Fixes every problem found while inspecting the live viewer serving the
4-model GB10 sweep of 2026-07-05, each verified against the code.
Expand Down
76 changes: 76 additions & 0 deletions internal/artifact/provenance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package artifact

import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
)

// GeneratorStamp is the provenance record `sweep plan` writes into generated
// specs. ContentHash covers the canonical spec document with the "generator"
// key removed, so any post-generation edit is detectable; see
// docs/2026-07-05-report-integrity-plan.md.
type GeneratorStamp struct {
Tool string `json:"tool"`
Version string `json:"version"`
Intent json.RawMessage `json:"intent,omitempty"`
LadderTrims []LadderTrim `json:"ladder_trims,omitempty"`
ContentHash string `json:"content_hash"`
}

// LadderTrim declares an author decision to cap a context's concurrency
// ladder, with the reason rendered in reports so trimmed points never look
// like silent holes.
type LadderTrim struct {
Context int `json:"context"`
MaxConcurrency int `json:"max_concurrency"`
Reason string `json:"reason"`
}

// Spec provenance statuses; reports label anything but a verified generated
// spec as a custom grid.
const (
SpecProvenanceGenerated = "generated"
SpecProvenanceEdited = "edited"
SpecProvenanceCustom = "custom"
)

// CanonicalSpecHash hashes a spec document with only the self-referential
// generator.content_hash removed, after canonicalizing through a generic
// JSON map (sorted keys), so the generator and every verifier agree
// byte-for-byte regardless of struct field order. The rest of the stamp —
// intent and ladder trims — is covered by the hash: reports trust those
// fields, so editing them must demote the spec.
func CanonicalSpecHash(raw []byte) (string, error) {
var doc map[string]any
if err := json.Unmarshal(raw, &doc); err != nil {
return "", err
}
if generator, ok := doc["generator"].(map[string]any); ok {
delete(generator, "content_hash")
}
canonical, err := json.Marshal(doc)
if err != nil {
return "", err
}
sum := sha256.Sum256(canonical)
return hex.EncodeToString(sum[:]), nil
}

// VerifySpecProvenance checks raw spec bytes against their embedded
// generator stamp: "generated" when the content hash matches, "edited" when
// a stamp exists but the content changed, "custom" when there is no stamp
// or the document does not parse.
func VerifySpecProvenance(raw []byte) (string, *GeneratorStamp) {
var doc struct {
Generator *GeneratorStamp `json:"generator"`
}
if err := json.Unmarshal(raw, &doc); err != nil || doc.Generator == nil {
return SpecProvenanceCustom, nil
}
hash, err := CanonicalSpecHash(raw)
if err != nil || hash != doc.Generator.ContentHash {
return SpecProvenanceEdited, doc.Generator
}
return SpecProvenanceGenerated, doc.Generator
}
70 changes: 60 additions & 10 deletions internal/benchcli/benchcli.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ func runSweepPlan(args []string) {
reference := flags.Bool("reference", true, "include the 4k max-throughput-reference capacity family")
stress := flags.Bool("stress", false, "add long-output decode spot checks (4096 tokens at 32k c4, 64k c1/c4) and the 128k points")
memFloor := flags.Float64("min-mem-available-gib", 0, "safety memory floor in GiB (default 40)")
vllmCommand := flags.String("vllm-command", "", "vllm executable for managed serves (machine-specific runtime path)")
gpuMemUtil := flags.Float64("gpu-memory-utilization", 0, "gpu memory utilization applied to every profile")
kvCacheBytes := flags.Int64("kv-cache-memory-bytes", 0, "pin vLLM KV cache size on every profile")
var trims trimFlags
flags.Var(&trims, "trim", "cap a context's ladder with a reason, e.g. 64k=8:'12 GiB KV budget'; repeatable")
out := flags.String("out", "", "output spec path (default stdout)")
_ = flags.Parse(args)
contextValues, err := parseTokenList(*contexts)
Expand All @@ -99,15 +104,19 @@ func runSweepPlan(args []string) {
os.Exit(2)
}
spec, err := sweepplan.Plan(sweepplan.PlanRequest{
Model: *model,
Contexts: contextValues,
Concurrency: concurrencyValues,
Repeats: *repeats,
NumPrompts: *numPrompts,
PromptsPerUser: *promptsPerUser,
IncludeReference: *reference,
IncludeStress: *stress,
MinMemAvailableGiB: *memFloor,
Model: *model,
Contexts: contextValues,
Concurrency: concurrencyValues,
Repeats: *repeats,
NumPrompts: *numPrompts,
PromptsPerUser: *promptsPerUser,
IncludeReference: *reference,
IncludeStress: *stress,
MinMemAvailableGiB: *memFloor,
VLLMCommand: *vllmCommand,
GPUMemoryUtilization: *gpuMemUtil,
KVCacheMemoryBytes: *kvCacheBytes,
Trims: trims.values,
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
Expand All @@ -130,6 +139,46 @@ func runSweepPlan(args []string) {
fmt.Printf("spec: %s\n", *out)
}

// trimFlags parses repeatable --trim values of the form
// "<context>=<max-concurrency>:<reason>", e.g. 64k=8:'12 GiB KV budget'.
type trimFlags struct {
values []vllmbench.LadderTrim
}

func (flags *trimFlags) String() string {
parts := make([]string, 0, len(flags.values))
for _, trim := range flags.values {
parts = append(parts, fmt.Sprintf("%d=%d:%s", trim.Context, trim.MaxConcurrency, trim.Reason))
}
return strings.Join(parts, ",")
}

func (flags *trimFlags) Set(value string) error {
context, rest, ok := strings.Cut(value, "=")
if !ok {
return fmt.Errorf("trim %q: want <context>=<max-concurrency>:<reason>", value)
}
maxPart, reason, ok := strings.Cut(rest, ":")
if !ok || strings.TrimSpace(reason) == "" {
return fmt.Errorf("trim %q: a reason after ':' is required", value)
}
contextValues, err := parseTokenList(context)
if err != nil || len(contextValues) != 1 {
return fmt.Errorf("trim %q: want one context like 64k or 65536", value)
}
contextTokens := contextValues[0]
maxConcurrency, err := strconv.Atoi(strings.TrimSpace(maxPart))
if err != nil || maxConcurrency <= 0 {
return fmt.Errorf("trim %q: max concurrency must be a positive integer", value)
}
flags.values = append(flags.values, vllmbench.LadderTrim{
Context: contextTokens,
MaxConcurrency: maxConcurrency,
Reason: strings.TrimSpace(reason),
})
return nil
}

// parseTokenList parses values such as "8k,16k,32768" into token counts.
func parseTokenList(value string) ([]int, error) {
var values []int
Expand Down Expand Up @@ -260,6 +309,7 @@ func runBench(args []string) {
_ = flags.Parse(args)
spec := mustLoadSpec(*specPath, filterFlags.Filter())
applyOverrides(&spec, overrides)
fmt.Printf("spec provenance: %s\n", spec.Provenance)
ctx := context.Background()
cancel := func() {}
if *timeout > 0 {
Expand Down Expand Up @@ -717,7 +767,7 @@ func usageRoot() {
localperf artifact check runs/example.sqlite
localperf artifact render runs/example.sqlite [--output runs/example.html] [--store]
localperf artifact merge --into runs/models/model.sqlite src1.sqlite [src2.sqlite ...]
localperf sweep plan --model model-id [--contexts 8k,16k,32k,64k] [--concurrency 1,4,8,16,32] [--repeats 1] [--reference] [--stress] [--out spec.json]
localperf sweep plan --model model-id [--contexts 8k,16k,32k,64k] [--concurrency 1,4,8,16,32] [--repeats 1] [--reference] [--stress] [--vllm-command /path/to/vllm] [--gpu-memory-utilization 0.4] [--kv-cache-memory-bytes N] [--trim 64k=8:'reason'] [--out spec.json]
localperf view runs/model.sqlite [runs/other.sqlite ...] [--addr 127.0.0.1:0] [--open]`)
}

Expand Down
115 changes: 114 additions & 1 deletion internal/report/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ type SQLiteReportDocument struct {
EventCounts []SQLiteReportCount
NotableEvents []SQLiteReportEvent
Commands []SQLiteReportCommand
SpecProvenance string
SpecGenerator *artifact.GeneratorStamp
SpecConcurrency []int
ExistingReports []SQLiteReportExport
ArtifactSummaries []SQLiteReportArtifactSummary
MeasurementMetrics map[int64]map[string]SQLiteReportMetric
Expand Down Expand Up @@ -449,6 +452,7 @@ func LoadSQLiteReport(path string) (SQLiteReportDocument, error) {
loadSQLiteReportEventCounts,
loadSQLiteReportNotableEvents,
loadSQLiteReportCommands,
loadSQLiteReportSpecProvenance,
loadSQLiteReportExports,
loadSQLiteReportArtifactSummaries,
} {
Expand All @@ -459,7 +463,8 @@ func LoadSQLiteReport(path string) (SQLiteReportDocument, error) {
doc.Measurements, doc.RepeatDetails = aggregateRepeatMeasurements(doc.Measurements)
doc.Legend = ReportMetrics
doc.MetadataItems = sqliteReportMetadataItems(doc)
doc.ThroughputRows = sqliteReportThroughputRows(doc)
realRows := sqliteReportThroughputRows(doc)
doc.ThroughputRows = append(realRows, trimmedThroughputRows(doc, realRows)...)
doc.ThroughputGroups = sqliteReportThroughputGroups(doc.ThroughputRows)
doc.PhaseSections = sqliteReportPhaseSections(doc.Measurements)
doc.Charts = sqliteReportCharts(doc.Measurements)
Expand Down Expand Up @@ -1346,8 +1351,116 @@ func sqliteReportCharts(measurements []SQLiteReportMeasurement) []SQLiteReportCh
return []SQLiteReportChart{{Title: "Aggregate Output Throughput", Unit: "tok/s", Height: 24 + len(bars)*28, Bars: bars}}
}

// loadSQLiteReportSpecProvenance verifies the latest run's original spec
// against its generator stamp: generated, edited after generation, or
// custom (no stamp). Verification happens here, from the stored bytes —
// the label is never taken on trust from the runner.
func loadSQLiteReportSpecProvenance(db *sql.DB, doc *SQLiteReportDocument) error {
var content string
err := db.QueryRow(`SELECT content FROM specs WHERE run_id = ? AND kind = 'original'`, doc.Run.ID).Scan(&content)
if err == sql.ErrNoRows {
doc.SpecProvenance = artifact.SpecProvenanceCustom
return nil
}
if err != nil {
return err
}
status, stamp := artifact.VerifySpecProvenance([]byte(content))
doc.SpecProvenance = status
doc.SpecGenerator = stamp
if status == artifact.SpecProvenanceGenerated && stamp != nil && len(stamp.Intent) > 0 {
var intent struct {
Concurrency []int `json:"concurrency"`
}
if err := json.Unmarshal(stamp.Intent, &intent); err == nil {
doc.SpecConcurrency = intent.Concurrency
}
}
return nil
}

func specProvenanceDisplay(doc SQLiteReportDocument) string {
switch doc.SpecProvenance {
case artifact.SpecProvenanceGenerated:
return "Generated default sweep (" + doc.SpecGenerator.Tool + ")"
case artifact.SpecProvenanceEdited:
return "Custom grid (edited after generation)"
default:
return "Custom grid (hand-authored)"
}
}

// trimmedThroughputRows synthesizes rows for author-trimmed ladder points so
// declared trims render like adaptive skips, never as silent holes. Only
// verified generated specs are trusted for this.
func trimmedThroughputRows(doc SQLiteReportDocument, existing []SQLiteReportThroughputRow) []SQLiteReportThroughputRow {
if doc.SpecProvenance != artifact.SpecProvenanceGenerated || doc.SpecGenerator == nil {
return nil
}
ladder := doc.SpecConcurrency
if len(ladder) == 0 {
return nil
}
// A real measurement — from any run in a model-level artifact — always
// wins over a synthesized trim marker for the same point.
measured := map[string]struct{}{}
for _, row := range existing {
measured[fmt.Sprintf("%d/%s/%d", row.ContextTarget, row.Mode, row.Concurrency)] = struct{}{}
}
var rows []SQLiteReportThroughputRow
for _, trim := range doc.SpecGenerator.LadderTrims {
label := contextLabel(trim.Context)
for _, concurrency := range ladder {
if concurrency <= trim.MaxConcurrency {
continue
}
for _, mode := range []string{"decode", "prefill"} {
if _, ok := measured[fmt.Sprintf("%d/%s/%d", trim.Context, mode, concurrency)]; ok {
continue
}
rows = append(rows, SQLiteReportThroughputRow{
Phase: bench.PhaseTitle(mode),
Mode: mode,
RunID: doc.Run.ID,
Profile: label,
Model: doc.Run.Name,
ContextWindow: trim.Context,
ContextLabel: "unverified (declared " + label + " active)",
ContextSortKey: trim.Context,
ContextTarget: trim.Context,
ContextSemantics: "active",
Concurrency: concurrency,
Shape: "-",
ThroughputTokS: "trimmed",
PerUserTokS: "trimmed",
TTFTMeanMS: "-",
TTFTP99MS: "-",
LatencyP95MS: "-",
Status: "skipped",
FailureLabel: "trimmed",
FailureReason: "trimmed by author: " + trim.Reason,
Detail: SQLiteReportCellDetail{
Available: true,
Phase: bench.PhaseTitle(mode),
Mode: mode,
Status: "skipped",
FailureLabel: "trimmed",
FailureReason: "trimmed by author: " + trim.Reason,
Profile: label,
ContextLabel: "unverified (declared " + label + " active)",
ContextWindow: trim.Context,
Concurrency: concurrency,
},
})
}
}
}
return rows
}

func sqliteReportMetadataItems(doc SQLiteReportDocument) []SQLiteReportMetadataItem {
items := []SQLiteReportMetadataItem{
{Label: "Spec", Value: specProvenanceDisplay(doc)},
{Label: "Engine", Value: joinUnique(engineSummaries(doc.Engines), ", ")},
{Label: "Runs", Value: fmt.Sprint(len(doc.Runs))},
{Label: "Hardware", Value: bench.FirstNonEmpty(doc.Run.Hardware, "-")},
Expand Down
Loading
Loading