From 485ab302656132def685fedc8696d3ce2d0f773b Mon Sep 17 00:00:00 2001 From: dschlueter Date: Mon, 15 Jun 2026 05:03:50 +0200 Subject: [PATCH] docs: LICENCE.md-Feature, .gitignore, Single-GPU-Doku MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pi-coder-judge-extension.ts: licenceMd() + writeLicenceMd() — erzeugt proprietäre LICENCE.md in der Doku-Phase (nur wenn noch nicht vorhanden) - tests: Guards und pure-logic-Tests für LICENCE.md ergänzt (38 Tests, alle grün) - .gitignore: Node, TypeScript-Artefakte, Backups, Editor-Dateien, OS-Metadaten - README.md + BEDIENUNGSANLEITUNG.md: vollständig auf Single-GPU-Architektur aktualisiert (ein Server, System-Prompt-Rollen, kein Quick-Judge, PASS WITH CONCERNS-Verhalten, LICENCE.md in /update_doku, start-single.sh) - SINGLE_SERVER_PLAN.md: entfernt (umgesetzt, kein Mehrwert mehr) Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 21 +++ BEDIENUNGSANLEITUNG.md | 162 ++++++++++++---------- README.md | 238 +++++++++++++++----------------- SINGLE_SERVER_PLAN.md | 150 -------------------- pi-coder-judge-extension.ts | 55 +++++++- tests/extension-guards.test.mjs | 14 ++ tests/pure-logic.test.mjs | 14 ++ 7 files changed, 297 insertions(+), 357 deletions(-) create mode 100644 .gitignore delete mode 100644 SINGLE_SERVER_PLAN.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..357bc1c --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Node (aktuell keine Dependencies, aber falls welche hinzukommen) +node_modules/ +npm-debug.log* +*.log + +# TypeScript-/Build-Artefakte +*.tsbuildinfo +dist/ + +# Backup-Dateien (z. B. settings.json.bak aus dem Install-Skript) +*.bak + +# Editor / IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db diff --git a/BEDIENUNGSANLEITUNG.md b/BEDIENUNGSANLEITUNG.md index 7665547..f56f927 100644 --- a/BEDIENUNGSANLEITUNG.md +++ b/BEDIENUNGSANLEITUNG.md @@ -1,8 +1,8 @@ # Bedienungsanleitung: pi_coder -pi_coder ist ein Werkzeug, das zwei lokale KI-Modelle als **Coder** und **Judge** einsetzt, -um Software automatisch zu schreiben, zu prüfen und zu verbessern — alles gesteuert über -einfache Slash-Kommandos in der pi-Agent-Oberfläche. +pi_coder ist ein Werkzeug, das ein lokales KI-Modell abwechselnd als **Coder** und **Judge** +einsetzt, um Software automatisch zu schreiben, zu prüfen und zu verbessern — alles gesteuert +über einfache Slash-Kommandos in der pi-Agent-Oberfläche. --- @@ -25,17 +25,22 @@ einfache Slash-Kommandos in der pi-Agent-Oberfläche. ## 1. Konzept: Coder und Judge -pi_coder verwendet zwei Rollen: +pi_coder verwendet zwei Rollen — bedient von **einem einzigen Modell**, das je nach Aufgabe +eine andere Persona annimmt: -**Coder** (Port 8001): Schreibt und repariert Code. Liest die Aufgabe aus `TASK.md`, -implementiert sie, führt Tests aus und erstellt Git-Commits. +**Coder-Persona**: Schreibt und repariert Code. Liest die Aufgabe aus `TASK.md`, implementiert +sie, führt Tests aus und erstellt Git-Commits. -**Judge** (Port 8002): Überprüft den Code mit dem Blick eines skeptischen Senior-Entwicklers. +**Judge-Persona**: Überprüft den Code mit dem Blick eines skeptischen Senior-Entwicklers. Prüft Korrektheit, Robustheit, Randfälle, Sicherheit und Produktionsreife. Gibt ein Urteil: - `PASS` — Code ist in Ordnung -- `PASS WITH CONCERNS` — grundsätzlich akzeptabel, aber mit Anmerkungen +- `PASS WITH CONCERNS` — hat Anmerkungen, die behoben werden müssen (wird wie `FAIL` behandelt, + außer du verwendest `--approve-concerns`) - `FAIL` — enthält Blocker, die behoben werden müssen +Die Umschaltung zwischen den Rollen erfolgt über unterschiedliche System-Prompts, die die +Extension automatisch vor jeder Inference einsetzt. Es läuft kein zweiter Server. + Der Grundgedanke: Coder und Judge haben keine „Höflichkeitsschranke" zueinander — der Judge kritisiert direkt und konkret, der Coder repariert ohne Widerspruch. @@ -47,14 +52,14 @@ der Judge kritisiert direkt und konkret, der Coder repariert ohne Widerspruch. ```bash cd ~/pi_coder -./start-servers.sh +./start-single.sh ``` Ausgabe bei Erfolg: ``` -[*] Starte beide Server parallel ... -[✓] Coder (:8001) bereit -[✓] Judge (:8002) bereit +[*] Verwende HF_HOME = /home/.../huggingface +[*] GPU device = 1 +[✓] Single-Server (:8001) bereit ``` Dauer: bis zu 5 Minuten (Modell wird in GPU-VRAM geladen; neuere llama.cpp-Versionen @@ -68,8 +73,7 @@ prüfen beim Start die VRAM-Verfügbarkeit, was zusätzliche Zeit kostet). ``` === LLaMA-Server Status === -qwen36-27b-coder (Port 8001): Container=RUNNING HTTP=OK -qwen36-27b-judge (Port 8002): Container=RUNNING HTTP=OK +qwen36-27b-single (Port 8001): Container=RUNNING HTTP=OK ``` ### pi agent öffnen @@ -86,53 +90,41 @@ pi ## 3. Server starten und stoppen -### GPU-Zuordnung (3-GPU-Setup) +### GPU-Zuordnung -Bei einem System mit drei GPUs sind die Rollen fest in den Startskripten verankert — -**keine manuelle Konfiguration nötig:** +Der Server läuft standardmäßig auf **GPU 1** (überschreibbar): -| GPU | Modell | Rolle | -|-----|--------|-------| -| GPU 0 (NVIDIA T600) | — | Display / Monitor — wird von den Skripten nicht angetastet | -| GPU 1 (RTX 3090, 24 GB) | Qwen3.6-27B | **Coder** (Port 8001) | -| GPU 2 (RTX 3090, 24 GB) | Qwen3.6-27B | **Judge** (Port 8002) | +```bash +./start-single.sh # GPU 1 (Standard) +GPU_DEVICE=0 ./start-single.sh # GPU 0 +``` -Jeder Server bekommt seine eigene GPU exklusiv — so gibt es keinen VRAM-Konflikt, -auch wenn beide gleichzeitig laufen. +Auf einem System mit mehreren GPUs empfiehlt es sich, GPU 0 (Display) freizuhalten +und GPU 1 für den KI-Server zu verwenden. -**Wichtig:** Andere GPU-Prozesse (z. B. TTS-Dienste, Ollama) auf GPU 1 oder GPU 2 können -VRAM belegen und dazu führen, dass ein Server nicht startet. Im Zweifel prüfen: +**Wichtig:** Andere GPU-Prozesse (z. B. TTS-Dienste, Ollama) auf der verwendeten GPU +können VRAM belegen und dazu führen, dass der Server nicht startet. Im Zweifel prüfen: ```bash nvidia-smi --query-gpu=index,name,memory.used,memory.free --format=csv ``` -### Beide starten (empfohlen) +### Server starten ```bash cd ~/pi_coder -./start-servers.sh +./start-single.sh ``` Erwartete Ausgabe: ``` -[*] Starte beide Server parallel ... -[✓] Coder (:8001) bereit -[✓] Judge (:8002) bereit +[*] Verwende HF_HOME = /home/.../huggingface +[*] GPU device = 1 +[✓] Single-Server (:8001) bereit ``` -Dauer: bis zu 5 Minuten. Danach sind beide Modelle im VRAM und sofort antwortbereit. +Dauer: bis zu 5 Minuten. Danach ist das Modell im VRAM und sofort antwortbereit. -### Einzelnen Server neu starten - -Z.B. wenn nur der Judge-Server abgestürzt ist: - -```bash -./start-judge.sh # Port 8002, GPU 2 -# oder: -./start-coder.sh # Port 8001, GPU 1 -``` - -### Beide stoppen +### Server stoppen ```bash ./stop-servers.sh @@ -149,7 +141,7 @@ Z.B. wenn nur der Judge-Server abgestürzt ist: Falls die GGUF-Datei an einem anderen Ort liegt: ```bash -HF_HOME=/mnt/daten/huggingface ./start-servers.sh +HF_HOME=/mnt/daten/huggingface ./start-single.sh ``` --- @@ -235,7 +227,7 @@ Der Judge: 3. Führt Tests aus 4. Gibt ein strukturiertes Urteil aus -**Beispiel-Ausgabe PASS:** +**Beispiel-Ausgabe PASS WITH CONCERNS:** ``` Urteil: PASS WITH CONCERNS @@ -326,17 +318,22 @@ Empfohlene Sofortmaßnahmen: keine - `--continue` — überspringt die Implementierungsphase und startet direkt mit dem Judge→Fix-Zyklus ab dem aktuellen Code-Stand. Nützlich wenn man bereits manuell `/coder`, `/judge` und `/fix` durchgeführt hat und den Rest automatisieren möchte. - Im `--continue`-Modus werden Coder- und Judge-Server gleichzeitig geprüft. - `--interactive` — pausiert nach erstem PASS für einen menschlichen Checkpoint. Details: siehe [Interactive-Modus](#interactive-modus) weiter unten. - `--no-tests` — überspringt die automatische Test-Erkennung. Sinnvoll wenn keine Test-Suite vorhanden ist oder Tests über externe Infrastruktur laufen. - `--approve-concerns` — behandelt „PASS WITH CONCERNS" wie „PASS": kein ShipIt-Call, - direktes SHIP. Für Projekte, bei denen du dem Judge-Urteil vertraust. + direktes SHIP. Standardmäßig löst PASS WITH CONCERNS immer einen Coder-Fix aus. - `--test-cmd "befehl"` — überschreibt die automatische Test-Erkennung mit einem eigenen Befehl (z.B. `--test-cmd "pytest tests/ -x"`). - `--test-timeout N` — maximale Laufzeit pro Test-Befehl in Sekunden (Standard: 120). +### PASS WITH CONCERNS — wichtig + +Ohne `--approve-concerns` gilt `PASS WITH CONCERNS` **wie `FAIL`** — der Coder bekommt +einen Fix-Auftrag und der Loop geht weiter. Erst sauberes `PASS` beendet die Schleife. +Mit `--approve-concerns` gilt PASS WITH CONCERNS als bestanden. + ### Beispiel: einfacher Auftrag ``` @@ -346,31 +343,33 @@ Empfohlene Sofortmaßnahmen: keine Was im Hintergrund passiert: ``` Phase 1: Coder implementiert... -Phase 2: Runde 1/2: Quick-Check (kompakter Erstcheck)... +Phase 2: Runde 1/2: Judge — TASK.md + letzter Commit + Tests... → Urteil: FAIL (2 Blocker) Phase 3: Runde 1/2: Coder fixt... Phase 4: Runde 2/2: Judge — TASK.md + letzter Commit + Tests... - → Urteil: PASS WITH CONCERNS -✓ PASS WITH CONCERNS nach Runde 2 -Finale ShipIt-Prüfung... (nur bei PASS WITH CONCERNS, nicht bei klarem PASS) - → SHIP + → Urteil: PASS +✓ PASS nach Runde 2 → SHIP [Dialog: Version → v0.1.0 (empfohlen)] ``` -**Runde 1 = Quick-Check:** Kompakter Prompt ohne TASK.md-Analyse — erkennt offensichtliche -Fehler schnell. Erst ab Runde 2 (oder bei `--continue`) kommt der vollständige Judge-Prompt. +Bei `PASS WITH CONCERNS` (ohne --approve-concerns): +``` + → Urteil: PASS WITH CONCERNS (Major: fehlendes --help) +Phase 3: Runde 2/2: Coder fixt Concerns... +Phase 4: Runde 3/3: Judge... + → Urteil: PASS +✓ PASS → SHIP +``` 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`. -Während des Ablaufs zeigt die Statuszeile die aktuelle Phase — mit laufendem Timer, -der beweist, dass die LLM tatsächlich arbeitet (und nicht hängt): +Während des Ablaufs zeigt die Statuszeile die aktuelle Phase — mit laufendem Timer: ``` ◉ 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… +◉○ Runde 1/2: Judge — TASK.md + letzter Commit + Tests [00:47] +●◉ Runde 1/2: Coder fixt — fehlendes Null-Check [01:05] +●● ✓ PASS nach Runde 2/2 — SHIP 🚀 SHIP – produktionsreif ``` @@ -392,6 +391,7 @@ Nach SHIP werden automatisch ausgeführt: 1. Code-Kommentare einfügen 2. README.md schreiben 3. BEDIENUNGSANLEITUNG.md schreiben +4. LICENCE.md anlegen (falls noch nicht vorhanden) ### Vom manuellen Workflow in den automatischen wechseln @@ -415,7 +415,8 @@ Stand und der Fix-Zyklus läuft automatisch bis PASS oder max. N Runden. ### Loop-Erkennung -Wenn zweimal hintereinander genau dieselben Blocker auftreten, bricht `/optimize` ab: +Wenn zweimal hintereinander genau dieselben Blocker oder Concerns auftreten, bricht +`/optimize` ab: ``` ⚠ Derselbe Blocker tritt erneut auf – Schleife abgebrochen. Bitte manuell prüfen. ``` @@ -563,11 +564,14 @@ Fix: Validierung in die Funktion generate_password() verschieben statt in parse_ ## 8. Dokumentation generieren: /update_doku Nach Abschluss der Entwicklung (nach `/shipit` oder `/optimize`) erstellt `/update_doku` -drei Dinge automatisch: +vier Dinge automatisch: 1. **Code-Kommentare** — erklärt das WARUM in den Quelldateien (Deutsch) 2. **README.md** — Entwicklerperspektive: Installation, Build, Verwendung 3. **BEDIENUNGSANLEITUNG.md** — Endnutzerperspektive: einfach, ohne Jargon +4. **LICENCE.md** — proprietäre Lizenz mit aktuellem Jahr und Inhaber aus `git config user.name` + (wird nur angelegt wenn noch keine `LICENCE.md` vorhanden ist — existierende Lizenzen + werden nie überschrieben) ``` /update_doku @@ -582,6 +586,7 @@ Nur geänderte Quelldateien werden neu kommentiert — unveränderte bleiben una Code-Kommentare: keine Änderungen seit letztem Lauf – übersprungen. README.md: 2 Datei(en) geändert – wird geprüft BEDIENUNGSANLEITUNG.md: 2 Datei(en) geändert – wird geprüft +LICENCE.md: bereits vorhanden – übersprungen. ``` ### Zusammen mit /optimize @@ -772,7 +777,7 @@ Die Checkboxen werden automatisch abgehakt: # Für Projekte wo "PASS WITH CONCERNS" ausreicht: /optimize "Kleines Refactoring" --approve-concerns -# Kombination: kein Test, kein ShipIt bei Concerns, 1 Runde +# Kombination: kein Test, PASS WITH CONCERNS akzeptieren, 1 Runde /optimize "Typo-Fix in Fehlermeldungen" --rounds 1 --no-tests --approve-concerns ``` @@ -851,7 +856,7 @@ Die Checkboxen werden automatisch abgehakt: ls $HF_HOME/models/qwen3/ # Oder mit explizitem Pfad starten: -HF_HOME=/korrekter/pfad ./start-servers.sh +HF_HOME=/korrekter/pfad ./start-single.sh ``` ### Server startet nicht / HTTP nicht erreichbar @@ -864,26 +869,26 @@ HF_HOME=/korrekter/pfad ./start-servers.sh 1. Zu wenig VRAM — Container bricht beim Laden ab: ```bash - docker logs qwen36-27b-coder | tail -50 + docker logs qwen36-27b-single | tail -50 # Suche nach: "CUDA out of memory" oder "failed to allocate" ``` - → Kontext reduzieren: `-c 32768` statt `-c 131072` + → Kontext reduzieren: `-c 65536` statt `-c 262144` in `start-single.sh` 2. GPU nicht verfügbar: ```bash - nvidia-smi # alle drei GPUs sichtbar? - # GPU 1 (Coder) testen: + nvidia-smi # GPU sichtbar? + # GPU 1 testen: docker run --gpus '"device=1"' --rm nvidia/cuda:12.0-base nvidia-smi - # GPU 2 (Judge) testen: - docker run --gpus '"device=2"' --rm nvidia/cuda:12.0-base nvidia-smi + # Andere GPU testen: + GPU_DEVICE=0 ./start-single.sh ``` -3. Port bereits belegt: +3. Port 8001 bereits belegt: ```bash - ss -tlnp | grep 800[12] + ss -tlnp | grep 8001 docker ps -a # alter Container noch vorhanden? ./stop-servers.sh - ./start-servers.sh + ./start-single.sh ``` ### "Agent is already processing a prompt" @@ -914,8 +919,8 @@ Lies die Datei neu ein und wende die Änderungen als unified diff mit apply_patc ⚠ Derselbe Blocker tritt erneut auf – Schleife abgebrochen. ``` -**Ursache:** Der Coder kann einen bestimmten Blocker nicht beheben — z.B. weil die -Aufgabe einen Widerspruch enthält oder ein externes System fehlt. +**Ursache:** Der Coder kann einen bestimmten Blocker oder Concern nicht beheben — z.B. +weil die Aufgabe einen Widerspruch enthält oder ein externes System fehlt. **Lösung:** Manuell eingreifen: ``` @@ -927,9 +932,14 @@ Dann den Blocker analysieren und entweder: - Die Aufgabe in TASK.md präzisieren - Bei komplexen Aufgaben mit mehr Runden wiederholen: `/optimize --continue --rounds 5` +Falls der Judge wiederholt Concerns findet, die du akzeptierst: +``` +/optimize --continue --approve-concerns +``` + ### Server läuft, aber pi wechselt nicht das Modell -**Ursache:** `models.json` wurde nach einer Änderung nicht neu deployt. +**Ursache:** `models.json` oder `settings.json` wurde nach einer Änderung nicht neu deployt. **Lösung:** ```bash diff --git a/README.md b/README.md index d5755b7..9f54073 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ -# pi_coder — Automatisierter Coder/Judge-Workflow für pi agent +# pi_coder_2 — Automatisierter Coder/Judge-Workflow für pi agent Dieses Repository enthält die Konfiguration und Skripte für einen automatisierten -Coding-Workflow mit zwei lokalen LLaMA-Modellen: ein Coder-Modell und ein Judge-Modell, -gesteuert über [pi agent](https://github.com/earendil-works/pi). +Coding-Workflow mit einem lokalen LLaMA-Modell, das abwechselnd als Coder und Judge +agiert — gesteuert über Pi Agent. Er dreht solange Optimierungsschleifen, bis das +Programm Produktionsreife hat. --- @@ -12,37 +13,40 @@ gesteuert über [pi agent](https://github.com/earendil-works/pi). Nutzer gibt Auftrag │ ▼ - /coder → qwen3.5-coder (:8001) → Implementierung + git commit + /coder → qwen3.5-single (:8001, Coder-Persona) → Implementierung + git commit │ ▼ - /judge → qwen3.5-judge (:8002) → Review: PASS / FAIL + Blocker + /judge → qwen3.5-single (:8001, Judge-Persona) → Review: PASS / FAIL + Blocker │ FAIL? ▼ - /fix → qwen3.5-coder (:8001) → Fixes + git commit + /fix → qwen3.5-single (:8001, Coder-Persona) → Fixes + git commit + │ + PASS WITH CONCERNS? ▼ + │ (ohne --approve-concerns: wie FAIL → weiterer Fix-Zyklus) │ PASS? ▼ - /shipit → qwen3.5-judge (:8002) → Finale Freigabe: SHIP / NO-SHIP - (nur bei "PASS WITH CONCERNS" — klares PASS → direkt SHIP) + /shipit → qwen3.5-single (:8001, Judge-Persona) → Finale Freigabe: SHIP / NO-SHIP + (nur bei "PASS WITH CONCERNS" --approve-concerns; klares PASS → direkt SHIP) /optimize = Coder→Judge→Fix-Schleife automatisch (bis PASS oder max. N Runden) --interactive: pausiert nach PASS für menschlichen Checkpoint + optionale Zusatzaufträge ``` -Beide Modelle laufen als **separate llama.cpp-Docker-Container** und sprechen eine -OpenAI-kompatible API (`/v1/chat/completions`). pi agent wechselt automatisch zwischen -den Endpunkten wenn du ein `/judge`-, `/fix`- oder `/coder`-Kommando aufrufst. +Ein **einzelner** llama.cpp-Docker-Container bedient beide Rollen. Die Rollenumschaltung +(Coder ↔ Judge) erfolgt über unterschiedliche System-Prompts, die der `before_agent_start`-Hook +der Extension vor jeder Inference injiziert. pi agent wechselt keine Server mehr — +nur die Persona im System-Prompt ändert sich. --- ## Modelle -| Rolle | Modell | Port | Container | Alias | GPU | -|--------|---------------------------------------------------|------|------------------|----------------|----------| -| 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 | device=2 | +| Rolle | Modell | Port | Container | Alias | GPU | +|----------------|---------------------------------------------------|------|--------------------|----------------|----------| +| Coder + Judge | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS | 8001 | qwen36-27b-single | qwen3.5-single | device=1 | -Beide Container verwenden dasselbe GGUF-Datei, aber mit unterschiedlichen -Serverparametern (Kontext, Temperatur, Parallelität). +Coder- und Judge-Rollen werden durch verschiedene System-Prompts realisiert — +dasselbe Modell, derselbe Container, unterschiedliche Persona. --- @@ -58,12 +62,12 @@ Serverparametern (Kontext, Temperatur, Parallelität). sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit sudo systemctl restart docker ``` -- 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. +- NVIDIA-GPU: empfohlen RTX 3090 (24 GB VRAM). Standard: GPU 1. Überschreibbar mit + `GPU_DEVICE=0 ./start-single.sh`. Bei 3 GPUs: GPU 0 für Display reservieren. - GGUF-Modell vorhanden unter: `$HF_HOME/models/qwen3/Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf` - Standard-Pfad: `HF_HOME=/home/dschlueter/nvme2n1p7_home/huggingface` - - Überschreibbar: `HF_HOME=/anderer/pfad ./start-servers.sh` + - Überschreibbar: `HF_HOME=/anderer/pfad ./start-single.sh` - [pi agent](https://github.com/earendil-works/pi) installiert (`~/.pi/`) --- @@ -75,16 +79,20 @@ Serverparametern (Kontext, Temperatur, Parallelität). git clone https://kitux.de/forgejo/dschlueter/pi_coder.git ~/pi_coder cd ~/pi_coder -# 2. Extension und Modell-Config nach ~/.pi/agent/ deployen +# 2. Extension, Modell-Config und settings.json nach ~/.pi/agent/ deployen ./install_servers_and_pi_coder_extension.sh # 3. pi agent neu laden (in der pi-Oberfläche) # /reload # 4. Server starten -./start-servers.sh +./start-single.sh ``` +Das Install-Skript sichert eine vorhandene `~/.pi/agent/settings.json` als `.bak` und +kopiert dann `settings.json` aus dem Repo. Damit ist der korrekte Default-Provider +(`llama-cpp-single/qwen3.5-single`) automatisch konfiguriert. + Nach späteren Änderungen an `pi-coder-judge-extension.ts` oder `models.json`: ```bash ./install_servers_and_pi_coder_extension.sh # kopiert nach ~/.pi/agent/ @@ -96,95 +104,71 @@ Nach späteren Änderungen an `pi-coder-judge-extension.ts` oder `models.json`: ## Server starten / stoppen / status ```bash -# Beide Server parallel starten (empfohlen — dauert 1–3 Minuten) -./start-servers.sh +# Server starten (empfohlen — dauert 1–3 Minuten) +./start-single.sh -# Einzeln starten (z.B. nur einen neu starten) -./start-coder.sh # Port 8001 -./start-judge.sh # Port 8002 +# Server auf anderer GPU starten +GPU_DEVICE=0 ./start-single.sh -# Beide stoppen +# Stoppen ./stop-servers.sh -# Status beider Server prüfen +# Status prüfen ./status.sh ``` -`start-servers.sh` startet beide Container gleichzeitig und wartet bis beide -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. +`start-single.sh` startet den Container und wartet bis HTTP-ready (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). -Wenn Server bereits laufen und du `start-servers.sh` (oder ein Einzelskript) -aufrufst, werden die laufenden Container zuerst per `docker rm -f` gestoppt -und dann neu gestartet — ein laufender Inference-Request wird dabei abgebrochen. +Wenn der Server bereits läuft und du `start-single.sh` aufrufst, wird der laufende +Container zuerst per `docker rm -f` gestoppt und dann neu gestartet — ein laufender +Inference-Request wird dabei abgebrochen. --- ## llama.cpp-Serverparameter im Detail -### Gemeinsame Parameter +### Parameter des Single-Servers (Port 8001) | Parameter | Wert | Bedeutung | |---|---|---| | `--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}'`. | +| `--reasoning on` | — | Aktiviert das interne Thinking-Budget des Modells (Qwen3-spezifisch). | | `--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. | -| `--top-k 20` | — | Nur die 20 wahrscheinlichsten nächsten Tokens werden berücksichtigt. | +| `--repeat-penalty 1.05` | — | Leichte Penalty für Wiederholungen. | +| `--top-k 20` | — | Nur die 20 wahrscheinlichsten nächsten Tokens. | | `--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. | -| `-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. | -| `--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. | +| `-ngl 999` | — | Alle Layer auf die GPU laden. | +| `-fa on` | — | Flash Attention. | +| `--kv-unified` | — | Einheitlicher KV-Cache über alle Schichten. | +| `--cache-type-k q4_0` | — | KV-Cache Keys 4-Bit quantisiert. Nötig für 256K Kontext auf einer 24-GB-GPU. | | `--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. | -| `--main-gpu 0` | — | GPU-Index (0 = erste des Containers) für Nicht-Tensor-Operationen. | -| `--gpus '"device=1"'` (Coder) | — | Docker: nur GPU 1 dem Coder-Container zuweisen. | -| `--gpus '"device=2"'` (Judge) | — | Docker: nur GPU 2 dem Judge-Container zuweisen. | - -### Coder-Server (Port 8001) — optimiert für Coding-Aufgaben - -| Parameter | Wert | Erklärung / Wirkung | -|---|---|---| -| `-c 262144` | 256K Tokens | Sehr großes Kontextfenster: gesamte Codebasis + langer Gesprächsverlauf passt rein. **Sehr hoher VRAM-Bedarf.** Reduziere auf `65536` wenn VRAM knapp. | -| `-n 16384` | 16K Tokens | Maximale Ausgabelänge pro Anfrage. Für Kommentieraufgaben (`/update_doku`) nötig. | -| `--temp 0.2` | — | Niedrige Temperatur: deterministisch, konsistenter Code. Erhöhe auf `0.4–0.6` für kreativere Lösungsansätze. | -| `--top-p 0.95` | — | Nucleus Sampling: 95 % der Wahrscheinlichkeitsmasse. Passend zu temp 0.2. | -| `--batch-size 1024` | — | Prompt-Verarbeitungs-Batch. Größer = schnelleres Einlesen langer Dateien. | -| `--ubatch-size 512` | — | Micro-Batch für GPU-Kernel. Muss ≤ batch-size sein. | -| `--parallel 2` | — | 2 gleichzeitige Request-Slots. Nützlich wenn pi agent schnell Folgeanfragen schickt. | - -### Judge-Server (Port 8002) — optimiert für Reviews - -| Parameter | Wert | Erklärung / Wirkung | -|---|---|---| -| `-c 262144` | 256K Tokens | Großes Kontextfenster: nötig bei langen /optimize-Runden, wo der Gesprächsverlauf stark anwächst. | -| `-n 16384` | 16K Tokens | Lange Reviews und Begründungen passen vollständig in die Ausgabe. | -| `--temp 0.1` | — | Sehr niedrige Temperatur: maximale Konsistenz und Reproduzierbarkeit der Urteile. | -| `--top-p 0.9` | — | Etwas enger als beim Coder — weniger Variation im Urteil gewünscht. | -| `--batch-size 512` | — | Kleiner als beim Coder — Judge bekommt selten sehr lange Prompts. | -| `--ubatch-size 256` | — | Entsprechend kleiner. | -| `--parallel 1` | — | Judge-Aufgaben sind immer sequenziell im Workflow, daher 1 Slot ausreichend. | +| `--cont-batching` | — | Continuous Batching. | +| `-c 262144` | 256K | Großes Kontextfenster für langen Gesprächsverlauf im Optimize-Loop. | +| `-n 16384` | 16K | Maximale Ausgabelänge. | +| `--temp 0.2` | — | Deterministisch — Coder-Rolle. Der Judge-System-Prompt gleicht die etwas höhere Temperatur aus. | +| `--parallel 2` | — | 2 Slots, damit pi agent Folgeanfragen schnell hintereinander stellen kann. | +| `--gpus '"device=1"'` | — | Standard: GPU 1. Überschreibbar: `GPU_DEVICE=0 ./start-single.sh`. | --- ## GPU-Konfiguration und VRAM -### Aktuelles Setup (1 GPU pro Server) +### Aktuelles Setup (1 GPU, 1 Server) -Jeder Server bekommt eine dedizierte GPU — dadurch kein VRAM-Konflikt: +Ein einzelner Container bedient beide Rollen sequenziell: ``` GPU 0 NVIDIA T600 → Display / Monitor (nicht für KI genutzt) -GPU 1 RTX 3090 (24 GB) → qwen36-27b-coder (Port 8001) -GPU 2 RTX 3090 (24 GB) → qwen36-27b-judge (Port 8002) +GPU 1 RTX 3090 (24 GB) → qwen36-27b-single (Port 8001, Coder + Judge) ``` -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`. +Da Coder und Judge nicht gleichzeitig inferieren, gibt es keinen VRAM-Konflikt. +Die Umschaltung zwischen den Rollen erfolgt ausschließlich über den System-Prompt — +der Container läuft ohne Unterbrechung durch. -### VRAM-Abschätzung für das 27B IQ4_XS-Modell (eine GPU) +### VRAM-Abschätzung für das 27B IQ4_XS-Modell | Komponente | Größe (ca.) | |---|---| @@ -194,36 +178,28 @@ Innerhalb des Containers ist dies stets `CUDA:0`, daher `--main-gpu 0` ohne `--t | KV-Cache bei 65 536 Tokens (q4_0) | ~2–3 GB | | **Summe (262k Kontext)** | **~22–24 GB** | -Bei einer **24-GB-GPU** ist 262k Kontext knapp kalkuliert. Wichtig: keine anderen Prozesse +Bei einer **24-GB-GPU** ist 262k Kontext knapp kalkuliert. Keine anderen Prozesse dürfen nennenswert VRAM belegen (z. B. TTS-Dienste, Ollama). Prüfen mit: ```bash nvidia-smi --query-gpu=index,memory.used,memory.free --format=csv ``` -Falls der VRAM nicht reicht: Kontext in den Startskripten auf `131072` oder `65536` reduzieren. +Falls der VRAM nicht reicht: Kontext in `start-single.sh` auf `131072` oder `65536` reduzieren. ### Anpassung für andere GPU-Konfigurationen ```bash -# 2 GPUs (beide für KI, keine Display-GPU): - --gpus '"device=0"' # Coder - --gpus '"device=1"' # Judge +# Andere GPU verwenden: +GPU_DEVICE=0 ./start-single.sh -# 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): +# 2 GPUs für einen Server (sehr großer Kontext): +# start-single.sh anpassen: --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. -Entweder ein kleineres Modell verwenden oder die Quantisierung erhöhen (IQ3_XS, Q4_K_M). - --- ## Parameter-Tuning-Guide @@ -232,23 +208,18 @@ Entweder ein kleineres Modell verwenden oder die Quantisierung erhöhen (IQ3_XS, | Wert | Eignung | |---|---| -| `0.0–0.1` | Maximale Reproduzierbarkeit. Gut für Judge/Review. | -| `0.1–0.3` | Guter Kompromiss für Coding. **Empfohlen für Coder.** | -| `0.4–0.6` | Kreativere Lösungen, mehr Varianz. Sinnvoll für Prototyping. | -| `0.7–1.0` | Kreativschreiben, Brainstorming. Für Coding meist zu viel Rauschen. | +| `0.0–0.1` | Maximale Reproduzierbarkeit. | +| `0.1–0.3` | Guter Kompromiss für Coding. **Aktuell gesetzt: 0.2.** | +| `0.4–0.6` | Kreativere Lösungen. | +| `0.7–1.0` | Für Coding meist zu viel Rauschen. | ### Kontextgröße (`-c`) -Je größer der Kontext, desto mehr VRAM braucht der KV-Cache. -Faustregel: KV-Cache ≈ `context_size × layers × head_dim × 2 × bytes_per_element`. -Bei q8_0 (1 Byte/Element) und Qwen3-27B (28 Schichten, 128 Head-Dim, 32 Heads): -KV-Cache ≈ `context_size × 28 × 128 × 32 × 2 × 1 Byte ≈ context_size × 0,23 MB` - | Kontext | KV-Cache (q4_0) | Empfehlung | |---|---|---| | 32 768 | ~1,9 GB | 1 × 16-GB-GPU | -| 65 536 | ~3,7 GB | 1 × 24-GB-GPU | -| 131 072 | ~7,5 GB | 2 × 16-GB-GPU | +| 65 536 | ~3,7 GB | 1 × 24-GB-GPU (konservativ) | +| 131 072 | ~7,5 GB | 1 × 24-GB-GPU | | 262 144 | ~8–10 GB | 1 × 24-GB-GPU (q4_0) — **aktuell gesetzt** | ### KV-Cache-Quantisierung @@ -257,31 +228,35 @@ KV-Cache ≈ `context_size × 28 × 128 × 32 × 2 × 1 Byte ≈ context_size × |---|---|---| | `f16` | 100 % (Basis) | Referenz | | `q8_0` | ~50 % | Kaum merklich schlechter | -| `q4_0` | ~25 % | Merklicher Qualitätsverlust bei langen Kontexten — aber nötig für 256K Kontext auf 2× 24 GB. **Aktuell gesetzt.** | +| `q4_0` | ~25 % | Merklicher Qualitätsverlust bei langen Kontexten — aber nötig für 256K Kontext auf 24 GB. **Aktuell gesetzt.** | -### Parallelität (`--parallel`) +--- -Mehr parallele Slots erhöhen den Durchsatz bei gleichzeitigen Anfragen, aber jeder Slot -reserviert Speicher im KV-Cache. Im pi-coder-Workflow sind echte Parallelaufrufe selten, -daher ist `--parallel 1` für den Judge ausreichend. Coder `--parallel 2` bietet Puffer -wenn pi agent Folgeanfragen schnell hintereinander schickt. +## Tests ausführen + +```bash +./run-tests.sh +``` + +Keine npm-Installation nötig — nutzt den integrierten `node:test`-Runner und +`--experimental-strip-types` für TypeScript. Testet Parsing-Logik, Konfigurationskonsistenz +und Extension-Guards gegen Regressionen. --- ## Dateien -| Datei | Zweck | +| Datei / Verzeichnis | Zweck | |---|---| -| `pi-coder-judge-extension.ts` | pi agent Extension (Kommandos, Tools, Hooks) | +| `pi-coder-judge-extension.ts` | pi agent Extension (Kommandos, Tools, Hooks, Rollen-Logik) | | `models.json` | Provider- und Modell-Konfiguration für pi agent | -| `test-utils.ts` | Unit-Tests für Hilfsfunktionen der Extension | -| `run-tests.sh` | Unit-Tests ausführen (TypeScript-Stripping + node) | -| `start-servers.sh` | Beide Server parallel starten (empfohlen) | -| `start-coder.sh` | Nur Coder-Container starten (Port 8001) | -| `start-judge.sh` | Nur Judge-Container starten (Port 8002) | -| `stop-servers.sh` | Beide Container stoppen | -| `status.sh` | Laufstatus beider Server anzeigen | -| `install_servers_and_pi_coder_extension.sh` | Extension + models.json nach `~/.pi/agent/` kopieren | +| `settings.json` | pi-agent-Settings (Default-Modell, aktivierte Modelle) — wird von Install-Skript deployt | +| `run-tests.sh` | Tests ausführen | +| `tests/` | Testdateien (pure-logic, config, extension-guards, scripts) | +| `start-single.sh` | Single-Server starten (Port 8001, Coder + Judge) | +| `stop-servers.sh` | Container stoppen | +| `status.sh` | Laufstatus anzeigen | +| `install_servers_and_pi_coder_extension.sh` | Extension + models.json + settings.json nach `~/.pi/agent/` deployen | | `examples/` | Demo-Projekte (Python, Rust, Go, C, Bash, HTML/JS) für Live-Demos | | `examples/restore-all.sh` | Examples nach Demo-Lauf in Ausgangszustand zurücksetzen | @@ -289,18 +264,18 @@ wenn pi agent Folgeanfragen schnell hintereinander schickt. ## pi-Kommandos (Kurzübersicht) -| Kommando | Modell | Beschreibung | +| Kommando | Persona | Beschreibung | |---|---|---| | `/coder ` | Coder | TASK.md anlegen, Implementierung starten | | `/judge [fokus]` | Judge | Code-Review gegen TASK.md + letzten Commit | | `/fix [hinweis]` | Coder | Judge-Kritik beheben, committen | | `/shipit` | Judge | Finale Freigabeprüfung | -| `/optimize [--rounds N] [--with-doku] [--continue] [--interactive]` | beide | Vollautomatische Schleife bis PASS (Standard: 2 Runden, Runde 1: Quick-Judge) | -| `/optimize ... [--no-tests] [--approve-concerns] [--test-cmd "cmd"] [--test-timeout N]` | beide | Test-Erkennung überspringen / PASS WITH CONCERNS direkt shippern | +| `/optimize [--rounds N] [--with-doku] [--continue] [--interactive]` | beide | Vollautomatische Schleife bis PASS (Standard: 2 Runden) | +| `/optimize ... [--no-tests] [--approve-concerns] [--test-cmd "cmd"] [--test-timeout N]` | beide | Test-Erkennung überspringen / PASS WITH CONCERNS akzeptieren | | `/patch <änderung>` | Coder | Gezielte Minimaländerung ohne Review | | `/quick_check [was]` | Judge | Schnelle Prüfung der letzten Änderung | | `/version` | — | Versionsnummer erhöhen (SemVer + Git-Tag) | -| `/update_doku` | Coder | Code kommentieren + README + Bedienungsanleitung | +| `/update_doku` | Coder | Code kommentieren + README + Bedienungsanleitung + LICENCE.md | | `/plan ` | Coder | Implementierungsplan in PLAN.md (kein Code) | | `/continue` | Coder | Unterbrochenen Prozess fortsetzen | | `/cancel` | — | Laufenden Loop nach aktuellem Schritt abbrechen | @@ -310,6 +285,15 @@ Ausführliche Beschreibung aller Kommandos mit Beispielen: siehe **BEDIENUNGSANL --- +## PASS WITH CONCERNS — Verhalten + +Im `/optimize`-Loop gilt `PASS WITH CONCERNS` **nicht** als bestanden — der Coder erhält +einen weiteren Fix-Auftrag, genau wie bei `FAIL`. Erst sauberes `PASS` beendet die Schleife. + +Opt-out: `--approve-concerns` lässt `PASS WITH CONCERNS` als bestanden gelten. + +--- + ## Live-Aktivitätsstatus Während der Ausführung zeigt pi_coder in der Statuszeile, was gerade passiert — @@ -320,11 +304,11 @@ inklusive eines laufenden `[MM:SS]`-Timers während jeder LLM-Inference-Phase: | Coder implementiert | `◉ Coder implementiert: Login-Flow mit JWT [01:23]` | | edit-Tool aktiv | `Editiere src/main.py…` | | git commit | `Git-Commit…` | -| Judge (Quick-Check, Runde 1) | `◉○ Runde 1/2: Quick-Check [00:47]` | -| Judge (voller Review, Runde 2) | `●◉ Runde 2/2: Judge — TASK.md + letzter Commit + Tests [02:15]` | +| Judge (Runde 1) | `◉○ Runde 1/2: Judge — TASK.md + letzter Commit + Tests [00:47]` | +| Judge (Runde 2) | `●◉ Runde 2/2: Judge — TASK.md + letzter Commit + Tests [02:15]` | | 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 | `⏸ PASS – warte auf /continue…` | Der Timer beweist, dass die LLM tatsächlich arbeitet. Steht er still, hängt der Prozess. diff --git a/SINGLE_SERVER_PLAN.md b/SINGLE_SERVER_PLAN.md deleted file mode 100644 index fc59ec9..0000000 --- a/SINGLE_SERVER_PLAN.md +++ /dev/null @@ -1,150 +0,0 @@ -# Plan: pi_coder_v2 — Single-Server (Option B) - -## Kontext - -pi_coder verwendet zwei llama.cpp-Server für Coder (GPU 1) und Judge (GPU 2), -die aber dasselbe GGUF-Modell ausführen. Die Rollentrennung entsteht ausschließlich -durch System-Prompts. Wenn nur eine GPU verfügbar ist, kann ein einziger Server -beide Rollen übernehmen — Rollenwechsel per Prompt statt per Server-Neustart. - -**pi_coder bleibt unverändert.** Die Umsetzung erfolgt im neuen Verzeichnis `~/pi_coder_v2`. - ---- - -## Schritt 1: Verzeichnis anlegen und Dateien kopieren - -```bash -mkdir ~/pi_coder_v2 -cp ~/pi_coder/pi-coder-judge-extension.ts ~/pi_coder_v2/ -cp ~/pi_coder/models.json ~/pi_coder_v2/ -cp ~/pi_coder/start-coder.sh ~/pi_coder_v2/ -cp ~/pi_coder/start-judge.sh ~/pi_coder_v2/ -cp ~/pi_coder/start-servers.sh ~/pi_coder_v2/ -cp ~/pi_coder/stop-servers.sh ~/pi_coder_v2/ -cp ~/pi_coder/status.sh ~/pi_coder_v2/ -cp ~/pi_coder/install_servers_and_pi_coder_extension.sh ~/pi_coder_v2/ -cp ~/pi_coder/README.md ~/pi_coder_v2/ -cp ~/pi_coder/BEDIENUNGSANLEITUNG.md ~/pi_coder_v2/ -cp ~/pi_coder/CLAUDE.md ~/pi_coder_v2/ -# Plan-Datei ebenfalls kopieren -cp ~/pi_coder_v2/SINGLE_SERVER_PLAN.md -# git init -cd ~/pi_coder_v2 && git init && git add -A && git commit -m "init: kopiert aus pi_coder" -``` - -Nicht kopieren: `run-tests.sh`, `test-utils.ts`, `examples/` (nicht relevant für Kernfunktion). - ---- - -## Schritt 2: `start-single.sh` anlegen - -Basiert auf `start-coder.sh` mit folgenden Änderungen: - -| Parameter | Alt (coder) | Neu (single) | -|---|---|---| -| `CONTAINER_NAME` | `qwen36-27b-coder` | `qwen36-27b-single` | -| `HOST_PORT` | 8001 | 8001 | -| `MODEL_ALIAS` | `qwen3.5-coder` | `qwen3.5-single` | -| `--temp` | 0.6 | 0.65 (Kompromiss) | -| `--gpus` | `"device=1"` | `"device=1"` (oder konfigurierbar) | - -Der Server registriert sich unter dem Alias `qwen3.5-single`. - ---- - -## Schritt 3: `models.json` anpassen - -Neuen Provider `llama-cpp-single` hinzufügen mit **beiden** Modell-IDs auf Port 8001: - -```json -"llama-cpp-single": { - "baseUrl": "http://127.0.0.1:8001/v1", - "api": "openai-completions", - "apiKey": "none", - "compat": { - "supportsDeveloperRole": false, - "supportsReasoningEffort": false, - "maxTokensField": "max_tokens", - "thinkingFormat": "qwen-chat-template" - }, - "models": [ - { - "id": "qwen3.5-coder", - "name": "Qwen3.6 27B Single-Server Coder (llama.cpp :8001)", - "reasoning": true, - "input": ["text"], - "contextWindow": 262144, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "qwen3.5-judge", - "name": "Qwen3.6 27B Single-Server Judge (llama.cpp :8001)", - "reasoning": true, - "input": ["text"], - "contextWindow": 262144, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - } - ] -} -``` - -Die bestehenden Provider `llama-cpp-coder` und `llama-cpp-judge` bleiben erhalten -(für Kompatibilität), werden aber nicht aktiv genutzt. - ---- - -## Schritt 4: `pi-coder-judge-extension.ts` — switchModel-Aufrufe - -Alle Aufrufe von `switchModel(pi, ctx, "llama-cpp-coder", "qwen3.5-coder")` -und `switchModel(pi, ctx, "llama-cpp-judge", "qwen3.5-judge")` werden auf -`"llama-cpp-single"` umgestellt. Der Modell-ID-Parameter (`"qwen3.5-coder"` / -`"qwen3.5-judge"`) bleibt unverändert — so bleiben die Registry-Wechsel erhalten, -sind aber sofort (kein Server-Neustart). - -Betroffene Stellen (ca. 8): -- `/coder` handler -- `/judge` handler -- `/fix` handler -- `/shipit` handler -- `/optimize` loop (Coder-Kickoff, Judge-Phase, Fix-Phase, ShipIt-Phase) -- `/patch` handler -- `/quick_check` handler -- `/plan` handler - ---- - -## Schritt 5: `install_servers_and_pi_coder_extension.sh` anpassen - -Pfade auf `pi_coder_v2` anpassen (falls nötig — Skript referenziert vermutlich `$REPO` -als relatives Verzeichnis, was automatisch korrekt ist). - ---- - -## Schritt 6: `waitUntilModelReady` im `--continue`-Modus - -Im `--continue`-Modus prüft die Extension beide Server parallel: -```typescript -const [coderReady, judgeReady] = await Promise.all([ - waitUntilModelReady(pi, ctx, 8001, "qwen3.5-coder"), - waitUntilModelReady(pi, ctx, 8002, "qwen3.5-judge"), -]); -``` - -Dies muss auf einen einzigen Check umgestellt werden: -```typescript -const ready = await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single"); -``` - ---- - -## Verifikation - -1. `cd ~/pi_coder_v2 && ./start-single.sh` → Server startet auf Port 8001 -2. `nvidia-smi` → nur eine GPU belegt, ~20 GB VRAM -3. `./install_servers_and_pi_coder_extension.sh` → Extension in `~/.pi/agent/` deployen -4. In pi: `/reload` → Extension neu laden -5. `/optimize ` → Loop läuft durch -6. In der Statuszeile: `switchModel` wechselt ohne spürbare Pause -7. Qualitätscheck: Judge-Urteil und Coder-Fix verhalten sich wie erwartet diff --git a/pi-coder-judge-extension.ts b/pi-coder-judge-extension.ts index 8d13b5b..837755f 100644 --- a/pi-coder-judge-extension.ts +++ b/pi-coder-judge-extension.ts @@ -324,6 +324,45 @@ function planPrompt(task: string): string { // ── Hilfsfunktionen ───────────────────────────────────────────────────────── +// Proprietäre Default-Lizenz ("Alle Rechte vorbehalten"), deutsch — passend zur übrigen Doku. +// Reine Template-Funktion (deterministisch): Lizenztext wird NIE vom LLM generiert. +function licenceMd(year: string, holder: string): string { + return [ + "# Lizenz", + "", + `Copyright (c) ${year} ${holder}`, + "", + "Alle Rechte vorbehalten.", + "", + "Diese Software und der zugehörige Quellcode sind urheberrechtlich geschützt.", + "Kein Teil dieser Software darf ohne vorherige ausdrückliche schriftliche", + "Genehmigung des Rechteinhabers kopiert, verändert, weitergegeben, veröffentlicht,", + "unterlizenziert oder anderweitig genutzt werden.", + "", + "DIE SOFTWARE WIRD OHNE MÄNGELGEWÄHR UND OHNE JEGLICHE AUSDRÜCKLICHE ODER", + "KONKLUDENTE GEWÄHRLEISTUNG BEREITGESTELLT. DER RECHTEINHABER HAFTET NICHT FÜR", + "SCHÄDEN, DIE AUS DER NUTZUNG DIESER SOFTWARE ENTSTEHEN.", + "" + ].join("\n"); +} + +// Legt LICENCE.md mit der proprietären Default-Lizenz an — nur wenn noch keine existiert +// (eine vorhandene Lizenz wird nie überschrieben). Inhaber aus 'git config user.name', +// sonst Platzhalter; Jahr aus dem aktuellen Datum. +async function writeLicenceMd(pi: ExtensionAPI, ctx: ExtensionCommandContext): Promise { + const check = await pi.exec("bash", ["-c", "test -f LICENCE.md && echo exists"], { cwd: ctx.cwd }); + if (check.stdout.trim() === "exists") { + ctx.ui.notify("LICENCE.md existiert bereits — unverändert.", "info"); + return; + } + const who = await pi.exec("bash", ["-c", "git config user.name 2>/dev/null"], { cwd: ctx.cwd }); + const holder = who.stdout.trim() || "[Rechteinhaber eintragen]"; + const year = String(new Date().getFullYear()); + // content als Argument ($1), nicht interpoliert — kein Shell-Escaping-Problem. + await pi.exec("bash", ["-c", `printf "%s" "$1" > LICENCE.md`, "_", licenceMd(year, holder)], { cwd: ctx.cwd }); + ctx.ui.notify(`LICENCE.md angelegt (proprietär, © ${year} ${holder}).`, "info"); +} + // Legt TASK.md neu an oder hängt einen Zusatzauftrag an. async function writeTaskMd( pi: ExtensionAPI, @@ -703,10 +742,18 @@ async function runUpdateDoku(pi: ExtensionAPI, ctx: ExtensionCommandContext): Pr ctx.ui.notify(`3/3 BEDIENUNGSANLEITUNG.md fehlgeschlagen: ${String(e?.message ?? e)}`, "error"); } + // LICENCE.md: deterministisch anlegen (nur wenn nicht vorhanden) — kein LLM. + try { + ctx.ui.setStatus("update_doku", "LICENCE.md wird geprüft…"); + await writeLicenceMd(pi, ctx); + } catch (e: any) { + ctx.ui.notify(`LICENCE.md fehlgeschlagen: ${String(e?.message ?? e)}`, "error"); + } + // Abschließender Dokumentations-Commit (immer, auch bei Teilfehlern) await pi.exec( "bash", - ["-c", "git add -A && git commit -m 'docs: update comments, README, BEDIENUNGSANLEITUNG' || true"], + ["-c", "git add -A && git commit -m 'docs: update comments, README, BEDIENUNGSANLEITUNG, LICENCE' || true"], { cwd: ctx.cwd } ); @@ -1331,7 +1378,7 @@ export default function (pi: ExtensionAPI) { if (withDoku) { await runUpdateDoku(pi, ctx); } else { - ctx.ui.notify("Nächster Schritt: /update_doku für Code-Kommentare, README.md und BEDIENUNGSANLEITUNG.md", "info"); + ctx.ui.notify("Nächster Schritt: /update_doku für Code-Kommentare, README.md, BEDIENUNGSANLEITUNG.md und LICENCE.md", "info"); } } } catch (e: any) { @@ -1387,7 +1434,7 @@ export default function (pi: ExtensionAPI) { // ── Dokumentations-Phase ───────────────────────────────────────────────── pi.registerCommand("update_doku", { - description: "Inkrementelle Code-Kommentare + README.md + BEDIENUNGSANLEITUNG.md via Git-Tags.", + description: "Inkrementelle Code-Kommentare + README.md + BEDIENUNGSANLEITUNG.md via Git-Tags; legt LICENCE.md an (proprietär, falls fehlend).", handler: async function (_args: string, ctx: ExtensionCommandContext) { await runUpdateDoku(pi, ctx); } @@ -1484,7 +1531,7 @@ export default function (pi: ExtensionAPI) { "/patch <änderung> Gezielte Minimaländerung → Coder", "/quick_check [was] Schnelle OK/PROBLEM-Prüfung → Judge", "/plan Implementierungsplan in PLAN.md → Coder", - "/update_doku Code-Kommentare + README.md + BEDIENUNGSANLEITUNG.md", + "/update_doku Code-Kommentare + README.md + BEDIENUNGSANLEITUNG.md + LICENCE.md", "/version Versionsnummer erhöhen (SemVer + Git-Tag)", "/discard Verwirft PLAN.md", "/new_project Projektverzeichnis + git init + .gitignore", diff --git a/tests/extension-guards.test.mjs b/tests/extension-guards.test.mjs index e092f8f..30a173d 100644 --- a/tests/extension-guards.test.mjs +++ b/tests/extension-guards.test.mjs @@ -44,3 +44,17 @@ test("Loop-Erkennung greift auch ohne Blocker-Abschnitt (Fallback auf Gesamttext test("currentRole wird nach jedem optimize-Lauf auf 'coder' zurückgesetzt", () => { assert.match(extSource, /currentRole\s*=\s*"coder"/); }); + +test("LICENCE.md: deterministisch in der Doku-Phase, nur wenn nicht vorhanden, proprietär", () => { + assert.match(extSource, /function licenceMd\b/); + assert.match(extSource, /async function writeLicenceMd\b/); + // wird in runUpdateDoku aufgerufen + assert.match(extSource, /await writeLicenceMd\(pi, ctx\)/); + // existierende Lizenz wird nicht überschrieben + assert.match(extSource, /test -f LICENCE\.md/); + // proprietärer Default + assert.match(extSource, /Alle Rechte vorbehalten/); + // Inhaber aus git config, Jahr aus aktuellem Datum + assert.match(extSource, /git config user\.name/); + assert.match(extSource, /new Date\(\)\.getFullYear\(\)/); +}); diff --git a/tests/pure-logic.test.mjs b/tests/pure-logic.test.mjs index 1513fe0..af1f446 100644 --- a/tests/pure-logic.test.mjs +++ b/tests/pure-logic.test.mjs @@ -8,6 +8,7 @@ import { extractFn } from "./helpers.mjs"; const parseVerdict = extractFn("parseVerdict"); const parseBlockers = extractFn("parseBlockers"); const normalizeForComparison = extractFn("normalizeForComparison"); +const licenceMd = extractFn("licenceMd"); test("parseVerdict: klares PASS", () => { assert.equal(parseVerdict("Review fertig.\nUrteil: PASS\nKeine Blocker."), "PASS"); @@ -64,3 +65,16 @@ test("normalizeForComparison: gleiche Blocker trotz Formatierungsunterschied → normalizeForComparison(b) ); }); + +test("licenceMd: proprietärer Default mit eingesetztem Jahr und Inhaber", () => { + const txt = licenceMd("2026", "Dieter Schlüter"); + assert.match(txt, /^# Lizenz/); + assert.match(txt, /Copyright \(c\) 2026 Dieter Schlüter/); + assert.match(txt, /Alle Rechte vorbehalten\./); + assert.match(txt, /ohne vorherige ausdrückliche schriftliche/); + assert.match(txt, /OHNE MÄNGELGEWÄHR/); +}); + +test("licenceMd: Platzhalter-Inhaber wird unverändert eingesetzt", () => { + assert.match(licenceMd("2030", "[Rechteinhaber eintragen]"), /Copyright \(c\) 2030 \[Rechteinhaber eintragen\]/); +});