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

@ -57,7 +57,8 @@ Ausgabe bei Erfolg:
[✓] Judge (:8002) bereit [✓] Judge (:8002) bereit
``` ```
Dauer: 13 Minuten (Modell wird in GPU-VRAM geladen). Dauer: bis zu 5 Minuten (Modell wird in GPU-VRAM geladen; neuere llama.cpp-Versionen
prüfen beim Start die VRAM-Verfügbarkeit, was zusätzliche Zeit kostet).
### Status prüfen ### Status prüfen
@ -324,8 +325,18 @@ Fehler schnell. Erst ab Runde 2 (oder bei `--continue`) kommt der vollständige
Bei klarem `PASS` entfällt die ShipIt-Runde — es wird direkt SHIP ausgelöst. Bei klarem `PASS` entfällt die ShipIt-Runde — es wird direkt SHIP ausgelöst.
Mit `--approve-concerns` gilt das auch für `PASS WITH CONCERNS`. Mit `--approve-concerns` gilt das auch für `PASS WITH CONCERNS`.
Während des Ablaufs zeigt die Statuszeile immer die aktuelle Aktivität: Während des Ablaufs zeigt die Statuszeile die aktuelle Phase — mit laufendem Timer,
`Coder implementiert…``Quick-Check…``Coder fixt Blocker…``Judge reviewt (Runde 2/2)…` der beweist, dass die LLM tatsächlich arbeitet (und nicht hängt):
```
◉ Coder implementiert: Login-Flow mit JWT [01:23]
◉○ Runde 1/2: Quick-Check [00:47]
●◉ Runde 2/2: Coder fixt — fehlendes Null-Check [01:05]
●● ✓ PASS nach Runde 2/2 — ShipIt…
🚀 SHIP produktionsreif
```
Steht der Timer still, hängt der Prozess — dann `/cancel` verwenden.
### Beispiel: mehr Runden ### Beispiel: mehr Runden

121
README.md
View file

@ -36,10 +36,10 @@ den Endpunkten wenn du ein `/judge`-, `/fix`- oder `/coder`-Kommando aufrufst.
## Modelle ## Modelle
| Rolle | Modell | Port | Container | Alias | | Rolle | Modell | Port | Container | Alias | GPU |
|--------|---------------------------------------------------|------|------------------|----------------| |--------|---------------------------------------------------|------|------------------|----------------|----------|
| Coder | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS | 8001 | qwen36-27b-coder | qwen3.5-coder | | Coder | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS | 8001 | qwen36-27b-coder | qwen3.5-coder | device=1 |
| Judge | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS | 8002 | qwen36-27b-judge | qwen3.5-judge | | Judge | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS | 8002 | qwen36-27b-judge | qwen3.5-judge | device=2 |
Beide Container verwenden dasselbe GGUF-Datei, aber mit unterschiedlichen Beide Container verwenden dasselbe GGUF-Datei, aber mit unterschiedlichen
Serverparametern (Kontext, Temperatur, Parallelität). Serverparametern (Kontext, Temperatur, Parallelität).
@ -58,7 +58,8 @@ Serverparametern (Kontext, Temperatur, Parallelität).
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker sudo systemctl restart docker
``` ```
- Mindestens eine NVIDIA-GPU (empfohlen: zwei GPUs mit je ≥ 16 GB VRAM) - NVIDIA-GPUs: empfohlen 2 × RTX 3090 (je 24 GB VRAM) für Coder und Judge parallel.
Bei 3 GPUs: GPU 0 für Display reservieren, GPU 1 → Coder, GPU 2 → Judge.
- GGUF-Modell vorhanden unter: - GGUF-Modell vorhanden unter:
`$HF_HOME/models/qwen3/Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf` `$HF_HOME/models/qwen3/Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf`
- Standard-Pfad: `HF_HOME=/home/dschlueter/nvme2n1p7_home/huggingface` - Standard-Pfad: `HF_HOME=/home/dschlueter/nvme2n1p7_home/huggingface`
@ -110,7 +111,9 @@ Nach späteren Änderungen an `pi-coder-judge-extension.ts` oder `models.json`:
``` ```
`start-servers.sh` startet beide Container gleichzeitig und wartet bis beide `start-servers.sh` startet beide Container gleichzeitig und wartet bis beide
HTTP-ready sind. Logs werden getrennt gesammelt und nur bei Fehler ausgegeben. HTTP-ready sind (max. 5 Minuten — die neue llama.cpp-Version prüft beim Start, ob
Modell + KV-Cache in den VRAM passen, was zusätzliche Zeit kostet). Logs werden
getrennt gesammelt und nur bei Fehler ausgegeben.
Wenn Server bereits laufen und du `start-servers.sh` (oder ein Einzelskript) Wenn Server bereits laufen und du `start-servers.sh` (oder ein Einzelskript)
aufrufst, werden die laufenden Container zuerst per `docker rm -f` gestoppt aufrufst, werden die laufenden Container zuerst per `docker rm -f` gestoppt
@ -125,19 +128,20 @@ und dann neu gestartet — ein laufender Inference-Request wird dabei abgebroche
| Parameter | Wert | Bedeutung | | Parameter | Wert | Bedeutung |
|---|---|---| |---|---|---|
| `--jinja` | — | Verwendet das im GGUF eingebettete Jinja-Chat-Template (Qwen-Format). Notwendig für korrekte `<\|im_start\|>`-Tokens. | | `--jinja` | — | Verwendet das im GGUF eingebettete Jinja-Chat-Template (Qwen-Format). Notwendig für korrekte `<\|im_start\|>`-Tokens. |
| `--reasoning on` | — | Aktiviert das interne Thinking-Budget des Modells (Qwen3-spezifisch). Ersetzt das frühere `--chat-template-kwargs '{"enable_thinking":true}'`. |
| `--no-context-shift` | — | Kontextfenster wird **nicht** verschoben wenn es voll ist — stattdessen Fehler. Verhindert stille Datenverluste. | | `--no-context-shift` | — | Kontextfenster wird **nicht** verschoben wenn es voll ist — stattdessen Fehler. Verhindert stille Datenverluste. |
| `--repeat-penalty 1.05` | — | Leichte Penalty für Wiederholungen. Wert > 1.0 unterdrückt Loops. | | `--repeat-penalty 1.05` | — | Leichte Penalty für Wiederholungen. Wert > 1.0 unterdrückt Loops. |
| `--top-k 40` | — | Nur die 40 wahrscheinlichsten nächsten Tokens werden berücksichtigt. | | `--top-k 20` | — | Nur die 20 wahrscheinlichsten nächsten Tokens werden berücksichtigt. |
| `--min-p 0.01` | — | Tokens mit Wahrscheinlichkeit < 1 % des wahrscheinlichsten Tokens werden ausgeschlossen. | | `--min-p 0.01` | — | Tokens mit Wahrscheinlichkeit < 1 % des wahrscheinlichsten Tokens werden ausgeschlossen. |
| `-ngl 999` | — | Alle Layer auf die GPU laden (999 = „alle"). Bei zu wenig VRAM reduzieren. | | `-ngl 999` | — | Alle Layer auf die GPU laden (999 = „alle"). Bei zu wenig VRAM reduzieren. |
| `-fa on` | — | Flash Attention — schnellere Attention-Berechnung, weniger VRAM für den Attention-Pass. | | `-fa on` | — | Flash Attention — schnellere Attention-Berechnung, weniger VRAM für den Attention-Pass. |
| `--kv-unified` | — | Einheitlicher KV-Cache über alle Schichten. Effizienter bei langen Kontexten. | | `--kv-unified` | — | Einheitlicher KV-Cache über alle Schichten. Effizienter bei langen Kontexten. |
| `--cache-type-k q4_0` | — | KV-Cache Keys in 4-Bit quantisiert. Spart ~75 % VRAM gegenüber fp16 — nötig für 256K Kontext auf 2× 24 GB. | | `--cache-type-k q4_0` | — | KV-Cache Keys in 4-Bit quantisiert. Spart ~75 % VRAM gegenüber fp16 — nötig für 256K Kontext auf einer 24-GB-GPU. |
| `--cache-type-v q4_0` | — | KV-Cache Values ebenfalls 4-Bit quantisiert. | | `--cache-type-v q4_0` | — | KV-Cache Values ebenfalls 4-Bit quantisiert. |
| `--cont-batching` | — | Continuous Batching: neue Anfragen werden in laufende Batches eingefügt — höherer Durchsatz bei mehreren parallelen Anfragen. | | `--cont-batching` | — | Continuous Batching: neue Anfragen werden in laufende Batches eingefügt — höherer Durchsatz bei mehreren parallelen Anfragen. |
| `--main-gpu 0` | — | GPU-Index (0 = erste der übergebenen GPUs) für Nicht-Tensor-Operationen. | | `--main-gpu 0` | — | GPU-Index (0 = erste des Containers) für Nicht-Tensor-Operationen. |
| `--tensor-split 0.5,0.5` | — | Modell-Gewichte 50/50 auf zwei GPUs aufteilen. | | `--gpus '"device=1"'` (Coder) | — | Docker: nur GPU 1 dem Coder-Container zuweisen. |
| `--gpus '"device=1,2"'` | — | Docker-Argument: GPU 1 und GPU 2 dem Container übergeben. | | `--gpus '"device=2"'` (Judge) | — | Docker: nur GPU 2 dem Judge-Container zuweisen. |
### Coder-Server (Port 8001) — optimiert für Coding-Aufgaben ### Coder-Server (Port 8001) — optimiert für Coding-Aufgaben
@ -165,58 +169,60 @@ und dann neu gestartet — ein laufender Inference-Request wird dabei abgebroche
--- ---
## Anpassung für eine einzelne GPU ## GPU-Konfiguration und VRAM
Mit einer GPU läuft das Modell vollständig auf dieser GPU statt verteilt. ### Aktuelles Setup (1 GPU pro Server)
Anpassungen in `start-coder.sh` und `start-judge.sh`:
```bash Jeder Server bekommt eine dedizierte GPU — dadurch kein VRAM-Konflikt:
# Vorher (2 GPUs, device 1 und 2):
--gpus '"device=1,2"' \
--main-gpu 0 \
--tensor-split 0.5,0.5 \
# Nachher (1 GPU, z.B. device 0): ```
--gpus '"device=0"' \ GPU 0 NVIDIA T600 → Display / Monitor (nicht für KI genutzt)
--main-gpu 0 \ GPU 1 RTX 3090 (24 GB) → qwen36-27b-coder (Port 8001)
# --tensor-split ← diese Zeile komplett entfernen GPU 2 RTX 3090 (24 GB) → qwen36-27b-judge (Port 8002)
``` ```
### VRAM-Abschätzung für das 27B IQ4_XS-Modell Die Skripte verwenden `--gpus '"device=1"'` (Coder) bzw. `'"device=2"'` (Judge).
Innerhalb des Containers ist dies stets `CUDA:0`, daher `--main-gpu 0` ohne `--tensor-split`.
### VRAM-Abschätzung für das 27B IQ4_XS-Modell (eine GPU)
| Komponente | Größe (ca.) | | Komponente | Größe (ca.) |
|---|---| |---|---|
| Modell-Gewichte (IQ4_XS, 27B) | ~14,5 GB | | Modell-Gewichte (IQ4_XS, 27B) | ~14 GB |
| KV-Cache bei 128K Kontext (q8_0) | ~14 GB | | KV-Cache bei 262 144 Tokens (q4_0) | ~810 GB |
| KV-Cache bei 64K Kontext (q8_0) | ~7 GB | | KV-Cache bei 131 072 Tokens (q4_0) | ~45 GB |
| KV-Cache bei 32K Kontext (q8_0) | ~3,5 GB | | KV-Cache bei 65 536 Tokens (q4_0) | ~23 GB |
| **Summe (262k Kontext)** | **~2224 GB** |
Bei einer **24-GB-GPU** ist nur ein Server gleichzeitig sinnvoll betreibbar: Bei einer **24-GB-GPU** ist 262k Kontext knapp kalkuliert. Wichtig: keine anderen Prozesse
- Modell-Gewichte: ~14,5 GB dürfen nennenswert VRAM belegen (z. B. TTS-Dienste, Ollama). Prüfen mit:
- KV-Cache bei 32K Kontext: ~3,5 GB
- Summe: ~18 GB → passt mit Puffer
**Empfehlung für eine 24-GB-GPU:**
```bash ```bash
# Coder — Kontext reduzieren nvidia-smi --query-gpu=index,memory.used,memory.free --format=csv
-c 32768 # statt 131072 ```
-n 8192 # statt 16384
# Judge — Kontext reduzieren Falls der VRAM nicht reicht: Kontext in den Startskripten auf `131072` oder `65536` reduzieren.
-c 32768 # statt 131072
### Anpassung für andere GPU-Konfigurationen
```bash
# 2 GPUs (beide für KI, keine Display-GPU):
--gpus '"device=0"' # Coder
--gpus '"device=1"' # Judge
# 1 GPU (beide Server auf gleicher GPU — nicht empfohlen, VRAM-Konflikt):
--gpus '"device=0"' # beide Server
--tensor-split 0.5,0.5 # Modell hälftig aufteilen
-c 65536 # Kontext stark reduzieren
# 2 GPUs für einen Server (wenn sehr großer Kontext nötig):
--gpus '"device=0,1"' \
--tensor-split 0.5,0.5 \
--main-gpu 0 \
-c 262144
``` ```
Bei einer **16-GB-GPU** ist die Modellgröße allein schon grenzwertig. Bei einer **16-GB-GPU** ist die Modellgröße allein schon grenzwertig.
Entweder ein kleineres Modell verwenden oder die Quantisierung weiter erhöhen (IQ3_XS, Q4_K_M). Entweder ein kleineres Modell verwenden oder die Quantisierung erhöhen (IQ3_XS, Q4_K_M).
### Beide Server auf einer GPU betreiben
Technisch möglich, aber beide Server laden das Modell gleichzeitig → doppelter VRAM-Bedarf.
Auf einer 24-GB-GPU daher **nicht empfohlen**. Alternativen:
- Nur einen Server gleichzeitig starten (manuell umschalten)
- Kleinere Quantisierung wählen (IQ3_XS: ~11 GB)
- `ollama` als Alternative — lädt Modelle bei Bedarf und entlädt sie wieder
--- ---
@ -243,7 +249,7 @@ KV-Cache ≈ `context_size × 28 × 128 × 32 × 2 × 1 Byte ≈ context_size ×
| 32 768 | ~1,9 GB | 1 × 16-GB-GPU | | 32 768 | ~1,9 GB | 1 × 16-GB-GPU |
| 65 536 | ~3,7 GB | 1 × 24-GB-GPU | | 65 536 | ~3,7 GB | 1 × 24-GB-GPU |
| 131 072 | ~7,5 GB | 2 × 16-GB-GPU | | 131 072 | ~7,5 GB | 2 × 16-GB-GPU |
| 262 144 | ~15 GB | 2 × 24-GB-GPU**aktuell gesetzt** | | 262 144 | ~810 GB | 1 × 24-GB-GPU (q4_0)**aktuell gesetzt** |
### KV-Cache-Quantisierung ### KV-Cache-Quantisierung
@ -306,16 +312,19 @@ Ausführliche Beschreibung aller Kommandos mit Beispielen: siehe **BEDIENUNGSANL
## Live-Aktivitätsstatus ## Live-Aktivitätsstatus
Während der Ausführung zeigt pi_coder in der Statuszeile, was gerade passiert: Während der Ausführung zeigt pi_coder in der Statuszeile, was gerade passiert —
inklusive eines laufenden `[MM:SS]`-Timers während jeder LLM-Inference-Phase:
| Situation | Anzeige | | Situation | Anzeige |
|---|---| |---|---|
| Coder implementiert | `Coder implementiert…` | | Coder implementiert | `◉ Coder implementiert: Login-Flow mit JWT [01:23]` |
| edit-Tool aktiv | `Editiere src/main.py…` | | edit-Tool aktiv | `Editiere src/main.py…` |
| git commit | `Git-Commit…` | | git commit | `Git-Commit…` |
| Judge reviewt (Runde 2/2) | `Judge reviewt (Runde 2/2)…` | | Judge (Quick-Check, Runde 1) | `◉○ Runde 1/2: Quick-Check [00:47]` |
| Tests laufen | `Tests laufen…` | | Judge (voller Review, Runde 2) | `●◉ Runde 2/2: Judge — TASK.md + letzter Commit + Tests [02:15]` |
| Fix-Phase | `Coder fixt Blocker…` | | Tests laufen | `●○ Runde 1/2: Tests laufen (pytest, max. 120s)…` |
| Fix-Phase | `●◉ Runde 2/2: Coder fixt — fehlendes Null-Check [01:05]` |
| ShipIt | `●●◉ ShipIt — finale Freigabe [00:33]` |
| Interactive-Pause (--interactive) | `⏸ PASS warte auf /continue…` | | Interactive-Pause (--interactive) | `⏸ PASS warte auf /continue…` |
So ist jederzeit erkennbar, in welcher Phase sich der automatische Loop befindet. Der Timer beweist, dass die LLM tatsächlich arbeitet. Steht er still, hängt der Prozess.

View file

@ -888,6 +888,28 @@ let interactivePauseActive = false;
let interactiveContinueRequested = false; let interactiveContinueRequested = false;
let interactivePauseTask = ""; let interactivePauseTask = "";
let currentActivity = ""; // Working-Message für den aktuellen Command-Kontext 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. // Erzeugt eine knappe Statuszeile aus Tool-Name und Argumenten.
function toolExecutionLabel(toolName: string, args: Record<string, any>): string { function toolExecutionLabel(toolName: string, args: Record<string, any>): string {
@ -1126,7 +1148,9 @@ export default function (pi: ExtensionAPI) {
return; return;
} }
currentActivity = "Coder implementiert…"; currentActivity = "Coder implementiert…";
startPhaseTimer(ctx, "optimize", `◉ Coder implementiert: ${taskPreview}`);
await sendAndWait(pi, ctx, coderKickoff(task)); await sendAndWait(pi, ctx, coderKickoff(task));
stopPhaseTimer();
await tickTaskMdStatus(pi, ctx, "Implementierung"); await tickTaskMdStatus(pi, ctx, "Implementierung");
if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", "Nach Implementierung"); return; } 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)…`); ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: Tests laufen (${label}, max. ${testTimeout}s)…`);
const testOutput = await runTestsParallel(pi, ctx, autoTestCmds, testTimeout); const testOutput = await runTestsParallel(pi, ctx, autoTestCmds, testTimeout);
const judgeLabel = useQuickJudge ? "Quick-Check" : "Judge analysiert"; const judgeLabel = useQuickJudge ? "Quick-Check" : "Judge analysiert";
ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: ${judgeLabel} Test-Ergebnis…`); currentActivity = `Judge Runde ${round}/${maxRounds}${useQuickJudge ? " (Quick)" : ""}`;
currentActivity = `Judge reviewt (Runde ${round}/${maxRounds})…`; startPhaseTimer(ctx, "optimize", `${prog} Runde ${round}/${maxRounds}: ${judgeLabel} Test-Ergebnis`);
await sendAndWait(pi, ctx, useQuickJudge await sendAndWait(pi, ctx, useQuickJudge
? quickJudgeWithTestsPrompt(testOutput, "") ? quickJudgeWithTestsPrompt(testOutput, "")
: judgeWithTestsPrompt(testOutput, "")); : judgeWithTestsPrompt(testOutput, ""));
stopPhaseTimer();
} else { } else {
const judgeLabel = useQuickJudge ? "Quick-Check" : "Judge — TASK.md + letzter Commit + Tests"; const judgeLabel = useQuickJudge ? "Quick-Check" : "Judge — TASK.md + letzter Commit + Tests";
ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: ${judgeLabel}`); currentActivity = `Judge Runde ${round}/${maxRounds}${useQuickJudge ? " (Quick)" : ""}`;
currentActivity = `Judge reviewt (Runde ${round}/${maxRounds})…`; startPhaseTimer(ctx, "optimize", `${prog} Runde ${round}/${maxRounds}: ${judgeLabel}`);
await sendAndWait(pi, ctx, useQuickJudge ? quickJudgePrompt("") : judgePrompt("")); await sendAndWait(pi, ctx, useQuickJudge ? quickJudgePrompt("") : judgePrompt(""));
stopPhaseTimer();
} }
if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", `Nach Judge Runde ${round}`); return; } if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", `Nach Judge Runde ${round}`); return; }
@ -1234,13 +1260,14 @@ export default function (pi: ExtensionAPI) {
const blockerHint = currentBlockers const blockerHint = currentBlockers
? (currentBlockers.length > 50 ? currentBlockers.slice(0, 47) + "…" : currentBlockers) ? (currentBlockers.length > 50 ? currentBlockers.slice(0, 47) + "…" : currentBlockers)
: "Kritikpunkte aus Judge-Bericht"; : "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")) { if (!await switchModel(pi, ctx, "llama-cpp-coder", "qwen3.5-coder")) {
finalNotify(ctx, "⛔ Modell-Fehler", "Coder-Modell (llama-cpp-coder) nicht verfügbar"); finalNotify(ctx, "⛔ Modell-Fehler", "Coder-Modell (llama-cpp-coder) nicht verfügbar");
return; return;
} }
currentActivity = "Coder fixt Blocker…"; currentActivity = `Coder fixt: ${blockerHint}`;
startPhaseTimer(ctx, "optimize", `${prog} Runde ${round}/${maxRounds}: Coder fixt — ${blockerHint}`);
await sendAndWait(pi, ctx, fixPrompt("")); await sendAndWait(pi, ctx, fixPrompt(""));
stopPhaseTimer();
if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", `Nach Fix Runde ${round}`); return; } if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", `Nach Fix Runde ${round}`); return; }
} }
@ -1282,7 +1309,9 @@ export default function (pi: ExtensionAPI) {
return; return;
} }
currentActivity = "Coder implementiert Zusatzauftrag…"; currentActivity = "Coder implementiert Zusatzauftrag…";
startPhaseTimer(ctx, "optimize", `◉ Coder implementiert Zusatzauftrag: ${addPreview}`);
await sendAndWait(pi, ctx, coderKickoff(interactivePauseTask)); await sendAndWait(pi, ctx, coderKickoff(interactivePauseTask));
stopPhaseTimer();
await tickTaskMdStatus(pi, ctx, "Implementierung"); await tickTaskMdStatus(pi, ctx, "Implementierung");
if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", "Nach Zusatz-Implementierung"); return; } if (cancelRequested) { finalNotify(ctx, "⛔ Abgebrochen", "Nach Zusatz-Implementierung"); return; }
keepGoing = true; keepGoing = true;
@ -1313,7 +1342,9 @@ export default function (pi: ExtensionAPI) {
return; return;
} }
currentActivity = "Judge: finale Freigabe…"; currentActivity = "Judge: finale Freigabe…";
startPhaseTimer(ctx, "optimize", `${"●".repeat(maxRounds)}◉ ShipIt — finale Freigabe`);
await sendAndWait(pi, ctx, shipitPrompt("")); await sendAndWait(pi, ctx, shipitPrompt(""));
stopPhaseTimer();
const shipText = getLastAssistantText(ctx); const shipText = getLastAssistantText(ctx);
const shipVerdict = shipText.match(/Urteil:\s*(SHIP|NO-SHIP)/i)?.[1]?.toUpperCase() ?? ""; const shipVerdict = shipText.match(/Urteil:\s*(SHIP|NO-SHIP)/i)?.[1]?.toUpperCase() ?? "";
@ -1340,6 +1371,7 @@ export default function (pi: ExtensionAPI) {
} catch (e: any) { } catch (e: any) {
finalNotify(ctx, "⛔ Fehler", String(e?.message ?? e)); finalNotify(ctx, "⛔ Fehler", String(e?.message ?? e));
} finally { } finally {
stopPhaseTimer();
// Sicherstellen dass keine Zustandsvariable in späteren /optimize-Aufruf leckt // Sicherstellen dass keine Zustandsvariable in späteren /optimize-Aufruf leckt
cancelRequested = false; cancelRequested = false;
currentModelKey = ""; currentModelKey = "";

View file

@ -22,7 +22,7 @@ fi
echo "[*] Starte llama.cpp-Server für Coder ..." echo "[*] Starte llama.cpp-Server für Coder ..."
docker run -d \ docker run -d \
--gpus '"device=1,2"' \ --gpus '"device=1"' \
--name "$CONTAINER_NAME" \ --name "$CONTAINER_NAME" \
--restart unless-stopped \ --restart unless-stopped \
-e HF_HOME="/hf_home" \ -e HF_HOME="/hf_home" \
@ -34,7 +34,7 @@ docker run -d \
-c 262144 \ -c 262144 \
-n 16384 \ -n 16384 \
--jinja \ --jinja \
--chat-template-kwargs '{"enable_thinking":true}' \ --reasoning on \
--no-context-shift \ --no-context-shift \
--temp 0.6 \ --temp 0.6 \
--top-p 0.80 \ --top-p 0.80 \
@ -42,7 +42,6 @@ docker run -d \
--min-p 0.01 \ --min-p 0.01 \
--repeat-penalty 1.05 \ --repeat-penalty 1.05 \
--main-gpu 0 \ --main-gpu 0 \
--tensor-split 0.5,0.5 \
-ngl 999 \ -ngl 999 \
-fa on \ -fa on \
--kv-unified \ --kv-unified \
@ -57,7 +56,7 @@ docker run -d \
echo "[*] Warte auf Modell-Bereitschaft (Completion-Check, max. 180 s) ..." echo "[*] Warte auf Modell-Bereitschaft (Completion-Check, max. 180 s) ..."
MODEL_READY=0 MODEL_READY=0
for i in {1..90}; do for i in {1..150}; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
-X POST "http://localhost:${HOST_PORT}/v1/chat/completions" \ -X POST "http://localhost:${HOST_PORT}/v1/chat/completions" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \

View file

@ -22,7 +22,7 @@ fi
echo "[*] Starte llama.cpp-Server für Judge ..." echo "[*] Starte llama.cpp-Server für Judge ..."
docker run -d \ docker run -d \
--gpus '"device=1,2"' \ --gpus '"device=2"' \
--name "$CONTAINER_NAME" \ --name "$CONTAINER_NAME" \
--restart unless-stopped \ --restart unless-stopped \
-e HF_HOME="/hf_home" \ -e HF_HOME="/hf_home" \
@ -34,7 +34,7 @@ docker run -d \
-c 262144 \ -c 262144 \
-n 16384 \ -n 16384 \
--jinja \ --jinja \
--chat-template-kwargs '{"enable_thinking":true}' \ --reasoning on \
--no-context-shift \ --no-context-shift \
--temp 0.7 \ --temp 0.7 \
--top-p 0.80 \ --top-p 0.80 \
@ -42,7 +42,6 @@ docker run -d \
--min-p 0.01 \ --min-p 0.01 \
--repeat-penalty 1.05 \ --repeat-penalty 1.05 \
--main-gpu 0 \ --main-gpu 0 \
--tensor-split 0.5,0.5 \
-ngl 999 \ -ngl 999 \
-fa on \ -fa on \
--kv-unified \ --kv-unified \
@ -57,7 +56,7 @@ docker run -d \
echo "[*] Warte auf Modell-Bereitschaft (Completion-Check, max. 180 s) ..." echo "[*] Warte auf Modell-Bereitschaft (Completion-Check, max. 180 s) ..."
MODEL_READY=0 MODEL_READY=0
for i in {1..90}; do for i in {1..150}; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
-X POST "http://localhost:${HOST_PORT}/v1/chat/completions" \ -X POST "http://localhost:${HOST_PORT}/v1/chat/completions" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \