Feat: setup-bewusste ctx-Vergabe - eine Quelle der Wahrheit (services/budget.py)

Bisher rechnete nur der Hirn-Wechsel setup-bewusst; die allgemeine ctx-Auto-Groesse
nahm den Gesamt-RAM in ISOLATION (ignorierte Hirn/warmes Set/Ko-Residenz) -> ctx
konnte zu gross gewaehlt werden.

Neu: services/budget.py buendelt GTT-Budget, Modell-Footprint und reservierten Speicher
gemaess VERIFIZIERTER Box-Residenz (Hirn immer resident; fast/vision duerfen weichen,
wenn grosses on-demand-Modell laedt). setup_aware_ctx() bemisst den groessten ctx, der
NEBEN dem bestehenden Setup passt - rollen-/gruppen-bewusst aus der echten Config.

- fit.py: max_ctx_in_budget() als budget-basierter Kern; max_ctx_for() delegiert
- models.py: install nutzt setup_aware_ctx; /api/fit liefert assigned_ctx + Budget-Herleitung
- agent.py: nutzt die gemeinsamen Helfer (entfernt Duplikate _gtt_budget_gb/_foot)
- AddModel: Ampel zeigt den setup-bewussten ctx ('ctx -> Nk') inkl. Budget-Tooltip;
  Rollen-Wechsel laedt die Vorschau neu (Rolle bestimmt das Budget)

Effekt: heavy-122B bekommt z.B. 16k statt 131072 (passt neben dem Hirn), waehrend
warme Kleinmodelle weiter grossen Kontext erhalten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-27 14:13:40 +02:00
parent 14325e7690
commit bfa6844124
9 changed files with 577 additions and 454 deletions
+2
View File
@@ -77,6 +77,8 @@ export interface FitResp {
params_b: number
fit: Fit
optimal_ctx: number
assigned_ctx: number
budget: { gtt_gb: number; reserved_gb: number; budget_gb: number; mode: string }
sys_ram_gb: number
}
+20 -6
View File
@@ -21,12 +21,13 @@ export function AddModel() {
? modelsData?.models.find((m) => (m.role || "").toLowerCase() === role)
: undefined
async function refreshFit(r: string, qq: string) {
async function refreshFit(r: string, qq: string, rl: string) {
setOomArmed(false)
if (!r.trim()) { setFit(null); return }
try {
const d = await api<FitResp>(
`/api/fit?params_b=0&quant=${encodeURIComponent(qq)}&ctx=8192&name=${encodeURIComponent(r)}`
`/api/fit?params_b=0&quant=${encodeURIComponent(qq)}&ctx=8192` +
`&name=${encodeURIComponent(r)}&role=${encodeURIComponent(rl)}`
)
setFit(d)
} catch {
@@ -46,7 +47,7 @@ export function AddModel() {
const pick = d.quants.length ? (d.quants.includes("Q4_K_M") ? "Q4_K_M" : d.quants[0]) : quant
if (d.quants.length) setQuant(pick)
setMsg(d.quants.length ? "" : "Keine GGUF-Dateien in diesem Repository gefunden.")
if (d.quants.length) refreshFit(d.repo, pick)
if (d.quants.length) refreshFit(d.repo, pick, role)
} catch (e) {
setMsg(`Fehler: ${e}`)
}
@@ -54,7 +55,12 @@ export function AddModel() {
function onQuantChange(qq: string) {
setQuant(qq)
refreshFit(repo, qq)
refreshFit(repo, qq, role)
}
function onRoleChange(rl: string) {
setRole(rl)
if (quants.length) refreshFit(repo, quant, rl)
}
async function search() {
@@ -130,8 +136,8 @@ export function AddModel() {
</select>
<select
value={role}
onChange={(e) => setRole(e.target.value)}
title="Rolle (optional) — bestimmt Auto-Konfiguration wie parallele Slots"
onChange={(e) => onRoleChange(e.target.value)}
title="Rolle (optional) — bestimmt Auto-Konfiguration + setup-bewussten Kontext"
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>
@@ -161,6 +167,14 @@ export function AddModel() {
<span className="font-mono opacity-90">
~{fit.params_b}B · ~{fit.fit.req_gb} GB / {fit.sys_ram_gb} GB RAM · ~{fit.fit.tps} t/s
</span>
{fit.fit.level !== "too_tight" && (
<span
className="font-mono opacity-80"
title={`Setup-bewusst: GTT ${fit.budget.gtt_gb} GB reserviert ${fit.budget.reserved_gb} GB (${fit.budget.mode}) → ${fit.budget.budget_gb} GB frei`}
>
ctx {(fit.assigned_ctx / 1024).toFixed(0)}k
</span>
)}
{fit.fit.level === "too_tight" && (
<span className="opacity-90"> passt nicht in den Speicher, würde beim Laden abstürzen (OOM).</span>
)}