Guide model setup consistently across categories
This commit is contained in:
1 parent
aee31359b6
commit
541101091d
6 files changed
+43
-7
No files matched your search
@@ -259,3 +259,14 @@ Der Ergebnisplayer bleibt beim Status-Polling bestehen, damit die Wiedergabe nic
|
|||||||
alle 1,5 Sekunden zurückgesetzt wird.
|
alle 1,5 Sekunden zurückgesetzt wird.
|
||||||
|
|
||||||
Native WireGuard-Anbindung: [Systemdienst, Zugriffsmodi und Migration](network/NATIVE.md). Der Tunnel läuft außerhalb von Docker; die isolierte Deck-App kann vorerst im Container bleiben.
|
Native WireGuard-Anbindung: [Systemdienst, Zugriffsmodi und Migration](network/NATIVE.md). Der Tunnel läuft außerhalb von Docker; die isolierte Deck-App kann vorerst im Container bleiben.
|
||||||
|
|
||||||
|
## Einheitliche Modelle Einrichtung
|
||||||
|
|
||||||
|
Bibliothek und Profile zeigen für alle Modellkategorien dieselben vier Schritte:
|
||||||
|
Erkennung, Laufzeit, Zusatzdateien und Ausführungsprüfung. Vorhandene Laufzeiten
|
||||||
|
werden wiederverwendet; fehlende führen direkt zur passenden Einrichtungsseite.
|
||||||
|
Unbekannte Familien und fehlende Generierungsanbindungen bleiben ausdrücklich
|
||||||
|
gesperrt. Der Komponentenbereich bietet vorhandene passende Dateien vorausgewählt
|
||||||
|
an und einen Sammeldownload fehlender Pflichtdateien. Bei mehreren verfügbaren
|
||||||
|
Varianten ist eine bewusste Einzelwahl nötig; ungeprüfte Quellen werden nicht
|
||||||
|
automatisch installiert. Speichern und Downloads starten keine Inferenz.
|
||||||
+1
-1
@@ -10,7 +10,7 @@ window.CatalogUI = (() => {
|
|||||||
}
|
}
|
||||||
function executionHTML(x) {
|
function executionHTML(x) {
|
||||||
if(!x)return '<p>Ausführbarkeit noch nicht geprüft.</p>';
|
if(!x)return '<p>Ausführbarkeit noch nicht geprüft.</p>';
|
||||||
return `<section class="note execution-setup"><strong>${escape(x.label)}</strong><p>Modellfamilie: ${escape(x.family||'Unbekannt')}${x.evidence?' · '+escape(x.evidence):''}</p>${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(' · ')}. Modellabhängig zuordnen; benötigte Zusatzdateien gehören zum vollständigen Paket.</p>`:''}${x.suggested_components?.length?`<p>Zusatzdateien in dieser Quelle (Kompatibilität ungeprüft): ${x.suggested_components.map(escape).join(' · ')}. Nach dem Download unter Profil → Einrichtung & Komponenten prüfen.</p>`:''}${x.route?`<a class="link" href="${escape(x.route)}">${x.installed?'Einrichtung ansehen':'Ausführung einrichten'} →</a>`:''}${x.candidates?.length?`<h4>Mögliche Laufzeiten</h4><p class="small">Diese Liste unterscheidet bekannte Zuordnungen von ungeprüften Kategorieoptionen. Eine Installation ersetzt keinen fehlenden Worker.</p>${x.candidates.map(c=>`<p><strong>${escape(c.label)}</strong> · ${c.installed?'bereits installiert – wiederverwenden':'nicht installiert'}<br>${escape(c.evidence)}${c.route?`<br><a class="link" href="${escape(c.route)}">${c.installed?'Laufzeit / Einrichtung ansehen':'Laufzeit herunterladen / bereitmachen'} →</a>`:'<br>Kein gepflegter Deck-Installer für diese Option verfügbar.'}</p>`).join('')}`:`<p>${escape(x.candidate_message||'')}</p>`}${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>`;
|
return `<section class="note execution-setup"><strong>${escape(x.label)}</strong>${x.setup_steps?.length?`<ol>${x.setup_steps.map(s=>`<li><strong>${escape(s.label)} · ${s.state==='complete'?'Erledigt':s.state==='blocked'?'Noch nicht möglich':'Nächster Schritt'}</strong><br>${escape(s.message)}${s.route&&s.state==='action'?` · <a href="${escape(s.route)}">Jetzt einrichten →</a>`:''}</li>`).join('')}</ol>`:''}<p>Modellfamilie: ${escape(x.family||'Unbekannt')}${x.evidence?' · '+escape(x.evidence):''}</p>${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(' · ')}. Modellabhängig zuordnen; benötigte Zusatzdateien gehören zum vollständigen Paket.</p>`:''}${x.suggested_components?.length?`<p>Zusatzdateien in dieser Quelle (Kompatibilität ungeprüft): ${x.suggested_components.map(escape).join(' · ')}. Nach dem Download unter Profil → Einrichtung & Komponenten prüfen.</p>`:''}${x.route?`<a class="link" href="${escape(x.route)}">${x.installed?'Einrichtung ansehen':'Ausführung einrichten'} →</a>`:''}${x.candidates?.length?`<details><summary>Technische Details und Laufzeitoptionen</summary><p class="small">Diese Liste unterscheidet bekannte Zuordnungen von ungeprüften Kategorieoptionen. Eine Installation ersetzt keinen fehlenden Worker.</p>${x.candidates.map(c=>`<p><strong>${escape(c.label)}</strong> · ${c.installed?'bereits installiert – wiederverwenden':'nicht installiert'}<br>${escape(c.evidence)}${c.route?`<br><a class="link" href="${escape(c.route)}">${c.installed?'Laufzeit / Einrichtung ansehen':'Laufzeit herunterladen / bereitmachen'} →</a>`:'<br>Kein gepflegter Deck-Installer für diese Option verfügbar.'}</p>`).join('')}</details>`:`<p>${escape(x.candidate_message||'')}</p>`}${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) {
|
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>
|
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>
|
||||||
|
|||||||
+15
-1
@@ -50,7 +50,7 @@ def _assess(model, installed, profile=None):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def assess(model, installed, profile=None):
|
def _assess_candidates(model, installed, profile=None):
|
||||||
result=_assess(model,installed,profile)
|
result=_assess(model,installed,profile)
|
||||||
result['candidates']=[]
|
result['candidates']=[]
|
||||||
if result['state']=='component':return result
|
if result['state']=='component':return result
|
||||||
@@ -91,3 +91,17 @@ def assess(model, installed, profile=None):
|
|||||||
add('category',label,key,route,'Option für diese Kategorie; Kompatibilität mit dieser unbekannten Modellfamilie NICHT bestätigt')
|
add('category',label,key,route,'Option für diese Kategorie; Kompatibilität mit dieser unbekannten Modellfamilie NICHT bestätigt')
|
||||||
result['candidate_message']='Keine passende Laufzeit zuverlässig ermittelt. Modellkarte und Architektur müssen geprüft werden; keine automatische Installation auf Verdacht.'
|
result['candidate_message']='Keine passende Laufzeit zuverlässig ermittelt. Modellkarte und Architektur müssen geprüft werden; keine automatische Installation auf Verdacht.'
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def assess(model, installed, profile=None):
|
||||||
|
result=_assess_candidates(model,installed,profile)
|
||||||
|
if result['state']=='component':return result
|
||||||
|
verified=result['state'] not in ('unsupported','workflow_missing')
|
||||||
|
runtime=(next((c for c in result['candidates'] if c['id'] in ('voxcpm','audio_cpp')),None) or next((c for c in result['candidates'] if c['id']!='category'),None)) if verified else None
|
||||||
|
worker_missing=result['state'] in ('adapter_missing','workflow_missing','unsupported')
|
||||||
|
result['setup_steps']=[
|
||||||
|
dict(label='Modell erkennen',state='complete' if verified else 'blocked',message='Modellfamilie erkannt' if verified else 'Noch kein geprüfter Einrichtungsweg'),
|
||||||
|
dict(label='Laufzeit bereitstellen',state=('complete' if runtime['installed'] else 'action') if runtime else 'blocked',message=('Vorhandene Laufzeit wird wiederverwendet' if runtime['installed'] else 'Passende Laufzeit einrichten') if runtime else 'Keine automatische Installation auf Verdacht',route=runtime['route'] if runtime else None),
|
||||||
|
dict(label='Zusatzdateien zuordnen',state='complete' if result['state']=='ready' or not result['components'] and verified else 'action' if verified else 'blocked',message='Unter Einrichtung & Komponenten: vorhandene Dateien verwenden, fehlende herunterladen' if result['components'] else 'Keine Pflichtdateien im geprüften Rezept' if verified else 'Komponentenbedarf noch nicht zuverlässig bekannt'),
|
||||||
|
dict(label='Ausführung prüfen',state='blocked' if worker_missing else 'complete' if result['state']=='ready' else 'action',message='Generierungsanbindung fehlt noch; Installation allein reicht nicht' if worker_missing else 'Profil bereit' if result['state']=='ready' else 'Nach Einrichtung im Testbereich prüfen')]
|
||||||
|
return result
|
||||||
+2
-2
@@ -37,11 +37,11 @@ window.ProfilesUI=(()=>{
|
|||||||
el('profile-editor').innerHTML='<section class="card editor"><p role="status">Komponenten werden geprüft …</p></section>';
|
el('profile-editor').innerHTML='<section class="card editor"><p role="status">Komponenten werden geprüft …</p></section>';
|
||||||
try{
|
try{
|
||||||
const data=await api('profiles/components?'+new URLSearchParams({model_id:profile.model_id,...(extraRepo?{repo:extraRepo}:{})}));if(!root.isConnected||sequence!==panelSequence)return;
|
const data=await api('profiles/components?'+new URLSearchParams({model_id:profile.model_id,...(extraRepo?{repo:extraRepo}:{})}));if(!root.isConnected||sequence!==panelSequence)return;
|
||||||
el('profile-editor').innerHTML=`<section class="card editor"><div class="card-top"><h2>Komponenten · ${e(profile.name)}</h2><button class="secondary" id="components-close">Schließen</button></div><p>${e(data.message)}</p><details><summary>Andere Komponentenquelle auswählen (ungeprüft)</summary><label>Hugging-Face-Link oder Repository<input id="component-source" placeholder="https://huggingface.co/Organisation/Modell"></label><button id="component-source-open" type="button">Dateien anzeigen</button></details>${data.supported?`<p>Quelle: <a class="link" href="${e(data.source)}" target="_blank" rel="noopener noreferrer">Modellkarte und Lizenz ↗</a> · ${e(data.license||'Lizenz nicht angegeben')}</p>${data.runtime_route?`<p><a class="link" href="${e(data.runtime_route)}">${data.runtime_installed?'Laufzeit ansehen':'Passende Laufzeit installieren'} →</a></p>`:''}<p>Laufzeit: ${e(data.runtime)} · ${data.manual?'Zuordnung ungeprüft':data.runtime_installed?'installiert':'noch nicht eingerichtet'}</p>${!data.manual?'<button type="button" id="components-download-all">Alle fehlenden Dateien herunterladen</button>':'<p>Nur benötigte Dateien einzeln auswählen; diese Liste ist kein automatischer Downloadplan.</p>'}<form id="components-form">${data.requirements.map(r=>`<section class="card component-card"><h3>${e(r.label)}</h3><label>Datei aus der Bibliothek<select name="${r.role}"><option value="">Noch nicht zugeordnet</option>${r.available.map(f=>`<option value="${f.id}" ${(profile.components?.[r.role]===f.id||(!data.manual&&!profile.components?.[r.role]&&r.available.length===1))?'selected':''}>${e(f.file)} · ${(f.size/1024**3).toFixed(2)} GiB</option>`).join('')}</select></label>${r.candidates.map((f,i)=>`<div class="note"><p data-download-feedback="${r.role}:${i}" role="status"></p><p class="hub-filename">${e(f.name)} · ${(f.size/1024**3).toFixed(2)} GiB · Version ${e(f.revision.slice(0,12))}</p>${f.gated&&!f.auth_configured?'<p>Hugging-Face-Zugang unter <a href="#access">Einstellungen → Zugang</a> hinterlegen und Modellfreigabe prüfen.</p>':''}<button type="button" class="secondary" data-component-download="${r.role}:${i}" ${(f.gated&&!f.auth_configured)||r.available.some(x=>x.file===f.name&&x.revision===f.revision)?'disabled':''}>${r.available.some(x=>x.file===f.name&&x.revision===f.revision)?'Bereits in Bibliothek':'Datei herunterladen'}</button></div>`).join('')||'<p>Keine passende Quelldatei verfügbar.</p>'}</section>`).join('')}<div class="card-actions"><button id="components-save">Zuordnung auf Athena speichern</button><button type="button" class="secondary" id="components-refresh">Bibliothek aktualisieren</button></div><p class="small">Lade je eine Datei pro benötigter Komponente. Downloads erscheinen im Reiter Downloads. Danach Bibliothek aktualisieren, Dateien auswählen und die Zuordnung speichern. Es startet kein Modell.</p></form>`:''}<p id="components-message" role="status"></p></section>`;
|
el('profile-editor').innerHTML=`<section class="card editor"><div class="card-top"><h2>Komponenten · ${e(profile.name)}</h2><button class="secondary" id="components-close">Schließen</button></div>${CatalogUI.executionHTML(profile.execution)}<p>${e(data.message)}</p><details><summary>Andere Komponentenquelle auswählen (ungeprüft)</summary><label>Hugging-Face-Link oder Repository<input id="component-source" placeholder="https://huggingface.co/Organisation/Modell"></label><button id="component-source-open" type="button">Dateien anzeigen</button></details>${data.supported?`<p>Quelle: <a class="link" href="${e(data.source)}" target="_blank" rel="noopener noreferrer">Modellkarte und Lizenz ↗</a> · ${e(data.license||'Lizenz nicht angegeben')}</p>${data.runtime_route?`<p><a class="link" href="${e(data.runtime_route)}">${data.runtime_installed?'Laufzeit ansehen':'Passende Laufzeit installieren'} →</a></p>`:''}<p>Laufzeit: ${e(data.runtime)} · ${data.manual?'Zuordnung ungeprüft':data.runtime_installed?'installiert':'noch nicht eingerichtet'}</p>${!data.manual?'<button type="button" id="components-download-all">Fehlende Pflichtdateien herunterladen</button>':'<p>Nur benötigte Dateien einzeln auswählen; diese Liste ist kein automatischer Downloadplan.</p>'}<form id="components-form">${data.requirements.map(r=>`<section class="card component-card"><h3>${e(r.label)}</h3><label>Datei aus der Bibliothek<select name="${r.role}"><option value="">Noch nicht zugeordnet</option>${r.available.map(f=>`<option value="${f.id}" ${(profile.components?.[r.role]===f.id||(!data.manual&&!profile.components?.[r.role]&&r.available.length===1))?'selected':''}>${e(f.file)} · ${(f.size/1024**3).toFixed(2)} GiB</option>`).join('')}</select></label>${r.candidates.map((f,i)=>`<div class="note"><p data-download-feedback="${r.role}:${i}" role="status"></p><p class="hub-filename">${e(f.name)} · ${(f.size/1024**3).toFixed(2)} GiB · Version ${e(f.revision.slice(0,12))}</p>${f.gated&&!f.auth_configured?'<p>Hugging-Face-Zugang unter <a href="#access">Einstellungen → Zugang</a> hinterlegen und Modellfreigabe prüfen.</p>':''}<button type="button" class="secondary" data-component-download="${r.role}:${i}" ${(f.gated&&!f.auth_configured)||r.available.some(x=>x.file===f.name&&x.revision===f.revision)?'disabled':''}>${r.available.some(x=>x.file===f.name&&x.revision===f.revision)?'Bereits in Bibliothek':'Datei herunterladen'}</button></div>`).join('')||'<p>Keine passende Quelldatei verfügbar.</p>'}</section>`).join('')}<div class="card-actions"><button id="components-save">Zuordnung auf Athena speichern</button><button type="button" class="secondary" id="components-refresh">Bibliothek aktualisieren</button></div><p class="small">Lade je eine Datei pro benötigter Komponente. Downloads erscheinen im Reiter Downloads. Danach Bibliothek aktualisieren, Dateien auswählen und die Zuordnung speichern. Es startet kein Modell.</p></form>`:''}<p id="components-message" role="status"></p></section>`;
|
||||||
const missing=data.requirements?.filter(r=>!r.available.length&&!r.optional)||[];
|
const missing=data.requirements?.filter(r=>!r.available.length&&!r.optional)||[];
|
||||||
if(el('components-download-all')){el('components-download-all').disabled=!missing.length;el('components-download-all').onclick=async()=>{
|
if(el('components-download-all')){el('components-download-all').disabled=!missing.length;el('components-download-all').onclick=async()=>{
|
||||||
const b=el('components-download-all');b.disabled=true;let count=0;
|
const b=el('components-download-all');b.disabled=true;let count=0;
|
||||||
try{const plan=missing.map(r=>{const f=r.candidates.find(f=>!f.gated||f.auth_configured);if(!f)throw Error('Downloadplan unvollständig: '+r.label+'. Quelle oder HF-Zugang prüfen. Es wurde noch kein Download gestartet.');return f;});for(const f of plan){const catalog=await api('catalog');if((catalog.downloads||[]).some(j=>['queued','downloading'].includes(j.state)&&j.repo===f.repo&&j.file===f.name&&j.revision===f.revision))continue;await api('catalog/download',{repo:f.repo,filename:f.name,revision:f.revision,kind:profile.kind});count++;}if(el('components-message'))el('components-message').textContent='Dateien sind im Downloadplan. Nach Abschluss Bibliothek aktualisieren und Zuordnung speichern; passende Dateien werden vorausgewählt.';}
|
try{const plan=missing.map(r=>{const choices=r.candidates.filter(f=>!f.gated||f.auth_configured);if(choices.length>1)throw Error('Bitte Variante für '+r.label+' einzeln auswählen. Es wurde noch kein Download gestartet.');const f=choices[0];if(!f)throw Error('Downloadplan unvollständig: '+r.label+'. Quelle oder HF-Zugang prüfen. Es wurde noch kein Download gestartet.');return f;});for(const f of plan){const catalog=await api('catalog');if((catalog.downloads||[]).some(j=>['queued','downloading'].includes(j.state)&&j.repo===f.repo&&j.file===f.name&&j.revision===f.revision))continue;await api('catalog/download',{repo:f.repo,filename:f.name,revision:f.revision,kind:profile.kind});count++;}if(el('components-message'))el('components-message').textContent='Dateien sind im Downloadplan. Nach Abschluss Bibliothek aktualisieren und Zuordnung speichern; passende Dateien werden vorausgewählt.';}
|
||||||
catch(error){if(el('components-message'))el('components-message').textContent=(count?count+' Datei(en) bereits im Downloadplan. ':'')+error.message;b.disabled=false;}
|
catch(error){if(el('components-message'))el('components-message').textContent=(count?count+' Datei(en) bereits im Downloadplan. ':'')+error.message;b.disabled=false;}
|
||||||
};}
|
};}
|
||||||
el('component-source-open').onclick=()=>{const value=el('component-source').value.trim();if(value)components(profile,value);};
|
el('component-source-open').onclick=()=>{const value=el('component-source').value.trim();if(value)components(profile,value);};
|
||||||
|
|||||||
+3
-3
@@ -278,10 +278,10 @@ class Profiles:
|
|||||||
candidates=[dict(f,repo=source.get('repo',info.get('repo',model['repo'])),revision=source['revision'],gated=source['gated'],auth_configured=source.get('auth_configured',False)) for name in info['files'] for f in source['files'] if f['name']==name]
|
candidates=[dict(f,repo=source.get('repo',info.get('repo',model['repo'])),revision=source['revision'],gated=source['gated'],auth_configured=source.get('auth_configured',False)) for name in info['files'] for f in source['files'] if f['name']==name]
|
||||||
requirements.append(dict(role=role,label=info['label'],available=available,candidates=candidates))
|
requirements.append(dict(role=role,label=info['label'],available=available,candidates=candidates))
|
||||||
if voice_recipe(model):return dict(supported=True,requirements=requirements,source='https://huggingface.co/'+model['repo']+'/blob/'+model['revision']+'/README.md',license=source['license'],runtime='VoxCPM · eigene Python/PyTorch-Laufzeit',runtime_installed=bool(getattr(self,'voxcpm_runtime_ready',lambda:False)()),runtime_route='#voxcpm-runtime',worker_available=False,message='VoxCPM2 benötigt genau diese fünf Zusatzdateien derselben Quellversion. Ein separater Textencoder und ein Vision-Projektor werden nicht benötigt. tokenizer.json und tokenizer_config.json gehören zusammen, sie sind keine doppelten Modelle. Vorhandene Dateien werden wiederverwendet. Die passende Laufzeit kann unter Einstellungen → Laufzeiten → VoxCPM installiert werden. Der Deck-Worker fehlt noch; ein vollständiges Dateipaket allein kann noch nicht gestartet werden.')
|
if voice_recipe(model):return dict(supported=True,requirements=requirements,source='https://huggingface.co/'+model['repo']+'/blob/'+model['revision']+'/README.md',license=source['license'],runtime='VoxCPM · eigene Python/PyTorch-Laufzeit',runtime_installed=bool(getattr(self,'voxcpm_runtime_ready',lambda:False)()),runtime_route='#voxcpm-runtime',worker_available=False,message='VoxCPM2 benötigt genau diese fünf Zusatzdateien derselben Quellversion. Ein separater Textencoder und ein Vision-Projektor werden nicht benötigt. tokenizer.json und tokenizer_config.json gehören zusammen, sie sind keine doppelten Modelle. Vorhandene Dateien werden wiederverwendet. Die passende Laufzeit kann unter Einstellungen → Laufzeiten → VoxCPM installiert werden. Der Deck-Worker fehlt noch; ein vollständiges Dateipaket allein kann noch nicht gestartet werden.')
|
||||||
if music_recipe(model):return dict(supported=True,requirements=requirements,source='https://huggingface.co/'+model['repo']+'/blob/'+model['revision']+'/README.md',license=source['license'],runtime='audio.cpp',runtime_installed=bool(getattr(self,'music_runtime_ready',lambda:False)()),message='Passendes YuE2-GGUF-Paket: je eine VAE-Datei und die vier Zusatzdateien derselben Quellversion. F16-VAE ist die erste Downloadempfehlung. Vorhandene Dateien wiederverwenden, fehlende herunterladen und danach zuordnen. Die YuE2-Musikanbindung verwendet diese Dateien über audio.cpp. Speichern startet kein Modell; anschließend Musik → Testen verwenden.')
|
if music_recipe(model):return dict(supported=True,requirements=requirements,source='https://huggingface.co/'+model['repo']+'/blob/'+model['revision']+'/README.md',license=source['license'],runtime='audio.cpp',runtime_route='#audio-cpp-runtime',runtime_installed=bool(getattr(self,'music_runtime_ready',lambda:False)()),message='Passendes YuE2-GGUF-Paket: je eine VAE-Datei und die vier Zusatzdateien derselben Quellversion. F16-VAE ist die erste Downloadempfehlung. Vorhandene Dateien wiederverwenden, fehlende herunterladen und danach zuordnen. Die YuE2-Musikanbindung verwendet diese Dateien über audio.cpp. Speichern startet kein Modell; anschließend Musik → Testen verwenden.')
|
||||||
if video:return dict(supported=True,requirements=requirements,source='https://huggingface.co/'+model['repo']+'/blob/'+model['revision']+'/README.md',license=source['license'],runtime='ComfyUI · gemeinsame Bild- und Videolaufzeit',runtime_installed=bool(getattr(self,'video_runtime_ready',lambda:False)()),message='Geprüftes Dateirezept nach Herstellerdokumentation für die zweistufige Distilled-Pipeline mit fester Bildanzahl. Alle Komponenten stammen aus derselben Version wie dein Transformer. Kein beliebiger Gemma-Encoder; keine Comfy-INT8-Dateien. Optionaler Duration-Head, Temporal-Upsampling und DFR sind nicht Teil dieses Rezepts. Vorhandene ComfyUI-Laufzeit wiederverwenden; Videomodell im Bereich Video aktivieren. Vollständige Dateien sind keine Speicherzusage.')
|
if video:return dict(supported=True,requirements=requirements,source='https://huggingface.co/'+model['repo']+'/blob/'+model['revision']+'/README.md',license=source['license'],runtime='ComfyUI · gemeinsame Bild- und Videolaufzeit',runtime_route='#image-runtime',runtime_installed=bool(getattr(self,'video_runtime_ready',lambda:False)()),message='Geprüftes Dateirezept nach Herstellerdokumentation für die zweistufige Distilled-Pipeline mit fester Bildanzahl. Alle Komponenten stammen aus derselben Version wie dein Transformer. Kein beliebiger Gemma-Encoder; keine Comfy-INT8-Dateien. Optionaler Duration-Head, Temporal-Upsampling und DFR sind nicht Teil dieses Rezepts. Vorhandene ComfyUI-Laufzeit wiederverwenden; Videomodell im Bereich Video aktivieren. Vollständige Dateien sind keine Speicherzusage.')
|
||||||
if not image_recipe(model):return dict(supported=True,requirements=requirements,source=source['url'],license=source['license'],runtime='audio.cpp · Paketdefinition',runtime_installed=bool(getattr(self,'music_runtime_ready',lambda:False)()),message='Zusatzdateien aus der installierten Laufzeit-Paketdefinition. Weitere modellabhängige Pakete können erforderlich sein; dies ist keine vollständige Worker-Zusage.')
|
if not image_recipe(model):return dict(supported=True,requirements=requirements,source=source['url'],license=source['license'],runtime='audio.cpp · Paketdefinition',runtime_installed=bool(getattr(self,'music_runtime_ready',lambda:False)()),message='Zusatzdateien aus der installierten Laufzeit-Paketdefinition. Weitere modellabhängige Pakete können erforderlich sein; dies ist keine vollständige Worker-Zusage.')
|
||||||
return dict(supported=True,requirements=requirements,source=source['url'],license=source['license'],runtime=image_adapter(model)['runtime'],runtime_installed=self.image_runtime_ready(),
|
return dict(supported=True,requirements=requirements,source=source['url'],license=source['license'],runtime=image_adapter(model)['runtime'],runtime_route='#image-runtime',runtime_installed=self.image_runtime_ready(),
|
||||||
message='Fehlende Dateien herunterladen, danach Bibliothek aktualisieren und zuordnen. Für FLUX.2 Klein empfehlen sich zum Einstieg 4 Schritte und Guidance 1. Die Bildgewichte werden bei Bedarf zwischen RAM und RTX 5080 verschoben.' if image_adapter(model)['workflow']=='flux' else 'Zusatzdateien laut Modellkarte für ComfyUI. Kompatibilität und Speicherbedarf müssen mit der eingerichteten Laufzeit noch praktisch geprüft werden.')
|
message='Fehlende Dateien herunterladen, danach Bibliothek aktualisieren und zuordnen. Für FLUX.2 Klein empfehlen sich zum Einstieg 4 Schritte und Guidance 1. Die Bildgewichte werden bei Bedarf zwischen RAM und RTX 5080 verschoben.' if image_adapter(model)['workflow']=='flux' else 'Zusatzdateien laut Modellkarte für ComfyUI. Kompatibilität und Speicherbedarf müssen mit der eingerichteten Laufzeit noch praktisch geprüft werden.')
|
||||||
def manual_components(self,model,repo=None):
|
def manual_components(self,model,repo=None):
|
||||||
source=self.catalog.files(repo or model['repo'],None if repo else model['revision'])
|
source=self.catalog.files(repo or model['repo'],None if repo else model['revision'])
|
||||||
|
|||||||
@@ -51,3 +51,14 @@ class ExecutionSetupTests(unittest.TestCase):
|
|||||||
result=self.check('music','audio-cpp/Yue2-3B-GGUF','yue2-vae-f16.gguf')
|
result=self.check('music','audio-cpp/Yue2-3B-GGUF','yue2-vae-f16.gguf')
|
||||||
self.assertEqual(result['state'],'component')
|
self.assertEqual(result['state'],'component')
|
||||||
self.assertEqual(result['candidates'],[])
|
self.assertEqual(result['candidates'],[])
|
||||||
|
|
||||||
|
def test_shared_setup_steps_reuse_runtime_and_never_claim_missing_worker(self):
|
||||||
|
for model,installed in [(dict(kind='image',repo=QWEN_REPO,file='model.gguf'),{'image':True}),(dict(kind='voice',repo='openbmb/VoxCPM2',file='model.safetensors'),{'voxcpm':True})]:
|
||||||
|
result=assess(model,installed)
|
||||||
|
self.assertEqual(result['setup_steps'][1]['state'],'complete')
|
||||||
|
self.assertIn('wiederverwendet',result['setup_steps'][1]['message'])
|
||||||
|
self.assertEqual(result['setup_steps'][-1]['state'],'blocked')
|
||||||
|
unknown=self.check('voice','unknown/model','model.safetensors')
|
||||||
|
self.assertTrue(all(s['state']=='blocked' for s in unknown['setup_steps']))
|
||||||
|
ready=assess(dict(kind='image',repo=QWEN_REPO,file='model.gguf'),{'image':True},dict(runnable=True))
|
||||||
|
self.assertTrue(all(s['state']=='complete' for s in ready['setup_steps']))
|
||||||
Reference in new issue
Block a user