Support official semantic llama.cpp releases and document prefill comparison

This commit is contained in:
Mikei386
2026-09-28 22:24:44 +02:00
parent 6255f0b8ea
commit 682211e6a9
5 changed files with 30 additions and 5 deletions
+16
View File
@@ -15,3 +15,19 @@ Deck-Varianten mit identischem Build, gespeichertem Medium-Profil (160000 Kontex
Ursache: Die frühere Reserve max(1024 MiB, 5 Prozent) verhinderte volle GPU-Belegung knapp und reduzierte das Limit auf 65. Die Modellmetadaten enthalten 65 Blöcke einschließlich eines MTP-Blocks. Der geprüfte llama-model.cpp-Code legt bei begrenztem Offloading frühe Modellblöcke auf die CPU; die Ausgabeschicht liegt am Ende der GPU-Auswahl. Die erste Vermutung einer CPU-Ausgabeschicht war daher falsch. Mit max(512 MiB, 2,5 Prozent) besteht die Prognose einschließlich MTP-Kontext und alle Schichten passen. Das RAM-Limit und die übrigen Startparameter bleiben unverändert.
Kurzer Einzelnutzer-Test, keine Aussage zur Stabilität bei maximal gefülltem Kontext oder paralleler Last. Alter Router: 2 Slots, Vision-Projektor, anderer Build; kein ansonsten identischer Vergleich. Ursachennachweis daher durch die kontrollierten Deck-Varianten. Nach Abschluss: alter Medium-Container gestoppt, Deck läuft mit bisherigem Zwei-Kern-Limit, Testmodell entladen.
## Prefill-Detailprüfung am 28.09.2026
Direkt am nativen `/completion`: synthetischer tokenisierter Text, exakt 512/4096/16384 Eingabe-Tokens, 32 Ausgabe-Tokens, Temperature 0, Seed 1234, cache_prompt=false. Je eine Aufwärmrunde und zwei Messungen pro Länge; alle Messungen cache_n=0. Keine Nutzerinhalte.
| Eingabe | Alter Router, 2 Slots | Deck, 1 Slot | Deck, 2 Slots mit Auto-Offload |
|---|---:|---:|---:|
| 512 | 1076,5 | 1004,6 | 1081,7 |
| 4096 | 1928,2 | 1892,6 | 1350,6 |
| 16384 | 1947,7 | 1928,4 | 1294,0 |
Einheit: mittlere Prefill-Token/s. Deck 1 Slot vs. Alt: bei 4096 rund 1,8 %, bei 16384 rund 1 % weniger Durchsatz. Der große relative Unterschied der früheren 87-Token-Messung skaliert nicht mit der Eingabelänge. Durchschnittliche Zeitdifferenzen hier circa 34/40/84 ms. Ursache der kleinen Restdifferenz nicht isoliert bewiesen; Builds und Slotzahl unterscheiden sich weiterhin.
Wichtiger Störfaktor im kontrollierten Slotversuch: Decks Speicherprognose mit 2 Slots wählte GPU-Layerlimit 65 statt 999. Damit ist diese Variante kein reiner Slotvergleich; zusätzliche CPU-Auslagerung erklärt einen konkreten anderen Ausführungspfad. Gespeichertes Benutzerprofil bleibt bei 1 Slot unverändert. Deck CPU-Budget 2, Threads 6; während erster Messungen und Beginn des zweiten Versuchs nr_throttled=0. CUDA-Architekturen 120;86, Release, GGML_NATIVE=OFF, Flash Attention ON. Aktive Binärdatei 0.5.0-dev, Commit c2a9e16 (registrierter Build b11229).
Offizieller Versionsstand zur Abfrage: regulär v0.5.0 vom 23.09., Nightly b11236 vom 28.09. Die bisherige GUI akzeptierte keine v-Tags und fragte nur zehn jüngste Releases ab. Korrigiert: reguläres Latest explizit zuerst, v-Tags zulässig, Build_IS_DEV passend setzen. Keine neue llama.cpp-Version gebaut oder aktiviert.
+4
View File
@@ -191,3 +191,7 @@ Zusätzliche konservative Speicherplanung: Projektordateigröße plus 2 GiB Arbe
Der LLM-Profileditor prüft das Hugging-Face-Repository der ausgewählten Modelldatei auf separate mmproj-GGUF-Dateien. Eigene Projektoren werden direkt angeboten; nur wenn dort keine gefunden werden, erscheint die externe Repository-Suche. Bei nicht erreichbaren Metadaten bleibt die Suche bis zur erneuten Prüfung geschlossen. Dies erkennt separate Dateien, nicht eingebettete Vision-Gewichte oder garantierte Architekturkompatibilität.
Eine Modellfamilie oder ein vollständiger Hugging-Face-Repository-Link sucht unabhängig vom Textgenerierungs-Tag (`GET /api/v1/catalog/search?kind=chat&purpose=projector&q=…`). Nach Auswahl eines Repositorys zeigt der Editor nur mmproj-GGUF-Dateien mit Größe, Quelle und Lizenz. Herunterladen startet keinen Worker. Fortschritt steht unter Downloads. Danach „Bibliothek aktualisieren“, Projektor auswählen, Gerät wählen und Profil speichern. Vorhandene Profilwerte bleiben beim Suchen erhalten. Externe Projektoren müssen zur Modellarchitektur und Version passen; sie erweitern reine Textarchitekturen nicht um Vision.
### Offizielle llama.cpp-Versionen
Die Update-Abfrage zeigt das aktuelle reguläre Release (`releases/latest`) zuerst und ergänzt die jüngsten Nightlies. Reguläre Tags `vX.Y.Z`, Nightly-Tags `bNNNN` und vollständige Commit-IDs sind zulässig. Reguläre Releases werden mit `LLAMA_BUILD_IS_DEV=OFF`, Nightlies mit `ON` gebaut. Ein reguläres Release ist keine automatische Kompatibilitätszusage für Decks Fit-/MTP-Adapter; Standardwechsel bleiben explizit. Versionsschema: https://github.com/ggml-org/ggml/discussions/1579
+2 -2
View File
@@ -1,7 +1,7 @@
const RuntimeUI=(()=>{
const e=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
async function api(path='',data){const r=await fetch('/api/v1/runtime'+path,data?{method:'POST',headers:{'Content-Type':'application/json','X-Athena-Deck':'1'},body:JSON.stringify(data)}:{});const v=await r.json();if(!r.ok)throw Error(v.error||'Anfrage fehlgeschlagen');return v;}
function html(){return `<div id="runtime-live"><div class="kicker">ATHENA / LLAMA.CPP</div><h1>llama.cpp &amp; Updates</h1><p>Offizielle Versionen getrennt bauen und prüfen. Ein Build lädt kein Modell und ändert keine produktiven Laufzeiten.</p><section class="card"><h2>Voraussetzungen</h2><button id="rt-check">Werkzeuge &amp; GPUs prüfen</button><div id="rt-prereq"></div></section><section class="card"><h2>Installation &amp; Build</h2><form id="rt-build"><label>Release oder vollständiger Commit<input name="revision" required pattern="b[0-9]{3,8}|[a-f0-9]{40}" placeholder="Unten Releases abfragen und auswählen"></label><label>Backend<select name="backend"><option>CUDA</option><option>CPU</option></select></label><label>Parallele Build-Jobs<select name="jobs"><option>1</option><option>2</option></select></label><p>GPU-Architekturen werden automatisch aus beiden Karten übernommen. Begrenzte Build-Last für den Parallelbetrieb.</p><button>llama.cpp herunterladen &amp; bauen</button></form><button id="rt-cancel" class="secondary">Laufenden Build abbrechen</button><p id="rt-message" role="status"></p><div id="rt-job"></div><details><summary>Build-Ausgabe (eigener Build)</summary><pre id="rt-log" style="max-height:320px;overflow:auto;white-space:pre-wrap"></pre></details></section><section class="card"><h2>Versionen &amp; Änderungen</h2><button id="rt-releases">Nach Updates suchen</button><div id="rt-releases-list"></div></section><section class="card"><h2>Geprüfte Builds</h2><div id="rt-builds"></div><button id="rt-rollback" class="secondary">Vorherigen Standard wiederherstellen</button><p>Als Standard auswählen startet keinen Prozess. Bestehende Router bleiben unverändert.</p></section><section class="card"><h2>Automatische Speicheranpassung</h2><p>Kein pauschales Reservefeld: Die Laufzeit soll Modell, gewünschten Kontext, Slots und den aktuell freien Speicher gemeinsam berücksichtigen. Unterstützte Builds verwenden llama.cpp <code>--fit on</code>; der Kontext wird ausdrücklich vorgegeben. Keine absichtlichen OOM-Tests auf den produktiv genutzten GPUs.</p><form id="rt-fit"><label>Vorhandenes Routerprofil<select name="profile" id="rt-profiles"></select></label><label>Gewünschter Gesamtkontext<input name="context" type="number" min="512" max="2097152" value="160000" required></label><label>Slots<input name="slots" type="number" min="1" max="16" value="2" required></label><button>Auto-Einpassung berechnen</button></form><pre id="rt-fit-result" style="white-space:pre-wrap"></pre><p>Ein Build allein kann keine Modell-Layerzahl bestimmen. Die Berechnung verwendet llama-fit-params ohne Gewichtsallokation. Sie ist eine Prognose für das Textmodell und kein Lasttest. Vision-Projektor und MTP sind noch nicht eingerechnet. Ein Modellstart erfolgt nicht.</p></section></div>`;}
function html(){return `<div id="runtime-live"><div class="kicker">ATHENA / LLAMA.CPP</div><h1>llama.cpp &amp; Updates</h1><p>Offizielle Versionen getrennt bauen und prüfen. Ein Build lädt kein Modell und ändert keine produktiven Laufzeiten.</p><section class="card"><h2>Voraussetzungen</h2><button id="rt-check">Werkzeuge &amp; GPUs prüfen</button><div id="rt-prereq"></div></section><section class="card"><h2>Installation &amp; Build</h2><form id="rt-build"><label>Release oder vollständiger Commit<input name="revision" required pattern="b[0-9]{3,8}|v[0-9]{1,4}[.][0-9]{1,4}[.][0-9]{1,4}|[a-f0-9]{40}" placeholder="Unten Releases abfragen und auswählen"></label><label>Backend<select name="backend"><option>CUDA</option><option>CPU</option></select></label><label>Parallele Build-Jobs<select name="jobs"><option>1</option><option>2</option></select></label><p>GPU-Architekturen werden automatisch aus beiden Karten übernommen. Begrenzte Build-Last für den Parallelbetrieb.</p><button>llama.cpp herunterladen &amp; bauen</button></form><button id="rt-cancel" class="secondary">Laufenden Build abbrechen</button><p id="rt-message" role="status"></p><div id="rt-job"></div><details><summary>Build-Ausgabe (eigener Build)</summary><pre id="rt-log" style="max-height:320px;overflow:auto;white-space:pre-wrap"></pre></details></section><section class="card"><h2>Versionen &amp; Änderungen</h2><p>Das aktuelle reguläre Release steht zuerst. Nightlies enthalten neuere Änderungen, sind aber keine pauschale Empfehlung. Modell-, MTP- und Projektor-Kompatibilität vor einem Standardwechsel testen.</p><button id="rt-releases">Nach Updates suchen</button><div id="rt-releases-list"></div></section><section class="card"><h2>Geprüfte Builds</h2><div id="rt-builds"></div><button id="rt-rollback" class="secondary">Vorherigen Standard wiederherstellen</button><p>Als Standard auswählen startet keinen Prozess. Bestehende Router bleiben unverändert.</p></section><section class="card"><h2>Automatische Speicheranpassung</h2><p>Kein pauschales Reservefeld: Die Laufzeit soll Modell, gewünschten Kontext, Slots und den aktuell freien Speicher gemeinsam berücksichtigen. Unterstützte Builds verwenden llama.cpp <code>--fit on</code>; der Kontext wird ausdrücklich vorgegeben. Keine absichtlichen OOM-Tests auf den produktiv genutzten GPUs.</p><form id="rt-fit"><label>Vorhandenes Routerprofil<select name="profile" id="rt-profiles"></select></label><label>Gewünschter Gesamtkontext<input name="context" type="number" min="512" max="2097152" value="160000" required></label><label>Slots<input name="slots" type="number" min="1" max="16" value="2" required></label><button>Auto-Einpassung berechnen</button></form><pre id="rt-fit-result" style="white-space:pre-wrap"></pre><p>Ein Build allein kann keine Modell-Layerzahl bestimmen. Die Berechnung verwendet llama-fit-params ohne Gewichtsallokation. Sie ist eine Prognose für das Textmodell und kein Lasttest. Vision-Projektor und MTP sind noch nicht eingerechnet. Ein Modellstart erfolgt nicht.</p></section></div>`;}
function bind(){const root=document.querySelector('#runtime-live');if(!root)return;let running=false;
const msg=t=>{if(root.isConnected)root.querySelector('#rt-message').textContent=t;};
async function status(){try{const s=await api();if(!root.isConnected)return;running=s.job?.state==='running';root.querySelector('#rt-job').textContent=s.job?`${s.job.revision} · ${s.job.state} · ${s.job.phase} ${s.job.error||''}`:'Noch kein Build gestartet.';root.querySelector('#rt-cancel').disabled=!running;root.querySelector('#rt-build button').disabled=running;root.querySelector('#rt-rollback').disabled=!s.previous;root.querySelector('#rt-builds').innerHTML=s.builds.map(b=>`<article><h3>${e(b.revision)} · ${e(b.backend)} ${s.active===b.id?'· STANDARD':''}</h3><p>Commit ${e(b.commit)} · GPU-Ziele ${e(b.architectures.join(', ')||'CPU')} · Auto-Fit ${b.fit_supported?'unterstützt':'nicht unterstützt'}</p><button data-build="${e(b.id)}" ${s.active===b.id?'disabled':''}>Als Standard auswählen</button></article>`).join('')||'<p>Noch kein geprüfter Build vorhanden.</p>';root.querySelectorAll('[data-build]').forEach(b=>b.onclick=()=>action('/activate',{build_id:b.dataset.build}));if(s.job){const log=await api('/log');if(root.isConnected)root.querySelector('#rt-log').textContent=log.text;}if(running)setTimeout(()=>{if(root.isConnected)status();},3000);}catch(err){msg(err.message);}}
@@ -10,7 +10,7 @@ const RuntimeUI=(()=>{
root.querySelector('#rt-check').onclick=prerequisites;
root.querySelector('#rt-build').onsubmit=event=>{event.preventDefault();const d=Object.fromEntries(new FormData(event.target));d.jobs=Number(d.jobs);action('/build',d);};
root.querySelector('#rt-cancel').onclick=()=>action('/cancel',{});root.querySelector('#rt-rollback').onclick=()=>action('/rollback',{});
root.querySelector('#rt-releases').onclick=async()=>{msg('Offizielle Releases werden abgefragt …');try{const v=await api('/releases');if(!root.isConnected)return;root.querySelector('#rt-releases-list').innerHTML=v.releases.map(r=>`<article><h3>${e(r.tag)} ${r.prerelease?'· Vorabversion':''} · ${e(r.published)}</h3><button data-release="${e(r.tag)}">Für Build auswählen</button><a href="${e(r.url)}" target="_blank" rel="noopener noreferrer">Original auf GitHub</a><details><summary>Änderungen / Release-Notes</summary><pre style="white-space:pre-wrap">${e(r.notes)}</pre></details></article>`).join('');root.querySelectorAll('[data-release]').forEach(b=>b.onclick=()=>{root.querySelector('[name=revision]').value=b.dataset.release;msg('Version ausgewählt. Build kann gestartet werden.');});msg(v.releases.length+' offizielle Releases geladen.');}catch(err){msg(err.message);}};
root.querySelector('#rt-releases').onclick=async()=>{msg('Offizielle Releases werden abgefragt …');try{const v=await api('/releases');if(!root.isConnected)return;root.querySelector('#rt-releases-list').innerHTML=v.releases.map(r=>`<article><h3>${e(r.tag)} ${r.prerelease?'· Nightly / Vorabversion':'· Reguläres Release'} · ${e(r.published)}</h3><button data-release="${e(r.tag)}">Für Build auswählen</button><a href="${e(r.url)}" target="_blank" rel="noopener noreferrer">Original auf GitHub</a><details><summary>Änderungen / Release-Notes</summary><pre style="white-space:pre-wrap">${e(r.notes)}</pre></details></article>`).join('');root.querySelectorAll('[data-release]').forEach(b=>b.onclick=()=>{root.querySelector('[name=revision]').value=b.dataset.release;msg('Version ausgewählt. Build kann gestartet werden.');});msg(v.releases.length+' offizielle Releases geladen.');}catch(err){msg(err.message);}};
api('/references').then(v=>{if(!root.isConnected)return;const select=root.querySelector('#rt-profiles');select.innerHTML=v.profiles.map(p=>`<option value="${e(p.id)}" ${p.id==='medium'?'selected':''}>${e(p.id)} · ${e(p.file)} ${p.available?'':'(nicht eingebunden)'}</option>`).join('');select.onchange=()=>{const p=v.profiles.find(p=>p.id===select.value);root.querySelector('#rt-fit [name=context]').value=p.context;root.querySelector('#rt-fit [name=slots]').value=p.slots;};}).catch(err=>msg(err.message));
root.querySelector('#rt-fit').onsubmit=async event=>{event.preventDefault();const b=event.target.querySelector('button');b.disabled=true;msg('Modellmetadaten und Speicherbedarf werden berechnet …');const d=Object.fromEntries(new FormData(event.target));d.context=Number(d.context);d.slots=Number(d.slots);try{const v=await api('/fit',d);if(root.isConnected)root.querySelector('#rt-fit-result').textContent=(v.success?'Prognose erfolgreich. Kein Modell geladen.':'Aktuell keine passende Einpassung ermittelt.')+(v.within_ram_limit===false?'\nNICHT STARTBAR in der Testinstanz: berechneter Host-RAM überschreitet deren Limit von '+v.ram_limit_mib+' MiB.':'')+'\nGPU-Layer (Prognose): '+(v.fitted?.gpu_layers??'nicht ermittelt')+'\nGPU-Verteilung: '+(v.fitted?.tensor_split??'ein Gerät / nicht ermittelt')+'\nAusgelassene belegte GPUs: '+v.excluded_gpus.join(', ')+'\nAutomatische Reserve (MiB): '+v.reserve_mib.join(', ')+'\n'+v.memory.map(m=>`${m.device}: Modell ${m.model_mib} MiB · Kontext ${m.context_mib} MiB · Rechenspeicher ${m.compute_mib} MiB`).join('\n')+'\n'+v.arguments+'\n'+v.details;msg('Berechnung abgeschlossen. Freier Speicher kann sich durch andere Dienste ändern.');}catch(err){msg(err.message);}finally{b.disabled=false;}};
prerequisites();status();
+4 -1
View File
@@ -107,6 +107,8 @@ class Runtime:
with self.lock:return json.loads(json.dumps(self.state))
def releases(self):
rows=github('releases?per_page=10')
latest=github('releases/latest')
rows=[latest]+[r for r in rows if r['tag_name']!=latest['tag_name']]
return {'releases':[dict(tag=r['tag_name'],name=r['name'],published=r['published_at'],notes=r.get('body',''),url=r['html_url'],prerelease=r['prerelease']) for r in rows if not r['draft']]}
def run(self,args,cwd,phase,timeout=7200):
with self.lock:
@@ -128,7 +130,7 @@ class Runtime:
stream.close()
with self.lock:self.process=None
def start(self,revision,backend='CUDA',jobs=1):
if not isinstance(revision,str) or not re.fullmatch(r'(?:b[0-9]{3,8}|[a-f0-9]{40})',revision):raise ValueError('Offizielles b-Release oder vollständigen Commit-SHA wählen.')
if not isinstance(revision,str) or not re.fullmatch(r'(?:b[0-9]{3,8}|v[0-9]{1,4}\.[0-9]{1,4}\.[0-9]{1,4}|[a-f0-9]{40})',revision):raise ValueError('Offizielles v-Release, b-Nightly oder vollständigen Commit-SHA wählen.')
if backend not in ('CUDA','CPU') or type(jobs)!=int or not 1<=jobs<=2:raise ValueError('CPU/CUDA und 1–2 Build-Jobs unterstützt.')
with self.lock:
if self.busy:raise ValueError('Ein Build läuft bereits.')
@@ -153,6 +155,7 @@ class Runtime:
prepare_mtp_source(directory)
arch=sorted(set(g['architecture'] for g in prereq['gpus']))
args=['cmake','-S','.', '-B','build','-DCMAKE_BUILD_TYPE=Release','-DGGML_NATIVE=OFF','-DLLAMA_BUILD_TESTS=OFF','-DLLAMA_BUILD_EXAMPLES=OFF','-DLLAMA_BUILD_SERVER=ON','-DGGML_CUDA='+('ON' if backend=='CUDA' else 'OFF')]
args+=['-DLLAMA_BUILD_IS_DEV='+('OFF' if revision.startswith('v') else 'ON')]
if backend=='CUDA':args+=['-DCMAKE_CUDA_ARCHITECTURES='+';'.join(arch)]
self.run(args,directory,'Build konfigurieren',300)
self.run(['cmake','--build','build','--target','llama-server','llama-fit-params','-j',str(jobs)],directory,'llama.cpp-Werkzeuge kompilieren')
+4 -2
View File
@@ -16,6 +16,8 @@ class RuntimeTests(unittest.TestCase):
r=Runtime(d)
with patch.object(r,'prerequisites',return_value={'cuda_ready':False}),patch('runtime.subprocess.Popen') as spawn:
with self.assertRaises(ValueError):r.start('b9000')
with self.assertRaises(ValueError):r.start('v0.5.0')
self.assertEqual(r.prerequisites.call_count,2)
spawn.assert_not_called()
def test_selection_rollback_persistence(self):
with tempfile.TemporaryDirectory() as d:
@@ -42,8 +44,8 @@ class RuntimeTests(unittest.TestCase):
with self.assertRaises(ValueError):r.fit('medium',160000,2)
def test_official_prereleases_are_not_hidden(self):
row=dict(tag_name='b11229',name='build',published_at='date',body='notes',html_url='https://github.com/ggml-org/llama.cpp/releases',prerelease=True,draft=False)
with tempfile.TemporaryDirectory() as d,patch('runtime.github',return_value=[row]):
self.assertTrue(Runtime(d).releases()['releases'][0]['prerelease'])
with tempfile.TemporaryDirectory() as d,patch('runtime.github',side_effect=[[row],dict(row,tag_name='v0.5.0',prerelease=False)]):
releases=Runtime(d).releases()['releases'];self.assertEqual(releases[0]['tag'],'v0.5.0');self.assertTrue(releases[1]['prerelease'])
def test_fit_excludes_busy_gpu_and_preserves_context(self):
with tempfile.TemporaryDirectory() as d:
r=Runtime(d);r.state.update(active='build',builds=[dict(id='build',backend='CUDA',fit_tool=True)])