Skip to content

Commit 2d27879

Browse files
committed
feat(metal): MTP confidence-capture instrument — LTHN_MTP_CONF + calibration force mode (#359)
Per drafted token, the drafter's softmax probability of its own greedy pick (host pass over the already-CPU-visible logits row, suppression mirrored) rides AssistantDraftBlockResult.Probs; the greedy generate loop appends one JSONL cycle line pairing probs with the verified accepted-prefix length and carry offset. LTHN_MTP_CONF_FORCE=1 bypasses the low-accept bail, deep bootstrap, and rate-exit verdicts so calibration samples the full regime distribution (the gate's economics select against exactly the cycles the curve needs); both levers off = byte-identical behaviour, zero extra work. Receipts: 26B 370 cycles/1836 drafts + 12B 250/1245 — reliability monotone (top bin 91%/89% match), cumprod truncation at theta=0.40 retains 99.2%/99.1% of accepted tokens for 8-9% draft work saved on both drafters; 40%/30% of cycles accept the full K=4 with median end-of-block cumprod 0.995/0.979 (the cap raise is the bigger lever, especially at depth: 83% acceptance at 16K). Co-Authored-By: Virgil <virgil@lethean.io>
1 parent d3700b1 commit 2d27879

3 files changed

Lines changed: 247 additions & 11 deletions

File tree

go/engine/metal/assistant_load.go

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,12 @@ type AssistantDraftStepResult struct {
120120
}
121121

122122
// AssistantDraftBlockResult is a chained native assistant proposal block.
123+
// Probs is nil unless the LTHN_MTP_CONF capture is armed (mtp_conf_diag.go):
124+
// per drafted position, the drafter's softmax probability of its own pick.
123125
type AssistantDraftBlockResult struct {
124126
Tokens []int32
125127
Hidden []byte
128+
Probs []float32
126129
}
127130

128131
// AssistantVerifyResult reports target-side verification of a proposed
@@ -1210,6 +1213,10 @@ func (pair *AssistantPair) draftBlockFromSessionWithSuppress(target *ArchSession
12101213
} else {
12111214
tokens = target.mtpDraftTokenScratch(maxDraftTokens)
12121215
}
1216+
var confProbs []float32
1217+
if mtpConfEnabled() {
1218+
confProbs = make([]float32, 0, maxDraftTokens)
1219+
}
12131220
fused := pair.fusedDraft()
12141221
if fused != nil {
12151222
if err := fused.loadKV(targetKVs); err != nil {
@@ -1263,6 +1270,9 @@ func (pair *AssistantPair) draftBlockFromSessionWithSuppress(target *ArchSession
12631270
nativeTraceLog(core.Sprintf("mtp-diag draft step %d: tok=%d fwd=%.1fms head=%.1fms greedy=%.1fms normed{%s}\n",
12641271
len(tokens), tok, fwdMs, headMs, greedyMs, mtpDiagBF16Stats(normed)))
12651272
}
1273+
if confProbs != nil {
1274+
confProbs = append(confProbs, mtpConfProb(logits, tok, suppress))
1275+
}
12661276
stepToken, currentHidden = tok, nextHidden
12671277
} else {
12681278
projectedOut := target.mtpProjectionScratch(pair.Assistant.Arch.Hidden * bf16Size)
@@ -1279,6 +1289,9 @@ func (pair *AssistantPair) draftBlockFromSessionWithSuppress(target *ArchSession
12791289
nativeTraceLog(core.Sprintf("mtp-diag draft step %d: tok=%d projected{%s} logits{%s}\n",
12801290
len(tokens), step.Token, mtpDiagBF16Stats(projected), mtpDiagBF16Stats(logitsOut)))
12811291
}
1292+
if confProbs != nil {
1293+
confProbs = append(confProbs, mtpConfProb(logitsOut, step.Token, suppress))
1294+
}
12821295
stepToken, currentHidden = step.Token, step.Hidden
12831296
}
12841297
tokens = append(tokens, stepToken)
@@ -1287,7 +1300,7 @@ func (pair *AssistantPair) draftBlockFromSessionWithSuppress(target *ArchSession
12871300
if !copyTokens {
12881301
target.mtpDraftTokens = tokens
12891302
}
1290-
return AssistantDraftBlockResult{Tokens: tokens, Hidden: currentHidden}, nil
1303+
return AssistantDraftBlockResult{Tokens: tokens, Hidden: currentHidden, Probs: confProbs}, nil
12911304
}
12921305

12931306
func (pair *AssistantPair) draftBlockSampledFromSessionWithSuppress(target *ArchSession, lastToken int32, maxDraftTokens int, copyTokens bool, params model.SampleParams, sampler *model.Sampler) (AssistantDraftBlockResult, error) {
@@ -1765,6 +1778,13 @@ func (pair *AssistantPair) GenerateFromSessionEach(target *ArchSession, promptID
17651778
nativeTraceLog(core.Sprintf("mtp-diag verify %d: draft=%.1fms verify=%.1fms last=%d block=%v accepted=%v replacement=%d allAccepted=%v\n",
17661779
result.TargetVerifyCalls, diagDraftMs, total-diagDraftMs, lastToken, block, verify.AcceptedTokens, verify.ReplacementToken, verify.AllAccepted))
17671780
}
1781+
if draft.Probs != nil {
1782+
off := 0
1783+
if carryPresent {
1784+
off = 1
1785+
}
1786+
mtpConfRecordCycle(posBeforeVerify, off, draft.Probs, verify.AcceptedCount)
1787+
}
17681788
emitStart := 0
17691789
if carryPresent && len(verify.AcceptedTokens) > 0 && verify.AcceptedTokens[0] == carryLead {
17701790
emitStart = 1
@@ -1801,7 +1821,7 @@ func (pair *AssistantPair) GenerateFromSessionEach(target *ArchSession, promptID
18011821
if verify.AllAccepted {
18021822
lowAcceptStreak = 0
18031823
carryLead = -1
1804-
if !wasProbing && reng.needsDeepBootstrap(target.pos, len(result.Tokens), maxNew) {
1824+
if !mtpConfForce && !wasProbing && reng.needsDeepBootstrap(target.pos, len(result.Tokens), maxNew) {
18051825
if mtpDiagForTest {
18061826
nativeTraceLog(core.Sprintf("mtp-diag reengage deep-bootstrap: pos=%d emitted=%d\n", target.pos, len(result.Tokens)))
18071827
}
@@ -1819,7 +1839,7 @@ func (pair *AssistantPair) GenerateFromSessionEach(target *ArchSession, promptID
18191839
} else {
18201840
bail = reng.engagedCycle(newDrafts, time.Since(cycleT0).Seconds(), len(result.Tokens))
18211841
}
1822-
if bail {
1842+
if bail && !mtpConfForce {
18231843
if done, perr := runPlainStretch(carryLead); perr != nil {
18241844
return result, perr
18251845
} else if done {
@@ -1842,7 +1862,7 @@ func (pair *AssistantPair) GenerateFromSessionEach(target *ArchSession, promptID
18421862
}
18431863
// Give up on drafting only after the drafter has stayed weak for several
18441864
// consecutive blocks — one near-tie block is transient, not a mismatched pair.
1845-
if !stopped && len(result.Tokens) < maxNew && lowAcceptStreak >= nativeAssistantLowAcceptPatience {
1865+
if !mtpConfForce && !stopped && len(result.Tokens) < maxNew && lowAcceptStreak >= nativeAssistantLowAcceptPatience {
18461866
if mtpReengageDisabled {
18471867
if err := nativeAssistantFinishLowAcceptFromTargetCache(target, &result, replacement, maxNew, eosID, suppress, yield); err != nil {
18481868
return result, err
@@ -1864,7 +1884,7 @@ func (pair *AssistantPair) GenerateFromSessionEach(target *ArchSession, promptID
18641884
if stopped {
18651885
break
18661886
}
1867-
if !wasProbing && reng.needsDeepBootstrap(target.pos, len(result.Tokens), maxNew) {
1887+
if !mtpConfForce && !wasProbing && reng.needsDeepBootstrap(target.pos, len(result.Tokens), maxNew) {
18681888
if mtpDiagForTest {
18691889
nativeTraceLog(core.Sprintf("mtp-diag reengage deep-bootstrap: pos=%d emitted=%d\n", target.pos, len(result.Tokens)))
18701890
}
@@ -1882,7 +1902,7 @@ func (pair *AssistantPair) GenerateFromSessionEach(target *ArchSession, promptID
18821902
} else {
18831903
bail = reng.engagedCycle(newDrafts+1, time.Since(cycleT0).Seconds(), len(result.Tokens))
18841904
}
1885-
if bail {
1905+
if bail && !mtpConfForce {
18861906
if done, perr := runPlainStretch(replacement); perr != nil {
18871907
return result, perr
18881908
} else if done {
@@ -2026,7 +2046,7 @@ func (pair *AssistantPair) GenerateSampledFromSessionEach(target *ArchSession, p
20262046
if verify.AllAccepted {
20272047
lowAcceptStreak = 0
20282048
carryLead = -1
2029-
if !wasProbing && reng.needsDeepBootstrap(target.pos, len(result.Tokens), maxNew) {
2049+
if !mtpConfForce && !wasProbing && reng.needsDeepBootstrap(target.pos, len(result.Tokens), maxNew) {
20302050
if mtpDiagForTest {
20312051
nativeTraceLog(core.Sprintf("mtp-diag reengage deep-bootstrap: pos=%d emitted=%d\n", target.pos, len(result.Tokens)))
20322052
}
@@ -2044,7 +2064,7 @@ func (pair *AssistantPair) GenerateSampledFromSessionEach(target *ArchSession, p
20442064
} else {
20452065
bail = reng.engagedCycle(newDrafts, time.Since(cycleT0).Seconds(), len(result.Tokens))
20462066
}
2047-
if bail {
2067+
if bail && !mtpConfForce {
20482068
if done, perr := runPlainStretch(carryLead); perr != nil {
20492069
return result, perr
20502070
} else if done {
@@ -2082,7 +2102,7 @@ func (pair *AssistantPair) GenerateSampledFromSessionEach(target *ArchSession, p
20822102
}
20832103
// One weak block is a transient near-tie, not a mismatched pair — only fall
20842104
// back to plain target decode after several consecutive weak blocks.
2085-
if !stopped && len(result.Tokens) < maxNew && lowAcceptStreak >= nativeAssistantLowAcceptPatience {
2105+
if !mtpConfForce && !stopped && len(result.Tokens) < maxNew && lowAcceptStreak >= nativeAssistantLowAcceptPatience {
20862106
if mtpReengageDisabled {
20872107
var err error
20882108
history, err = nativeAssistantFinishLowAcceptSampledFromTargetCache(target, &result, replacement, maxNew, stopTokens, sampler, params, history, yield)
@@ -2107,7 +2127,7 @@ func (pair *AssistantPair) GenerateSampledFromSessionEach(target *ArchSession, p
21072127
if stopped {
21082128
break
21092129
}
2110-
if !wasProbing && reng.needsDeepBootstrap(target.pos, len(result.Tokens), maxNew) {
2130+
if !mtpConfForce && !wasProbing && reng.needsDeepBootstrap(target.pos, len(result.Tokens), maxNew) {
21112131
if mtpDiagForTest {
21122132
nativeTraceLog(core.Sprintf("mtp-diag reengage deep-bootstrap: pos=%d emitted=%d\n", target.pos, len(result.Tokens)))
21132133
}
@@ -2125,7 +2145,7 @@ func (pair *AssistantPair) GenerateSampledFromSessionEach(target *ArchSession, p
21252145
} else {
21262146
bail = reng.engagedCycle(newDrafts+1, time.Since(cycleT0).Seconds(), len(result.Tokens))
21272147
}
2128-
if bail {
2148+
if bail && !mtpConfForce {
21292149
if done, perr := runPlainStretch(replacement); perr != nil {
21302150
return result, perr
21312151
} else if done {

go/engine/metal/mtp_conf_diag.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// SPDX-Licence-Identifier: EUPL-1.2
2+
3+
//go:build darwin && arm64
4+
5+
package native
6+
7+
import (
8+
"math"
9+
"os"
10+
"strconv"
11+
"sync"
12+
13+
core "dappco.re/go"
14+
)
15+
16+
// LTHN_MTP_CONF=<path> — the #359 instrument: append one JSON line per MTP
17+
// verify cycle pairing the drafter's per-position self-confidence (softmax
18+
// probability of its own greedy pick) with the target's accepted prefix
19+
// length. The reliability curve derived from this capture is where the
20+
// confidence-scheduling threshold θ comes from — DSpark's 0.4 is calibrated
21+
// to their trained confidence head on Qwen3, not to a gemma4 drafter's raw
22+
// softmax, so we measure our own. Diag-only: when unset the draft loop takes
23+
// zero extra work (no softmax, no allocation, no I/O). Greedy lane only —
24+
// the sampled draft lane is not instrumented.
25+
var mtpConfCapturePath = os.Getenv("LTHN_MTP_CONF")
26+
27+
func mtpConfEnabled() bool { return mtpConfCapturePath != "" }
28+
29+
// LTHN_MTP_CONF_FORCE=1 — calibration mode (greedy lane): with the capture
30+
// armed, draft every cycle by bypassing the low-accept bail and the deep
31+
// bootstrap. The re-engagement gate's economics select AGAINST the very
32+
// cycles the curve needs (it stops drafting exactly where the drafter is
33+
// weak), so an unforced capture oversamples the good regimes. Never set on a
34+
// serving run — it deliberately trades tok/s for unbiased coverage.
35+
var mtpConfForce = os.Getenv("LTHN_MTP_CONF_FORCE") == "1" && mtpConfCapturePath != ""
36+
37+
// mtpConfProb returns softmax probability of tokID over a host bf16 logits
38+
// row, mirroring draftGreedyTokenWithSuppress's iteration exactly: suppressed
39+
// ids are excluded from the distribution (the greedy pick was made without
40+
// them, so its confidence must be too). Two passes, f64 accumulation.
41+
func mtpConfProb(logits []byte, tokID int32, suppressed []int32) float32 {
42+
vocab := len(logits) / bf16Size
43+
if vocab == 0 || tokID < 0 || int(tokID) >= vocab {
44+
return 0
45+
}
46+
maxV := float32(math.Inf(-1))
47+
for id := range vocab {
48+
if nativeAssistantSuppressed(int32(id), suppressed) {
49+
continue
50+
}
51+
if v := bf16ToF32(logits[id*bf16Size], logits[id*bf16Size+1]); v > maxV {
52+
maxV = v
53+
}
54+
}
55+
var sum float64
56+
for id := range vocab {
57+
if nativeAssistantSuppressed(int32(id), suppressed) {
58+
continue
59+
}
60+
v := bf16ToF32(logits[id*bf16Size], logits[id*bf16Size+1])
61+
sum += math.Exp(float64(v - maxV))
62+
}
63+
if sum <= 0 {
64+
return 0
65+
}
66+
tok := bf16ToF32(logits[tokID*bf16Size], logits[tokID*bf16Size+1])
67+
return float32(math.Exp(float64(tok-maxV)) / sum)
68+
}
69+
70+
// mtpConfSink is the process-lifetime append writer. Open is lazy so serving
71+
// binaries that never draft pay nothing; a failed open disables capture for
72+
// the rest of the process (a diag lever must never break generation).
73+
type mtpConfSink struct {
74+
mu sync.Mutex
75+
f *os.File
76+
dead bool
77+
}
78+
79+
var mtpConfOut mtpConfSink
80+
81+
// mtpConfRecordCycle appends one cycle line:
82+
//
83+
// {"pos":18432,"carry":1,"probs":[0.9531,0.8125,0.4023,0.1201],"accepted":3}
84+
//
85+
// pos is the target position before verify; carry is the block offset (a
86+
// pending lead token verified ahead of the draft), so drafted position k is
87+
// prefix-accepted iff k+carry < accepted. Labels are derived at analysis
88+
// time; the line stores raw facts only.
89+
func mtpConfRecordCycle(pos, carry int, probs []float32, accepted int) {
90+
mtpConfOut.mu.Lock()
91+
defer mtpConfOut.mu.Unlock()
92+
if mtpConfOut.dead {
93+
return
94+
}
95+
if mtpConfOut.f == nil {
96+
f, err := os.OpenFile(mtpConfCapturePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
97+
if err != nil {
98+
mtpConfOut.dead = true
99+
nativeTraceLog(core.Sprintf("mtp-conf: capture disabled, open %s: %v\n", mtpConfCapturePath, err))
100+
return
101+
}
102+
mtpConfOut.f = f
103+
}
104+
buf := make([]byte, 0, 64+12*len(probs))
105+
buf = append(buf, `{"pos":`...)
106+
buf = strconv.AppendInt(buf, int64(pos), 10)
107+
buf = append(buf, `,"carry":`...)
108+
buf = strconv.AppendInt(buf, int64(carry), 10)
109+
buf = append(buf, `,"probs":[`...)
110+
for i, p := range probs {
111+
if i > 0 {
112+
buf = append(buf, ',')
113+
}
114+
buf = strconv.AppendFloat(buf, float64(p), 'f', 4, 32)
115+
}
116+
buf = append(buf, `],"accepted":`...)
117+
buf = strconv.AppendInt(buf, int64(accepted), 10)
118+
buf = append(buf, '}', '\n')
119+
if _, err := mtpConfOut.f.Write(buf); err != nil {
120+
mtpConfOut.dead = true
121+
nativeTraceLog(core.Sprintf("mtp-conf: capture disabled, write: %v\n", err))
122+
}
123+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// SPDX-Licence-Identifier: EUPL-1.2
2+
3+
//go:build darwin && arm64
4+
5+
package native
6+
7+
import (
8+
"math"
9+
"os"
10+
"path/filepath"
11+
"testing"
12+
)
13+
14+
func mtpConfTestRow(logits []float32) []byte {
15+
row := make([]byte, len(logits)*bf16Size)
16+
for i, v := range logits {
17+
h := f32ToBF16(v)
18+
row[i*bf16Size] = byte(h)
19+
row[i*bf16Size+1] = byte(h >> 8)
20+
}
21+
return row
22+
}
23+
24+
// TestMTPConfProb pins the softmax probability against a hand-computed
25+
// distribution on a 4-token row (all values exactly representable in bf16).
26+
func TestMTPConfProb(t *testing.T) {
27+
row := mtpConfTestRow([]float32{2, 1, 0, -1})
28+
var sum float64
29+
for _, v := range []float64{2, 1, 0, -1} {
30+
sum += math.Exp(v - 2)
31+
}
32+
want := float32(math.Exp(0) / sum)
33+
got := mtpConfProb(row, 0, nil)
34+
if math.Abs(float64(got-want)) > 1e-6 {
35+
t.Fatalf("prob(top) = %v, want %v", got, want)
36+
}
37+
wantLow := float32(math.Exp(-3) / sum)
38+
if gotLow := mtpConfProb(row, 3, nil); math.Abs(float64(gotLow-wantLow)) > 1e-6 {
39+
t.Fatalf("prob(bottom) = %v, want %v", gotLow, wantLow)
40+
}
41+
if bad := mtpConfProb(row, 4, nil); bad != 0 {
42+
t.Fatalf("prob(out of range) = %v, want 0", bad)
43+
}
44+
}
45+
46+
// TestMTPConfProbSuppressed pins that suppressed ids are excluded from the
47+
// distribution, mirroring draftGreedyTokenWithSuppress: with the top token
48+
// suppressed, the runner-up's probability is over the remaining support.
49+
func TestMTPConfProbSuppressed(t *testing.T) {
50+
row := mtpConfTestRow([]float32{2, 1, 0, -1})
51+
var sum float64
52+
for _, v := range []float64{1, 0, -1} {
53+
sum += math.Exp(v - 1)
54+
}
55+
want := float32(math.Exp(0) / sum)
56+
got := mtpConfProb(row, 1, []int32{0})
57+
if math.Abs(float64(got-want)) > 1e-6 {
58+
t.Fatalf("prob(runner-up, top suppressed) = %v, want %v", got, want)
59+
}
60+
}
61+
62+
// TestMTPConfRecordCycle pins the JSONL line format byte-exactly and that the
63+
// sink appends across calls.
64+
func TestMTPConfRecordCycle(t *testing.T) {
65+
path := filepath.Join(t.TempDir(), "conf.jsonl")
66+
savedPath := mtpConfCapturePath
67+
mtpConfCapturePath = path
68+
mtpConfOut.mu.Lock()
69+
savedF, savedDead := mtpConfOut.f, mtpConfOut.dead
70+
mtpConfOut.f, mtpConfOut.dead = nil, false
71+
mtpConfOut.mu.Unlock()
72+
defer func() {
73+
mtpConfOut.mu.Lock()
74+
if mtpConfOut.f != nil {
75+
mtpConfOut.f.Close()
76+
}
77+
mtpConfOut.f, mtpConfOut.dead = savedF, savedDead
78+
mtpConfOut.mu.Unlock()
79+
mtpConfCapturePath = savedPath
80+
}()
81+
82+
mtpConfRecordCycle(100, 1, []float32{0.5, 0.25}, 2)
83+
mtpConfRecordCycle(104, 0, []float32{0.9990}, 0)
84+
data, err := os.ReadFile(path)
85+
if err != nil {
86+
t.Fatalf("read capture: %v", err)
87+
}
88+
want := `{"pos":100,"carry":1,"probs":[0.5000,0.2500],"accepted":2}` + "\n" +
89+
`{"pos":104,"carry":0,"probs":[0.9990],"accepted":0}` + "\n"
90+
if string(data) != want {
91+
t.Fatalf("capture = %q, want %q", data, want)
92+
}
93+
}

0 commit comments

Comments
 (0)