Skip to content
Draft
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
2 changes: 1 addition & 1 deletion decentralized-api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
cosmossdk.io/x/feegrant v0.1.1
cosmossdk.io/x/upgrade v0.1.4
devshard v0.0.0-00010101000000-000000000000
github.com/cockroachdb/apd/v2 v2.0.2
github.com/cometbft/cometbft v0.38.21
github.com/consensys/gnark-crypto v0.18.1
github.com/cosmos/btcutil v1.0.5
Expand Down Expand Up @@ -103,7 +104,6 @@ require (
github.com/chzyer/readline v1.5.1 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
github.com/cockroachdb/errors v1.12.0 // indirect
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect
Expand Down
114 changes: 114 additions & 0 deletions decentralized-api/internal/validation/conformance_vectors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package validation

// Cross-language conformance harness for deterministic sampling.
//
// testdata/conformance_vectors.json is generated from the vLLM reference
// implementation (see DETERMINISTIC_SAMPLING_CONTRACT.md) and is the executable
// form of the contract. The Go validator MUST reproduce every vector bit-for-bit
// before it can be trusted for cross-language sampling replay.
//
// Status (item ①): this harness loads and validates the vectors' invariants so
// the fixture is wired in and CI-checked. The actual Go decimal pipeline + RNG
// (item ②) is not implemented yet; TestConformanceReplay skips until it lands,
// at which point it becomes the gate for cross-language parity.

import (
"encoding/json"
"os"
"path/filepath"
"testing"
)

type conformanceCase struct {
Name string `json:"name"`
Logprobs map[string]string `json:"logprobs"`
Temperature string `json:"temperature"`
TopP *string `json:"top_p"`
TopK *int `json:"top_k"`
MinP *string `json:"min_p"`
SeedStr string `json:"seed_str"`
ExpectedWeights map[string]int64 `json:"expected_weights"`
ExpectedWeightSum int64 `json:"expected_weight_sum"`
ExpectedToken string `json:"expected_token"`
}

type conformanceVectors struct {
ContractVersion string `json:"contract_version"`
WeightScale int64 `json:"weight_scale"`
RNGReference struct {
Seed string `json:"seed"`
FirstU64 []uint64 `json:"first_u64"`
} `json:"rng_reference"`
Cases []conformanceCase `json:"cases"`
}

func loadConformanceVectors(t *testing.T) conformanceVectors {
t.Helper()
path := filepath.Join("testdata", "conformance_vectors.json")
raw, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
var v conformanceVectors
if err := json.Unmarshal(raw, &v); err != nil {
t.Fatalf("unmarshal %s: %v", path, err)
}
return v
}

// TestConformanceVectorsWellFormed locks the fixture in: it must parse, cover the
// pinned contract constants, and be internally consistent. This runs today.
func TestConformanceVectorsWellFormed(t *testing.T) {
v := loadConformanceVectors(t)

if v.ContractVersion != "1.0.0" {
t.Errorf("contract_version = %q, want 1.0.0", v.ContractVersion)
}
if v.WeightScale != 65536 {
t.Errorf("weight_scale = %d, want 65536 (2^16)", v.WeightScale)
}
if len(v.Cases) == 0 {
t.Fatal("no cases in conformance vectors")
}

// RNG reference vector pinned by the contract (§5).
if len(v.RNGReference.FirstU64) == 0 {
t.Fatal("missing rng_reference.first_u64")
}
const wantFirstU64 = uint64(4286832458236889005)
if got := v.RNGReference.FirstU64[0]; got != wantFirstU64 {
t.Errorf("rng_reference.first_u64[0] = %d, want %d", got, wantFirstU64)
}

for _, c := range v.Cases {
var sum int64
for _, w := range c.ExpectedWeights {
if w < 0 {
t.Errorf("%s: negative weight %d", c.Name, w)
}
sum += w
}
if sum != v.WeightScale {
t.Errorf("%s: weights sum to %d, want %d", c.Name, sum, v.WeightScale)
}
if c.ExpectedWeightSum != v.WeightScale {
t.Errorf("%s: expected_weight_sum = %d, want %d",
c.Name, c.ExpectedWeightSum, v.WeightScale)
}
if _, ok := c.ExpectedWeights[c.ExpectedToken]; !ok {
t.Errorf("%s: expected_token %q not among expected_weights",
c.Name, c.ExpectedToken)
}
}
}

// TestConformanceReplay: the cross-language parity gate is IMPLEMENTED and
// PASSING in the ./detsample sub-package, which hosts the Go decimal pipeline +
// Sha256CounterRNG and is dependency-free so it runs offline. See
// detsample.TestPipelineWeightsMatch (bit-identical integer weights vs the vLLM
// reference) and detsample.TestPipelineTokenMatch (identical sampled token) over
// the same conformance_vectors.json. This placeholder stays skipped; the parent
// `validation` package invokes detsample once the validator is wired to it.
func TestConformanceReplay(t *testing.T) {
t.Skip("parity gate lives in ./detsample (TestPipelineWeightsMatch / TestPipelineTokenMatch)")
}
72 changes: 72 additions & 0 deletions decentralized-api/internal/validation/detsample/distance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package detsample

// Stage-2 distance ("Check 1"). The validator recomputes logprobs on the model
// and compares them to the signed ones; a large distance means the signed
// distribution is not this model's. This mirrors validation_sampling.mae_distance
// (Python) so the chain and vLLM agree on the metric.
//
// The metric is the mean over positions of the mean absolute logprob difference
// over the executor's reported top-K support. GPU-evidence follow-up Experiment 4
// (gonka-ai/gonka#1199): MAE over the top-K support separates honest from a wrong
// model by ~40-65x, while the sampled-token delta alone overlaps and cannot gate.

import (
"math"
"sort"
)

// Stage2MAEFraudThreshold is the fraud cut on MAEDistance. PLACEHOLDER pending
// Decision D (#1199): Experiments 4/5 put the honest floor near ~0.01 MAE and a
// clearly different model near ~0.4-0.8, with int8 quantization a gray zone around
// ~0.1-0.2. 0.25 accepts int8 as "the model" while still catching int4 and cheaper
// models. The final value — and whether int8 counts as the model — is a policy
// decision, NOT a measurement. Not enforcing.
const Stage2MAEFraudThreshold = 0.25

// maeMissingPenalty is charged for a token present on the executor side but absent
// on the validator side, so a truncated/mismatched distribution reads as distant.
const maeMissingPenalty = 10.0

// MAEDistance returns the mean-over-positions MAE over the executor's reported
// support. Returns 10.0 (maximally distant) on a length mismatch, 0.0 on empty.
func MAEDistance(executor, validator []map[string]float64) float64 {
if len(executor) == 0 {
return 0.0
}
if len(executor) != len(validator) {
return 10.0 // length mismatch -> maximally distant
}

perPosition := make([]float64, 0, len(executor))
for i, exec := range executor {
if len(exec) == 0 {
continue
}
val := validator[i]
// Sum over sorted token IDs so the float accumulation order matches the
// Python validator's (byte-exact cross-language distance).
tids := make([]string, 0, len(exec))
for tid := range exec {
tids = append(tids, tid)
}
sort.Strings(tids)
var sum float64
for _, tid := range tids {
if v, ok := val[tid]; ok {
sum += math.Abs(exec[tid] - v)
} else {
sum += maeMissingPenalty
}
}
perPosition = append(perPosition, sum/float64(len(exec)))
}

if len(perPosition) == 0 {
return 0.0
}
var total float64
for _, d := range perPosition {
total += d
}
return total / float64(len(perPosition))
}
43 changes: 43 additions & 0 deletions decentralized-api/internal/validation/detsample/distance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package detsample

import (
"math"
"testing"
)

var distPos0 = map[string]float64{"5": -0.5, "10": -1.2, "2": -2.5, "100": -3.9, "7": -4.1}
var distPos1 = map[string]float64{"3": -0.3, "42": -1.1, "9": -2.2, "11": -3.0, "1": -4.5}

func TestMAEDistanceIdenticalIsZero(t *testing.T) {
if d := MAEDistance([]map[string]float64{distPos0}, []map[string]float64{distPos0}); d != 0.0 {
t.Errorf("MAEDistance identical = %v, want 0", d)
}
}

func TestMAEDistanceUniformShift(t *testing.T) {
shifted := make(map[string]float64, len(distPos0))
for tid, v := range distPos0 {
shifted[tid] = v - 0.5
}
d := MAEDistance([]map[string]float64{distPos0}, []map[string]float64{shifted})
if math.Abs(d-0.5) > 1e-9 {
t.Errorf("MAEDistance uniform 0.5 shift = %v, want 0.5", d)
}
}

func TestMAEDistanceLengthMismatchIsMax(t *testing.T) {
d := MAEDistance(
[]map[string]float64{distPos0, distPos1},
[]map[string]float64{distPos0})
if d != 10.0 {
t.Errorf("MAEDistance length mismatch = %v, want 10", d)
}
}

func TestMAEDistanceMissingTokenPenalized(t *testing.T) {
partial := map[string]float64{"5": -0.5, "10": -1.2, "2": -2.5, "100": -3.9} // one token dropped
d := MAEDistance([]map[string]float64{distPos0}, []map[string]float64{partial})
if d <= Stage2MAEFraudThreshold {
t.Errorf("MAEDistance missing token = %v, want > %v", d, Stage2MAEFraudThreshold)
}
}
Loading