Feat: Vulkan/RADV-Engine + vocab-gepruefte Spec-Drafts + Provisioning/Sync

Engine-Cutover ROCm/HIP -> Vulkan/RADV (gfx1151): +12-22% tg auf MoE (llama-bench
verifiziert, fast 53->65 t/s). ROCm-Build bleibt als Rollback unter /opt/llamacpp.

- Backend: vocab-aware Speculative Decoding. services/gguf_meta.py liest den
  Tokenizer-Fingerprint (model/pre/n_vocab) direkt aus dem GGUF-Header (ohne Modell-Load);
  register_model + migrate_config haengen nur VOCAB-KOMPATIBLE Drafts an (inkl. --spec-type,
  das in dieser llama.cpp-Generation noetig ist). Neue Endpoints /api/models/drafts + /{id}/draft.
- Frontend: idiotensichere Spec-Draft-UI (SpecDraftModal) - nur kompatible Drafts waehlbar,
  inkompatible gesperrt mit Begruendung; SPEC/SPEC?-Badge nach echtem Aktiv-Status; Rolle in AddModel.
- maintenance.py: Engine-Update-Quelle -> ggml-org/llama.cpp (Build-Nummer-Vergleich),
  ENGINE_PATH=/opt/llamacpp-vulkan.
- Startup-Warmup der brains (deploy/warmup.sh, self-detaching ExecStartPost) + deploy/provision-engine.sh.
- Cleanup: tote LiteLLM gateway/config.yaml + alle Referenzen (config.py/backup.py/backup.sh) entfernt;
  README + docs/memory aktualisiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-27 01:54:29 +02:00
parent 530d77ff1b
commit c48e583790
23 changed files with 1128 additions and 508 deletions
+23 -6
View File
@@ -7,8 +7,10 @@ export function AddModel() {
const [quants, setQuants] = useState<string[]>([])
const [quant, setQuant] = useState("Q4_K_M")
const [msg, setMsg] = useState("")
const [role, setRole] = useState("")
const [q, setQ] = useState("")
const [results, setResults] = useState<{ repo: string; downloads: number }[]>([])
const ROLE_OPTS = ["fast", "heavy", "coder", "vision", "scout"]
async function loadQuants(r?: string) {
const rr = r ?? repo
@@ -42,10 +44,16 @@ export function AddModel() {
setMsg("Download-Job wird initiiert...")
try {
await api("/api/models/install", {
method: "POST",
body: JSON.stringify({ repo, quant, jinja: true }),
method: "POST",
body: JSON.stringify({ repo, quant, role: role || undefined, jinja: true }),
})
setMsg(`Download gestartet: ${repo} (${quant}). Fortschritt wird oben angezeigt.`)
setMsg(
`Download gestartet: ${repo} (${quant})${role ? `, Rolle: ${role}` : ""}. ` +
`Fortschritt oben.` +
(role === "fast" || role === "coder"
? " Speculative Draft (Vocab-geprüft) kannst du nach dem Download an der Modellkarte (»Spec«) setzen."
: "")
)
} catch (e) {
setMsg(`Download-Fehler: ${e}`)
}
@@ -71,13 +79,22 @@ export function AddModel() {
</button>
{quants.length > 0 && (
<>
<select
value={quant}
onChange={(e) => setQuant(e.target.value)}
<select
value={quant}
onChange={(e) => setQuant(e.target.value)}
className="h-9 rounded-lg border border-border/60 bg-background/40 px-3 text-xs outline-none text-foreground font-semibold"
>
{quants.map((qq) => <option key={qq} value={qq} className="bg-popover text-foreground">{qq}</option>)}
</select>
<select
value={role}
onChange={(e) => setRole(e.target.value)}
title="Rolle (optional) — bestimmt Auto-Konfiguration wie parallele Slots"
className="h-9 rounded-lg border border-border/60 bg-background/40 px-3 text-xs outline-none text-foreground font-semibold"
>
<option value="" className="bg-popover text-foreground">Rolle</option>
{ROLE_OPTS.map((r) => <option key={r} value={r} className="bg-popover text-foreground">{r}</option>)}
</select>
<button
onClick={install}
className="h-9 px-4 rounded-lg bg-primary text-primary-foreground text-xs font-semibold hover:opacity-90 transition-all cursor-pointer flex items-center gap-1.5"
+51 -9
View File
@@ -1,12 +1,13 @@
import { useState, useRef, useCallback } from "react"
import { Download, Trash2, Edit3, Activity, HardDrive, X, Check, Copy } from "lucide-react"
import { api } from "@/lib/api"
import { Download, Trash2, Edit3, Activity, HardDrive, X, Check, Copy, Zap } from "lucide-react"
import { api, type ModelInfo } from "@/lib/api"
import { useModels, useRouting, useConnect, useUpdates, useQueryClient, qk } from "@/lib/queries"
import { useDialog } from "@/lib/useDialog"
import { CapsChips } from "@/components/CapsChips"
import { cn } from "@/lib/utils"
import { fmtSize, fmtCtx } from "@/lib/format"
import { getBrandInfo, ROLES } from "@/components/models/ModelBadges"
import { SpecDraftModal } from "@/components/models/SpecDraftModal"
export function Cockpit() {
const qc = useQueryClient()
@@ -26,6 +27,7 @@ export function Cockpit() {
// UI state
const [activeClient, setActiveClient] = useState<"roocode" | "cursor" | "opencode" | "zed" | "continue" | null>(null)
const [activeRoleForAssign, setActiveRoleForAssign] = useState<string | null>(null)
const [specModel, setSpecModel] = useState<ModelInfo | null>(null)
const [copied, setCopied] = useState(false)
const [hoveredNode, setHoveredNode] = useState<string | null>(null)
const [viewMode, setViewMode] = useState<"grid" | "list">("grid")
@@ -742,11 +744,15 @@ export function Cockpit() {
PC
</span>
)}
{m.spec_draft_model && (
{m.spec_active ? (
<span className="px-1.5 py-0.5 rounded bg-emerald-500/10 border border-emerald-500/20 text-[9px] font-mono text-emerald-400 font-bold uppercase" title={`Speculative Decoding aktiv (Draft: ${m.spec_draft_model})`}>
SPEC
</span>
)}
) : m.spec_draft_model ? (
<span className="px-1.5 py-0.5 rounded bg-amber-500/10 border border-amber-500/20 text-[9px] font-mono text-amber-400 font-bold uppercase" title={`Draft gesetzt (${m.spec_draft_model}), aber INAKTIV — --spec-type fehlt oder Vocab inkompatibel. Über 'Spec' neu konfigurieren.`}>
SPEC?
</span>
) : null}
{m.parallel_slots > 1 && (
<span className="px-1.5 py-0.5 rounded bg-violet-500/10 border border-violet-500/20 text-[9px] font-mono text-violet-400 font-bold uppercase" title={`${m.parallel_slots} parallele Slots aktiv`}>
SLOTS: {m.parallel_slots}
@@ -825,6 +831,18 @@ export function Cockpit() {
>
Ctx
</button>
<button
onClick={() => setSpecModel(m)}
className={cn(
"h-7 px-2.5 rounded-lg border text-[9px] font-bold uppercase transition-colors cursor-pointer flex items-center gap-1",
m.spec_active ? "border-emerald-500/30 text-emerald-400 hover:bg-emerald-500/10"
: m.spec_draft_model ? "border-amber-500/30 text-amber-400 hover:bg-amber-500/10"
: "border-border/40 text-muted-foreground hover:bg-accent"
)}
title="Speculative Draft konfigurieren (Vocab-geprüft)"
>
<Zap className="h-3 w-3" /> Spec
</button>
<button
onClick={() => handleDelete(m.name)}
className="h-7 w-7 rounded-lg border border-border/40 text-muted-foreground hover:text-red-400 hover:bg-red-500/5 flex items-center justify-center transition-colors cursor-pointer"
@@ -885,11 +903,15 @@ export function Cockpit() {
PC
</span>
)}
{m.spec_draft_model && (
{m.spec_active ? (
<span className="px-1.5 py-0.5 rounded bg-emerald-500/10 border border-emerald-500/20 text-[8px] font-mono text-emerald-400 font-bold uppercase shrink-0" title={`Speculative Decoding aktiv (Draft: ${m.spec_draft_model})`}>
SPEC
</span>
)}
) : m.spec_draft_model ? (
<span className="px-1.5 py-0.5 rounded bg-amber-500/10 border border-amber-500/20 text-[8px] font-mono text-amber-400 font-bold uppercase shrink-0" title={`Draft gesetzt (${m.spec_draft_model}), aber INAKTIV — --spec-type fehlt oder Vocab inkompatibel.`}>
SPEC?
</span>
) : null}
{m.parallel_slots > 1 && (
<span className="px-1.5 py-0.5 rounded bg-violet-500/10 border border-violet-500/20 text-[8px] font-mono text-violet-400 font-bold uppercase shrink-0" title={`${m.parallel_slots} parallele Slots aktiv`}>
SLOTS: {m.parallel_slots}
@@ -937,15 +959,27 @@ export function Cockpit() {
>
{isRunning ? "Entladen" : "Laden"}
</button>
<button
<button
onClick={() => handleSetCtx(m.name, m.ctx)}
className="h-7 px-2.5 rounded-lg border border-border/40 text-[9px] font-bold uppercase hover:bg-accent text-muted-foreground transition-all cursor-pointer"
title="Kontextlänge anpassen"
>
Ctx
</button>
<button
onClick={() => handleDelete(m.name)}
<button
onClick={() => setSpecModel(m)}
className={cn(
"h-7 px-2.5 rounded-lg border text-[9px] font-bold uppercase transition-all cursor-pointer flex items-center gap-1",
m.spec_active ? "border-emerald-500/30 text-emerald-400 hover:bg-emerald-500/10"
: m.spec_draft_model ? "border-amber-500/30 text-amber-400 hover:bg-amber-500/10"
: "border-border/40 text-muted-foreground hover:bg-accent"
)}
title="Speculative Draft konfigurieren (Vocab-geprüft)"
>
<Zap className="h-3 w-3" /> Spec
</button>
<button
onClick={() => handleDelete(m.name)}
className="h-7 w-7 rounded-lg flex items-center justify-center border border-border/40 text-muted-foreground hover:text-red-400 hover:bg-red-500/5 transition-all cursor-pointer"
title="Modell löschen"
>
@@ -1016,6 +1050,14 @@ export function Cockpit() {
</div>
)}
{specModel && (
<SpecDraftModal
model={specModel}
onClose={() => setSpecModel(null)}
onChanged={reload}
/>
)}
{dialogElement}
</div>
)