window.ProfilesUI=(()=>{ const e=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); const labels={video_device:'Videopipeline',text_encoder_device:'Textencoder-Gerät',gpu_reserve_mode:'GPU-Reservemodus',gpu_reserve_mib:'GPU-Reserve (MiB)',vision_projector:'Vision-Projektor',vision_device:'Projektor-Gerät',gpu_offload:'GPU-Ausführung',mtp:'MTP',mtp_tokens:'MTP: maximale Draft-Token',mtp_min_p:'MTP: Mindestwahrscheinlichkeit',temperature:'Temperature',top_p:'Top-p',top_k:'Top-k',gpu_devices:'GPUs (in Reihenfolge)',split_mode:'GPU-Split',tensor_split:'GPU-Verteilung',context:'Gesamtes Kontextbudget (Token)',slots:'Parallele Slots',threads:'CPU-Threads',batch:'Batch-Größe',ubatch:'Microbatch-Größe',width:'Breite (Pixel)',height:'Höhe (Pixel)',steps:'Schritte',seed:'Seed (−1 = zufällig)',guidance:'Guidance / CFG',speed:'Sprechgeschwindigkeit',frames:'Bildanzahl',fps:'Bilder pro Sekunde'}; const html=()=>'

Deine Profile

Auf Athena gespeichert. Eine Modelldatei kann mehrere Profile mit unterschiedlichen Parametern haben.

'; async function api(path,data){const r=await fetch('/api/v1/'+path,data?{method:'POST',headers:{'Content-Type':'application/json','X-Athena-Deck':'1'},body:JSON.stringify(data)}:{});const v=await r.json();if(!r.ok)throw Error(v.error||'Anfrage fehlgeschlagen');return v;} function bind(kind,modelId){ const root=document.querySelector('#live-profiles'),el=id=>root.querySelector('#'+id);let rows=[],models=[],schema={},enabled=[],gpus=[],projectors=[],entries=[],panelSequence=0; const message=t=>{if(root.isConnected)el('profile-message').textContent=t;}; async function load(){try{const [p,c,h,ep]=await Promise.all([api('profiles'),api('catalog'),['chat','video'].includes(kind)?api('hardware').catch(()=>({gpus:[]})):Promise.resolve({gpus:[]}),api('endpoint')]);entries=c.entries;projectors=c.entries.filter(x=>x.role==='vision_projector'&&x.file.endsWith('.gguf'));enabled=ep.profiles.filter(x=>x.enabled).map(x=>x.id);gpus=h.gpus||[];if(!root.isConnected)return;rows=p.profiles.filter(x=>x.kind===kind);models=c.entries.filter(x=>x.kind===kind&&x.profile_eligible===true);schema=p.schemas[kind];el('profile-new').disabled=!models.length; el('profile-list').innerHTML=rows.map(p=>`
GESPEICHERT AUF ATHENA

${e(p.name)}

${e(p.model?.file||'Modelldatei nicht verfügbar')}

${Object.entries(p.parameters).map(([k,v])=>`${e(labels[k]||k)}: ${e(['video_device','text_encoder_device'].includes(k)?({auto:'Automatisch · RTX 5080 bevorzugt',same:'Wie Videopipeline'}[v]||gpus.find(g=>g.uuid===v)?.name||v):k==='gpu_reserve_mode'?({auto:'Automatisch',manual:'Manuell',none:'Keine zusätzliche Reserve'}[v]||v):k==='gpu_reserve_mib'?(Object.entries(v).map(([id,n])=>`${gpus.find(g=>g.uuid===id)?.name||id}: ${n} MiB`).join(', ')||'gemäß Reservemodus'):k==='gpu_devices'?(v.length?v.map((id,i)=>`CUDA${i}: ${gpus.find(g=>g.uuid===id)?.name||id}`).join(', '):'Automatisch'):Array.isArray(v)?(v.join(', ')||'Automatisch'):v)}`).join(' · ')}

${kind==='image'?`

Prompt-Aufwerter: Text-zu-Bild ${p.prompt_enhancer?.t2i?'an':'aus'} · Bildbearbeitung ${p.prompt_enhancer?.i2i?'an':'aus'} · Gerät ${e(p.prompt_enhancer?.device||'auto')}

`:''}${CatalogUI.executionHTML(p.execution)}
${p.runnable?(kind==='video'?'Laufzeit bereit · Video-Modus in Übersicht aktivieren':'Laufzeit bereit · lädt bei Anfrage'):'Was fehlt zur Ausführung?'}${p.blockers.map(t=>`

${e(t)}

`).join('')}
${['image','video'].includes(kind)?``:''}${kind==='image'?``:''}
`).join('')||`

Noch keine Profile

${models.length?'Wähle eine heruntergeladene Modelldatei und lege dein erstes Profil an.':'Lade zuerst eine Gewichtsdatei unter Entdecken herunter.'}

`; el('profile-list').insertAdjacentHTML('afterbegin',`

${kind==='image'?'Der feste API-Name athena-image verwendet das hier freigegebene Bildprofil und dessen Auflösung. Höchstens ein Bildprofil ist am API-Endpunkt freigegeben. Ein neuer Haken ersetzt die bisherige Auswahl.':['audio','stt'].includes(kind)?'Eingerichtete Sprachprofile können am API-Endpunkt freigegeben werden.':kind==='chat'?'Mehrere Sprachmodellprofile können gleichzeitig am API-Endpunkt freigegeben sein.':kind==='video'?'Genau ein Videoprofil kann am API-Endpunkt freigegeben sein. Ein neuer Haken ersetzt die bisherige Auswahl. Die Übersicht lädt dieses Profil beim Wechsel auf Video; API-Name: athena-video. Zum Ändern der Freigabe zuerst auf LLM wechseln.':'Für diesen Bereich ist noch keine ausführbare Laufzeit am Endpunkt angebunden.'} Freigegeben bedeutet nicht bereits in den GPU-Speicher geladen.

`); root.querySelectorAll('[data-publish]').forEach(b=>b.onchange=async()=>{const checked=b.checked;root.querySelectorAll('[data-publish]').forEach(x=>x.disabled=true);try{await api('endpoint/profile',{id:b.dataset.publish,enabled:checked});await load();message(kind==='video'?'Video-Freigabe gespeichert. Die Übersicht lädt dieses Profil beim Wechsel auf Video.':'Endpunkt-Freigabe gespeichert. Geladen wird erst bei einer API-Anfrage.');}catch(error){await load();message(error.message);}}); root.querySelectorAll('[data-delete]').forEach(b=>b.onclick=async()=>{const p=rows.find(p=>p.id===b.dataset.delete);if(!confirm(`Profil „${p.name}“ löschen? Modelldateien und Komponenten bleiben in der Bibliothek.`))return;b.disabled=true;try{await api('profiles/delete',{id:p.id,revision:p.revision});panelSequence++;el('profile-editor').replaceChildren();await load();message('Profil gelöscht. Modelldateien und Komponenten bleiben erhalten.');}catch(error){message(error.message);b.disabled=false;}}); root.querySelectorAll('[data-components]').forEach(b=>b.onclick=()=>components(rows.find(p=>p.id===b.dataset.components))); root.querySelectorAll('[data-enhancers]').forEach(b=>b.onclick=()=>enhancers(rows.find(p=>p.id===b.dataset.enhancers))); root.querySelectorAll('[data-edit]').forEach(b=>b.onclick=()=>editor(rows.find(p=>p.id===b.dataset.edit))); root.querySelectorAll('[data-copy]').forEach(b=>b.onclick=()=>{const p=rows.find(p=>p.id===b.dataset.copy);editor({...p,id:null,revision:0,name:p.name.slice(0,55)+'-kopie'});}); }catch(error){message(error.message);}} async function enhancers(profile){ const sequence=++panelSequence,selected=profile.prompt_enhancer||{t2i:null,i2i:null,device:'auto'}; el('profile-editor').innerHTML=`

Prompt-Aufwerter · ${e(profile.name)}

Optional pro Bildprofil. Text-zu-Bild und Bearbeitung mit Referenzbildern verwenden getrennte offizielle Qwen-Modelle. Andere Bildfamilien können sie nur nach eigener Prüfung sinnvoll nutzen.

Der Aufwerter wird vor der Bildpipeline geladen und danach vollständig entladen. Beide PE-Modelle benötigen je etwa 18,8 GB Download. Die Wahl einer einzelnen GPU garantiert keinen rein dortigen Betrieb; CPU-Auslagerung kann erforderlich sein. Die Bildauflösung bleibt im Profil gespeichert; ein Seitenverhältnis aus der PE-Antwort ist nur ein Vorschlag.

Offizielle Modelle installieren

`; const note=el('enhancer-message');el('enhancer-close').onclick=()=>el('profile-editor').replaceChildren(); el('enhancer-form').onsubmit=async event=>{event.preventDefault();const form=event.target,values=new FormData(form),button=form.querySelector('button');button.disabled=true;try{await api('profiles/prompt-enhancer',{id:profile.id,revision:profile.revision,prompt_enhancer:{t2i:values.get('t2i')||null,i2i:values.get('i2i')||null,device:values.get('device')}});if(!root.isConnected)return;el('profile-editor').replaceChildren();await load();message('Prompt-Aufwerter im Bildprofil gespeichert.');}catch(error){note.textContent=error.message;}finally{button.disabled=false;}}; async function refresh(){ try{const state=await api('prompt-enhancers');if(!root.isConnected||sequence!==panelSequence)return; el('enhancer-models').innerHTML=['t2i','i2i'].map(task=>{const item=state.models[task];return `

${task.toUpperCase()} · ${item.installed?'Installiert · '+e(item.revision?.slice(0,12)):'Nicht installiert'} · Modellkarte und Lizenz ↗

`;}).join('')+`

${e(state.job?.phase||'Kein Download läuft.')}

${state.job?.state==='running'?'':''}`; el('enhancer-models').querySelectorAll('[data-install]').forEach(button=>button.onclick=async()=>{button.disabled=true;try{await api('prompt-enhancers/install',{task:button.dataset.install});note.textContent='Download gestartet. Die Bildmodelle bleiben unberührt.';}catch(error){note.textContent=error.message;}}); el('enhancer-cancel')?.addEventListener('click',async()=>{try{await api('prompt-enhancers/cancel',{});}catch(error){note.textContent=error.message;}}); }catch(error){if(root.isConnected&&sequence===panelSequence)note.textContent=error.message;} finally{if(root.isConnected&&sequence===panelSequence)setTimeout(refresh,3000);} } refresh();el('profile-editor').scrollIntoView({behavior:'smooth',block:'start'}); } async function components(profile){ const sequence=++panelSequence; el('profile-editor').innerHTML='

Komponenten werden geprüft …

'; try{ const data=await api('profiles/components?'+new URLSearchParams({model_id:profile.model_id}));if(!root.isConnected||sequence!==panelSequence)return; el('profile-editor').innerHTML=`

Komponenten · ${e(profile.name)}

${e(data.message)}

${data.supported?`

Quelle: Modellkarte und Lizenz ↗ · ${e(data.license||'Lizenz nicht angegeben')}

${profile.kind==='image'?'Bildlaufzeit → · ':'Videolaufzeit · '}${e(data.runtime)} · ${data.runtime_installed?'installiert':'noch nicht eingerichtet'}

${data.requirements.map(r=>`

${e(r.label)}

${r.candidates.map((f,i)=>`

${e(f.name)} · ${(f.size/1024**3).toFixed(2)} GiB · Version ${e(f.revision.slice(0,12))}

${f.gated&&!f.auth_configured?'

Hugging-Face-Zugang unter Einstellungen → Zugang hinterlegen und Modellfreigabe prüfen.

':''}
`).join('')||'

Keine passende Quelldatei verfügbar.

'}
`).join('')}

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.

`:''}

`; const missing=data.requirements?.filter(r=>!r.available.length)||[]; 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; try{for(const r of missing){const f=r.candidates.find(f=>!f.gated||f.auth_configured);if(!f)throw Error('Keine herunterladbare Datei; HF-Zugang oder Quellversion prüfen für '+r.label);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=error.message;b.disabled=false;} };} el('components-close').onclick=()=>el('profile-editor').replaceChildren(); el('components-refresh')?.addEventListener('click',()=>components(profile)); el('components-form')?.addEventListener('submit',async event=>{event.preventDefault();const button=el('components-save');button.disabled=true;try{await api('profiles/components',{id:profile.id,revision:profile.revision,components:Object.fromEntries(new FormData(event.target))});if(!root.isConnected)return;el('profile-editor').replaceChildren();await load();message('Komponenten dem Profil zugeordnet. Kein Modell gestartet.');}catch(error){if(el('components-message'))el('components-message').textContent=error.message;}finally{button.disabled=false;}}); root.querySelectorAll('[data-component-download]').forEach(b=>b.onclick=async()=>{const [role,index]=b.dataset.componentDownload.split(':'),f=data.requirements.find(r=>r.role===role).candidates[Number(index)];b.disabled=true;try{await api('catalog/download',{repo:f.repo,filename:f.name,revision:f.revision,kind:profile.kind});if(el('components-message'))el('components-message').textContent='Download gestartet. Fortschritt und Abbruch findest du unter Downloads. Nach Abschluss hier die Bibliothek aktualisieren.';}catch(error){if(el('components-message'))el('components-message').textContent=error.message;b.disabled=false;}}); }catch(error){message(error.message);el('profile-editor').innerHTML='

Komponenten konnten nicht geladen werden. Öffne die Ansicht erneut.

';} } function gpuEditor(params){ const selected=params.gpu_devices||[],available=[...gpus];for(const uuid of selected)if(!available.some(g=>g.uuid===uuid))available.push({uuid,name:'Derzeit nicht verfügbar'}); const options=(value,empty)=>``+available.map(g=>``).join(''); return `

GPU-Auswahl und Verteilung

Manuelle Reserven gelten für die jeweils ausgewählte GPU. Ohne Reserve entfällt nur der zusätzliche GPU-Puffer; Modell, KV-Cache und die konservative Projektor-Prognose bleiben berücksichtigt. OOM bleibt möglich. Für einen Abbruch statt CPU-Auslagerung „Vollständig auf GPU“ wählen. Die Reihenfolge legt die logischen CUDA-Geräte des Profils fest. Für Medium: RTX 5080 zuerst, RTX 3060 danach, Layer-Split und 85,15. Leere Verteilung bedeutet automatisch. Dies ist keine Zusage, dass Modell und Kontext in den verfügbaren Speicher passen.

`; } function bindProjectorDiscovery(){ const form=el('profile-form'),box=el('projector-discovery'),select=form.elements.vision_projector; let sequence=0; const live=n=>box.isConnected&&n===sequence; const mmproj=f=>/(?:^|[\/_-])mmproj(?:[\/_.-]|$)/i.test(f.name)&&f.name.toLowerCase().endsWith('.gguf'); async function refreshLibrary(){ const c=await api('catalog');if(!box.isConnected)return; const selected=select.value;entries=c.entries;projectors=c.entries.filter(x=>x.role==='vision_projector'&&x.file.endsWith('.gguf')); select.innerHTML=''+projectors.map(x=>``).join('');select.value=selected; return c; } function showFiles(detail,target,n){ const files=detail.files.filter(mmproj); target.innerHTML=`

Quelle: ${e(detail.repo)} · Lizenz: ${e(detail.license||'nicht angegeben')}

`; if(!files.length){target.insertAdjacentHTML('beforeend','

Keine mmproj-GGUF-Datei in diesem Repository gefunden.

');return;} files.forEach(f=>{ const row=document.createElement('div');row.className='note'; row.innerHTML=`

${e(f.name)} · ${(f.size/1024**2).toFixed(1)} MiB

`;target.append(row); const button=row.querySelector('button'),status=row.querySelector('span'); function update(){const entry=projectors.find(x=>x.repo===detail.repo&&x.revision===detail.revision&&x.file===f.name);button.textContent=entry?'Im Profil auswählen':'Herunterladen';button.disabled=!!detail.gated&&!detail.auth_configured;return entry;} update();button.onclick=async()=>{button.disabled=true;try{ await refreshLibrary();if(!live(n))return;const entry=update(); if(entry){select.value=entry.id;status.textContent=' Ausgewählt. Profil speichern, um die Zuordnung zu übernehmen.';return;} await api('catalog/download',{repo:detail.repo,filename:f.name,revision:detail.revision,kind:'chat'}); status.textContent=' Download läuft. Fortschritt unter Downloads; danach Bibliothek aktualisieren und hier auswählen.'; }catch(error){status.textContent=error.message;}finally{if(live(n))update();}}; }); } async function check(){ const n=++sequence,model=models.find(x=>x.id===form.elements.model_id.value); box.innerHTML='

Eigenes Modell-Repository wird auf Projektoren geprüft …

'; try{ let own=projectors.filter(x=>x.repo===model.repo); const detail=await api('catalog/files?'+new URLSearchParams({repo:model.repo}));if(!live(n))return; const hasOwn=own.length>0||detail.files.some(mmproj); box.innerHTML=`

${hasOwn?'Projektor aus dem Modell-Repository':'Externen Vision-Projektor hinzufügen'}

${hasOwn?'Das Modell-Repository bietet einen eigenen Projektor. Wähle eine passende Variante.':'Im Modell-Repository wurde keine separate mmproj-Datei gefunden. Suche im Repository des ursprünglichen Vision-Modells.'}

Nur für kompatible Vision-Architekturen. Ein Projektor macht aus einem reinen Textmodell kein Vision-Modell. Modellkarte und Modellversion prüfen.

`; const status=box.querySelector('[data-status]'),target=box.querySelector('[data-files]'); box.querySelector('[data-refresh]').onclick=async()=>{try{await refreshLibrary();if(live(n)){status.textContent='Bibliothek aktualisiert. Projektor oben auswählen und Profil speichern.';}}catch(error){status.textContent=error.message;}}; if(hasOwn){showFiles(detail,target,n);return;} const search=box.querySelector('[data-search]');search.innerHTML='
'; let searchSequence=0; search.querySelector('button').onclick=async()=>{ const current=++searchSequence;status.textContent='Repositories werden gesucht …';target.replaceChildren(); try{const result=await api('catalog/search?'+new URLSearchParams({q:search.querySelector('input').value.trim(),kind:'chat',purpose:'projector'}));if(!live(n)||current!==searchSequence)return; const results=search.querySelector('[data-results]');results.replaceChildren();status.textContent=result.models.length?'Repository auswählen; angezeigt werden anschließend ausschließlich Projektordateien.':'Keine Treffer. Versuche den direkten Repository-Link.'; result.models.forEach(m=>{const b=document.createElement('button');b.type='button';b.className='secondary';b.textContent=m.repo;results.append(b);b.onclick=async()=>{const request=++searchSequence;status.textContent='Projektordateien werden geprüft …';try{const d=await api('catalog/files?'+new URLSearchParams({repo:m.repo}));if(!live(n)||request!==searchSequence)return;showFiles(d,target,n);status.textContent='Kompatibilität anhand der verlinkten Modellkarte prüfen.';}catch(error){if(live(n)&&request===searchSequence)status.textContent=error.message;}};}); }catch(error){if(live(n)&¤t===searchSequence)status.textContent=error.message;} }; }catch(error){if(live(n)){box.innerHTML='

Eigener Projektor konnte nicht geprüft werden. Externe Suche bleibt bis zur Prüfung geschlossen.

';box.querySelector('p').append(' '+error.message);box.querySelector('button').onclick=check;}} } form.elements.model_id.addEventListener('change',check);check(); } function videoDevices(data){ const params=data.parameters; const options=(key,fallback,title)=>``; return `

Komponenten und Geräte

${options('video_device','auto','Videomodell / Diffusion')}${options('text_encoder_device','same','Textencoder')}

Video-VAE, Audio-VAE und Spatial-Upsampler laufen derzeit auf der GPU der Videopipeline. Unabhängige Zuordnung dieser Komponenten ist noch nicht unterstützt.

Disk-Streaming bleibt aktiv. Dateigröße ist keine VRAM-Messung: Aktivierungen, Auflösung und Bildanzahl benötigen zusätzlichen Speicher. Änderungen gelten nach erneutem Wechsel in den Videomodus.

`; } function bindVideoDevices(data){ const form=el('profile-form'),target=el('video-device-advice'); function refresh(){ const main=models.find(m=>m.id===form.elements.model_id.value),encoder=main?.id===data.model_id?entries.find(m=>m.id===data.components?.text_encoder):null; const primary=form.elements.video_device.value==='auto'?(gpus.find(g=>g.name.includes('5080'))||[...gpus].sort((a,b)=>(b.total_mib||0)-(a.total_mib||0))[0]):gpus.find(g=>g.uuid===form.elements.video_device.value); const secondary=form.elements.text_encoder_device.value==='same'?primary:gpus.find(g=>g.uuid===form.elements.text_encoder_device.value); function assessment(label,file,gpu){ if(!file)return `

${label}: Komponente noch nicht zugeordnet; Größenvergleich nicht möglich.

`; if(!gpu)return `

${label}: ausgewählte GPU nicht verfügbar. Hardware-Verbindung prüfen.

`; const size=file.size/1024**3,total=gpu.total_mib/1024,free=Number.isFinite(gpu.used_mib)&&Number.isFinite(gpu.total_mib)?(gpu.total_mib-gpu.used_mib)/1024:null; const valid=Number.isFinite(size)&&size>0&&Number.isFinite(total)&&total>0; return `

${label} → ${e(gpu.name)}
Datei: ${valid?size.toFixed(2)+' GiB':'unbekannt'} · VRAM gesamt: ${Number.isFinite(total)?total.toFixed(1)+' GiB':'unbekannt'} · aktuell frei: ${free===null?'unbekannt':free.toFixed(1)+' GiB'}. ${!valid?'Speichereignung unbekannt.':size>total?'Datei größer als gesamter VRAM: vollständiges Laden in unverändertem BF16 ist nicht vorgesehen; Streaming nötig.':free!==null&&size>free?'Datei größer als aktuell freier VRAM. Laufende Belegung kann sich beim Moduswechsel ändern.':'Dateigröße unter VRAM-Kapazität; tatsächlicher Spitzenbedarf noch nicht gemessen.'}

`; } target.innerHTML='

Empfehlung: RTX 5080 für die Videopipeline. Textencoder zunächst auf derselben GPU lassen; RTX 3060 ist eine optionale getrennte Ausführung mit Streaming, keine Zusage für mehr Geschwindigkeit oder vollständige VRAM-Residenz.

'+assessment('Videomodell',main,primary)+assessment('Textencoder',encoder,secondary)+'

Hardwarewerte sind eine Momentaufnahme beim Öffnen der Profilansicht. Ein erfolgreicher Test mit deiner Auflösung bleibt erforderlich.

'; } for(const key of ['video_device','text_encoder_device','model_id'])form.elements[key].addEventListener('change',refresh); refresh(); } function editor(p=null,id=null){ panelSequence++; if(!models.length)return;const data=p||{id:null,revision:0,name:'',model_id:id||models[0].id,parameters:Object.fromEntries(Object.entries(schema).map(([k,v])=>[k,v[2]]))}; if(!p&&kind==='image'&&models.find(m=>m.id===data.model_id)?.repo==='RunningHubAI/rh-flux.2-klein-9b-fp16-unet-2067980602644717569'){data.parameters.steps=4;data.parameters.guidance=1;} el('profile-editor').innerHTML=`

${data.id?'Profil bearbeiten':'Profil anlegen'}

${Object.entries(schema).map(([k,[min,max,defaultValue]])=>``).join('')}
${kind==='video'?videoDevices(data):''}${kind==='chat'?gpuEditor(data.parameters)+`

Vision-Projektor

Passende mmproj.gguf unter Entdecken herunterladen – auch aus einem anderen Repository als das quantisierte Modell. Modell und Projektor müssen zusammenpassen; der Dateiname allein garantiert keine Kompatibilität. GPU-Ziel muss oben ausgewählt sein. GPU-Projektoren erhalten eine zusätzliche konservative Speicherreserve.

Nur für GGUF-Modelle mit MTP-Gewichten und kompatible Builds. Zusätzlicher Speicherbedarf; Geschwindigkeit hängt vom Modell ab. Referenz Medium: 2 Draft-Token, Mindestwahrscheinlichkeit 0,05, Draft-KV f16.

`:''}

Speichern startet kein Modell. Die Ausführung benötigt eine passende Laufzeit und gegebenenfalls zusätzliche Modelldateien. ${kind==='chat'?'Das Kontextbudget gilt insgesamt für alle Slots.':''}

`; if(kind==='chat'){const threads=el('profile-form').querySelector('[name=threads]').closest('label');const advanced=document.createElement('details');advanced.innerHTML='Erweitert: CPU-Threads

CPU-Threads steuern CPU-Arbeit und Offloading, nicht die Anzahl der CUDA-Rechenkerne.

';advanced.append(threads);el('profile-form').insertBefore(advanced,el('profile-form').querySelector('.note'));} if(kind==='chat'){bindProjectorDiscovery();const f=el('profile-form');const reserveFields=()=>{for(const n of ['reserve_first','reserve_second'])f.elements[n].disabled=f.elements.gpu_reserve_mode.value!=='manual';};f.elements.gpu_reserve_mode.onchange=reserveFields;reserveFields();} if(kind==='video')bindVideoDevices(data); el('profile-close').onclick=()=>el('profile-editor').replaceChildren(); el('profile-form').onsubmit=async event=>{event.preventDefault();const form=event.target,values=new FormData(form),button=form.querySelector('button:not([type=button])');button.disabled=true; try{const parameters=Object.fromEntries(Object.keys(schema).map(k=>[k,Number(values.get(k))]));if(kind==='video'){parameters.video_device=values.get('video_device');parameters.text_encoder_device=values.get('text_encoder_device');}if(kind==='chat'){parameters.vision_projector=values.get('vision_projector')||null;parameters.vision_device=values.get('vision_device');parameters.mtp=values.has('mtp');parameters.gpu_offload=values.get('gpu_offload');if(values.get('gpu_second')&&!values.get('gpu_first'))throw Error('Bitte zuerst die erste GPU auswählen.');if((data.parameters.gpu_devices||[]).length>2)throw Error('Dieses Profil enthält mehr als zwei GPUs; der Editor unterstützt derzeit zwei.');parameters.gpu_devices=[values.get('gpu_first'),values.get('gpu_second')].filter(Boolean);parameters.gpu_reserve_mode=values.get('gpu_reserve_mode');parameters.gpu_reserve_mib=parameters.gpu_reserve_mode==='manual'?Object.fromEntries([['gpu_first','reserve_first'],['gpu_second','reserve_second']].filter(([g])=>values.get(g)).map(([g,r])=>[values.get(g),Number(values.get(r))])):{};parameters.split_mode=values.get('split_mode');const raw=String(values.get('tensor_split')).trim();parameters.tensor_split=raw?raw.split(',').map(x=>x.trim()?Number(x):NaN):[];}await api('profiles/save',{id:data.id,revision:data.revision,name:values.get('name'),kind,model_id:values.get('model_id'),parameters});if(!root.isConnected)return;el('profile-editor').replaceChildren();await load();message('Profil auf Athena gespeichert. Kein Modell gestartet.');} catch(error){if(root.isConnected)el('profile-error').textContent=error.message;}finally{button.disabled=false;} }; el('profile-editor').scrollIntoView({behavior:'smooth',block:'start'}); } el('profile-new').disabled=true;el('profile-new').onclick=()=>editor(); load().then(()=>{if(modelId&&root.isConnected)editor(null,modelId);}); } return {html,bind}; })();