feat: GPU-Split, Phase-Timer, llama.cpp-Update-Fixes

GPU-Setup:
- Coder → GPU 1 (device=1), Judge → GPU 2 (device=2), kein --tensor-split
- --chat-template-kwargs deprecated → --reasoning on (neue llama.cpp-API)
- Smoke-Test-Timeout 180s → 300s (neue fitting-Phase beim Start)

Fortschrittsanzeige:
- Phase-Timer [MM:SS] in Statuszeile während LLM-Inference (sendAndWait)
- currentActivity in Fix-Phase zeigt konkreten Blocker statt generischem Text

Dokumentation:
- README: GPU-Tabelle, VRAM-Abschätzung, --reasoning on, Timer-Beispiele
- BEDIENUNGSANLEITUNG: Ladezeit, Fortschrittsanzeige mit Timer-Beispielen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-15 00:19:23 +02:00
commit 3f7ffe60c2
5 changed files with 122 additions and 72 deletions

View file

@ -888,6 +888,28 @@ let interactivePauseActive = false;
let interactiveContinueRequested = false;
let interactivePauseTask = "";
let currentActivity = ""; // Working-Message für den aktuellen Command-Kontext
let phaseTimerHandle: ReturnType<typeof setInterval> | null = null;
// Startet einen Sekundentakt, der [MM:SS] in die Statuszeile schreibt — zeigt dem Nutzer,
// dass die LLM-Inference läuft und nicht hängt. Muss mit stopPhaseTimer() beendet werden.
function startPhaseTimer(ctx: ExtensionCommandContext, statusKey: string, label: string): void {
stopPhaseTimer();
const start = Date.now();
ctx.ui.setStatus(statusKey, `${label} [00:00]`);
phaseTimerHandle = setInterval(() => {
const s = Math.round((Date.now() - start) / 1000);
const mm = String(Math.floor(s / 60)).padStart(2, "0");
const ss = String(s % 60).padStart(2, "0");
ctx.ui.setStatus(statusKey, `${label} [${mm}:${ss}]`);
}, 1000);
}
function stopPhaseTimer(): void {
if (phaseTimerHandle !== null) {
clearInterval(phaseTimerHandle);
phaseTimerHandle = null;
}
}
// Erzeugt eine knappe Statuszeile aus Tool-Name und Argumenten.
function toolExecutionLabel(toolName: string, args: Record<string, any>): string {
@ -1126,7 +1148,9 @@ export default function (pi: ExtensionAPI) {
return;
}
currentActivity = "Coder implementiert…";
startPhaseTimer(ctx, "optimize", `◉ Coder implementiert: ${taskPreview}`);
await sendAndWait(pi, ctx, coderKickoff(task));
stopPhaseTimer();
await tickTaskMdStatus(pi, ctx, "Implementierung");
if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", "Nach Implementierung"); return; }
@ -1187,16 +1211,18 @@ export default function (pi: ExtensionAPI) {
ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: Tests laufen (${label}, max. ${testTimeout}s)…`);
const testOutput = await runTestsParallel(pi, ctx, autoTestCmds, testTimeout);
const judgeLabel = useQuickJudge ? "Quick-Check" : "Judge analysiert";
ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: ${judgeLabel} Test-Ergebnis…`);
currentActivity = `Judge reviewt (Runde ${round}/${maxRounds})…`;
currentActivity = `Judge Runde ${round}/${maxRounds}${useQuickJudge ? " (Quick)" : ""}`;
startPhaseTimer(ctx, "optimize", `${prog} Runde ${round}/${maxRounds}: ${judgeLabel} Test-Ergebnis`);
await sendAndWait(pi, ctx, useQuickJudge
? quickJudgeWithTestsPrompt(testOutput, "")
: judgeWithTestsPrompt(testOutput, ""));
stopPhaseTimer();
} else {
const judgeLabel = useQuickJudge ? "Quick-Check" : "Judge — TASK.md + letzter Commit + Tests";
ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: ${judgeLabel}`);
currentActivity = `Judge reviewt (Runde ${round}/${maxRounds})…`;
currentActivity = `Judge Runde ${round}/${maxRounds}${useQuickJudge ? " (Quick)" : ""}`;
startPhaseTimer(ctx, "optimize", `${prog} Runde ${round}/${maxRounds}: ${judgeLabel}`);
await sendAndWait(pi, ctx, useQuickJudge ? quickJudgePrompt("") : judgePrompt(""));
stopPhaseTimer();
}
if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", `Nach Judge Runde ${round}`); return; }
@ -1234,13 +1260,14 @@ export default function (pi: ExtensionAPI) {
const blockerHint = currentBlockers
? (currentBlockers.length > 50 ? currentBlockers.slice(0, 47) + "…" : currentBlockers)
: "Kritikpunkte aus Judge-Bericht";
ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: Coder fixt — ${blockerHint}`);
if (!await switchModel(pi, ctx, "llama-cpp-coder", "qwen3.5-coder")) {
finalNotify(ctx, "⛔ Modell-Fehler", "Coder-Modell (llama-cpp-coder) nicht verfügbar");
return;
}
currentActivity = "Coder fixt Blocker…";
currentActivity = `Coder fixt: ${blockerHint}`;
startPhaseTimer(ctx, "optimize", `${prog} Runde ${round}/${maxRounds}: Coder fixt — ${blockerHint}`);
await sendAndWait(pi, ctx, fixPrompt(""));
stopPhaseTimer();
if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", `Nach Fix Runde ${round}`); return; }
}
@ -1282,7 +1309,9 @@ export default function (pi: ExtensionAPI) {
return;
}
currentActivity = "Coder implementiert Zusatzauftrag…";
startPhaseTimer(ctx, "optimize", `◉ Coder implementiert Zusatzauftrag: ${addPreview}`);
await sendAndWait(pi, ctx, coderKickoff(interactivePauseTask));
stopPhaseTimer();
await tickTaskMdStatus(pi, ctx, "Implementierung");
if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", "Nach Zusatz-Implementierung"); return; }
keepGoing = true;
@ -1313,7 +1342,9 @@ export default function (pi: ExtensionAPI) {
return;
}
currentActivity = "Judge: finale Freigabe…";
startPhaseTimer(ctx, "optimize", `${"●".repeat(maxRounds)}◉ ShipIt — finale Freigabe`);
await sendAndWait(pi, ctx, shipitPrompt(""));
stopPhaseTimer();
const shipText = getLastAssistantText(ctx);
const shipVerdict = shipText.match(/Urteil:\s*(SHIP|NO-SHIP)/i)?.[1]?.toUpperCase() ?? "";
@ -1340,6 +1371,7 @@ export default function (pi: ExtensionAPI) {
} catch (e: any) {
finalNotify(ctx, "⛔ Fehler", String(e?.message ?? e));
} finally {
stopPhaseTimer();
// Sicherstellen dass keine Zustandsvariable in späteren /optimize-Aufruf leckt
cancelRequested = false;
currentModelKey = "";