From e1a54e34857d42416d7fe203d038a4e051eb302f Mon Sep 17 00:00:00 2001 From: Mikei386 <44135113+Mikei386@users.noreply.github.com> Date: Mon, 28 Sep 2026 15:24:55 +0200 Subject: [PATCH] Add visual model catalog, editable profile drafts and llama.cpp build settings --- README.md | 14 ++++++--- STUDIO.md | 64 +++++++++++++++++++++++++++++++++++++++ app.js | 1 + index.html | 2 +- network/install_remote.py | 2 +- server.py | 4 +-- studio.js | 58 +++++++++++++++++++++++++++++++++++ style.css | 4 +++ 8 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 STUDIO.md create mode 100644 studio.js diff --git a/README.md b/README.md index bae05f6..532bc7e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,15 @@ -# Athena Deck · Prototyp 0.3 +# Athena Deck · Prototyp 0.4 Eigenständige neue Oberfläche, Python-Standardbibliothek und HTML/CSS/JavaScript. Keine Installation von Python-Paketen oder Frontend-Builds nötig. Python >= 3.10. -## Neu: Zugang und API-Token +## Neu: Modellverwaltung und llama.cpp-Einstellungen + +Katalog, Bibliothek, Profilentwürfe und eine Laufzeit-/Update-Ansicht sind als +bedienbarer GUI-Prototyp vorhanden. Entwürfe bleiben im Browser; Modellstarts, +Downloads und Builds sind deaktiviert. Details: [GUI-Prototyp](STUDIO.md). + +## Zugang und API-Token Beim ersten Öffnen werden Oberflächenkennwort und separater API-Token eingerichtet. Beide sind unter **Zugang & API** änderbar. Die lokale Vorschau ist jetzt ebenfalls @@ -83,8 +89,8 @@ Keine Historie und kein Hintergrund-Collector bei geschlossener Hardware-Seite. - `index.html`, `app.js`, `style.css`: neue responsive Oberfläche. - `test_server.py`: Prozess-Lebenszyklus, Kontrollgrenzen, Hardware-Ausfall. -Sprachmodelle/Chat, Bildgenerierung, Audio und Weitere Dienste sind ausdrücklich -Platzhalter. Keine Downloads, Installation, Presets, Chats oder Modellwechsel. +Sprachmodelle/Chat, Bildgenerierung, Audio und Video besitzen jetzt eine +GUI-Vorschau; Weitere Dienste bleibt Platzhalter. Keine Downloads, Installation, Presets, Chats oder Modellwechsel. Spätere Modellprofile und native llama.cpp-Worker sollten eigene Service-Adapter mit derselben Status-/Start-/Stopp-Trennung erhalten. Noch kein Worker-Registry, Scheduler oder produktionsreifer Worker-Supervisor. Die optionale Server-Instanz diff --git a/STUDIO.md b/STUDIO.md new file mode 100644 index 0000000..0c87ed6 --- /dev/null +++ b/STUDIO.md @@ -0,0 +1,64 @@ +# Modellverwaltung und llama.cpp · GUI-Prototyp 0.4 + +Diese Erweiterung ist ausdrücklich rein optisch und lokal bedienbar. Keine +Modelldateien, Laufzeiten, Downloads, Builds oder produktiven Dienste werden verändert. + +## Ansichten + +- Sprachmodelle/Chat, Bild, Audio und Video: Entdecken, Bibliothek, Profile, Laufend. +- Katalog: Suche/Quellenfilter über gekennzeichnete Beispiele, Detailansicht mit + getrennter Prüfung von Unterstützung, grundsätzlicher Eignung und aktueller + Startmöglichkeit. Keine Online-Suche und keine berechneten Speicherwerte. +- Bibliothek: Beispiel einer einmaligen Modelldatei mit mehreren Profilen; + ausdrücklich keine bestätigte Installation. +- LLM-Profile: drei vorbelegte Beispiele, Anlegen, Bearbeiten, Duplizieren und + Entfernen lokaler Entwürfe. Name, Modellvariante, Kontextbudget, Slots, + GPU-Zuordnung, KV-Cache, gemeinsamer Pool, GPU-Gewichtung, Batch/Microbatch, + CPU-Threads, Flash Attention und vorgesehener Vision-Projektor. +- Einzigartige API-Profilnamen, gültige Zahlenbereiche, Microbatch <= Batch und + nicht ausschließlich nullwertige GPU-Gewichtung werden im Formular geprüft. +- API-Vorschau zeigt die vorgesehenen Profilnamen. `/v1/models` wird dadurch + **nicht** als funktionsfähiger Endpunkt implementiert. +- Laufend: leerer Zustand sowie geplanter Ablauf des Profilwechsels, ohne + simulierte laufende Modelle oder Warteschlangen. +- Bild/Audio/Video-Profile: vorbereitete, deaktivierte aufgabenspezifische Felder. + +## Einstellungen → llama.cpp & Updates + +Build-Entwurf mit Backend (CUDA/CPU/Vulkan), gewünschtem Release oder Commit, +automatischer bzw. expliziter GPU-Architekturauswahl, Toolkit-Prüfung, parallelen +Build-Jobs und vorgesehener VRAM-Reserve. Für Athena sind RTX 5080 und RTX 3060 als +bekanntes Zielsystem benannt; eine Live-Build-Kompatibilitätsprüfung erfolgt nicht. + +Die Update-Ansicht lässt sich aufklappen. Sie zeigt Platz für aktuellen Build, +verfügbares Release, echte Release-Notes mit Quelle und Datum sowie Neubau und +spätere Wiederherstellung. Alle zustandsändernden Laufzeit-Aktionen und die +Live-Update-Suche sind deaktiviert. Keine erfundenen Versionsnummern oder Fixes. +Ein Link führt zu den offiziellen llama.cpp-Releases. + +## Speicherung und spätere Anbindung + +Entwürfe liegen ausschließlich im localStorage dieses Browsers und Origins unter +`athena-deck-studio-v1`. Sie bleiben nach Neuladen erhalten, enthalten keine +Zugangsdaten und werden nicht zum Server übertragen. Andere Browser oder die +Server-Instanz teilen diesen Stand nicht. Bei gesperrtem Browser-Speicher weist die +Oberfläche darauf hin, dass der Entwurf nur im aktuellen Speicher bleibt. + +Die neuen Ansichten in `studio.js` rufen keine API auf. Vorhandene Anmeldung, +Hardwareanzeige, Demo-Service und Netzwerkverwaltung behalten ihre bisherigen +Funktionen. `server.py` liefert zusätzlich nur die neue JavaScript-Datei aus. + +Vor echter Nutzung erforderlich: Modellkatalog-Adapter, Metadaten-/GGUF-Auswertung, +Downloadverwaltung, persistente Profil-API, GPU-/KV-Planung, Build-Manager und +Prozess-Supervisor mit Warteschlange. Gespeicherte Entwürfe dürfen nicht ungeprüft +als Shell-Befehle interpretiert werden. Die genaue Kontext-/Slot-Semantik muss an +die eingesetzte llama.cpp-Version gekoppelt und überprüft werden. + +## Prüfung + +JavaScript-Syntax geprüft. In einer isolierten statischen Browservorschau: +Profil bearbeitet (Kontext geändert), gespeichert und nach Reload wiedergefunden; +Build-Einstellungen und Update-Ansicht geöffnet, deaktivierte Aktionsknöpfe sowie +Layout kontrolliert. Die Testvorschau verwendet einen eigenen Origin, sodass keine +Entwürfe der eigentlichen Anwendung überschrieben wurden. Keine SSH-/Modell- oder +Build-Aktion für diese GUI-Erweiterung ausgeführt. diff --git a/app.js b/app.js index 0a3248c..4598c60 100644 --- a/app.js +++ b/app.js @@ -8,6 +8,7 @@ const metric=(title,value)=>`
${title}${value}v==null?'':``; const placeholders={chat:['Sprachmodelle / Chat','Modellprofile, Laufzeit und Chat','Hier werden später Modellprofile und native llama.cpp-Prozesse verwaltet.'],image:['Bildgenerierung','Worker und Aufträge','Hier entsteht später die Übersicht für Bild-Worker und deren Zustand.'],audio:['Audio','Spracheingabe und Sprachausgabe','Hier werden später getrennte Worker für Erkennung und Ausgabe eingebunden.'],services:['Weitere Dienste','Erweiterbare Dienste','Hier finden später zusätzliche Worker und ihre Schnittstellen Platz.']}; async function refresh(){if(refreshing)return;refreshing=true;const page=location.hash.slice(1)||'home';document.querySelectorAll('nav a').forEach(a=>a.classList.toggle('active',a.hash==='#'+page));try{error.textContent='';let html; +if(['chat','image','audio','video','runtime'].includes(page)){Studio.render(page);return;} if(page==='access'){await accessPage();return;} if(page==='network'){await networkPage();return;} if(page==='home'){const s=await api('status'),d=s.demo;html=heading('DEIN KONTROLLZENTRUM','Alles beginnt mit einer klaren Basis.','Eine kleine Oberfläche für Status, Hardware und den ersten steuerbaren Dienst.')+`
Anwendung
Bereit

Athena Deck läuft auf ${esc(s.location)}.

Laufzeit ${fmt(s.uptime_seconds,' s')} · API v1
Betriebsmodus
Isoliert

Ein eigenständiger Prototyp mit lesendem Hardware-Zugriff auf Athena.

Keine Modell- oder Router-Steuerung angebunden

Demo-Dienst

LOKAL / CPU

Ein minimaler HTTP-Prozess zum Testen von Start, Stopp und Erreichbarkeit.

${metric('Prozess',d.state==='running'?'Läuft':'Gestoppt')}${metric('Health-Prüfung',d.reachable?'Erreichbar':'Nicht erreichbar')}${metric('Lokaler Port',d.port??'—')}
Lädt keine Modelle · endet beim Beenden von Athena Deck
`; diff --git a/index.html b/index.html index f455b3b..59d6ee5 100644 --- a/index.html +++ b/index.html @@ -1 +1 @@ -Athena Deck
ATHENA CONTROL SURFACEv0.3 · Prototyp
+Athena Deck
ATHENA CONTROL SURFACEv0.4 · GUI-Prototyp
diff --git a/network/install_remote.py b/network/install_remote.py index 9dae2f2..74ce7de 100644 --- a/network/install_remote.py +++ b/network/install_remote.py @@ -12,7 +12,7 @@ import sys NAME = 'athena-deck-network' BASE = Path('/opt/athena-deck') -FILES = {'auth.py', 'access-ui.js', 'server.py', 'demo.py', 'collect_hardware.py', 'index.html', 'app.js', 'style.css', 'network-ui.js', 'login.html', 'login.js', 'network/__init__.py', 'network/config.py', 'network/policy.py', 'network/rpc.py', 'network/agent.py', 'network/client.py', 'network/Dockerfile', '.dockerignore'} +FILES = {'studio.js', 'auth.py', 'access-ui.js', 'server.py', 'demo.py', 'collect_hardware.py', 'index.html', 'app.js', 'style.css', 'network-ui.js', 'login.html', 'login.js', 'network/__init__.py', 'network/config.py', 'network/policy.py', 'network/rpc.py', 'network/agent.py', 'network/client.py', 'network/Dockerfile', '.dockerignore'} def run(*args, **kwargs): return subprocess.run(args, capture_output=True, timeout=600, **kwargs) diff --git a/server.py b/server.py index 9033c7e..0e1abc6 100644 --- a/server.py +++ b/server.py @@ -252,12 +252,12 @@ class Handler(BaseHTTPRequestHandler): return self.respond({'error':'Anmeldung erforderlich.'},401) if not self.authenticated() and self.path == '/': return self.respond((ROOT/'login.html').read_bytes(), mime='text/html; charset=utf-8') - routes = {'/': ('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')} + routes = {'/': ('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')} if self.path in routes: name, mime = routes[self.path] return self.respond((ROOT/name).read_bytes(), mime=mime) if self.path == '/api/v1/status': - return self.respond(dict(name='Athena Deck', version='0.3.0', state='ready', uptime_seconds=round(time.time()-self.server.started), mode='isolated', location='Athena' if os.environ.get('DECK_AUTH_RPC') else 'Mac', demo=self.server.demo.status())) + return self.respond(dict(name='Athena Deck', version='0.4.0', state='ready', uptime_seconds=round(time.time()-self.server.started), mode='isolated', location='Athena' if os.environ.get('DECK_AUTH_RPC') else 'Mac', demo=self.server.demo.status())) if self.path == '/api/v1/hardware': return self.respond(self.server.hardware.snapshot()) if self.path == '/api/v1/network': diff --git a/studio.js b/studio.js new file mode 100644 index 0000000..effd6c2 --- /dev/null +++ b/studio.js @@ -0,0 +1,58 @@ +/* Visual prototype only. Drafts stay in this browser; no runtime/download API calls. */ +const Studio=(()=>{ + const key='athena-deck-studio-v1'; + const seed={profiles:[{id:'alltag',name:'qwen-alltag',model:'qwen-iq4',context:76800,slots:1,gpu:'both',split:'85,15',cache:'q8_0',batch:2048,ubatch:128,threads:8,unified:true,flash:true,vision:false},{id:'parallel',name:'qwen-parallel',model:'qwen-iq4',context:160000,slots:2,gpu:'both',split:'85,15',cache:'q8_0',batch:2048,ubatch:128,threads:8,unified:true,flash:true,vision:false},{id:'lang',name:'qwen-langkontext',model:'qwen-iq4',context:262144,slots:1,gpu:'both',split:'80,20',cache:'q8_0',batch:2048,ubatch:128,threads:8,unified:true,flash:true,vision:false}],runtime:{backend:'CUDA',revision:'',architectures:'auto',custom:'',jobs:4,toolkit:'auto',reserve:2048}}; + let state=structuredClone(seed);try{const saved=JSON.parse(localStorage.getItem(key));if(saved?.profiles&&Array.isArray(saved.profiles)&&saved.runtime)state=saved;}catch{} + let section='discover',category='',editing=null; + const models=[{id:'qwen-iq4',kind:'chat',name:'Qwen 27B',variant:'IQ4_XS · GGUF',source:'Hugging Face',description:'Sprachmodell · Beispiel für mehrere Profile mit einer Modelldatei.'},{id:'qwen-q4',kind:'chat',name:'Qwen 27B',variant:'Q4_K_M · GGUF',source:'Hugging Face',description:'Alternative Quantisierung · eigener Bibliothekseintrag.'},{id:'image-example',kind:'image',name:'Bildmodell',variant:'Modellpaket · Beispiel',source:'Modellkatalog',description:'Textencoder, Diffusionsmodell und VAE als zusammengehöriges Paket.'},{id:'tts-example',kind:'audio',name:'Sprachausgabe',variant:'TTS · Beispiel',source:'Modellkatalog',description:'Sprachmodell und Stimmen mit passender Audio-Laufzeit.'},{id:'asr-example',kind:'audio',name:'Spracherkennung',variant:'ASR · Beispiel',source:'Modellkatalog',description:'Audio in Text umwandeln · CPU-/GPU-Eignung später prüfen.'},{id:'video-example',kind:'video',name:'Videomodell',variant:'Modellpaket · Beispiel',source:'Modellkatalog',description:'Auflösung, Bildanzahl und Zusatzkomponenten gemeinsam planen.'}]; + const labels={chat:'Sprachmodelle / Chat',image:'Bildgenerierung',audio:'Audio',video:'Video'}; + const e=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); + const count=n=>Number(n).toLocaleString('de-DE'); + const selected=(a,b)=>a===b?'selected':''; + const check=v=>v?'checked':''; + function save(){try{localStorage.setItem(key,JSON.stringify(state));return true;}catch{notice('Speichern im Browser nicht möglich. Entwurf bleibt nur für diese Ansicht erhalten.');return false;}} + function notice(message){const node=document.querySelector('#studio-notice');if(node)node.textContent=message;} + const banner=()=>'
GUI-VORSCHAUBeispiele und lokale Entwürfe. Keine Downloads, Builds oder Modellstarts.
'; + function header(title,description){return `
ATHENA / MODELLVERWALTUNG

${title}

${description}

${banner()}`;} + function render(page,force=false){ + if(!force&&document.querySelector('#studio')?.dataset.page===page)return; + if(category!==page){category=page;section='discover';editing=null;} + const view=document.querySelector('#view'); + view.innerHTML=`
${page==='runtime'?runtime():workspace(page)}

`; + bind(page); + } + function workspace(page){ + return header(labels[page], 'Modelle entdecken, als Bibliothek organisieren und über Profile bereitstellen.')+`
${[['discover','Entdecken'],['library','Bibliothek'],['profiles','Profile'],['running','Laufend']].map(([id,label])=>``).join('')}
${section==='discover'?discover(page):section==='library'?library(page):section==='profiles'?profiles(page):running(page)}
`; + } + function modelCard(m,library=false){return `
${m.kind==='chat'?'Aa':m.kind==='image'?'▧':m.kind==='audio'?'≈':'▷'}${library?'Bibliotheksbeispiel':'Katalogbeispiel'}

${e(m.name)}

${e(m.variant)}

${e(m.description)}

Quelle
${e(m.source)} · nicht verbunden
Downloadgröße
Noch nicht ermittelt
Eignung für Athena
Noch nicht geprüft
${library&&m.kind==='chat'?``:''}
`;} + function discover(page){return `

Die Suche filtert nur diese Beispieldaten. Eine Live-Verbindung zu Hugging Face folgt später.

${models.filter(m=>m.kind===page).map(m=>modelCard(m)).join('')}
`;} + function library(page){return `

Deine Modellbibliothek

Eine Modelldatei kann von mehreren Profilen verwendet werden.

0 verifizierte Installationen
${models.filter(m=>m.kind===page).slice(0,1).map(m=>modelCard(m,true)).join('')}

Die Einträge zeigen die geplante Darstellung. Es wurden weder lokale Modelldateien importiert noch Downloads ausgeführt.

`;} + function profiles(page){ + if(page!=='chat')return `
VORBEREITET

${page==='image'?'Auflösung und Generierung':page==='audio'?'Sprache, Stimmen und Verarbeitung':'Auflösung und Videolänge'}

${page==='image'?'':page==='audio'?'':''}

Eigene Worker-Adapter und Eignungsregeln folgen. LLM-Kontextparameter werden hier nicht übernommen.

`; + return `

Ein Modell. Mehrere Möglichkeiten.

API-Namen und Startparameter als lokale Profilentwürfe.

${state.profiles.map(p=>`
PROFILENTWURF

${e(p.name)}

${e(models.find(m=>m.id===p.model)?.variant||'Modell nicht zugeordnet')}

Kontextbudget${count(p.context)}
Slots${e(p.slots)}
ZustandNicht bereitgestellt
`).join('')||'
Noch keine Profilentwürfe. Lege dein erstes Profil an.
'}
GEPLANTE API · NICHT AKTIV

Ein Endpunkt für deine Profile

GET /v1/models
${e(JSON.stringify({data:state.profiles.map(p=>({id:p.name,object:'model'}))},null,2))}

Später wählt der Client den Profilnamen im Feld model. Deck verwaltet den passenden llama-server-Prozess.

`; + } + function running(page){return `
○

Keine von Deck verwaltete Modelllaufzeit

${page==='chat'?'llama.cpp ist in dieser Vorschau nicht angebunden. Gespeicherte Profile starten keinen Prozess.':'Für diesen Bereich ist noch kein Worker angebunden.'}

Laufzeit-Einstellungen ansehen →

So soll der Profilwechsel ablaufen

  1. Anfrage zuordnenProfil anhand des API-Namens auswählen.
  2. Arbeit abschließenLaufende Anfragen beenden lassen; neue einreihen.
  3. Prozess wechselnllama-server beenden und mit dem Zielprofil starten.
  4. Bereitschaft prüfenErst danach wartende Anfragen weiterleiten.

Mehrere angebotene Profile sind nicht automatisch mehrere geladene Modelle. Produktive Athena-Prozesse werden in dieser Vorschau nicht gesteuert.

`;} + function detail(id){const m=models.find(m=>m.id===id);document.querySelector('#model-detail').innerHTML=`

${e(m.name)} · Eignungsprüfung

NOCH NICHT BERECHNET
Unterstützt?

Format und Laufzeit-Kompatibilität noch nicht geprüft.

Passt grundsätzlich?

Gewichte, Zusatzkomponenten und Arbeitsspeicherbedarf noch nicht ermittelt.

Kann jetzt starten?

Keine Ressourcenreservierung oder Prozesssteuerung angebunden.

${m.kind==='chat'?'Kontextlänge, KV-Cache, Slots und GPU-Aufteilung beeinflussen den Speicherbedarf. Die beiden GPUs werden nicht pauschal als ein gemeinsamer VRAM-Pool gerechnet.':'Die spätere Einschätzung berücksichtigt die zur Aufgabe passenden Parameter und den jeweiligen Worker.'} Es gibt in dieser Vorschau keine erfundenen Speicherwerte.

`;document.querySelector('#model-detail').scrollIntoView({behavior:'smooth',block:'nearest'});} + function field(label,name,value,type='number',attrs=''){return ``;} + function editor(id,model){editing=id||null;const p=id?state.profiles.find(p=>p.id===id):{...seed.profiles[0],name:'qwen-neues-profil',model:model||'qwen-iq4'};if(!p)return; + document.querySelector('#profile-editor').innerHTML=`

${id?'Profil bearbeiten':'Neues Profil'}

NUR ENTWURF
${field('API-Profilname','name',p.name,'text','required pattern="[a-zA-Z0-9][a-zA-Z0-9_-]{1,63}" maxlength="64"')}${field('Gesamtes Kontextbudget (Token)','context',p.context,'number','required min="512" max="2097152" step="1"')}${field('Parallele Slots','slots',p.slots,'number','required min="1" max="16" step="1"')}

Das Kontextbudget gilt für das Profil, nicht automatisch je Slot. Die genaue Slot-Zuteilung und Unterstützung werden später mit der gewählten llama.cpp-Version geprüft.

Erweiterte Startoptionen
${field('Gewichtung 5080 : 3060','split',p.split,'text','required pattern="[0-9]+(\\.[0-9]+)?,[0-9]+(\\.[0-9]+)?"')}${field('Batch-Größe','batch',p.batch,'number','required min="1" max="8192"')}${field('Microbatch-Größe','ubatch',p.ubatch,'number','required min="1" max="8192"')}${field('CPU-Threads','threads',p.threads,'number','required min="1" max="256"')}
${id?'':''}
`; + document.querySelector('#profile-form').onsubmit=event=>{event.preventDefault();const data=new FormData(event.target),value=Object.fromEntries(data);for(const n of ['context','slots','batch','ubatch','threads'])value[n]=Number(value[n]);for(const n of ['unified','flash','vision'])value[n]=data.has(n);const out=document.querySelector('#profile-error');if(state.profiles.some(q=>q.id!==editing&&q.name===value.name)){out.textContent='Dieser API-Profilname ist bereits vergeben.';return;}if(value.ubatch>value.batch){out.textContent='Microbatch darf nicht größer als Batch sein.';return;}if(value.gpu==='both'&&!value.split.split(',').some(v=>Number(v)>0)){out.textContent='Mindestens eine GPU-Gewichtung muss größer als null sein.';return;}value.id=editing||crypto.randomUUID();const idx=state.profiles.findIndex(q=>q.id===editing);if(idx<0)state.profiles.push(value);else state.profiles[idx]=value;const stored=save();render(category,true);notice(stored?'Profilentwurf in diesem Browser gespeichert. Kein Modell gestartet.':'Entwurf nur im Speicher gehalten; Browser-Speicher nicht verfügbar.');}; + document.querySelector('#cancel-edit').onclick=()=>document.querySelector('#profile-editor').replaceChildren(); + if(id)document.querySelector('#delete-profile').onclick=()=>{state.profiles=state.profiles.filter(q=>q.id!==id);save();render(category,true);notice('Lokalen Profilentwurf entfernt. Modelldateien wurden nicht verändert.');}; + document.querySelector('#profile-editor').scrollIntoView({behavior:'smooth',block:'start'}); + } + function runtime(){const r=state.runtime;return header('llama.cpp-Laufzeit','Installation, GPU-Unterstützung und Versionen an einem Ort vorbereiten.')+`
INSTALLIERTE VERSION

Nicht angebunden

Kein Build erkannt oder ausgeführt.

UPDATE-STATUS

Noch nicht geprüft

Keine Live-Abfrage von Releases.

ZIELSYSTEM

Athena

Geplant: RTX 5080 + RTX 3060

Installation & Build

LOKALER ENTWURF
${field('Gewünschtes Release / Commit','revision',r.revision,'text','placeholder="Noch keine Version ausgewählt" maxlength="80"')}${field('Explizite Architekturziele (optional)','custom',r.custom,'text','placeholder="Später anhand beider GPUs prüfen" maxlength="80"')}${field('Parallele Build-Jobs','jobs',r.jobs,'number','required min="1" max="64"')}${field('Geplante VRAM-Reserve je GPU (MiB)','reserve',r.reserve,'number','required min="256" max="16384" step="256"')}

Bei verschiedenen GPUs muss der CUDA-Build die Architektur beider Karten unterstützen. Die konkreten Build-Ziele und die passende Toolkit-Version werden später auf Athena geprüft. Diese Vorschau installiert keine Treiber oder Host-Pakete.

Updates & Änderungen

Vorgesehen: installierte Version mit verfügbaren Releases vergleichen, Fixes ansehen und einen neuen Build getrennt vorbereiten.

Geplanter Build-Ablauf

  1. Voraussetzungen prüfenCompiler, CUDA, beide GPUs und freier Speicher.
  2. Version beziehenAusgewähltes Release oder festen Commit herunterladen.
  3. Separat bauen & prüfenNeue Binärdatei prüfen; laufende Version beibehalten.
  4. Gezielt aktivierenNach laufenden Anfragen wechseln; vorigen Build behalten.

Kein Build aktiv · keine Warteschlange · keine Wiederherstellungsversion vorhanden.

`;} + function bind(page){ + document.querySelectorAll('[data-section]').forEach(b=>b.onclick=()=>{section=b.dataset.section;render(page,true);}); + document.querySelectorAll('[data-detail]').forEach(b=>b.onclick=()=>detail(b.dataset.detail)); + document.querySelectorAll('[data-create]').forEach(b=>b.onclick=()=>{const model=b.dataset.create;section='profiles';render(page,true);editor(null,model);}); + document.querySelectorAll('[data-edit]').forEach(b=>b.onclick=()=>editor(b.dataset.edit)); + document.querySelectorAll('[data-duplicate]').forEach(b=>b.onclick=()=>{const old=state.profiles.find(p=>p.id===b.dataset.duplicate);let n=2,name=old.name.slice(0,55)+'-kopie';while(state.profiles.some(p=>p.name===name))name=old.name.slice(0,50)+'-kopie-'+n++;state.profiles.push({...old,id:crypto.randomUUID(),name});save();render(page,true);notice('Profil lokal dupliziert. Kein zusätzlicher Prozess gestartet.');}); + document.querySelector('#new-profile')?.addEventListener('click',()=>editor()); + const filter=()=>{const q=document.querySelector('#model-search').value.toLowerCase(),source=document.querySelector('#source-filter').value;const filtered=models.filter(m=>m.kind===page&&(source==='all'||source===m.source)&&(m.name+' '+m.variant).toLowerCase().includes(q));document.querySelector('#model-results').innerHTML=filtered.map(m=>modelCard(m)).join('');document.querySelector('#no-models').hidden=!!filtered.length;document.querySelectorAll('[data-detail]').forEach(b=>b.onclick=()=>detail(b.dataset.detail));}; + document.querySelector('#model-search')?.addEventListener('input',filter);document.querySelector('#source-filter')?.addEventListener('change',filter); + document.querySelector('#release-preview')?.addEventListener('click',()=>{const p=document.querySelector('#release-panel');p.hidden=!p.hidden;}); + document.querySelector('#build-form')?.addEventListener('submit',event=>{event.preventDefault();const value=Object.fromEntries(new FormData(event.target));value.jobs=Number(value.jobs);value.reserve=Number(value.reserve);if(value.architectures==='custom'&&!value.custom.trim()){notice('Bitte explizite Architekturziele eintragen oder automatische Erkennung wählen.');return;}state.runtime=value;notice(save()?'Build-Entwurf im Browser gespeichert. Kein Download oder Build ausgeführt.':'Entwurf nur für diese Sitzung gespeichert.');}); + } + return {render}; +})(); diff --git a/style.css b/style.css index 8296517..e25f409 100644 --- a/style.css +++ b/style.css @@ -2,3 +2,7 @@ label{display:block;margin:18px 0 8px;font-size:13px;color:#c4d1c6}input,select{display:block;width:100%;max-width:560px;padding:12px;margin-bottom:16px;background:#101718;color:#e4eee5;border:1px solid #465348;border-radius:7px;font:inherit}input[type=file]{font-size:13px}button{margin-bottom:8px}.network-message{white-space:pre-wrap;color:#d1eabf}.warning{color:#f2c891}.login{margin:10vh auto;width:100%;max-width:640px}.network-status{display:flex;gap:30px;flex-wrap:wrap}.network-status strong{display:block;margin:8px 0}.link{color:#c5edaf}.network-card a{color:#c5edaf}.network-card button{font-size:13px}.network-card .note{overflow-wrap:anywhere} #access-page .card{margin-top:20px}#access-message{color:#d1eabf;white-space:pre-wrap} +/* Model studio: all sample and draft states are explicit. */ +aside{overflow-y:auto}nav{gap:3px}nav a{padding:11px 12px}.prototype-banner{display:flex;align-items:center;gap:14px;background:#202b23;border:1px solid #40503f;border-radius:9px;padding:15px 18px;margin:24px 0;color:#bdd0ba;font-size:12px}.studio-tabs{display:flex;gap:7px;border-bottom:1px solid #344038;padding-bottom:10px;margin:28px 0}.studio-tabs button{font-size:13px}.studio-tabs button span{margin-left:8px;opacity:.6}.model-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px}.model-card h2{margin-bottom:8px}.model-icon{display:inline-flex;align-items:center;justify-content:center;width:42px;height:42px;background:#29372b;color:#d0e5c4;border-radius:10px;font-size:23px}.model-facts{font-size:12px;line-height:1.6}.model-facts>div{display:flex;justify-content:space-between;gap:15px;padding:10px 0;border-bottom:1px solid #2a3430}.model-facts dt{color:#819386}.model-facts dd{margin:0;text-align:right;color:#c9d3ca}.unknown-dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:#a2aea5;margin-right:5px}.card-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:22px}.card-actions button{margin-right:0;font-size:12px}.studio-toolbar{display:grid;grid-template-columns:2fr 1fr;gap:18px}.studio-toolbar label{margin-top:0}.studio-toolbar input,.studio-toolbar select{max-width:none;margin-bottom:0}.small{font-size:12px}.section-heading{display:flex;justify-content:space-between;gap:20px;align-items:center;margin-bottom:20px}.section-heading h2{margin-bottom:3px}.section-heading p{margin-top:5px}.section-heading button{white-space:nowrap}.profile-list{display:grid;gap:12px}.profile-row{display:grid;grid-template-columns:1.5fr 1fr .5fr 1fr;gap:18px;align-items:center;padding:22px}.profile-row h2{font-size:18px;margin:9px 0 4px;overflow-wrap:anywhere}.profile-row p{font-size:12px;margin:0}.profile-row strong{display:block;margin-top:10px;font-size:14px;font-weight:500}.profile-actions{grid-column:1/-1;display:flex;border-top:1px solid #303a37;padding-top:14px}.profile-actions button{font-size:12px;margin-bottom:0}.endpoint-preview,.detail-card,.editor{margin-top:24px}pre{background:#101718;padding:20px;border-radius:8px;overflow:auto;font-size:12px;line-height:1.6;color:#bed4b8;max-height:280px}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 24px}.form-grid input,.form-grid select{max-width:none}.checkbox{display:flex;align-items:center;gap:10px}.checkbox input{width:16px;height:16px;margin:0;accent-color:#bfdda9}.editor details{border-top:1px solid #303a37;margin-top:22px;padding-top:18px}summary{cursor:pointer;color:#ccdbc7;font-size:14px}.danger{color:#e9aaa0!important}#studio-notice{color:#c7e4b6}#profile-error{color:#ffc1a7}.runtime-empty{text-align:center;padding:40px;margin-bottom:20px}.runtime-empty a{display:block;margin-top:16px;font-size:13px}.flow{list-style:none;counter-reset:steps;padding:0;display:grid;grid-template-columns:repeat(2,1fr);gap:20px;margin:28px 0}.flow li{counter-increment:steps;padding:0 0 0 35px;position:relative}.flow li:before{content:counter(steps,decimal-leading-zero);position:absolute;left:0;color:#b6d2a8;font-size:12px;top:2px}.flow b{font-size:13px;font-weight:500}.flow span{display:block;color:#82968a;font-size:12px;line-height:1.7;margin-top:7px}.runtime-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:15px;margin-bottom:22px}.runtime-stats .card{padding:22px}.runtime-stats h2{font-size:18px}.runtime-stats p{font-size:12px}#studio[data-page=runtime]>.card{margin-top:20px}.release-comparison{display:flex;gap:35px;align-items:center;margin:25px 0}.fit-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:22px}.fit-grid b{font-size:13px}.fit-grid p{font-size:12px}@media(max-width:1100px){.model-grid{grid-template-columns:1fr}.runtime-stats{grid-template-columns:1fr}.profile-row{grid-template-columns:1fr 1fr}.fit-grid{grid-template-columns:1fr}}@media(max-width:600px){.form-grid,.studio-toolbar,.flow{grid-template-columns:1fr}.prototype-banner,.section-heading{align-items:flex-start;flex-direction:column}.studio-tabs{flex-wrap:wrap}.studio-tabs button{padding:10px 12px}.profile-row{gap:18px}.card{padding:20px}.model-facts>div{flex-direction:column;gap:3px}.model-facts dd{text-align:left}} + +.nav-heading{font-size:10px;letter-spacing:1.5px;color:#75867b;margin:15px 12px 5px}