refactor: Modell- und Container-Alias auf coding_model vereinheitlicht

Statt modell-spezifischer Namen (qwen3.5-single, qwen36-27b-single) wird
überall der semantisch neutrale Bezeichner coding_model verwendet.
Dadurch kann das Modell bei Bedarf ausgetauscht werden, ohne Konfiguration,
Skripte oder Tests anpassen zu müssen.

Geändert: models.json, settings.json, start-single.sh, stop-servers.sh,
status.sh, pi-coder-judge-extension.ts, tests/, README.md, BEDIENUNGSANLEITUNG.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-15 05:26:58 +02:00
commit 2b0ca79dca
10 changed files with 39 additions and 39 deletions

View file

@ -73,7 +73,7 @@ prüfen beim Start die VRAM-Verfügbarkeit, was zusätzliche Zeit kostet).
```
=== LLaMA-Server Status ===
qwen36-27b-single (Port 8001): Container=RUNNING HTTP=OK
coding_model (Port 8001): Container=RUNNING HTTP=OK
```
### pi agent öffnen
@ -869,7 +869,7 @@ HF_HOME=/korrekter/pfad ./start-single.sh
1. Zu wenig VRAM — Container bricht beim Laden ab:
```bash
docker logs qwen36-27b-single | tail -50
docker logs coding_model | tail -50
# Suche nach: "CUDA out of memory" oder "failed to allocate"
```
→ Kontext reduzieren: `-c 65536` statt `-c 262144` in `start-single.sh`

View file

@ -13,19 +13,19 @@ Programm Produktionsreife hat.
Nutzer gibt Auftrag
/coder → qwen3.5-single (:8001, Coder-Persona) → Implementierung + git commit
/coder → coding_model (:8001, Coder-Persona) → Implementierung + git commit
/judge → qwen3.5-single (:8001, Judge-Persona) → Review: PASS / FAIL + Blocker
/judge → coding_model (:8001, Judge-Persona) → Review: PASS / FAIL + Blocker
FAIL? ▼
/fix → qwen3.5-single (:8001, Coder-Persona) → Fixes + git commit
/fix → coding_model (:8001, Coder-Persona) → Fixes + git commit
PASS WITH CONCERNS? ▼
│ (ohne --approve-concerns: wie FAIL → weiterer Fix-Zyklus)
PASS? ▼
/shipit → qwen3.5-single (:8001, Judge-Persona) → Finale Freigabe: SHIP / NO-SHIP
/shipit → coding_model (: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)
@ -43,7 +43,7 @@ nur die Persona im System-Prompt ändert sich.
| 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 |
| Coder + Judge | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS | 8001 | coding_model | coding_model | device=1 |
Coder- und Judge-Rollen werden durch verschiedene System-Prompts realisiert —
dasselbe Modell, derselbe Container, unterschiedliche Persona.
@ -91,7 +91,7 @@ cd ~/pi_coder
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.
(`llama-cpp-single/coding_model`) automatisch konfiguriert.
Nach späteren Änderungen an `pi-coder-judge-extension.ts` oder `models.json`:
```bash
@ -161,7 +161,7 @@ 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-single (Port 8001, Coder + Judge)
GPU 1 RTX 3090 (24 GB) → coding_model (Port 8001, Coder + Judge)
```
Da Coder und Judge nicht gleichzeitig inferieren, gibt es keinen VRAM-Konflikt.

View file

@ -28,7 +28,7 @@
},
"models": [
{
"id": "qwen3.5-single",
"id": "coding_model",
"name": "Qwen3.6 27B Single-Server (llama.cpp :8001)",
"reasoning": true,
"input": ["text"],

View file

@ -1,6 +1,6 @@
// pi-coder-judge-extension.ts
// Automatisierter Coder-Judge-Fix-Workflow für AI-Coding-Assistenten
// Ein Modell (qwen3.5-single) auf Single-Server (Port 8001); Rollen Coder/Judge via System-Prompt
// Ein Modell (coding_model) auf Single-Server (Port 8001); Rollen Coder/Judge via System-Prompt
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
@ -432,13 +432,13 @@ async function tickTaskMdStatus(
);
}
// Single-Server: genau ein Modell (qwen3.5-single auf :8001) für beide Rollen.
// Single-Server: genau ein Modell (coding_model auf :8001) für beide Rollen.
// Der Rollenwechsel Coder/Judge erfolgt NICHT mehr über den Modellwechsel, sondern
// über den System-Prompt (siehe before_agent_start-Hook + currentRole). Diese Funktion
// stellt nur sicher, dass das eine Single-Modell aktiv ist; nach dem ersten Aufruf
// ist der Wechsel via currentModelKey gecacht und damit ein No-op.
const SINGLE_PROVIDER = "llama-cpp-single";
const SINGLE_MODEL_ID = "qwen3.5-single";
const SINGLE_MODEL_ID = "coding_model";
async function ensureSingleModel(
pi: ExtensionAPI,
@ -1071,7 +1071,7 @@ export default function (pi: ExtensionAPI) {
ctx.ui.notify("Benutzung: /coder <auftrag>", "error");
return;
}
if (!await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single")) {
if (!await waitUntilModelReady(pi, ctx, 8001, "coding_model")) {
ctx.ui.notify("Server nicht bereit (Port 8001) — start-single.sh ausführen", "error");
return;
}
@ -1085,7 +1085,7 @@ export default function (pi: ExtensionAPI) {
pi.registerCommand("judge", {
description: "Review gegen TASK.md + git show HEAD → Judge-Rolle (:8001).",
handler: async function (args: string, ctx: ExtensionCommandContext) {
if (!await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single")) {
if (!await waitUntilModelReady(pi, ctx, 8001, "coding_model")) {
ctx.ui.notify("Server nicht bereit (Port 8001) — start-single.sh ausführen", "error");
return;
}
@ -1098,7 +1098,7 @@ export default function (pi: ExtensionAPI) {
pi.registerCommand("fix", {
description: "Fixt Judge-Kritik, committet Ergebnis → Coder-Rolle (:8001).",
handler: async function (args: string, ctx: ExtensionCommandContext) {
if (!await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single")) {
if (!await waitUntilModelReady(pi, ctx, 8001, "coding_model")) {
ctx.ui.notify("Server nicht bereit (Port 8001) — start-single.sh ausführen", "error");
return;
}
@ -1111,7 +1111,7 @@ export default function (pi: ExtensionAPI) {
pi.registerCommand("shipit", {
description: "Finale Freigabe gegen TASK.md + git log → Judge-Rolle (:8001).",
handler: async function (args: string, ctx: ExtensionCommandContext) {
if (!await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single")) {
if (!await waitUntilModelReady(pi, ctx, 8001, "coding_model")) {
ctx.ui.notify("Server nicht bereit (Port 8001) — start-single.sh ausführen", "error");
return;
}
@ -1176,7 +1176,7 @@ export default function (pi: ExtensionAPI) {
// Im --continue-Modus: Single-Server prüfen (Port 8001 — Coder + Judge in einem).
ctx.ui.setStatus("optimize", "Single-Server wird geprüft (Port 8001)…");
const serverReady = await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single");
const serverReady = await waitUntilModelReady(pi, ctx, 8001, "coding_model");
if (!serverReady) {
finalNotify(ctx, "⛔ Server nicht erreichbar", "Port 8001 — kein HTTP 200 nach 3 min. start-single.sh ausführen");
return;
@ -1202,7 +1202,7 @@ export default function (pi: ExtensionAPI) {
// GPU-Ladevorgangs und ist kein verlässliches Signal. Nur HTTP 200 auf einen
// echten Completion-Request bedeutet: Modell ist im VRAM und bereit.
ctx.ui.setStatus("optimize", "Single-Server wird geprüft…");
if (!await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single")) {
if (!await waitUntilModelReady(pi, ctx, 8001, "coding_model")) {
finalNotify(ctx, "⛔ Server nicht erreichbar", "Port 8001 — kein HTTP 200 nach 3 min. start-single.sh ausführen");
return;
}
@ -1408,7 +1408,7 @@ export default function (pi: ExtensionAPI) {
ctx.ui.notify("Benutzung: /patch <beschreibung der änderung>", "error");
return;
}
if (!await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single")) {
if (!await waitUntilModelReady(pi, ctx, 8001, "coding_model")) {
ctx.ui.notify("Server nicht bereit (Port 8001) — start-single.sh ausführen", "error");
return;
}
@ -1421,7 +1421,7 @@ export default function (pi: ExtensionAPI) {
pi.registerCommand("quick_check", {
description: "Schnelle OK/PROBLEM-Prüfung einer kleinen Codeänderung → Judge-Rolle (:8001).",
handler: async function (args: string, ctx: ExtensionCommandContext) {
if (!await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single")) {
if (!await waitUntilModelReady(pi, ctx, 8001, "coding_model")) {
ctx.ui.notify("Server nicht bereit (Port 8001) — start-single.sh ausführen", "error");
return;
}
@ -1489,7 +1489,7 @@ export default function (pi: ExtensionAPI) {
ctx.ui.notify("Benutzung: /plan <auftrag>", "error");
return;
}
if (!await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single")) {
if (!await waitUntilModelReady(pi, ctx, 8001, "coding_model")) {
ctx.ui.notify("Server nicht bereit (Port 8001) — start-single.sh ausführen", "error");
return;
}
@ -1571,7 +1571,7 @@ export default function (pi: ExtensionAPI) {
}
// Standard-Verhalten: unterbrochenen Prozess wieder aufnehmen
if (!await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single")) {
if (!await waitUntilModelReady(pi, ctx, 8001, "coding_model")) {
ctx.ui.notify("Server nicht bereit (Port 8001) — start-single.sh ausführen", "error");
return;
}

View file

@ -1,9 +1,9 @@
{
"lastChangelogVersion": "0.79.3",
"defaultProvider": "llama-cpp-single",
"defaultModel": "qwen3.5-single",
"defaultModel": "coding_model",
"enabledModels": [
"llama-cpp-single/qwen3.5-single"
"llama-cpp-single/coding_model"
],
"defaultThinkingLevel": "high",
"packages": [

View file

@ -4,10 +4,10 @@ set -euo pipefail
HF_HOME="${HF_HOME:-/home/dschlueter/nvme2n1p7_home/huggingface}"
MODEL_REL_PATH="models/qwen3/Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf"
IMAGE="ghcr.io/ggml-org/llama.cpp:server-cuda"
CONTAINER_NAME="qwen36-27b-single"
CONTAINER_NAME="coding_model"
HOST_PORT=8001
CONTAINER_PORT=8000
MODEL_ALIAS="qwen3.5-single"
MODEL_ALIAS="coding_model"
GPU_DEVICE="${GPU_DEVICE:-1}"
echo "[*] Verwende HF_HOME = $HF_HOME"

View file

@ -30,4 +30,4 @@ check_server() {
}
echo "=== LLaMA-Server Status ==="
check_server "qwen36-27b-single" 8001 "qwen3.5-single"
check_server "coding_model" 8001 "coding_model"

View file

@ -2,7 +2,7 @@
set -euo pipefail
# Single-GPU-Architektur: nur noch ein Container (Coder + Judge in einem).
SINGLE="qwen36-27b-single"
SINGLE="coding_model"
if docker ps -a --format '{{.Names}}' | grep -q "^${SINGLE}\$"; then
docker rm -f "$SINGLE" >/dev/null

View file

@ -8,11 +8,11 @@ import { readJson, readRepoFile } from "./helpers.mjs";
const models = readJson("models.json");
const settings = readJson("settings.json");
test("models.json: Provider llama-cpp-single hat genau ein Modell qwen3.5-single auf Port 8001", () => {
test("models.json: Provider llama-cpp-single hat genau ein Modell coding_model auf Port 8001", () => {
const p = models.providers["llama-cpp-single"];
assert.ok(p, "Provider llama-cpp-single fehlt");
assert.match(p.baseUrl, /:8001\//, "llama-cpp-single muss auf Port 8001 zeigen");
assert.deepEqual(p.models.map((m) => m.id), ["qwen3.5-single"]);
assert.deepEqual(p.models.map((m) => m.id), ["coding_model"]);
});
test("models.json: tote Dual-/Alt-Provider sind entfernt", () => {
@ -27,8 +27,8 @@ test("models.json: kein Verweis mehr auf den toten Port 8002", () => {
test("settings.json: Default zeigt auf den Single-Server (nicht Port 8002)", () => {
assert.equal(settings.defaultProvider, "llama-cpp-single");
assert.equal(settings.defaultModel, "qwen3.5-single");
assert.deepEqual(settings.enabledModels, ["llama-cpp-single/qwen3.5-single"]);
assert.equal(settings.defaultModel, "coding_model");
assert.deepEqual(settings.enabledModels, ["llama-cpp-single/coding_model"]);
});
test("settings.json: defaultProvider/defaultModel existieren in models.json", () => {
@ -61,7 +61,7 @@ test("settings.json ist valides JSON ohne überraschende Felder", () => {
test("Konsistenz: Extension und settings.json nennen denselben Provider/dasselbe Modell", () => {
const ext = readRepoFile("pi-coder-judge-extension.ts");
assert.match(ext, /SINGLE_PROVIDER = "llama-cpp-single"/);
assert.match(ext, /SINGLE_MODEL_ID = "qwen3.5-single"/);
assert.match(ext, /SINGLE_MODEL_ID = "coding_model"/);
assert.equal(settings.defaultProvider, "llama-cpp-single");
assert.equal(settings.defaultModel, "qwen3.5-single");
assert.equal(settings.defaultModel, "coding_model");
});

View file

@ -22,19 +22,19 @@ test("Dual-GPU-Skripte sind entfernt", () => {
}
});
test("start-single.sh nutzt eine GPU und registriert Alias qwen3.5-single", () => {
test("start-single.sh nutzt eine GPU und registriert Alias coding_model", () => {
const s = readRepoFile("start-single.sh");
assert.match(s, /--alias\s+"\$\{MODEL_ALIAS\}"/);
assert.match(s, /MODEL_ALIAS="qwen3\.5-single"/);
assert.match(s, /MODEL_ALIAS="coding_model"/);
assert.match(s, /device=\$\{GPU_DEVICE\}/, "GPU muss konfigurierbar/einzeln sein");
});
test("stop-servers.sh stoppt den Single-Container", () => {
assert.match(readRepoFile("stop-servers.sh"), /qwen36-27b-single/);
assert.match(readRepoFile("stop-servers.sh"), /coding_model/);
});
test("status.sh prüft den Single-Container", () => {
assert.match(readRepoFile("status.sh"), /qwen36-27b-single/);
assert.match(readRepoFile("status.sh"), /coding_model/);
});
test("install-Skript deployt settings.json mit vorherigem Backup", () => {