Spaces:
Running
Running
Add five measured experiments and public CPU reproduction
#3
by alphabell-research - opened
This view is limited to 50 files because it contains too many changes. See the raw diff here.
- .gitattributes +4 -0
- .github/workflows/checks.yml +1 -0
- .gitignore +1 -0
- NOTICE +7 -1
- README.md +23 -3
- app.js +10 -9
- experiments/articles/01-one-word-routing.md +35 -0
- experiments/articles/02-blind-quantization.md +29 -0
- experiments/articles/03-activation-surgery.md +31 -0
- experiments/articles/04-correction-detective.md +31 -0
- experiments/articles/05-model-music.md +41 -0
- experiments/articles/README.md +13 -0
- experiments/assets/01-routing.png +0 -0
- experiments/assets/02-quantization.png +0 -0
- experiments/assets/03-surgery.png +0 -0
- experiments/assets/04-memory.png +0 -0
- experiments/assets/05-music.png +0 -0
- experiments/assets/flint-music.wav +3 -0
- experiments/assets/flint-music.webm +3 -0
- experiments/assets/media.json +27 -0
- experiments/assets/qwen-open-music.wav +3 -0
- experiments/assets/qwen-open-music.webm +3 -0
- experiments/data/results.json +0 -0
- experiments/data/summary.json +18 -0
- experiments/data/traces.json +314 -0
- experiments/index.html +22 -0
- experiments/lab.css +3 -0
- experiments/lab.js +325 -0
- experiments/reproduce/LLAMA-LICENSE +21 -0
- experiments/reproduce/LLAMA-MIT-LICENSE +21 -0
- experiments/reproduce/README.md +94 -0
- experiments/reproduce/capture-study-build.sh +17 -0
- experiments/reproduce/capture-study-verification.json +168 -0
- experiments/reproduce/capture-study.cpp +134 -0
- experiments/reproduce/capture-study.py +102 -0
- experiments/reproduce/checkpoints.json +30 -0
- experiments/reproduce/flint-capture-equivalence.json +33 -0
- experiments/reproduce/llama-flint-runtime.patch +704 -0
- experiments/reproduce/quant-surgery-build.sh +17 -0
- experiments/reproduce/quant-surgery.cpp +112 -0
- experiments/reproduce/quant-surgery.py +129 -0
- experiments/sound.css +4 -0
- experiments/sound.html +40 -0
- experiments/sound.js +186 -0
- experiments/sound.test.cjs +60 -0
- index.html +1 -1
- models.json +4 -3
- package.json +1 -1
- style.css +2 -0
- tests/experiment-data.test.cjs +78 -0
.gitattributes
CHANGED
|
@@ -35,3 +35,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
cover.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
preview.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
cover.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
preview.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
experiments/assets/flint-music.wav filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
experiments/assets/flint-music.webm filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
experiments/assets/qwen-open-music.wav filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
experiments/assets/qwen-open-music.webm filter=lfs diff=lfs merge=lfs -text
|
.github/workflows/checks.yml
CHANGED
|
@@ -30,3 +30,4 @@ jobs:
|
|
| 30 |
browser_path="$(command -v google-chrome || command -v chromium)"
|
| 31 |
"$browser_path" --version
|
| 32 |
python verify.py --base http://127.0.0.1:8000 --chromium "$browser_path"
|
|
|
|
|
|
| 30 |
browser_path="$(command -v google-chrome || command -v chromium)"
|
| 31 |
"$browser_path" --version
|
| 32 |
python verify.py --base http://127.0.0.1:8000 --chromium "$browser_path"
|
| 33 |
+
python verify-experiments.py --base http://127.0.0.1:8000 --chromium "$browser_path" --out reports-experiments
|
.gitignore
CHANGED
|
@@ -3,3 +3,4 @@ __pycache__/
|
|
| 3 |
node_modules/
|
| 4 |
reports/
|
| 5 |
.DS_Store
|
|
|
|
|
|
| 3 |
node_modules/
|
| 4 |
reports/
|
| 5 |
.DS_Store
|
| 6 |
+
reports-experiments/
|
NOTICE
CHANGED
|
@@ -16,4 +16,10 @@ with this viewer. No model weights are included.
|
|
| 16 |
|
| 17 |
Capture engine reference: llama.cpp, MIT License
|
| 18 |
https://github.com/ggml-org/llama.cpp
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
Capture engine reference: llama.cpp, MIT License
|
| 18 |
https://github.com/ggml-org/llama.cpp
|
| 19 |
+
A source patch and experiment harness are included in experiments/reproduce.
|
| 20 |
+
The patch applies to the pinned public llama.cpp source and retains its MIT license.
|
| 21 |
+
No compiled inference executable or model weights are included in this Space.
|
| 22 |
+
|
| 23 |
+
Qwen measurements use alphaBell Qwen Open, a post-trained derivative.
|
| 24 |
+
Its separate checkpoint release uses alphaBell's research and noncommercial license.
|
| 25 |
+
Parent architecture configurations are distinct from captured checkpoint identity.
|
README.md
CHANGED
|
@@ -12,6 +12,8 @@ thumbnail: https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/mai
|
|
| 12 |
models:
|
| 13 |
- RecursiveSI/flint
|
| 14 |
- Qwen/Qwen3.8-27B
|
|
|
|
|
|
|
| 15 |
tags:
|
| 16 |
- interpretability
|
| 17 |
- visualization
|
|
@@ -27,12 +29,26 @@ tags:
|
|
| 27 |
|
| 28 |
**What changes inside a language model as it writes its next token?**
|
| 29 |
|
| 30 |
-
Explore two different architectures with real Q8_0 tensor captures: Flint's sparse expert routing and Qwen's recurrent memory. Open a block, head or expert, then step through the tokens that used it.
|
| 31 |
|
| 32 |
[**Open the explorer**](https://huggingface.co/spaces/alphabell/Model-Observatory) · [Qwen view](https://alphabell-model-observatory.static.hf.space/?model=qwen) · [Source](https://huggingface.co/spaces/alphabell/Model-Observatory/tree/main) · [alphaBell](https://alphabell.com)
|
| 33 |
|
| 34 |

|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
## Try it
|
| 37 |
|
| 38 |
1. Open **Flint**. Choose a token, then click one of the six selected experts in layer 12. Its timeline shows when the router selected it.
|
|
@@ -44,7 +60,7 @@ The two examples use the same prompt, “Explain why the sky is blue in two sent
|
|
| 44 |
|
| 45 |
## Two architectures
|
| 46 |
|
| 47 |
-
| | Recursive Flint |
|
| 48 |
| --- | --- | --- |
|
| 49 |
| Decoder blocks | 49 | 64 |
|
| 50 |
| Attention | 14 full, 35 sliding | 48 Gated DeltaNet, 16 full |
|
|
@@ -64,7 +80,11 @@ Each frame is the forward pass that **predicts** the selected output token. The
|
|
| 64 |
|
| 65 |
## Captures and provenance
|
| 66 |
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
The public Space is a static capture explorer. It does not generate new answers. A separate instrumented capture engine is required to produce additional traces. [Trace format and compatibility](TRACE-FORMAT.md) explains the accepted exports; ordinary chat API responses do not contain these measurements.
|
| 70 |
|
|
|
|
| 12 |
models:
|
| 13 |
- RecursiveSI/flint
|
| 14 |
- Qwen/Qwen3.8-27B
|
| 15 |
+
- alphabell/alphaBell-Qwen3.8-27B-Open-GGUF
|
| 16 |
+
- alphabell/Flint-Matched-GGUF
|
| 17 |
tags:
|
| 18 |
- interpretability
|
| 19 |
- visualization
|
|
|
|
| 29 |
|
| 30 |
**What changes inside a language model as it writes its next token?**
|
| 31 |
|
| 32 |
+
Explore two different architectures with real Q8_0 tensor captures: Flint's sparse expert routing and alphaBell Qwen Open's recurrent memory. Open a block, head or expert, then step through the tokens that used it.
|
| 33 |
|
| 34 |
[**Open the explorer**](https://huggingface.co/spaces/alphabell/Model-Observatory) · [Qwen view](https://alphabell-model-observatory.static.hf.space/?model=qwen) · [Source](https://huggingface.co/spaces/alphabell/Model-Observatory/tree/main) · [alphaBell](https://alphabell.com)
|
| 35 |
|
| 36 |

|
| 37 |
|
| 38 |
+
## Five measured experiments
|
| 39 |
+
|
| 40 |
+
[**Open all five experiments**](https://alphabell-model-observatory.static.hf.space/experiments/)
|
| 41 |
+
|
| 42 |
+
1. [One word changed](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=reroute): twelve captures from three controlled prompt pairs on both models.
|
| 43 |
+
2. [Spot the 4-bit model](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=quant): twelve blinded Q4_1/Q8_0 answer pairs, with measured timing, process memory and exact checks.
|
| 44 |
+
3. [Activation surgery](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=surgery): scale one Flint feed-forward branch to one, half or zero and inspect the downstream effects.
|
| 45 |
+
4. [The correction detective](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=memory): predict answers to current-fact and original-fact questions, then reveal the actual runs.
|
| 46 |
+
5. [Model music](https://alphabell-model-observatory.static.hf.space/experiments/sound.html): hear deterministic sonification of real routing and recurrent-head values, with synchronized replay and WAV downloads.
|
| 47 |
+
|
| 48 |
+
The experiments contain 60 new model generations. Eleven of twelve quantization pairs returned identical text; the differing arithmetic pair was wrong at both precisions. The activation study retains all six cases and every measured frame. Both models passed all six short memory tasks. These are small, fixed demonstrations, with one greedy generation per condition.
|
| 49 |
+
|
| 50 |
+
[Results and provenance](experiments/data/results.json) · [Capture manifest](experiments/data/traces.json) · [Reproduction source](experiments/reproduce/) · [Technical articles](experiments/articles/)
|
| 51 |
+
|
| 52 |
## Try it
|
| 53 |
|
| 54 |
1. Open **Flint**. Choose a token, then click one of the six selected experts in layer 12. Its timeline shows when the router selected it.
|
|
|
|
| 60 |
|
| 61 |
## Two architectures
|
| 62 |
|
| 63 |
+
| | Recursive Flint | alphaBell Qwen Open text decoder |
|
| 64 |
| --- | --- | --- |
|
| 65 |
| Decoder blocks | 49 | 64 |
|
| 66 |
| Attention | 14 full, 35 sliding | 48 Gated DeltaNet, 16 full |
|
|
|
|
| 80 |
|
| 81 |
## Captures and provenance
|
| 82 |
|
| 83 |
+
The Qwen participant is alphaBell Qwen Open 27B, a post-trained derivative of Qwen3.8-27B. Its tracing checkpoint was converted from the lab BF16 model to Q8_0; it is not the untouched parent or a dequantization of the public FP8 sibling. The parent configuration identifies architecture, rather than the identity of the captured weights. This applies to the original sky capture as well as the new experiments.
|
| 84 |
+
|
| 85 |
+
The exact matched [Flint](https://huggingface.co/alphabell/Flint-Matched-GGUF) and [Qwen Open](https://huggingface.co/alphabell/alphaBell-Qwen3.8-27B-Open-GGUF) Q4_1/Q8_0 files are separate model repositories. Their licenses remain separate from the viewer code license.
|
| 86 |
+
|
| 87 |
+
Both examples were measured with an instrumented llama.cpp evaluation callback at commit `74686f08`, with Flash Attention disabled. The engine is an assembled source tree rebuilt from the public upstream base and [included patch](experiments/reproduce/README.md). Capture uses native tensor strides and supports reshaped routing weights. The saved metadata records CPU execution, 16 threads and a 2048-token context. Playback speed changes presentation, independently of the original capture timings.
|
| 88 |
|
| 89 |
The public Space is a static capture explorer. It does not generate new answers. A separate instrumented capture engine is required to produce additional traces. [Trace format and compatibility](TRACE-FORMAT.md) explains the accepted exports; ordinary chat API responses do not contain these measurements.
|
| 90 |
|
app.js
CHANGED
|
@@ -140,7 +140,7 @@ async function loadHistory(){
|
|
| 140 |
$('history').replaceChildren();
|
| 141 |
for(const row of examples.filter(r=>r.model===S.model.id)){
|
| 142 |
const b=document.createElement('button');b.className='history-row';b.textContent=row.tokens+' tokens · Sky explanation';b.title=row.prompt;
|
| 143 |
-
b.onclick=()=>openExample(row.model).catch(captureError);$('history').append(b);
|
| 144 |
}
|
| 145 |
}
|
| 146 |
function loadTrace(d){
|
|
@@ -157,7 +157,7 @@ function mapPoint(ev){
|
|
| 157 |
function initControls(){
|
| 158 |
$('shareButton').onclick=async()=>{const url=new URL(location.href);url.searchParams.set('layer',S.layer);url.searchParams.set('token',S.frame+1);try{await navigator.clipboard.writeText(url.href);toast('Example view link copied. Imported traces are not included.');}catch{toast('Copy the address from your browser to share this model.');}};
|
| 159 |
$('modelSelect').onchange=()=>switchModel($('modelSelect').value);$('openBlock').onclick=()=>openDetail('block');$('flowAttention').onclick=()=>openDetail('head');$('flowMlp').onclick=()=>openDetail(isMoe()?'expert':'ffn');$('sharedExpert').onclick=()=>openDetail('shared');
|
| 160 |
-
$('runButton').onclick=()=>{if(S.frames.length){selectFrame(0);startPlayback(false);}else openExample(S.model.id).catch(captureError);};
|
| 161 |
$('prevLayer').onclick=()=>selectLayer(S.layer-1);$('nextLayer').onclick=()=>selectLayer(S.layer+1);$('scrubber').oninput=()=>selectFrame(Number($('scrubber').value));$('stepBack').onclick=()=>selectFrame(S.frame-1);$('stepForward').onclick=()=>selectFrame(S.frame+1);$('liveButton').onclick=()=>selectFrame(S.frames.length-1);$('playButton').onclick=togglePlayback;$('playSpeed').onchange=()=>changeSpeed($('playSpeed').value);
|
| 162 |
$('structureButton').onclick=()=>{S.view='structure';$('structureButton').classList.add('selected');$('historyButton').classList.remove('selected');requestDraw();};$('historyButton').onclick=()=>{S.view='history';$('historyButton').classList.add('selected');$('structureButton').classList.remove('selected');requestDraw();};
|
| 163 |
$('modelCanvas').onkeydown=e=>{if(e.key==='ArrowDown'||e.key==='ArrowUp'){e.preventDefault();selectLayer(S.layer+(e.key==='ArrowDown'?1:-1));}if(e.key==='Enter'){e.preventDefault();openDetail('block');}};$('modelCanvas').onclick=e=>{const p=mapPoint(e);if(!p)return;S.layer=p.layer;S.head=Math.min(S.head,headCount()-1);if(p.frame!==undefined){selectFrame(p.frame);openDetail('block');}else if(p.head!==null&&p.head!==undefined)openDetail('head',p.head);else if(p.expert!==null&&p.expert!==undefined)openDetail('expert',p.expert);else if(p.ffn)openDetail('ffn');else if(isMoe()&&Math.abs(p.x-S.geometry.sx)<9)openDetail('shared');else openDetail('block');};
|
|
@@ -168,10 +168,10 @@ function initControls(){
|
|
| 168 |
}
|
| 169 |
async function init(){
|
| 170 |
try{
|
| 171 |
-
|
| 172 |
$('modelSelect').replaceChildren();for(const m of S.models)$('modelSelect').add(new Option(m.title,m.id));
|
| 173 |
const query=new URL(location.href).searchParams,model=S.models.find(m=>m.id===query.get('model'))||S.models[0];
|
| 174 |
-
initControls();applyModel(model);await loadHistory();await openExample(model.id);
|
| 175 |
const layer=Number(query.get('layer')),frame=Number(query.get('token'));
|
| 176 |
if(query.has('layer')&&Number.isInteger(layer))selectLayer(layer);
|
| 177 |
if(query.has('token')&&Number.isInteger(frame))selectFrame(frame-1);
|
|
@@ -179,30 +179,31 @@ async function init(){
|
|
| 179 |
}catch(e){captureError(e);}
|
| 180 |
}
|
| 181 |
|
| 182 |
-
let examples=[];
|
| 183 |
const traceCache=new Map();
|
| 184 |
async function readJSON(file){const r=await fetch(file);if(!r.ok)throw new Error('Could not load '+file+'. Try reloading.');return r.json();}
|
| 185 |
async function sha256(bytes){return Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256',bytes)),b=>b.toString(16).padStart(2,'0')).join('');}
|
| 186 |
function captureError(e){$('engineStatus').textContent='Trace unavailable';$('runNote').textContent=e.message;$('runButton').disabled=false;$('runButton').textContent='Retry trace';toast(e.message);}
|
| 187 |
function renderCapture(d){
|
| 188 |
if(!d)return;const settings=d.settings||{};
|
|
|
|
| 189 |
$('captureSettings').textContent='Temperature '+(settings.temperature??'unmeasured')+' · seed '+(settings.seed??'unmeasured')+' · reasoning '+(settings.reasoning?'on':'off');
|
| 190 |
$('engineStatus').replaceChildren(document.createElement('i'),document.createTextNode('Measured Q8 replay'));$('engineStatus').classList.add('ready');
|
| 191 |
$('runButton').textContent='▶ Replay from start';$('runButton').disabled=false;
|
| 192 |
$('traceOrigin').textContent=examples.some(e=>e.id===d.id)?'Example capture · '+S.frames.length+' tokens':'Imported capture · '+S.frames.length+' tokens';
|
| 193 |
}
|
| 194 |
-
async function openExample(id){
|
| 195 |
-
if(S.loadingTrace)return;const row=examples.find(r=>r.model===id);if(!row)throw new Error('No example capture is available for this model.');
|
| 196 |
S.loadingTrace=true;$('runButton').disabled=true;$('runNote').textContent='Loading measured capture…';update();
|
| 197 |
try{
|
| 198 |
-
let d=traceCache.get(id);
|
| 199 |
if(!d){
|
| 200 |
const response=await fetch(row.file);if(!response.ok)throw new Error('The example capture could not load. Try again.');
|
| 201 |
const compressed=await response.arrayBuffer();if(await sha256(compressed)!==row.gzip_sha256)throw new Error('Capture checksum failed. Reload to get the latest files.');
|
| 202 |
if(typeof DecompressionStream==='undefined')throw new Error('This browser needs gzip stream support. Open in a current Chrome, Firefox or Safari.');
|
| 203 |
const plain=await new Response(new Blob([compressed]).stream().pipeThrough(new DecompressionStream('gzip'))).arrayBuffer();
|
| 204 |
if(await sha256(plain)!==row.sha256)throw new Error('Decoded capture checksum failed. Reload to try again.');
|
| 205 |
-
d=JSON.parse(new TextDecoder().decode(plain));validateObservatoryTrace(d,S.models);traceCache.set(id,d);
|
| 206 |
}
|
| 207 |
loadTrace(d);selectFrame(0);$('runNote').textContent='Real tensor capture. Press Replay or select a token.';
|
| 208 |
}finally{S.loadingTrace=false;update();}
|
|
|
|
| 140 |
$('history').replaceChildren();
|
| 141 |
for(const row of examples.filter(r=>r.model===S.model.id)){
|
| 142 |
const b=document.createElement('button');b.className='history-row';b.textContent=row.tokens+' tokens · Sky explanation';b.title=row.prompt;
|
| 143 |
+
b.textContent=row.tokens+' tokens · '+(row.label||'Sky explanation');b.onclick=()=>openExample(row.model,row.id).catch(captureError);$('history').append(b);
|
| 144 |
}
|
| 145 |
}
|
| 146 |
function loadTrace(d){
|
|
|
|
| 157 |
function initControls(){
|
| 158 |
$('shareButton').onclick=async()=>{const url=new URL(location.href);url.searchParams.set('layer',S.layer);url.searchParams.set('token',S.frame+1);try{await navigator.clipboard.writeText(url.href);toast('Example view link copied. Imported traces are not included.');}catch{toast('Copy the address from your browser to share this model.');}};
|
| 159 |
$('modelSelect').onchange=()=>switchModel($('modelSelect').value);$('openBlock').onclick=()=>openDetail('block');$('flowAttention').onclick=()=>openDetail('head');$('flowMlp').onclick=()=>openDetail(isMoe()?'expert':'ffn');$('sharedExpert').onclick=()=>openDetail('shared');
|
| 160 |
+
$('runButton').onclick=()=>{if(S.frames.length){selectFrame(0);startPlayback(false);}else openExample(S.model.id,pendingExample).catch(captureError);};
|
| 161 |
$('prevLayer').onclick=()=>selectLayer(S.layer-1);$('nextLayer').onclick=()=>selectLayer(S.layer+1);$('scrubber').oninput=()=>selectFrame(Number($('scrubber').value));$('stepBack').onclick=()=>selectFrame(S.frame-1);$('stepForward').onclick=()=>selectFrame(S.frame+1);$('liveButton').onclick=()=>selectFrame(S.frames.length-1);$('playButton').onclick=togglePlayback;$('playSpeed').onchange=()=>changeSpeed($('playSpeed').value);
|
| 162 |
$('structureButton').onclick=()=>{S.view='structure';$('structureButton').classList.add('selected');$('historyButton').classList.remove('selected');requestDraw();};$('historyButton').onclick=()=>{S.view='history';$('historyButton').classList.add('selected');$('structureButton').classList.remove('selected');requestDraw();};
|
| 163 |
$('modelCanvas').onkeydown=e=>{if(e.key==='ArrowDown'||e.key==='ArrowUp'){e.preventDefault();selectLayer(S.layer+(e.key==='ArrowDown'?1:-1));}if(e.key==='Enter'){e.preventDefault();openDetail('block');}};$('modelCanvas').onclick=e=>{const p=mapPoint(e);if(!p)return;S.layer=p.layer;S.head=Math.min(S.head,headCount()-1);if(p.frame!==undefined){selectFrame(p.frame);openDetail('block');}else if(p.head!==null&&p.head!==undefined)openDetail('head',p.head);else if(p.expert!==null&&p.expert!==undefined)openDetail('expert',p.expert);else if(p.ffn)openDetail('ffn');else if(isMoe()&&Math.abs(p.x-S.geometry.sx)<9)openDetail('shared');else openDetail('block');};
|
|
|
|
| 168 |
}
|
| 169 |
async function init(){
|
| 170 |
try{
|
| 171 |
+
const loaded=await Promise.all([readJSON('models.json'),readJSON('traces/index.json'),readJSON('experiments/data/traces.json')]);S.models=loaded[0];examples=[...loaded[1],...loaded[2]];
|
| 172 |
$('modelSelect').replaceChildren();for(const m of S.models)$('modelSelect').add(new Option(m.title,m.id));
|
| 173 |
const query=new URL(location.href).searchParams,model=S.models.find(m=>m.id===query.get('model'))||S.models[0];
|
| 174 |
+
initControls();applyModel(model);await loadHistory();await openExample(model.id,query.get('capture'));
|
| 175 |
const layer=Number(query.get('layer')),frame=Number(query.get('token'));
|
| 176 |
if(query.has('layer')&&Number.isInteger(layer))selectLayer(layer);
|
| 177 |
if(query.has('token')&&Number.isInteger(frame))selectFrame(frame-1);
|
|
|
|
| 179 |
}catch(e){captureError(e);}
|
| 180 |
}
|
| 181 |
|
| 182 |
+
let examples=[],pendingExample=null;
|
| 183 |
const traceCache=new Map();
|
| 184 |
async function readJSON(file){const r=await fetch(file);if(!r.ok)throw new Error('Could not load '+file+'. Try reloading.');return r.json();}
|
| 185 |
async function sha256(bytes){return Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256',bytes)),b=>b.toString(16).padStart(2,'0')).join('');}
|
| 186 |
function captureError(e){$('engineStatus').textContent='Trace unavailable';$('runNote').textContent=e.message;$('runButton').disabled=false;$('runButton').textContent='Retry trace';toast(e.message);}
|
| 187 |
function renderCapture(d){
|
| 188 |
if(!d)return;const settings=d.settings||{};
|
| 189 |
+
const url=new URL(location.href),row=examples.find(e=>e.id===d.id);if(row&&row.id!==examples.find(e=>e.model===row.model)?.id)url.searchParams.set('capture',row.id);else url.searchParams.delete('capture');history.replaceState({},'',url);
|
| 190 |
$('captureSettings').textContent='Temperature '+(settings.temperature??'unmeasured')+' · seed '+(settings.seed??'unmeasured')+' · reasoning '+(settings.reasoning?'on':'off');
|
| 191 |
$('engineStatus').replaceChildren(document.createElement('i'),document.createTextNode('Measured Q8 replay'));$('engineStatus').classList.add('ready');
|
| 192 |
$('runButton').textContent='▶ Replay from start';$('runButton').disabled=false;
|
| 193 |
$('traceOrigin').textContent=examples.some(e=>e.id===d.id)?'Example capture · '+S.frames.length+' tokens':'Imported capture · '+S.frames.length+' tokens';
|
| 194 |
}
|
| 195 |
+
async function openExample(id,captureId=null){
|
| 196 |
+
if(S.loadingTrace)return;const row=examples.find(r=>r.model===id&&r.id===captureId)||examples.find(r=>r.model===id);if(!row)throw new Error('No example capture is available for this model.');pendingExample=row.id;
|
| 197 |
S.loadingTrace=true;$('runButton').disabled=true;$('runNote').textContent='Loading measured capture…';update();
|
| 198 |
try{
|
| 199 |
+
let d=traceCache.get(row.id);
|
| 200 |
if(!d){
|
| 201 |
const response=await fetch(row.file);if(!response.ok)throw new Error('The example capture could not load. Try again.');
|
| 202 |
const compressed=await response.arrayBuffer();if(await sha256(compressed)!==row.gzip_sha256)throw new Error('Capture checksum failed. Reload to get the latest files.');
|
| 203 |
if(typeof DecompressionStream==='undefined')throw new Error('This browser needs gzip stream support. Open in a current Chrome, Firefox or Safari.');
|
| 204 |
const plain=await new Response(new Blob([compressed]).stream().pipeThrough(new DecompressionStream('gzip'))).arrayBuffer();
|
| 205 |
if(await sha256(plain)!==row.sha256)throw new Error('Decoded capture checksum failed. Reload to try again.');
|
| 206 |
+
d=JSON.parse(new TextDecoder().decode(plain));validateObservatoryTrace(d,S.models);traceCache.set(row.id,d);
|
| 207 |
}
|
| 208 |
loadTrace(d);selectFrame(0);$('runNote').textContent='Real tensor capture. Press Replay or select a token.';
|
| 209 |
}finally{S.loadingTrace=false;update();}
|
experiments/articles/01-one-word-routing.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# One word changed. Did Flint choose different experts?
|
| 2 |
+
|
| 3 |
+

|
| 4 |
+
|
| 5 |
+
Changing `blue` to `red` changed Flint's answer. At the first captured generation step in decoder block 12, it still selected the same six experts.
|
| 6 |
+
|
| 7 |
+
That is a useful starting point for looking inside a mixture-of-experts model. A different answer does not require a completely different expert set at every block. The weights, ordering, other blocks and later generation steps can change too.
|
| 8 |
+
|
| 9 |
+
We built [One word changed](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=reroute), a small interactive experiment in alphaBell Model Observatory. Pick a prompt pair, step through the captured output and inspect the measurements next to the answers.
|
| 10 |
+
|
| 11 |
+
The three prompt edits are deliberately easy to understand:
|
| 12 |
+
|
| 13 |
+
- Return a JSON color value of `blue` or `red`.
|
| 14 |
+
- Name the opposite of `hot` or `cold`.
|
| 15 |
+
- Write a Python expression using the `maximum` or `minimum` of two values.
|
| 16 |
+
|
| 17 |
+
Flint returned `{"color":"blue"}` and `{"color":"red"}`, `Cold` and `Hot`, and `max(x, y)` and `min(x, y)`. These are small, controlled changes with visible effects, rather than a comparison between unrelated essays.
|
| 18 |
+
|
| 19 |
+
The expert selections show both stability and change. In the first captured step of the blue/red pair, block 12 selected IDs 22, 76, 57, 13, 160 and 169 in both runs, with a different ordering. Block 24 shared five of six selected experts. Across all five ordinally paired output steps and all 47 routed blocks, 122 of 235 expert sets differed.
|
| 20 |
+
|
| 21 |
+
Those last counts include later steps after the generated answers diverged. They describe the captures, not a clean estimate of the isolated effect of the edited prompt word. The full traces let you distinguish the first captured step from the rest. The comparison grids open the lowest block whose first-step expert set changed; block 12 remains available in the full trace inspector.
|
| 22 |
+
|
| 23 |
+
The comparison also includes the alphaBell Qwen Open 27B derivative. Its text decoder uses dense feed-forward computation with recurrent and full-attention blocks, so we do not invent an expert-routing grid for it. The architecture determines which measurements make sense to display.
|
| 24 |
+
|
| 25 |
+
Both checkpoints use Q8_0 here. The captures used a CPU runtime based on llama.cpp, context 2,048, temperature zero, seed 42 and reasoning disabled, with a 48-token generation limit. The viewer replays those recorded results; it does not run a new prompt in your browser. The token slider pairs generated positions, which should not be mistaken for semantic alignment between different answers.
|
| 26 |
+
|
| 27 |
+
There is no “red expert” claim hiding in the visualization. An expert ID is an index, not an interpretation of its role. An observed selection difference is not sufficient evidence that one expert caused an answer.
|
| 28 |
+
|
| 29 |
+
[Open the experiment](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=reroute), then [download the results and provenance](https://alphabell-model-observatory.static.hf.space/experiments/data/results.json). The experiment includes links to the measured traces and prompt downloads.
|
| 30 |
+
|
| 31 |
+
A useful next case would change one negation while keeping the requested output format fixed. Share the exact pair and the behavior you expect, so the result can be checked rather than explained after the fact.
|
| 32 |
+
|
| 33 |
+
Credits: [RecursiveSI's Flint](https://huggingface.co/RecursiveSI/flint) supplies the mixture-of-experts model; [llama.cpp](https://github.com/ggml-org/llama.cpp) supplies the underlying inference infrastructure. alphaBell built the capture comparison and viewer.
|
| 34 |
+
|
| 35 |
+
Exact experiment checkpoints: [Flint matched GGUF](https://huggingface.co/alphabell/Flint-Matched-GGUF) and [alphaBell Qwen Open GGUF](https://huggingface.co/alphabell/alphaBell-Qwen3.8-27B-Open-GGUF).
|
experiments/articles/02-blind-quantization.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Can you spot the 4-bit model? Eleven of our twelve answer pairs were identical.
|
| 2 |
+
|
| 3 |
+

|
| 4 |
+
|
| 5 |
+
We hid the precision labels and compared Q4_1 with Q8_0. In 11 of 12 prompt/model pairs, the captured answer text was exactly the same.
|
| 6 |
+
|
| 7 |
+
The one different pair did not produce a winner. Asked to calculate `(17 * 23) - 19`, Flint answered `374` at Q8_0 and `352` at Q4_1. The correct answer is `372`.
|
| 8 |
+
|
| 9 |
+
The blind test retains all twelve pairs, including identical answers and shared errors. [Spot the 4-bit model](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=quant) asks you to guess before revealing the precision, outputs, timings and checks. Answer order is deterministic for each case. Guesses stay in your browser, and the displayed tally is local rather than a community vote count.
|
| 10 |
+
|
| 11 |
+
We tested six prompts on each of two checkpoints: Flint and the alphaBell Qwen Open 27B derivative. The tasks cover arithmetic, exact JSON, a factual symbol, an in-context correction, a Python expression and a sentence with required words. Each prompt was run at Q8_0 and Q4_1.
|
| 12 |
+
|
| 13 |
+
Several comparisons are indistinguishable from the answer alone. Both Flint variants returned the same JSON object. Both answered `Au` for gold. Both returned the same photosynthesis sentence. All six Qwen Open answer pairs were identical, including its incorrect arithmetic answer of `362`.
|
| 14 |
+
|
| 15 |
+
That last example matters. Agreement between precisions is evidence of output stability on a case, not evidence that the answer is right. A blind preference can miss an error that both variants share. We keep the expected answer and check rule beside the captured text so you can inspect both questions separately.
|
| 16 |
+
|
| 17 |
+
The smaller checkpoint files did save space. Flint's files were approximately 42.35 GB at Q8_0 and 24.96 GB at Q4_1; Qwen Open's were approximately 28.60 GB and 16.83 GB. Those are decimal file sizes, not GPU memory requirements or a guarantee about the memory needed by a deployment.
|
| 18 |
+
|
| 19 |
+
Smaller also did not mean uniformly faster in this run. For the gold-symbol prompt, the Qwen Open requests took about 4.67 seconds at Q8_0 and 7.81 seconds at Q4_1. These are instrumented CPU measurements from individual requests, not repeated performance estimates. The downloadable data names the timing metrics and records process peak RSS, including loading and warm-up.
|
| 20 |
+
|
| 21 |
+
The controls were eight CPU threads, context 2,048, temperature zero, seed 42, reasoning disabled and a 64-token output limit. Flash Attention was disabled. The attention KV cache used the runtime's default F16 representation. No quality conclusion about an entire precision format follows from these twelve short pairs.
|
| 22 |
+
|
| 23 |
+
[Try the blind comparison](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=quant), then [inspect every result and its provenance](https://alphabell-model-observatory.static.hf.space/experiments/data/results.json).
|
| 24 |
+
|
| 25 |
+
Suggest an exact, short prompt where you expect Q4_1 and Q8_0 to diverge. Include the scoring rule before seeing the answers. We can add submitted cases to the same reproducible comparison.
|
| 26 |
+
|
| 27 |
+
Credits: [RecursiveSI's Flint](https://huggingface.co/RecursiveSI/flint) and [llama.cpp](https://github.com/ggml-org/llama.cpp). The [public alphaBell Qwen Open FP8 sibling](https://huggingface.co/alphabell/alphaBell-Qwen3.8-27B-Open-FP8) is related to the tested derivative, but it is a different weight artifact from these GGUF checkpoints.
|
| 28 |
+
|
| 29 |
+
Exact experiment checkpoints: [Flint matched GGUF](https://huggingface.co/alphabell/Flint-Matched-GGUF) and [alphaBell Qwen Open GGUF](https://huggingface.co/alphabell/alphaBell-Qwen3.8-27B-Open-GGUF).
|
experiments/articles/03-activation-surgery.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# We zeroed one Flint block's feed-forward output. The score stayed 4 out of 6.
|
| 2 |
+
|
| 3 |
+

|
| 4 |
+
|
| 5 |
+
The answers changed. The tiny test score did not.
|
| 6 |
+
|
| 7 |
+
We scaled the computed feed-forward output of Flint decoder block 12 by 1, 0.5 and 0. Across six short checks, every condition passed four and failed two. That does not make the intervention harmless. It shows how much a single summary score can hide.
|
| 8 |
+
|
| 9 |
+
[What else broke?](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=surgery) puts the baseline, half-scale and zero-scale outputs side by side. It exposes the intervention and check rules instead of calling the result a new fine-tune.
|
| 10 |
+
|
| 11 |
+
The operation is precise: scale `ffn_out-12`, using zero-based decoder-block indexing, before residual addition. The intervention applies to all prompt and generated positions on each forward pass. Model weights are unchanged. There is no training stage, adapter, prompt-only simulation or released “surgery model” implied by this experiment.
|
| 12 |
+
|
| 13 |
+
For the arithmetic prompt `(17 * 23) - 19`, the baseline returned `374`, half-scale returned `352`, and zero-scale returned `340`. All three are wrong: the expected result is `372`. A metric that only records pass or fail sees no change, while the actual behavior clearly moved.
|
| 14 |
+
|
| 15 |
+
The Python-expression case tells a similar story. Baseline and half-scale returned `return n % 2 == 0`; zero-scale returned `def is_even(n): return n % 2 == 0`. The task requested one expression for a function body, so all three failed the expression check. The changed output was still meaningful to inspect, even though the score was flat.
|
| 16 |
+
|
| 17 |
+
The four passing checks covered exact JSON, the chemical symbol for gold, a corrected color and a constrained photosynthesis sentence. At zero scale, the photosynthesis wording changed while still satisfying the stated rule. “Same score” therefore includes both unchanged answers and changed answers that pass the same test.
|
| 18 |
+
|
| 19 |
+
We also recorded the tensor measurement around the intervention. In the first arithmetic step, block 12's FFN output RMS was approximately 0.004232 before scaling. It remained approximately 0.004232 at scale 1, became 0.002116 at scale 0.5, and became zero at scale 0. Those readings check that the operation happened where specified. Neighboring-block and downstream measurements remain available for inspection.
|
| 20 |
+
|
| 21 |
+
The checkpoint was Flint Q8_0. The run used eight CPU threads, context 2,048, temperature zero, seed 42, reasoning disabled and at most 64 generated tokens. Each condition reused the same case prompt and scoring rule. The capture runner is based on llama.cpp; the reproducibility material describes the intervention code.
|
| 22 |
+
|
| 23 |
+
Six short prompts cannot establish whether this block is broadly dispensable, identify a capability localized to it, or predict the effect of editing other blocks. The useful result is narrower: this particular activation intervention changed some outputs without changing the aggregate pass count.
|
| 24 |
+
|
| 25 |
+
[Inspect the three conditions](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=surgery) and [download the results, checks and provenance](https://alphabell-model-observatory.static.hf.space/experiments/data/results.json).
|
| 26 |
+
|
| 27 |
+
A good follow-up contribution is a matched pair: one task that should change and one nearby task that should survive. State both expectations before running the intervention. That gives a behavior-edit claim a regression test.
|
| 28 |
+
|
| 29 |
+
Credits: [RecursiveSI's Flint](https://huggingface.co/RecursiveSI/flint) and the [llama.cpp project](https://github.com/ggml-org/llama.cpp). alphaBell implemented the controlled activation experiment and comparison interface.
|
| 30 |
+
|
| 31 |
+
Exact experiment checkpoints: [Flint matched GGUF](https://huggingface.co/alphabell/Flint-Matched-GGUF) and [alphaBell Qwen Open GGUF](https://huggingface.co/alphabell/alphaBell-Qwen3.8-27B-Open-GGUF).
|
experiments/articles/04-correction-detective.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# We tried to catch two models repeating an outdated fact. Both passed.
|
| 2 |
+
|
| 3 |
+

|
| 4 |
+
|
| 5 |
+
A museum access code starts at `4319`. It changes to `8624`. In another log, it changes again to `5972`.
|
| 6 |
+
|
| 7 |
+
Ask for the current code and the last update should win. Ask for the original code and the first value should survive. Those are different requirements, and a model can satisfy one while failing the other.
|
| 8 |
+
|
| 9 |
+
We made [Remembered, but outdated](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=memory), a short prediction game built around that distinction. Read the fictional log, choose the answer the model should give, and reveal the captured answers afterward. Your prediction stays in your browser.
|
| 10 |
+
|
| 11 |
+
The initial batch contains six cases. There are current-value and original-value questions for a single correction, two corrections, and a correction with unrelated sentences in the log. Each case was run on Flint and the alphaBell Qwen Open 27B derivative, both at Q8_0.
|
| 12 |
+
|
| 13 |
+
Both models answered all six cases correctly. That is twelve successful captured runs, with no stale-fact failure in this batch.
|
| 14 |
+
|
| 15 |
+
For the one-correction log, both returned `8624` when asked for the current code and `4319` when asked for the original. With two corrections, both returned `5972` for the current code while still returning `4319` for the original. In the distractor version, both recovered the appropriate value despite repeated sentences about a sealed crate and a lit lamp.
|
| 16 |
+
|
| 17 |
+
This is a useful negative finding. We started with an engaging failure hypothesis and did not get the failure. Publishing the cases makes it possible to extend the test without pretending the first version demonstrated a weakness.
|
| 18 |
+
|
| 19 |
+
The scope is deliberately small. These are short, self-contained prompts, all below 256 prompt tokens. The observed prompt lengths ranged from 67 to 144 tokens, including each checkpoint's formatting. The experiment does not test retrieval from a large document, facts remembered across conversations, persistence after a session ends, or performance near the model's context limit.
|
| 20 |
+
|
| 21 |
+
The models received the relevant log in the current input. Updating a fact within that input does not mean the weights were updated. Successfully reporting the original value also does not reveal where the fact is represented internally. A topology visualization alone would not establish either claim.
|
| 22 |
+
|
| 23 |
+
The captures used a CPU runtime based on llama.cpp, context 2,048, temperature zero, seed 42, reasoning disabled and a 16-token generation limit. The expected answer is defined by the fictional log, so scoring does not depend on a changing real-world fact. Both the exact prompt and captured output are included with each condition.
|
| 24 |
+
|
| 25 |
+
[Play the correction detective](https://alphabell-model-observatory.static.hf.space/experiments/?experiment=memory), then [download all cases and provenance](https://alphabell-model-observatory.static.hf.space/experiments/data/results.json).
|
| 26 |
+
|
| 27 |
+
For the next batch, contribute a log with two entities, several dated updates and separate original/current questions. Keep a clear answer key. A harder case should preserve an unambiguous target rather than make the wording confusing and count confusion as a memory failure.
|
| 28 |
+
|
| 29 |
+
Credits: [RecursiveSI's Flint](https://huggingface.co/RecursiveSI/flint) and [llama.cpp](https://github.com/ggml-org/llama.cpp). The Qwen participant is alphaBell's Qwen Open 27B derivative; the [public FP8 sibling](https://huggingface.co/alphabell/alphaBell-Qwen3.8-27B-Open-FP8) is a different artifact from the Q8_0 GGUF used here.
|
| 30 |
+
|
| 31 |
+
Exact experiment checkpoints: [Flint matched GGUF](https://huggingface.co/alphabell/Flint-Matched-GGUF) and [alphaBell Qwen Open GGUF](https://huggingface.co/alphabell/alphaBell-Qwen3.8-27B-Open-GGUF).
|
experiments/articles/05-model-music.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# What does a model sound like? We turned 103 captured token frames into music.
|
| 2 |
+
|
| 3 |
+

|
| 4 |
+
|
| 5 |
+
One prompt, two model architectures, 103 recorded output-token frames. We turned their measured activity into a small instrument you can play in a browser.
|
| 6 |
+
|
| 7 |
+
[A model, heard](https://alphabell-model-observatory.static.hf.space/experiments/sound.html) uses the same captures as alphaBell Model Observatory. Flint supplies 59 frames; the alphaBell Qwen Open 27B derivative supplies 44. Both answered: “Explain why the sky is blue in two sentences.” Both captures use Q8_0.
|
| 8 |
+
|
| 9 |
+
Choose a decoder block, select a token duration and press play. You can scrub through the answer, inspect the values currently sounding and download a WAV of the resulting piece. No new inference runs, and no prompt or audio is uploaded by the player.
|
| 10 |
+
|
| 11 |
+
The sound is a designed mapping of measurements, not a recording of thoughts or a model's voice. Its value is that the design is explicit and repeatable. With the same capture, block, timing and volume, the implementation produces the same audio samples.
|
| 12 |
+
|
| 13 |
+
For Flint, the six selected routed experts each become a sine tone. An expert ID selects a note in a repeating three-octave C pentatonic scale. Routing weight sets amplitude using `0.11 × sqrt(weight)`. Dense blocks and the shared expert are excluded from this score, so the six tones correspond to the six routed selections.
|
| 14 |
+
|
| 15 |
+
For Qwen Open, the player uses recurrent blocks. At each token, the six value heads with the largest measured output RMS become triangle tones. Head ID selects a note on the same scale, one octave higher. Amplitude is `0.08 × head RMS / maximum head RMS`, where that maximum is computed across the chosen block's capture. All 48 recurrent value heads remain visible in the grid, including those not sounding.
|
| 16 |
+
|
| 17 |
+
These are different units and different selection rules. The piece is not an audible model-quality ranking, nor a fair test of which model has “more activity.” Flint's routing weights and Qwen Open's head-output RMS mean different things.
|
| 18 |
+
|
| 19 |
+
The musical choices also discard information. Pitch repeats every 15 IDs, so distinct experts or heads can produce the same note. Only six Qwen heads sound at each step. Changing the scale or normalization would change the aesthetic result without changing the underlying capture. The displayed IDs and values help keep those choices inspectable.
|
| 20 |
+
|
| 21 |
+
Playback uses a fixed token duration, from 200 to 900 milliseconds, rather than the original inference timing. This lets you listen at a useful pace without treating a slower CPU request as a longer musical event. The export is mono at 22,050 Hz, with short attack and release envelopes to soften note boundaries.
|
| 22 |
+
|
| 23 |
+
The original captures were recorded with a CPU runtime based on llama.cpp, context 2,048, temperature zero, seed 42 and reasoning disabled. This edition plays those recorded results. The source manifest supplies frame counts and SHA-256 checksums so the score can be traced back to its input.
|
| 24 |
+
|
| 25 |
+
[Play the instrument](https://alphabell-model-observatory.static.hf.space/experiments/sound.html), [inspect the capture manifest](https://alphabell-model-observatory.static.hf.space/traces/index.json), or return to the [topology viewer](https://alphabell-model-observatory.static.hf.space/).
|
| 26 |
+
|
| 27 |
+
Try one specific contribution: propose a mapping that preserves more information while remaining listenable. State which measurement chooses pitch, amplitude and duration, and what comparison a listener could reasonably make from it.
|
| 28 |
+
|
| 29 |
+
Credits: [RecursiveSI's Flint](https://huggingface.co/RecursiveSI/flint) and [llama.cpp](https://github.com/ggml-org/llama.cpp). alphaBell built the capture viewer and sonification. Qwen Open here is alphaBell's derivative, rather than an untouched official Qwen checkpoint.
|
| 30 |
+
|
| 31 |
+
Exact experiment checkpoints: [Flint matched GGUF](https://huggingface.co/alphabell/Flint-Matched-GGUF) and [alphaBell Qwen Open GGUF](https://huggingface.co/alphabell/alphaBell-Qwen3.8-27B-Open-GGUF).
|
| 32 |
+
|
| 33 |
+
## Short clips
|
| 34 |
+
|
| 35 |
+
Flint, 59 captured tokens:
|
| 36 |
+
|
| 37 |
+
<video controls playsinline preload="metadata" src="https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/main/experiments/assets/flint-music.webm" poster="https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/main/experiments/assets/05-music.png"></video>
|
| 38 |
+
|
| 39 |
+
Qwen Open, 44 captured tokens:
|
| 40 |
+
|
| 41 |
+
<video controls playsinline preload="metadata" src="https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/main/experiments/assets/qwen-open-music.webm" poster="https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/main/experiments/assets/05-music.png"></video>
|
experiments/articles/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# alphaBell experiment articles
|
| 2 |
+
|
| 3 |
+
Five technical articles accompany the interactive experiments. Each links to the direct static application so case selection works outside the Hugging Face wrapper.
|
| 4 |
+
|
| 5 |
+
| Article | Experiment | Core observation |
|
| 6 |
+
| --- | --- | --- |
|
| 7 |
+
| [One word changed](01-one-word-routing.md) | Routing comparison | A changed answer can preserve the selected expert set at a particular block. |
|
| 8 |
+
| [Spot the 4-bit model](02-blind-quantization.md) | Blind Q4_1/Q8_0 comparison | Eleven of twelve captured answer pairs were identical. |
|
| 9 |
+
| [What else broke?](03-activation-surgery.md) | Controlled FFN activation scaling | Output changes coexisted with an unchanged four-of-six pass count. |
|
| 10 |
+
| [Correction detective](04-correction-detective.md) | Current and original facts | Both models passed all six short correction and recall cases. |
|
| 11 |
+
| [A model, heard](05-model-music.md) | Capture sonification | An explicit deterministic mapping turns 103 real token frames into audio. |
|
| 12 |
+
|
| 13 |
+
The companion [results file](https://alphabell-model-observatory.static.hf.space/experiments/data/results.json) includes experiment prompts, outputs, checks and provenance. The [capture manifest](https://alphabell-model-observatory.static.hf.space/traces/index.json) identifies the sonification inputs.
|
experiments/assets/01-routing.png
ADDED
|
experiments/assets/02-quantization.png
ADDED
|
experiments/assets/03-surgery.png
ADDED
|
experiments/assets/04-memory.png
ADDED
|
experiments/assets/05-music.png
ADDED
|
experiments/assets/flint-music.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:71e1ea5b816e4ee18fc9f9af213056bbf34de08b57c6e035131b2e164422668a
|
| 3 |
+
size 910710
|
experiments/assets/flint-music.webm
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c8c2eb796aa9973c0dee1dd880ea973a8ef3541f535c23b8cd92a15b98349fcc
|
| 3 |
+
size 989655
|
experiments/assets/media.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"mapping": "sound.js; 350ms per token, volume 0.35",
|
| 3 |
+
"flint_block": 12,
|
| 4 |
+
"qwen_block": 0,
|
| 5 |
+
"files": [
|
| 6 |
+
{
|
| 7 |
+
"file": "flint-music.webm",
|
| 8 |
+
"bytes": 989655,
|
| 9 |
+
"sha256": "c8c2eb796aa9973c0dee1dd880ea973a8ef3541f535c23b8cd92a15b98349fcc"
|
| 10 |
+
},
|
| 11 |
+
{
|
| 12 |
+
"file": "qwen-open-music.webm",
|
| 13 |
+
"bytes": 945509,
|
| 14 |
+
"sha256": "6a272628055ab07833679f48c99a2ca08f6f58d9ee2aeacc1e88b0dcb161006b"
|
| 15 |
+
},
|
| 16 |
+
{
|
| 17 |
+
"file": "flint-music.wav",
|
| 18 |
+
"bytes": 910710,
|
| 19 |
+
"sha256": "71e1ea5b816e4ee18fc9f9af213056bbf34de08b57c6e035131b2e164422668a"
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"file": "qwen-open-music.wav",
|
| 23 |
+
"bytes": 679184,
|
| 24 |
+
"sha256": "5a111e668b71d07adcb4d357200baccc11abdc383f83ad9ee3c551ea9f030c23"
|
| 25 |
+
}
|
| 26 |
+
]
|
| 27 |
+
}
|
experiments/assets/qwen-open-music.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5a111e668b71d07adcb4d357200baccc11abdc383f83ad9ee3c551ea9f030c23
|
| 3 |
+
size 679184
|
experiments/assets/qwen-open-music.webm
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6a272628055ab07833679f48c99a2ca08f6f58d9ee2aeacc1e88b0dcb161006b
|
| 3 |
+
size 945509
|
experiments/data/results.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
experiments/data/summary.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"capture_runs": 24,
|
| 3 |
+
"quant_runs": 24,
|
| 4 |
+
"surgery_conditions": 18,
|
| 5 |
+
"surgery_additional_runs": 12,
|
| 6 |
+
"unique_measured_runs": 60,
|
| 7 |
+
"quant_pairs": 12,
|
| 8 |
+
"identical_quant_pairs": 11,
|
| 9 |
+
"memory_answers_passed": 12,
|
| 10 |
+
"memory_answers_total": 12,
|
| 11 |
+
"full_capture_frames": 80,
|
| 12 |
+
"first_frame_changed_expert_blocks": {
|
| 13 |
+
"json-color-flint": 18,
|
| 14 |
+
"opposite-flint": 25,
|
| 15 |
+
"python-flint": 20
|
| 16 |
+
},
|
| 17 |
+
"results_sha256": "b60b8d8b302e5077c01a738cc667d94ef7768ce8085c8dd0636b1d7068c9a140"
|
| 18 |
+
}
|
experiments/data/traces.json
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"id": "memory-distractors-current-flint",
|
| 4 |
+
"model": "flint",
|
| 5 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. The museum access code is now 8624. The old code is no longer current. Question: What is the current museum access code? Answer with only the four digits.",
|
| 6 |
+
"tokens": 2,
|
| 7 |
+
"file": "traces/experiments/memory-distractors-current-flint.json.gz",
|
| 8 |
+
"sha256": "5afd29ce9f49160b2f35f8f9dcb880848a52722c2e59e7887e06185f13922777",
|
| 9 |
+
"gzip_sha256": "9fb651c14a66e00268d4814e874e9073a97aa391fa776c69f8b7146420547d8f",
|
| 10 |
+
"bytes": 960941,
|
| 11 |
+
"compressed_bytes": 372753,
|
| 12 |
+
"created": 1792753004575,
|
| 13 |
+
"label": "A correction among distractors: current fact \u00b7 flint"
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
"id": "memory-distractors-current-qwen",
|
| 17 |
+
"model": "qwen",
|
| 18 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. The museum access code is now 8624. The old code is no longer current. Question: What is the current museum access code? Answer with only the four digits.",
|
| 19 |
+
"tokens": 4,
|
| 20 |
+
"file": "traces/experiments/memory-distractors-current-qwen.json.gz",
|
| 21 |
+
"sha256": "4912dac302ed9cf3075d82423964d7f2cb78fd37cef9b02771cddf7f94121238",
|
| 22 |
+
"gzip_sha256": "31b0d9a35744a6a4563d128763ea43535be5120ec6ea8a635bd0e21a009d64bf",
|
| 23 |
+
"bytes": 1205054,
|
| 24 |
+
"compressed_bytes": 518952,
|
| 25 |
+
"created": 1792753006737,
|
| 26 |
+
"label": "A correction among distractors: current fact \u00b7 qwen"
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"id": "memory-distractors-original-flint",
|
| 30 |
+
"model": "flint",
|
| 31 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. The museum access code is now 8624. The old code is no longer current. Question: What is the original museum access code? Answer with only the four digits.",
|
| 32 |
+
"tokens": 2,
|
| 33 |
+
"file": "traces/experiments/memory-distractors-original-flint.json.gz",
|
| 34 |
+
"sha256": "117d4f848f94ee889eeeeba4334c8e913f08021e3858428622f12d1808d30287",
|
| 35 |
+
"gzip_sha256": "0dade5419d2dcce1886350aa7b6f9d006f980c246dce74be9e52c4a96524509a",
|
| 36 |
+
"bytes": 960809,
|
| 37 |
+
"compressed_bytes": 373102,
|
| 38 |
+
"created": 1792753018956,
|
| 39 |
+
"label": "A correction among distractors: original fact \u00b7 flint"
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"id": "memory-distractors-original-qwen",
|
| 43 |
+
"model": "qwen",
|
| 44 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. The museum access code is now 8624. The old code is no longer current. Question: What is the original museum access code? Answer with only the four digits.",
|
| 45 |
+
"tokens": 4,
|
| 46 |
+
"file": "traces/experiments/memory-distractors-original-qwen.json.gz",
|
| 47 |
+
"sha256": "41adb8cdd9b1be4587d767ee73513a15b8fff5b7d32e27a6dba6c0fee1eb60a5",
|
| 48 |
+
"gzip_sha256": "faa06f0a66ddd34bbb520f85179239a500e10b51ef3b79522bfd5ced0ed90b3d",
|
| 49 |
+
"bytes": 1203653,
|
| 50 |
+
"compressed_bytes": 518535,
|
| 51 |
+
"created": 1792753021157,
|
| 52 |
+
"label": "A correction among distractors: original fact \u00b7 qwen"
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"id": "memory-short-current-flint",
|
| 56 |
+
"model": "flint",
|
| 57 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. The museum access code is now 8624. The old code is no longer current. Question: What is the current museum access code? Answer with only the four digits.",
|
| 58 |
+
"tokens": 2,
|
| 59 |
+
"file": "traces/experiments/memory-short-current-flint.json.gz",
|
| 60 |
+
"sha256": "6e1345ee29f71094f8d51982f2ed905fbd0be51a26b3780152fc78ed004d45c7",
|
| 61 |
+
"gzip_sha256": "1c857156e7a8284e79de717741508a6eab5ea099e6bc42c49bb52ee799091b32",
|
| 62 |
+
"bytes": 943320,
|
| 63 |
+
"compressed_bytes": 372203,
|
| 64 |
+
"created": 1792752983606,
|
| 65 |
+
"label": "One correction: current fact \u00b7 flint"
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"id": "memory-short-current-qwen",
|
| 69 |
+
"model": "qwen",
|
| 70 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. The museum access code is now 8624. The old code is no longer current. Question: What is the current museum access code? Answer with only the four digits.",
|
| 71 |
+
"tokens": 4,
|
| 72 |
+
"file": "traces/experiments/memory-short-current-qwen.json.gz",
|
| 73 |
+
"sha256": "f91be43725b8603628494a4b71bfb76888d1f69165295b98209857d10e4a06e5",
|
| 74 |
+
"gzip_sha256": "2177ad2e9e2d62726f2e94e339c9423cf7c5cf12c1010fe89b2f0ea9d8a9360d",
|
| 75 |
+
"bytes": 1195637,
|
| 76 |
+
"compressed_bytes": 517455,
|
| 77 |
+
"created": 1792752985768,
|
| 78 |
+
"label": "One correction: current fact \u00b7 qwen"
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"id": "memory-short-original-flint",
|
| 82 |
+
"model": "flint",
|
| 83 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. The museum access code is now 8624. The old code is no longer current. Question: What is the original museum access code? Answer with only the four digits.",
|
| 84 |
+
"tokens": 2,
|
| 85 |
+
"file": "traces/experiments/memory-short-original-flint.json.gz",
|
| 86 |
+
"sha256": "34e0fdef73702915345d5a00ceb2b97407c3d3403d33bdeffbe1e4df6260baac",
|
| 87 |
+
"gzip_sha256": "e730312e416fa2331342bc02277b13e57ee3bb3941c2f0acdfddf86214191a2e",
|
| 88 |
+
"bytes": 943171,
|
| 89 |
+
"compressed_bytes": 372679,
|
| 90 |
+
"created": 1792752994024,
|
| 91 |
+
"label": "One correction: original fact \u00b7 flint"
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"id": "memory-short-original-qwen",
|
| 95 |
+
"model": "qwen",
|
| 96 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. The museum access code is now 8624. The old code is no longer current. Question: What is the original museum access code? Answer with only the four digits.",
|
| 97 |
+
"tokens": 4,
|
| 98 |
+
"file": "traces/experiments/memory-short-original-qwen.json.gz",
|
| 99 |
+
"sha256": "5e8b91b89677b0d014f3a705cbca017b559984960928b11c0b28241a7eb792e0",
|
| 100 |
+
"gzip_sha256": "afefafa284bf44fdfa36cadd5493ba204afd1a9ba93870525b272f7f452d3829",
|
| 101 |
+
"bytes": 1195602,
|
| 102 |
+
"compressed_bytes": 517420,
|
| 103 |
+
"created": 1792752996323,
|
| 104 |
+
"label": "One correction: original fact \u00b7 qwen"
|
| 105 |
+
},
|
| 106 |
+
{
|
| 107 |
+
"id": "memory-twice-current-flint",
|
| 108 |
+
"model": "flint",
|
| 109 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. The museum access code changes to 8624. Later it changes again to 5972. The earlier codes are no longer current. Question: What is the current museum access code? Answer with only the four digits.",
|
| 110 |
+
"tokens": 2,
|
| 111 |
+
"file": "traces/experiments/memory-twice-current-flint.json.gz",
|
| 112 |
+
"sha256": "d15d5a0d54ab5283f053b2671fbeb018a4cb58f40fc12fc045773122a5a177e6",
|
| 113 |
+
"gzip_sha256": "21b7e8fbdb5eb767cf71c9780b2763cf1f0723f2a5f2854a2ac4403b11c28475",
|
| 114 |
+
"bytes": 954308,
|
| 115 |
+
"compressed_bytes": 373646,
|
| 116 |
+
"created": 1792753033494,
|
| 117 |
+
"label": "Two successive corrections: current fact \u00b7 flint"
|
| 118 |
+
},
|
| 119 |
+
{
|
| 120 |
+
"id": "memory-twice-current-qwen",
|
| 121 |
+
"model": "qwen",
|
| 122 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. The museum access code changes to 8624. Later it changes again to 5972. The earlier codes are no longer current. Question: What is the current museum access code? Answer with only the four digits.",
|
| 123 |
+
"tokens": 4,
|
| 124 |
+
"file": "traces/experiments/memory-twice-current-qwen.json.gz",
|
| 125 |
+
"sha256": "20cc6fc733b5b12a5b2257f6ae28b24979a0096465868020e9101f60131a2ef6",
|
| 126 |
+
"gzip_sha256": "e6dac7b0201e32adc60202638f0f0c84ed2f257c065513c4649d5ab39610a6af",
|
| 127 |
+
"bytes": 1201186,
|
| 128 |
+
"compressed_bytes": 518453,
|
| 129 |
+
"created": 1792753035778,
|
| 130 |
+
"label": "Two successive corrections: current fact \u00b7 qwen"
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"id": "memory-twice-original-flint",
|
| 134 |
+
"model": "flint",
|
| 135 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. The museum access code changes to 8624. Later it changes again to 5972. The earlier codes are no longer current. Question: What is the original museum access code? Answer with only the four digits.",
|
| 136 |
+
"tokens": 2,
|
| 137 |
+
"file": "traces/experiments/memory-twice-original-flint.json.gz",
|
| 138 |
+
"sha256": "ff79ad7fc15b469c91b7d959b4c86d5242feb8e83b375d0196700d940c023fe8",
|
| 139 |
+
"gzip_sha256": "15f3245da6145e40c7c3b56722bd78ebcd26f0f4363ef64c153a538ad1a7cf8e",
|
| 140 |
+
"bytes": 954081,
|
| 141 |
+
"compressed_bytes": 373616,
|
| 142 |
+
"created": 1792753046085,
|
| 143 |
+
"label": "Two successive corrections: original fact \u00b7 flint"
|
| 144 |
+
},
|
| 145 |
+
{
|
| 146 |
+
"id": "memory-twice-original-qwen",
|
| 147 |
+
"model": "qwen",
|
| 148 |
+
"prompt": "Read this fictional log. The original museum access code is 4319. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. A crate is sealed. A lamp is on. The museum access code changes to 8624. Later it changes again to 5972. The earlier codes are no longer current. Question: What is the original museum access code? Answer with only the four digits.",
|
| 149 |
+
"tokens": 4,
|
| 150 |
+
"file": "traces/experiments/memory-twice-original-qwen.json.gz",
|
| 151 |
+
"sha256": "5490a9f4775c6de2478a68b85327c007c625ac5384691eb2c28b904661ad1c8b",
|
| 152 |
+
"gzip_sha256": "3a0baf879172d8a97353305063312519a98d1c97151400743b2b0e11fab58d74",
|
| 153 |
+
"bytes": 1202381,
|
| 154 |
+
"compressed_bytes": 518903,
|
| 155 |
+
"created": 1792753048335,
|
| 156 |
+
"label": "Two successive corrections: original fact \u00b7 qwen"
|
| 157 |
+
},
|
| 158 |
+
{
|
| 159 |
+
"id": "reroute-json-color-flint-a",
|
| 160 |
+
"model": "flint",
|
| 161 |
+
"prompt": "Return only JSON with one key named color and value blue.",
|
| 162 |
+
"tokens": 5,
|
| 163 |
+
"file": "traces/experiments/reroute-json-color-flint-a.json.gz",
|
| 164 |
+
"sha256": "ff0da38c0292fe7f8e46a2e8a932ccb983e69877327081f738e212ad8100ea25",
|
| 165 |
+
"gzip_sha256": "a1a2c501c40294dcb039a15c115513850d55a69bea61e1fe2fca715acae43ebe",
|
| 166 |
+
"bytes": 2346341,
|
| 167 |
+
"compressed_bytes": 919171,
|
| 168 |
+
"created": 1792752946971,
|
| 169 |
+
"label": "One word changes the JSON value \u00b7 a"
|
| 170 |
+
},
|
| 171 |
+
{
|
| 172 |
+
"id": "reroute-json-color-flint-b",
|
| 173 |
+
"model": "flint",
|
| 174 |
+
"prompt": "Return only JSON with one key named color and value red.",
|
| 175 |
+
"tokens": 5,
|
| 176 |
+
"file": "traces/experiments/reroute-json-color-flint-b.json.gz",
|
| 177 |
+
"sha256": "f9f13339bdc7df2f3fd42d40a9b87d04cf531c752c2a99f943093d058fea616b",
|
| 178 |
+
"gzip_sha256": "a6de8843f680c529d5123fff8bc1c663fdb59d4b9018308bdd41b4beb3e52213",
|
| 179 |
+
"bytes": 2346598,
|
| 180 |
+
"compressed_bytes": 919420,
|
| 181 |
+
"created": 1792752951404,
|
| 182 |
+
"label": "One word changes the JSON value \u00b7 b"
|
| 183 |
+
},
|
| 184 |
+
{
|
| 185 |
+
"id": "reroute-json-color-qwen-a",
|
| 186 |
+
"model": "qwen",
|
| 187 |
+
"prompt": "Return only JSON with one key named color and value blue.",
|
| 188 |
+
"tokens": 5,
|
| 189 |
+
"file": "traces/experiments/reroute-json-color-qwen-a.json.gz",
|
| 190 |
+
"sha256": "a13f3e97372c12e6242c17777e7f0f4a27f01ec91d6ed03304979bd72c0549e0",
|
| 191 |
+
"gzip_sha256": "892c728cea74109639ecf7d125e6677df9ed9f2c68c32a5292204c200ec47bbe",
|
| 192 |
+
"bytes": 1490772,
|
| 193 |
+
"compressed_bytes": 643912,
|
| 194 |
+
"created": 1792752953882,
|
| 195 |
+
"label": "One word changes the JSON value \u00b7 a"
|
| 196 |
+
},
|
| 197 |
+
{
|
| 198 |
+
"id": "reroute-json-color-qwen-b",
|
| 199 |
+
"model": "qwen",
|
| 200 |
+
"prompt": "Return only JSON with one key named color and value red.",
|
| 201 |
+
"tokens": 5,
|
| 202 |
+
"file": "traces/experiments/reroute-json-color-qwen-b.json.gz",
|
| 203 |
+
"sha256": "c1d3757878ef7c54bcfeb06d0658fca745a39d59fba2aeabf20e709f0fff532c",
|
| 204 |
+
"gzip_sha256": "b87a820c6240ff199010edefd907d98cb2340122f670aaaac3c3182c6f52af51",
|
| 205 |
+
"bytes": 1491042,
|
| 206 |
+
"compressed_bytes": 643871,
|
| 207 |
+
"created": 1792752960146,
|
| 208 |
+
"label": "One word changes the JSON value \u00b7 b"
|
| 209 |
+
},
|
| 210 |
+
{
|
| 211 |
+
"id": "reroute-opposite-flint-a",
|
| 212 |
+
"model": "flint",
|
| 213 |
+
"prompt": "Answer with one word: name the opposite of hot.",
|
| 214 |
+
"tokens": 1,
|
| 215 |
+
"file": "traces/experiments/reroute-opposite-flint-a.json.gz",
|
| 216 |
+
"sha256": "27eefa9070e87ee9c6139055d8b5ff8ab6eea0f7d5be48672fd7bf815fa998f8",
|
| 217 |
+
"gzip_sha256": "83f6529578d08dd395eabecd1f2e3d68adc06337feca4cd8270990a897a76f6e",
|
| 218 |
+
"bytes": 473055,
|
| 219 |
+
"compressed_bytes": 185653,
|
| 220 |
+
"created": 1792752934308,
|
| 221 |
+
"label": "One word changes the opposite \u00b7 a"
|
| 222 |
+
},
|
| 223 |
+
{
|
| 224 |
+
"id": "reroute-opposite-flint-b",
|
| 225 |
+
"model": "flint",
|
| 226 |
+
"prompt": "Answer with one word: name the opposite of cold.",
|
| 227 |
+
"tokens": 1,
|
| 228 |
+
"file": "traces/experiments/reroute-opposite-flint-b.json.gz",
|
| 229 |
+
"sha256": "226b7b20822499069e0f582c56a563988526549967acde9c9367050e26bc4fd5",
|
| 230 |
+
"gzip_sha256": "6f378e33a36b498d171dde4e97d0b33f97643eaa3f035ab9c61f7ff7634d0c66",
|
| 231 |
+
"bytes": 473107,
|
| 232 |
+
"compressed_bytes": 185674,
|
| 233 |
+
"created": 1792752936544,
|
| 234 |
+
"label": "One word changes the opposite \u00b7 b"
|
| 235 |
+
},
|
| 236 |
+
{
|
| 237 |
+
"id": "reroute-opposite-qwen-a",
|
| 238 |
+
"model": "qwen",
|
| 239 |
+
"prompt": "Answer with one word: name the opposite of hot.",
|
| 240 |
+
"tokens": 1,
|
| 241 |
+
"file": "traces/experiments/reroute-opposite-qwen-a.json.gz",
|
| 242 |
+
"sha256": "49939c0748d0ae66d5a51e7a1e4701cbbf663ae360c81d3faeabf5981cf312c9",
|
| 243 |
+
"gzip_sha256": "6090859303c7aa620c03e66ab3a5229e4c761f60119db663e0a777253d9aa6e2",
|
| 244 |
+
"bytes": 302818,
|
| 245 |
+
"compressed_bytes": 131424,
|
| 246 |
+
"created": 1792752938769,
|
| 247 |
+
"label": "One word changes the opposite \u00b7 a"
|
| 248 |
+
},
|
| 249 |
+
{
|
| 250 |
+
"id": "reroute-opposite-qwen-b",
|
| 251 |
+
"model": "qwen",
|
| 252 |
+
"prompt": "Answer with one word: name the opposite of cold.",
|
| 253 |
+
"tokens": 1,
|
| 254 |
+
"file": "traces/experiments/reroute-opposite-qwen-b.json.gz",
|
| 255 |
+
"sha256": "4ddaa3b02ff685bccee1d6a947800842f9396da12b4623b356fef63106b84ff2",
|
| 256 |
+
"gzip_sha256": "c8cc0a1236967408295aadf413b5c10d3443857c56d8418b845b1343d9bb7453",
|
| 257 |
+
"bytes": 302398,
|
| 258 |
+
"compressed_bytes": 131168,
|
| 259 |
+
"created": 1792752942914,
|
| 260 |
+
"label": "One word changes the opposite \u00b7 b"
|
| 261 |
+
},
|
| 262 |
+
{
|
| 263 |
+
"id": "reroute-python-flint-a",
|
| 264 |
+
"model": "flint",
|
| 265 |
+
"prompt": "Write only a Python expression that selects the maximum of x and y.",
|
| 266 |
+
"tokens": 5,
|
| 267 |
+
"file": "traces/experiments/reroute-python-flint-a.json.gz",
|
| 268 |
+
"sha256": "a80bb8703120bb432927555d1c599ea6a7769645a13414fb7d57e23c240e684e",
|
| 269 |
+
"gzip_sha256": "3d0bcff6db675e0a5450ac2744eb4031862eeb5844ae9ac0e3592ceef3875d57",
|
| 270 |
+
"bytes": 2347333,
|
| 271 |
+
"compressed_bytes": 919644,
|
| 272 |
+
"created": 1792752966319,
|
| 273 |
+
"label": "One word changes the function \u00b7 a"
|
| 274 |
+
},
|
| 275 |
+
{
|
| 276 |
+
"id": "reroute-python-flint-b",
|
| 277 |
+
"model": "flint",
|
| 278 |
+
"prompt": "Write only a Python expression that selects the minimum of x and y.",
|
| 279 |
+
"tokens": 5,
|
| 280 |
+
"file": "traces/experiments/reroute-python-flint-b.json.gz",
|
| 281 |
+
"sha256": "80d072551a9135db60dc2ee6902731cd1bcb4843cc629c9c17038f4eeafbc586",
|
| 282 |
+
"gzip_sha256": "4a40ab0cdf7341870e8e378633e5377b4a6614e7fcaef307d69af94326d15c6d",
|
| 283 |
+
"bytes": 2347533,
|
| 284 |
+
"compressed_bytes": 920189,
|
| 285 |
+
"created": 1792752968702,
|
| 286 |
+
"label": "One word changes the function \u00b7 b"
|
| 287 |
+
},
|
| 288 |
+
{
|
| 289 |
+
"id": "reroute-python-qwen-a",
|
| 290 |
+
"model": "qwen",
|
| 291 |
+
"prompt": "Write only a Python expression that selects the maximum of x and y.",
|
| 292 |
+
"tokens": 5,
|
| 293 |
+
"file": "traces/experiments/reroute-python-qwen-a.json.gz",
|
| 294 |
+
"sha256": "3eeb0196c7ad311743476653d65cb26d64561afd87452792f508f5371a59531e",
|
| 295 |
+
"gzip_sha256": "c52ca893eaf76cd8db897dd9b0b66f01545cc945c0edc562c08b2bb6aecd3bec",
|
| 296 |
+
"bytes": 1490170,
|
| 297 |
+
"compressed_bytes": 643193,
|
| 298 |
+
"created": 1792752971166,
|
| 299 |
+
"label": "One word changes the function \u00b7 a"
|
| 300 |
+
},
|
| 301 |
+
{
|
| 302 |
+
"id": "reroute-python-qwen-b",
|
| 303 |
+
"model": "qwen",
|
| 304 |
+
"prompt": "Write only a Python expression that selects the minimum of x and y.",
|
| 305 |
+
"tokens": 5,
|
| 306 |
+
"file": "traces/experiments/reroute-python-qwen-b.json.gz",
|
| 307 |
+
"sha256": "ab952c5215dfc8dae2c2136497eca8c38d470f6169829bec1b8df571a4665a72",
|
| 308 |
+
"gzip_sha256": "23025da3491932313747db32ff949ac6cbd208171716ad624c7a5d847b03fa9f",
|
| 309 |
+
"bytes": 1489618,
|
| 310 |
+
"compressed_bytes": 642994,
|
| 311 |
+
"created": 1792752977371,
|
| 312 |
+
"label": "One word changes the function \u00b7 b"
|
| 313 |
+
}
|
| 314 |
+
]
|
experiments/index.html
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
| 5 |
+
<title>Five ways to look inside a model | alphaBell Experiments</title>
|
| 6 |
+
<meta name="description" content="Change a word, guess the quantization, test a behavior change and follow a corrected fact. Explore real Flint and Qwen experiments from alphaBell.">
|
| 7 |
+
<meta property="og:title" content="Five measured model experiments | alphaBell"><meta property="og:description" content="One word, two precisions, a branch intervention, corrected facts and model music. Explore the actual runs."><meta property="og:image" content="https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/main/experiments/assets/01-routing.png"><meta name="twitter:card" content="summary_large_image"><meta name="theme-color" content="#176b59"><link rel="icon" href="../favicon.svg"><link rel="stylesheet" href="lab.css">
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<a class="skip" href="#experiment">Skip to experiment</a>
|
| 11 |
+
<header><a class="brand" href="../index.html"><span class="brand-icon">α</span><span>alphaBell <span class="brand-divider">/</span> <b>experiments</b></span></a><nav aria-label="Site"><a href="../index.html">Observatory ↗</a><a href="sound.html">Model music ↗</a></nav></header>
|
| 12 |
+
<main>
|
| 13 |
+
<section class="hero"><div><p class="eyebrow">THE ALPHABELL EXPERIMENT SERIES</p><h1>Small changes.<br><span>Surprising models.</span></h1><p class="intro">Change one word. Hide the precision. Correct a fact. Five hands-on experiments with real model outputs and measurements.</p></div><div class="hero-note"><span class="signal" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i><i></i></span><p>Make a prediction.<br>Then open the evidence.</p><span>Recorded model runs. Explore at your own pace.</span></div></section>
|
| 14 |
+
<nav class="experiment-grid" aria-label="Choose an experiment" id="experimentNav"></nav>
|
| 15 |
+
<section id="experiment" tabindex="-1" aria-labelledby="experimentTitle"><div class="loading" role="status">Opening measured experiments…</div></section>
|
| 16 |
+
<section class="evidence" id="evidence" hidden><div><p class="eyebrow">TAKE THE EVIDENCE WITH YOU</p><h2>Inspect. Reproduce. Try to break it.</h2><p>Download the recorded results and the prompts used to produce them. Your guesses stay in this browser.</p></div><div class="evidence-actions"><button id="downloadResults">Download results ↓</button><button id="downloadFixtures">Download prompts ↓</button><button id="resetGuesses" class="quiet">Clear local guesses</button></div><details id="methodDetails"><summary>Methods, source models and limits</summary><div id="methodBody"></div></details></section>
|
| 17 |
+
</main>
|
| 18 |
+
<footer><a href="https://alphabell.com" rel="noreferrer" target="_blank">alphaBell</a><span>Every result has a prompt. Every claim has a measurement.</span><a href="https://huggingface.co/spaces/alphabell/Model-Observatory/tree/main" target="_blank" rel="noreferrer">Source ↗</a></footer>
|
| 19 |
+
<div class="toast" id="toast" role="status" hidden></div>
|
| 20 |
+
<noscript>Enable JavaScript to explore the recorded experiments.</noscript>
|
| 21 |
+
<script src="lab.js" defer></script>
|
| 22 |
+
</body></html>
|
experiments/lab.css
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root{--bg:#f6f7f3;--paper:#fff;--ink:#24372f;--muted:#66756c;--line:#e0e7dd;--green:#176b59;--lime:#d5f59b;--mono:ui-monospace,SFMono-Regular,Consolas,monospace;--sans:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.6 var(--sans)}button,input,select{font:inherit}button,a,input,select,summary{-webkit-tap-highlight-color:transparent}a{color:var(--green);text-underline-offset:4px}button{color:inherit;cursor:pointer;border:1px solid var(--line);background:var(--paper);border-radius:7px;padding:10px 15px;min-height:44px}button:hover{border-color:#8dad9a;background:#f2f7ed}button:disabled{opacity:.45;cursor:default}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,summary:focus-visible{outline:3px solid var(--green);outline-offset:4px}[hidden]{display:none!important}header{display:flex;justify-content:space-between;align-items:center;gap:20px;padding:17px 36px;border-bottom:1px solid var(--line);background:var(--paper)}.brand{display:flex;align-items:center;gap:11px;color:var(--ink);text-decoration:none;font-size:15px}.brand b{font-weight:500}.brand-icon{display:grid;place-items:center;background:var(--green);color:var(--lime);width:35px;height:35px;border-radius:9px;font-size:25px}.brand-divider{margin:0 5px;color:#a1afa3}header nav{display:flex;gap:24px;font-size:12px}.skip{position:fixed;top:-80px;left:12px;z-index:20;background:white;padding:10px}.skip:focus{top:10px}main{max-width:1320px;margin:auto;padding:0 36px 56px}.hero{display:grid;grid-template-columns:minmax(0,1fr) 300px;align-items:center;gap:60px;padding:62px 0 42px}.eyebrow{font:10px/1.5 var(--mono);letter-spacing:1.4px;color:var(--green);margin:0 0 14px}h1{font-size:clamp(38px,5vw,66px);font-weight:500;letter-spacing:-2.7px;line-height:1.07;margin:0 0 22px}h1 span{color:var(--green)}.intro{max-width:610px;font-size:16px;color:var(--muted);margin:0;line-height:1.7}.hero-note{border-left:1px solid var(--line);padding:26px 0 26px 35px}.hero-note p{font-size:20px;line-height:1.4;margin:18px 0 13px}.hero-note>span:last-child{font-size:11px;color:var(--muted)}.signal{display:flex;align-items:center;gap:6px;height:37px}.signal i{display:block;width:8px;background:var(--green);border-radius:2px;height:35%}.signal i:nth-child(2){height:80%;background:#95bd75}.signal i:nth-child(3){height:55%}.signal i:nth-child(4){height:100%;background:#badf8d}.signal i:nth-child(5){height:70%}.signal i:nth-child(6){height:40%;background:#95bd75}.signal i:nth-child(7){height:65%}.experiment-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin-bottom:34px}.experiment-card{position:relative;display:flex;flex-direction:column;min-width:0;padding:18px 17px 16px;text-decoration:none;color:var(--ink);border:1px solid var(--line);border-radius:9px;background:var(--paper);transition:border-color .15s,transform .15s}.experiment-card:hover{border-color:#88aa93;transform:translateY(-2px)}.experiment-card[aria-current=page]{background:#e8f1df;border-color:#a5c28c}.card-number{font:10px var(--mono);color:var(--green);margin-bottom:22px}.experiment-card strong{font-size:15px;font-weight:500;line-height:1.35;min-height:40px}.experiment-card small{font-size:11px;color:var(--muted);line-height:1.5;margin-top:10px}.card-arrow{position:absolute;right:16px;top:14px;color:var(--green)}#experiment{scroll-margin-top:20px;outline:none}.experiment-head{display:flex;justify-content:space-between;gap:28px;align-items:flex-end;padding:30px 0 24px;border-top:1px solid var(--line)}h2{font-size:30px;font-weight:500;letter-spacing:-1px;line-height:1.2;margin:0 0 11px}.experiment-head p:not(.eyebrow){color:var(--muted);margin:0;max-width:700px}.case-picker{display:flex;flex-direction:column;gap:7px;min-width:195px;font-size:11px;color:var(--muted)}select{width:100%;border:1px solid var(--line);border-radius:6px;background:var(--paper);color:var(--ink);padding:10px 32px 10px 11px;min-height:44px;max-width:350px}.case-layout{background:var(--paper);border:1px solid var(--line);border-radius:11px;overflow:hidden}.case-top{display:flex;justify-content:space-between;gap:25px;padding:23px 26px;border-bottom:1px solid var(--line);background:#fcfdf9}.case-top h3{font-size:18px;font-weight:500;line-height:1.4;margin:0 0 8px}.case-top p{margin:0;color:var(--muted);font-size:12px}.tag{display:inline-flex;align-items:center;border:1px solid #d7e2cd;border-radius:5px;padding:4px 8px;background:#f1f6e9;font:10px var(--mono);white-space:nowrap;align-self:flex-start;color:#4b6e41}.prompt{background:#f5f7f1;border:1px solid var(--line);padding:15px 17px;border-radius:7px;white-space:pre-wrap;overflow-wrap:anywhere;font:12px/1.8 var(--mono);margin:12px 0 0;color:#4e6557}.case-body{padding:25px}.columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px}.columns.three{grid-template-columns:repeat(3,minmax(0,1fr))}.condition{min-width:0;border:1px solid var(--line);border-radius:8px;overflow:hidden;background:white}.condition-heading{padding:15px 17px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;gap:12px;align-items:center}.condition-heading h4{font-size:13px;font-weight:600;margin:0}.condition-content{padding:17px}.output{white-space:pre-wrap;overflow-wrap:anywhere;font-size:13px;line-height:1.8;margin:0;max-height:370px;overflow:auto;scrollbar-width:thin}.condition .prompt{margin:0 0 16px;font-size:11px}.metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(115px,1fr));gap:14px 17px;margin:19px 0 0;padding-top:17px;border-top:1px solid var(--line)}.metrics div{min-width:0}.metrics dt{font-size:10px;color:var(--muted);overflow-wrap:anywhere}.metrics dd{margin:5px 0 0;font:12px var(--mono);color:var(--green);overflow-wrap:anywhere}.annotation{font-size:12px;color:var(--muted);margin:18px 0;line-height:1.8}.annotation strong{color:var(--ink);font-weight:500}.notice{padding:15px 17px;border-radius:7px;background:#edf4e6;border:1px solid #dbe8cf;font-size:12px;line-height:1.8;margin-bottom:20px}.notice strong{font-weight:600}.controls{display:flex;align-items:center;flex-wrap:wrap;gap:10px;margin-top:20px}.controls button.primary,.primary{background:var(--green);color:#fff;border-color:var(--green)}.controls button.primary:hover,.primary:hover{background:#105947}.controls .quiet,button.quiet{background:transparent;color:var(--muted);font-size:12px}.share{font-size:11px;white-space:nowrap}.result-note{padding:16px 18px;border:1px solid #c8dfb7;background:#edf6e4;border-radius:7px;margin-top:19px;font-size:13px}.result-note p{margin:7px 0 0;font-size:12px;color:var(--muted)}.question{font-size:19px;font-weight:500;line-height:1.4;margin:0 0 17px}.choice-row{display:flex;gap:10px;flex-wrap:wrap}.choice-row button{flex:1;min-width:145px;text-align:center}.choice-row button[aria-pressed=true]{background:#e2f1cf;border-color:#8bb96c;color:#214c35}.guess-panel{padding:22px;background:#f5f8ef;border:1px solid var(--line);border-radius:8px;margin-top:22px}.local-tally{font:10px/1.8 var(--mono);color:var(--muted);margin:13px 0 0}.token-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap;background:#f5f8ef;border:1px solid var(--line);border-radius:7px;padding:14px 16px;margin:0 0 20px}.token-controls label{font:11px var(--mono)}.token-controls input{flex:1;min-width:100px;accent-color:var(--green)}.token-controls button{padding:4px 10px;min-height:34px}.token-readout{font:11px var(--mono);color:var(--green);white-space:nowrap}.expert-grid{display:grid;grid-template-columns:repeat(32,minmax(0,1fr));gap:3px;margin:12px 0 10px}.expert-dot{aspect-ratio:1;background:#eaf0e4;border-radius:2px;border:1px solid #dfe8d6}.expert-dot.active{background:var(--green);border-color:var(--green)}.routing-token{font:12px var(--mono);white-space:pre-wrap;overflow-wrap:anywhere;min-height:32px;padding:7px 10px;border-radius:4px;background:#f1f6e9;display:block}.routing-label{font:10px var(--mono);color:var(--muted);margin:13px 0 0}.expert-list{font:11px/1.8 var(--mono);color:var(--green);overflow-wrap:anywhere;min-height:40px}.routing-key{font-size:10px;color:var(--muted);margin:0}.condition-link{display:inline-block;font-size:11px;margin-top:14px}.reveal-blank{border:1px dashed #c8d6bf;border-radius:7px;padding:18px;margin-top:20px;font-size:12px;color:var(--muted)}details{border-top:1px solid var(--line);padding:17px 0;margin-top:20px}summary{font-size:12px;font-weight:500;cursor:pointer;width:fit-content;padding:3px 0}details p,details li{font-size:12px;line-height:1.8;color:var(--muted)}details pre{max-height:340px;overflow:auto;padding:16px;background:#f2f5ee;border-radius:5px;font:11px/1.8 var(--mono);white-space:pre-wrap;overflow-wrap:anywhere}details ul,details ol{padding-left:22px}.evidence{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:20px;margin-top:44px;padding:30px 0 0;border-top:1px solid var(--line)}.evidence h2{font-size:23px}.evidence p:not(.eyebrow){font-size:12px;color:var(--muted);margin:0;max-width:640px}.evidence-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;justify-content:flex-end}.evidence-actions button{font-size:11px}.evidence details{grid-column:1/-1;margin-top:0}.loading,.error{padding:55px 25px;border:1px solid var(--line);border-radius:10px;text-align:center;background:var(--paper);color:var(--muted)}.error h2{font-size:22px;color:var(--ink)}.error p{font-size:13px;max-width:550px;margin:15px auto}.error button{margin-top:6px}footer{display:flex;justify-content:space-between;gap:20px;padding:24px 36px;border-top:1px solid var(--line);font-size:10px;color:var(--muted)}footer a{text-decoration:none}footer span{text-align:center}.toast{position:fixed;left:50%;bottom:25px;transform:translateX(-50%);padding:12px 20px;background:var(--ink);color:white;border-radius:7px;max-width:90%;font-size:12px;z-index:10}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:1000px){.experiment-grid{grid-template-columns:repeat(3,minmax(0,1fr))}.experiment-card:last-child{grid-column:span 2}.hero{gap:30px;grid-template-columns:minmax(0,1fr) 235px}.hero-note{padding-left:25px}.columns.three{grid-template-columns:1fr}.evidence{grid-template-columns:1fr}.evidence-actions{justify-content:flex-start}.experiment-head{align-items:flex-start}.case-picker{min-width:180px}}@media(max-width:680px){header{padding:14px 18px;align-items:flex-start;flex-wrap:wrap;gap:12px}.brand{font-size:13px}.brand-icon{width:31px;height:31px;font-size:23px}header nav{gap:18px;font-size:10px;margin-left:42px;margin-top:-2px}main{padding:0 18px 35px}.hero{grid-template-columns:1fr;padding:36px 0 28px;gap:22px}h1{font-size:43px;letter-spacing:-2px}.intro{font-size:14px}.hero-note{display:flex;align-items:center;gap:16px;padding:13px 0;border-left:0;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}.hero-note p{font-size:13px;margin:0;min-width:125px}.hero-note>span:last-child{font-size:10px;line-height:1.5}.signal{gap:3px;width:44px;flex-shrink:0}.signal i{width:4px}.experiment-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-bottom:22px}.experiment-card{padding:14px 13px}.experiment-card:last-child{grid-column:span 2;display:grid;grid-template-columns:32px minmax(0,1fr);gap:6px 10px;align-items:center}.experiment-card:last-child strong{min-height:0}.experiment-card:last-child small{grid-column:2;margin-top:0}.experiment-card:last-child .card-number{margin:0;grid-row:span 2}.card-number{margin-bottom:15px}.experiment-card strong{font-size:13px;min-height:35px}.experiment-card small{font-size:10px}.experiment-head{flex-direction:column;gap:18px;padding:25px 0 19px}.experiment-head h2{font-size:28px}.experiment-head p:not(.eyebrow){font-size:12px}.case-picker{width:100%;min-width:0}.case-picker select{max-width:none}.case-top{padding:19px 17px;gap:12px}.case-top h3{font-size:16px}.case-top .tag{font-size:8px;white-space:normal;max-width:95px;text-align:center}.case-body{padding:17px}.columns{grid-template-columns:1fr;gap:15px}.condition-heading,.condition-content{padding:14px}.output{font-size:12px;max-height:320px}.metrics{gap:12px}.guess-panel{padding:17px}.question{font-size:17px}.choice-row button{min-width:100px;font-size:12px}.token-controls{gap:10px;padding:12px}.token-controls label{width:100%}.token-controls input{min-width:70px}.expert-grid{gap:2px}.prompt{font-size:11px;padding:12px}.evidence{margin-top:28px}.evidence h2{font-size:21px}.evidence-actions{gap:7px}.evidence-actions button{padding:9px 11px}footer{padding:20px 18px;flex-wrap:wrap;gap:12px}footer span{order:3;flex-basis:100%;text-align:left}.controls{gap:8px}.controls button{font-size:11px;padding:9px 12px}.share{font-size:10px}.hero-note>span:last-child{max-width:120px}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}
|
| 2 |
+
|
| 3 |
+
#experiment{scroll-margin-top:20px}
|
experiments/lab.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(function (global) {
|
| 2 |
+
'use strict';
|
| 3 |
+
const DEFINITIONS = {
|
| 4 |
+
reroute: { number: '01', title: 'One word changed.', short: 'Follow the routing', summary: 'Compare two prompts and the measured expert selections they produced.' },
|
| 5 |
+
quant: { number: '02', title: 'Spot the 4-bit model.', short: 'A blind precision test', summary: 'Read two answers before seeing their precision, timings and checks.' },
|
| 6 |
+
surgery: { number: '03', title: 'What else broke?', short: 'A controlled intervention', summary: 'Inspect a behavior intervention and its effects across the same test cases.' },
|
| 7 |
+
memory: { number: '04', title: 'Remembered, but outdated.', short: 'The correction detective', summary: 'Predict the right answer, then see whether the model used a correction or repeated an old fact.' },
|
| 8 |
+
sound: { number: '05', title: 'A model, heard.', short: 'Listen to real routing', summary: 'Turn measured model activity into a musical score you can inspect.' }
|
| 9 |
+
};
|
| 10 |
+
function assert(condition, message) { if (!condition) throw new Error(message); }
|
| 11 |
+
function validateResults(data) {
|
| 12 |
+
assert(data && data.schema_version === 1, 'This results file uses an unsupported format.');
|
| 13 |
+
assert(data.experiments && typeof data.experiments === 'object', 'Experiment results are missing.');
|
| 14 |
+
for (const key of ['reroute', 'quant', 'surgery', 'memory']) {
|
| 15 |
+
const exp = data.experiments[key];
|
| 16 |
+
assert(exp && Array.isArray(exp.cases) && exp.cases.length > 0, `The ${key} experiment has no cases.`);
|
| 17 |
+
const seen = new Set();
|
| 18 |
+
for (const item of exp.cases) {
|
| 19 |
+
assert(item && typeof item.id === 'string' && item.id.length > 0 && !seen.has(item.id), `Invalid or repeated case ID in ${key}.`);
|
| 20 |
+
seen.add(item.id);
|
| 21 |
+
assert(Array.isArray(item.conditions) && item.conditions.length >= 1 && item.conditions.length <= 12, `Conditions are missing in ${key}/${item.id}.`);
|
| 22 |
+
const conditionIds = new Set();
|
| 23 |
+
for (const condition of item.conditions) {
|
| 24 |
+
assert(condition && typeof condition.id === 'string' && !conditionIds.has(condition.id), `Invalid condition ID in ${key}/${item.id}.`);
|
| 25 |
+
conditionIds.add(condition.id);
|
| 26 |
+
assert(typeof condition.output === 'string', `A captured answer is missing in ${key}/${item.id}.`);
|
| 27 |
+
if (condition.routing && condition.routing.tokens !== undefined) {
|
| 28 |
+
assert(Array.isArray(condition.routing.tokens), `Routing tokens must be an array in ${key}/${item.id}.`);
|
| 29 |
+
for (const frame of condition.routing.tokens) {
|
| 30 |
+
assert(frame && typeof frame.token === 'string' && Array.isArray(frame.experts), 'A routing frame is invalid.');
|
| 31 |
+
assert(frame.experts.every(id => Number.isInteger(id) && id >= 0 && id < (condition.routing.expert_count || 256)), 'An expert index is outside this model.');
|
| 32 |
+
if (frame.weights !== undefined) assert(Array.isArray(frame.weights) && frame.weights.length === frame.experts.length && frame.weights.every(w => Number.isFinite(w) && w >= 0), 'Routing weights are invalid.');
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
if (key === 'quant') assert(item.conditions.length === 2, 'The blind comparison needs exactly two answers.');
|
| 37 |
+
if (key === 'memory') assert(Array.isArray(item.choices) && item.choices.length >= 2 && item.choices.every(c => typeof c === 'string'), 'The memory case needs answer choices.');
|
| 38 |
+
}
|
| 39 |
+
}
|
| 40 |
+
return data;
|
| 41 |
+
}
|
| 42 |
+
function stablePairOrder(id, conditions) {
|
| 43 |
+
let hash = 2166136261;
|
| 44 |
+
for (const char of String(id)) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619) >>> 0;
|
| 45 |
+
return hash % 2 ? conditions.slice().reverse() : conditions.slice();
|
| 46 |
+
}
|
| 47 |
+
function isLowBit(condition) { return /(?:^|[^0-9])q?4(?:[^0-9]|$)/i.test(String(condition && condition.quant || '')); }
|
| 48 |
+
function formatMetric(value) {
|
| 49 |
+
if (typeof value === 'number') return Number.isInteger(value) ? value.toLocaleString('en-US') : value.toLocaleString('en-US', { maximumFractionDigits: 5 });
|
| 50 |
+
if (typeof value === 'boolean') return value ? 'Yes' : 'No';
|
| 51 |
+
return typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value ?? '');
|
| 52 |
+
}
|
| 53 |
+
function readableKey(key) { return key.replace(/_/g, ' ').replace(/\bms\b/g, '(ms)').replace(/\bseconds\b/g, '(s)'); }
|
| 54 |
+
function promptFor(item, condition) {
|
| 55 |
+
if (typeof condition.prompt === 'string') return condition.prompt;
|
| 56 |
+
const linked = Array.isArray(item.prompts) && item.prompts.find(p => p.id === condition.prompt_id);
|
| 57 |
+
return linked ? linked.text : typeof item.prompt === 'string' ? item.prompt : '';
|
| 58 |
+
}
|
| 59 |
+
function fixturesFrom(data) {
|
| 60 |
+
const experiments = {};
|
| 61 |
+
for (const [key, exp] of Object.entries(data.experiments)) {
|
| 62 |
+
experiments[key] = {
|
| 63 |
+
method: exp.method,
|
| 64 |
+
cases: exp.cases.map(item => ({ id: item.id, title: item.title, prompt: item.prompt, prompts: item.prompts, question: item.question, ground_truth: item.ground_truth, choices: item.choices, intervention: item.intervention, conditions: item.conditions.map(c => ({ id: c.id, model: c.model, quant: c.quant, prompt: promptFor(item, c), settings: c.settings, intervention: c.intervention })) }))
|
| 65 |
+
};
|
| 66 |
+
}
|
| 67 |
+
return { schema_version: 1, generated_at: data.generated_at, provenance: data.provenance, methodology: data.methodology, experiments };
|
| 68 |
+
}
|
| 69 |
+
function routingFrame(condition, step) {
|
| 70 |
+
const frames = condition.routing && condition.routing.tokens;
|
| 71 |
+
return Array.isArray(frames) && step < frames.length ? frames[step] : null;
|
| 72 |
+
}
|
| 73 |
+
const api = { validateResults, stablePairOrder, isLowBit, formatMetric, promptFor, fixturesFrom, routingFrame };
|
| 74 |
+
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
| 75 |
+
if (typeof document === 'undefined') return;
|
| 76 |
+
const $ = id => document.getElementById(id);
|
| 77 |
+
let data, active = 'reroute', currentCase, tokenStep = 0, toastTimer;
|
| 78 |
+
const reveals = new Set();
|
| 79 |
+
const stateKey = 'alphabell-experiments-v1';
|
| 80 |
+
let guesses = { quant: {}, memory: {} };
|
| 81 |
+
try { const saved = JSON.parse(localStorage.getItem(stateKey) || 'null'); if (saved && saved.quant && saved.memory) guesses = saved; } catch (_) { /* Private browser storage may be unavailable. */ }
|
| 82 |
+
function node(tag, className, content) {
|
| 83 |
+
const element = document.createElement(tag);
|
| 84 |
+
if (className) element.className = className;
|
| 85 |
+
if (content !== undefined) element.textContent = String(content);
|
| 86 |
+
return element;
|
| 87 |
+
}
|
| 88 |
+
function append(parent, ...children) { for (const child of children) if (child) parent.append(child); return parent; }
|
| 89 |
+
function button(text, action, className) { const el = node('button', className, text); el.type = 'button'; el.addEventListener('click', action); return el; }
|
| 90 |
+
function safeURL(url) {
|
| 91 |
+
if (typeof url !== 'string') return null;
|
| 92 |
+
try { const parsed = new URL(url, location.href); return ['http:', 'https:'].includes(parsed.protocol) ? parsed.href : null; } catch (_) { return null; }
|
| 93 |
+
}
|
| 94 |
+
function link(text, url, className) {
|
| 95 |
+
const href = safeURL(url);
|
| 96 |
+
if (!href) return null;
|
| 97 |
+
const el = node('a', className, text); el.href = href;
|
| 98 |
+
if (new URL(href).origin !== location.origin) { el.target = '_blank'; el.rel = 'noreferrer'; }
|
| 99 |
+
return el;
|
| 100 |
+
}
|
| 101 |
+
function toast(message) { $('toast').textContent = message; $('toast').hidden = false; clearTimeout(toastTimer); toastTimer = setTimeout(() => { $('toast').hidden = true; }, 3000); }
|
| 102 |
+
function save() { try { localStorage.setItem(stateKey, JSON.stringify(guesses)); } catch (_) { toast('This browser cannot save guesses. Your current answer is still recorded for this visit.'); } }
|
| 103 |
+
function download(object, name) {
|
| 104 |
+
const url = URL.createObjectURL(new Blob([JSON.stringify(object, null, 2) + '\n'], { type: 'application/json' }));
|
| 105 |
+
const anchor = document.createElement('a'); anchor.href = url; anchor.download = name; anchor.click(); setTimeout(() => URL.revokeObjectURL(url), 1000);
|
| 106 |
+
}
|
| 107 |
+
function displayValue(parent, value) {
|
| 108 |
+
if (value === undefined || value === null || value === '') return;
|
| 109 |
+
if (typeof value === 'string') { parent.append(node('p', '', value)); return; }
|
| 110 |
+
if (Array.isArray(value)) { const list = node('ul'); value.forEach(item => append(list, node('li', '', typeof item === 'object' ? JSON.stringify(item) : item))); parent.append(list); return; }
|
| 111 |
+
parent.append(node('pre', '', JSON.stringify(value, null, 2)));
|
| 112 |
+
}
|
| 113 |
+
function metrics(object) {
|
| 114 |
+
const list = node('dl', 'metrics');
|
| 115 |
+
for (const [key, value] of Object.entries(object || {})) {
|
| 116 |
+
if (value === undefined || value === null) continue;
|
| 117 |
+
list.append(append(node('div'), node('dt', '', readableKey(key)), node('dd', '', formatMetric(value))));
|
| 118 |
+
}
|
| 119 |
+
return list.children.length ? list : null;
|
| 120 |
+
}
|
| 121 |
+
function setupNav() {
|
| 122 |
+
$('experimentNav').replaceChildren();
|
| 123 |
+
for (const [key, def] of Object.entries(DEFINITIONS)) {
|
| 124 |
+
const anchor = node('a', 'experiment-card'); anchor.href = key === 'sound' ? 'sound.html' : '?experiment=' + key;
|
| 125 |
+
anchor.dataset.experiment = key;
|
| 126 |
+
append(anchor, node('span', 'card-number', def.number), node('strong', '', def.title), node('small', '', def.short), node('span', 'card-arrow', '↗'));
|
| 127 |
+
if (key !== 'sound') anchor.addEventListener('click', event => { event.preventDefault(); selectExperiment(key, null, true); });
|
| 128 |
+
$('experimentNav').append(anchor);
|
| 129 |
+
}
|
| 130 |
+
}
|
| 131 |
+
function selectExperiment(key, id, push) {
|
| 132 |
+
active = ['reroute', 'quant', 'surgery', 'memory'].includes(key) ? key : 'reroute';
|
| 133 |
+
if (!data) {
|
| 134 |
+
const url = new URL(location.href); url.searchParams.set('experiment', active); url.searchParams.delete('case'); history.pushState(null, '', url);
|
| 135 |
+
return;
|
| 136 |
+
}
|
| 137 |
+
const cases = data.experiments[active].cases;
|
| 138 |
+
currentCase = cases.find(item => item.id === id) || cases[0];
|
| 139 |
+
tokenStep = 0;
|
| 140 |
+
if (push) {
|
| 141 |
+
const url = new URL(location.href); url.searchParams.set('experiment', active); url.searchParams.set('case', currentCase.id); history.pushState(null, '', url);
|
| 142 |
+
}
|
| 143 |
+
document.querySelectorAll('[data-experiment]').forEach(a => { if (a.dataset.experiment === active) a.setAttribute('aria-current', 'page'); else a.removeAttribute('aria-current'); });
|
| 144 |
+
render();
|
| 145 |
+
if (push) $('experiment').scrollIntoView({block:'start',behavior:'smooth'});
|
| 146 |
+
}
|
| 147 |
+
function conditionCard(item, condition, options = {}) {
|
| 148 |
+
const card = node('section', 'condition');
|
| 149 |
+
const heading = node('div', 'condition-heading');
|
| 150 |
+
append(heading, node('h4', '', options.label || condition.label || condition.id));
|
| 151 |
+
if (!options.blind) heading.append(node('span', 'tag', [condition.model, condition.quant].filter(Boolean).join(' · ')));
|
| 152 |
+
const content = node('div', 'condition-content');
|
| 153 |
+
const prompt = promptFor(item, condition);
|
| 154 |
+
if (options.showPrompt && prompt) content.append(node('div', 'prompt', prompt));
|
| 155 |
+
content.append(node('div', 'output', condition.output || '(The model produced no answer.)'));
|
| 156 |
+
if (!options.blind) {
|
| 157 |
+
append(content, metrics(condition.metrics));
|
| 158 |
+
if (condition.checks) { const detail = node('details'); append(detail, node('summary', '', 'Answer checks')); displayValue(detail, condition.checks); content.append(detail); }
|
| 159 |
+
append(content, link('Open measured trace ↗', condition.trace_url, 'condition-link'));
|
| 160 |
+
}
|
| 161 |
+
append(card, heading, content); return card;
|
| 162 |
+
}
|
| 163 |
+
function shareButton() {
|
| 164 |
+
return button('Share this case ↗', async () => {
|
| 165 |
+
const url = new URL(location.href); url.searchParams.set('experiment', active); url.searchParams.set('case', currentCase.id);
|
| 166 |
+
try { await navigator.clipboard.writeText(url.href); toast('Case link copied.'); } catch (_) { toast('Copy the address in your browser to share this case.'); }
|
| 167 |
+
}, 'quiet share');
|
| 168 |
+
}
|
| 169 |
+
function render() {
|
| 170 |
+
const exp = data.experiments[active], def = DEFINITIONS[active], item = currentCase;
|
| 171 |
+
document.title = def.title + ' | alphaBell Experiments';
|
| 172 |
+
const fragment = document.createDocumentFragment(), head = node('div', 'experiment-head');
|
| 173 |
+
const title = node('h2', '', exp.title || def.title); title.id = 'experimentTitle';
|
| 174 |
+
append(head, append(node('div'), node('p', 'eyebrow', 'EXPERIMENT ' + def.number), title, node('p', '', exp.summary || def.summary)));
|
| 175 |
+
const picker = node('label', 'case-picker', 'Choose a recorded case'); const select = node('select'); select.id = 'caseSelect';
|
| 176 |
+
exp.cases.forEach((c, index) => { const option = node('option', '', String(index + 1).padStart(2, '0') + ' · ' + (c.title || c.id)); option.value = c.id; option.selected = c.id === item.id; select.append(option); });
|
| 177 |
+
select.addEventListener('change', () => selectExperiment(active, select.value, true)); append(picker, select); head.append(picker); fragment.append(head);
|
| 178 |
+
const layout = node('article', 'case-layout');
|
| 179 |
+
const top = node('div', 'case-top'), context = append(node('div'), node('h3', '', item.title || item.id));
|
| 180 |
+
if (item.description) context.append(node('p', '', item.description));
|
| 181 |
+
if (item.prompt && active !== 'reroute') context.append(node('div', 'prompt', item.prompt));
|
| 182 |
+
append(top, context, node('span', 'tag', 'Recorded experiment'));
|
| 183 |
+
const body = node('div', 'case-body');
|
| 184 |
+
if (active === 'reroute') renderReroute(body, item);
|
| 185 |
+
if (active === 'quant') renderQuant(body, item);
|
| 186 |
+
if (active === 'surgery') renderSurgery(body, item);
|
| 187 |
+
if (active === 'memory') renderMemory(body, item);
|
| 188 |
+
const controls = node('div', 'controls');
|
| 189 |
+
append(controls, button('Download this case ↓', () => download({ schema_version: data.schema_version, experiment: active, provenance: data.provenance, method: exp.method, case: item }, 'alphabell-' + active + '-' + item.id.replace(/[^a-z0-9_-]/gi, '_') + '.json'), 'quiet'), shareButton());
|
| 190 |
+
body.append(controls);
|
| 191 |
+
const method = node('details'); append(method, node('summary', '', 'How to read this experiment')); displayValue(method, exp.method); displayValue(method, exp.limitations); displayValue(method, exp.reproduce); if (item.provenance) displayValue(method, item.provenance); body.append(method);
|
| 192 |
+
append(layout, top, body); fragment.append(layout); $('experiment').replaceChildren(fragment);
|
| 193 |
+
}
|
| 194 |
+
function renderReroute(body, item) {
|
| 195 |
+
if (item.conditions.every(c => !c.routing)) body.append(node('p', 'annotation', 'Qwen Open uses recurrent heads and dense feed-forward blocks. Compare measured head activity in the full traces; expert grids apply to Flint.'));
|
| 196 |
+
body.append(node('div', 'notice', 'A routing change is a measured change in expert selection. It does not identify what an expert means, or prove that an expert caused a particular answer.'));
|
| 197 |
+
const frames = item.conditions.map(c => c.routing && c.routing.tokens || []), maxFrames = Math.max(...frames.map(f => f.length));
|
| 198 |
+
const columns = node('div', item.conditions.length > 2 ? 'columns three' : 'columns');
|
| 199 |
+
const panels = [];
|
| 200 |
+
if (maxFrames > 0) {
|
| 201 |
+
const controls = node('div', 'token-controls'), label = node('label', '', 'Generated token step'); label.htmlFor = 'routingStep';
|
| 202 |
+
const slider = node('input'); slider.type = 'range'; slider.id = 'routingStep'; slider.min = '0'; slider.max = String(maxFrames - 1); slider.value = String(tokenStep);
|
| 203 |
+
const readout = node('span', 'token-readout'); readout.setAttribute('aria-live', 'polite');
|
| 204 |
+
const update = step => { tokenStep = Math.max(0, Math.min(maxFrames - 1, step)); slider.value = String(tokenStep); readout.textContent = (tokenStep + 1) + ' / ' + maxFrames; panels.forEach(panel => paintRouting(panel, tokenStep)); };
|
| 205 |
+
const previous = button('‹', () => update(tokenStep - 1)); previous.setAttribute('aria-label', 'Previous token');
|
| 206 |
+
const next = button('›', () => update(tokenStep + 1)); next.setAttribute('aria-label', 'Next token');
|
| 207 |
+
slider.addEventListener('input', () => update(Number(slider.value))); append(controls, label, previous, slider, next, readout); body.append(controls);
|
| 208 |
+
body.append(node('p', 'annotation', 'The slider pairs tokens by generated position. Different answers may use different token boundaries and stop at different lengths.'));
|
| 209 |
+
readout.textContent = (tokenStep + 1) + ' / ' + maxFrames;
|
| 210 |
+
}
|
| 211 |
+
for (const condition of item.conditions) {
|
| 212 |
+
const card = conditionCard(item, condition, { showPrompt: true });
|
| 213 |
+
if (condition.routing && Array.isArray(condition.routing.tokens) && condition.routing.tokens.length) {
|
| 214 |
+
const panel = node('div', 'routing-panel'); panel.condition = condition;
|
| 215 |
+
const label = 'Selected experts' + (Number.isInteger(condition.routing.layer) ? ', block ' + condition.routing.layer : '');
|
| 216 |
+
append(panel, node('p', 'routing-label', label), node('code', 'routing-token'), node('div', 'expert-grid'), node('p', 'expert-list'), node('p', 'routing-key', 'Each square is an expert ID. Dark squares were selected in this captured token.'));
|
| 217 |
+
const grid = panel.querySelector('.expert-grid');
|
| 218 |
+
for (let id = 0; id < (condition.routing.expert_count || 256); id++) { const dot = node('span', 'expert-dot'); dot.title = 'Expert ' + id; dot.setAttribute('aria-hidden', 'true'); grid.append(dot); }
|
| 219 |
+
card.querySelector('.condition-content').prepend(panel); panels.push(panel); paintRouting(panel, tokenStep);
|
| 220 |
+
}
|
| 221 |
+
columns.append(card);
|
| 222 |
+
}
|
| 223 |
+
body.append(columns);
|
| 224 |
+
if (item.comparison) { const details = node('details'); append(details, node('summary', '', 'Measured comparison')); displayValue(details, item.comparison); body.append(details); }
|
| 225 |
+
}
|
| 226 |
+
function paintRouting(panel, step) {
|
| 227 |
+
const frame = routingFrame(panel.condition, step), token = panel.querySelector('.routing-token'), list = panel.querySelector('.expert-list');
|
| 228 |
+
token.textContent = frame ? frame.token : 'This answer has ended.';
|
| 229 |
+
const experts = new Set(frame ? frame.experts : []);
|
| 230 |
+
Array.from(panel.querySelector('.expert-grid').children).forEach((dot, id) => dot.classList.toggle('active', experts.has(id)));
|
| 231 |
+
list.textContent = frame ? frame.experts.map((id, index) => '#' + id + (frame.weights ? ' (' + formatMetric(frame.weights[index]) + ')' : '')).join(' · ') : 'No captured frame at this position.';
|
| 232 |
+
}
|
| 233 |
+
function renderQuant(body, item) {
|
| 234 |
+
const ordered = stablePairOrder(item.id, item.conditions), key = 'quant:' + item.id, revealed = reveals.has(key);
|
| 235 |
+
body.append(node('p', 'annotation', 'Both answers use the same case prompt. Model names, precision and measurements stay hidden until you reveal the evidence. Answer order is deterministic for each case.'));
|
| 236 |
+
const columns = node('div', 'columns'); ordered.forEach((condition, index) => columns.append(conditionCard(item, condition, { blind: !revealed, label: revealed ? 'Answer ' + String.fromCharCode(65 + index) + ' · ' + (condition.label || condition.id) : 'Answer ' + String.fromCharCode(65 + index) }))); body.append(columns);
|
| 237 |
+
const panel = node('section', 'guess-panel'); append(panel, node('h4', 'question', 'Which answer came from the 4-bit model?'));
|
| 238 |
+
const choices = node('div', 'choice-row');
|
| 239 |
+
ordered.forEach((condition, index) => {
|
| 240 |
+
const pick = button('Answer ' + String.fromCharCode(65 + index) + ' is 4-bit', () => { guesses.quant[item.id] = condition.id; save(); reveals.add(key); render(); });
|
| 241 |
+
pick.disabled = revealed; pick.setAttribute('aria-pressed', String(guesses.quant[item.id] === condition.id)); choices.append(pick);
|
| 242 |
+
}); panel.append(choices);
|
| 243 |
+
if (!revealed) { const controls = node('div', 'controls'); controls.append(button('Reveal without guessing', () => { reveals.add(key); render(); }, 'quiet')); panel.append(controls); }
|
| 244 |
+
else {
|
| 245 |
+
const low = ordered.find(isLowBit), guess = guesses.quant[item.id], chosen = ordered.find(c => c.id === guess);
|
| 246 |
+
const message = !low ? 'Precision labels are shown above.' : guess ? (isLowBit(chosen) ? 'You spotted it.' : 'The other answer was 4-bit.') : 'The precision is revealed.';
|
| 247 |
+
const result = node('div', 'result-note', message);
|
| 248 |
+
if (low) result.append(node('p', '', 'Answer ' + String.fromCharCode(65 + ordered.indexOf(low)) + ' used ' + low.quant + '. A single answer is not a quality benchmark. Check the full outputs and measured results.'));
|
| 249 |
+
append(panel, result); if (item.checks) displayValue(panel, item.checks);
|
| 250 |
+
}
|
| 251 |
+
let total = 0, correct = 0;
|
| 252 |
+
for (const test of data.experiments.quant.cases) { const guess = guesses.quant[test.id]; if (guess) { total++; const cond = test.conditions.find(c => c.id === guess); if (cond && isLowBit(cond)) correct++; } }
|
| 253 |
+
panel.append(node('p', 'local-tally', 'LOCAL ONLY · ' + correct + ' / ' + total + ' correct guesses saved in this browser. No votes are uploaded.'));
|
| 254 |
+
body.append(panel);
|
| 255 |
+
}
|
| 256 |
+
function renderSurgery(body, item) {
|
| 257 |
+
const intervention = item.intervention || {};
|
| 258 |
+
const notice = node('div', 'notice'); append(notice, node('strong', '', intervention.kind ? readableKey(intervention.kind) : 'Recorded intervention'));
|
| 259 |
+
if (intervention.description) notice.append(node('div', '', intervention.description));
|
| 260 |
+
if (/prompt/i.test(intervention.kind || '')) notice.append(node('div', '', 'This intervention changes the prompt. It does not modify model weights.'));
|
| 261 |
+
if (/activation/i.test(intervention.kind || '')) notice.append(node('div', '', 'This intervention changes activations during execution. It does not rewrite the model weights.'));
|
| 262 |
+
body.append(notice);
|
| 263 |
+
const columns = node('div', item.conditions.length > 2 ? 'columns three' : 'columns'); item.conditions.forEach(c => columns.append(conditionCard(item, c, { showPrompt: true }))); body.append(columns);
|
| 264 |
+
if (item.checks) { const results = node('div', 'result-note'); results.append(node('strong', '', 'Behavior and regression checks')); displayValue(results, item.checks); body.append(results); }
|
| 265 |
+
if (item.comparison) { const results = node('details'); results.append(node('summary', '', 'Intervention comparison')); displayValue(results, item.comparison); body.append(results); }
|
| 266 |
+
body.append(node('p', 'annotation', 'A visible change is a result for this case and intervention. Check the other cases before generalizing about the model. Negative and unchanged results are useful evidence too.'));
|
| 267 |
+
}
|
| 268 |
+
function truthText(item) {
|
| 269 |
+
return typeof item.ground_truth === 'string' ? item.ground_truth : item.ground_truth && (item.ground_truth.answer || item.ground_truth.value) || JSON.stringify(item.ground_truth);
|
| 270 |
+
}
|
| 271 |
+
function renderMemory(body, item) {
|
| 272 |
+
const key = 'memory:' + item.id, revealed = reveals.has(key), selected = guesses.memory[item.id];
|
| 273 |
+
const panel = node('section', 'guess-panel'); append(panel, node('h4', 'question', item.question || 'What should the model answer after the correction?'));
|
| 274 |
+
const choices = node('div', 'choice-row');
|
| 275 |
+
item.choices.forEach(choice => {
|
| 276 |
+
const pick = button(choice, () => { guesses.memory[item.id] = choice; save(); render(); }); pick.disabled = revealed; pick.setAttribute('aria-pressed', String(selected === choice)); choices.append(pick);
|
| 277 |
+
}); panel.append(choices);
|
| 278 |
+
if (!revealed) {
|
| 279 |
+
const reveal = button('Reveal model answers', () => { reveals.add(key); render(); }, 'primary'); reveal.disabled = !selected;
|
| 280 |
+
const controls = node('div', 'controls'); controls.append(reveal); panel.append(controls);
|
| 281 |
+
panel.append(node('p', 'local-tally', 'Choose the answer the model should give. Your choice stays in this browser.'));
|
| 282 |
+
body.append(panel, node('div', 'reveal-blank', 'The captured answers and scoring stay hidden until you make a prediction.'));
|
| 283 |
+
} else {
|
| 284 |
+
const truth = truthText(item), correct = String(selected).trim().toLowerCase() === String(truth).trim().toLowerCase();
|
| 285 |
+
const result = node('div', 'result-note', (correct ? 'Your answer matches the ground truth. ' : 'Compare your prediction with the ground truth. ') + 'Expected answer: ' + truth);
|
| 286 |
+
result.append(node('p', '', 'Your prediction: ' + selected + '. The model answers below are separate measured results.'));
|
| 287 |
+
panel.append(result); body.append(panel);
|
| 288 |
+
const columns = node('div', item.conditions.length > 2 ? 'columns three' : 'columns'); item.conditions.forEach(c => columns.append(conditionCard(item, c, { showPrompt: true }))); body.append(columns);
|
| 289 |
+
if (item.checks) { const check = node('div', 'result-note'); check.append(node('strong', '', 'Correction and recall checks')); displayValue(check, item.checks); body.append(check); }
|
| 290 |
+
}
|
| 291 |
+
body.append(node('p', 'annotation', 'Using an in-context correction and remembering a fact are different tests. The prompts show which information each condition received. These results do not measure a permanent change to model memory.'));
|
| 292 |
+
}
|
| 293 |
+
function renderMethods() {
|
| 294 |
+
const target = $('methodBody'); target.replaceChildren();
|
| 295 |
+
if (data.generated_at) target.append(node('p', '', 'Results prepared: ' + new Intl.DateTimeFormat('en-US', {dateStyle:'medium',timeStyle:'short',timeZone:'America/Los_Angeles'}).format(new Date(data.generated_at)) + ' Pacific time.'));
|
| 296 |
+
displayValue(target, data.methodology);
|
| 297 |
+
const provenance = data.provenance || {};
|
| 298 |
+
if (Array.isArray(provenance.model_sources)) {
|
| 299 |
+
target.append(node('h3', '', 'Source models'));
|
| 300 |
+
const list = node('ul'); provenance.model_sources.forEach(source => { const li = node('li'); append(li, link(source.name || source.url, source.url)); if (source.revision) li.append(document.createTextNode(' · revision ' + source.revision)); list.append(li); }); target.append(list);
|
| 301 |
+
}
|
| 302 |
+
const detail = { ...provenance }; delete detail.model_sources; displayValue(target, detail);
|
| 303 |
+
}
|
| 304 |
+
async function load() {
|
| 305 |
+
$('experiment').replaceChildren(node('div', 'loading', 'Opening measured experiments…'));
|
| 306 |
+
try {
|
| 307 |
+
const response = await fetch('data/results.json', { cache: 'no-cache' });
|
| 308 |
+
if (!response.ok) throw new Error('The results could not be downloaded (HTTP ' + response.status + ').');
|
| 309 |
+
data = validateResults(await response.json());
|
| 310 |
+
renderMethods(); $('evidence').hidden = false;
|
| 311 |
+
const params = new URLSearchParams(location.search); selectExperiment(params.get('experiment'), params.get('case'), false); if (params.has('experiment')) requestAnimationFrame(() => $('experiment').scrollIntoView({block:'start'}));
|
| 312 |
+
} catch (error) {
|
| 313 |
+
$('evidence').hidden = true;
|
| 314 |
+
const panel = node('div', 'error'); panel.setAttribute('role', 'alert');
|
| 315 |
+
append(panel, node('h2', '', 'The measured results could not be opened.'), node('p', '', error.message || 'Please try loading the experiment again.'), button('Try again', load));
|
| 316 |
+
$('experiment').replaceChildren(panel);
|
| 317 |
+
}
|
| 318 |
+
}
|
| 319 |
+
setupNav();
|
| 320 |
+
$('downloadResults').addEventListener('click', () => { if (data) download(data, 'alphabell-experiment-results.json'); });
|
| 321 |
+
$('downloadFixtures').addEventListener('click', () => { if (data) download(fixturesFrom(data), 'alphabell-experiment-prompts.json'); });
|
| 322 |
+
$('resetGuesses').addEventListener('click', () => { guesses = { quant: {}, memory: {} }; reveals.clear(); save(); if (data) render(); toast('Local guesses cleared.'); });
|
| 323 |
+
window.addEventListener('popstate', () => { if (data) { const params = new URLSearchParams(location.search); selectExperiment(params.get('experiment'), params.get('case'), false); } });
|
| 324 |
+
load();
|
| 325 |
+
})(typeof window !== 'undefined' ? window : globalThis);
|
experiments/reproduce/LLAMA-LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2023-2026 The ggml authors
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
experiments/reproduce/LLAMA-MIT-LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2023-2026 The ggml authors
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
experiments/reproduce/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Matched quantization and FFN surgery
|
| 2 |
+
|
| 3 |
+
The scripts replay six fixed prompts on Flint and alphaBell Qwen Open with matched Q4_1 and Q8_0 checkpoints. A second experiment scales Flint's computed feed-forward output in block 12 to 1, 0.5 or 0 before the residual addition. Block numbering starts at zero. The intervention applies to every prompt and decode position; it changes runtime activations and leaves checkpoint files unchanged.
|
| 4 |
+
|
| 5 |
+
The suite uses CPU execution, eight threads, nice 10, a 2048-token context, Flash Attention disabled, temperature zero, seed 42, reasoning disabled and a 64-token output limit. Each checkpoint receives a warm-up. Every request clears model memory before processing its prompt.
|
| 6 |
+
|
| 7 |
+
## Runtime source
|
| 8 |
+
|
| 9 |
+
The measured runtime identifies its assembled source as `74686f084c53506a7afa471d857db02e5c912543`. Rebuild the same source using the public upstream base and included patch. The resulting Git tree must be `83e9d4c276f84753cce063502a2a9e2325f15ce3`; the build script checks this exact tree and rejects unstaged changes. No upstream commit containing the assembled runtime is required.
|
| 10 |
+
|
| 11 |
+
Prerequisites tested on Ubuntu 24.04: GCC 13.3.0, CMake 3.28.3, Git 2.43.0 and Python 3.12.3. The JSON header is vendored in llama.cpp. The Python runner uses only the standard library.
|
| 12 |
+
|
| 13 |
+
Run from this directory. The commands create a dedicated runtime checkout:
|
| 14 |
+
|
| 15 |
+
```bash
|
| 16 |
+
git clone --no-checkout https://github.com/ggml-org/llama.cpp.git runtime-source
|
| 17 |
+
git -C runtime-source checkout --detach 0a1ef309cdbb9f2024f58a38fb4c42352c5db569
|
| 18 |
+
git -C runtime-source apply --index ../llama-flint-runtime.patch
|
| 19 |
+
cmake -S runtime-source -B runtime-source/build \
|
| 20 |
+
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DGGML_CUDA=OFF
|
| 21 |
+
cmake --build runtime-source/build --target llama -j 4
|
| 22 |
+
bash quant-surgery-build.sh runtime-source runtime-source/build/bin quant-surgery
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
The included patch adds Flint support and hexadecimal string escapes. llama.cpp retains its MIT license. The measured build had CUDA support available but explicitly selected an empty GPU device list and zero GPU layers. The CPU-only build above avoids allocating a GPU.
|
| 26 |
+
|
| 27 |
+
## Checkpoints and execution
|
| 28 |
+
|
| 29 |
+
The exact matched checkpoints are available in [alphaBell Flint Matched GGUF](https://huggingface.co/alphabell/Flint-Matched-GGUF) and [alphaBell Qwen Open GGUF](https://huggingface.co/alphabell/alphaBell-Qwen3.8-27B-Open-GGUF).
|
| 30 |
+
|
| 31 |
+
Set `FLINT_Q8_GGUF`, `FLINT_Q4_GGUF`, `QWEN_Q8_GGUF` and `QWEN_Q4_GGUF` to the matching checkpoint files. The release records each file's byte count and SHA-256. Each pair was converted from one BF16 source, then quantized with `llama-quantize --pure` into Q4_1 and Q8_0. Qwen excludes vision and the multi-token prediction head. Its source is the alphaBell post-trained BF16 model, rather than the unmodified Qwen parent or the published FP8 file.
|
| 32 |
+
|
| 33 |
+
```bash
|
| 34 |
+
python3 quant-surgery.py \
|
| 35 |
+
--engine ./quant-surgery \
|
| 36 |
+
--flint-q8 "$FLINT_Q8_GGUF" --flint-q4 "$FLINT_Q4_GGUF" \
|
| 37 |
+
--qwen-q8 "$QWEN_Q8_GGUF" --qwen-q4 "$QWEN_Q4_GGUF" \
|
| 38 |
+
--flint-config ../../config.json \
|
| 39 |
+
--qwen-config ../../models/qwen.json \
|
| 40 |
+
--output results/quant-surgery.json
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
The runner starts one temporary CPU worker at a time, closes each worker after its checkpoint's cases, and saves results after each case. It writes model specifications and diagnostic logs beside the output. No service restart or running model endpoint is involved.
|
| 44 |
+
|
| 45 |
+
## Measurements and checks
|
| 46 |
+
|
| 47 |
+
Each result includes the exact output, completion reason, input/output token counts, native elapsed time, client request time, first-token latency, decode throughput and process peak resident memory. Peak memory uses Linux `ru_maxrss`, in KiB, and includes loading and warm-up. Latency includes this instrumented CPU engine's overhead. These short cases are an exploratory comparison, rather than a general performance benchmark.
|
| 48 |
+
|
| 49 |
+
For each output token, the surgery records FFN output RMS in blocks 11, 12 and 13, plus block 13 residual RMS. The validation checks that block 12 RMS follows the requested scale in every recorded frame. On the first frame, block 11 must match the baseline exactly and block 13's residual must change under either intervention. Later generated tokens can differ, so later frames need not share identical inputs.
|
| 50 |
+
|
| 51 |
+
The fixed checks cover exact arithmetic, strict JSON, factual recall, fact correction, a Python expression and a one-sentence explanation. Python expressions are checked against an explicit allowlist; generated code is never executed. All six cases and both quantizations are retained. Identical answers remain visible in the blind comparison. A score here describes these six prompts only.
|
| 52 |
+
|
| 53 |
+
Flint's checkpoint license is Apache 2.0. The alphaBell Qwen Open derivative has alphaBell's research and noncommercial license, with its Qwen and Alpaca notices. Follow each checkpoint's model card and license when using or redistributing weights.
|
| 54 |
+
|
| 55 |
+
## Full topology captures
|
| 56 |
+
|
| 57 |
+
`capture-study.cpp` is the standalone Observatory trace recorder used by the one-word routing, fact-correction and trace-music projects. It records Flint attention/head RMS, top-six routing, shared/routed FFN RMS and residual RMS. For alphaBell Qwen Open it records full attention and dense output, plus recurrent head RMS, write gates, retention and state RMS. Tensor byte strides and reshaped routing weights are preserved.
|
| 58 |
+
|
| 59 |
+
Build it against the same pinned runtime source and libraries:
|
| 60 |
+
|
| 61 |
+
```bash
|
| 62 |
+
bash capture-study-build.sh runtime-source runtime-source/build/bin capture-study
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
The runner reads the exact 24 public routing and correction fixtures from `../data/results.json`, using 16 CPU threads. Routing captures allow 48 output tokens; correction captures allow 16. All use temperature zero, seed 42, reasoning disabled, a 2048-token context and Flash Attention disabled. It verifies the Q8_0 checkpoint hashes before generation and clears model memory between prompts.
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
python3 capture-study.py \
|
| 69 |
+
--engine ./capture-study \
|
| 70 |
+
--flint-model "$FLINT_Q8_GGUF" --qwen-model "$QWEN_Q8_GGUF" \
|
| 71 |
+
--output-dir captures \
|
| 72 |
+
--compare-root ../..
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
`--compare-root` checks every new token ID, byte sequence, input position and layer measurement against the recorded public traces. It excludes timing and creation metadata, which describe the new run. Use repeated `--only` arguments to select particular fixtures:
|
| 76 |
+
|
| 77 |
+
```bash
|
| 78 |
+
python3 capture-study.py \
|
| 79 |
+
--engine ./capture-study \
|
| 80 |
+
--flint-model "$FLINT_Q8_GGUF" \
|
| 81 |
+
--only reroute-json-color-flint-a \
|
| 82 |
+
--output-dir blue-capture \
|
| 83 |
+
--compare-root ../..
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
The output contains compressed trace JSON, a SHA-256 manifest and a verification report. Native specifications and diagnostic logs stay in a separate `worker-files` subdirectory. Public trace files contain model source URLs and public fixture IDs, with no host or filesystem paths. To import one into Observatory:
|
| 87 |
+
|
| 88 |
+
```bash
|
| 89 |
+
gzip -dc blue-capture/reroute-json-color-flint-a.json.gz > blue-capture.json
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
The original recorded Flint Q8 container and the matched release container have different descriptive names in their headers. Their tensor descriptors, every tensor payload byte, tokenizer arrays and remaining metadata match. `flint-capture-equivalence.json` records that comparison and both container hashes.
|
| 93 |
+
|
| 94 |
+
On 23 October 2026, the rebuilt capture producer replayed all 24 fixtures with 80 frames. Every recorded layer measurement, output token ID, token byte sequence and input position matched the published captures exactly. Both regenerated model formats also passed Observatory import validation. See `capture-study-verification.json`.
|
experiments/reproduce/capture-study-build.sh
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
if [ "$#" -ne 3 ]; then
|
| 4 |
+
echo 'usage: capture-study-build.sh LLAMA_SOURCE LLAMA_LIBRARY_DIR OUTPUT' >&2
|
| 5 |
+
exit 2
|
| 6 |
+
fi
|
| 7 |
+
experiment_dir=$(cd "$(dirname "$0")" && pwd)
|
| 8 |
+
expected_tree=83e9d4c276f84753cce063502a2a9e2325f15ce3
|
| 9 |
+
actual_tree=$(git -C "$1" write-tree)
|
| 10 |
+
if [ "$actual_tree" != "$expected_tree" ] || ! git -C "$1" diff --quiet; then
|
| 11 |
+
echo 'Runtime source differs from the measured source tree. Follow README.md.' >&2
|
| 12 |
+
exit 1
|
| 13 |
+
fi
|
| 14 |
+
nice -n 10 c++ -std=c++17 -O2 \
|
| 15 |
+
-I"$1/include" -I"$1/ggml/include" -I"$1/vendor" \
|
| 16 |
+
"$experiment_dir/capture-study.cpp" -L"$2" -Wl,-rpath,"$2" \
|
| 17 |
+
-l:libllama.so.0 -lggml -lggml-base -pthread -o "$3"
|
experiments/reproduce/capture-study-verification.json
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"completed_at": "2026-10-23T11:36:10.484924+00:00",
|
| 3 |
+
"captures": 24,
|
| 4 |
+
"frames": 80,
|
| 5 |
+
"comparison_count": 24,
|
| 6 |
+
"comparisons": [
|
| 7 |
+
{
|
| 8 |
+
"capture": "reroute-json-color-flint-a",
|
| 9 |
+
"frames": 5,
|
| 10 |
+
"token_ids_bytes_positions_equal": true,
|
| 11 |
+
"all_layer_measurements_exact": true
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"capture": "reroute-json-color-flint-b",
|
| 15 |
+
"frames": 5,
|
| 16 |
+
"token_ids_bytes_positions_equal": true,
|
| 17 |
+
"all_layer_measurements_exact": true
|
| 18 |
+
},
|
| 19 |
+
{
|
| 20 |
+
"capture": "reroute-opposite-flint-a",
|
| 21 |
+
"frames": 1,
|
| 22 |
+
"token_ids_bytes_positions_equal": true,
|
| 23 |
+
"all_layer_measurements_exact": true
|
| 24 |
+
},
|
| 25 |
+
{
|
| 26 |
+
"capture": "reroute-opposite-flint-b",
|
| 27 |
+
"frames": 1,
|
| 28 |
+
"token_ids_bytes_positions_equal": true,
|
| 29 |
+
"all_layer_measurements_exact": true
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"capture": "reroute-python-flint-a",
|
| 33 |
+
"frames": 5,
|
| 34 |
+
"token_ids_bytes_positions_equal": true,
|
| 35 |
+
"all_layer_measurements_exact": true
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"capture": "reroute-python-flint-b",
|
| 39 |
+
"frames": 5,
|
| 40 |
+
"token_ids_bytes_positions_equal": true,
|
| 41 |
+
"all_layer_measurements_exact": true
|
| 42 |
+
},
|
| 43 |
+
{
|
| 44 |
+
"capture": "memory-distractors-current-flint",
|
| 45 |
+
"frames": 2,
|
| 46 |
+
"token_ids_bytes_positions_equal": true,
|
| 47 |
+
"all_layer_measurements_exact": true
|
| 48 |
+
},
|
| 49 |
+
{
|
| 50 |
+
"capture": "memory-distractors-original-flint",
|
| 51 |
+
"frames": 2,
|
| 52 |
+
"token_ids_bytes_positions_equal": true,
|
| 53 |
+
"all_layer_measurements_exact": true
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"capture": "memory-short-current-flint",
|
| 57 |
+
"frames": 2,
|
| 58 |
+
"token_ids_bytes_positions_equal": true,
|
| 59 |
+
"all_layer_measurements_exact": true
|
| 60 |
+
},
|
| 61 |
+
{
|
| 62 |
+
"capture": "memory-short-original-flint",
|
| 63 |
+
"frames": 2,
|
| 64 |
+
"token_ids_bytes_positions_equal": true,
|
| 65 |
+
"all_layer_measurements_exact": true
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"capture": "memory-twice-current-flint",
|
| 69 |
+
"frames": 2,
|
| 70 |
+
"token_ids_bytes_positions_equal": true,
|
| 71 |
+
"all_layer_measurements_exact": true
|
| 72 |
+
},
|
| 73 |
+
{
|
| 74 |
+
"capture": "memory-twice-original-flint",
|
| 75 |
+
"frames": 2,
|
| 76 |
+
"token_ids_bytes_positions_equal": true,
|
| 77 |
+
"all_layer_measurements_exact": true
|
| 78 |
+
},
|
| 79 |
+
{
|
| 80 |
+
"capture": "reroute-json-color-qwen-a",
|
| 81 |
+
"frames": 5,
|
| 82 |
+
"token_ids_bytes_positions_equal": true,
|
| 83 |
+
"all_layer_measurements_exact": true
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"capture": "reroute-json-color-qwen-b",
|
| 87 |
+
"frames": 5,
|
| 88 |
+
"token_ids_bytes_positions_equal": true,
|
| 89 |
+
"all_layer_measurements_exact": true
|
| 90 |
+
},
|
| 91 |
+
{
|
| 92 |
+
"capture": "reroute-opposite-qwen-a",
|
| 93 |
+
"frames": 1,
|
| 94 |
+
"token_ids_bytes_positions_equal": true,
|
| 95 |
+
"all_layer_measurements_exact": true
|
| 96 |
+
},
|
| 97 |
+
{
|
| 98 |
+
"capture": "reroute-opposite-qwen-b",
|
| 99 |
+
"frames": 1,
|
| 100 |
+
"token_ids_bytes_positions_equal": true,
|
| 101 |
+
"all_layer_measurements_exact": true
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"capture": "reroute-python-qwen-a",
|
| 105 |
+
"frames": 5,
|
| 106 |
+
"token_ids_bytes_positions_equal": true,
|
| 107 |
+
"all_layer_measurements_exact": true
|
| 108 |
+
},
|
| 109 |
+
{
|
| 110 |
+
"capture": "reroute-python-qwen-b",
|
| 111 |
+
"frames": 5,
|
| 112 |
+
"token_ids_bytes_positions_equal": true,
|
| 113 |
+
"all_layer_measurements_exact": true
|
| 114 |
+
},
|
| 115 |
+
{
|
| 116 |
+
"capture": "memory-distractors-current-qwen",
|
| 117 |
+
"frames": 4,
|
| 118 |
+
"token_ids_bytes_positions_equal": true,
|
| 119 |
+
"all_layer_measurements_exact": true
|
| 120 |
+
},
|
| 121 |
+
{
|
| 122 |
+
"capture": "memory-distractors-original-qwen",
|
| 123 |
+
"frames": 4,
|
| 124 |
+
"token_ids_bytes_positions_equal": true,
|
| 125 |
+
"all_layer_measurements_exact": true
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"capture": "memory-short-current-qwen",
|
| 129 |
+
"frames": 4,
|
| 130 |
+
"token_ids_bytes_positions_equal": true,
|
| 131 |
+
"all_layer_measurements_exact": true
|
| 132 |
+
},
|
| 133 |
+
{
|
| 134 |
+
"capture": "memory-short-original-qwen",
|
| 135 |
+
"frames": 4,
|
| 136 |
+
"token_ids_bytes_positions_equal": true,
|
| 137 |
+
"all_layer_measurements_exact": true
|
| 138 |
+
},
|
| 139 |
+
{
|
| 140 |
+
"capture": "memory-twice-current-qwen",
|
| 141 |
+
"frames": 4,
|
| 142 |
+
"token_ids_bytes_positions_equal": true,
|
| 143 |
+
"all_layer_measurements_exact": true
|
| 144 |
+
},
|
| 145 |
+
{
|
| 146 |
+
"capture": "memory-twice-original-qwen",
|
| 147 |
+
"frames": 4,
|
| 148 |
+
"token_ids_bytes_positions_equal": true,
|
| 149 |
+
"all_layer_measurements_exact": true
|
| 150 |
+
}
|
| 151 |
+
],
|
| 152 |
+
"all_selected_captures_complete": true,
|
| 153 |
+
"capture_source_sha256": "645a408083eb77a67269e39aae8248994ee3c59ee7842630c537c0e84a7c4cf0",
|
| 154 |
+
"engine_source_tree": "83e9d4c276f84753cce063502a2a9e2325f15ce3",
|
| 155 |
+
"checkpoint_source": "Matched Q8_0 release files, verified against checkpoints.json",
|
| 156 |
+
"observatory_import_validation": [
|
| 157 |
+
{
|
| 158 |
+
"model": "flint",
|
| 159 |
+
"frames": 5,
|
| 160 |
+
"accepted": true
|
| 161 |
+
},
|
| 162 |
+
{
|
| 163 |
+
"model": "qwen",
|
| 164 |
+
"frames": 5,
|
| 165 |
+
"accepted": true
|
| 166 |
+
}
|
| 167 |
+
]
|
| 168 |
+
}
|
experiments/reproduce/capture-study.cpp
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "llama.h"
|
| 2 |
+
#include "ggml.h"
|
| 3 |
+
#include "ggml-backend.h"
|
| 4 |
+
#include "nlohmann/json.hpp"
|
| 5 |
+
#include <algorithm>
|
| 6 |
+
#include <chrono>
|
| 7 |
+
#include <cmath>
|
| 8 |
+
#include <cstring>
|
| 9 |
+
#include <fstream>
|
| 10 |
+
#include <iostream>
|
| 11 |
+
#include <numeric>
|
| 12 |
+
#include <string>
|
| 13 |
+
#include <vector>
|
| 14 |
+
#include <unistd.h>
|
| 15 |
+
|
| 16 |
+
using json=nlohmann::json;
|
| 17 |
+
using Clock=std::chrono::steady_clock;
|
| 18 |
+
static std::string STOP,MODEL_ID,ACTIVE_ID;
|
| 19 |
+
static void emit(json j){j["model"]=MODEL_ID;if(!ACTIVE_ID.empty())j["id"]=ACTIVE_ID;std::cout<<j.dump(-1,' ',false,json::error_handler_t::replace)<<std::endl;}
|
| 20 |
+
static double seconds(Clock::time_point t){return std::chrono::duration<double>(Clock::now()-t).count();}
|
| 21 |
+
static void quiet(enum ggml_log_level level,const char*text,void*){if(level>=GGML_LOG_LEVEL_WARN)std::cerr<<text;}
|
| 22 |
+
struct Capture{
|
| 23 |
+
json config,layers=json::array();int step=0,position=0;bool enabled=false;
|
| 24 |
+
bool linear(int l)const{return config.at("layer_types")[l]=="linear_attention";}
|
| 25 |
+
int heads(int l)const{return linear(l)?config.at("linear_num_value_heads").get<int>():config.at("num_attention_heads").get<int>();}
|
| 26 |
+
int dimension(int l)const{return linear(l)?config.at("linear_value_head_dim").get<int>():config.at("head_dim").get<int>();}
|
| 27 |
+
};
|
| 28 |
+
static bool stopped(void*){
|
| 29 |
+
if(STOP.empty()||access(STOP.c_str(),F_OK)!=0)return false;
|
| 30 |
+
std::ifstream file(STOP);std::string id;std::getline(file,id);return id==ACTIVE_ID;
|
| 31 |
+
}
|
| 32 |
+
static float at(const ggml_tensor*t,const std::vector<uint8_t>&b,int x,int y=0,int z=0,int w=0){
|
| 33 |
+
size_t off=x*t->nb[0]+y*t->nb[1]+z*t->nb[2]+w*t->nb[3];
|
| 34 |
+
if(off+ggml_type_size(t->type)>b.size())throw std::runtime_error(std::string(t->name)+" tensor stride outside capture");
|
| 35 |
+
if(t->type==GGML_TYPE_F32){float v;memcpy(&v,b.data()+off,4);return v;}
|
| 36 |
+
if(t->type==GGML_TYPE_F16){ggml_fp16_t v;memcpy(&v,b.data()+off,2);return ggml_fp16_to_fp32(v);}
|
| 37 |
+
if(t->type==GGML_TYPE_I32){int32_t v;memcpy(&v,b.data()+off,4);return v;}
|
| 38 |
+
throw std::runtime_error("Unsupported activation dtype");
|
| 39 |
+
}
|
| 40 |
+
static bool capture(ggml_tensor*t,bool ask,void*user){
|
| 41 |
+
auto&c=*static_cast<Capture*>(user);if(!c.enabled)return !ask;
|
| 42 |
+
std::string name=t->name;auto dash=name.rfind('-');if(dash==std::string::npos)return !ask;
|
| 43 |
+
std::string kind=name.substr(0,dash);
|
| 44 |
+
bool head=MODEL_ID=="flint"?kind=="kqv_out":kind=="final_output"||kind=="attn_gated";
|
| 45 |
+
bool wanted=head||kind=="kq_soft_max"||kind=="ffn_moe_topk"||kind=="ffn_moe_weights_norm"||kind=="ffn_shexp"||kind=="ffn_moe_out"||kind=="ffn_out"||kind=="l_out"||kind=="beta_sigmoid"||kind=="gate"||kind=="new_state";
|
| 46 |
+
if(ask)return wanted;if(!wanted)return true;
|
| 47 |
+
int layer=std::stoi(name.substr(dash+1));if(layer<0||layer>=c.config.at("num_hidden_layers").get<int>())return true;
|
| 48 |
+
auto&l=c.layers[layer];std::vector<uint8_t>buf(ggml_nbytes(t));ggml_backend_tensor_get(t,buf.data(),0,buf.size());int last=t->ne[1]-1;
|
| 49 |
+
if(head){
|
| 50 |
+
json heads=json::array();int dim=c.dimension(layer),count=c.heads(layer);
|
| 51 |
+
if(t->ne[0]!=count*dim)throw std::runtime_error("Head output shape does not match model configuration");
|
| 52 |
+
for(int h=0;h<count;h++){double sq=0;for(int d=0;d<dim;d++){double v=at(t,buf,h*dim+d,last);sq+=v*v;}heads.push_back(std::sqrt(sq/dim));}l["heads"]=heads;
|
| 53 |
+
}else if(kind=="kq_soft_max"){
|
| 54 |
+
json heads=json::array();int valid=std::min<int>(t->ne[0],c.position+1);
|
| 55 |
+
for(int h=0;h<std::min<int>(c.heads(layer),t->ne[2]);h++){
|
| 56 |
+
std::vector<std::pair<float,int>>ranked;double entropy=0,mass=0;
|
| 57 |
+
for(int k=0;k<valid;k++){float w=at(t,buf,k,last,h);mass+=w;if(w>0)entropy-=w*std::log(w);ranked.emplace_back(w,k);}
|
| 58 |
+
int count=std::min<int>(8,ranked.size());std::partial_sort(ranked.begin(),ranked.begin()+count,ranked.end(),std::greater<>());
|
| 59 |
+
json top=json::array();for(int i=0;i<count;i++)if(ranked[i].first>0)top.push_back({ranked[i].second,ranked[i].first});
|
| 60 |
+
heads.push_back({{"top",top},{"entropy",entropy},{"sink",std::max(0.0,1-mass)}});
|
| 61 |
+
}l["attention"]=heads;
|
| 62 |
+
}else if(kind=="beta_sigmoid"||kind=="gate"){
|
| 63 |
+
json values=json::array();for(int h=0;h<c.heads(layer);h++){
|
| 64 |
+
// The decode graph reshapes gate in place from [heads,T] to [1,heads,T].
|
| 65 |
+
const double v=t->ne[0]==1?at(t,buf,0,h,t->ne[2]-1):at(t,buf,h,last);
|
| 66 |
+
values.push_back(kind=="beta_sigmoid"?v:std::exp(v));
|
| 67 |
+
}
|
| 68 |
+
l[kind=="beta_sigmoid"?"write_gate":"retention"]=values;
|
| 69 |
+
}else if(kind=="new_state"){
|
| 70 |
+
json values=json::array();for(int h=0;h<c.heads(layer);h++){double sq=0;for(int y=0;y<t->ne[1];y++)for(int x=0;x<t->ne[0];x++){double v=at(t,buf,x,y,h);sq+=v*v;}values.push_back(std::sqrt(sq/(t->ne[0]*t->ne[1])));}l["state_rms"]=values;
|
| 71 |
+
}else if(kind=="ffn_moe_topk"){
|
| 72 |
+
json ids=json::array();for(int i=0;i<c.config.at("num_experts_per_tok").get<int>();i++)ids.push_back(int(at(t,buf,i,last)));l["experts"]=ids;
|
| 73 |
+
}else if(kind=="ffn_moe_weights_norm"){
|
| 74 |
+
json weights=json::array();for(int i=0;i<c.config.at("num_experts_per_tok").get<int>();i++)weights.push_back(t->ne[0]==1?at(t,buf,0,i,t->ne[2]-1):at(t,buf,i,last));l["weights"]=weights;
|
| 75 |
+
}else{
|
| 76 |
+
double sq=0;for(int i=0;i<t->ne[0];i++){double v=at(t,buf,i,last);sq+=v*v;}
|
| 77 |
+
l[kind=="l_out"?"residual":kind=="ffn_shexp"?"shared":kind=="ffn_out"?"ffn":"routed"]=std::sqrt(sq/t->ne[0]);
|
| 78 |
+
if(kind=="l_out"){auto live=l;live.erase("attention");emit({{"type","layer"},{"step",c.step},{"layer",layer},{"data",live}});}
|
| 79 |
+
}
|
| 80 |
+
return !stopped(nullptr);
|
| 81 |
+
}
|
| 82 |
+
static std::string piece(const llama_vocab*v,llama_token t){
|
| 83 |
+
std::vector<char>b(512);int n=llama_token_to_piece(v,t,b.data(),b.size(),0,true);if(n<0){b.resize(-n);n=llama_token_to_piece(v,t,b.data(),b.size(),0,true);}return std::string(b.data(),std::max(0,n));
|
| 84 |
+
}
|
| 85 |
+
int main(int argc,char**argv){
|
| 86 |
+
if(argc<3){std::cerr<<"usage: capture-study MODEL.gguf SPEC.json\n";return 2;}
|
| 87 |
+
Capture c;json spec;
|
| 88 |
+
try{std::ifstream file(argv[2]);file>>spec;c.config=spec.at("architecture");MODEL_ID=spec.at("id");STOP=spec.at("stop_path");}catch(const std::exception&e){emit({{"type","fatal"},{"error",std::string("Invalid model specification: ")+e.what()}});return 1;}
|
| 89 |
+
llama_log_set(quiet,nullptr);llama_backend_init();auto mp=llama_model_default_params();mp.n_gpu_layers=0;ggml_backend_dev_t devices[]={nullptr};mp.devices=devices;
|
| 90 |
+
auto model=llama_model_load_from_file(argv[1],mp);if(!model){emit({{"type","fatal"},{"error","Could not load model checkpoint"}});return 1;}
|
| 91 |
+
if(llama_model_n_layer(model)!=c.config.at("num_hidden_layers").get<int>()||llama_model_n_embd(model)!=c.config.at("hidden_size").get<int>()){emit({{"type","fatal"},{"error","Checkpoint dimensions do not match the model specification"}});return 1;}
|
| 92 |
+
auto vocab=llama_model_get_vocab(model);
|
| 93 |
+
auto cp=llama_context_default_params();cp.n_ctx=2048;cp.n_batch=256;cp.n_ubatch=256;cp.n_threads=16;cp.n_threads_batch=16;cp.n_rs_seq=0;
|
| 94 |
+
cp.flash_attn_type=LLAMA_FLASH_ATTN_TYPE_DISABLED;cp.cb_eval=capture;cp.cb_eval_user_data=&c;cp.abort_callback=stopped;
|
| 95 |
+
auto ctx=llama_init_from_model(model,cp);if(!ctx){emit({{"type","fatal"},{"error","Could not initialize tracing context"}});return 1;}
|
| 96 |
+
emit({{"type","ready"},{"engine","llama.cpp 74686f08"},{"quantization",spec.at("quantization")},{"device","CPU"},{"threads",16},{"context",2048}});
|
| 97 |
+
std::string line;
|
| 98 |
+
while(std::getline(std::cin,line)){
|
| 99 |
+
llama_sampler*sampler=nullptr;
|
| 100 |
+
try{
|
| 101 |
+
auto req=json::parse(line);ACTIVE_ID=req.value("id","manual");unlink(STOP.c_str());llama_memory_clear(llama_get_memory(ctx),true);
|
| 102 |
+
std::string prompt=req.at("prompt");for(size_t pos=0;(pos=prompt.find("<|",pos))!=std::string::npos;pos+=5)prompt.replace(pos,2,"<|");
|
| 103 |
+
bool reasoning=req.value("reasoning",false),flint=MODEL_ID=="flint";
|
| 104 |
+
std::string system="You are a helpful assistant. Answer clearly and concisely.";
|
| 105 |
+
std::string text=flint?"<|system|>"+system+"<|user|>"+prompt+"<|assistant|>"+(reasoning?"":"<think></think>"):"<|im_start|>system\n"+system+"<|im_end|>\n<|im_start|>user\n"+prompt+"<|im_end|>\n<|im_start|>assistant\n<think>\n"+(reasoning?"":"\n</think>\n\n");
|
| 106 |
+
int n=-llama_tokenize(vocab,text.data(),text.size(),nullptr,0,flint,true);
|
| 107 |
+
if(n<=0)throw std::runtime_error("Could not tokenize the prompt");
|
| 108 |
+
if(n>256)throw std::runtime_error("Prompt exceeds the tracing limit of 256 tokens. Shorten it and run again.");
|
| 109 |
+
std::vector<llama_token>tokens(n);llama_tokenize(vocab,text.data(),text.size(),tokens.data(),n,flint,true);
|
| 110 |
+
json pieces=json::array();for(auto t:tokens)pieces.push_back(piece(vocab,t));
|
| 111 |
+
int limit=std::clamp(req.value("max_tokens",64),1,256);sampler=llama_sampler_chain_init(llama_sampler_chain_default_params());double temperature=req.value("temperature",0.0);
|
| 112 |
+
if(temperature<=0)llama_sampler_chain_add(sampler,llama_sampler_init_greedy());else{llama_sampler_chain_add(sampler,llama_sampler_init_top_k(40));llama_sampler_chain_add(sampler,llama_sampler_init_top_p(.95,1));llama_sampler_chain_add(sampler,llama_sampler_init_temp(temperature));llama_sampler_chain_add(sampler,llama_sampler_init_dist(req.value("seed",42)));}
|
| 113 |
+
emit({{"type","start"},{"prompt_tokens",n},{"tokens",pieces},{"max_tokens",limit},{"temperature",temperature},{"seed",req.value("seed",42)},{"reasoning",reasoning}});
|
| 114 |
+
auto started=Clock::now();int pos=0,count=n,generated=0;std::string reason="limit";
|
| 115 |
+
for(int step=0;step<limit;step++){
|
| 116 |
+
c.step=step;c.position=pos+count-1;c.layers=json::array();for(int i=0;i<c.config.at("num_hidden_layers").get<int>();i++)c.layers.push_back({{"index",i}});c.enabled=true;
|
| 117 |
+
auto before=Clock::now();auto batch=llama_batch_get_one(tokens.data(),count);int rc=llama_decode(ctx,batch);llama_synchronize(ctx);
|
| 118 |
+
if(rc){if(stopped(nullptr)){reason="stopped";break;}throw std::runtime_error("Inference failed with code "+std::to_string(rc));}pos+=count;
|
| 119 |
+
for(int l=0;l<c.config.at("num_hidden_layers").get<int>();l++){
|
| 120 |
+
const auto&v=c.layers[l];if(!v.contains("heads")||v["heads"].size()!=size_t(c.heads(l))||!v.contains("residual"))throw std::runtime_error("Incomplete head capture at layer "+std::to_string(l));
|
| 121 |
+
if(c.linear(l)&&(!v.contains("state_rms")||!v.contains("write_gate")||!v.contains("retention")))throw std::runtime_error("Incomplete recurrent capture at layer "+std::to_string(l));
|
| 122 |
+
if(!c.linear(l)&&(!v.contains("attention")||v["attention"].size()!=size_t(c.heads(l))))throw std::runtime_error("Incomplete attention capture at layer "+std::to_string(l));
|
| 123 |
+
}
|
| 124 |
+
auto id=llama_sampler_sample(sampler,ctx,-1);llama_sampler_accept(sampler,id);if(llama_vocab_is_eog(vocab,id)){reason="eos";break;}
|
| 125 |
+
auto p=piece(vocab,id);generated++;
|
| 126 |
+
emit({{"type","token"},{"step",step},{"token_id",id},{"text",p},{"token_bytes",std::vector<uint8_t>(p.begin(),p.end())},{"input_position",c.position},{"input_text",pieces.back()},{"layers",c.layers},{"elapsed",seconds(started)},{"duration",seconds(before)},{"phase",step==0?"prefill":"decode"}});
|
| 127 |
+
pieces.push_back(p);tokens={id};count=1;if(stopped(nullptr)){reason="stopped";break;}
|
| 128 |
+
}
|
| 129 |
+
c.enabled=false;llama_sampler_free(sampler);sampler=nullptr;emit({{"type","done"},{"reason",reason},{"generated",generated},{"elapsed",seconds(started)}});
|
| 130 |
+
}catch(const std::exception&e){c.enabled=false;if(sampler)llama_sampler_free(sampler);emit({{"type","error"},{"error",e.what()}});}
|
| 131 |
+
ACTIVE_ID.clear();
|
| 132 |
+
}
|
| 133 |
+
llama_free(ctx);llama_model_free(model);llama_backend_free();
|
| 134 |
+
}
|
experiments/reproduce/capture-study.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Produce full routing, attention and recurrent-state traces for public fixtures."""
|
| 3 |
+
import argparse, datetime, gzip, hashlib, json, pathlib, queue, re, subprocess, threading, time
|
| 4 |
+
from urllib.parse import parse_qs, urlparse
|
| 5 |
+
|
| 6 |
+
REPRO = pathlib.Path(__file__).resolve().parent
|
| 7 |
+
PROJECT = REPRO.parent.parent
|
| 8 |
+
MODELS = {
|
| 9 |
+
'flint':{'id':'flint','title':'Recursive Flint','short_name':'Flint','family':'MoE transformer','quantization':'Q8_0','source_url':'https://huggingface.co/alphabell/Flint-Matched-GGUF','learned_sink':True,'stats':[['49','','layers'],['32','','query heads / layer'],['6','256','routed experts / token'],['2.5B','39.8B','active parameters']]},
|
| 10 |
+
'qwen':{'id':'qwen','title':'alphaBell Qwen Open 27B','short_name':'Qwen Open','family':'Hybrid recurrent transformer','quantization':'Q8_0','source_url':'https://huggingface.co/alphabell/alphaBell-Qwen3.8-27B-Open-GGUF','learned_sink':False,'stats':[['64','','layers'],['48','24','linear / full heads'],['17408','','dense FFN width'],['5120','','hidden width']],'checkpoint_origin':'alphaBell fine-tuned Qwen3.8-27B, converted from the lab BF16 checkpoint to GGUF; the public FP8 release is a sibling format.'}
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
def load_cases(path):
|
| 14 |
+
document=json.loads(path.read_text());cases=[]
|
| 15 |
+
for section in ('reroute','memory'):
|
| 16 |
+
for case in document['experiments'][section]['cases']:
|
| 17 |
+
for condition in case['conditions']:
|
| 18 |
+
query=parse_qs(urlparse(condition['trace_url']).query);model=query['model'][0];capture=query['capture'][0]
|
| 19 |
+
if model not in MODELS or not re.fullmatch('[a-z0-9-]+',capture):raise ValueError('Invalid public fixture identity')
|
| 20 |
+
settings=condition['settings']
|
| 21 |
+
expected_limit=48 if section=='reroute' else 16
|
| 22 |
+
if settings!={'max_tokens':expected_limit,'temperature':0,'seed':42,'reasoning':False}:raise ValueError('Unexpected fixture decoding settings')
|
| 23 |
+
cases.append({'id':capture,'model':model,'prompt':condition.get('prompt',case.get('prompt')),'settings':settings,'expected_output':condition['output'],'title':case['title'],'section':section})
|
| 24 |
+
if len(cases)!=24 or len({c['id'] for c in cases})!=24:raise ValueError('Expected 24 distinct public fixtures')
|
| 25 |
+
return cases
|
| 26 |
+
|
| 27 |
+
class Worker:
|
| 28 |
+
def __init__(self,engine,checkpoint,spec,log):
|
| 29 |
+
self.q=queue.Queue();self.log=log.open('w')
|
| 30 |
+
self.p=subprocess.Popen(['nice','-n','10',str(engine.resolve()),str(checkpoint.resolve()),str(spec.resolve())],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=self.log,text=True,bufsize=1)
|
| 31 |
+
def reader():
|
| 32 |
+
try:
|
| 33 |
+
for line in self.p.stdout:
|
| 34 |
+
try:self.q.put(json.loads(line))
|
| 35 |
+
except ValueError:self.q.put({'type':'error','error':'Invalid native JSON'})
|
| 36 |
+
finally:self.q.put({'type':'eof'})
|
| 37 |
+
self.reader=threading.Thread(target=reader,daemon=True);self.reader.start()
|
| 38 |
+
self.ready=self.q.get(timeout=300)
|
| 39 |
+
if self.ready.get('type')!='ready' or self.ready.get('threads')!=16 or self.ready.get('device')!='CPU':self.close();raise RuntimeError('Capture worker failed to start with the required CPU settings')
|
| 40 |
+
def capture(self,case,architecture,provenance):
|
| 41 |
+
self.p.stdin.write(json.dumps({'id':case['id'],'prompt':case['prompt'],**case['settings']})+'\n');self.p.stdin.flush()
|
| 42 |
+
frames=[];start=None;sequence=0;created=int(time.time()*1000)
|
| 43 |
+
while True:
|
| 44 |
+
event=self.q.get(timeout=180);event['seq']=sequence;sequence+=1
|
| 45 |
+
if event['type'] in ('error','fatal','eof'):raise RuntimeError(event.get('error','Capture worker ended unexpectedly'))
|
| 46 |
+
if event.get('model')!=case['model'] or event.get('id')!=case['id']:raise RuntimeError('Mismatched native event identity')
|
| 47 |
+
if event['type']=='start':start=event
|
| 48 |
+
elif event['type']=='token':frames.append(event)
|
| 49 |
+
elif event['type']=='done':break
|
| 50 |
+
if not start or not frames or event.get('reason')!='eos':raise RuntimeError('Fixture did not produce a complete EOS trace')
|
| 51 |
+
output=b''.join(bytes(f['token_bytes']) for f in frames).decode('utf-8',errors='replace')
|
| 52 |
+
if output!=case['expected_output']:raise RuntimeError('Fixture output differs from the recorded public result')
|
| 53 |
+
return {'schema':'flint.trace.v1' if case['model']=='flint' else 'model.trace.v1','model':{**MODELS[case['model']],'architecture':architecture},'id':case['id'],'prompt':case['prompt'],'settings':case['settings'],'created':created,'engine':self.ready,'architecture':architecture,'start':start,'frames':frames,'result':event,'provenance':provenance}
|
| 54 |
+
def close(self):
|
| 55 |
+
if self.p.poll() is None:
|
| 56 |
+
try:self.p.stdin.close();self.p.wait(timeout=30)
|
| 57 |
+
except (BrokenPipeError,subprocess.TimeoutExpired):self.p.terminate();self.p.wait(timeout=30)
|
| 58 |
+
self.log.close()
|
| 59 |
+
|
| 60 |
+
def compare_trace(actual,reference):
|
| 61 |
+
if actual['prompt']!=reference['prompt'] or actual['settings']!=reference['settings']:raise ValueError('Fixture comparison settings differ')
|
| 62 |
+
if len(actual['frames'])!=len(reference['frames']):raise ValueError('Fixture frame count differs')
|
| 63 |
+
for current,prior in zip(actual['frames'],reference['frames']):
|
| 64 |
+
for key in ('layers','step','token_id','token_bytes','input_position'):
|
| 65 |
+
if current[key]!=prior[key]:raise ValueError('Recorded trace differs at token '+str(current['step'])+', field '+key)
|
| 66 |
+
return {'capture':actual['id'],'frames':len(actual['frames']),'token_ids_bytes_positions_equal':True,'all_layer_measurements_exact':True}
|
| 67 |
+
|
| 68 |
+
def main():
|
| 69 |
+
parser=argparse.ArgumentParser();parser.add_argument('--engine',type=pathlib.Path,required=True);parser.add_argument('--flint-model',type=pathlib.Path);parser.add_argument('--qwen-model',type=pathlib.Path);parser.add_argument('--flint-config',type=pathlib.Path,default=PROJECT/'config.json');parser.add_argument('--qwen-config',type=pathlib.Path,default=PROJECT/'models/qwen.json');parser.add_argument('--fixtures',type=pathlib.Path,default=PROJECT/'experiments/data/results.json');parser.add_argument('--output-dir',type=pathlib.Path,required=True);parser.add_argument('--only',action='append',help='Exact public capture ID; repeat to select several fixtures');parser.add_argument('--compare-root',type=pathlib.Path,help='Public Observatory checkout containing the recorded trace manifest and gzip files');args=parser.parse_args()
|
| 70 |
+
cases=load_cases(args.fixtures)
|
| 71 |
+
if args.only:
|
| 72 |
+
wanted=set(args.only);cases=[c for c in cases if c['id'] in wanted]
|
| 73 |
+
if len(cases)!=len(wanted):raise ValueError('Unknown fixture selection')
|
| 74 |
+
args.output_dir.mkdir(exist_ok=True,parents=True);work=args.output_dir/'worker-files';work.mkdir(exist_ok=True)
|
| 75 |
+
expected=json.loads((REPRO/'checkpoints.json').read_text());manifest=[];comparisons=[]
|
| 76 |
+
reference_index={}
|
| 77 |
+
if args.compare_root:reference_index={e['id']:e for e in json.loads((args.compare_root/'experiments/data/traces.json').read_text())}
|
| 78 |
+
for model in ('flint','qwen'):
|
| 79 |
+
selected=[c for c in cases if c['model']==model]
|
| 80 |
+
if not selected:continue
|
| 81 |
+
checkpoint=args.flint_model if model=='flint' else args.qwen_model
|
| 82 |
+
if checkpoint is None:raise ValueError('Supply the selected model checkpoint')
|
| 83 |
+
required=expected[model]['Q8_0']
|
| 84 |
+
if checkpoint.stat().st_size!=required['bytes']:raise ValueError('Checkpoint byte count differs from the release manifest')
|
| 85 |
+
with checkpoint.open('rb') as f:checkpoint_sha=hashlib.file_digest(f,'sha256').hexdigest()
|
| 86 |
+
if checkpoint_sha!=required['sha256']:raise ValueError('Checkpoint SHA-256 differs from the release manifest')
|
| 87 |
+
architecture=json.loads((args.flint_config if model=='flint' else args.qwen_config).read_text());spec=work/(model+'-spec.json')
|
| 88 |
+
spec.write_text(json.dumps({'id':model,'quantization':'Q8_0','architecture':architecture,'stop_path':str((work/('stop-'+model)).resolve())}))
|
| 89 |
+
provenance={'producer':'alphaBell standalone capture study','engine_commit':'74686f084c53506a7afa471d857db02e5c912543','engine_source_tree':'83e9d4c276f84753cce063502a2a9e2325f15ce3','capture_source_sha256':hashlib.sha256((REPRO/'capture-study.cpp').read_bytes()).hexdigest(),'binary_sha256':hashlib.sha256(args.engine.read_bytes()).hexdigest(),'checkpoint_sha256':checkpoint_sha,'checkpoint_bytes':checkpoint.stat().st_size,'fixture_sha256':hashlib.sha256(args.fixtures.read_bytes()).hexdigest(),'device':'CPU','threads':16,'context':2048,'flash_attention':False,'weights_modified':False}
|
| 90 |
+
worker=Worker(args.engine,checkpoint,spec,work/(model+'.stderr.log'))
|
| 91 |
+
try:
|
| 92 |
+
for case in selected:
|
| 93 |
+
trace=worker.capture(case,architecture,provenance)
|
| 94 |
+
if args.compare_root:
|
| 95 |
+
entry=reference_index[case['id']];reference=json.loads(gzip.decompress((args.compare_root/entry['file']).read_bytes()));comparisons.append(compare_trace(trace,reference))
|
| 96 |
+
raw=json.dumps(trace,ensure_ascii=False,separators=(',',':')).encode();compressed=gzip.compress(raw,mtime=0);name=case['id']+'.json.gz';(args.output_dir/name).write_bytes(compressed)
|
| 97 |
+
manifest.append({'id':case['id'],'model':model,'prompt':case['prompt'],'tokens':len(trace['frames']),'file':name,'sha256':hashlib.sha256(raw).hexdigest(),'gzip_sha256':hashlib.sha256(compressed).hexdigest(),'bytes':len(raw),'compressed_bytes':len(compressed),'created':trace['created'],'label':case['title']})
|
| 98 |
+
(args.output_dir/'index.json').write_text(json.dumps(manifest,indent=2)+'\n');print(case['id'],len(trace['frames']),'complete',flush=True)
|
| 99 |
+
finally:worker.close()
|
| 100 |
+
report={'completed_at':datetime.datetime.now(datetime.timezone.utc).isoformat(),'captures':len(manifest),'frames':sum(c['tokens'] for c in manifest),'comparison_count':len(comparisons),'comparisons':comparisons,'all_selected_captures_complete':True}
|
| 101 |
+
(args.output_dir/'verification.json').write_text(json.dumps(report,indent=2)+'\n');print(json.dumps(report),flush=True)
|
| 102 |
+
if __name__=='__main__':main()
|
experiments/reproduce/checkpoints.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"qwen": {
|
| 3 |
+
"Q8_0": {
|
| 4 |
+
"file": "qwen-q8_0.gguf",
|
| 5 |
+
"bytes": 28595763712,
|
| 6 |
+
"sha256": "b2a3172342ff220a8f9a06fc2fd252d1e84615ce8dfc00b2a66941610aa8f43c",
|
| 7 |
+
"gguf_name": "Qwen3.8 27B Open"
|
| 8 |
+
},
|
| 9 |
+
"Q4_1": {
|
| 10 |
+
"file": "qwen-q4_1.gguf",
|
| 11 |
+
"bytes": 16829921792,
|
| 12 |
+
"sha256": "7c0814054c69120ff0e5b3ac3f625f98a9f86185fc39f5d71eae02cb54eb5825",
|
| 13 |
+
"gguf_name": "Qwen3.8 27B Open"
|
| 14 |
+
}
|
| 15 |
+
},
|
| 16 |
+
"flint": {
|
| 17 |
+
"Q8_0": {
|
| 18 |
+
"file": "flint-q8_0.gguf",
|
| 19 |
+
"bytes": 42350359392,
|
| 20 |
+
"sha256": "f97ff2fc77564a05919357685ba8e1892bec279cf733fdb6e3ef0ea8c8e45543",
|
| 21 |
+
"gguf_name": "Flint Bf16"
|
| 22 |
+
},
|
| 23 |
+
"Q4_1": {
|
| 24 |
+
"file": "flint-q4_1.gguf",
|
| 25 |
+
"bytes": 24955171680,
|
| 26 |
+
"sha256": "1d8ea8d2fd133baa54a7412d56d39d20499a9efc080b45486f23853e3f072143",
|
| 27 |
+
"gguf_name": "Flint Bf16"
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
}
|
experiments/reproduce/flint-capture-equivalence.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema": "alphabell.gguf-equivalence.v1",
|
| 3 |
+
"original_container": {
|
| 4 |
+
"label": "Recorded Observatory Flint Q8_0 capture checkpoint",
|
| 5 |
+
"bytes": 42350359392,
|
| 6 |
+
"sha256": "147f02266f809e7bb4b28fdeef9606520d9693b601f8bc0d817caf11a2ed1e5b"
|
| 7 |
+
},
|
| 8 |
+
"matched_container": {
|
| 9 |
+
"file": "flint-q8_0.gguf",
|
| 10 |
+
"bytes": 42350359392,
|
| 11 |
+
"sha256": "f97ff2fc77564a05919357685ba8e1892bec279cf733fdb6e3ef0ea8c8e45543"
|
| 12 |
+
},
|
| 13 |
+
"method": "Read-only byte comparison of all tensor descriptors, padding and tensor payload; decoded comparison of every GGUF metadata value including tokenizer arrays.",
|
| 14 |
+
"tensor_descriptors_and_padding_equal": true,
|
| 15 |
+
"tensor_descriptor_region_bytes_compared": 49153,
|
| 16 |
+
"tensor_payload_equal": true,
|
| 17 |
+
"tensor_payload_bytes_compared": 42344889472,
|
| 18 |
+
"tensor_payload_offset": 5469920,
|
| 19 |
+
"tokenizer_values_equal": true,
|
| 20 |
+
"only_metadata_differences": [
|
| 21 |
+
{
|
| 22 |
+
"key": "general.name",
|
| 23 |
+
"original": "Recursive Flint",
|
| 24 |
+
"matched": "Flint Bf16"
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"key": "general.size_label",
|
| 28 |
+
"original": "40B",
|
| 29 |
+
"matched": "256x2.1B"
|
| 30 |
+
}
|
| 31 |
+
],
|
| 32 |
+
"passed": true
|
| 33 |
+
}
|
experiments/reproduce/llama-flint-runtime.patch
ADDED
|
@@ -0,0 +1,704 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
diff --git a/common/jinja/lexer.cpp b/common/jinja/lexer.cpp
|
| 2 |
+
index 598982c2fe4a30bc362c5140f2f904cca89f041e..bf2dfacdadd28815e05f527f3066ab8a62cbe71a 100644
|
| 3 |
+
--- a/common/jinja/lexer.cpp
|
| 4 |
+
+++ b/common/jinja/lexer.cpp
|
| 5 |
+
@@ -1,7 +1,9 @@
|
| 6 |
+
#include "lexer.h"
|
| 7 |
+
#include "runtime.h"
|
| 8 |
+
+#include "unicode.h"
|
| 9 |
+
|
| 10 |
+
#include <cctype>
|
| 11 |
+
+#include <charconv>
|
| 12 |
+
#include <functional>
|
| 13 |
+
#include <map>
|
| 14 |
+
#include <string>
|
| 15 |
+
@@ -75,6 +77,24 @@ lexer_result lexer::tokenize(const std::string & source) {
|
| 16 |
+
}
|
| 17 |
+
// add escaped char
|
| 18 |
+
char escaped_char = src[pos++];
|
| 19 |
+
+ if (escaped_char == 'x') {
|
| 20 |
+
+ if (pos + 1 >= src.size()) {
|
| 21 |
+
+ throw lexer_exception("incomplete hexadecimal escape", source, pos);
|
| 22 |
+
+ }
|
| 23 |
+
+
|
| 24 |
+
+ unsigned int value = 0;
|
| 25 |
+
+ const char * first = &src[pos];
|
| 26 |
+
+ const char * last = first + 2;
|
| 27 |
+
+
|
| 28 |
+
+ auto [ptr, ec] = std::from_chars(first, last, value, 16);
|
| 29 |
+
+ if (ec != std::errc{} || ptr != last) {
|
| 30 |
+
+ throw lexer_exception("invalid hexadecimal escape", source, pos);
|
| 31 |
+
+ }
|
| 32 |
+
+
|
| 33 |
+
+ str += common_unicode_cpt_to_utf8(value);
|
| 34 |
+
+ pos += 2;
|
| 35 |
+
+ continue;
|
| 36 |
+
+ }
|
| 37 |
+
if (escape_chars.find(escaped_char) == escape_chars.end()) {
|
| 38 |
+
throw lexer_exception(std::string("unknown escape character \\") + escaped_char, source, pos);
|
| 39 |
+
}
|
| 40 |
+
diff --git a/conversion/__init__.py b/conversion/__init__.py
|
| 41 |
+
index bddfe3c1f531a656cf0c1be79615f77f2fc7247b..1476a9901a1417aef1bec82bd3d62afcb65e3df4 100644
|
| 42 |
+
--- a/conversion/__init__.py
|
| 43 |
+
+++ b/conversion/__init__.py
|
| 44 |
+
@@ -19,6 +19,7 @@ __all__ = [
|
| 45 |
+
TEXT_MODEL_MAP: dict[str, str] = {
|
| 46 |
+
"AfmoeForCausalLM": "afmoe",
|
| 47 |
+
"LagunaForCausalLM": "laguna",
|
| 48 |
+
+ "FlintForCausalLM": "flint",
|
| 49 |
+
"ApertusForCausalLM": "llama",
|
| 50 |
+
"ArceeForCausalLM": "llama",
|
| 51 |
+
"ArcticForCausalLM": "arctic",
|
| 52 |
+
diff --git a/conversion/base.py b/conversion/base.py
|
| 53 |
+
index dec1d43222ea63d0b8cba923d5798dea8e404736..7d787181c4761870c861786742ae383eed953b88 100644
|
| 54 |
+
--- a/conversion/base.py
|
| 55 |
+
+++ b/conversion/base.py
|
| 56 |
+
@@ -1701,7 +1701,7 @@ class TextModel(ModelBase):
|
| 57 |
+
self.gguf_writer.add_expert_group_used_count(n_group_used)
|
| 58 |
+
logger.info(f"gguf: expert groups used count = {n_group_used}")
|
| 59 |
+
|
| 60 |
+
- if (score_func := self.find_hparam(["score_function", "scoring_func", "score_func", "moe_router_activation", "moe_router_activation_func", "expert_selection_fn", "router_score_func"], optional=True)) is not None:
|
| 61 |
+
+ if (score_func := self.find_hparam(["score_function", "scoring_func", "score_func", "moe_router_activation", "moe_router_activation_func", "expert_selection_fn", "router_score_func", "router_score_function"], optional=True)) is not None:
|
| 62 |
+
if score_func == "sigmoid":
|
| 63 |
+
self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID)
|
| 64 |
+
elif score_func == "softmax":
|
| 65 |
+
@@ -1939,6 +1939,9 @@ class TextModel(ModelBase):
|
| 66 |
+
if chkhsh == "52df12b4c8d4176e7481aab4b6e8454d1fd0a210a04a574f6d4e067d10e23c3e":
|
| 67 |
+
# ref: https://huggingface.co/CohereLabs/North-Mini-Code-1.0
|
| 68 |
+
res = "cohere2moe"
|
| 69 |
+
+ if chkhsh == "d5420e601e979078c670bc503d9ac411c311fa76b76a76267c31e0d6ba816637":
|
| 70 |
+
+ # ref: https://huggingface.co/RecursiveSI/flint-preview
|
| 71 |
+
+ res = "flint"
|
| 72 |
+
if chkhsh == "e636dc30a262dcc0d8c323492e32ae2b70728f4df7dfe9737d9f920a282b8aea":
|
| 73 |
+
# ref: https://huggingface.co/Qwen/Qwen1.5-7B
|
| 74 |
+
res = "qwen2"
|
| 75 |
+
diff --git a/conversion/flint.py b/conversion/flint.py
|
| 76 |
+
new file mode 100644
|
| 77 |
+
index 0000000000000000000000000000000000000000..1fd4776f294e76d6782005bd588314f733a216ad
|
| 78 |
+
--- /dev/null
|
| 79 |
+
+++ b/conversion/flint.py
|
| 80 |
+
@@ -0,0 +1,38 @@
|
| 81 |
+
+from __future__ import annotations
|
| 82 |
+
+
|
| 83 |
+
+from .base import ModelBase, gguf
|
| 84 |
+
+from .qwen import Qwen2MoeModel
|
| 85 |
+
+
|
| 86 |
+
+
|
| 87 |
+
+@ModelBase.register("FlintForCausalLM")
|
| 88 |
+
+@ModelBase.example("RecursiveSI/flint-preview")
|
| 89 |
+
+class FlintModel(Qwen2MoeModel):
|
| 90 |
+
+ model_arch = gguf.MODEL_ARCH.FLINT
|
| 91 |
+
+
|
| 92 |
+
+ def set_gguf_parameters(self):
|
| 93 |
+
+ super().set_gguf_parameters()
|
| 94 |
+
+ dense = self.hparams["mlp_only_layers"]
|
| 95 |
+
+ if dense != list(range(len(dense))):
|
| 96 |
+
+ raise ValueError("Flint dense layers must precede the MoE layers")
|
| 97 |
+
+ self.gguf_writer.add_leading_dense_block_count(len(dense))
|
| 98 |
+
+ self.gguf_writer.add_expert_shared_count(
|
| 99 |
+
+ self.hparams["shared_expert_intermediate_size"] // self.hparams["moe_intermediate_size"]
|
| 100 |
+
+ )
|
| 101 |
+
+ self.gguf_writer.add_expert_weights_norm(True)
|
| 102 |
+
+ self.gguf_writer.add_expert_weights_scale(1.0)
|
| 103 |
+
+ self.gguf_writer.add_sliding_window(self.hparams["sliding_window"])
|
| 104 |
+
+ self.gguf_writer.add_sliding_window_pattern([
|
| 105 |
+
+ kind == "sliding_attention" for kind in self.hparams["layer_types"]
|
| 106 |
+
+ ])
|
| 107 |
+
+ self.gguf_writer.add_rope_pattern([
|
| 108 |
+
+ i not in self.hparams["no_rope_layer_indices"] for i in range(self.block_count)
|
| 109 |
+
+ ])
|
| 110 |
+
+
|
| 111 |
+
+ def modify_tensors(self, data_torch, name, bid):
|
| 112 |
+
+ if name.endswith(".softmax_offset"):
|
| 113 |
+
+ name += ".weight"
|
| 114 |
+
+ if name.endswith(".gate.expert_bias"):
|
| 115 |
+
+ if not self.hparams["router_expert_bias"]:
|
| 116 |
+
+ return
|
| 117 |
+
+ name += ".bias"
|
| 118 |
+
+ yield from super().modify_tensors(data_torch, name, bid)
|
| 119 |
+
diff --git a/convert_hf_to_gguf_update.py b/convert_hf_to_gguf_update.py
|
| 120 |
+
index d3aa3c3807f01d1343a08b6be1ba6188df01a314..683e061db68af946be5b43d706a292a7f6e96a48 100755
|
| 121 |
+
--- a/convert_hf_to_gguf_update.py
|
| 122 |
+
+++ b/convert_hf_to_gguf_update.py
|
| 123 |
+
@@ -101,6 +101,7 @@ models = [
|
| 124 |
+
{"name": "command-r", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/CohereForAI/c4ai-command-r-v01", },
|
| 125 |
+
{"name": "tiny_aya", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/CohereLabs/tiny-aya-base", },
|
| 126 |
+
{"name": "cohere2moe", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/CohereLabs/North-Mini-Code-1.0", },
|
| 127 |
+
+ {"name": "flint", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/RecursiveSI/flint-preview", },
|
| 128 |
+
{"name": "qwen2", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/Qwen/Qwen1.5-7B", },
|
| 129 |
+
{"name": "olmo", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/allenai/OLMo-1.7-7B-hf", },
|
| 130 |
+
{"name": "dbrx", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/databricks/dbrx-base", },
|
| 131 |
+
diff --git a/gguf-py/gguf/constants.py b/gguf-py/gguf/constants.py
|
| 132 |
+
index 2840348cba242b7b286de98807b7504dfccc1d56..70d141c1e9ea16dc8c3160e1ef1771e73b7295c5 100644
|
| 133 |
+
--- a/gguf-py/gguf/constants.py
|
| 134 |
+
+++ b/gguf-py/gguf/constants.py
|
| 135 |
+
@@ -575,6 +575,7 @@ class MODEL_ARCH(IntEnum):
|
| 136 |
+
QWEN2VL = auto()
|
| 137 |
+
QWEN3 = auto()
|
| 138 |
+
QWEN3MOE = auto()
|
| 139 |
+
+ FLINT = auto()
|
| 140 |
+
QWEN3NEXT = auto()
|
| 141 |
+
HRM_TEXT = auto()
|
| 142 |
+
BEAM = auto()
|
| 143 |
+
@@ -1391,6 +1392,7 @@ MODEL_ARCH_NAMES: dict[MODEL_ARCH, str] = {
|
| 144 |
+
MODEL_ARCH.QWEN2MOE: "qwen2moe",
|
| 145 |
+
MODEL_ARCH.QWEN2VL: "qwen2vl",
|
| 146 |
+
MODEL_ARCH.QWEN3: "qwen3",
|
| 147 |
+
+ MODEL_ARCH.FLINT: "flint",
|
| 148 |
+
MODEL_ARCH.QWEN3MOE: "qwen3moe",
|
| 149 |
+
MODEL_ARCH.QWEN3NEXT: "qwen3next",
|
| 150 |
+
MODEL_ARCH.HRM_TEXT: "hrm_text",
|
| 151 |
+
@@ -2953,6 +2955,33 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
| 152 |
+
MODEL_TENSOR.FFN_DOWN_EXP,
|
| 153 |
+
MODEL_TENSOR.FFN_UP_EXP,
|
| 154 |
+
],
|
| 155 |
+
+
|
| 156 |
+
+ MODEL_ARCH.FLINT: [
|
| 157 |
+
+ MODEL_TENSOR.TOKEN_EMBD,
|
| 158 |
+
+ MODEL_TENSOR.OUTPUT_NORM,
|
| 159 |
+
+ MODEL_TENSOR.OUTPUT,
|
| 160 |
+
+ MODEL_TENSOR.ATTN_NORM,
|
| 161 |
+
+ MODEL_TENSOR.ATTN_QKV,
|
| 162 |
+
+ MODEL_TENSOR.ATTN_Q,
|
| 163 |
+
+ MODEL_TENSOR.ATTN_Q_NORM,
|
| 164 |
+
+ MODEL_TENSOR.ATTN_K,
|
| 165 |
+
+ MODEL_TENSOR.ATTN_K_NORM,
|
| 166 |
+
+ MODEL_TENSOR.ATTN_V,
|
| 167 |
+
+ MODEL_TENSOR.ATTN_OUT,
|
| 168 |
+
+ MODEL_TENSOR.FFN_NORM,
|
| 169 |
+
+ MODEL_TENSOR.FFN_GATE_INP,
|
| 170 |
+
+ MODEL_TENSOR.FFN_GATE_EXP,
|
| 171 |
+
+ MODEL_TENSOR.FFN_DOWN_EXP,
|
| 172 |
+
+ MODEL_TENSOR.FFN_UP_EXP,
|
| 173 |
+
+ MODEL_TENSOR.ATTN_SINKS,
|
| 174 |
+
+ MODEL_TENSOR.FFN_GATE,
|
| 175 |
+
+ MODEL_TENSOR.FFN_UP,
|
| 176 |
+
+ MODEL_TENSOR.FFN_DOWN,
|
| 177 |
+
+ MODEL_TENSOR.FFN_GATE_SHEXP,
|
| 178 |
+
+ MODEL_TENSOR.FFN_UP_SHEXP,
|
| 179 |
+
+ MODEL_TENSOR.FFN_DOWN_SHEXP,
|
| 180 |
+
+ MODEL_TENSOR.FFN_EXP_PROBS_B,
|
| 181 |
+
+ ],
|
| 182 |
+
MODEL_ARCH.QWEN3NEXT: [
|
| 183 |
+
MODEL_TENSOR.TOKEN_EMBD,
|
| 184 |
+
MODEL_TENSOR.OUTPUT_NORM,
|
| 185 |
+
diff --git a/gguf-py/gguf/tensor_mapping.py b/gguf-py/gguf/tensor_mapping.py
|
| 186 |
+
index 3586d65285ca5f76446bb624cf384c006c76b386..79c9add78a1383b24a6f56212d2b995c19e69b9e 100644
|
| 187 |
+
--- a/gguf-py/gguf/tensor_mapping.py
|
| 188 |
+
+++ b/gguf-py/gguf/tensor_mapping.py
|
| 189 |
+
@@ -429,6 +429,7 @@ class TensorNameMap:
|
| 190 |
+
),
|
| 191 |
+
|
| 192 |
+
MODEL_TENSOR.ATTN_SINKS: (
|
| 193 |
+
+ "model.layers.{bid}.self_attn.softmax_offset", # flint
|
| 194 |
+
"model.layers.{bid}.self_attn.sinks", # openai-moe
|
| 195 |
+
"model.layers.{bid}.self_attn.attention_sink_bias", # mimov2
|
| 196 |
+
"model.layers.{bid}.self_attn.learnable_sink_param", # hy-v4
|
| 197 |
+
diff --git a/src/llama-arch.cpp b/src/llama-arch.cpp
|
| 198 |
+
index 4534be22f37b5e088d50a359902a72723dfcb749..27236619ad03c2fdff6bc7b4a69d7b6b68ca7671 100644
|
| 199 |
+
--- a/src/llama-arch.cpp
|
| 200 |
+
+++ b/src/llama-arch.cpp
|
| 201 |
+
@@ -34,6 +34,7 @@ static const std::map<llm_arch, const char *> LLM_ARCH_NAMES = {
|
| 202 |
+
{ LLM_ARCH_QWEN2MOE, "qwen2moe" },
|
| 203 |
+
{ LLM_ARCH_QWEN2VL, "qwen2vl" },
|
| 204 |
+
{ LLM_ARCH_QWEN3, "qwen3" },
|
| 205 |
+
+ { LLM_ARCH_FLINT, "flint" },
|
| 206 |
+
{ LLM_ARCH_QWEN3MOE, "qwen3moe" },
|
| 207 |
+
{ LLM_ARCH_QWEN3NEXT, "qwen3next" },
|
| 208 |
+
{ LLM_ARCH_QWEN3VL, "qwen3vl" },
|
| 209 |
+
diff --git a/src/llama-arch.h b/src/llama-arch.h
|
| 210 |
+
index 5a8ca425001cb82d2cc281ab8f4e05c5e079901a..54bb7003b3e852e7d65d1cac252aa6ee7b8a5e4b 100644
|
| 211 |
+
--- a/src/llama-arch.h
|
| 212 |
+
+++ b/src/llama-arch.h
|
| 213 |
+
@@ -40,6 +40,7 @@ enum llm_arch {
|
| 214 |
+
LLM_ARCH_QWEN2VL,
|
| 215 |
+
LLM_ARCH_QWEN3,
|
| 216 |
+
LLM_ARCH_QWEN3MOE,
|
| 217 |
+
+ LLM_ARCH_FLINT,
|
| 218 |
+
LLM_ARCH_QWEN3NEXT,
|
| 219 |
+
LLM_ARCH_QWEN3VL,
|
| 220 |
+
LLM_ARCH_QWEN3VLMOE,
|
| 221 |
+
diff --git a/src/llama-graph.cpp b/src/llama-graph.cpp
|
| 222 |
+
index 59caffe87fce5d1f5c9deee138a8f59cd32dbad1..1c84d8246c23d586a8fcd640eacfc18f9f553f1d 100644
|
| 223 |
+
--- a/src/llama-graph.cpp
|
| 224 |
+
+++ b/src/llama-graph.cpp
|
| 225 |
+
@@ -1684,7 +1684,10 @@ llm_graph_amax_src1 llm_graph_context::build_amax_src1_f16(
|
| 226 |
+
constexpr float k_ln_2 = 0.6931471805599453f;
|
| 227 |
+
|
| 228 |
+
ggml_tensor * e;
|
| 229 |
+
- e = ggml_scale(ctx0, amax, 1.0f/32768.0f);
|
| 230 |
+
+ // note: this can technically be:
|
| 231 |
+
+ // e = ggml_scale(ctx0, amax, 1.0f/32768.0f);
|
| 232 |
+
+ // but we leave x32 headroom for the F16 sums of 32 values in Q8 activation blocks
|
| 233 |
+
+ e = ggml_scale(ctx0, amax, 1.0f/1024.0f);
|
| 234 |
+
e = ggml_log (ctx0, e);
|
| 235 |
+
e = ggml_scale(ctx0, e, k_log2_e);
|
| 236 |
+
e = ggml_ceil (ctx0, e);
|
| 237 |
+
@@ -2505,7 +2508,7 @@ ggml_tensor * llm_graph_context::build_moe_ffn(
|
| 238 |
+
// ref: https://github.com/ggml-org/llama.cpp/pull/30656#issuecomment-6269597922
|
| 239 |
+
llm_graph_amax_src1 amax_src1 = { nullptr, nullptr };
|
| 240 |
+
|
| 241 |
+
- if (arch == LLM_ARCH_MISTRAL4 || arch == LLM_ARCH_MELLUM) {
|
| 242 |
+
+ if (arch == LLM_ARCH_MISTRAL4 || arch == LLM_ARCH_MELLUM || arch == LLM_ARCH_FLINT) {
|
| 243 |
+
amax_src1 = build_amax_src1_f16(cur);
|
| 244 |
+
cur = amax_src1.res;
|
| 245 |
+
}
|
| 246 |
+
diff --git a/src/llama-graph.h b/src/llama-graph.h
|
| 247 |
+
index abe5133c97a22b380c46b3488111c5615dee7d43..58cbbae5bf83db45f7aff180dae024ced75d1978 100644
|
| 248 |
+
--- a/src/llama-graph.h
|
| 249 |
+
+++ b/src/llama-graph.h
|
| 250 |
+
@@ -1121,7 +1121,7 @@ struct llm_graph_context {
|
| 251 |
+
ggml_tensor * slots = nullptr) const;
|
| 252 |
+
|
| 253 |
+
// rescale `cur` (the src1 of a matmul) by a power of two, computed from its maximum absolute
|
| 254 |
+
- // when the values already fit in F16, the factors are exactly 1.0f
|
| 255 |
+
+ // keep both individual values and Q8 activation block sums within F16 range
|
| 256 |
+
llm_graph_amax_src1 build_amax_src1_f16(
|
| 257 |
+
ggml_tensor * cur) const;
|
| 258 |
+
|
| 259 |
+
diff --git a/src/llama-kv-cache.cpp b/src/llama-kv-cache.cpp
|
| 260 |
+
index b7dfadef4638f6221608f48aff0a1f83df281063..6f0e3f7f796f8705317d219a2555d727d6c3a1c6 100644
|
| 261 |
+
--- a/src/llama-kv-cache.cpp
|
| 262 |
+
+++ b/src/llama-kv-cache.cpp
|
| 263 |
+
@@ -882,7 +882,10 @@ bool llama_kv_cache::update(llama_context * lctx, bool do_shift, const stream_co
|
| 264 |
+
LLAMA_LOG_DEBUG("%s: applying K-shift\n", __func__);
|
| 265 |
+
|
| 266 |
+
// apply K-shift if needed
|
| 267 |
+
- if (hparams.rope_type != LLAMA_ROPE_TYPE_NONE) {
|
| 268 |
+
+ if (hparams.rope_type != LLAMA_ROPE_TYPE_NONE &&
|
| 269 |
+
+ std::any_of(layers.begin(), layers.end(), [&](const auto & layer) {
|
| 270 |
+
+ return hparams.has_rope(layer.il);
|
| 271 |
+
+ })) {
|
| 272 |
+
ggml_backend_sched_reset(sched);
|
| 273 |
+
|
| 274 |
+
auto * res = lctx->get_gf_res_reserve();
|
| 275 |
+
diff --git a/src/llama-model.cpp b/src/llama-model.cpp
|
| 276 |
+
index 6712f0f22f2a742f7c93d0f5bfebed25d53337ce..0dd49db7ef1c91f27bb12e661710e68db106e162 100644
|
| 277 |
+
--- a/src/llama-model.cpp
|
| 278 |
+
+++ b/src/llama-model.cpp
|
| 279 |
+
@@ -112,6 +112,8 @@ static llama_model * llama_model_mapping(llm_arch arch, const llama_model_params
|
| 280 |
+
return new llama_model_qwen2moe(params);
|
| 281 |
+
case LLM_ARCH_QWEN3:
|
| 282 |
+
return new llama_model_qwen3(params);
|
| 283 |
+
+ case LLM_ARCH_FLINT:
|
| 284 |
+
+ return new llama_model_flint(params);
|
| 285 |
+
case LLM_ARCH_QWEN3MOE:
|
| 286 |
+
return new llama_model_qwen3moe(params);
|
| 287 |
+
case LLM_ARCH_QWEN3VL:
|
| 288 |
+
@@ -3569,6 +3571,7 @@ llama_rope_type llama_model_rope_type(const llama_model * model) {
|
| 289 |
+
case LLM_ARCH_QWEN2MOE:
|
| 290 |
+
case LLM_ARCH_QWEN3:
|
| 291 |
+
case LLM_ARCH_VLLM_DECISION2:
|
| 292 |
+
+ case LLM_ARCH_FLINT:
|
| 293 |
+
case LLM_ARCH_QWEN3MOE:
|
| 294 |
+
case LLM_ARCH_LLADA_MOE:
|
| 295 |
+
case LLM_ARCH_RND1:
|
| 296 |
+
diff --git a/src/llama-vocab.cpp b/src/llama-vocab.cpp
|
| 297 |
+
index a3d5dedef33246a9d53fbf9738985fb82b37b396..05c0f96a29ccc136c952f1edc358bf2c2306382b 100644
|
| 298 |
+
--- a/src/llama-vocab.cpp
|
| 299 |
+
+++ b/src/llama-vocab.cpp
|
| 300 |
+
@@ -281,6 +281,13 @@ struct llm_tokenizer_bpe : llm_tokenizer {
|
| 301 |
+
llm_tokenizer_bpe(const llama_vocab & vocab) {
|
| 302 |
+
GGML_ASSERT(vocab.get_type() == LLAMA_VOCAB_TYPE_BPE);
|
| 303 |
+
switch (vocab.get_pre_type()) {
|
| 304 |
+
+ case LLAMA_VOCAB_PRE_TYPE_FLINT:
|
| 305 |
+
+ regex_exprs = {
|
| 306 |
+
+ "\\p{N}{1,3}",
|
| 307 |
+
+ "[一-龥-ゟ゠-ヿ]+",
|
| 308 |
+
+ "[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]*[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]+(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]+[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]*(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?| ?[\\p{P}\\p{S}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
|
| 309 |
+
+ };
|
| 310 |
+
+ break;
|
| 311 |
+
case LLAMA_VOCAB_PRE_TYPE_LLAMA3:
|
| 312 |
+
regex_exprs = {
|
| 313 |
+
// original regex from tokenizer.json
|
| 314 |
+
@@ -2417,6 +2424,9 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
|
| 315 |
+
LLAMA_LOG_WARN("%s: ************************************ \n", __func__);
|
| 316 |
+
LLAMA_LOG_WARN("%s: \n", __func__);
|
| 317 |
+
pre_type = LLAMA_VOCAB_PRE_TYPE_DEFAULT;
|
| 318 |
+
+ } else if (tokenizer_pre == "flint") {
|
| 319 |
+
+ pre_type = LLAMA_VOCAB_PRE_TYPE_FLINT;
|
| 320 |
+
+ clean_spaces = false;
|
| 321 |
+
} else if (tokenizer_pre == "default") {
|
| 322 |
+
pre_type = LLAMA_VOCAB_PRE_TYPE_DEFAULT;
|
| 323 |
+
} else if (tokenizer_pre == "minicpm5") {
|
| 324 |
+
@@ -3147,7 +3157,6 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
|
| 325 |
+
// this is currently determined based on the token text, which is obviously not ideal
|
| 326 |
+
// ref: https://github.com/ggml-org/llama.cpp/issues/9606
|
| 327 |
+
special_eog_ids.clear();
|
| 328 |
+
-
|
| 329 |
+
if (special_fim_pad_id != LLAMA_TOKEN_NULL && special_eog_ids.count(special_fim_pad_id) == 0) {
|
| 330 |
+
special_eog_ids.insert(special_fim_pad_id);
|
| 331 |
+
}
|
| 332 |
+
@@ -3181,6 +3190,7 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
|
| 333 |
+
|| t.first == "[EOT]" // Kimi-K2
|
| 334 |
+
|| t.first == "[EOS]" // Kimi-K2
|
| 335 |
+
|| t.first == "<|end_of_text|>"
|
| 336 |
+
+ || (pre_type == LLAMA_VOCAB_PRE_TYPE_FLINT && (t.first == "<|user|>" || t.first == "<|observation|>"))
|
| 337 |
+
|| t.first == "<end_of_utterance>" // smoldocling
|
| 338 |
+
|| t.first == "<eos>" // gemma4
|
| 339 |
+
|| t.first == "<turn|>" // gemma4
|
| 340 |
+
diff --git a/src/llama-vocab.h b/src/llama-vocab.h
|
| 341 |
+
index 415acd22bf40448094ea216f9e6487bb18c72134..a87c0de42b33297f143f27de6807cef303144962 100644
|
| 342 |
+
--- a/src/llama-vocab.h
|
| 343 |
+
+++ b/src/llama-vocab.h
|
| 344 |
+
@@ -70,6 +70,7 @@ enum llama_vocab_pre_type {
|
| 345 |
+
LLAMA_VOCAB_PRE_TYPE_UFAKZEKA = 59,
|
| 346 |
+
LLAMA_VOCAB_PRE_TYPE_MMBERT = 60,
|
| 347 |
+
LLAMA_VOCAB_PRE_TYPE_K2_HORIZON = 61,
|
| 348 |
+
+ LLAMA_VOCAB_PRE_TYPE_FLINT = 62,
|
| 349 |
+
};
|
| 350 |
+
|
| 351 |
+
struct LLM_KV;
|
| 352 |
+
diff --git a/src/models/flint.cpp b/src/models/flint.cpp
|
| 353 |
+
new file mode 100644
|
| 354 |
+
index 0000000000000000000000000000000000000000..e7e6f6b792df45b079434de06e1b372e829de13a
|
| 355 |
+
--- /dev/null
|
| 356 |
+
+++ b/src/models/flint.cpp
|
| 357 |
+
@@ -0,0 +1,194 @@
|
| 358 |
+
+#include "models.h"
|
| 359 |
+
+
|
| 360 |
+
+void llama_model_flint::load_arch_hparams(llama_model_loader & ml) {
|
| 361 |
+
+ ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps);
|
| 362 |
+
+ ml.get_key(LLM_KV_LEADING_DENSE_BLOCK_COUNT, hparams.n_layer_dense_lead);
|
| 363 |
+
+ ml.get_key_or_arr(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, hparams.n_ff_exp_arr, hparams.n_layer_all);
|
| 364 |
+
+ ml.get_key(LLM_KV_EXPERT_SHARED_COUNT, hparams.n_expert_shared);
|
| 365 |
+
+ ml.get_key(LLM_KV_EXPERT_WEIGHTS_SCALE, hparams.expert_weights_scale, false);
|
| 366 |
+
+ ml.get_key(LLM_KV_EXPERT_WEIGHTS_NORM, hparams.expert_weights_norm);
|
| 367 |
+
+ ml.get_key(LLM_KV_EXPERT_GATING_FUNC, hparams.expert_gating_func);
|
| 368 |
+
+
|
| 369 |
+
+ ml.get_key(LLM_KV_EXPERT_SHARED_FEED_FORWARD_LENGTH, hparams.n_ff_shexp);
|
| 370 |
+
+ ml.get_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, hparams.is_swa_impl);
|
| 371 |
+
+ ml.get_arr(LLM_KV_ATTENTION_ROPE_PATTERN, hparams.rope_pattern);
|
| 372 |
+
+ ml.get_key(LLM_KV_ATTENTION_SLIDING_WINDOW, hparams.n_swa);
|
| 373 |
+
+ hparams.swa_type = LLAMA_SWA_TYPE_STANDARD;
|
| 374 |
+
+ type = LLM_TYPE_UNKNOWN;
|
| 375 |
+
+}
|
| 376 |
+
+
|
| 377 |
+
+void llama_model_flint::load_arch_tensors(llama_model_loader &) {
|
| 378 |
+
+ LLAMA_LOAD_LOCALS;
|
| 379 |
+
+
|
| 380 |
+
+ const int64_t n_ff_exp = hparams.n_ff_exp();
|
| 381 |
+
+
|
| 382 |
+
+ tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0);
|
| 383 |
+
+
|
| 384 |
+
+ output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0);
|
| 385 |
+
+ output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, 0);
|
| 386 |
+
+
|
| 387 |
+
+ for (int i = 0; i < n_layer; ++i) {
|
| 388 |
+
+ auto & layer = layers[i];
|
| 389 |
+
+
|
| 390 |
+
+ layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), {n_embd}, 0);
|
| 391 |
+
+
|
| 392 |
+
+ create_tensor_qkv(layer, i, n_embd, n_embd_head_k * n_head, n_embd_k_gqa, n_embd_v_gqa, 0);
|
| 393 |
+
+ layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), {n_embd_head_k * n_head, n_embd}, 0);
|
| 394 |
+
+
|
| 395 |
+
+ layer.attn_k_norm = create_tensor(tn(LLM_TENSOR_ATTN_K_NORM, "weight", i), {n_embd_head_k}, 0);
|
| 396 |
+
+ layer.attn_q_norm = create_tensor(tn(LLM_TENSOR_ATTN_Q_NORM, "weight", i), {n_embd_head_k}, 0);
|
| 397 |
+
+
|
| 398 |
+
+ layer.attn_sinks = create_tensor(tn(LLM_TENSOR_ATTN_SINKS, "weight", i), {n_head}, 0);
|
| 399 |
+
+
|
| 400 |
+
+ layer.ffn_norm = create_tensor(tn(LLM_TENSOR_FFN_NORM, "weight", i), {n_embd}, 0);
|
| 401 |
+
+
|
| 402 |
+
+ if (i < (int) hparams.n_layer_dense_lead) {
|
| 403 |
+
+ layer.ffn_gate = create_tensor(tn(LLM_TENSOR_FFN_GATE, "weight", i), {n_embd, n_ff}, 0);
|
| 404 |
+
+ layer.ffn_down = create_tensor(tn(LLM_TENSOR_FFN_DOWN, "weight", i), { n_ff, n_embd}, 0);
|
| 405 |
+
+ layer.ffn_up = create_tensor(tn(LLM_TENSOR_FFN_UP, "weight", i), {n_embd, n_ff}, 0);
|
| 406 |
+
+ } else {
|
| 407 |
+
+ layer.ffn_gate_inp = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "weight", i), {n_embd, n_expert}, 0);
|
| 408 |
+
+ layer.ffn_exp_probs_b = create_tensor(tn(LLM_TENSOR_FFN_EXP_PROBS_B, "bias", i), {n_expert}, TENSOR_NOT_REQUIRED);
|
| 409 |
+
+
|
| 410 |
+
+ if (n_expert == 0) {
|
| 411 |
+
+ throw std::runtime_error("n_expert must be > 0");
|
| 412 |
+
+ }
|
| 413 |
+
+ if (n_expert_used == 0) {
|
| 414 |
+
+ throw std::runtime_error("n_expert_used must be > 0");
|
| 415 |
+
+ }
|
| 416 |
+
+
|
| 417 |
+
+ // MoE branch
|
| 418 |
+
+ layer.ffn_gate_exps = create_tensor(tn(LLM_TENSOR_FFN_GATE_EXPS, "weight", i), { n_embd, n_ff_exp, n_expert}, 0);
|
| 419 |
+
+ layer.ffn_down_exps = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "weight", i), {n_ff_exp, n_embd, n_expert}, 0);
|
| 420 |
+
+ layer.ffn_up_exps = create_tensor(tn(LLM_TENSOR_FFN_UP_EXPS, "weight", i), { n_embd, n_ff_exp, n_expert}, 0);
|
| 421 |
+
+
|
| 422 |
+
+ // Shared expert branch
|
| 423 |
+
+ layer.ffn_gate_shexp = create_tensor(tn(LLM_TENSOR_FFN_GATE_SHEXP, "weight", i), {n_embd, hparams.n_ff_shexp}, 0);
|
| 424 |
+
+ layer.ffn_down_shexp = create_tensor(tn(LLM_TENSOR_FFN_DOWN_SHEXP, "weight", i), { hparams.n_ff_shexp, n_embd}, 0);
|
| 425 |
+
+ layer.ffn_up_shexp = create_tensor(tn(LLM_TENSOR_FFN_UP_SHEXP, "weight", i), {n_embd, hparams.n_ff_shexp}, 0);
|
| 426 |
+
+ }
|
| 427 |
+
+ }
|
| 428 |
+
+}
|
| 429 |
+
+
|
| 430 |
+
+std::unique_ptr<llm_graph_context> llama_model_flint::build_arch_graph(const llm_graph_params & params) const {
|
| 431 |
+
+ return std::make_unique<graph>(*this, params);
|
| 432 |
+
+}
|
| 433 |
+
+
|
| 434 |
+
+llama_model_flint::graph::graph(const llama_model & model, const llm_graph_params & params) :
|
| 435 |
+
+ llm_graph_context(params) {
|
| 436 |
+
+ const int64_t n_embd_head = hparams.n_embd_head_v();
|
| 437 |
+
+
|
| 438 |
+
+ GGML_ASSERT(n_embd_head == hparams.n_embd_head_k());
|
| 439 |
+
+ GGML_ASSERT(n_embd_head == n_rot);
|
| 440 |
+
+
|
| 441 |
+
+ ggml_tensor * cur;
|
| 442 |
+
+ ggml_tensor * inpL;
|
| 443 |
+
+
|
| 444 |
+
+ inpL = build_inp_embd(model.tok_embd);
|
| 445 |
+
+
|
| 446 |
+
+ // inp_pos - contains the positions
|
| 447 |
+
+ ggml_tensor * inp_pos = build_inp_pos();
|
| 448 |
+
+
|
| 449 |
+
+ auto * inp_attn = build_attn_inp_kv_iswa();
|
| 450 |
+
+
|
| 451 |
+
+ ggml_tensor * inp_out_ids = build_inp_out_ids();
|
| 452 |
+
+
|
| 453 |
+
+ for (int il = 0; il < n_layer; ++il) {
|
| 454 |
+
+ res->t_layer_inp[il] = inpL;
|
| 455 |
+
+ ggml_tensor * inpSA = inpL;
|
| 456 |
+
+
|
| 457 |
+
+ // norm
|
| 458 |
+
+ cur = build_norm(inpL, model.layers[il].attn_norm, NULL, LLM_NORM_RMS, il);
|
| 459 |
+
+ cb(cur, "attn_norm", il);
|
| 460 |
+
+
|
| 461 |
+
+ // self_attention
|
| 462 |
+
+ {
|
| 463 |
+
+ // compute Q and K and RoPE them
|
| 464 |
+
+ auto [Qcur, Kcur, Vcur] = build_qkv(model.layers[il], cur,
|
| 465 |
+
+ n_embd_head, n_head, n_head_kv, il);
|
| 466 |
+
+
|
| 467 |
+
+ Qcur = build_norm(Qcur, model.layers[il].attn_q_norm, nullptr, LLM_NORM_RMS, il);
|
| 468 |
+
+ Kcur = build_norm(Kcur, model.layers[il].attn_k_norm, nullptr, LLM_NORM_RMS, il);
|
| 469 |
+
+ if (hparams.has_rope(il)) {
|
| 470 |
+
+ Qcur = ggml_rope_ext(ctx0, Qcur, inp_pos, nullptr, n_rot, rope_type, n_ctx_orig,
|
| 471 |
+
+ freq_base, freq_scale, ext_factor, attn_factor, beta_fast, beta_slow);
|
| 472 |
+
+ Kcur = ggml_rope_ext(ctx0, Kcur, inp_pos, nullptr, n_rot, rope_type, n_ctx_orig,
|
| 473 |
+
+ freq_base, freq_scale, ext_factor, attn_factor, beta_fast, beta_slow);
|
| 474 |
+
+ }
|
| 475 |
+
+ cb(Qcur, "Qcur", il);
|
| 476 |
+
+ cb(Kcur, "Kcur", il);
|
| 477 |
+
+ cb(Vcur, "Vcur", il);
|
| 478 |
+
+
|
| 479 |
+
+ cur = build_attn(inp_attn,
|
| 480 |
+
+ model.layers[il].wo, model.layers[il].wo_b, model.layers[il].wo_s,
|
| 481 |
+
+ Qcur, Kcur, Vcur, nullptr, model.layers[il].attn_sinks, nullptr, 1.0f / sqrtf(float(n_embd_head)), il);
|
| 482 |
+
+ }
|
| 483 |
+
+ if (il == n_layer - 1 && inp_out_ids) {
|
| 484 |
+
+ cur = ggml_get_rows(ctx0, cur, inp_out_ids);
|
| 485 |
+
+ inpSA = ggml_get_rows(ctx0, inpSA, inp_out_ids);
|
| 486 |
+
+ }
|
| 487 |
+
+ ggml_tensor * ffn_inp = ggml_add(ctx0, cur, inpSA);
|
| 488 |
+
+ cb(ffn_inp, "ffn_inp", il);
|
| 489 |
+
+
|
| 490 |
+
+ // MoE branch
|
| 491 |
+
+ cur = build_norm(ffn_inp, model.layers[il].ffn_norm, NULL, LLM_NORM_RMS, il);
|
| 492 |
+
+ cb(cur, "ffn_norm", il);
|
| 493 |
+
+
|
| 494 |
+
+ if ((uint32_t) il < hparams.n_layer_dense_lead) {
|
| 495 |
+
+ cur = build_ffn(cur,
|
| 496 |
+
+ model.layers[il].ffn_up, NULL, model.layers[il].ffn_up_s,
|
| 497 |
+
+ model.layers[il].ffn_gate, NULL, model.layers[il].ffn_gate_s,
|
| 498 |
+
+ model.layers[il].ffn_down, NULL, model.layers[il].ffn_down_s,
|
| 499 |
+
+ NULL, LLM_FFN_SILU, LLM_FFN_PAR, il);
|
| 500 |
+
+ cb(cur, "ffn_out", il);
|
| 501 |
+
+ } else {
|
| 502 |
+
+ ggml_tensor * moe_out = build_moe_ffn(cur,
|
| 503 |
+
+ model.layers[il].ffn_gate_inp,
|
| 504 |
+
+ model.layers[il].ffn_up_exps,
|
| 505 |
+
+ model.layers[il].ffn_gate_exps,
|
| 506 |
+
+ model.layers[il].ffn_down_exps,
|
| 507 |
+
+ model.layers[il].ffn_exp_probs_b,
|
| 508 |
+
+ n_expert, n_expert_used,
|
| 509 |
+
+ LLM_FFN_SILU, hparams.expert_weights_norm,
|
| 510 |
+
+ hparams.expert_weights_scale,
|
| 511 |
+
+ (llama_expert_gating_func_type) hparams.expert_gating_func,
|
| 512 |
+
+ il, nullptr, nullptr,
|
| 513 |
+
+ model.layers[il].ffn_up_exps_s,
|
| 514 |
+
+ model.layers[il].ffn_gate_exps_s,
|
| 515 |
+
+ model.layers[il].ffn_down_exps_s);
|
| 516 |
+
+ cb(moe_out, "ffn_moe_out", il);
|
| 517 |
+
+
|
| 518 |
+
+ {
|
| 519 |
+
+ ggml_tensor * ffn_shexp =
|
| 520 |
+
+ build_ffn(cur,
|
| 521 |
+
+ model.layers[il].ffn_up_shexp, NULL, model.layers[il].ffn_up_shexp_s,
|
| 522 |
+
+ model.layers[il].ffn_gate_shexp, NULL, model.layers[il].ffn_gate_shexp_s,
|
| 523 |
+
+ model.layers[il].ffn_down_shexp, NULL, model.layers[il].ffn_down_shexp_s,
|
| 524 |
+
+ NULL, LLM_FFN_SILU, LLM_FFN_PAR, il);
|
| 525 |
+
+ cb(ffn_shexp, "ffn_shexp", il);
|
| 526 |
+
+
|
| 527 |
+
+ cur = ggml_add(ctx0, moe_out, ffn_shexp);
|
| 528 |
+
+ cb(cur, "ffn_out", il);
|
| 529 |
+
+ }
|
| 530 |
+
+ }
|
| 531 |
+
+ cur = ggml_add(ctx0, cur, ffn_inp);
|
| 532 |
+
+
|
| 533 |
+
+ cur = build_cvec(cur, il);
|
| 534 |
+
+ cb(cur, "l_out", il);
|
| 535 |
+
+
|
| 536 |
+
+ // input for next layer
|
| 537 |
+
+ inpL = cur;
|
| 538 |
+
+ }
|
| 539 |
+
+ cur = inpL;
|
| 540 |
+
+
|
| 541 |
+
+ cur = build_norm(cur, model.output_norm, NULL, LLM_NORM_RMS, -1);
|
| 542 |
+
+
|
| 543 |
+
+ cb(cur, "result_norm", -1);
|
| 544 |
+
+ res->t_embd = cur;
|
| 545 |
+
+
|
| 546 |
+
+ // lm_head
|
| 547 |
+
+ cur = build_lora_mm(model.output, cur, model.output_s);
|
| 548 |
+
+
|
| 549 |
+
+ cb(cur, "result_output", -1);
|
| 550 |
+
+ res->t_logits = cur;
|
| 551 |
+
+}
|
| 552 |
+
diff --git a/src/models/models.h b/src/models/models.h
|
| 553 |
+
index 9aa450deabff3ab532eb3719f8196166bfdb9a72..7a3f271e85a60b49de1e4a27c33f2f8a41d2ea9b 100644
|
| 554 |
+
--- a/src/models/models.h
|
| 555 |
+
+++ b/src/models/models.h
|
| 556 |
+
@@ -678,6 +678,19 @@ struct llama_model_qwen3moe : public llama_model_base {
|
| 557 |
+
};
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
+struct llama_model_flint : public llama_model_base {
|
| 561 |
+
+ llama_model_flint(const struct llama_model_params & params) : llama_model_base(params) {}
|
| 562 |
+
+ void load_arch_hparams(llama_model_loader & ml) override;
|
| 563 |
+
+ void load_arch_tensors(llama_model_loader & ml) override;
|
| 564 |
+
+
|
| 565 |
+
+ struct graph : public llm_graph_context {
|
| 566 |
+
+ graph(const llama_model & model, const llm_graph_params & params);
|
| 567 |
+
+ };
|
| 568 |
+
+
|
| 569 |
+
+ std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
|
| 570 |
+
+};
|
| 571 |
+
+
|
| 572 |
+
+
|
| 573 |
+
struct llama_model_qwen3vl : public llama_model_base {
|
| 574 |
+
llama_model_qwen3vl(const struct llama_model_params & params) : llama_model_base(params) {}
|
| 575 |
+
void load_arch_hparams(llama_model_loader & ml) override;
|
| 576 |
+
diff --git a/tests/fusion/MTL.csv b/tests/fusion/MTL.csv
|
| 577 |
+
index e6cd43a82b9bef3d435bed4bc33937931bf104e8..56044b350e457f5c2c0fe5d3d7d77a46cc4e220e 100644
|
| 578 |
+
--- a/tests/fusion/MTL.csv
|
| 579 |
+
+++ b/tests/fusion/MTL.csv
|
| 580 |
+
@@ -71,6 +71,9 @@ falcon ,0 ,any ,ADD+ADD , 2
|
| 581 |
+
falcon ,0 ,any ,NORM+MUL+ADD , 5
|
| 582 |
+
falcon-h1 ,0 ,any ,ADD+ADD , 2
|
| 583 |
+
falcon-h1 ,0 ,any ,RMS_NORM+MUL , 9
|
| 584 |
+
+flint ,0 ,any ,ADD+ADD , 1
|
| 585 |
+
+flint ,0 ,any ,MUL+ADD , 2
|
| 586 |
+
+flint ,0 ,any ,RMS_NORM+MUL , 9
|
| 587 |
+
gemma ,0 ,any ,RMS_NORM+MUL , 5
|
| 588 |
+
gemma2 ,0 ,any ,RMS_NORM+MUL , 5
|
| 589 |
+
gemma2 ,0 ,any ,RMS_NORM+MUL+ADD , 4
|
| 590 |
+
diff --git a/tests/test-jinja.cpp b/tests/test-jinja.cpp
|
| 591 |
+
index ddeaa1fe9a799bd27acf5002ef9b37174049a643..327ab365c44ecaa6d3dcec689a6539b88dc53564 100644
|
| 592 |
+
--- a/tests/test-jinja.cpp
|
| 593 |
+
+++ b/tests/test-jinja.cpp
|
| 594 |
+
@@ -25,6 +25,7 @@ static void test_expressions(testing & t);
|
| 595 |
+
static void test_set_statement(testing & t);
|
| 596 |
+
static void test_filters(testing & t);
|
| 597 |
+
static void test_literals(testing & t);
|
| 598 |
+
+static void test_hexadecimal_escapes(testing & t);
|
| 599 |
+
static void test_comments(testing & t);
|
| 600 |
+
static void test_macros(testing & t);
|
| 601 |
+
static void test_namespace(testing & t);
|
| 602 |
+
@@ -65,6 +66,7 @@ int main(int argc, char *argv[]) {
|
| 603 |
+
t.test("set statement", test_set_statement);
|
| 604 |
+
t.test("filters", test_filters);
|
| 605 |
+
t.test("literals", test_literals);
|
| 606 |
+
+ t.test("hexadecimal escapes", test_hexadecimal_escapes);
|
| 607 |
+
t.test("comments", test_comments);
|
| 608 |
+
t.test("macros", test_macros);
|
| 609 |
+
t.test("namespace", test_namespace);
|
| 610 |
+
@@ -1171,6 +1173,54 @@ static void test_literals(testing & t) {
|
| 611 |
+
);
|
| 612 |
+
}
|
| 613 |
+
|
| 614 |
+
+static void test_hexadecimal_escapes(testing & t) {
|
| 615 |
+
+ test_template(t, "remove NUL from message content",
|
| 616 |
+
+ R"({{ message.content | replace("\x00", "") }})",
|
| 617 |
+
+ {{"message", {{"content", std::string("\0a\0b\0", 5)}}}},
|
| 618 |
+
+ "ab"
|
| 619 |
+
+ );
|
| 620 |
+
+
|
| 621 |
+
+ test_template(t, "non-ASCII hex escape produces UTF-8",
|
| 622 |
+
+ R"({{ '\xE9' }})",
|
| 623 |
+
+ json::object(),
|
| 624 |
+
+ "\xc3\xa9"
|
| 625 |
+
+ );
|
| 626 |
+
+
|
| 627 |
+
+ test_template(t, "consume exactly two hex digits",
|
| 628 |
+
+ R"({{ '\x414' }})",
|
| 629 |
+
+ json::object(),
|
| 630 |
+
+ "A4"
|
| 631 |
+
+ );
|
| 632 |
+
+
|
| 633 |
+
+ test_template(t, "escaped backslashes remain literal",
|
| 634 |
+
+ R"({{ '\\xE9' }})",
|
| 635 |
+
+ json::object(),
|
| 636 |
+
+ R"(\xE9)"
|
| 637 |
+
+ );
|
| 638 |
+
+
|
| 639 |
+
+ if (!g_python_mode) {
|
| 640 |
+
+ const char * invalid_templates[] = {
|
| 641 |
+
+ R"({{ '\x' }})",
|
| 642 |
+
+ R"({{ '\x0' }})",
|
| 643 |
+
+ R"({{ '\xG1' }})",
|
| 644 |
+
+ R"({{ '\x1G' }})",
|
| 645 |
+
+ R"({{ '\x)",
|
| 646 |
+
+ R"({{ '\x0)",
|
| 647 |
+
+ };
|
| 648 |
+
+ for (const auto * tmpl : invalid_templates) {
|
| 649 |
+
+ t.test("reject " + json(tmpl).dump(), [&](testing & t) {
|
| 650 |
+
+ bool rejected = false;
|
| 651 |
+
+ try {
|
| 652 |
+
+ jinja::lexer().tokenize(tmpl);
|
| 653 |
+
+ } catch (const jinja::lexer_exception &) {
|
| 654 |
+
+ rejected = true;
|
| 655 |
+
+ }
|
| 656 |
+
+ t.assert_true("malformed hex escape must raise a lexer error", rejected);
|
| 657 |
+
+ });
|
| 658 |
+
+ }
|
| 659 |
+
+ }
|
| 660 |
+
+}
|
| 661 |
+
+
|
| 662 |
+
static void test_comments(testing & t) {
|
| 663 |
+
test_template(t, "inline comment",
|
| 664 |
+
"before{# comment #}after",
|
| 665 |
+
diff --git a/tests/test-llama-archs.cpp b/tests/test-llama-archs.cpp
|
| 666 |
+
index 21d27e17d930eee2e2555f01d80fbd7a81cd4927..e1cd5867ee0e57383b7775d49b7b8df8acd69af3 100644
|
| 667 |
+
--- a/tests/test-llama-archs.cpp
|
| 668 |
+
+++ b/tests/test-llama-archs.cpp
|
| 669 |
+
@@ -358,13 +358,16 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
|
| 670 |
+
ms.add_kv(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, uint32_t(5));
|
| 671 |
+
} else if (arch == LLM_ARCH_COHERE2MOE || arch == LLM_ARCH_MIMO2 || arch == LLM_ARCH_STEP35 || arch == LLM_ARCH_SPARK2_5 ||
|
| 672 |
+
arch == LLM_ARCH_MUSE_GLIMMER || arch == LLM_ARCH_MUSE_SPARK || arch == LLM_ARCH_GRANITE_SWA || arch == LLM_ARCH_DOTS3NOTE ||
|
| 673 |
+
- arch == LLM_ARCH_KOLIBRI1 || arch == LLM_ARCH_MAPLE) {
|
| 674 |
+
+ arch == LLM_ARCH_KOLIBRI1 || arch == LLM_ARCH_MAPLE || arch == LLM_ARCH_FLINT) {
|
| 675 |
+
std::vector<uint32_t> pattern;
|
| 676 |
+
pattern.reserve(n_layer);
|
| 677 |
+
for (uint32_t il = 0; il < n_layer; il++) {
|
| 678 |
+
pattern.push_back(il % 2);
|
| 679 |
+
}
|
| 680 |
+
ms.add_kv(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, pattern);
|
| 681 |
+
+ if (arch == LLM_ARCH_FLINT) {
|
| 682 |
+
+ ms.add_kv(LLM_KV_ATTENTION_ROPE_PATTERN, pattern);
|
| 683 |
+
+ }
|
| 684 |
+
} else {
|
| 685 |
+
ms.add_kv(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, uint32_t(2));
|
| 686 |
+
}
|
| 687 |
+
@@ -522,6 +525,9 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
|
| 688 |
+
ms.add_kv(LLM_KV_EXPERT_GATING_FUNC, gating_func); // sigmoid : softmax : sqrtsoftplus
|
| 689 |
+
ms.add_kv(LLM_KV_EXPERT_GROUP_SCALE, 1.0f);
|
| 690 |
+
ms.add_kv(LLM_KV_EXPERTS_PER_GROUP, uint32_t(1));
|
| 691 |
+
+ if (arch == LLM_ARCH_FLINT) {
|
| 692 |
+
+ ms.add_kv(LLM_KV_EXPERT_WEIGHTS_NORM, true);
|
| 693 |
+
+ }
|
| 694 |
+
if (arch == LLM_ARCH_K2_HORIZON) {
|
| 695 |
+
ms.add_kv(LLM_KV_ATTENTION_VALUE_EXPERT_COUNT, uint32_t(2));
|
| 696 |
+
ms.add_kv(LLM_KV_ATTENTION_VALUE_EXPERT_USED_COUNT, uint32_t(2));
|
| 697 |
+
@@ -751,6 +757,7 @@ static bool check_causal_attn_toggle(
|
| 698 |
+
|
| 699 |
+
static bool moe_mandatory(const llm_arch arch) {
|
| 700 |
+
switch (arch) {
|
| 701 |
+
+ case LLM_ARCH_FLINT:
|
| 702 |
+
case LLM_ARCH_LLAMA4:
|
| 703 |
+
case LLM_ARCH_MAION_CODER:
|
| 704 |
+
case LLM_ARCH_COHERE2MOE:
|
experiments/reproduce/quant-surgery-build.sh
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
if [ "$#" -ne 3 ]; then
|
| 4 |
+
echo 'usage: quant-surgery-build.sh LLAMA_SOURCE LLAMA_LIBRARY_DIR OUTPUT' >&2
|
| 5 |
+
exit 2
|
| 6 |
+
fi
|
| 7 |
+
experiment_dir=$(cd "$(dirname "$0")" && pwd)
|
| 8 |
+
expected_tree=83e9d4c276f84753cce063502a2a9e2325f15ce3
|
| 9 |
+
actual_tree=$(git -C "$1" write-tree)
|
| 10 |
+
if [ "$actual_tree" != "$expected_tree" ] || ! git -C "$1" diff --quiet; then
|
| 11 |
+
echo 'Runtime source differs from the measured source tree. Follow README.md.' >&2
|
| 12 |
+
exit 1
|
| 13 |
+
fi
|
| 14 |
+
nice -n 10 c++ -std=c++17 -O2 \
|
| 15 |
+
-I"$1/include" -I"$1/ggml/include" -I"$1/vendor" \
|
| 16 |
+
"$experiment_dir/quant-surgery.cpp" -L"$2" -Wl,-rpath,"$2" \
|
| 17 |
+
-l:libllama.so.0 -lggml -lggml-base -pthread -o "$3"
|
experiments/reproduce/quant-surgery.cpp
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "llama.h"
|
| 2 |
+
#include "ggml.h"
|
| 3 |
+
#include "ggml-backend.h"
|
| 4 |
+
#include "nlohmann/json.hpp"
|
| 5 |
+
#include <algorithm>
|
| 6 |
+
#include <chrono>
|
| 7 |
+
#include <cmath>
|
| 8 |
+
#include <cstring>
|
| 9 |
+
#include <fstream>
|
| 10 |
+
#include <iostream>
|
| 11 |
+
#include <numeric>
|
| 12 |
+
#include <string>
|
| 13 |
+
#include <vector>
|
| 14 |
+
#include <unistd.h>
|
| 15 |
+
#include <sys/resource.h>
|
| 16 |
+
|
| 17 |
+
using json=nlohmann::json;
|
| 18 |
+
using Clock=std::chrono::steady_clock;
|
| 19 |
+
static std::string STOP,MODEL_ID,ACTIVE_ID;
|
| 20 |
+
static void emit(json j){j["model"]=MODEL_ID;if(!ACTIVE_ID.empty())j["id"]=ACTIVE_ID;std::cout<<j.dump(-1,' ',false,json::error_handler_t::replace)<<std::endl;}
|
| 21 |
+
static double seconds(Clock::time_point t){return std::chrono::duration<double>(Clock::now()-t).count();}
|
| 22 |
+
static void quiet(enum ggml_log_level level,const char*text,void*){if(level>=GGML_LOG_LEVEL_WARN)std::cerr<<text;}
|
| 23 |
+
struct Capture{
|
| 24 |
+
json config,layers=json::array();int step=0,position=0;bool enabled=false;
|
| 25 |
+
bool linear(int l)const{return config.at("layer_types")[l]=="linear_attention";}
|
| 26 |
+
int heads(int l)const{return linear(l)?config.at("linear_num_value_heads").get<int>():config.at("num_attention_heads").get<int>();}
|
| 27 |
+
int dimension(int l)const{return linear(l)?config.at("linear_value_head_dim").get<int>():config.at("head_dim").get<int>();}
|
| 28 |
+
};
|
| 29 |
+
static bool stopped(void*){
|
| 30 |
+
if(STOP.empty()||access(STOP.c_str(),F_OK)!=0)return false;
|
| 31 |
+
std::ifstream file(STOP);std::string id;std::getline(file,id);return id==ACTIVE_ID;
|
| 32 |
+
}
|
| 33 |
+
static float at(const ggml_tensor*t,const std::vector<uint8_t>&b,int x,int y=0,int z=0,int w=0){
|
| 34 |
+
size_t off=x*t->nb[0]+y*t->nb[1]+z*t->nb[2]+w*t->nb[3];
|
| 35 |
+
if(off+ggml_type_size(t->type)>b.size())throw std::runtime_error(std::string(t->name)+" tensor stride outside capture");
|
| 36 |
+
if(t->type==GGML_TYPE_F32){float v;memcpy(&v,b.data()+off,4);return v;}
|
| 37 |
+
if(t->type==GGML_TYPE_F16){ggml_fp16_t v;memcpy(&v,b.data()+off,2);return ggml_fp16_to_fp32(v);}
|
| 38 |
+
if(t->type==GGML_TYPE_I32){int32_t v;memcpy(&v,b.data()+off,4);return v;}
|
| 39 |
+
throw std::runtime_error("Unsupported activation dtype");
|
| 40 |
+
}
|
| 41 |
+
static double intervention_scale=1.0;
|
| 42 |
+
static int intervention_layer=12;
|
| 43 |
+
static bool capture(ggml_tensor*t,bool ask,void*user){
|
| 44 |
+
auto&c=*static_cast<Capture*>(user);if(!c.enabled)return !ask;
|
| 45 |
+
std::string name=t->name;auto dash=name.rfind('-');if(dash==std::string::npos)return !ask;
|
| 46 |
+
std::string kind=name.substr(0,dash);int layer=std::stoi(name.substr(dash+1));
|
| 47 |
+
bool wanted=(kind=="ffn_out"&&(layer>=11&&layer<=13))||(kind=="l_out"&&layer==13);
|
| 48 |
+
if(ask)return wanted;if(!wanted)return true;
|
| 49 |
+
if(t->type!=GGML_TYPE_F32&&t->type!=GGML_TYPE_F16)throw std::runtime_error("Unexpected intervention activation dtype");
|
| 50 |
+
if(!ggml_is_contiguous(t))throw std::runtime_error("Intervention requires a contiguous FFN output");
|
| 51 |
+
std::vector<uint8_t>buf(ggml_nbytes(t));ggml_backend_tensor_get(t,buf.data(),0,buf.size());
|
| 52 |
+
auto rms=[&](){double sq=0;int last=t->ne[1]-1;for(int x=0;x<t->ne[0];x++){double v=at(t,buf,x,last);sq+=v*v;}return std::sqrt(sq/t->ne[0]);};
|
| 53 |
+
double before=rms();bool changed=MODEL_ID=="flint"&&kind=="ffn_out"&&layer==intervention_layer&&intervention_scale!=1;
|
| 54 |
+
if(changed){
|
| 55 |
+
for(int64_t i=0;i<ggml_nelements(t);i++){
|
| 56 |
+
if(t->type==GGML_TYPE_F32){float v;memcpy(&v,buf.data()+4*i,4);v*=intervention_scale;memcpy(buf.data()+4*i,&v,4);}
|
| 57 |
+
else{ggml_fp16_t v;memcpy(&v,buf.data()+2*i,2);v=ggml_fp32_to_fp16(ggml_fp16_to_fp32(v)*intervention_scale);memcpy(buf.data()+2*i,&v,2);}
|
| 58 |
+
}
|
| 59 |
+
ggml_backend_tensor_set(t,buf.data(),0,buf.size());
|
| 60 |
+
}
|
| 61 |
+
double after=rms();c.layers.push_back({{"layer",layer},{"tensor",kind},{"before_rms",before},{"after_rms",after},{"scale",changed?intervention_scale:1.0},{"tensor_bytes",ggml_nbytes(t)}});
|
| 62 |
+
return !stopped(nullptr);
|
| 63 |
+
}
|
| 64 |
+
static std::string piece(const llama_vocab*v,llama_token t){
|
| 65 |
+
std::vector<char>b(512);int n=llama_token_to_piece(v,t,b.data(),b.size(),0,true);if(n<0){b.resize(-n);n=llama_token_to_piece(v,t,b.data(),b.size(),0,true);}return std::string(b.data(),std::max(0,n));
|
| 66 |
+
}
|
| 67 |
+
int main(int argc,char**argv){
|
| 68 |
+
if(argc<3){std::cerr<<"usage: quant-surgery MODEL.gguf SPEC.json\n";return 2;}
|
| 69 |
+
Capture c;json spec;
|
| 70 |
+
try{std::ifstream file(argv[2]);file>>spec;c.config=spec.at("architecture");MODEL_ID=spec.at("id");STOP=spec.at("stop_path");}catch(const std::exception&e){emit({{"type","fatal"},{"error",std::string("Invalid model specification: ")+e.what()}});return 1;}
|
| 71 |
+
llama_log_set(quiet,nullptr);llama_backend_init();auto mp=llama_model_default_params();mp.n_gpu_layers=0;ggml_backend_dev_t devices[]={nullptr};mp.devices=devices;
|
| 72 |
+
auto model=llama_model_load_from_file(argv[1],mp);if(!model){emit({{"type","fatal"},{"error","Could not load model checkpoint"}});return 1;}
|
| 73 |
+
if(llama_model_n_layer(model)!=c.config.at("num_hidden_layers").get<int>()||llama_model_n_embd(model)!=c.config.at("hidden_size").get<int>()){emit({{"type","fatal"},{"error","Checkpoint dimensions do not match the model specification"}});return 1;}
|
| 74 |
+
auto vocab=llama_model_get_vocab(model);
|
| 75 |
+
auto cp=llama_context_default_params();cp.n_ctx=2048;cp.n_batch=256;cp.n_ubatch=256;cp.n_threads=8;cp.n_threads_batch=8;cp.n_rs_seq=0;
|
| 76 |
+
cp.flash_attn_type=LLAMA_FLASH_ATTN_TYPE_DISABLED;cp.cb_eval=capture;cp.cb_eval_user_data=&c;cp.abort_callback=stopped;
|
| 77 |
+
auto ctx=llama_init_from_model(model,cp);if(!ctx){emit({{"type","fatal"},{"error","Could not initialize tracing context"}});return 1;}
|
| 78 |
+
emit({{"type","ready"},{"engine","llama.cpp 74686f08"},{"quantization",spec.at("quantization")},{"device","CPU"},{"threads",8},{"context",2048}});
|
| 79 |
+
std::string line;
|
| 80 |
+
while(std::getline(std::cin,line)){
|
| 81 |
+
llama_sampler*sampler=nullptr;
|
| 82 |
+
try{
|
| 83 |
+
auto req=json::parse(line);intervention_scale=req.value("intervention_scale",1.0);intervention_layer=req.value("intervention_layer",12);if(intervention_scale<0||intervention_scale>1)throw std::runtime_error("Scale must be between zero and one");if(MODEL_ID!="flint"&&intervention_scale!=1)throw std::runtime_error("Intervention supports Flint only");ACTIVE_ID=req.value("id","manual");unlink(STOP.c_str());llama_memory_clear(llama_get_memory(ctx),true);
|
| 84 |
+
std::string prompt=req.at("prompt");for(size_t pos=0;(pos=prompt.find("<|",pos))!=std::string::npos;pos+=5)prompt.replace(pos,2,"<|");
|
| 85 |
+
bool reasoning=req.value("reasoning",false),flint=MODEL_ID=="flint";
|
| 86 |
+
std::string system="You are a helpful assistant. Answer clearly and concisely.";
|
| 87 |
+
std::string text=flint?"<|system|>"+system+"<|user|>"+prompt+"<|assistant|>"+(reasoning?"":"<think></think>"):"<|im_start|>system\n"+system+"<|im_end|>\n<|im_start|>user\n"+prompt+"<|im_end|>\n<|im_start|>assistant\n<think>\n"+(reasoning?"":"\n</think>\n\n");
|
| 88 |
+
int n=-llama_tokenize(vocab,text.data(),text.size(),nullptr,0,flint,true);
|
| 89 |
+
if(n<=0)throw std::runtime_error("Could not tokenize the prompt");
|
| 90 |
+
if(n>256)throw std::runtime_error("Prompt exceeds the tracing limit of 256 tokens. Shorten it and run again.");
|
| 91 |
+
std::vector<llama_token>tokens(n);llama_tokenize(vocab,text.data(),text.size(),tokens.data(),n,flint,true);
|
| 92 |
+
json pieces=json::array();for(auto t:tokens)pieces.push_back(piece(vocab,t));
|
| 93 |
+
int limit=std::clamp(req.value("max_tokens",64),1,256);sampler=llama_sampler_chain_init(llama_sampler_chain_default_params());double temperature=req.value("temperature",0.0);
|
| 94 |
+
if(temperature<=0)llama_sampler_chain_add(sampler,llama_sampler_init_greedy());else{llama_sampler_chain_add(sampler,llama_sampler_init_top_k(40));llama_sampler_chain_add(sampler,llama_sampler_init_top_p(.95,1));llama_sampler_chain_add(sampler,llama_sampler_init_temp(temperature));llama_sampler_chain_add(sampler,llama_sampler_init_dist(req.value("seed",42)));}
|
| 95 |
+
emit({{"type","start"},{"prompt_tokens",n},{"tokens",pieces},{"max_tokens",limit},{"temperature",temperature},{"seed",req.value("seed",42)},{"reasoning",reasoning}});
|
| 96 |
+
auto started=Clock::now();int pos=0,count=n,generated=0;std::string reason="limit";
|
| 97 |
+
for(int step=0;step<limit;step++){
|
| 98 |
+
c.step=step;c.position=pos+count-1;c.layers=json::array();c.enabled=true;
|
| 99 |
+
auto before=Clock::now();auto batch=llama_batch_get_one(tokens.data(),count);int rc=llama_decode(ctx,batch);llama_synchronize(ctx);
|
| 100 |
+
if(rc){if(stopped(nullptr)){reason="stopped";break;}throw std::runtime_error("Inference failed with code "+std::to_string(rc));}pos+=count;
|
| 101 |
+
if(c.layers.size()!=4)throw std::runtime_error("Missing intervention measurement callbacks");
|
| 102 |
+
auto id=llama_sampler_sample(sampler,ctx,-1);llama_sampler_accept(sampler,id);if(llama_vocab_is_eog(vocab,id)){reason="eos";break;}
|
| 103 |
+
auto p=piece(vocab,id);generated++;
|
| 104 |
+
emit({{"type","token"},{"step",step},{"token_id",id},{"text",p},{"token_bytes",std::vector<uint8_t>(p.begin(),p.end())},{"input_position",c.position},{"input_text",pieces.back()},{"layers",c.layers},{"elapsed",seconds(started)},{"duration",seconds(before)},{"phase",step==0?"prefill":"decode"}});
|
| 105 |
+
pieces.push_back(p);tokens={id};count=1;if(stopped(nullptr)){reason="stopped";break;}
|
| 106 |
+
}
|
| 107 |
+
c.enabled=false;llama_sampler_free(sampler);sampler=nullptr;struct rusage usage;getrusage(RUSAGE_SELF,&usage);emit({{"max_rss_kib",usage.ru_maxrss},{"intervention_scale",intervention_scale},{"intervention_layer",intervention_layer},{"type","done"},{"reason",reason},{"generated",generated},{"elapsed",seconds(started)}});
|
| 108 |
+
}catch(const std::exception&e){c.enabled=false;if(sampler)llama_sampler_free(sampler);emit({{"type","error"},{"error",e.what()}});}
|
| 109 |
+
ACTIVE_ID.clear();
|
| 110 |
+
}
|
| 111 |
+
llama_free(ctx);llama_model_free(model);llama_backend_free();
|
| 112 |
+
}
|
experiments/reproduce/quant-surgery.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Run matched CPU quantization and reversible activation-scaling experiments."""
|
| 3 |
+
import argparse, datetime, hashlib, json, math, pathlib, queue, re, subprocess, threading, time
|
| 4 |
+
|
| 5 |
+
CASES = [
|
| 6 |
+
{"id":"arithmetic","title":"Exact arithmetic","prompt":"Calculate (17 * 23) - 19. Reply with the integer only.","check":"integer","expected":"372"},
|
| 7 |
+
{"id":"json","title":"Strict JSON format","prompt":"Return exactly this JSON object and no other text: {\"status\":\"ok\",\"count\":3}","check":"json","expected":{"status":"ok","count":3}},
|
| 8 |
+
{"id":"fact","title":"Short factual recall","prompt":"What is the chemical symbol for gold? Reply with the symbol only.","check":"exact","expected":"Au"},
|
| 9 |
+
{"id":"correction","title":"Use the corrected fact","prompt":"The access color was blue. Correction: the access color is now amber. What is the current access color? Reply with one word only.","check":"exact","expected":"amber"},
|
| 10 |
+
{"id":"code","title":"Python expression repair","prompt":"Complete the function with one Python expression only, no markdown: def is_even(n): return ___ . The function must return True exactly when integer n is even.","check":"expression","expected":"n % 2 == 0"},
|
| 11 |
+
{"id":"brief","title":"One sentence explanation","prompt":"Explain photosynthesis in exactly one sentence containing the words sunlight and glucose.","check":"sentence","expected":["sunlight","glucose"]},
|
| 12 |
+
]
|
| 13 |
+
|
| 14 |
+
def evaluate(case, output):
|
| 15 |
+
text=output.strip()
|
| 16 |
+
if case['check']=='integer': passed=text==case['expected']
|
| 17 |
+
elif case['check']=='exact': passed=text.casefold()==case['expected'].casefold()
|
| 18 |
+
elif case['check']=='json':
|
| 19 |
+
try: passed=json.loads(text)==case['expected']
|
| 20 |
+
except (ValueError,TypeError): passed=False
|
| 21 |
+
elif case['check']=='sentence': passed=all(w in text.lower() for w in case['expected']) and len(re.findall(r'[.!?]+(?:\s|$)',text))==1 and '\n' not in text
|
| 22 |
+
elif case['check']=='expression':
|
| 23 |
+
# Compare a small, explicit set of valid expressions. Do not execute model code.
|
| 24 |
+
compact=re.sub(r'\s+','',text).lower()
|
| 25 |
+
passed=compact in {'n%2==0','(n%2)==0','not(n%2)','notn%2','(n&1)==0','n&1==0','n%2!=1'}
|
| 26 |
+
else: raise ValueError(case['check'])
|
| 27 |
+
return {"passed":passed,"rule":case['check'],"expected":case['expected'],"character_count":len(text),"word_count":len(text.split())}
|
| 28 |
+
|
| 29 |
+
class Engine:
|
| 30 |
+
def __init__(self,binary,checkpoint,spec,log):
|
| 31 |
+
self.log=open(log,'w'); self.q=queue.Queue();started=time.perf_counter()
|
| 32 |
+
self.p=subprocess.Popen(['nice','-n','10',str(binary.resolve()),str(checkpoint.resolve()),str(spec.resolve())],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=self.log,text=True,bufsize=1)
|
| 33 |
+
def read():
|
| 34 |
+
try:
|
| 35 |
+
for line in self.p.stdout:
|
| 36 |
+
try:self.q.put(json.loads(line))
|
| 37 |
+
except ValueError:self.q.put({'type':'error','error':'Non-JSON native output'})
|
| 38 |
+
finally:self.q.put({'type':'eof'})
|
| 39 |
+
self.thread=threading.Thread(target=read,daemon=True);self.thread.start()
|
| 40 |
+
self.ready=self.q.get(timeout=300)
|
| 41 |
+
if self.ready.get('type')!='ready':self.close();raise RuntimeError(self.ready)
|
| 42 |
+
self.load_seconds=time.perf_counter()-started
|
| 43 |
+
def run(self,case,condition):
|
| 44 |
+
request={"id":case['id']+'-'+condition['id'],"prompt":case['prompt'],"max_tokens":64,"temperature":0,"seed":42,"reasoning":False,"intervention_layer":12,"intervention_scale":condition.get('scale',1.0)}
|
| 45 |
+
self.p.stdin.write(json.dumps(request)+'\n');self.p.stdin.flush();started=time.perf_counter();frames=[];first=None;start=None
|
| 46 |
+
while True:
|
| 47 |
+
event=self.q.get(timeout=180)
|
| 48 |
+
if event['type']=='start':start=event
|
| 49 |
+
if event['type']=='token':
|
| 50 |
+
if first is None:first=time.perf_counter()-started
|
| 51 |
+
frames.append(event)
|
| 52 |
+
if event['type'] in ('error','fatal','eof'):raise RuntimeError(event)
|
| 53 |
+
if event['type']=='done':break
|
| 54 |
+
wall=time.perf_counter()-started
|
| 55 |
+
output=b''.join(bytes(f['token_bytes']) for f in frames).decode('utf-8',errors='replace')
|
| 56 |
+
if event['reason']!='eos':print('TRUNCATED',request['id'],flush=True)
|
| 57 |
+
metrics={"generated_tokens":len(frames),"prompt_tokens":start['prompt_tokens'],"elapsed_seconds":event['elapsed'],"request_wall_seconds":wall,"time_to_first_token_seconds":first,"decode_tokens_per_second":(len(frames)-1)/(event['elapsed']-frames[0]['elapsed']) if len(frames)>1 else None,"process_peak_rss_kib":event['max_rss_kib'],"termination":event['reason'],"complete":event['reason']=='eos'}
|
| 58 |
+
result={**{k:v for k,v in condition.items() if k!='scale'},"output":output,"metrics":metrics,"check":evaluate(case,output),"settings":{k:request[k] for k in ('max_tokens','temperature','seed','reasoning','intervention_layer','intervention_scale')},"measurements":[{"step":f['step'],"token_id":f['token_id'],"input_position":f['input_position'],"layers":f['layers']} for f in frames]}
|
| 59 |
+
return result
|
| 60 |
+
def close(self):
|
| 61 |
+
if self.p.poll() is None:
|
| 62 |
+
try:self.p.stdin.close();self.p.wait(timeout=30)
|
| 63 |
+
except (BrokenPipeError,subprocess.TimeoutExpired):self.p.terminate();self.p.wait(timeout=30)
|
| 64 |
+
self.log.close()
|
| 65 |
+
|
| 66 |
+
def validate_results(results):
|
| 67 |
+
aggregate={"quantization_pairs":0,"surgery_cases":0,"intervention_frames":0,"upstream_first_frame_matches":0,"downstream_first_frame_changes":0,"complete_generations":0}
|
| 68 |
+
for case in results['quantization_cases']:
|
| 69 |
+
if {c['quant'] for c in case['conditions']}!={'Q4_1','Q8_0'}:raise ValueError('Incomplete quantization pair')
|
| 70 |
+
aggregate['quantization_pairs']+=1
|
| 71 |
+
for condition in case['conditions']:
|
| 72 |
+
if not condition['metrics']['complete']:raise ValueError('Truncated generation')
|
| 73 |
+
aggregate['complete_generations']+=1
|
| 74 |
+
for case in results['surgery_cases']:
|
| 75 |
+
base=case['conditions'][0];base_first={(v['layer'],v['tensor']):v for v in base['measurements'][0]['layers']}
|
| 76 |
+
for condition in case['conditions']:
|
| 77 |
+
scale=condition['settings']['intervention_scale']
|
| 78 |
+
for frame in condition['measurements']:
|
| 79 |
+
v=next(x for x in frame['layers'] if x['layer']==12 and x['tensor']=='ffn_out')
|
| 80 |
+
if not math.isclose(v['after_rms'],v['before_rms']*scale,rel_tol=2e-6,abs_tol=1e-12):raise ValueError('Intervention scaling mismatch')
|
| 81 |
+
aggregate['intervention_frames']+=1
|
| 82 |
+
first={(v['layer'],v['tensor']):v for v in condition['measurements'][0]['layers']}
|
| 83 |
+
if first[11,'ffn_out']['after_rms']!=base_first[11,'ffn_out']['after_rms']:raise ValueError('Upstream control changed')
|
| 84 |
+
aggregate['upstream_first_frame_matches']+=1
|
| 85 |
+
if scale!=1:
|
| 86 |
+
if first[13,'l_out']['after_rms']==base_first[13,'l_out']['after_rms']:raise ValueError('Downstream intervention not observed')
|
| 87 |
+
aggregate['downstream_first_frame_changes']+=1
|
| 88 |
+
if not condition['metrics']['complete']:raise ValueError('Truncated surgery generation')
|
| 89 |
+
aggregate['complete_generations']+=1
|
| 90 |
+
aggregate['surgery_cases']+=1
|
| 91 |
+
aggregate['passed']=True
|
| 92 |
+
return aggregate
|
| 93 |
+
|
| 94 |
+
def main():
|
| 95 |
+
ap=argparse.ArgumentParser();ap.add_argument('--engine',type=pathlib.Path,required=True);ap.add_argument('--flint-q8',type=pathlib.Path,required=True);ap.add_argument('--flint-q4',type=pathlib.Path,required=True);ap.add_argument('--qwen-q8',type=pathlib.Path,required=True);ap.add_argument('--qwen-q4',type=pathlib.Path,required=True);ap.add_argument('--flint-config',type=pathlib.Path,required=True);ap.add_argument('--qwen-config',type=pathlib.Path,required=True);ap.add_argument('--output',type=pathlib.Path,required=True);args=ap.parse_args()
|
| 96 |
+
out=args.output.resolve();out.parent.mkdir(parents=True,exist_ok=True)
|
| 97 |
+
spec_dir=out.parent/'specs';spec_dir.mkdir(exist_ok=True)
|
| 98 |
+
expected_checkpoints=json.loads(pathlib.Path(__file__).with_name('checkpoints.json').read_text())
|
| 99 |
+
provenance={"engine":"llama.cpp 74686f084c53506a7afa471d857db02e5c912543","device":"CPU","threads":8,"nice":10,"context":2048,"flash_attention":False,"kv_cache":"default F16 attention KV; model-native recurrent state","sampling":{"temperature":0,"seed":42,"max_tokens":64,"reasoning":False},"intervention":{"tensor":"ffn_out-12","layer_indexing":"zero-based","applies_to":"all prompt and generated positions on each forward pass","operation":"scale computed FFN output before residual addition","scales":[1,0.5,0],"weights_modified":False},"memory_metric":"ru_maxrss process high-water mark, includes model loading and warm-up; KiB on Linux","latency_metric":"native monotonic request elapsed plus client wall clock; first-token latency includes prompt prefill","created_at":datetime.datetime.now(datetime.timezone.utc).isoformat(),"source_sha256":hashlib.sha256(pathlib.Path(__file__).with_suffix('.cpp').read_bytes()).hexdigest(),"binary_sha256":hashlib.sha256(args.engine.read_bytes()).hexdigest(),"hardware":{"cpu":next(v["data"] for v in json.loads(subprocess.check_output(["lscpu","-J"],text=True))["lscpu"] if v["field"]=="Model name:")},"checkpoints":[],"warmups":[]}
|
| 100 |
+
results={"schema":"alphabell.quant-surgery.v1","provenance":provenance,"quantization_cases":[],"surgery_cases":[]}
|
| 101 |
+
records={c['id']:{"id":c['id'],"title":c['title'],"prompt":c['prompt'],"checks":{"rule":c['check'],"expected":c['expected']},"conditions":[]} for c in CASES}
|
| 102 |
+
def save():
|
| 103 |
+
temp=out.with_suffix('.tmp');temp.write_text(json.dumps(results,ensure_ascii=False,indent=2));temp.replace(out)
|
| 104 |
+
for model,quant,path in [('flint','Q8_0',args.flint_q8),('flint','Q4_1',args.flint_q4),('qwen','Q8_0',args.qwen_q8),('qwen','Q4_1',args.qwen_q4)]:
|
| 105 |
+
expected=expected_checkpoints[model][quant]
|
| 106 |
+
if path.stat().st_size!=expected['bytes']:raise ValueError('Checkpoint byte count differs from release manifest')
|
| 107 |
+
with path.open('rb') as checkpoint_file: checkpoint_sha=hashlib.file_digest(checkpoint_file,'sha256').hexdigest()
|
| 108 |
+
if checkpoint_sha!=expected['sha256']:raise ValueError('Checkpoint SHA-256 differs from release manifest')
|
| 109 |
+
config=json.loads((args.flint_config if model=='flint' else args.qwen_config).read_text())
|
| 110 |
+
spec=spec_dir/f'{model}-{quant}.json';spec.write_text(json.dumps({'id':model,'quantization':quant,'architecture':config,'stop_path':str(spec_dir/f'stop-{model}-{quant}')}))
|
| 111 |
+
provenance['checkpoints'].append({'model':model,'quantization':quant,'file_bytes':path.stat().st_size,'sha256':checkpoint_sha,'gguf_name':expected['gguf_name'],'architecture_sha256':hashlib.sha256(json.dumps(config,sort_keys=True).encode()).hexdigest()})
|
| 112 |
+
engine=Engine(args.engine,path,spec,out.parent/f'{model}-{quant}.stderr.log')
|
| 113 |
+
try:
|
| 114 |
+
warmcase={'id':'warmup','prompt':'What is 2 plus 2? Reply with the integer only.','check':'integer','expected':'4'}
|
| 115 |
+
warm=engine.run(warmcase,{'id':'warmup','model':model,'quant':quant,'label':'Warm-up','scale':1});provenance['warmups'].append({'model':model,'quant':quant,'load_seconds':engine.load_seconds,'metrics':warm['metrics']})
|
| 116 |
+
for case in CASES:
|
| 117 |
+
condition={'id':f'{model}-{quant.lower()}','label':quant,'model':model,'quant':quant,'scale':1}
|
| 118 |
+
result=engine.run(case,condition);records[case['id']]['conditions'].append(result)
|
| 119 |
+
if model=='flint' and quant=='Q8_0':
|
| 120 |
+
surgery={"id":case['id'],"title":case['title'],"prompt":case['prompt'],"checks":records[case['id']]['checks'],"intervention":provenance['intervention'],"conditions":[{**result,'id':'baseline','label':'Baseline, scale 1'}]}
|
| 121 |
+
for scale,label in [(0.5,'Half FFN, scale 0.5'),(0,'Zero FFN, scale 0')]:
|
| 122 |
+
altered=engine.run(case,{'id':'half' if scale else 'zero','label':label,'model':model,'quant':quant,'scale':scale});surgery['conditions'].append(altered)
|
| 123 |
+
results['surgery_cases'].append(surgery)
|
| 124 |
+
results['quantization_cases']=[{**records[c['id']],"id":modelname+'-'+c['id'],"model":modelname,"conditions":[x for x in records[c['id']]['conditions'] if x['model']==modelname]} for modelname in ('flint','qwen') for c in CASES if any(x['model']==modelname for x in records[c['id']]['conditions'])]
|
| 125 |
+
save();print(model,quant,case['id'],len(result['output']),result['check']['passed'],round(result['metrics']['elapsed_seconds'],2),flush=True)
|
| 126 |
+
finally:engine.close()
|
| 127 |
+
results['validation']=validate_results(results);provenance['completed_at']=datetime.datetime.now(datetime.timezone.utc).isoformat();save()
|
| 128 |
+
print('COMPLETE',out,flush=True)
|
| 129 |
+
if __name__=='__main__':main()
|
experiments/sound.css
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root{color-scheme:light;--bg:#f4f7ed;--paper:#fffef8;--ink:#203c30;--muted:#60745f;--line:#d5dfcb;--green:#285c43}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}a{color:var(--green);text-underline-offset:4px}header,main,footer{width:min(1180px,calc(100% - 48px));margin:auto}header{height:88px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);gap:20px}header>a:last-child{font-size:13px}.brand{text-decoration:none;font-family:Georgia,serif;font-size:38px;display:flex;align-items:center;gap:10px}.brand span{font-family:system-ui,sans-serif;font-size:20px;font-weight:600;letter-spacing:-.6px}main{display:grid;grid-template-columns:.85fr 1.15fr;gap:52px;padding:66px 0}.eyebrow{font:11px/1.6 ui-monospace,monospace;letter-spacing:1.8px;color:var(--muted);margin:0 0 22px}h1{font:normal clamp(44px,5.8vw,76px)/1.04 Georgia,serif;letter-spacing:-2.5px;margin:0 0 28px}h2{font:normal 34px/1.12 Georgia,serif;letter-spacing:-.7px;margin:0 0 20px}.lede{font-size:19px;line-height:1.65}.honest{font-size:13px;border-left:2px solid #9fb58f;padding-left:16px;color:var(--muted);max-width:350px}.studio{min-width:0;border:1px solid var(--line);border-radius:16px;background:var(--paper);padding:24px;box-shadow:0 10px 32px #31552c08}.controls{display:grid;grid-template-columns:1fr 1fr;gap:15px}label{font-size:11px;text-transform:uppercase;letter-spacing:.7px;color:var(--muted);font-weight:600;display:block}select{display:block;width:100%;min-width:0;margin-top:6px;border:1px solid var(--line);border-radius:7px;padding:9px 8px;background:#f7f9f1;color:var(--ink);font:13px system-ui,sans-serif;min-height:40px}input[type=range]{accent-color:var(--green);width:100%;min-height:24px;cursor:pointer;margin:8px 0 0}output{float:right}.status{font-size:12px;line-height:1.5;color:var(--muted);margin:20px 0 12px;min-height:36px}.status.error{color:#943d27}.retry{margin-bottom:12px}.visual{background:#f0f4e8;border:1px solid var(--line);border-radius:9px;padding:14px 12px}.canvas-head,.canvas-foot{display:flex;justify-content:space-between;gap:10px;font:10px/1.4 ui-monospace,monospace;color:var(--muted)}#soundCanvas{width:100%;height:250px;display:block;margin:12px 0}.canvas-foot{font-size:9px}.canvas-foot>span:last-child{display:flex;align-items:center;gap:5px;white-space:nowrap}.canvas-foot i{width:40px;height:7px;background:linear-gradient(90deg,#dce6d0,#246644);display:inline-block}.transport{display:flex;flex-wrap:wrap;gap:8px;margin:18px 0}button{cursor:pointer;min-height:42px;border:1px solid var(--line);border-radius:7px;padding:10px 13px;background:#fafcf5;color:var(--ink);font:600 12px system-ui,sans-serif}button.primary{background:var(--green);color:#fff;border-color:var(--green)}button:disabled{opacity:.45;cursor:default}button:focus-visible,select:focus-visible,input:focus-visible,a:focus-visible{outline:3px solid #78a366;outline-offset:3px}.scrubber-label{display:flex;justify-content:space-between}.token-now{display:flex;align-items:center;gap:15px;padding:15px 0;border-bottom:1px solid var(--line);min-width:0}.token-now span{font:9px ui-monospace,monospace;color:var(--muted);flex-shrink:0}.token-now strong{white-space:pre-wrap;overflow-wrap:anywhere;font:22px/1.4 Georgia,serif;min-width:0}.prompt{font-size:12px;color:var(--muted)}.prompt strong{font-size:10px;text-transform:uppercase;letter-spacing:.6px}.readings{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:7px}.reading{background:#f0f4e8;padding:8px;border-radius:5px;font:10px/1.7 ui-monospace,monospace;overflow-wrap:anywhere}.reading strong{display:block;font-size:11px;color:var(--green)}.notes{grid-column:1/-1;display:grid;grid-template-columns:.85fr 1.15fr;gap:52px;border-top:1px solid var(--line);padding-top:46px;margin-top:12px}.notes p{color:var(--muted);font-size:14px}.mapping h3{font-size:14px;margin:0 0 7px}.mapping p{margin:0 0 24px}.mapping p.fine{font-size:12px}footer{border-top:1px solid var(--line);display:flex;flex-wrap:wrap;gap:20px;padding:24px 0 36px;color:var(--muted);font-size:11px}footer span{margin-right:auto}[hidden]{display:none!important}@media(max-width:780px){header,main,footer{width:calc(100% - 32px)}header{height:72px}main{grid-template-columns:1fr;gap:30px;padding-top:34px}h1{font-size:54px}.intro{max-width:580px}.lede{font-size:17px}.honest{max-width:none}.notes{grid-template-columns:1fr;gap:18px}.studio{padding:18px}.notes h2{max-width:350px}}@media(max-width:380px){header,main,footer{width:calc(100% - 24px)}header>a:last-child{font-size:11px}.brand{font-size:32px}.brand span{font-size:18px}h1{font-size:46px}.studio{padding:13px}.controls{gap:10px}select{font-size:11px;padding-left:5px}.transport{gap:6px}button{font-size:11px;padding:9px}#soundCanvas{height:230px}.canvas-head,.canvas-foot{font-size:8px}.readings{gap:5px}.reading{padding:6px;font-size:9px}.reading strong{font-size:10px}}
|
| 2 |
+
|
| 3 |
+
.recorded-clips{max-width:1200px;margin:0 auto;padding:0 32px 44px;font-size:13px;line-height:2}.recorded-clips h2{font-size:25px;margin-bottom:4px}.recorded-clips p{color:#66756c}.recorded-clips a{color:#176b59;white-space:nowrap}
|
| 4 |
+
@media(max-width:600px){.recorded-clips{padding:0 20px 30px}}
|
experiments/sound.html
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 6 |
+
<meta name="description" content="Play real Flint expert routing and Qwen recurrent head measurements as deterministic music. An alphaBell Model Observatory experiment.">
|
| 7 |
+
<meta property="og:title" content="What does a model sound like? | alphaBell"><meta property="og:description" content="Play real routing and recurrent-head measurements as deterministic music, with synchronized visuals and WAV export."><meta property="og:image" content="https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/main/experiments/assets/05-music.png"><meta name="twitter:card" content="summary_large_image">
|
| 8 |
+
<meta name="theme-color" content="#f1f5e8">
|
| 9 |
+
<title>What does a model sound like? | alphaBell</title>
|
| 10 |
+
<link rel="icon" href="../favicon.svg" type="image/svg+xml">
|
| 11 |
+
<link rel="stylesheet" href="sound.css">
|
| 12 |
+
<script src="../trace-validation.js" defer></script>
|
| 13 |
+
<script src="sound.js" defer></script>
|
| 14 |
+
</head>
|
| 15 |
+
<body>
|
| 16 |
+
<header><a class="brand" href="../">α <span>alphaBell</span></a><a href="./">All experiments ↗</a></header>
|
| 17 |
+
<main>
|
| 18 |
+
<div class="intro"><p class="eyebrow">MODEL OBSERVATORY / EXPERIMENT 05</p><h1>What does a<br>model sound like?</h1><p class="lede">Turn measured expert routing and recurrent head activity into a small, strange piece of music. Same capture. Same mapping. Same notes.</p><p class="honest">This is a musical mapping of real numbers, not a recording of a model's voice or thoughts.</p></div>
|
| 19 |
+
<section class="studio" aria-label="Capture sonification player">
|
| 20 |
+
<div class="controls">
|
| 21 |
+
<label>Capture<select id="soundModel"><option value="flint">Flint · Q8_0 · 59 tokens</option><option value="qwen">Qwen Open · Q8_0 · 44 tokens</option></select></label>
|
| 22 |
+
<label>Decoder block<select id="soundLayer" disabled></select></label>
|
| 23 |
+
<label>Token duration<select id="soundSpeed"><option value="0.2">200 ms · fast</option><option value="0.35" selected>350 ms · medium</option><option value="0.6">600 ms · slow</option><option value="0.9">900 ms · very slow</option></select></label>
|
| 24 |
+
<label>Volume <output id="volumeLabel">35%</output><input id="soundVolume" type="range" min="0" max="100" value="35" aria-label="Volume"></label>
|
| 25 |
+
</div>
|
| 26 |
+
<div id="soundStatus" class="status" role="status" aria-live="polite">Loading the measured capture…</div>
|
| 27 |
+
<button id="soundRetry" class="retry" hidden>Retry capture download</button>
|
| 28 |
+
<div class="visual"><div class="canvas-head"><span id="activityTitle">Measured activity</span><span id="frameCount">Token 0 / 0</span></div><canvas id="soundCanvas" aria-label="Measured activity for the selected output token" role="img"></canvas><div class="canvas-foot"><span id="gridLegend">Every cell is one measured unit</span><span>low <i></i> high</span></div></div>
|
| 29 |
+
<div class="transport"><button id="soundPlay" class="primary" disabled>▶ Play capture</button><button id="soundStop" disabled>Stop</button><button id="soundDownload" disabled>Download WAV ↓</button></div>
|
| 30 |
+
<label class="scrubber-label" for="soundScrubber">Output token <span id="playTime">0:00 / 0:00</span></label><input id="soundScrubber" type="range" min="0" max="0" value="0" disabled aria-label="Select output token">
|
| 31 |
+
<div class="token-now"><span>OUTPUT TOKEN</span><strong id="currentToken">…</strong></div>
|
| 32 |
+
<p class="prompt"><strong>Recorded prompt</strong><br>Explain why the sky is blue in two sentences.</p>
|
| 33 |
+
<div id="voiceReadings" class="readings" aria-label="Current sounding measurements"></div>
|
| 34 |
+
</section>
|
| 35 |
+
<section class="notes" aria-labelledby="mappingTitle"><div><p class="eyebrow">A COMPOSITION WITH A RECEIPT</p><h2 id="mappingTitle">The numbers choose the notes.</h2><p>These are the same real captures available in Model Observatory. No new inference runs here. Switching blocks changes which recorded measurements you hear.</p><a href="../">Inspect the original topology ↗</a></div><div class="mapping"><h3>Flint: six routed experts</h3><p>Each selected expert becomes a sine tone. Its expert ID chooses a note in a repeating, three-octave C pentatonic scale. Its routing weight sets amplitude using 0.11 × √weight. The shared expert and dense blocks are excluded.</p><h3>Qwen Open: recurrent head output</h3><p>The six recurrent value heads with the largest output RMS at each token become triangle tones. Head ID chooses a note on the same scale, one octave higher. Amplitude is 0.08 × RMS / the largest head RMS across the chosen block's capture. All 48 heads remain visible.</p><h3>How to read the comparison</h3><p>Both use a fixed token clock selected above, not the original inference timing. Pitch repeats every 15 IDs, so different IDs can share a note. Colors show routing weight for Flint and normalized head RMS for Qwen. The two mappings have different units and are not a model-quality comparison.</p><p class="fine">Mono, 22,050 Hz. Identical settings produce identical samples. A short attack and release soften note boundaries. WAV export includes the selected volume and stays on your device.</p></div></section>
|
| 36 |
+
</main>
|
| 37 |
+
<section class="recorded-clips" aria-label="Download recorded music clips"><h2>Take a clip with you.</h2><p>The same mapping, 350 ms per token and 35% volume.</p><a href="assets/flint-music.webm" download>Flint video ↓</a> · <a href="assets/flint-music.wav" download>Flint WAV ↓</a> · <a href="assets/qwen-open-music.webm" download>Qwen Open video ↓</a> · <a href="assets/qwen-open-music.wav" download>Qwen Open WAV ↓</a></section>
|
| 38 |
+
<footer><span>Built by alphaBell</span><a href="../traces/index.json">Capture manifests and SHA-256 ↗</a><a href="../TRACE-FORMAT.md">Trace format ↗</a></footer>
|
| 39 |
+
</body>
|
| 40 |
+
</html>
|
experiments/sound.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* alphaBell: deterministic sonification of measured capture data. */
|
| 2 |
+
(function (root) {
|
| 3 |
+
'use strict';
|
| 4 |
+
const PENTATONIC = [0, 2, 4, 7, 9];
|
| 5 |
+
const SAMPLE_RATE = 22050;
|
| 6 |
+
function frequency(id, model) {
|
| 7 |
+
const note = id % 15;
|
| 8 |
+
const midi = (model === 'qwen' ? 60 : 48) + Math.floor(note / 5) * 12 + PENTATONIC[note % 5];
|
| 9 |
+
return 440 * 2 ** ((midi - 69) / 12);
|
| 10 |
+
}
|
| 11 |
+
function buildScore(trace, layer, seconds = .35) {
|
| 12 |
+
if (!trace || !Array.isArray(trace.frames) || !trace.frames.length || trace.frames.length > 256) throw new Error('A complete capture is required.');
|
| 13 |
+
const model = trace.model?.id || (trace.schema === 'flint.trace.v1' ? 'flint' : null);
|
| 14 |
+
if (!['flint', 'qwen'].includes(model) || !Number.isInteger(layer) || layer < 0 || !Number.isFinite(seconds) || seconds < .15 || seconds > 1 || trace.frames.length * seconds > 90) throw new Error('Unsupported score settings.');
|
| 15 |
+
const frames = trace.frames.map(frame => {
|
| 16 |
+
const block = frame.layers?.[layer];
|
| 17 |
+
if (!block) throw new Error('The chosen block is missing.');
|
| 18 |
+
if (model === 'flint') {
|
| 19 |
+
if (!Array.isArray(block.experts) || block.experts.length !== 6 || !Array.isArray(block.weights) || block.weights.length !== 6 || new Set(block.experts).size !== 6 || !block.experts.every(id => Number.isInteger(id) && id >= 0 && id < 256) || !block.weights.every(v => Number.isFinite(v) && v >= 0 && v <= 1)) throw new Error('Choose a Flint block with six measured routed experts.');
|
| 20 |
+
} else if (!Array.isArray(block.retention) || !Array.isArray(block.heads) || block.heads.length !== 48 || !block.heads.every(v => Number.isFinite(v) && v >= 0)) throw new Error('Choose a Qwen recurrent block with 48 measured value heads.');
|
| 21 |
+
return block;
|
| 22 |
+
});
|
| 23 |
+
let maximum = 0;
|
| 24 |
+
if (model === 'qwen') for (const block of frames) for (const value of block.heads) maximum = Math.max(maximum, value);
|
| 25 |
+
const events = frames.map((block, index) => {
|
| 26 |
+
let voices;
|
| 27 |
+
if (model === 'flint') voices = block.experts.map((id, rank) => ({ id, value: block.weights[rank], normalized: block.weights[rank], amplitude: .11 * Math.sqrt(block.weights[rank]), frequency: frequency(id, model), waveform: 'sine' }));
|
| 28 |
+
else voices = block.heads.map((value, id) => ({ id, value, normalized: maximum ? value / maximum : 0 })).sort((a, b) => b.value - a.value || a.id - b.id).slice(0, 6).map(voice => ({ ...voice, amplitude: .08 * voice.normalized, frequency: frequency(voice.id, model), waveform: 'triangle' }));
|
| 29 |
+
return { index, text: trace.frames[index].text, start: index * seconds, duration: seconds, voices };
|
| 30 |
+
});
|
| 31 |
+
return { model, layer, seconds, maximum, duration: events.length * seconds, sampleRate: SAMPLE_RATE, events };
|
| 32 |
+
}
|
| 33 |
+
function renderPCM(score) {
|
| 34 |
+
if (!score || !Number.isFinite(score.duration) || score.duration <= 0 || score.duration > 90 || score.sampleRate !== SAMPLE_RATE || !Array.isArray(score.events)) throw new Error('Unsupported audio duration or sample rate.');
|
| 35 |
+
const pcm = new Float32Array(Math.ceil(score.duration * SAMPLE_RATE));
|
| 36 |
+
for (const event of score.events) {
|
| 37 |
+
const start = Math.round(event.start * SAMPLE_RATE), end = Math.min(pcm.length, Math.round((event.start + event.duration) * SAMPLE_RATE));
|
| 38 |
+
const length = end - start, attack = Math.max(1, Math.min(Math.round(.012 * SAMPLE_RATE), length / 4)), release = Math.max(1, Math.min(Math.round(.05 * SAMPLE_RATE), length / 3));
|
| 39 |
+
for (let i = 0; i < length; i++) {
|
| 40 |
+
const envelope = Math.min(1, i / attack, (length - 1 - i) / release);
|
| 41 |
+
let value = 0;
|
| 42 |
+
for (const voice of event.voices) {
|
| 43 |
+
const phase = 2 * Math.PI * voice.frequency * i / SAMPLE_RATE;
|
| 44 |
+
const wave = voice.waveform === 'triangle' ? 2 / Math.PI * Math.asin(Math.sin(phase)) : Math.sin(phase);
|
| 45 |
+
value += wave * voice.amplitude;
|
| 46 |
+
}
|
| 47 |
+
pcm[start + i] = Math.max(-1, Math.min(1, value * envelope));
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
return pcm;
|
| 51 |
+
}
|
| 52 |
+
function encodeWAV(pcm, volume = .35) {
|
| 53 |
+
if (!(pcm instanceof Float32Array) || pcm.length > SAMPLE_RATE * 90 || !Number.isFinite(volume) || volume < 0 || volume > 1) throw new Error('Unsupported WAV samples or volume.');
|
| 54 |
+
const buffer = new ArrayBuffer(44 + pcm.length * 2), view = new DataView(buffer);
|
| 55 |
+
const str = (at, value) => { for (let i = 0; i < value.length; i++) view.setUint8(at + i, value.charCodeAt(i)); };
|
| 56 |
+
str(0, 'RIFF'); view.setUint32(4, buffer.byteLength - 8, true); str(8, 'WAVE'); str(12, 'fmt '); view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, 1, true); view.setUint32(24, SAMPLE_RATE, true); view.setUint32(28, SAMPLE_RATE * 2, true); view.setUint16(32, 2, true); view.setUint16(34, 16, true); str(36, 'data'); view.setUint32(40, pcm.length * 2, true);
|
| 57 |
+
for (let i = 0; i < pcm.length; i++) { const value = Math.max(-1, Math.min(1, pcm[i] * volume)); view.setInt16(44 + i * 2, Math.round(value * (value < 0 ? 32768 : 32767)), true); }
|
| 58 |
+
return buffer;
|
| 59 |
+
}
|
| 60 |
+
const API = { frequency, buildScore, renderPCM, encodeWAV, SAMPLE_RATE };
|
| 61 |
+
if (typeof module !== 'undefined' && module.exports) module.exports = API;
|
| 62 |
+
root.ObservatorySound = API;
|
| 63 |
+
if (typeof document === 'undefined') return;
|
| 64 |
+
|
| 65 |
+
const $ = id => document.getElementById(id);
|
| 66 |
+
const state = { trace: null, score: null, pcm: null, buffer: null, context: null, gain: null, source: null, playing: false, starting: false, animation: 0, offset: 0, started: 0, frame: 0, request: 0, cache: new Map(), models: null, manifest: null };
|
| 67 |
+
const formatTime = seconds => `${Math.floor(seconds / 60)}:${String(Math.floor(seconds % 60)).padStart(2, '0')}`;
|
| 68 |
+
function status(text, error = false) { $('soundStatus').textContent = text; $('soundStatus').classList.toggle('error', error); }
|
| 69 |
+
function buttons(ready) { for (const id of ['soundPlay', 'soundStop', 'soundDownload', 'soundScrubber', 'soundLayer']) $(id).disabled = !ready; }
|
| 70 |
+
function position() { return state.playing ? Math.min(state.score.duration, state.offset + state.context.currentTime - state.started) : state.offset; }
|
| 71 |
+
function pause(reset = false) {
|
| 72 |
+
state.animation++;
|
| 73 |
+
if (state.playing) state.offset = position();
|
| 74 |
+
state.playing = false;
|
| 75 |
+
if (state.source) { state.source.onended = null; state.source.stop(); state.source.disconnect(); state.source = null; }
|
| 76 |
+
if (reset) { state.offset = 0; state.frame = 0; }
|
| 77 |
+
$('soundPlay').textContent = '▶ Play capture';
|
| 78 |
+
if (state.score) paint();
|
| 79 |
+
}
|
| 80 |
+
function remake() {
|
| 81 |
+
pause(true);
|
| 82 |
+
if (!state.trace) return;
|
| 83 |
+
state.score = buildScore(state.trace, Number($('soundLayer').value), Number($('soundSpeed').value));
|
| 84 |
+
state.pcm = renderPCM(state.score); state.buffer = null;
|
| 85 |
+
$('soundScrubber').max = state.score.events.length - 1;
|
| 86 |
+
buttons(true);
|
| 87 |
+
status(`Verified Q8_0 capture · ${state.score.events.length} measured output tokens · ${state.score.duration.toFixed(1)} seconds of mapped audio. Press Play to enable sound.`);
|
| 88 |
+
paint();
|
| 89 |
+
}
|
| 90 |
+
async function play() {
|
| 91 |
+
if (!state.score || state.starting) return;
|
| 92 |
+
if (state.playing) { pause(); return; }
|
| 93 |
+
state.starting = true;
|
| 94 |
+
const score = state.score;
|
| 95 |
+
try {
|
| 96 |
+
const Audio = root.AudioContext || root.webkitAudioContext;
|
| 97 |
+
if (!Audio) throw new Error('Audio playback is unavailable in this browser. You can still download the WAV.');
|
| 98 |
+
if (!state.context) { state.context = new Audio(); state.gain = state.context.createGain(); state.gain.connect(state.context.destination); }
|
| 99 |
+
await state.context.resume();
|
| 100 |
+
if (state.score !== score || document.hidden) return;
|
| 101 |
+
if (state.offset >= state.score.duration) state.offset = 0;
|
| 102 |
+
if (!state.buffer) { state.buffer = state.context.createBuffer(1, state.pcm.length, SAMPLE_RATE); state.buffer.copyToChannel(state.pcm, 0); }
|
| 103 |
+
state.gain.gain.setValueAtTime(Number($('soundVolume').value) / 100, state.context.currentTime);
|
| 104 |
+
const source = state.context.createBufferSource(); source.buffer = state.buffer; source.connect(state.gain); state.source = source;
|
| 105 |
+
state.started = state.context.currentTime; state.playing = true;
|
| 106 |
+
source.onended = () => { if (state.source === source) { state.offset = state.score.duration; state.playing = false; state.source = null; source.disconnect(); $('soundPlay').textContent = '▶ Replay capture'; paint(); } };
|
| 107 |
+
source.start(0, state.offset); $('soundPlay').textContent = 'Ⅱ Pause capture';
|
| 108 |
+
const animation = ++state.animation;
|
| 109 |
+
const tick = () => { if (!state.playing || state.animation !== animation) return; paint(); requestAnimationFrame(tick); }; requestAnimationFrame(tick);
|
| 110 |
+
} catch (error) { pause(); status(error.message, true); } finally { state.starting = false; }
|
| 111 |
+
}
|
| 112 |
+
function paint() {
|
| 113 |
+
if (!state.score) return;
|
| 114 |
+
const time = position(), index = Math.min(state.score.events.length - 1, Math.floor(time / state.score.seconds)); state.frame = index;
|
| 115 |
+
const event = state.score.events[index], block = state.trace.frames[index].layers[state.score.layer], flint = state.score.model === 'flint';
|
| 116 |
+
$('frameCount').textContent = `Token ${index + 1} / ${state.score.events.length}`;
|
| 117 |
+
$('playTime').textContent = `${formatTime(time)} / ${formatTime(state.score.duration)}`;
|
| 118 |
+
$('soundScrubber').value = index;
|
| 119 |
+
$('currentToken').textContent = event.text.replace(/\n/g, '↵') || '∅';
|
| 120 |
+
$('activityTitle').textContent = `${flint ? 'EXPERT ROUTING' : 'RECURRENT HEAD OUTPUT'} · BLOCK ${state.score.layer}`;
|
| 121 |
+
$('gridLegend').textContent = flint ? '256 experts · 6 selected' : '48 heads · top 6 audible';
|
| 122 |
+
const canvas = $('soundCanvas'), width = canvas.clientWidth, height = canvas.clientHeight, ratio = Math.min(2, root.devicePixelRatio || 1);
|
| 123 |
+
if (canvas.width !== Math.round(width * ratio) || canvas.height !== Math.round(height * ratio)) { canvas.width = Math.round(width * ratio); canvas.height = Math.round(height * ratio); }
|
| 124 |
+
const context = canvas.getContext('2d'); context.setTransform(ratio, 0, 0, ratio, 0, 0); context.clearRect(0, 0, width, height);
|
| 125 |
+
const columns = flint ? 16 : 8, rows = flint ? 16 : 6, gap = flint ? 3 : 6, cw = (width - gap * (columns - 1)) / columns, ch = (height - gap * (rows - 1)) / rows;
|
| 126 |
+
for (let id = 0; id < columns * rows; id++) {
|
| 127 |
+
const rank = flint ? block.experts.indexOf(id) : -1;
|
| 128 |
+
const value = flint ? (rank < 0 ? 0 : block.weights[rank]) : (state.score.maximum ? block.heads[id] / state.score.maximum : 0);
|
| 129 |
+
const shade = Math.pow(Math.min(1, value * (flint ? 3 : 1)), .6);
|
| 130 |
+
const a = [220,230,208], b = [36,102,68]; context.fillStyle = `rgb(${a.map((v, i) => Math.round(v + (b[i] - v) * shade)).join(',')})`;
|
| 131 |
+
const x = (id % columns) * (cw + gap), y = Math.floor(id / columns) * (ch + gap); context.fillRect(x, y, cw, ch);
|
| 132 |
+
if (!flint) { context.fillStyle = shade > .5 ? '#fffef8' : '#526849'; context.font = '10px ui-monospace,monospace'; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillText(String(id), x + cw / 2, y + ch / 2); }
|
| 133 |
+
if (event.voices.some(voice => voice.id === id)) { context.strokeStyle = '#1d4e35'; context.lineWidth = 1.5; context.strokeRect(x + .7, y + .7, cw - 1.4, ch - 1.4); }
|
| 134 |
+
}
|
| 135 |
+
if ($('voiceReadings').dataset.frame !== `${state.score.model}:${state.score.layer}:${index}`) {
|
| 136 |
+
$('voiceReadings').dataset.frame = `${state.score.model}:${state.score.layer}:${index}`;
|
| 137 |
+
$('voiceReadings').replaceChildren(...event.voices.map(voice => { const div = document.createElement('div'); div.className = 'reading'; const label = document.createElement('strong'); label.textContent = `${flint ? 'Expert' : 'Head'} ${voice.id} · ${voice.frequency.toFixed(0)} Hz`; div.append(label, document.createTextNode(flint ? `${(100 * voice.value).toFixed(2)}% weight` : `${voice.value.toPrecision(4)} RMS`)); return div; }));
|
| 138 |
+
}
|
| 139 |
+
}
|
| 140 |
+
async function sha256(bytes) { return Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', bytes)), b => b.toString(16).padStart(2, '0')).join(''); }
|
| 141 |
+
async function load() {
|
| 142 |
+
const request = ++state.request, model = $('soundModel').value;
|
| 143 |
+
pause(true); buttons(false); state.score = null; state.trace = null; state.pcm = null; state.buffer = null; $('soundRetry').hidden = true;
|
| 144 |
+
$('currentToken').textContent = '…'; $('voiceReadings').replaceChildren(); $('voiceReadings').dataset.frame = '';
|
| 145 |
+
status(`Loading ${model === 'flint' ? 'Flint' : 'Qwen'} measured capture and verifying both SHA-256 checksums…`);
|
| 146 |
+
try {
|
| 147 |
+
if (!state.models || !state.manifest) {
|
| 148 |
+
const [modelsResponse, manifestResponse] = await Promise.all([fetch('../models.json'), fetch('../traces/index.json')]);
|
| 149 |
+
if (!modelsResponse.ok || !manifestResponse.ok) throw new Error('The capture registry could not be loaded.');
|
| 150 |
+
[state.models, state.manifest] = await Promise.all([modelsResponse.json(), manifestResponse.json()]);
|
| 151 |
+
}
|
| 152 |
+
let trace = state.cache.get(model);
|
| 153 |
+
if (!trace) {
|
| 154 |
+
const row = state.manifest.find(item => item.model === model);
|
| 155 |
+
if (!row) throw new Error('No capture is available for this model.');
|
| 156 |
+
const response = await fetch('../' + row.file); if (!response.ok) throw new Error('The capture download failed.');
|
| 157 |
+
const compressed = await response.arrayBuffer(); if (await sha256(compressed) !== row.gzip_sha256) throw new Error('Compressed capture checksum did not match. Retry the download.');
|
| 158 |
+
if (typeof DecompressionStream === 'undefined') throw new Error('This browser needs gzip stream support. Open in a current Chrome, Firefox or Safari.');
|
| 159 |
+
const plain = await new Response(new Blob([compressed]).stream().pipeThrough(new DecompressionStream('gzip'))).arrayBuffer();
|
| 160 |
+
if (await sha256(plain) !== row.sha256) throw new Error('Decoded capture checksum did not match. Retry the download.');
|
| 161 |
+
trace = JSON.parse(new TextDecoder().decode(plain));
|
| 162 |
+
root.validateObservatoryTrace(trace, state.models);
|
| 163 |
+
state.cache.set(model, trace);
|
| 164 |
+
}
|
| 165 |
+
if (request !== state.request) return;
|
| 166 |
+
state.trace = trace;
|
| 167 |
+
const config = state.models.find(item => item.id === model).architecture;
|
| 168 |
+
const eligible = config.layer_types.map((type, index) => ({ type, index })).filter(({ type, index }) => model === 'flint' ? index >= (config.first_k_dense_replace || 0) && !(config.mlp_only_layers || []).includes(index) : type === 'linear_attention');
|
| 169 |
+
$('soundLayer').replaceChildren(...eligible.map(({ index }) => { const option = document.createElement('option'); option.value = index; option.textContent = `Block ${index} · ${model === 'flint' ? 'MoE' : 'recurrent'}`; return option; }));
|
| 170 |
+
$('soundLayer').value = model === 'flint' ? '12' : '0'; remake();
|
| 171 |
+
} catch (error) { if (request === state.request) { status(error.message + ' Your browser has not started audio.', true); $('soundRetry').hidden = false; } }
|
| 172 |
+
}
|
| 173 |
+
$('soundModel').addEventListener('change', load);
|
| 174 |
+
$('soundRetry').addEventListener('click', load);
|
| 175 |
+
$('soundLayer').addEventListener('change', remake);
|
| 176 |
+
$('soundSpeed').addEventListener('change', remake);
|
| 177 |
+
$('soundPlay').addEventListener('click', play);
|
| 178 |
+
$('soundStop').addEventListener('click', () => pause(true));
|
| 179 |
+
$('soundVolume').addEventListener('input', () => { const volume = Number($('soundVolume').value) / 100; $('volumeLabel').textContent = Math.round(volume * 100) + '%'; if (state.gain) state.gain.gain.setTargetAtTime(volume, state.context.currentTime, .02); });
|
| 180 |
+
$('soundScrubber').addEventListener('input', () => { const wasPlaying = state.playing, frame = Number($('soundScrubber').value); pause(); state.offset = frame * state.score.seconds; paint(); if (wasPlaying) play(); });
|
| 181 |
+
$('soundDownload').addEventListener('click', () => { if (!state.pcm) return; const blob = new Blob([encodeWAV(state.pcm, Number($('soundVolume').value) / 100)], { type: 'audio/wav' }); const url = URL.createObjectURL(blob), anchor = document.createElement('a'); anchor.href = url; anchor.download = `alphabell-${state.score.model}-block-${state.score.layer}-${Math.round(state.score.seconds * 1000)}ms.wav`; anchor.click(); setTimeout(() => URL.revokeObjectURL(url), 30000); });
|
| 182 |
+
root.addEventListener('resize', paint);
|
| 183 |
+
document.addEventListener('visibilitychange', () => { if (document.hidden) pause(); });
|
| 184 |
+
root.addEventListener('pagehide', () => { pause(); if (state.context) state.context.close(); });
|
| 185 |
+
load();
|
| 186 |
+
})(typeof globalThis !== 'undefined' ? globalThis : this);
|
experiments/sound.test.cjs
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'use strict';
|
| 2 |
+
const test = require('node:test');
|
| 3 |
+
const assert = require('node:assert/strict');
|
| 4 |
+
const fs = require('node:fs');
|
| 5 |
+
const zlib = require('node:zlib');
|
| 6 |
+
const path = require('node:path');
|
| 7 |
+
const sound = require('./sound.js');
|
| 8 |
+
const traces = Object.fromEntries(['flint','qwen'].map(model => [model, JSON.parse(zlib.gunzipSync(fs.readFileSync(path.join(__dirname, '../traces/' + model + '-sky-q8.json.gz'))))]));
|
| 9 |
+
test('Flint score preserves all selected expert IDs and exact measured weights', () => {
|
| 10 |
+
const score = sound.buildScore(traces.flint, 12, .2);
|
| 11 |
+
assert.equal(score.events.length, 59);
|
| 12 |
+
for (const event of score.events) {
|
| 13 |
+
const block = traces.flint.frames[event.index].layers[12];
|
| 14 |
+
assert.deepEqual(event.voices.map(voice => voice.id), block.experts);
|
| 15 |
+
assert.deepEqual(event.voices.map(voice => voice.value), block.weights);
|
| 16 |
+
event.voices.forEach(voice => assert.equal(voice.amplitude, .11 * Math.sqrt(voice.value)));
|
| 17 |
+
}
|
| 18 |
+
});
|
| 19 |
+
test('Qwen score uses the exact six largest recurrent head RMS values and a capture-wide scale', () => {
|
| 20 |
+
const score = sound.buildScore(traces.qwen, 0, .2);
|
| 21 |
+
const maximum = Math.max(...traces.qwen.frames.flatMap(frame => frame.layers[0].heads));
|
| 22 |
+
assert.equal(score.maximum, maximum);
|
| 23 |
+
for (const event of score.events) {
|
| 24 |
+
const heads = traces.qwen.frames[event.index].layers[0].heads;
|
| 25 |
+
const expected = heads.map((value,id) => ({value,id})).sort((a,b) => b.value-a.value || a.id-b.id).slice(0,6);
|
| 26 |
+
assert.deepEqual(event.voices.map(({value,id}) => ({value,id})), expected);
|
| 27 |
+
event.voices.forEach(voice => { assert.equal(voice.normalized, voice.value / maximum); assert.equal(voice.waveform, 'triangle'); });
|
| 28 |
+
}
|
| 29 |
+
});
|
| 30 |
+
test('Synthesis is deterministic, non-silent, bounded, and aligned with token duration', () => {
|
| 31 |
+
for (const [model, layer] of [['flint',12], ['qwen',0]]) {
|
| 32 |
+
const score = sound.buildScore(traces[model], layer, .2), first = sound.renderPCM(score), second = sound.renderPCM(score);
|
| 33 |
+
assert.deepEqual(first, second);
|
| 34 |
+
assert.equal(first.length, Math.ceil(score.duration * sound.SAMPLE_RATE));
|
| 35 |
+
let peak = 0; for (const value of first) { assert.ok(Number.isFinite(value)); peak = Math.max(peak, Math.abs(value)); }
|
| 36 |
+
assert.ok(peak > .001 && peak < 1);
|
| 37 |
+
assert.equal(first[0], 0); assert.equal(first.at(-1), 0);
|
| 38 |
+
}
|
| 39 |
+
});
|
| 40 |
+
test('WAV encodes actual deterministic signed PCM with the chosen volume', () => {
|
| 41 |
+
const pcm = new Float32Array([0, .5, -.5, 1, -1]), buffer = sound.encodeWAV(pcm, .5), view = new DataView(buffer);
|
| 42 |
+
assert.equal(Buffer.from(buffer).toString('ascii', 0, 4), 'RIFF');
|
| 43 |
+
assert.equal(Buffer.from(buffer).toString('ascii', 8, 12), 'WAVE');
|
| 44 |
+
assert.equal(view.getUint32(24,true), 22050); assert.equal(view.getUint16(22,true), 1); assert.equal(view.getUint16(34,true),16);
|
| 45 |
+
assert.equal(view.getUint32(40,true), 10); assert.equal(buffer.byteLength,54);
|
| 46 |
+
assert.deepEqual(Array.from({length:5}, (_,i) => view.getInt16(44+i*2,true)), [0,8192,-8192,16384,-16384]);
|
| 47 |
+
assert.ok(new Int16Array(sound.encodeWAV(pcm,0),44).every(value => value === 0));
|
| 48 |
+
});
|
| 49 |
+
test('Reject dense Flint, full attention Qwen, excessive duration, and invalid volume', () => {
|
| 50 |
+
assert.throws(() => sound.buildScore(traces.flint,0));
|
| 51 |
+
const full = traces.qwen.architecture.layer_types.indexOf('full_attention');
|
| 52 |
+
assert.throws(() => sound.buildScore(traces.qwen,full));
|
| 53 |
+
assert.throws(() => sound.buildScore(traces.flint,12,2));
|
| 54 |
+
assert.throws(() => sound.renderPCM({duration:91,sampleRate:22050,events:[]}));
|
| 55 |
+
assert.throws(() => sound.encodeWAV(new Float32Array(1),-1));
|
| 56 |
+
});
|
| 57 |
+
test('ID pitch repeats every 15 IDs and Qwen is one octave above Flint', () => {
|
| 58 |
+
assert.equal(sound.frequency(2,'flint'), sound.frequency(17,'flint'));
|
| 59 |
+
assert.equal(sound.frequency(2,'qwen'), sound.frequency(2,'flint')*2);
|
| 60 |
+
});
|
index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Flint & Qwen Model Observatory | alphaBell</title><meta name="description" content="Explore Flint expert routing and Qwen recurrent memory with real Q8 tensor captures. Replay every token, inspect every block and import your own trace."><meta property="og:title" content="Model Observatory by alphaBell"><meta property="og:description" content="Two architectures. Real tensor captures. Every token."><meta property="og:image" content="https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/main/cover.png"><meta name="theme-color" content="#176b59"><link rel="icon" href="favicon.svg"><link rel="stylesheet" href="style.css"></head>
|
| 4 |
<body>
|
| 5 |
<header class="models-header"><div class="header-start"><a class="brand" href="./"><span class="brand-icon">α</span><span>alphaBell<span class="brand-divider">/</span><b>observatory</b></span></a><label class="model-control"><span>MODEL</span><select id="modelSelect" aria-label="Model" disabled><option>Recursive Flint</option></select></label></div><div class="header-end"><span class="engine-status" id="engineStatus"><i></i>Loading capture</span><button class="icon-button" id="aboutButton" title="How to read the model" aria-label="How to read the model">?</button></div></header>
|
| 6 |
-
<section class="overview"><div><div class="eyebrow" id="modelEyebrow">MODEL OBSERVATORY</div><h1>Inside Flint & Qwen.</h1></div><div class="stats" id="modelStats"></div></section>
|
| 7 |
<main>
|
| 8 |
<aside class="input-panel">
|
| 9 |
<section class="prompt-section"><div class="section-heading"><h2>01 <span>Measured capture</span></h2><span class="small-tag" id="quantization">Q8_0</span></div><p id="traceOrigin" class="trace-origin">Opening example capture…</p><label class="sr-only" for="prompt" id="promptLabel">Captured prompt</label><textarea id="prompt" readonly spellcheck="false">Explain why the sky is blue in two sentences.</textarea><p class="capture-settings" id="captureSettings"></p><button id="runButton" class="primary" disabled>Loading capture…</button><p id="runNote" class="run-note" role="status">Structure is ready while the capture loads.</p><details class="capture-help"><summary>Use your own capture</summary><p>Import a compatible Flint or Qwen trace JSON. It stays in your browser. This public viewer replays measured captures; new prompts need a separate capture engine.</p><a href="https://huggingface.co/spaces/alphabell/Model-Observatory/blob/main/TRACE-FORMAT.md" target="_blank" rel="noreferrer">Trace format ↗</a></details></section>
|
|
|
|
| 3 |
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Flint & Qwen Model Observatory | alphaBell</title><meta name="description" content="Explore Flint expert routing and Qwen recurrent memory with real Q8 tensor captures. Replay every token, inspect every block and import your own trace."><meta property="og:title" content="Model Observatory by alphaBell"><meta property="og:description" content="Two architectures. Real tensor captures. Every token."><meta property="og:image" content="https://huggingface.co/spaces/alphabell/Model-Observatory/resolve/main/cover.png"><meta name="theme-color" content="#176b59"><link rel="icon" href="favicon.svg"><link rel="stylesheet" href="style.css"></head>
|
| 4 |
<body>
|
| 5 |
<header class="models-header"><div class="header-start"><a class="brand" href="./"><span class="brand-icon">α</span><span>alphaBell<span class="brand-divider">/</span><b>observatory</b></span></a><label class="model-control"><span>MODEL</span><select id="modelSelect" aria-label="Model" disabled><option>Recursive Flint</option></select></label></div><div class="header-end"><span class="engine-status" id="engineStatus"><i></i>Loading capture</span><button class="icon-button" id="aboutButton" title="How to read the model" aria-label="How to read the model">?</button></div></header>
|
| 6 |
+
<section class="overview"><div><div class="eyebrow" id="modelEyebrow">MODEL OBSERVATORY</div><h1>Inside Flint & Qwen.</h1><a class="experiment-entry" href="experiments/">Try the five experiments ↗</a></div><div class="stats" id="modelStats"></div></section>
|
| 7 |
<main>
|
| 8 |
<aside class="input-panel">
|
| 9 |
<section class="prompt-section"><div class="section-heading"><h2>01 <span>Measured capture</span></h2><span class="small-tag" id="quantization">Q8_0</span></div><p id="traceOrigin" class="trace-origin">Opening example capture…</p><label class="sr-only" for="prompt" id="promptLabel">Captured prompt</label><textarea id="prompt" readonly spellcheck="false">Explain why the sky is blue in two sentences.</textarea><p class="capture-settings" id="captureSettings"></p><button id="runButton" class="primary" disabled>Loading capture…</button><p id="runNote" class="run-note" role="status">Structure is ready while the capture loads.</p><details class="capture-help"><summary>Use your own capture</summary><p>Import a compatible Flint or Qwen trace JSON. It stays in your browser. This public viewer replays measured captures; new prompts need a separate capture engine.</p><a href="https://huggingface.co/spaces/alphabell/Model-Observatory/blob/main/TRACE-FORMAT.md" target="_blank" rel="noreferrer">Trace format ↗</a></details></section>
|
models.json
CHANGED
|
@@ -147,8 +147,8 @@
|
|
| 147 |
},
|
| 148 |
{
|
| 149 |
"id": "qwen",
|
| 150 |
-
"title": "
|
| 151 |
-
"short_name": "Qwen",
|
| 152 |
"family": "Hybrid recurrent transformer",
|
| 153 |
"quantization": "Q8_0",
|
| 154 |
"source_url": "https://huggingface.co/Qwen/Qwen3.8-27B/blob/main/config.json",
|
|
@@ -279,6 +279,7 @@
|
|
| 279 |
"hidden_act": "silu",
|
| 280 |
"num_experts": 0,
|
| 281 |
"first_k_dense_replace": 64
|
| 282 |
-
}
|
|
|
|
| 283 |
}
|
| 284 |
]
|
|
|
|
| 147 |
},
|
| 148 |
{
|
| 149 |
"id": "qwen",
|
| 150 |
+
"title": "alphaBell Qwen Open 27B",
|
| 151 |
+
"short_name": "Qwen Open",
|
| 152 |
"family": "Hybrid recurrent transformer",
|
| 153 |
"quantization": "Q8_0",
|
| 154 |
"source_url": "https://huggingface.co/Qwen/Qwen3.8-27B/blob/main/config.json",
|
|
|
|
| 279 |
"hidden_act": "silu",
|
| 280 |
"num_experts": 0,
|
| 281 |
"first_k_dense_replace": 64
|
| 282 |
+
},
|
| 283 |
+
"checkpoint_origin": "alphaBell fine-tuned Qwen3.8-27B, converted from the lab BF16 checkpoint to GGUF; the public FP8 release is a sibling format."
|
| 284 |
}
|
| 285 |
]
|
package.json
CHANGED
|
@@ -5,5 +5,5 @@
|
|
| 5 |
"description": "Real Flint and Qwen tensor captures, explored token by token",
|
| 6 |
"author": "alphaBell",
|
| 7 |
"license": "MIT",
|
| 8 |
-
"scripts": { "test": "node --test tests/*.test.cjs" }
|
| 9 |
}
|
|
|
|
| 5 |
"description": "Real Flint and Qwen tensor captures, explored token by token",
|
| 6 |
"author": "alphaBell",
|
| 7 |
"license": "MIT",
|
| 8 |
+
"scripts": { "test": "node --test tests/*.test.cjs experiments/*.test.cjs" }
|
| 9 |
}
|
style.css
CHANGED
|
@@ -143,3 +143,5 @@ button.expert-chip{border:0}
|
|
| 143 |
/* Keep all decoder blocks inside their panel at short desktop heights. */
|
| 144 |
#modelCanvas{position:absolute;inset:0}
|
| 145 |
@media(min-width:981px){main{height:max(760px,calc(100vh - 325px));grid-template-rows:minmax(0,1fr)}.input-panel,.model-panel{min-height:0}.model-map{min-height:0}}
|
|
|
|
|
|
|
|
|
| 143 |
/* Keep all decoder blocks inside their panel at short desktop heights. */
|
| 144 |
#modelCanvas{position:absolute;inset:0}
|
| 145 |
@media(min-width:981px){main{height:max(760px,calc(100vh - 325px));grid-template-rows:minmax(0,1fr)}.input-panel,.model-panel{min-height:0}.model-map{min-height:0}}
|
| 146 |
+
|
| 147 |
+
.experiment-entry{display:inline-block;margin-top:9px;font:11px system-ui,sans-serif;color:#176b59;text-underline-offset:4px}
|
tests/experiment-data.test.cjs
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'use strict';
|
| 2 |
+
const test=require('node:test'),assert=require('node:assert/strict'),fs=require('node:fs'),path=require('node:path'),zlib=require('node:zlib'),crypto=require('node:crypto');
|
| 3 |
+
const root=path.resolve(__dirname,'..');
|
| 4 |
+
const data=JSON.parse(fs.readFileSync(path.join(root,'experiments/data/results.json')));
|
| 5 |
+
const summary=JSON.parse(fs.readFileSync(path.join(root,'experiments/data/summary.json')));
|
| 6 |
+
const manifest=JSON.parse(fs.readFileSync(path.join(root,'experiments/data/traces.json')));
|
| 7 |
+
const registry=JSON.parse(fs.readFileSync(path.join(root,'models.json')));
|
| 8 |
+
const {validateObservatoryTrace}=require('../trace-validation.js');
|
| 9 |
+
const sha=b=>crypto.createHash('sha256').update(b).digest('hex');
|
| 10 |
+
test('all 24 new topology captures retain valid measured frames and both checksums',()=>{
|
| 11 |
+
assert.equal(manifest.length,24);let frames=0;
|
| 12 |
+
for(const row of manifest){
|
| 13 |
+
const compressed=fs.readFileSync(path.join(root,row.file)),plain=zlib.gunzipSync(compressed),trace=JSON.parse(plain);
|
| 14 |
+
assert.equal(sha(compressed),row.gzip_sha256);assert.equal(sha(plain),row.sha256);assert.equal(trace.id,row.id);assert.equal(trace.result.reason,'eos');assert.equal(trace.frames.length,row.tokens);
|
| 15 |
+
validateObservatoryTrace(trace,registry);frames+=trace.frames.length;
|
| 16 |
+
}
|
| 17 |
+
assert.equal(frames,80);assert.equal(summary.full_capture_frames,frames);
|
| 18 |
+
});
|
| 19 |
+
test('prompt pairs change exactly one word and compare the first prediction',()=>{
|
| 20 |
+
const cases=data.experiments.reroute.cases;assert.equal(cases.length,6);
|
| 21 |
+
for(const item of cases){
|
| 22 |
+
const a=item.prompts[0].text.split(/\s+/),b=item.prompts[1].text.split(/\s+/);assert.equal(a.length,b.length);assert.equal(a.filter((v,i)=>v!==b[i]).length,1);
|
| 23 |
+
assert.equal(item.comparison.prompt_words_changed,1);assert.deepEqual(item.comparison.prompt_token_counts,[item.conditions[0].metrics.prompt_tokens,item.conditions[1].metrics.prompt_tokens]);
|
| 24 |
+
if(item.id.endsWith('flint')){
|
| 25 |
+
assert.equal(item.comparison.routed_blocks_compared,47);assert.ok(item.comparison.blocks_with_different_expert_sets>0);
|
| 26 |
+
for(const condition of item.conditions){assert.equal(condition.routing.layer,item.comparison.displayed_zero_based_block);assert.equal(condition.routing.tokens[0].experts.length,6);}
|
| 27 |
+
}else for(const condition of item.conditions)assert.equal(condition.routing,undefined);
|
| 28 |
+
}
|
| 29 |
+
});
|
| 30 |
+
test('every blind pair uses one matched Q4_1 and Q8_0 checkpoint and keeps identical answers',()=>{
|
| 31 |
+
const pairs=data.experiments.quant.cases;assert.equal(pairs.length,12);
|
| 32 |
+
assert.equal(pairs.filter(x=>x.conditions[0].output===x.conditions[1].output).length,11);
|
| 33 |
+
for(const item of pairs){assert.deepEqual(item.conditions.map(x=>x.quant).sort(),['Q4_1','Q8_0']);for(const c of item.conditions){assert.equal(c.metrics.complete,true);assert.equal(c.metrics.termination,'eos');assert.ok(c.metrics.process_peak_rss_kib>0);}}
|
| 34 |
+
const arithmetic=pairs.find(x=>x.id==='flint-arithmetic');assert.equal(17*23-19,372);
|
| 35 |
+
for(const c of arithmetic.conditions){assert.notEqual(Number(c.output),17*23-19);assert.equal(c.metrics.objective_check_passed,false);}
|
| 36 |
+
});
|
| 37 |
+
test('every saved intervention frame follows its exact requested scale',()=>{
|
| 38 |
+
let altered=0,unchanged=0,total=0;
|
| 39 |
+
for(const item of data.experiments.surgery.cases){
|
| 40 |
+
assert.equal(item.conditions.length,3);const base=item.conditions[0];
|
| 41 |
+
assert.deepEqual(item.conditions.map(c=>c.settings.intervention_scale),[1,.5,0]);
|
| 42 |
+
for(const c of item.conditions){
|
| 43 |
+
assert.equal(c.metrics.termination,'eos');
|
| 44 |
+
for(const f of c.measurements){
|
| 45 |
+
const row=f.layers.find(x=>x.layer===12&&x.tensor==='ffn_out');assert.ok(row);const expected=row.before_rms*c.settings.intervention_scale;
|
| 46 |
+
assert.ok(Math.abs(row.after_rms-expected)<=Math.max(1e-10,Math.abs(expected)*1e-7));total++;
|
| 47 |
+
}
|
| 48 |
+
const layers=c.measurements[0].layers,original=base.measurements[0].layers;
|
| 49 |
+
const rms=(rows,layer,tensor)=>rows.find(x=>x.layer===layer&&x.tensor===tensor).after_rms;
|
| 50 |
+
assert.equal(rms(layers,11,'ffn_out'),rms(original,11,'ffn_out'));unchanged++;
|
| 51 |
+
if(c.settings.intervention_scale!==1){assert.notEqual(rms(layers,13,'l_out'),rms(original,13,'l_out'));altered++;}
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
assert.equal(total,132);assert.equal(unchanged,18);assert.equal(altered,12);
|
| 55 |
+
for(const id of ['baseline','half','zero'])assert.equal(data.experiments.surgery.cases.filter(x=>x.conditions.find(c=>c.id===id).check.passed).length,4);
|
| 56 |
+
});
|
| 57 |
+
test('memory results separately retain original-fact and correction tasks',()=>{
|
| 58 |
+
const cases=data.experiments.memory.cases;assert.equal(cases.length,6);assert.equal(cases.filter(x=>x.kind==='current').length,3);assert.equal(cases.filter(x=>x.kind==='original').length,3);
|
| 59 |
+
let count=0;for(const item of cases){assert.ok(item.choices.includes(item.ground_truth));for(const c of item.conditions){assert.equal(c.output.trim(),item.ground_truth);assert.equal(c.metrics.exact_answer_passed,true);assert.ok(c.metrics.prompt_tokens<=144);count++;}}
|
| 60 |
+
assert.equal(count,12);assert.equal(summary.memory_answers_passed,count);
|
| 61 |
+
});
|
| 62 |
+
test('checkpoint and assembled source provenance agree with the reproduction manifests',()=>{
|
| 63 |
+
const checkpoints=JSON.parse(fs.readFileSync(path.join(root,'experiments/reproduce/checkpoints.json')));
|
| 64 |
+
for(const c of data.provenance.checkpoints){const model=checkpoints[c.model][c.quantization];assert.equal(c.file_bytes,model.bytes);assert.equal(c.sha256,model.sha256);}
|
| 65 |
+
assert.equal(data.provenance.source_tree,'83e9d4c276f84753cce063502a2a9e2325f15ce3');
|
| 66 |
+
assert.equal(sha(fs.readFileSync(path.join(root,'experiments/reproduce/llama-flint-runtime.patch'))),data.provenance.source_patch_sha256);
|
| 67 |
+
assert.equal(registry.find(x=>x.id==='qwen').title,'alphaBell Qwen Open 27B');
|
| 68 |
+
});
|
| 69 |
+
test('downloadable clips have verified media headers and WAVs exactly matching the public mapping',()=>{
|
| 70 |
+
const media=JSON.parse(fs.readFileSync(path.join(root,'experiments/assets/media.json')));
|
| 71 |
+
const sound=require('../experiments/sound.js'),original=JSON.parse(fs.readFileSync(path.join(root,'traces/index.json')));
|
| 72 |
+
for(const entry of media.files){const bytes=fs.readFileSync(path.join(root,'experiments/assets',entry.file));assert.equal(bytes.length,entry.bytes);assert.equal(sha(bytes),entry.sha256);assert.equal(bytes.subarray(0,4).toString('hex'),entry.file.endsWith('.wav')?'52494646':'1a45dfa3');}
|
| 73 |
+
for(const model of ['flint','qwen']){
|
| 74 |
+
const row=original.find(x=>x.model===model),trace=JSON.parse(zlib.gunzipSync(fs.readFileSync(path.join(root,row.file))));
|
| 75 |
+
const expected=Buffer.from(sound.encodeWAV(sound.renderPCM(sound.buildScore(trace,model==='flint'?12:0,.35)),.35));
|
| 76 |
+
assert.deepEqual(fs.readFileSync(path.join(root,'experiments/assets',model==='flint'?'flint-music.wav':'qwen-open-music.wav')),expected);
|
| 77 |
+
}
|
| 78 |
+
});
|