Feat: fit-aware Brain-Update (Speicher-Budget) + brains-Kontext gesenkt (Always-On-Footprint)

- Brain-Kontexte gesenkt (live config): hermes 128K->64K, fast/vision 128K->32K
  -> Always-Warm-Footprint 74GB->51GB, ~23GB frei; heavy/coder passen wieder daneben.
- /api/agent/brain liefert jetzt `budget`: projiziert den Always-On-Footprint mit dem
  empfohlenen Brain gegen das GTT-Budget (aus amdgpu.gttsize) und prueft, ob das groesste
  on-demand-Modell daneben passt (fit.estimate_memory_gb).
- Cockpit Agent-Hirn-Karte: Budget-Zeile (gruen/rot) + Warnung im Update-Confirm, wenn ein
  zu grosses Always-On-Brain das groesste on-demand-Modell verdraengen wuerde. Button wird rot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-27 02:56:32 +02:00
parent 510de69250
commit 5b699aaa79
6 changed files with 141 additions and 56 deletions
+9
View File
@@ -315,8 +315,17 @@ export interface HermesBrainCandidate {
fit: Fit
}
export interface HermesBrainBudget {
gtt_gb: number
warm_projected_gb: number // Always-On-Footprint mit dem empfohlenen Brain
free_after_gb: number
largest_ondemand_gb: number // größtes on-demand-Modell (z.B. heavy)
fits: boolean // passt das größte on-demand daneben?
}
export interface HermesBrainResp {
current: HermesBrainModel | null
recommended: HermesBrainCandidate | null
update_available: boolean
budget?: HermesBrainBudget | null
}
+25 -2
View File
@@ -177,9 +177,13 @@ export function Cockpit() {
}
async function handleBrainUpdate(repo: string) {
const b = brain?.budget
const warn = b && !b.fits
? `\n\n⚠ Speicher-Warnung: Als Always-On würde dieses Brain ~${b.warm_projected_gb} GB belegen (Budget ${b.gtt_gb} GB) → nur ${b.free_after_gb} GB frei. Das größte on-demand-Modell (~${b.largest_ondemand_gb} GB) passt dann NICHT mehr gleichzeitig daneben. Erwäge ein kleineres Brain oder weniger Kontext.`
: ""
showConfirm(
"Agent-Hirn aktualisieren?",
`Neues Hermes-Modell '${repo.split("/").pop()}' herunterladen und als Agent-Hirn (Rolle hermes) setzen? Hermes nutzt danach automatisch das neue Modell.`,
`Neues Hermes-Modell '${repo.split("/").pop()}' herunterladen und als Agent-Hirn (Rolle hermes) setzen? Hermes nutzt danach automatisch das neue Modell.${warn}`,
async () => {
try {
await api("/api/models/install", {
@@ -676,7 +680,12 @@ export function Cockpit() {
{brain.update_available && brain.recommended ? (
<button
onClick={() => handleBrainUpdate(brain.recommended!.repo)}
className="h-8 px-3 shrink-0 flex items-center justify-center gap-1.5 rounded-lg bg-amber-500 text-black text-[10px] font-bold uppercase hover:bg-amber-400 transition-all cursor-pointer shadow-md shadow-amber-500/10"
className={cn(
"h-8 px-3 shrink-0 flex items-center justify-center gap-1.5 rounded-lg text-[10px] font-bold uppercase transition-all cursor-pointer shadow-md",
brain.budget && !brain.budget.fits
? "bg-red-500 text-white hover:bg-red-400 shadow-red-500/10"
: "bg-amber-500 text-black hover:bg-amber-400 shadow-amber-500/10"
)}
>
<Download className="h-3.5 w-3.5" /> Aktualisieren
</button>
@@ -694,6 +703,20 @@ export function Cockpit() {
(v{brain.recommended.version}, {brain.recommended.params_b}B) von NousResearch.
</div>
)}
{brain.budget && (
<div className={cn("text-[10px] flex items-start gap-1.5 leading-relaxed",
brain.budget.fits ? "text-muted-foreground/80" : "text-red-400 font-semibold")}>
<HardDrive className="h-3 w-3 shrink-0 mt-0.5" />
<span>
{brain.update_available ? "Mit diesem Brain als Always-On: " : "Always-On-Budget: "}
~{brain.budget.warm_projected_gb} / {brain.budget.gtt_gb} GB belegt {brain.budget.free_after_gb} GB frei
{brain.budget.fits
? ` · größtes on-demand-Modell (~${brain.budget.largest_ondemand_gb} GB) passt daneben ✓`
: ` · größtes on-demand-Modell (~${brain.budget.largest_ondemand_gb} GB) passt NICHT mehr daneben ⚠ — kleineres Brain/weniger Kontext`}
</span>
</div>
)}
</div>
)}