Add separation profiles with activation and explicit GPU assignment

This commit is contained in:
Mikei386 committed 2026-10-01 20:43:56 +02:00
1 parent 356abbeb9d
commit 932284a831
8 files changed
+82 -8

No files matched your search

+35 -3
View File
@@ -1,14 +1,46 @@
"""Bounded WAV tests with coordinated GPU ownership and disposable input."""
import io,json,os,signal,subprocess,threading,time,uuid,wave
import io,json,os,re,signal,subprocess,threading,time,uuid,wave
from pathlib import Path
from image_test import probe
from separator_runtime import MODELS
def validate_profiles(settings):
if not isinstance(settings,dict) or set(settings)-set(MODELS):raise ValueError('Unbekanntes Audio-Trennprofil.')
for value in settings.values():
if not isinstance(value,dict) or set(value)!={'enabled','device'} or type(value['enabled']) is not bool or not isinstance(value['device'],str) or value['device']!='auto' and not re.fullmatch(r'GPU-[a-fA-F0-9-]{16,64}',value['device']):raise ValueError('Ungültiges Audio-Trennprofil.')
return settings
class SeparatorTests:
def __init__(self,root,runtime,scheduler):
self.root=Path(root);self.runtime=runtime;self.scheduler=scheduler;self.lock=threading.RLock();self.job=None;self.process=None;self.cancel=threading.Event();self.thread=None
def profiles(self):
path=self.root.parent/'separator-profiles.json'
settings=validate_profiles(json.loads(path.read_text())) if path.exists() else {}
return {name:dict(settings.get(name,dict(enabled=True,device='auto'))) for name in MODELS}
def configure(self,data):
if not isinstance(data,dict) or set(data)!={'model','enabled','device'} or data['model'] not in MODELS:raise ValueError('Modell, Aktivierung und GPU erforderlich.')
validate_profiles({data['model']:{k:data[k] for k in ('enabled','device')}})
if data['device']!='auto' and not any(g['uuid']==data['device'] for g in probe()):raise ValueError('Gewählte GPU ist nicht vorhanden.')
with self.lock:
if self.job and self.job['state']=='running':raise ValueError('Zuerst den laufenden Trennauftrag beenden.')
settings=self.profiles();settings[data['model']]={k:data[k] for k in ('enabled','device')}
from auth import atomic_write
atomic_write(self.root.parent/'separator-profiles.json',settings)
return self.status()
def status(self):
with self.lock:return dict(job=dict(self.job) if self.job else None)
try:devices=[dict(uuid=g['uuid'],name=g['name']) for g in probe()];hardware_error=None
except (OSError,ValueError,subprocess.SubprocessError):devices=[];hardware_error='GPU-Erkennung derzeit nicht verfügbar.'
with self.lock:return dict(job=dict(self.job) if self.job else None,profiles=self.profiles(),devices=devices,hardware_error=hardware_error)
def select_gpu(self,model):
settings=self.profiles()[model]
if not settings['enabled']:raise ValueError('Dieses Audio-Trennprofil ist deaktiviert. Unter Profile aktivieren.')
available=[g for g in probe() if not g['processes'] and g['free_mib']>4000]
if settings['device']!='auto':
gpu=next((g for g in available if g['uuid']==settings['device']),None)
if not gpu:raise ValueError('Die gewählte GPU ist nicht ausreichend frei. GPU-Auswahl im Profil prüfen; kein stiller Wechsel.')
return gpu
return next((g for term in ('RTX 5080','RTX 3060','') for g in available if term in g['name']),None)
def start(self,model,audio,target=None,full=False):
if isinstance(model,str) and model in MODELS and not self.profiles()[model]['enabled']:raise ValueError('Dieses Audio-Trennprofil ist deaktiviert. Unter Profile aktivieren.')
if full and self.scheduler.gpu_mode!='separator':raise ValueError('Bitte unter Steuerung Audio-Trennung starten.')
targets={'vocals':'model_bs_roformer_ep_317_sdr_12.9755.ckpt','drums':'htdemucs_ft.yaml','bass':'htdemucs_ft.yaml','guitar':'htdemucs_6s.yaml','piano':'htdemucs_6s.yaml','other':'htdemucs_6s.yaml'}
if target is not None and targets.get(target)!=model:raise ValueError('Zielspur und Modellpaket passen nicht zusammen.')
@@ -27,7 +59,7 @@ class SeparatorTests:
release=None;directory=self.root/self.job['id'];state='failed';phase='Trennung fehlgeschlagen.'
try:
release=self.scheduler.image_reservation(kind='separator')
gpu=next((g for term in ('RTX 3060','RTX 5080') for g in probe() if term in g['name'] and not g['processes'] and g['free_mib']>4000),None)
gpu=self.select_gpu(model)
if not gpu:raise ValueError('Keine ausreichend freie GPU. Fremde Dienste werden nicht beendet.')
directory.mkdir(mode=0o700);(directory/'input.wav').write_bytes(audio);diagnostics=(directory/'worker.tmp').open('wb')
with self.lock: