Offer runtime candidates across model categories and prepare audio.cpp with six build jobs

This commit is contained in:
Mikei386
2026-09-30 21:04:13 +02:00
parent 1dcd71b128
commit d700c68394
17 changed files with 239 additions and 21 deletions
+1
View File
@@ -24,6 +24,7 @@ function appearancePage(){
sync(); sync();
} }
async function refresh(){if(refreshing)return;refreshing=true;const page=location.hash.slice(1)||'dashboard';document.querySelectorAll('nav a').forEach(a=>a.classList.toggle('active',a.hash==='#'+page));try{error.textContent='';let html; async function refresh(){if(refreshing)return;refreshing=true;const page=location.hash.slice(1)||'dashboard';document.querySelectorAll('nav a').forEach(a=>a.classList.toggle('active',a.hash==='#'+page));try{error.textContent='';let html;
if(page==='audio-cpp-runtime'){AudioCppUI.render();return;}
if(page==='ltx-original-runtime'){LTXOriginalUI.render();return;} if(page==='ltx-original-runtime'){LTXOriginalUI.render();return;}
if(['video','video-runtime'].includes(page)){VideoUI.runtime();return;} if(['video','video-runtime'].includes(page)){VideoUI.runtime();return;}
if(['chat','image','audio','stt','music','voice','video','runtime','runtimes','image-runtime','stt-runtime','tts-runtime','docker-runtime','services'].includes(page)){Studio.render(page);return;} if(['chat','image','audio','stt','music','voice','video','runtime','runtimes','image-runtime','stt-runtime','tts-runtime','docker-runtime','services'].includes(page)){Studio.render(page);return;}
+10
View File
@@ -0,0 +1,10 @@
window.AudioCppUI=(()=>{
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/audio-cpp-runtime'+path,data?{method:'POST',headers:{'Content-Type':'application/json','X-Athena-Deck':'1'},body:JSON.stringify(data)}:{});const s=await r.json();if(!r.ok)throw Error(s.error||'Anfrage fehlgeschlagen');return s;}
function render(){const view=document.querySelector('#view');if(view.querySelector('#audio-cpp-page'))return;
view.innerHTML='<div id="audio-cpp-page"><h1>audio.cpp</h1><p>Gemeinsame C++-/CUDA-Laufzeit für unterstützte Audio-Modellfamilien. Passendes GGUF-Paket und ein Deck-Worker sind zusätzlich erforderlich.</p><section class="card"><h2>Herunterladen und bereitmachen</h2><div id="ac-status"></div><button id="ac-install">audio.cpp herunterladen &amp; CUDA-Build erstellen</button><button id="ac-cancel" class="secondary">Build abbrechen</button><p id="ac-message" role="status"></p><details><summary>Eigene Build-Ausgabe</summary><pre id="ac-log" style="white-space:pre-wrap;max-height:320px;overflow:auto"></pre></details></section><section class="card"><h2>YuE2 und Ladypoly WebUI</h2><p>Modellfamilie erkannt. Für Generierung fehlen noch der Deck-Musikworker und die Verbindung zur Ladypoly-Oberfläche. Dieser Build lädt keine Gewichte, startet kein Modell und gibt kein Profil am Endpunkt frei.</p><a href="#music">Zur Musik-Bibliothek →</a></section></div>';
const root=view.firstChild;let timer;
async function poll(){if(!root.isConnected)return;try{const s=await api();if(!root.isConnected)return;const running=s.job?.state==='running';root.querySelector('#ac-install').disabled=s.installed||running;root.querySelector('#ac-cancel').disabled=!running;root.querySelector('#ac-status').innerHTML=`<p><strong>${s.installed?'Installiert · Worker-Anbindung noch offen':'Noch nicht installiert'}</strong></p><p>Version ${e(s.version)} · Commit <code>${e(s.revision)}</code></p><p>Mindestens ${e(s.required_disk_gib)} GiB freier Speicher. Eigener Build mit sechs Jobs, CUDA-Ziele werden aus den vorhandenen GPUs ermittelt. Bestehende Laufzeiten werden nicht verändert.</p>${s.job?`<p>${e(s.job.state)} · ${e(s.job.phase)}</p>${running?'<progress aria-label="audio.cpp-Build läuft"></progress>':''}`:''}`;if(s.job){const log=await api('/log');if(root.isConnected)root.querySelector('#ac-log').textContent=log.text;}}catch(x){if(root.isConnected)root.querySelector('#ac-message').textContent=x.message;}if(root.isConnected){clearTimeout(timer);timer=setTimeout(poll,3000);}}
for(const [id,path] of [['ac-install','/install'],['ac-cancel','/cancel']])root.querySelector('#'+id).onclick=async()=>{root.querySelector('#'+id).disabled=true;try{await api(path,{});root.querySelector('#ac-message').textContent='Aktion angenommen.';}catch(x){root.querySelector('#ac-message').textContent=x.message;}poll();};poll();}
return {render};
})();
+73
View File
@@ -0,0 +1,73 @@
"""Pinned audio.cpp build, isolated from model execution and existing runtimes."""
import json
import os
from pathlib import Path
import shutil
import sys
import time
from ltx_original_runtime import LTXOriginalRuntime
REVISION='f2b4937306daa25f5c78520f3c626ed31495a37a'
VERSION='v0.8.1'
class AudioCppRuntime(LTXOriginalRuntime):
name="audio.cpp"
def __init__(self,root):
super().__init__(root,None)
request=self.root/'resume-request'
if request.is_file():
request.unlink()
self.start()
def paths(self):
p=self.root/'active.json'
if p.is_file():
ident=json.loads(p.read_text()).get('id','')
if len(ident)==32 and all(c in '0123456789abcdef' for c in ident):
base=self.root/ident
if (base/'ready').is_file():return base/'build/bin/audiocpp_cli',base/'source'
return self.root/'missing-cli',self.root/'missing-source'
def status(self):
cli,source=self.paths()
return dict(installed=cli.is_file() and (source/'model_specs/yue2.json').is_file(),revision=REVISION,version=VERSION,job=dict(self.job) if self.job else None,required_disk_gib=6,adapter_ready=False,origin='0xShug0/audio.cpp',message='Gemeinsame Laufzeit. Ein Build startet kein Modell; YuE2-Worker und Ladypoly-Anbindung sind noch nicht eingerichtet.')
def start(self):
if sys.platform!='linux':raise ValueError('audio.cpp wird auf dem Debian-Server gebaut.')
missing=[name for name in ('git','cmake','g++','nvcc','nvidia-smi') if not shutil.which(name)]
if missing:raise ValueError('Build-Werkzeuge fehlen: '+', '.join(missing)+'. Keine automatische Host-Paket- oder Treiberänderung.')
self.resume_id=(self.job or {}).get('id')
return super().start()
def _run(self):
base=self.root/self.job['id'];state='failed';phase='audio.cpp-Build fehlgeschlagen.'
try:
previous=self.root/getattr(self,'resume_id','missing') if getattr(self,'resume_id',None) else None
if previous and (previous/'source/.git').is_dir() and not (previous/'ready').exists():
base=previous;self.job['id']=previous.name;self._save()
else:base.mkdir(mode=0o700)
tmp=base/'tmp';tmp.mkdir(exist_ok=True)
env=dict(os.environ,TMPDIR=str(tmp),GIT_TERMINAL_PROMPT='0',OMP_NUM_THREADS='2')
source=base/'source';build=base/'build'
self._phase('Offizielle audio.cpp-Quelle herunterladen')
if not (source/'.git').is_dir():
self._command(['git','init',source],env)
self._command(['git','-C',source,'fetch','--depth','1','https://github.com/0xShug0/audio.cpp.git',REVISION],env)
self._command(['git','-C',source,'checkout','--detach','FETCH_HEAD'],env)
else:self._command(['git','-C',source,'rev-parse','--verify',REVISION+'^{commit}'],env)
# Compile for the actual available GPUs; no weight allocation or inference.
import subprocess
raw=subprocess.check_output(['nvidia-smi','--query-gpu=compute_cap','--format=csv,noheader'],text=True)
arch=sorted({line.strip().replace('.','') for line in raw.splitlines() if line.strip()})
if not arch or any(not a.isdigit() for a in arch):raise ValueError('CUDA-Architekturen nicht ermittelbar.')
self._phase('CUDA-Build konfigurieren · GPU-Ziele '+', '.join(arch))
self._command(['cmake','-S',source,'-B',build,'-DCMAKE_BUILD_TYPE=Release','-DENGINE_ENABLE_CUDA=ON','-DCMAKE_CUDA_ARCHITECTURES='+';'.join(arch),'-DAUDIOCPP_DEPLOYMENT_BUILD=ON','-DAUDIOCPP_BUILD_SERVER_FRONTENDS=OFF','-DENGINE_BUILD_TESTS=OFF'],env)
self._phase('audio.cpp bauen · sechs parallele Build-Jobs')
self._command(['cmake','--build',build,'--target','audiocpp_cli','audiocpp_server','--parallel','6'],env)
self._phase('Programmstart ohne Modell prüfen')
self._command([build/'bin/audiocpp_cli','--help'],env)
if not (source/'model_specs/yue2.json').is_file():raise ValueError('YuE2-Modellspezifikation fehlt.')
if self.cancel.is_set():raise InterruptedError()
(base/'ready').write_text(REVISION)
marker=self.root/'active.tmp';marker.write_text(json.dumps({'id':base.name}));marker.replace(self.root/'active.json')
state='complete';phase='audio.cpp installiert. Kein Modell gestartet; Deck-Worker noch erforderlich.'
except InterruptedError:state='cancelled';phase='Build abgebrochen.'
except Exception as exc:phase=str(exc)
finally:
# Keep unfinished compiler artifacts so retry resumes the same pinned build.
with self.lock:self.process=None;self.job.update(state=state,phase=phase,finished_at=time.time());self._save()
+16 -5
View File
@@ -72,7 +72,8 @@ def validate(doc):
if 'video/original-work/settings.json' in settings and (not isinstance(settings['video/original-work/settings.json'],dict) or len(json.dumps(settings['video/original-work/settings.json']))>65536):raise ValueError('Ungültige LTX-Einstellungen.') if 'video/original-work/settings.json' in settings and (not isinstance(settings['video/original-work/settings.json'],dict) or len(json.dumps(settings['video/original-work/settings.json']))>65536):raise ValueError('Ungültige LTX-Einstellungen.')
validate_record(doc.get('credentials')) validate_record(doc.get('credentials'))
runtimes=doc.get('runtimes') runtimes=doc.get('runtimes')
if not isinstance(runtimes,dict) or set(runtimes)-{'llama','llama_builds','image','tts','enhancers','swarm_nodes','ltx_original'} or not {'llama','llama_builds','image','tts','enhancers','swarm_nodes'}<=set(runtimes):raise ValueError('Ungültiges Laufzeitrezept.') if not isinstance(runtimes,dict) or set(runtimes)-{'llama','llama_builds','image','tts','enhancers','swarm_nodes','ltx_original','audio_cpp'} or not {'llama','llama_builds','image','tts','enhancers','swarm_nodes'}<=set(runtimes):raise ValueError('Ungültiges Laufzeitrezept.')
if 'audio_cpp' in runtimes and type(runtimes['audio_cpp']) is not bool:raise ValueError('Ungültige audio.cpp-Laufzeit.')
if 'ltx_original' in runtimes and type(runtimes['ltx_original']) is not bool:raise ValueError('Ungültige LTX-Laufzeit.') if 'ltx_original' in runtimes and type(runtimes['ltx_original']) is not bool:raise ValueError('Ungültige LTX-Laufzeit.')
if not isinstance(runtimes['llama_builds'],list) or len(runtimes['llama_builds'])>50 or any(not isinstance(b,dict) or set(b)!={'commit','backend'} or not re.fullmatch('[a-f0-9]{40}',b.get('commit','')) or b.get('backend') not in ('CUDA','CPU') for b in runtimes['llama_builds']):raise ValueError('Ungültige Buildrezepte.') if not isinstance(runtimes['llama_builds'],list) or len(runtimes['llama_builds'])>50 or any(not isinstance(b,dict) or set(b)!={'commit','backend'} or not re.fullmatch('[a-f0-9]{40}',b.get('commit','')) or b.get('backend') not in ('CUDA','CPU') for b in runtimes['llama_builds']):raise ValueError('Ungültige Buildrezepte.')
if runtimes['llama'] is not None: if runtimes['llama'] is not None:
@@ -137,7 +138,10 @@ class Backup:
if not s.network.disabled: if not s.network.disabled:
network=s.network.call('backup-export') network=s.network.call('backup-export')
else:warnings.append('Deck-Netzwerkmodul deaktiviert; fremder WireGuard-Gateway wird nicht gesichert.') else:warnings.append('Deck-Netzwerkmodul deaktiviert; fremder WireGuard-Gateway wird nicht gesichert.')
document=dict(format='athena-deck',version=VERSION,created_at=time.time(),settings=settings,models=models,credentials=s.credentials.read(),theme=theme,network=network,services=services,runtime_versions=dict(image=dict(comfy=s.image_runtime.status()['comfy_revision'],gguf=s.image_runtime.status()['gguf_revision']),tts=s.tts_runtime.status()['revision']),runtimes=dict(ltx_original=bool(getattr(s,'ltx_original_runtime',None) and s.ltx_original_runtime.status()['installed']),llama=llama,llama_builds=[dict(commit=b['commit'],backend=b['backend']) for b in state['builds']],image=s.image_runtime.status()['installed'],tts=s.tts_runtime.status()['installed'],enhancers={task:data['revision'] for task in ('t2i','i2i') if (data:=s.prompt_enhancer.installed(task))},swarm_nodes=(self.root/'video/swarm-comfy-nodes').is_dir()),warnings=warnings) document=dict(format='athena-deck',version=VERSION,created_at=time.time(),settings=settings,models=models,credentials=s.credentials.read(),theme=theme,network=network,services=services,runtime_versions=dict(image=dict(comfy=s.image_runtime.status()['comfy_revision'],gguf=s.image_runtime.status()['gguf_revision']),tts=s.tts_runtime.status()['revision']),runtimes=dict(audio_cpp=bool(getattr(s,'audio_cpp_runtime',None) and s.audio_cpp_runtime.status()['installed']),ltx_original=bool(getattr(s,'ltx_original_runtime',None) and s.ltx_original_runtime.status()['installed']),llama=llama,llama_builds=[dict(commit=b['commit'],backend=b['backend']) for b in state['builds']],image=s.image_runtime.status()['installed'],tts=s.tts_runtime.status()['installed'],enhancers={task:data['revision'] for task in ('t2i','i2i') if (data:=s.prompt_enhancer.installed(task))},swarm_nodes=(self.root/'video/swarm-comfy-nodes').is_dir()),warnings=warnings)
if document['runtimes']['audio_cpp']:
from audio_cpp_runtime import REVISION
document['runtime_versions']['audio_cpp']=REVISION
if document['runtimes']['ltx_original']: if document['runtimes']['ltx_original']:
from ltx_original_runtime import DESKTOP_REV,LTX_REV from ltx_original_runtime import DESKTOP_REV,LTX_REV
document['runtime_versions']['ltx_original']=dict(desktop=DESKTOP_REV,inference=LTX_REV) document['runtime_versions']['ltx_original']=dict(desktop=DESKTOP_REV,inference=LTX_REV)
@@ -167,10 +171,13 @@ class Backup:
elif s.network.status().get('enabled') or s.network.status().get('mode')!='lan':blockers.append('Deck-Tunnel vor Netzwerk-Restore im bestätigten LAN-Modus deaktivieren.') elif s.network.status().get('enabled') or s.network.status().get('mode')!='lan':blockers.append('Deck-Tunnel vor Netzwerk-Restore im bestätigten LAN-Modus deaktivieren.')
if doc['network']['mode']!='lan':warnings.append('Tunnelzugang wird mit Rückfallzeit aktiviert und muss über den Tunnel bestätigt werden.') if doc['network']['mode']!='lan':warnings.append('Tunnelzugang wird mit Rückfallzeit aktiviert und muss über den Tunnel bestätigt werden.')
versions=doc.get('runtime_versions',{}) versions=doc.get('runtime_versions',{})
if versions.get('audio_cpp'):
from audio_cpp_runtime import REVISION
if versions['audio_cpp']!=REVISION:blockers.append('Backup verlangt eine andere audio.cpp-Version. Passende Deck-Version verwenden.')
if versions.get('ltx_original'): if versions.get('ltx_original'):
from ltx_original_runtime import DESKTOP_REV,LTX_REV from ltx_original_runtime import DESKTOP_REV,LTX_REV
if versions['ltx_original']!=dict(desktop=DESKTOP_REV,inference=LTX_REV):blockers.append('Backup verlangt eine andere originale LTX-Version. Passende Deck-Version verwenden.') if versions['ltx_original']!=dict(desktop=DESKTOP_REV,inference=LTX_REV):blockers.append('Backup verlangt eine andere originale LTX-Version. Passende Deck-Version verwenden.')
if versions and {k:v for k,v in versions.items() if k!='ltx_original'}!=dict(image=dict(comfy=s.image_runtime.status()['comfy_revision'],gguf=s.image_runtime.status()['gguf_revision']),tts=s.tts_runtime.status()['revision']):blockers.append('Backup verlangt andere Bild-/TTS-Laufzeitversionen. Passende Deck-Version oder geprüftes Runtime-Upgrade verwenden.') if versions and {k:v for k,v in versions.items() if k not in ('ltx_original','audio_cpp')}!=dict(image=dict(comfy=s.image_runtime.status()['comfy_revision'],gguf=s.image_runtime.status()['gguf_revision']),tts=s.tts_runtime.status()['revision']):blockers.append('Backup verlangt andere Bild-/TTS-Laufzeitversionen. Passende Deck-Version oder geprüftes Runtime-Upgrade verwenden.')
for c in doc['services']: for c in doc['services']:
if not c.get('registry') and c.get('build_recipe')!='swarm-ui':warnings.append(c['name']+': Image nur lokal vorhanden; Registry-/Buildquelle fehlt.') if not c.get('registry') and c.get('build_recipe')!='swarm-ui':warnings.append(c['name']+': Image nur lokal vorhanden; Registry-/Buildquelle fehlt.')
ports=s.endpoint.allowed_ports ports=s.endpoint.allowed_ports
@@ -195,7 +202,7 @@ class Backup:
if len(set(data['services']))!=len(data['services']) or any(name not in services for name in data['services']):raise ValueError('Unbekannter Zusatzdienst.') if len(set(data['services']))!=len(data['services']) or any(name not in services for name in data['services']):raise ValueError('Unbekannter Zusatzdienst.')
if any(not services[name].get('registry') and not services[name].get('build_recipe') for name in data['services']):raise ValueError('Gewählter Dienst hat keine Wiederherstellungsquelle.') if any(not services[name].get('registry') and not services[name].get('build_recipe') for name in data['services']):raise ValueError('Gewählter Dienst hat keine Wiederherstellungsquelle.')
s=self.server s=self.server
if s.catalog.pending or s.catalog.job and s.catalog.job['state']=='downloading' or s.runtime.busy or any((getattr(o,'job',None) or {}).get('state')=='running' for o in (s.image_runtime,s.tts_runtime,s.ltx_original_runtime,s.prompt_enhancer,s.auto_tests,s.chat_tests,s.image_tests,s.tts_tests,s.stt)):raise ValueError('Zuerst laufende Downloads, Builds und Tests abschließen oder abbrechen.') if s.catalog.pending or s.catalog.job and s.catalog.job['state']=='downloading' or s.runtime.busy or any((getattr(o,'job',None) or {}).get('state')=='running' for o in (s.image_runtime,s.tts_runtime,s.ltx_original_runtime,getattr(s,'audio_cpp_runtime',None),s.prompt_enhancer,s.auto_tests,s.chat_tests,s.image_tests,s.tts_tests,s.stt)):raise ValueError('Zuerst laufende Downloads, Builds und Tests abschließen oder abbrechen.')
self.cancel.clear();self.job=dict(id=secrets.token_hex(16),state='running',phase='Wiederherstellung vorbereiten',completed=0,total=len(doc['models'])+len(data['services'])+5,items=[],started_at=time.time(),theme=doc.get('theme','deck')) self.cancel.clear();self.job=dict(id=secrets.token_hex(16),state='running',phase='Wiederherstellung vorbereiten',completed=0,total=len(doc['models'])+len(data['services'])+5,items=[],started_at=time.time(),theme=doc.get('theme','deck'))
self._save();threading.Thread(target=self._restore,args=(copy.deepcopy(doc),data['services'],data['restore_credentials']),daemon=True).start();self.plan=None self._save();threading.Thread(target=self._restore,args=(copy.deepcopy(doc),data['services'],data['restore_credentials']),daemon=True).start();self.plan=None
return self.status() return self.status()
@@ -221,6 +228,8 @@ class Backup:
self._phase('Vorhandene Bild-/Videolaufzeit prüfen oder installieren');s.image_runtime.start();self._wait(s.image_runtime.status) self._phase('Vorhandene Bild-/Videolaufzeit prüfen oder installieren');s.image_runtime.start();self._wait(s.image_runtime.status)
if r['tts'] and not s.tts_runtime.status()['installed']: if r['tts'] and not s.tts_runtime.status()['installed']:
self._phase('TTS-Laufzeit und Zusatzdateien installieren');s.tts_runtime.start();self._wait(s.tts_runtime.status) self._phase('TTS-Laufzeit und Zusatzdateien installieren');s.tts_runtime.start();self._wait(s.tts_runtime.status)
if r.get('audio_cpp') and not s.audio_cpp_runtime.status()['installed']:
self._phase('audio.cpp-Laufzeit installieren');s.audio_cpp_runtime.start();self._wait(s.audio_cpp_runtime.status)
if r.get('ltx_original') and not s.ltx_original_runtime.status()['installed']: if r.get('ltx_original') and not s.ltx_original_runtime.status()['installed']:
self._phase('Originale LTX-Laufzeit installieren');s.ltx_original_runtime.start();self._wait(s.ltx_original_runtime.status) self._phase('Originale LTX-Laufzeit installieren');s.ltx_original_runtime.start();self._wait(s.ltx_original_runtime.status)
targets=r['llama_builds']+([r['llama']] if r['llama'] and r['llama'] not in r['llama_builds'] else []) targets=r['llama_builds']+([r['llama']] if r['llama'] and r['llama'] not in r['llama_builds'] else [])
@@ -360,5 +369,7 @@ class Backup:
with self.lock: with self.lock:
self.cancel.set() self.cancel.set()
if self.busy(): if self.busy():
self.server.catalog.stop();self.server.runtime.stop();self.server.image_runtime.stop();self.server.tts_runtime.stop();self.server.ltx_original_runtime.stop();self.server.prompt_enhancer.stop() self.server.catalog.stop();self.server.runtime.stop();self.server.image_runtime.stop();self.server.tts_runtime.stop();self.server.ltx_original_runtime.stop();
if getattr(self.server,'audio_cpp_runtime',None):self.server.audio_cpp_runtime.stop()
self.server.prompt_enhancer.stop()
return {'cancellation_requested':True} return {'cancellation_requested':True}
+1 -1
View File
@@ -9,7 +9,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(' · ')}. 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>`; 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.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>`;
} }
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>
+1 -1
View File
@@ -197,7 +197,7 @@ class Catalog:
files=[] files=[]
for f in data.get('siblings',[]): for f in data.get('siblings',[]):
name=f['rfilename'];p=PurePosixPath(name) name=f['rfilename'];p=PurePosixPath(name)
if p.is_absolute() or '..' in p.parts or p.suffix.lower() not in ('.gguf','.safetensors','.json'):continue if p.is_absolute() or '..' in p.parts or p.suffix.lower() not in ('.gguf','.safetensors','.json','.tiktoken'):continue
size=f.get('size',f.get('lfs',{}).get('size')) size=f.get('size',f.get('lfs',{}).get('size'))
if not isinstance(size,int) or size<1:continue if not isinstance(size,int) or size<1:continue
files.append(dict(name=name,size=size,sha256=f.get('lfs',{}).get('sha256'))) files.append(dict(name=name,size=size,sha256=f.get('lfs',{}).get('sha256')))
+2 -2
View File
@@ -16,8 +16,8 @@ WORKDIR /app
COPY deploy/image-requirements.lock /app/deploy/image-requirements.lock COPY deploy/image-requirements.lock /app/deploy/image-requirements.lock
COPY deploy/tts-requirements.lock /app/deploy/tts-requirements.lock COPY deploy/tts-requirements.lock /app/deploy/tts-requirements.lock
COPY deploy/ltx-original-requirements.lock /app/deploy/ltx-original-requirements.lock COPY deploy/ltx-original-requirements.lock /app/deploy/ltx-original-requirements.lock
COPY ltx_original_runtime.py ltx_original_entry.py video_original.py backup.py backup_codec.py audio_policy.py prompt_enhancer.py prompt_enhancer_worker.py api_compat.py stt.py execution_setup.py tts_runtime.py tts_test.py tts_worker.py auto_test.py chat_test.py endpoint.py inference.py docker_support.py image_encoder_node.py image_runtime.py image_test.py image_upload.py profiles.py capacity.py server.py runtime.py video.py video_proxy.py video_comfy.py video_comfy_node.py video_comfy_proxy.py catalog.py hub_auth.py auth.py collect_hardware.py dashboard_data.py dashboard_history.py /app/ COPY audio_cpp_runtime.py ltx_original_runtime.py ltx_original_entry.py video_original.py backup.py backup_codec.py audio_policy.py prompt_enhancer.py prompt_enhancer_worker.py api_compat.py stt.py execution_setup.py tts_runtime.py tts_test.py tts_worker.py auto_test.py chat_test.py endpoint.py inference.py docker_support.py image_encoder_node.py image_runtime.py image_test.py image_upload.py profiles.py capacity.py server.py runtime.py video.py video_proxy.py video_comfy.py video_comfy_node.py video_comfy_proxy.py catalog.py hub_auth.py auth.py collect_hardware.py dashboard_data.py dashboard_history.py /app/
COPY ltx-original-ui.js backup-ui.js video-ui.js stt-ui.js tts-ui.js auto-test-ui.js chat-test-ui.js endpoint-ui.js docker-ui.js image-test-ui.js profiles-ui.js dashboard-ui.js index.html app.js studio.js runtime-ui.js catalog-ui.js style.css dashboard.css themes.css login.html login.js access-ui.js network-ui.js /app/ COPY audio-cpp-ui.js ltx-original-ui.js backup-ui.js video-ui.js stt-ui.js tts-ui.js auto-test-ui.js chat-test-ui.js endpoint-ui.js docker-ui.js image-test-ui.js profiles-ui.js dashboard-ui.js index.html app.js studio.js runtime-ui.js catalog-ui.js style.css dashboard.css themes.css login.html login.js access-ui.js network-ui.js /app/
COPY deploy/docker_backup.py /app/deploy/docker_backup.py COPY deploy/docker_backup.py /app/deploy/docker_backup.py
COPY network/__init__.py network/client.py network/config.py network/rpc.py /app/network/ COPY network/__init__.py network/client.py network/config.py network/rpc.py /app/network/
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 HOME=/tmp \ ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 HOME=/tmp \
+2 -2
View File
@@ -6,8 +6,8 @@ WORKDIR /app
COPY deploy/image-requirements.lock /app/deploy/image-requirements.lock COPY deploy/image-requirements.lock /app/deploy/image-requirements.lock
COPY deploy/tts-requirements.lock /app/deploy/tts-requirements.lock COPY deploy/tts-requirements.lock /app/deploy/tts-requirements.lock
COPY deploy/ltx-original-requirements.lock /app/deploy/ltx-original-requirements.lock COPY deploy/ltx-original-requirements.lock /app/deploy/ltx-original-requirements.lock
COPY ltx_original_runtime.py ltx_original_entry.py video_original.py backup.py backup_codec.py audio_policy.py prompt_enhancer.py prompt_enhancer_worker.py api_compat.py stt.py execution_setup.py tts_runtime.py tts_test.py tts_worker.py auto_test.py chat_test.py endpoint.py inference.py docker_support.py image_encoder_node.py image_runtime.py image_test.py image_upload.py profiles.py capacity.py server.py runtime.py video.py video_proxy.py video_comfy.py video_comfy_node.py video_comfy_proxy.py catalog.py hub_auth.py auth.py collect_hardware.py dashboard_data.py dashboard_history.py /app/ COPY audio_cpp_runtime.py ltx_original_runtime.py ltx_original_entry.py video_original.py backup.py backup_codec.py audio_policy.py prompt_enhancer.py prompt_enhancer_worker.py api_compat.py stt.py execution_setup.py tts_runtime.py tts_test.py tts_worker.py auto_test.py chat_test.py endpoint.py inference.py docker_support.py image_encoder_node.py image_runtime.py image_test.py image_upload.py profiles.py capacity.py server.py runtime.py video.py video_proxy.py video_comfy.py video_comfy_node.py video_comfy_proxy.py catalog.py hub_auth.py auth.py collect_hardware.py dashboard_data.py dashboard_history.py /app/
COPY ltx-original-ui.js backup-ui.js video-ui.js stt-ui.js tts-ui.js auto-test-ui.js chat-test-ui.js endpoint-ui.js docker-ui.js image-test-ui.js profiles-ui.js dashboard-ui.js index.html app.js studio.js runtime-ui.js catalog-ui.js style.css dashboard.css themes.css login.html login.js access-ui.js network-ui.js /app/ COPY audio-cpp-ui.js ltx-original-ui.js backup-ui.js video-ui.js stt-ui.js tts-ui.js auto-test-ui.js chat-test-ui.js endpoint-ui.js docker-ui.js image-test-ui.js profiles-ui.js dashboard-ui.js index.html app.js studio.js runtime-ui.js catalog-ui.js style.css dashboard.css themes.css login.html login.js access-ui.js network-ui.js /app/
COPY deploy/docker_backup.py /app/deploy/docker_backup.py COPY deploy/docker_backup.py /app/deploy/docker_backup.py
COPY network/__init__.py network/client.py network/config.py network/rpc.py /app/network/ COPY network/__init__.py network/client.py network/config.py network/rpc.py /app/network/
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 HOME=/tmp \ ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 HOME=/tmp \
+3 -3
View File
@@ -14,7 +14,7 @@ import urllib.request
ROOT = Path(__file__).resolve().parent.parent ROOT = Path(__file__).resolve().parent.parent
LABEL = 'de.casaderoll.athena-deck.standalone' LABEL = 'de.casaderoll.athena-deck.standalone'
FILES = ['ltx_original_runtime.py','ltx_original_entry.py','video_original.py','ltx-original-ui.js','deploy/ltx-original-requirements.lock','backup.py','backup_codec.py','backup-ui.js','deploy/docker_backup.py','deploy/swarm-ui/Dockerfile','deploy/swarm-ui/patch-source.py','deploy/swarm-ui/proxy.py','deploy/swarm-ui/entrypoint.py','deploy/Dockerfile.app-update','video_comfy.py','video_comfy_node.py','video_comfy_proxy.py','prompt_enhancer.py','prompt_enhancer_worker.py','image_upload.py','audio_policy.py','deploy/tts-requirements.lock','stt.py','stt-ui.js','api_compat.py','execution_setup.py','tts_runtime.py','tts_test.py','tts_worker.py','tts-ui.js','auto_test.py','auto-test-ui.js','chat_test.py','chat-test-ui.js','endpoint.py','inference.py','endpoint-ui.js','docker_support.py','docker-ui.js','deploy/docker_helper.py','deploy/setup_docker_helper.py','image_encoder_node.py','image_runtime.py','image_test.py','image-test-ui.js','profiles.py','profiles-ui.js','capacity.py','runtime.py','runtime-ui.js','video.py','video_proxy.py','video-ui.js','catalog.py','hub_auth.py','catalog-ui.js','server.py','auth.py','collect_hardware.py','dashboard_data.py','dashboard_history.py','dashboard-ui.js','dashboard.css','themes.css','deploy/import_dashboard_history.py','index.html','app.js','studio.js','style.css','login.html','login.js','access-ui.js','network-ui.js','network/__init__.py','network/client.py','network/config.py','network/rpc.py','deploy/Dockerfile','deploy/image-requirements.lock'] FILES = ['audio_cpp_runtime.py','audio-cpp-ui.js','ltx_original_runtime.py','ltx_original_entry.py','video_original.py','ltx-original-ui.js','deploy/ltx-original-requirements.lock','backup.py','backup_codec.py','backup-ui.js','deploy/docker_backup.py','deploy/swarm-ui/Dockerfile','deploy/swarm-ui/patch-source.py','deploy/swarm-ui/proxy.py','deploy/swarm-ui/entrypoint.py','deploy/Dockerfile.app-update','video_comfy.py','video_comfy_node.py','video_comfy_proxy.py','prompt_enhancer.py','prompt_enhancer_worker.py','image_upload.py','audio_policy.py','deploy/tts-requirements.lock','stt.py','stt-ui.js','api_compat.py','execution_setup.py','tts_runtime.py','tts_test.py','tts_worker.py','tts-ui.js','auto_test.py','auto-test-ui.js','chat_test.py','chat-test-ui.js','endpoint.py','inference.py','endpoint-ui.js','docker_support.py','docker-ui.js','deploy/docker_helper.py','deploy/setup_docker_helper.py','image_encoder_node.py','image_runtime.py','image_test.py','image-test-ui.js','profiles.py','profiles-ui.js','capacity.py','runtime.py','runtime-ui.js','video.py','video_proxy.py','video-ui.js','catalog.py','hub_auth.py','catalog-ui.js','server.py','auth.py','collect_hardware.py','dashboard_data.py','dashboard_history.py','dashboard-ui.js','dashboard.css','themes.css','deploy/import_dashboard_history.py','index.html','app.js','studio.js','style.css','login.html','login.js','access-ui.js','network-ui.js','network/__init__.py','network/client.py','network/config.py','network/rpc.py','deploy/Dockerfile','deploy/image-requirements.lock']
def run(*args, check=True, interactive=False): def run(*args, check=True, interactive=False):
@@ -95,7 +95,7 @@ def launch(config):
base=Path(config['base']) base=Path(config['base'])
args=['docker','run','-d','--name',config['name'],'--label',LABEL+'='+str(base), args=['docker','run','-d','--name',config['name'],'--label',LABEL+'='+str(base),
'--restart','unless-stopped','--read-only','--cap-drop','ALL','--security-opt','no-new-privileges:true', '--restart','unless-stopped','--read-only','--cap-drop','ALL','--security-opt','no-new-privileges:true',
'--pids-limit','256' if config.get('image_runtime') else '128','--memory','32g' if config.get('image_runtime') else '8g','--cpus','2', '--tmpfs','/tmp:rw,nosuid,nodev,size=256m', '--pids-limit','256' if config.get('image_runtime') else '128','--memory','32g' if config.get('image_runtime') else '8g','--cpus','6', '--tmpfs','/tmp:rw,nosuid,nodev,size=256m',
'-v',str(base/'state')+':/var/lib/deck:rw','--mount','type=bind,src=/proc,dst=/host/proc,readonly','-e','DECK_HOST_PROC=/host/proc','-e','DECK_ALLOWED_HOSTS=127.0.0.1:'+str(config['port'])+',localhost:'+str(config['port']), '-v',str(base/'state')+':/var/lib/deck:rw','--mount','type=bind,src=/proc,dst=/host/proc,readonly','-e','DECK_HOST_PROC=/host/proc','-e','DECK_ALLOWED_HOSTS=127.0.0.1:'+str(config['port'])+',localhost:'+str(config['port']),
'-p','127.0.0.1:'+str(config['port'])+':8108', '-p','127.0.0.1:'+str(config['port'])+':8108',
'--health-cmd', 'python3 -c "import urllib.request,json; assert json.load(urllib.request.urlopen(\'http://127.0.0.1:8108/api/v1/auth/status\',timeout=3))[\'initialized\']"', '--health-cmd', 'python3 -c "import urllib.request,json; assert json.load(urllib.request.urlopen(\'http://127.0.0.1:8108/api/v1/auth/status\',timeout=3))[\'initialized\']"',
@@ -204,7 +204,7 @@ def update(base, rollback=False, force=False, api_ports=None, reuse_runtime=Fals
backup_name=config['name']+'-previous' backup_name=config['name']+'-previous'
if inspect(backup_name):raise RuntimeError('Rückfall-Containername belegt. Keine Änderung ausgeführt.') if inspect(backup_name):raise RuntimeError('Rückfall-Containername belegt. Keine Änderung ausgeführt.')
stamp=str(time.time_ns()) stamp=str(time.time_ns())
shutil.copytree(base/'state',base/'backups'/stamp,ignore=shutil.ignore_patterns('ltx-original-runtime','original-work','tts-runtime','tts-tests','models','runtime','runtime-verification','image-tests','image-verification','image-verification-*','image-runtime','video-runtime','video-verification','video-verification-*','comfy-work','router-verification-*')) shutil.copytree(base/'state',base/'backups'/stamp,ignore=shutil.ignore_patterns('audio-cpp-runtime','ltx-original-runtime','original-work','tts-runtime','tts-tests','models','runtime','runtime-verification','image-tests','image-verification','image-verification-*','image-runtime','video-runtime','video-verification','video-verification-*','comfy-work','router-verification-*'))
was_running=previous['State']['Running'] was_running=previous['State']['Running']
if was_running:run('docker','stop','--time','15',config['name']) if was_running:run('docker','stop','--time','15',config['name'])
run('docker','rename',config['name'],backup_name) run('docker','rename',config['name'],backup_name)
+17
View File
@@ -0,0 +1,17 @@
# Laufzeiten erkennen und vorbereiten
In **Bibliothek → Modell** und bei der Einrichtung von Profilen zeigt Deck die erkannte Modellfamilie, den Ausführungsstatus und **Mögliche Laufzeiten**. Das gilt für Sprache/Chat, Bild, TTS, STT, Musik, Stimme und Video.
Eine vorhandene Laufzeit wird wiederverwendet. Ein fehlender Worker oder Workflow bleibt ausdrücklich sichtbar. Unbekannte Familien erhalten nur gekennzeichnete Recherchehinweise aus ihrer Kategorie; das ist keine Kompatibilitätszusage. Es wird nichts automatisch installiert.
Über den Link „Laufzeit herunterladen / bereitmachen“ gelangt man zur jeweiligen Laufzeitseite. Dort wird die Installation bewusst gestartet. Ohne gepflegten Installer zeigt Deck dies an, statt eine Installation vorzutäuschen.
## YuE2 GGUF / audio.cpp
`audio-cpp/Yue2-3B-GGUF`, Hauptdateien `yue2-3b-*.gguf`, werden als YuE2 erkannt. Die VAE-Dateien sind Komponenten, keine eigenständigen Modelle. Das Paket braucht zusätzlich VAE, Modell-/Generierungskonfiguration, VAE-Konfiguration und Qwen-Tokenizer; `.tiktoken` ist herunterladbar.
**Einstellungen → Laufzeiten → audio.cpp** bietet einen isolierten CUDA-Build aus `0xShug0/audio.cpp`, Version v0.8.1, Commit `f2b4937306daa25f5c78520f3c626ed31495a37a`. Voraussetzung: Debian/Linux, Git, CMake, g++, nvcc, nvidia-smi und mindestens 6 GiB frei. GPU-Architekturen werden gelesen; gebaut wird mit sechs Jobs. Keine Host-Pakete, Treiber oder bestehenden Laufzeiten werden verändert. Fehlende Voraussetzungen werden gemeldet.
Installation liegt im Deck-Zustandsverzeichnis unter `audio-cpp-runtime`. Status, Abbruch und eigene Build-Ausgabe sind in der GUI verfügbar. Der CLI-Start wird ohne Modell geprüft. Dieser Build lädt **keine Gewichte**. Der ausführbare Deck-Musikworker und die Ladypoly-WebUI-Anbindung fehlen noch; sie sind separat umzusetzen. Auch andere audio.cpp-Familien brauchen geprüfte modellspezifische Anbindungen.
Interne Admin-API: `GET /api/v1/audio-cpp-runtime`, `GET /api/v1/audio-cpp-runtime/log`, `POST /api/v1/audio-cpp-runtime/install` und `/cancel` (JSON `{}`, vorhandene Sitzung und CSRF-Header). Backup/Restore enthält das gepinnte Laufzeitrezept und baut es bei Bedarf neu; keine Binärdateien im Backup.
+34 -1
View File
@@ -4,7 +4,7 @@ from stt import supported as stt_supported
from profiles import image_recipe, video_recipe from profiles import image_recipe, video_recipe
from tts_runtime import REPO, REVISION from tts_runtime import REPO, REVISION
def assess(model, installed, profile=None): def _assess(model, installed, profile=None):
kind=model.get('kind'); filename=model.get('file',''); repo=model.get('repo','') kind=model.get('kind'); filename=model.get('file',''); repo=model.get('repo','')
meta=model.get('runtime_metadata') if isinstance(model.get('runtime_metadata'),dict) else {} meta=model.get('runtime_metadata') if isinstance(model.get('runtime_metadata'),dict) else {}
architecture=meta.get('architecture') if isinstance(meta.get('architecture'),str) else None architecture=meta.get('architecture') if isinstance(meta.get('architecture'),str) else None
@@ -46,3 +46,36 @@ def assess(model, installed, profile=None):
elif result['installed']: elif result['installed']:
result.update(state='profile_incomplete',label='Profil prüfen – Voraussetzungen fehlen') result.update(state='profile_incomplete',label='Profil prüfen – Voraussetzungen fehlen')
return result return result
def assess(model, installed, profile=None):
result=_assess(model,installed,profile)
result['candidates']=[]
if result['state']=='component':return result
kind=model.get('kind');repo=model.get('repo','').lower();name=model.get('file','').lower()
meta=model.get('runtime_metadata') or {};family=str(meta.get('architecture') or '').lower()
def add(id,label,key,route,reason,maintained=True):
result['candidates'].append(dict(id=id,label=label,installed=bool(installed.get(key)),route=route,installation_available=maintained,evidence=reason))
if result.get('runtime'):
add('current',result['runtime'],kind,result['route'],'Vorhandene Deck-Zuordnung')
if kind=='music' and repo=='audio-cpp/yue2-3b-gguf' and name.startswith('yue2-3b-') and name.endswith('.gguf'):
result.update(family='YuE2',evidence='audio-cpp/Yue2-3B-GGUF · Hauptmodell',state='adapter_missing',label='YuE2 erkannt · Deck-Worker fehlt',message='Passende Laufzeit: audio.cpp. Sie kann einmalig heruntergeladen und gebaut werden. Zur Generierung fehlen noch der Deck-Musikworker und die Ladypoly-Anbindung. Installation allein schaltet dieses Modell nicht frei.',components=['YuE2-VAE GGUF','Modell-/Generierungskonfiguration, VAE-Konfiguration und Qwen-Tokenizer (sidecars)'])
add('audio_cpp','audio.cpp','audio_cpp','#audio-cpp-runtime','Geprüfte GGUF-Quelle für die YuE2-Modellfamilie')
elif kind=='music' and ('yue2' in repo or 'yue2' in family):
result.update(family='YuE2',evidence='Repository-/Architekturhinweis; Ausführung ungeprüft')
add('yue2','YuE2 Python / PyTorch','yue2',None,'Originale YuE2-Pipeline; Originalgewichte erforderlich',False)
if kind in ('audio','stt','music','voice') and not result['candidates']:
if 'audio-cpp/' in repo and name.endswith('.gguf'):
add('audio_cpp','audio.cpp','audio_cpp','#audio-cpp-runtime','audio-cpp-Repository; konkrete Familie und Zusatzdateien noch prüfen')
elif meta.get('library_name')=='transformers':
add('transformers','Hugging Face Transformers / PyTorch','transformers',None,'Hub nennt Transformers; modellabhängiger Worker erforderlich',False)
if kind=='video' and not result['candidates'] and meta.get('library_name')=='diffusers':
add('diffusers','Diffusers / PyTorch','diffusers',None,'Hub nennt Diffusers; passende Video-Pipeline noch prüfen',False)
if not result['candidates']:
# Category-level options are explicit research hints, never compatibility claims.
options={'chat':('llama.cpp','chat','#runtime'), 'image':('ComfyUI','image','#image-runtime'), 'video':('ComfyUI','image','#image-runtime'), 'audio':('audio.cpp','audio_cpp','#audio-cpp-runtime'), 'stt':('audio.cpp','audio_cpp','#audio-cpp-runtime'), 'music':('audio.cpp','audio_cpp','#audio-cpp-runtime'), 'voice':('audio.cpp','audio_cpp','#audio-cpp-runtime')}
if kind in options:
label,key,route=options[kind]
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.'
return result
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Athena Deck</title><link rel="stylesheet" href="/style.css"><link rel="stylesheet" href="/dashboard.css"><link rel="stylesheet" href="/themes.css"><script src="/network-ui.js" defer></script><script src="/access-ui.js" defer></script><script src="/catalog-ui.js" defer></script><script src="/runtime-ui.js" defer></script><script src="/profiles-ui.js" defer></script><script src="/auto-test-ui.js" defer></script><script src="/chat-test-ui.js" defer></script><script src="/image-test-ui.js" defer></script><script src="/docker-ui.js" defer></script><script src="/stt-ui.js" defer></script><script src="/tts-ui.js" defer></script><script src="/studio.js" defer></script><script src="/ltx-original-ui.js" defer></script><script src="/video-ui.js" defer></script><script src="/endpoint-ui.js" defer></script><script src="/dashboard-ui.js" defer></script><script src="/backup-ui.js" defer></script><script src="/app.js" defer></script></head><body><aside><a class="brand" href="#dashboard"><span>Α</span> ATHENA <b>DECK</b></a><p class="eyebrow">WORKSPACE / ATHENA</p><nav aria-label="Hauptnavigation"><a href="#dashboard">Dashboard <span>01</span></a><a href="#home">Übersicht <span>02</span></a><a href="#chat">Sprachmodelle / Chat <span>02</span></a><a href="#image">Bildgenerierung <span>03</span></a><p class="nav-heading">AUDIO</p><a class="nav-sub" href="#audio">Text-to-Speech (TTS)</a><a class="nav-sub" href="#stt">Spracherkennung (STT)</a><a class="nav-sub" href="#music">Musik</a><a class="nav-sub" href="#voice">Stimme / Voice</a><a href="#video">Video <span>05</span></a><a href="#services">Weitere Dienste <span>06</span></a><p class="nav-heading">EINSTELLUNGEN</p><a href="#backup">Backup &amp; Restore <span>↗</span></a><a href="#appearance">Darstellung <span>↗</span></a><a href="#runtimes">Laufzeiten <span>↗</span></a><a class="nav-sub" href="#runtime">llama.cpp</a><a class="nav-sub" href="#image-runtime">Bildgenerierung</a><a class="nav-sub" href="#stt-runtime">Spracherkennung</a><a class="nav-sub" href="#tts-runtime">Text-to-Speech</a><a class="nav-sub" href="#ltx-original-runtime">LTX Original</a><a class="nav-sub" href="#docker-runtime">Docker</a><a href="#endpoint">Endpunkt &amp; Profile <span>↗</span></a><a href="#network">Netzwerk <span>↗</span></a><a href="#access">Zugang &amp; API <span>↗</span></a></nav><footer><i></i> Isolierte Entwicklungsumgebung<p>Eigener Modell-Router<br>Hardware · nur lesend</p></footer></aside><main><header><span>ATHENA CONTROL SURFACE</span><span class="pill">v0.7 · Router</span></header><div id="view"></div><p id="error" role="alert"></p></main></body></html> <!doctype html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Athena Deck</title><link rel="stylesheet" href="/style.css"><link rel="stylesheet" href="/dashboard.css"><link rel="stylesheet" href="/themes.css"><script src="/network-ui.js" defer></script><script src="/access-ui.js" defer></script><script src="/catalog-ui.js" defer></script><script src="/runtime-ui.js" defer></script><script src="/profiles-ui.js" defer></script><script src="/auto-test-ui.js" defer></script><script src="/chat-test-ui.js" defer></script><script src="/image-test-ui.js" defer></script><script src="/docker-ui.js" defer></script><script src="/stt-ui.js" defer></script><script src="/tts-ui.js" defer></script><script src="/studio.js" defer></script><script src="/audio-cpp-ui.js" defer></script><script src="/ltx-original-ui.js" defer></script><script src="/video-ui.js" defer></script><script src="/endpoint-ui.js" defer></script><script src="/dashboard-ui.js" defer></script><script src="/backup-ui.js" defer></script><script src="/app.js" defer></script></head><body><aside><a class="brand" href="#dashboard"><span>Α</span> ATHENA <b>DECK</b></a><p class="eyebrow">WORKSPACE / ATHENA</p><nav aria-label="Hauptnavigation"><a href="#dashboard">Dashboard <span>01</span></a><a href="#home">Übersicht <span>02</span></a><a href="#chat">Sprachmodelle / Chat <span>02</span></a><a href="#image">Bildgenerierung <span>03</span></a><p class="nav-heading">AUDIO</p><a class="nav-sub" href="#audio">Text-to-Speech (TTS)</a><a class="nav-sub" href="#stt">Spracherkennung (STT)</a><a class="nav-sub" href="#music">Musik</a><a class="nav-sub" href="#voice">Stimme / Voice</a><a href="#video">Video <span>05</span></a><a href="#services">Weitere Dienste <span>06</span></a><p class="nav-heading">EINSTELLUNGEN</p><a href="#backup">Backup &amp; Restore <span>↗</span></a><a href="#appearance">Darstellung <span>↗</span></a><a href="#runtimes">Laufzeiten <span>↗</span></a><a class="nav-sub" href="#runtime">llama.cpp</a><a class="nav-sub" href="#image-runtime">Bildgenerierung</a><a class="nav-sub" href="#stt-runtime">Spracherkennung</a><a class="nav-sub" href="#tts-runtime">Text-to-Speech</a><a class="nav-sub" href="#audio-cpp-runtime">audio.cpp</a><a class="nav-sub" href="#ltx-original-runtime">LTX Original</a><a class="nav-sub" href="#docker-runtime">Docker</a><a href="#endpoint">Endpunkt &amp; Profile <span>↗</span></a><a href="#network">Netzwerk <span>↗</span></a><a href="#access">Zugang &amp; API <span>↗</span></a></nav><footer><i></i> Isolierte Entwicklungsumgebung<p>Eigener Modell-Router<br>Hardware · nur lesend</p></footer></aside><main><header><span>ATHENA CONTROL SURFACE</span><span class="pill">v0.7 · Router</span></header><div id="view"></div><p id="error" role="alert"></p></main></body></html>
+3 -2
View File
@@ -14,6 +14,7 @@ DESKTOP_REV='68cd86c15e5fd25f56229ea63c0dbcb0338f7812'
LTX_REV='d151147788a9284cca791edc6ce898007e727fe6' LTX_REV='d151147788a9284cca791edc6ce898007e727fe6'
class LTXOriginalRuntime: class LTXOriginalRuntime:
name="LTX Original"
def __init__(self,root,image_runtime): def __init__(self,root,image_runtime):
self.root=Path(root);self.image_runtime=image_runtime;self.lock=threading.RLock();self.cancel=threading.Event();self.process=None;self.worker=None;self.job=None self.root=Path(root);self.image_runtime=image_runtime;self.lock=threading.RLock();self.cancel=threading.Event();self.process=None;self.worker=None;self.job=None
path=self.root/'installation.json' path=self.root/'installation.json'
@@ -37,8 +38,8 @@ class LTXOriginalRuntime:
with self.lock:self.job['phase']=phase;self._save() with self.lock:self.job['phase']=phase;self._save()
def start(self): def start(self):
with self.lock: with self.lock:
if self.job and self.job['state']=='running':raise ValueError('LTX-Installation läuft bereits.') if self.job and self.job['state']=='running':raise ValueError(self.name+'-Installation läuft bereits.')
if self.status()['installed']:raise ValueError('LTX Original ist bereits installiert.') if self.status()['installed']:raise ValueError(self.name+' ist bereits installiert.')
if sys.platform!='linux' or not shutil.which('git'):raise ValueError('Debian/Linux mit Git und Python venv erforderlich.') if sys.platform!='linux' or not shutil.which('git'):raise ValueError('Debian/Linux mit Git und Python venv erforderlich.')
self.root.mkdir(parents=True,exist_ok=True,mode=0o700) self.root.mkdir(parents=True,exist_ok=True,mode=0o700)
need=self.status()['required_disk_gib'] need=self.status()['required_disk_gib']
+12 -2
View File
@@ -115,6 +115,8 @@ class Server(ThreadingHTTPServer):
self.auto_tests.stop();self.chat_tests.stop();self.image_tests.stop();self.prompt_enhancer.stop_preview();self.tts_tests.stop();self.worker.stop() self.auto_tests.stop();self.chat_tests.stop();self.image_tests.stop();self.prompt_enhancer.stop_preview();self.tts_tests.stop();self.worker.stop()
from video_original import VideoRuntimes from video_original import VideoRuntimes
from ltx_original_runtime import LTXOriginalRuntime from ltx_original_runtime import LTXOriginalRuntime
from audio_cpp_runtime import AudioCppRuntime
self.audio_cpp_runtime=AudioCppRuntime(self.catalog.root.parent/'audio-cpp-runtime')
self.ltx_original_runtime=LTXOriginalRuntime(self.catalog.root.parent/'ltx-original-runtime',self.image_runtime) self.ltx_original_runtime=LTXOriginalRuntime(self.catalog.root.parent/'ltx-original-runtime',self.image_runtime)
self.video=VideoRuntimes(self.scheduler,stop_gpu_work,self.docker,self.catalog.root.parent/'video',self.catalog,self.profiles,self.image_runtime,original_runtime=self.ltx_original_runtime) self.video=VideoRuntimes(self.scheduler,stop_gpu_work,self.docker,self.catalog.root.parent/'video',self.catalog,self.profiles,self.image_runtime,original_runtime=self.ltx_original_runtime)
self.endpoint.video=self.video self.endpoint.video=self.video
@@ -278,7 +280,7 @@ class Handler(BaseHTTPRequestHandler):
return self.headers.get('Host') in allowed return self.headers.get('Host') in allowed
def execution_setup(self, model, profile=None): def execution_setup(self, model, profile=None):
installed={'stt':bool(self.server.runtime.status().get('active')),'chat':bool(self.server.runtime.status().get('active')),'image':self.server.image_runtime.status().get('installed',False),'audio':self.server.tts_runtime.status().get('installed',False),'video':self.server.video.status()['runtime']['installed']} installed={'stt':bool(self.server.runtime.status().get('active')),'chat':bool(self.server.runtime.status().get('active')),'image':self.server.image_runtime.status().get('installed',False),'audio':self.server.tts_runtime.status().get('installed',False),'video':self.server.video.status()['runtime']['installed'],'audio_cpp':self.server.audio_cpp_runtime.status()['installed']}
return execution_assess(model,installed,profile) return execution_assess(model,installed,profile)
def do_GET(self): def do_GET(self):
@@ -292,7 +294,7 @@ class Handler(BaseHTTPRequestHandler):
return self.respond({'error':'Anmeldung erforderlich.'},401) return self.respond({'error':'Anmeldung erforderlich.'},401)
if not self.authenticated() and self.path == '/': if not self.authenticated() and self.path == '/':
return self.respond((ROOT/'login.html').read_bytes(), mime='text/html; charset=utf-8') return self.respond((ROOT/'login.html').read_bytes(), mime='text/html; charset=utf-8')
routes = {'/ltx-original-ui.js':('ltx-original-ui.js','text/javascript'),'/backup-ui.js':('backup-ui.js','text/javascript'),'/dashboard-ui.js':('dashboard-ui.js','text/javascript'),'/dashboard.css':('dashboard.css','text/css'),'/themes.css':('themes.css','text/css'),'/video-ui.js':('video-ui.js','text/javascript'),'/stt-ui.js':('stt-ui.js','text/javascript'),'/tts-ui.js':('tts-ui.js','text/javascript'),'/auto-test-ui.js':('auto-test-ui.js','text/javascript'),'/chat-test-ui.js':('chat-test-ui.js','text/javascript'),'/endpoint-ui.js':('endpoint-ui.js','text/javascript'),'/docker-ui.js': ('docker-ui.js','text/javascript'), '/': ('index.html', 'text/html; charset=utf-8'), '/app.js': ('app.js', 'text/javascript'), '/style.css': ('style.css', 'text/css'), '/network-ui.js': ('network-ui.js', 'text/javascript'), '/access-ui.js': ('access-ui.js', 'text/javascript'), '/studio.js': ('studio.js', 'text/javascript'), '/catalog-ui.js': ('catalog-ui.js','text/javascript'), '/runtime-ui.js': ('runtime-ui.js','text/javascript'), '/profiles-ui.js': ('profiles-ui.js','text/javascript'), '/image-test-ui.js': ('image-test-ui.js','text/javascript')} routes = {'/audio-cpp-ui.js':('audio-cpp-ui.js','text/javascript'),'/ltx-original-ui.js':('ltx-original-ui.js','text/javascript'),'/backup-ui.js':('backup-ui.js','text/javascript'),'/dashboard-ui.js':('dashboard-ui.js','text/javascript'),'/dashboard.css':('dashboard.css','text/css'),'/themes.css':('themes.css','text/css'),'/video-ui.js':('video-ui.js','text/javascript'),'/stt-ui.js':('stt-ui.js','text/javascript'),'/tts-ui.js':('tts-ui.js','text/javascript'),'/auto-test-ui.js':('auto-test-ui.js','text/javascript'),'/chat-test-ui.js':('chat-test-ui.js','text/javascript'),'/endpoint-ui.js':('endpoint-ui.js','text/javascript'),'/docker-ui.js': ('docker-ui.js','text/javascript'), '/': ('index.html', 'text/html; charset=utf-8'), '/app.js': ('app.js', 'text/javascript'), '/style.css': ('style.css', 'text/css'), '/network-ui.js': ('network-ui.js', 'text/javascript'), '/access-ui.js': ('access-ui.js', 'text/javascript'), '/studio.js': ('studio.js', 'text/javascript'), '/catalog-ui.js': ('catalog-ui.js','text/javascript'), '/runtime-ui.js': ('runtime-ui.js','text/javascript'), '/profiles-ui.js': ('profiles-ui.js','text/javascript'), '/image-test-ui.js': ('image-test-ui.js','text/javascript')}
if self.path in routes: if self.path in routes:
name, mime = routes[self.path] name, mime = routes[self.path]
return self.respond((ROOT/name).read_bytes(), mime=mime) return self.respond((ROOT/name).read_bytes(), mime=mime)
@@ -311,6 +313,8 @@ class Handler(BaseHTTPRequestHandler):
if urlsplit(self.path).path == '/api/v1/tts/audio': if urlsplit(self.path).path == '/api/v1/tts/audio':
try:return self.respond(self.server.tts_tests.audio(parse_qs(urlsplit(self.path).query).get('id',[''])[0]),mime='audio/wav') try:return self.respond(self.server.tts_tests.audio(parse_qs(urlsplit(self.path).query).get('id',[''])[0]),mime='audio/wav')
except (OSError,ValueError):return self.respond({'error':'Audio nicht verfügbar.'},404) except (OSError,ValueError):return self.respond({'error':'Audio nicht verfügbar.'},404)
if self.path == '/api/v1/audio-cpp-runtime':return self.respond(self.server.audio_cpp_runtime.status())
if self.path == '/api/v1/audio-cpp-runtime/log':return self.respond(self.server.audio_cpp_runtime.log())
if self.path == '/api/v1/ltx-original-runtime':return self.respond(self.server.ltx_original_runtime.status()) if self.path == '/api/v1/ltx-original-runtime':return self.respond(self.server.ltx_original_runtime.status())
if self.path == '/api/v1/ltx-original-runtime/log':return self.respond(self.server.ltx_original_runtime.log()) if self.path == '/api/v1/ltx-original-runtime/log':return self.respond(self.server.ltx_original_runtime.log())
if self.path == '/api/v1/video':return self.respond(self.server.video.status()) if self.path == '/api/v1/video':return self.respond(self.server.video.status())
@@ -482,6 +486,11 @@ class Handler(BaseHTTPRequestHandler):
if data:raise ValueError('Keine Parameter erwartet.') if data:raise ValueError('Keine Parameter erwartet.')
return self.respond(self.server.chat_tests.stop() if self.path.endswith('/cancel') else self.server.chat_tests.unload()) return self.respond(self.server.chat_tests.stop() if self.path.endswith('/cancel') else self.server.chat_tests.unload())
except ValueError as exc:return self.respond({'error':str(exc)},400) except ValueError as exc:return self.respond({'error':str(exc)},400)
if self.path in ('/api/v1/audio-cpp-runtime/install','/api/v1/audio-cpp-runtime/cancel'):
try:
if self.read_json():raise ValueError('Keine Installationsparameter erwartet.')
return self.respond(self.server.audio_cpp_runtime.stop() if self.path.endswith('/cancel') else self.server.audio_cpp_runtime.start())
except (OSError,ValueError) as exc:return self.respond({'error':str(exc)},400)
if self.path in ('/api/v1/ltx-original-runtime/install','/api/v1/ltx-original-runtime/cancel'): if self.path in ('/api/v1/ltx-original-runtime/install','/api/v1/ltx-original-runtime/cancel'):
try: try:
if self.read_json():raise ValueError('Keine Installationsparameter erwartet.') if self.read_json():raise ValueError('Keine Installationsparameter erwartet.')
@@ -658,6 +667,7 @@ def main():
server.tts_runtime.stop() server.tts_runtime.stop()
server.image_runtime.stop() server.image_runtime.stop()
server.ltx_original_runtime.stop() server.ltx_original_runtime.stop()
server.audio_cpp_runtime.stop()
server.prompt_enhancer.stop() server.prompt_enhancer.stop()
server.image_tests.stop() server.image_tests.stop()
server.runtime.stop() server.runtime.stop()
+1 -1
View File
@@ -27,7 +27,7 @@ const Studio=(()=>{
else if(section!=='running')CatalogUI.bind(page,section==='library',section==='downloads'); else if(section!=='running')CatalogUI.bind(page,section==='library',section==='downloads');
document.querySelectorAll('[data-section]').forEach(b=>b.onclick=()=>{section=b.dataset.section;render(page,true);}); document.querySelectorAll('[data-section]').forEach(b=>b.onclick=()=>{section=b.dataset.section;render(page,true);});
} }
function runtimeOverview(){return `<div class="kicker">EINSTELLUNGEN</div><h1>Laufzeiten</h1><p>Laufzeiten werden einmal eingerichtet und für kompatible Modelle wiederverwendet. Entdecken, Bibliothek und Profile zeigen den passenden Einrichtungsweg; unbekannte Modelle werden als noch nicht unterstützt gekennzeichnet. Textencoder und VAE werden separat in der Modellbibliothek verwaltet und einem Profil zugeordnet.</p><div class="grid"><section class="card"><h2>Spracherkennung</h2><p>Qwen3-ASR mit llama.cpp und Audio-Projektor auf der CPU.</p><a class="link" href="#stt-runtime">STT einrichten →</a></section><section class="card"><h2>Text-to-Speech</h2><p>Qwen3-TTS mit CUDA und eingebauten Stimmen einrichten.</p><a class="link" href="#tts-runtime">TTS einrichten →</a></section><section class="card"><h2>llama.cpp</h2><p>Sprachmodelle · CPU / CUDA. Versionen herunterladen, bauen und verwalten.</p><a class="link" href="#runtime">llama.cpp öffnen →</a></section><section class="card"><h2>Bildgenerierung</h2><p>Eigene Bildlaufzeit und modellabhängige Zusatzkomponenten.</p><a class="link" href="#image-runtime">Bildlaufzeit öffnen →</a></section><section class="card"><h2>LTX Original</h2><p>Originales LTX-Desktop-Backend mit nativer LTX-API für LTX DeskWEB.</p><a class="link" href="#ltx-original-runtime">LTX Original einrichten →</a></section><section class="card"><h2>Docker</h2><p>Optionale Container-Umgebung für zusätzliche Oberflächen und Studios.</p><a class="link" href="#docker-runtime">Docker öffnen →</a></section></div>`;} function runtimeOverview(){return `<div class="kicker">EINSTELLUNGEN</div><h1>Laufzeiten</h1><p>Laufzeiten werden einmal eingerichtet und für kompatible Modelle wiederverwendet. Entdecken, Bibliothek und Profile zeigen den passenden Einrichtungsweg; unbekannte Modelle werden als noch nicht unterstützt gekennzeichnet. Textencoder und VAE werden separat in der Modellbibliothek verwaltet und einem Profil zugeordnet.</p><div class="grid"><section class="card"><h2>Spracherkennung</h2><p>Qwen3-ASR mit llama.cpp und Audio-Projektor auf der CPU.</p><a class="link" href="#stt-runtime">STT einrichten →</a></section><section class="card"><h2>Text-to-Speech</h2><p>Qwen3-TTS mit CUDA und eingebauten Stimmen einrichten.</p><a class="link" href="#tts-runtime">TTS einrichten →</a></section><section class="card"><h2>llama.cpp</h2><p>Sprachmodelle · CPU / CUDA. Versionen herunterladen, bauen und verwalten.</p><a class="link" href="#runtime">llama.cpp öffnen →</a></section><section class="card"><h2>Bildgenerierung</h2><p>Eigene Bildlaufzeit und modellabhängige Zusatzkomponenten.</p><a class="link" href="#image-runtime">Bildlaufzeit öffnen →</a></section><section class="card"><h2>audio.cpp</h2><p>Gemeinsame Audio-Laufzeit; Deck-Worker werden separat angebunden.</p><a class="link" href="#audio-cpp-runtime">audio.cpp herunterladen / bereitmachen →</a></section><section class="card"><h2>LTX Original</h2><p>Originales LTX-Desktop-Backend mit nativer LTX-API für LTX DeskWEB.</p><a class="link" href="#ltx-original-runtime">LTX Original einrichten →</a></section><section class="card"><h2>Docker</h2><p>Optionale Container-Umgebung für zusätzliche Oberflächen und Studios.</p><a class="link" href="#docker-runtime">Docker öffnen →</a></section></div>`;}
function imageRuntime(){return `<div class="kicker">EINSTELLUNGEN / LAUFZEITEN</div><h1>Bildgenerierung</h1><section class="card"><h2>ComfyUI + ComfyUI-GGUF</h2><p>Eigene Bildlaufzeit für Qwen-Image-2.1 GGUF und das unterstützte FLUX.2-Klein-9B-Modell. Die Umgebung kann über den Deck-Installer mitgeliefert oder hier separat installiert werden. Modelle laden erst bei einem Testauftrag.</p><p>Textencoder läuft auf der RTX 3060, Diffusionsmodell und VAE auf der RTX 5080. Bei großen FLUX-Gewichten wird zusätzlich System-RAM genutzt. Beide GPUs müssen frei sein. Pro Test wird ein eigener Prozess gestartet und anschließend beendet.</p><div id="image-runtime-install"><p>Installationsstatus wird geprüft …</p></div><a class="link" href="#image">Bildgenerierung → Testen öffnen</a></section>${ImageTestUI.html(true)}`;} function imageRuntime(){return `<div class="kicker">EINSTELLUNGEN / LAUFZEITEN</div><h1>Bildgenerierung</h1><section class="card"><h2>ComfyUI + ComfyUI-GGUF</h2><p>Eigene Bildlaufzeit für Qwen-Image-2.1 GGUF und das unterstützte FLUX.2-Klein-9B-Modell. Die Umgebung kann über den Deck-Installer mitgeliefert oder hier separat installiert werden. Modelle laden erst bei einem Testauftrag.</p><p>Textencoder läuft auf der RTX 3060, Diffusionsmodell und VAE auf der RTX 5080. Bei großen FLUX-Gewichten wird zusätzlich System-RAM genutzt. Beide GPUs müssen frei sein. Pro Test wird ein eigener Prozess gestartet und anschließend beendet.</p><div id="image-runtime-install"><p>Installationsstatus wird geprüft …</p></div><a class="link" href="#image">Bildgenerierung → Testen öffnen</a></section>${ImageTestUI.html(true)}`;}
function openProfile(page,modelId){render(page,true,modelId);} function openProfile(page,modelId){render(page,true,modelId);}
return {render,openProfile}; return {render,openProfile};
+44
View File
@@ -0,0 +1,44 @@
import json
from pathlib import Path
import tempfile
import time
import unittest
from unittest.mock import patch
from audio_cpp_runtime import AudioCppRuntime,REVISION
class AudioCppTests(unittest.TestCase):
def test_marker_cannot_escape_root(self):
with tempfile.TemporaryDirectory() as root:
Path(root,'active.json').write_text(json.dumps({'id':'../../outside'}))
self.assertFalse(AudioCppRuntime(root).status()['installed'])
def test_build_failure_is_not_published(self):
with tempfile.TemporaryDirectory() as root:
runtime=AudioCppRuntime(root);runtime.job=dict(id='a'*32,state='running')
with patch.object(runtime,'_command',side_effect=RuntimeError('synthetic failure')):runtime._run()
self.assertEqual(runtime.job['state'],'failed')
self.assertFalse(runtime.status()['installed'])
self.assertFalse(Path(root,'a'*32,'ready').exists())
def test_publish_only_after_cli_smoke_and_spec(self):
with tempfile.TemporaryDirectory() as root:
runtime=AudioCppRuntime(root);runtime.job=dict(id='a'*32,state='running');commands=[]
def command(args,env):
commands.append([str(a) for a in args])
base=Path(root,'a'*32)
if str(args[0])=='cmake' and '--build' in args:
(base/'source/model_specs').mkdir(parents=True)
(base/'source/model_specs/yue2.json').write_text('{}')
(base/'build/bin').mkdir(parents=True)
(base/'build/bin/audiocpp_cli').touch()
with patch.object(runtime,'_command',side_effect=command),patch('subprocess.check_output',return_value='12.0\n8.6\n'):runtime._run()
self.assertEqual(runtime.job['state'],'complete')
self.assertTrue(runtime.status()['installed'])
self.assertFalse(runtime.status()['adapter_ready'])
self.assertIn('-DCMAKE_CUDA_ARCHITECTURES=120;86',commands[3])
self.assertEqual(commands[-1][-1],'--help')
self.assertEqual(Path(root,'a'*32,'ready').read_text(),REVISION)
def test_missing_prerequisites_dont_start_thread(self):
with tempfile.TemporaryDirectory() as root:
runtime=AudioCppRuntime(root)
with patch('audio_cpp_runtime.sys.platform','linux'),patch('audio_cpp_runtime.shutil.which',return_value=None):
with self.assertRaisesRegex(ValueError,'Build-Werkzeuge fehlen'):runtime.start()
self.assertIsNone(runtime.worker)
+18
View File
@@ -33,3 +33,21 @@ class ExecutionSetupTests(unittest.TestCase):
self.assertEqual(result['family'],'NewImagePipeline') self.assertEqual(result['family'],'NewImagePipeline')
self.assertIn('Workflow',result['message']) self.assertIn('Workflow',result['message'])
self.assertEqual(assess(dict(model,kind='audio'),{'image':True})['state'],'unsupported') self.assertEqual(assess(dict(model,kind='audio'),{'image':True})['state'],'unsupported')
def test_every_category_has_explicit_unverified_candidate(self):
for kind in ('chat','image','audio','stt','music','voice','video'):
result=self.check(kind,'unknown/family','weights.safetensors')
self.assertTrue(result['candidates'])
self.assertIn('NICHT bestätigt',result['candidates'][0]['evidence'])
self.assertNotEqual(result['state'],'ready')
def test_yue2_gguf_recognition_does_not_claim_worker(self):
result=self.check('music','audio-cpp/Yue2-3B-GGUF','yue2-3b-q8_0.gguf',{'audio_cpp':True})
self.assertEqual(result['family'],'YuE2')
self.assertEqual(result['state'],'adapter_missing')
self.assertTrue(result['candidates'][0]['installed'])
self.assertEqual(result['candidates'][0]['route'],'#audio-cpp-runtime')
self.assertTrue(result['components'])
def test_component_never_offers_runtime_or_profile(self):
result=self.check('music','audio-cpp/Yue2-3B-GGUF','yue2-vae-f16.gguf')
self.assertEqual(result['state'],'component')
self.assertEqual(result['candidates'],[])