Guide model execution setup across catalog library and profiles

This commit is contained in:
Mikei386
2026-09-29 08:27:19 +02:00
parent 88e0edcc59
commit 7bd9096e67
11 changed files with 95 additions and 13 deletions
+9 -4
View File
@@ -7,6 +7,10 @@ window.CatalogUI = (() => {
const response = await fetch('/api/v1/catalog'+path, data ? {method:'POST',headers:{'Content-Type':'application/json','X-Athena-Deck':'1'},body:JSON.stringify(data)} : {});
const result = await response.json(); if (!response.ok) throw Error(result.error || 'Anfrage fehlgeschlagen'); return result;
}
function executionHTML(x) {
if(!x)return '<p>Ausführbarkeit noch nicht geprüft.</p>';
return `<section class="note execution-setup"><strong>${escape(x.label)}</strong>${x.runtime?`<p>${escape(x.runtime)} · ${x.installed?'bereits installiert – wird wiederverwendet':'noch nicht eingerichtet'}</p>`:''}<p>${escape(x.message)}</p>${x.components?.length?`<p>Benötigt: ${x.components.map(escape).join(' · ')}. Bei Profilen unter „Komponenten“ zuordnen; TTS enthält das komplette Paket.</p>`:''}${x.route?`<a class="link" href="${escape(x.route)}">${x.installed?'Einrichtung ansehen':'Ausführung einrichten'} →</a>`:''}${x.alternative?`<p><a class="link" href="${escape(x.alternative.route)}">${escape(x.alternative.label)} →</a></p>`:''}<p class="small">Installierte Laufzeit bedeutet nicht, dass Modell und Kontext in den Speicher passen. Ein Test startet nur auf deinen Auftrag.</p></section>`;
}
function html(library, downloads=false) {
return `<section id="live-catalog"><div class="section-heading"><div><span class="kicker">HUGGING FACE · LIVE-KATALOG</span><h2>${downloads ? 'Downloads' : library ? 'Deine Dateien auf Athena' : 'Das nächste Modell entdecken'}</h2><p>${downloads ? 'Abgeschlossene Einträge ausblenden. Die Dateien bleiben in deiner Bibliothek.' : library ? 'Heruntergeladene Dateien mit festgehaltener Version und Integritätsprüfung.' : 'Modell suchen, Variante vergleichen und gezielt auf Athena herunterladen.'}</p></div><button class="secondary" id="hub-refresh">Aktualisieren</button></div>
<div id="hub-capacity" class="small muted"></div><div id="hub-job"></div><p id="hub-message" role="status" aria-live="polite"></p>
@@ -26,7 +30,7 @@ window.CatalogUI = (() => {
root.querySelectorAll('[data-repo]').forEach(b => b.onclick = () => loadFiles(b.dataset.repo));
}
function renderLibrary() {
el('hub-results').innerHTML = state.entries.filter(x => x.kind === kind).map(x => `<article class="card"><span class="pill">${escape(x.role_label)} · HERUNTERGELADEN</span><h3>${escape(x.repo)}</h3><p class="hub-filename">${escape(x.file)}</p><strong>${size(x.size)}</strong><p>${x.profile_eligible?'Modelldatei · über ein Profil verwenden':'Zusatzdatei · wird einem Modellprofil unter Komponenten zugeordnet'}</p><details><summary>Version und Integrität</summary><p class="hub-filename">Revision ${escape(x.revision)}</p><p>${x.upstream_hash_verified ? 'SHA-256 mit Quelle geprüft' : 'SHA-256 lokal erfasst; Größe mit Quelle geprüft'}</p></details>${x.profile_eligible===true?`<div class="card-actions"><button data-profile-model="${x.id}">Profil anlegen</button></div>`:""}</article>`).join('') || '<div class="empty wide"><h3>Deine Bibliothek ist noch leer</h3><p>Unter „Entdecken“ kannst du Dateien für diesen Bereich auswählen und herunterladen.</p></div>';
el('hub-results').innerHTML = state.entries.filter(x => x.kind === kind).map(x => `<article class="card"><span class="pill">${escape(x.role_label)} · HERUNTERGELADEN</span><h3>${escape(x.repo)}</h3><p class="hub-filename">${escape(x.file)}</p><strong>${size(x.size)}</strong><p>${x.profile_eligible?'Modelldatei · über ein Profil verwenden':'Zusatzdatei · wird einem Modellprofil unter Komponenten zugeordnet'}</p><details><summary>Version und Integrität</summary><p class="hub-filename">Revision ${escape(x.revision)}</p><p>${x.upstream_hash_verified ? 'SHA-256 mit Quelle geprüft' : 'SHA-256 lokal erfasst; Größe mit Quelle geprüft'}</p></details>${executionHTML(x.execution)}${x.profile_eligible===true?`<div class="card-actions"><button data-profile-model="${x.id}">Profil anlegen</button></div>`:""}</article>`).join('') || '<div class="empty wide"><h3>Deine Bibliothek ist noch leer</h3><p>Unter „Entdecken“ kannst du Dateien für diesen Bereich auswählen und herunterladen.</p></div>';
}
function renderJob() {
el('hub-capacity').textContent = `Athena · ${size(state.free_bytes)} freier Speicher · 10 GiB bleiben als Reserve frei`;
@@ -57,6 +61,7 @@ window.CatalogUI = (() => {
if (!detail || !el('hub-download')) return;
const chosen = el('hub-variants').querySelector('input:checked');
const file = chosen ? detail.files[Number(chosen.value)] : null;
if(el('hub-execution'))el('hub-execution').innerHTML=file?executionHTML(file.execution):'<p>Datei auswählen, um die passende Laufzeit zu prüfen.</p>';
const busy = starting;
const queued=file && (state.downloads||[]).some(j=>['queued','downloading'].includes(j.state)&&j.repo===detail.repo&&j.revision===detail.revision&&j.file===file.name);
const exists = file && installed(file);
@@ -77,8 +82,8 @@ window.CatalogUI = (() => {
const sequence = ++detailSequence; selected = repo; detail = null; renderModels();
el('hub-files').innerHTML = '<p role="status">Modelldetails werden geladen …</p>';
try {
const result = await api('/files?'+new URLSearchParams({repo})); if (!root.isConnected || sequence !== detailSequence) return; detail = result;
el('hub-files').innerHTML = `<span class="kicker">MODELLDETAILS</span><h2 class="hub-filename">${escape(repo)}</h2><div class="hub-badges"><span class="pill">${detail.gated ? 'Zugang beschränkt' : 'Öffentlich'}</span><span class="pill">${detail.files.length} unterstützte Dateien</span></div><p>Vision: ${escape(detail.capabilities?.vision||'Unbekannt')} · Tool-Calling: ${escape(detail.capabilities?.tools||'Unbekannt')}. Hub-Hinweise, keine Laufzeitgarantie; fehlende Angaben bedeuten nicht „nein“.</p><p>Lizenz: ${escape(detail.license || 'Nicht angegeben')} · <a class="link" href="${escape(detail.url)}" target="_blank" rel="noopener noreferrer">Modellkarte lesen ↗</a></p><div class="hub-fit" id="hub-fit-result">Wähle eine Datei für die Speicherprüfung.</div>${detail.gated ? '<p class="warning">Dieses Repository benötigt eine Freigabe. Downloads mit Hugging-Face-Zugangsdaten werden noch nicht unterstützt.</p>' : ''}<h3>Variante oder Datei auswählen</h3><div class="hub-file-controls"><label>Dateiname filtern<input id="hub-file-filter" placeholder="z. B. Q4_K_M"></label><label>Format<select id="hub-format"><option value="">Alle Formate</option><option value=".gguf">GGUF</option><option value=".safetensors">Safetensors</option><option value=".json">JSON / Konfiguration</option></select></label></div><div id="hub-variants" class="hub-variants"></div><div class="hub-review"><p id="hub-selection" class="hub-filename"></p><button id="hub-download" disabled>Ausgewählte Datei herunterladen</button><p class="small">Einzeldatei-Download auf Athena. Geteilte Modelle und Bild-/Audio-/Video-Pipelines benötigen weitere Dateien; dies installiert noch keinen vollständigen Worker.</p><details><summary>Feste Quellversion</summary><p class="hub-filename">${escape(detail.revision)}</p></details></div>`;
const result = await api('/files?'+new URLSearchParams({repo,kind})); if (!root.isConnected || sequence !== detailSequence) return; detail = result;
el('hub-files').innerHTML = `<span class="kicker">MODELLDETAILS</span><h2 class="hub-filename">${escape(repo)}</h2><div class="hub-badges"><span class="pill">${detail.gated ? 'Zugang beschränkt' : 'Öffentlich'}</span><span class="pill">${detail.files.length} unterstützte Dateien</span></div><p>Vision: ${escape(detail.capabilities?.vision||'Unbekannt')} · Tool-Calling: ${escape(detail.capabilities?.tools||'Unbekannt')}. Hub-Hinweise, keine Laufzeitgarantie; fehlende Angaben bedeuten nicht „nein“.</p><p>Lizenz: ${escape(detail.license || 'Nicht angegeben')} · <a class="link" href="${escape(detail.url)}" target="_blank" rel="noopener noreferrer">Modellkarte lesen ↗</a></p><div id="hub-execution"></div><div class="hub-fit" id="hub-fit-result">Wähle eine Datei für die Speicherprüfung.</div>${detail.gated ? '<p class="warning">Dieses Repository benötigt eine Freigabe. Downloads mit Hugging-Face-Zugangsdaten werden noch nicht unterstützt.</p>' : ''}<h3>Variante oder Datei auswählen</h3><div class="hub-file-controls"><label>Dateiname filtern<input id="hub-file-filter" placeholder="z. B. Q4_K_M"></label><label>Format<select id="hub-format"><option value="">Alle Formate</option><option value=".gguf">GGUF</option><option value=".safetensors">Safetensors</option><option value=".json">JSON / Konfiguration</option></select></label></div><div id="hub-variants" class="hub-variants"></div><div class="hub-review"><p id="hub-selection" class="hub-filename"></p><button id="hub-download" disabled>Ausgewählte Datei herunterladen</button><p class="small">Einzeldatei-Download auf Athena. Geteilte Modelle und Bild-/Audio-/Video-Pipelines benötigen weitere Dateien; dies installiert noch keinen vollständigen Worker.</p><details><summary>Feste Quellversion</summary><p class="hub-filename">${escape(detail.revision)}</p></details></div>`;
el('hub-file-filter').oninput = renderFiles; el('hub-format').onchange = renderFiles; renderFiles();
el('hub-download').onclick = async () => {
const chosen = el('hub-variants').querySelector('input:checked'); if (!chosen || starting) return;
@@ -94,5 +99,5 @@ window.CatalogUI = (() => {
if (!library && !downloads) { try { el('hub-base-only').checked=localStorage.getItem('deck-base-only')==='true'; } catch {} el('hub-base-only').onchange=()=>{try { localStorage.setItem('deck-base-only',String(el('hub-base-only').checked)); } catch {} search(new FormData(el('hub-search')).get('q').trim());}; el('hub-sort').onchange = () => search(new FormData(el('hub-search')).get('q').trim()); el('hub-search').onsubmit = event => { event.preventDefault(); search(new FormData(event.target).get('q').trim()); }; search(''); }
refresh();
}
return {html,bind};
return {html,bind,executionHTML};
})();