validation/detsample: Go chain-validator for reproducible sampling (Stage-1 replay)#1448
Draft
bonujel wants to merge 11 commits into
Draft
validation/detsample: Go chain-validator for reproducible sampling (Stage-1 replay)#1448bonujel wants to merge 11 commits into
bonujel wants to merge 11 commits into
Conversation
Land the cross-language contract fixtures at the gonka<->vLLM boundary: - testdata/conformance_vectors.json: golden vectors generated from the vLLM reference impl (contract v1.0.0), the executable form of the contract. - DETERMINISTIC_SAMPLING_CONTRACT.md: mirror of the authoritative vLLM contract. - conformance_vectors_test.go: TestConformanceVectorsWellFormed validates the fixture invariants today (weight_scale=2^16, per-case sum, RNG reference). TestConformanceReplay skips pending the Go decimal pipeline + Sha256CounterRNG (item 2), at which point it becomes the cross-language parity gate.
…§5/§6) First cross-language parity result for the deterministic-sampling scheme. The Go Sha256CounterRNG and integer categorical sampler reproduce the Python reference bit-for-bit: TestRNGReference matches the pinned RNG stream, and TestCategoricalReplayFromExpectedWeights feeds every conformance vector's committed expected_weights through the Go sampler and gets the exact expected_token (all 10 cases). Isolated in a stdlib-only sub-package so it compiles and tests offline, free of the parent package's chain deps. This closes the §5/§6 half of cross-language reproducibility; the decimal pipeline (§4, logprobs -> weights) lands next and completes the parity gate (TestConformanceReplay).
…y proven Port the reference logprobs->integer-weights pipeline (contract §4) to Go using cockroachdb/apd (General Decimal Arithmetic, same spec as CPython libmpdec) under prec=10 / ROUND_HALF_EVEN, line-for-line with the Python reference. Result: apd reproduces libmpdec bit-for-bit at prec=10 across all 10 conformance cases — INCLUDING the softmax exp(), the biggest unknown. TestPipelineWeightsMatch asserts identical integer weights; TestPipelineTokenMatch asserts identical sampled tokens (full §4+§5+§6 replay). This empirically closes the S3 cross-language-reproducibility gap the review flagged as untested on either side. Note: apd/v2 is currently an indirect dep (via cosmos-sdk); run 'go mod tidy' on a networked machine to promote it to direct.
… parity S1 step 2/2 (Go side of gonka-ai/vllm#56's derive_chain_bound_seed). DeriveChainBoundSeed reproduces the Python digests bit-for-bit and fails closed on the identical boundary (printable-ASCII 0x21..0x7E, <=256, non-empty id; int64 seed). Verified against the shared seed_derivation vectors: TestChainBoundSeedAccept (5 digests incl. the pinned golden vector, negative seed, int64-max, devshard-style id), TestChainBoundSeedReject (empty / whitespace / control / non-ASCII / too-long), TestChainBoundSeedDomainSeparation. Full detsample suite (sampling + seed) green offline. Seed parity now joins the sampling parity proven earlier; the whole replay surface (seed -> weights -> token) is cross-language bit-identical.
…erdict Add the layer the chain validator calls: VerifyPosition(Position) -> Result, built on the bit-verified primitives. The verdict is Honest / Fraud / Inconclusive — version gating and the greedy (temperature 0) exemption run before any fraud verdict, so a version skew or a validator-side replay error is never punished as executor fraud (the failure-taxonomy gap from the review). Tests over the conformance vectors: every honest case -> Honest; every tampered token -> Fraud; unknown contract/seed version and greedy -> Inconclusive (and a tamper under an unsupported version stays Inconclusive — gating wins). Offline, dependency-free. Wiring this into inference_validation.go is the GPU/networked step; sequence replay waits on the open RNG-semantics item.
…p guard) - Ryanchen911#1 Split Uint64Below into pure rejectionLimit + reduceUnbiased so the reject-and-retry branch is directly testable. Previously the pipeline only ever passed n=2^16 (power of two) so the branch was dead in tests. TestReduceUnbiasedRejects forces it via an injected draw source; TestUint64BelowParity replays the non-power-of-2 vectors vs the Python reference; TestRejectionLimit / TestSampleCategoricalWeightsErrors cover the boundary math and guard paths. - Ryanchen911#2 VerifyPosition now guards temperature <= 0 (or unparseable) explicitly and returns Inconclusive, instead of relying on a downstream divide-by-zero. Full detsample suite green offline. Note (Ryanchen911#3): apd/v2 is still an indirect dep; run 'go mod tidy' on a networked machine to promote it (can't fetch the module graph offline here).
…nchen911#3) detsample imports apd/v2 directly for the decimal pipeline; it was carried as an indirect dep via cosmos-sdk. Promote it so the dependency is declared honestly.
Mirrors the Python serving-side additions (validation_sampling.verify_sequence / mae_distance) so the Go chain validator and the vLLM Python validator reach the same verdict from the same artifact — restoring Python<->Go parity at the sequence level, not just the primitives (gonka-ai#1199 follow-up). - VerifySequence: replays a whole response over VerifyPosition, composing each position's seed as "<base>|<pos>" (Decision B = per-position, byte-identical to the Python side), and aggregates three-valued: any Fraud -> Fraud (first position); else >=1 Honest -> Honest (Inconclusive positions defer to Stage-2); else all Inconclusive. Version/seed-domain/greedy gating and the unbounded- support gate (supportIsBounded: bounded iff top_k or min_p active) run before any per-position replay, so a request the validator cannot cover never yields Fraud. - MAEDistance: Stage-2 metric as mean-over-positions MAE over the reported top-K support (Experiment 4), replacing the relative-diff ratio. Stage2MAEFraud Threshold is a documented Decision-D placeholder, non-enforcing. 11 table tests (honest/fraud/inconclusive/unbounded/greedy/version/missing- logprobs aggregation + distance). go test ./internal/validation/detsample/ green. Refs: gonka-ai#1199
…byte-parity Consumes the sequence_cases / distance_cases the vLLM reference added to the shared conformance_vectors.json (byte-identical file), extending Python<->Go parity from the primitives to the sequence layer: VerifySequence reproduces the per-position-seed aggregation and MAEDistance reproduces the Stage-2 distance bit-for-bit (gonka-ai#1199 follow-up). - MAEDistance: sum over sorted token IDs so the float64 accumulation order matches the Python reference exactly; the conformance test asserts exact equality (e.g. uniform_shift = 0.5000000000000001). - sequence_conformance_test.go: TestSequenceConformance / TestDistanceConformance drive VerifySequence / MAEDistance over every committed case. Refs: gonka-ai#1199
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
The gonka half of the two-stage inference-validation design in #1199: the Go chain-validator that replays reproducible sampling artifacts, plus the shared cross-language conformance vectors that prove it is byte-for-byte identical to the vLLM-side Python validator.
This PR is:
decentralized-api/internal/validation/detsample/deterministic-sampling-clean→main), which carries the executor, the Python validator, and the GPU evidenceIt is the gonka half of a symmetric pair: vLLM produces reproducible sampling artifacts and a Python validator; this package is the Go validator the chain runs. Both reduce the same signed logprobs to the same sampled token from the same seed — verified by a common vector file.
How it works
The chain validates an inference in two stages.
Scope of this PR. Stage 1 is complete; Stage 2 contributes the distance metric only. Signature verification of the artifact (Decision A) and wiring into consensus are out of scope. In short: this makes honest executor output replayable and provably consistent across languages, not yet enforceable.
What's in the diff
RNG and pipeline
rng.goSha256CounterRNG: portable counter-mode RNG,u64 = first_8_bytes(SHA256(seed ‖ counter_be_u64)).Uint64Below(unbiased[0,n)via rejection sampling) andSampleCategoricalWeightsover integer weights, which raises on an all-zero weight vector instead of a silent fallback.pipeline.goLogprobsToWeights/DecimalSampleFromLogprobs: the decimal pipeline viacockroachdb/apd/v2.top_k/top_p/min_pfilter → renormalize → integer weights summing to exactly2^16(65536).[65530, 65541]and false-rejects 14–77% of honest positions; the decimal total is exactly2^16, giving 0/128 false-rejects (E1).Seed derivation
seed.goDeriveChainBoundSeed(userSeed int64, inferenceID string): Go side of fix: bind deterministic sampling seed to chain inference id vllm#56, mirroring the Pythonderive_chain_bound_seedbyte-for-byte.gonka-deterministic-sampling-v1), byte-length-prefixed framing, fail-closed: rejects empty,>256-char, and non-printable-ASCII inference IDs.Verdict — single position and sequence
verify.goVerifyPosition(Position) Result: single-position replay with a three-valuedVerdict {honest, fraud, inconclusive}.sequence.goVerifySequence(SequenceRequest, []SequencePosition) SequenceResult: sequence-level Stage-1 orchestration.base|pos, loopsVerifyPosition, and aggregates: any Fraud → Fraud; else at least one Honest → Honest; else Inconclusive.verify_sequence.Stage-2 distance metric
distance.goMAEDistance(executor, validator): Stage-2 distance as MAE over the signed top-K support, with sorted-key summation for byte-exact cross-language equality.Stage2MAEFraudThresholdis a documented placeholder (Decision D); nothing here enforces. Mirrors the Pythonmae_distance.Cross-language conformance (S3)
testdata/conformance_vectors.json+conformance_vectors_test.go+sequence_conformance_test.goUint64Belowsequences, float→string canonicalization, seed-derivation reference, pipeline cases, and now sequence-level and distance cases too.0.5000000000000001matches exactly — closing the previously-untested cross-language gap (S3).Testing
go test ./internal/validation/detsample/is green. Coverage:rng_test.go,pipeline_test.goseed_test.go— domain-separation and fail-closed invariantsverify_test.go— three-valued verdict pathssequence_test.go— aggregation across honest / fraud / inconclusive / unbounded / greedy / version / missing-logprobsdistance_test.goreject_test.go— rejection-sampling pathsconformance_vectors_test.go,sequence_conformance_test.go— the shared vectorsStill open — do not merge for enforcement
responseHash, which tokens' logprobs are signed (the filter's kept set, not a fixed K), canonicalization, filter-edge rules, and a version descriptor.SupportedContractVersion = "1.0.0"is a placeholder pending A; everything downstream depends on it.inference_validation.godoes not calldetsampleyet. Turning it on is gated on Decision A and enforcement gating (Decision D).DeriveChainBoundSeedbinds(user_seed, inference_id).inference_id(via fix: bind deterministic sampling seed to chain inference id vllm#56) removes the executor-choosable hole, butuser_seedstays request-controlled, leaving residual offline grinding. Bind inputs the executor cannot pick (executor address / block hash) or commituser_seedon-chain.MAEDistance) is settled; the threshold value, epochs, and cross-architecture CI gate are policy.prompt_hash.Note: RNG semantics (Decision B) are resolved to per-position independent seeding (
base|pos), matched by the executor on the vLLM side.Companion PR
The vLLM side — the executor sampling fix, the Python validator, and the GPU evidence — is gonka-ai/vllm#62.
Refs: #1199