From a8a6ce3b2970f1976e3aa9e3aca620ba5b2fb238 Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Fri, 4 Sep 2026 14:52:50 +0200 Subject: [PATCH] =?UTF-8?q?feat(radar):=20Blick=20nach=20draussen=20als=20?= =?UTF-8?q?Skript=20=E2=80=94=20und=20heavy=20zieht=20auf=20Qwen3.8-27B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Warum: DFlash2 fuer den Coder lag vom 19.08. bis 04.09. ungenutzt auf der Platte, weil der KISS-Radar nur nach innen sah. stack-upstream.py fragt die Watchlist als Fakten ab (GitHub-Issues/PRs/Releases/Branches, PyPI, neue Repos eines HF-Autors, Zeilen einer URL), vergleicht mit dem letzten Lauf und meldet nur Aenderungen als ACHTUNG NEU. stack-radar.sh haengt das an den IST-Zustand; faellt es aus, bleibt der Innen-Bericht vollstaendig. Erster Lauf fand sofort: MMQ-Issue 21284 ist geschlossen, pocket-tts 3.1.0, Electron 44 — alles Dinge, die der Radar seit Juli haette melden sollen. heavy: gpt-oss-120b (AA-Index 24, 60 GB) gibt die Rolle an Qwen3.8-27B ab (Index 52, 17 GB, 31 t/s mit DFlash2) — ein Modell, zwei Rollen. gpt-oss bleibt ohne Alias als Rollback. Live gemessen ueber :9010 mit Reasoning. Co-Authored-By: Claude Fable 5.1 --- deploy/jobs/stack-radar.sh | 17 +++ deploy/jobs/stack-upstream.py | 201 +++++++++++++++++++++++++++ deploy/llama-swap.config.yaml | 5 +- deploy/opencode-config/opencode.json | 2 +- deploy/trend-radar-watchlist.json | 68 ++++++--- docs/wissen/STACK.md | 2 +- 6 files changed, 268 insertions(+), 27 deletions(-) create mode 100644 deploy/jobs/stack-upstream.py diff --git a/deploy/jobs/stack-radar.sh b/deploy/jobs/stack-radar.sh index 90ed702..46c8000 100644 --- a/deploy/jobs/stack-radar.sh +++ b/deploy/jobs/stack-radar.sh @@ -42,6 +42,20 @@ if [ -z "$IST" ]; then exit 1 fi +# Der Blick nach DRAUSSEN (04.09.2026): Releases, PRs, neue Entwurfsmodelle — +# als Skript-Fakten, gegen den letzten Lauf verglichen. Ohne das lag DFlash2 fuer +# den Coder zwei Wochen ungenutzt auf der Platte. Faellt es aus, bleibt der +# Innen-Bericht vollstaendig; das Ausbleiben steht dann selbst als ACHTUNG drin. +UPSTREAM_SKRIPT="${UPSTREAM_SKRIPT:-$HIER/stack-upstream.py}" +if [ -f "$UPSTREAM_SKRIPT" ]; then + UPSTREAM="$(timeout 240 python3 "$UPSTREAM_SKRIPT" 2>&1)" \ + || UPSTREAM="UPSTREAM (Watchlist) + ACHTUNG Upstream-Pruefung abgebrochen (exit $?) — $UPSTREAM_SKRIPT nachsehen" + IST="$IST + +$UPSTREAM" +fi + ROT="$(printf '%s\n' "$IST" | grep -E '^\s+(ROT|ACHTUNG)' || true)" ANZ_ROT="$(printf '%s\n' "$ROT" | grep -c 'ROT' || true)" @@ -61,6 +75,9 @@ Regeln: Fuellmaterial wird nicht gelesen. - Was du an den Messwerten selbst merkwuerdig findest, gehoert unter ACHTUNG, nicht unter Verbesserungen. +- Zeilen mit NEU im Abschnitt UPSTREAM sind Neuigkeiten von draussen. Nenne + jede kurz und gib den Hinweis dahinter (der Pfeil-Satz) als naechsten Schritt + wieder. 'erstmals erfasst' und 'unveraendert' sind KEINE Neuigkeiten. - Wenn alles gruen ist, sag das in einem Satz und hoere auf. - Keine Ueberschriften-Deko, keine Emojis, hoechstens 200 Woerter. diff --git a/deploy/jobs/stack-upstream.py b/deploy/jobs/stack-upstream.py new file mode 100644 index 0000000..9f29648 --- /dev/null +++ b/deploy/jobs/stack-upstream.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +"""stack-upstream.py — der Blick nach draussen, in Fakten (04.09.2026). + +Ergaenzt stack-ist.sh: das prueft, ob die Box STIMMT. Dieses Skript prueft, ob +DRAUSSEN etwas passiert ist, das uns betrifft — neue Releases, gemergte PRs, +neue Entwurfsmodelle, neue Modelle der Familien, die wir fahren. + +Warum es das braucht: Am 19.08. lagen die DFlash2-Entwuerfe fuer den Coder auf +der Platte, am 27.08. konnte llama.cpp sie, und bis zum 04.09. hat es niemand +gemerkt — der Coder lief zwei Wochen mit 12,6 statt 31 t/s. Der KISS-Radar sah +nur nach innen. Jetzt sieht er auch nach draussen, aber ohne Modell-Fantasie: +jede Zeile hier ist ein abgerufener Wert, verglichen mit dem Wert vom letzten Lauf. + +Quelle: deploy/trend-radar-watchlist.json (Eintraege mit `typ`, siehe unten). +Zustand: ~/.hermes/state/stack-upstream.json (Wert je Eintrag vom letzten Lauf). +Ausgabe: im Format von stack-ist.sh — `OK` / `ACHTUNG NEU:` / `ACHTUNG ... nicht abrufbar`. +Beim ERSTEN Lauf wird nur erfasst (OK), nicht gemeldet — sonst waere jeder +Eintrag sofort "neu". + +Typen: + github_issue repo, nummer → offen/geschlossen + github_pr repo, nummer → offen/gemergt/geschlossen + github_release repo → juengster Release-Tag + github_release_major repo → nur Major-Sprung meldet + github_branch repo, branch → juengster Commit (Datum) + pypi paket → Version + hf_author author → neue Modell-Repos des Autors + url_zeilen url, muster, max_zeilen → Zeilen mit Muster, Aenderung = neu + +Kein set -e-Aequivalent: jeder Eintrag wird einzeln behandelt, ein Fehler +nimmt die anderen nicht mit. +""" +from __future__ import annotations + +import hashlib +import json +import os +import re +import sys +import urllib.error +import urllib.request +from pathlib import Path + +WATCHLIST = Path(sys.argv[1] if len(sys.argv) > 1 else + os.path.expanduser("~/mission-control-v2/deploy/trend-radar-watchlist.json")) +STATE = Path(os.environ.get("STACK_UPSTREAM_STATE", + os.path.expanduser("~/.hermes/state/stack-upstream.json"))) +TIMEOUT = 20 +UA = "mc2-stack-upstream/1.0 (+https://github.com/Hitonabi)" + + +def ok(msg: str) -> None: + print(f" OK {msg}") + + +def neu(msg: str) -> None: + print(f" ACHTUNG NEU: {msg}") + + +def warn(msg: str) -> None: + print(f" ACHTUNG {msg}") + + +def hole(url: str, json_erwartet: bool = True): + req = urllib.request.Request(url, headers={"User-Agent": UA, "Accept": "application/json"}) + tok = os.environ.get("GITHUB_TOKEN") + if tok and "api.github.com" in url: + req.add_header("Authorization", f"Bearer {tok}") + with urllib.request.urlopen(req, timeout=TIMEOUT) as r: + roh = r.read().decode("utf-8", "replace") + return json.loads(roh) if json_erwartet else roh + + +# ------------------------------------------------------------- Pruefer --- +# Jeder Pruefer liefert (wert, anzeige). `wert` wird mit dem letzten Lauf +# verglichen, `anzeige` ist die menschenlesbare Fassung. + +def p_github_issue(e): + d = hole(f"https://api.github.com/repos/{e['repo']}/issues/{e['nummer']}") + st = "geschlossen" if d.get("state") == "closed" else "offen" + return st, f"#{e['nummer']} {e['repo']} ist {st} — {d.get('title', '')[:70]}" + + +def p_github_pr(e): + d = hole(f"https://api.github.com/repos/{e['repo']}/pulls/{e['nummer']}") + if d.get("merged_at"): + st = f"gemergt am {d['merged_at'][:10]}" + elif d.get("state") == "closed": + st = "geschlossen ohne Merge" + else: + st = "offen" + (" (Entwurf)" if d.get("draft") else "") + return st, f"PR #{e['nummer']} {e['repo']} {st} — {d.get('title', '')[:70]}" + + +def _release(e): + d = hole(f"https://api.github.com/repos/{e['repo']}/releases/latest") + return d.get("tag_name", "?"), (d.get("published_at") or "")[:10] + + +def p_github_release(e): + tag, wann = _release(e) + return tag, f"{e['repo']} juengster Release {tag} ({wann})" + + +def p_github_release_major(e): + tag, wann = _release(e) + m = re.search(r"(\d+)", tag) + major = m.group(1) if m else tag + return major, f"{e['repo']} Major {major} (Release {tag}, {wann})" + + +def p_github_branch(e): + d = hole(f"https://api.github.com/repos/{e['repo']}/commits?sha={e['branch']}&per_page=1") + c = d[0] + wann = c["commit"]["committer"]["date"][:10] + return c["sha"][:10], f"{e['repo']}@{e['branch']} letzter Commit {wann} — {c['commit']['message'].splitlines()[0][:60]}" + + +def p_pypi(e): + d = hole(f"https://pypi.org/pypi/{e['paket']}/json") + v = d["info"]["version"] + return v, f"PyPI {e['paket']} = {v}" + + +def p_hf_author(e): + d = hole(f"https://huggingface.co/api/models?author={e['author']}&sort=lastModified&direction=-1&limit={e.get('limit', 15)}") + ids = sorted(m["id"] for m in d) + return "\n".join(ids), f"{len(ids)} juengste Repos von {e['author']} auf Hugging Face" + + +def p_url_zeilen(e): + roh = hole(e["url"], json_erwartet=False) + zeilen = [z.strip() for z in roh.splitlines() if e["muster"] in z][: int(e.get("max_zeilen", 30))] + h = hashlib.sha1("\n".join(zeilen).encode()).hexdigest()[:10] + return h, f"{len(zeilen)} Zeilen mit '{e['muster']}' in {e['url'].split('/')[2]} (Stand {h})" + + +PRUEFER = { + "github_issue": p_github_issue, + "github_pr": p_github_pr, + "github_release": p_github_release, + "github_release_major": p_github_release_major, + "github_branch": p_github_branch, + "pypi": p_pypi, + "hf_author": p_hf_author, + "url_zeilen": p_url_zeilen, +} + + +def main() -> int: + print("UPSTREAM (Watchlist — was hat sich draussen getan?)") + try: + eintraege = json.loads(WATCHLIST.read_text(encoding="utf-8")).get("eintraege", []) + except Exception as exc: # noqa: BLE001 + warn(f"Watchlist {WATCHLIST} nicht lesbar: {exc}") + return 0 + try: + state = json.loads(STATE.read_text(encoding="utf-8")) if STATE.exists() else {} + except Exception: # noqa: BLE001 + state = {} + + for e in eintraege: + key, typ = e.get("key", "?"), e.get("typ", "?") + pruefer = PRUEFER.get(typ) + if not pruefer: + warn(f"{key}: unbekannter Typ '{typ}' — Eintrag wird ignoriert") + continue + try: + wert, anzeige = pruefer(e) + except urllib.error.HTTPError as exc: + warn(f"{key}: nicht abrufbar (HTTP {exc.code})") + continue + except Exception as exc: # noqa: BLE001 + warn(f"{key}: nicht abrufbar ({type(exc).__name__}: {str(exc)[:60]})") + continue + + alt = state.get(key, {}).get("wert") + if alt is None: + ok(f"{key}: erstmals erfasst — {anzeige}") + elif alt == wert: + ok(f"{key}: unveraendert — {anzeige}") + else: + if typ == "hf_author": + neue = sorted(set(wert.split("\n")) - set(alt.split("\n"))) + anzeige = f"neue Repos von {e['author']}: " + ", ".join(neue[:8]) if neue else anzeige + hinweis = e.get("warum", "") + neu(f"{key}: {anzeige}" + (f"\n → {hinweis[:220]}" if hinweis else "")) + state[key] = {"wert": wert, "anzeige": anzeige} + + try: + STATE.parent.mkdir(parents=True, exist_ok=True) + tmp = STATE.with_suffix(".tmp") + tmp.write_text(json.dumps(state, ensure_ascii=False, indent=1), encoding="utf-8") + os.replace(tmp, STATE) + except Exception as exc: # noqa: BLE001 + warn(f"Zustand nicht speicherbar ({exc}) — naechster Lauf meldet alles erneut") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/deploy/llama-swap.config.yaml b/deploy/llama-swap.config.yaml index e2647be..f6d1582 100644 --- a/deploy/llama-swap.config.yaml +++ b/deploy/llama-swap.config.yaml @@ -32,6 +32,7 @@ models: ttl: 5400 aliases: - coder + - heavy capabilities: in: [text, image] out: [text] @@ -64,8 +65,8 @@ models: cmd: | llama-server -m /srv/models/gpt-oss-120b-GGUF/gpt-oss-120b-mxfp4-00001-of-00003.gguf --host 127.0.0.1 --port ${PORT} -c 32768 -ngl 999 -fa on --no-mmap --jinja --cache-reuse 256 -cram 16384 ttl: 600 - aliases: - - heavy + # 04.09.2026: Rolle `heavy` an Qwen3.8-27B abgegeben (AA-Index 52 vs 24, 17 statt 60 GB, 31 t/s mit DFlash2). + # Ohne Alias = Rollback-Reserve, direkt als `gpt-oss-120b` ansprechbar. capabilities: in: [text] out: [text] diff --git a/deploy/opencode-config/opencode.json b/deploy/opencode-config/opencode.json index 6a7ba27..cd49ae1 100644 --- a/deploy/opencode-config/opencode.json +++ b/deploy/opencode-config/opencode.json @@ -18,7 +18,7 @@ } }, "heavy": { - "name": "heavy - Planen / Review", + "name": "heavy - Planen / Review (= Qwen3.8-27B, seit 04.09. mit DFlash2)", "tool_call": true, "limit": { "context": 32768, diff --git a/deploy/trend-radar-watchlist.json b/deploy/trend-radar-watchlist.json index f3ce1e5..8c0c895 100644 --- a/deploy/trend-radar-watchlist.json +++ b/deploy/trend-radar-watchlist.json @@ -1,51 +1,73 @@ { - "_hinweis": "Living Watchlist des Trend-Radars. Diese Datei MUSS autonom vom Trend-Radar Agenten umgeschrieben werden, wenn er neue Quellen findet oder alte obsolet werden.", + "_hinweis": "Watchlist des Stack-Radars. Wird von deploy/jobs/stack-upstream.py (Skript, kein Modell) samstags abgefragt; jeder Eintrag = ein abrufbarer Wert, verglichen mit dem letzten Lauf. Typen: github_issue, github_pr, github_release, github_release_major, github_branch, pypi, hf_author, url_zeilen. `warum` wird bei NEU mitgemeldet — dort steht, was dann zu tun ist.", "eintraege": [ { - "key": "mmq-prefill-gfx1151", - "typ": "github_issue", + "key": "qwen-modelle", + "typ": "hf_author", + "author": "Qwen", + "limit": 20, + "warum": "Lucys Hirn ist Qwen3.6-35B-A3B, der Coder Qwen3.8-27B. Ein neues A3B-Modell waere der Hirn-Kandidat (dichte Modelle nie, Verdikt 17.07.); ein neues 27B/30B der Coder-Kandidat. Immer erst auf der Box messen." + }, + { + "key": "zlab-drafts", + "typ": "hf_author", + "author": "z-lab", + "limit": 20, + "warum": "z-lab baut die DFlash-Entwurfsmodelle. DFlash2 brachte dem Coder 12,6 -> 31 t/s (04.09.). Ein DFlash2 fuer Qwen3.6-35B-A3B waere derselbe Hebel fuers Hirn — DFlash v1 faellt bei 32k Tiefe auf 1,02x." + }, + { + "key": "flash-next-mtp", + "typ": "github_pr", "repo": "ggml-org/llama.cpp", - "nummer": 21284, - "bedingung": "Issue geschlossen = MMQ-Tuning für gfx1151 vermutlich gemerged", - "warum": "Getunte MMQ-Defaults brachten +60 % ROCm-Prefill auf 35B-MoE (unser Hirn-Typ) und +20 % auf 122B. Wenn gemerged: ROCm-Langkontext-Bench für coder fällig (Vulkan-Verdikt bleibt bis zur Messung)." + "nummer": 28243, + "warum": "MTP fuer Qwen3.8-Flash-Next (125B-A6B). Erst mit Merge + Vulkan-Zahlen lohnt ein Blick — und nur, wenn eine Quantisierung neben das Warm-Set passt (Regel: Warm-Set + Modell <= 115 GB; UD-IQ4_XS = 94 GB passt NICHT)." + }, + { + "key": "hrx-backend", + "typ": "github_branch", + "repo": "AMD-Ecosystem/llama.cpp", + "branch": "hrx-graph-develop-v2", + "warum": "AMDs HRX-Runtime (Lemonade 11.9): gegen HIP +21-25 % Decode, +20-123 % Prefill, also etwa Vulkan-Niveau beim Decode und vorn beim Prefill. Stand 04.09. nur Qwen3-30B-A3B/Llama-3/Gemma-3. Interessant, sobald Qwen3.6/3.8 und Spec-Decoding laufen — dann Box-Bench gegen Vulkan." }, { "key": "rocm-releases", "typ": "github_release", "repo": "ROCm/ROCm", - "bedingung": "Neuer ROCm-Release seit letzter Meldung", - "warum": "Seit 7.14 ist TheRock der Produktions-Stack und AMD optimiert offiziell für die Halo-Familie — ROCm könnte Vulkan bei der Token-Erzeugung einholen. Bei neuem Release: Community-Grid-Zeilen unten gegenlesen." + "warum": "ROCm 10.0 (27.08.) hat das Vulkan-Verdikt nicht gekippt: HIP gewinnt Prefill, Vulkan Decode. Bei neuem Release: Strix-Halo-Community-Grid gegenlesen, erst bei tg-Trendwende Box-Bench (Lychee-Technology liefert fertige ROCm-Builds)." + }, + { + "key": "mmq-prefill-gfx1151", + "typ": "github_issue", + "repo": "ggml-org/llama.cpp", + "nummer": 21284, + "warum": "Getunte MMQ-Defaults brachten +60 % ROCm-Prefill auf 35B-MoE (unser Hirn-Typ). Wenn geschlossen: ROCm-Langkontext-Bench fuer coder faellig (Vulkan-Verdikt bleibt bis zur Messung)." + }, + { + "key": "strix-halo-grid", + "typ": "url_zeilen", + "url": "https://raw.githubusercontent.com/hogeheer499-commits/strix-halo-guide/main/README.md", + "muster": "t/s", + "max_zeilen": 30, + "warum": "Der ehrlichste externe Messpunkt auf unserer Hardware. Bei Aenderung: auf Vulkan<->ROCm-Trendwende und neue Spec-Decoding-Verfahren achten." }, { "key": "electron-major", "typ": "github_release_major", "repo": "electron/electron", - "bedingung": "Neue MAJOR-Version seit letzter Meldung", - "warum": "Lucys Desktop-Shell am PC. Versionsstand NIE dieser Notiz glauben — IMMER live pruefen: lucy-desktop/package.json + node_modules/electron (22.07.: 43.2.0 = aktueller Stable; der fruehere '33'-Stand hier war veraltet und produzierte einen Fehlalarm im Rundumschlag). Neue Major = Chromium-Jahrgang Sicherheitspatches; Update = npm install + npm run dist am PC, bleibt Handarbeit; der Radar erinnert nur." + "warum": "Lucys Desktop-Shell am PC. Versionsstand IMMER live pruefen (lucy-desktop/package.json), nie dieser Notiz glauben. Neue Major = Chromium-Sicherheitspatches; Update = npm install + npm run dist am PC, bleibt Handarbeit." }, { "key": "pocket-tts", "typ": "pypi", "paket": "pocket-tts", - "bedingung": "Neue Version auf PyPI", - "warum": "Lucys Stimme am PC (installiert 2.1.0, german_24l) — neue Versionen können Stimm-/Pausen-Fixes bringen; Update bleibt Handarbeit am PC (pocket = DIE Stimme, Verdikt 16.07.)." + "warum": "Lucys Stimme am PC (2.1.0, german_24l). Neue Versionen koennen Stimm-/Pausen-Fixes bringen; Update bleibt Handarbeit am PC (pocket = DIE Stimme, Verdikt 16.07.)." }, { "key": "lmstudio-bionic", "typ": "github_issue", "repo": "lmstudio-ai/lmstudio-bug-tracker", "nummer": 2185, - "bedingung": "Issue geschlossen = Bionic-Linux-Port vermutlich da", - "warum": "LM Studio Bionic (eigenstaendige Agent-App, Preview seit 16.07.26, Win+Mac, gratis, closed source). Stand 22.07.: KEINE eigenen OpenAI-Endpunkte anbindbar (Box :9001 bleibt draussen — nur In-App-Modelle, LM Link mit Konto+Tailscale-Mesh oder Secure Cloud), keine CLI/API/Headless. Fuer den Stack erst interessant bei: Linux-Port (dieses Issue), Custom-Endpoint-Support oder CLI — dazu Changelog lmstudio.ai/changelog gegenlesen; llmster auf der Box waere der LM-Link-Rechenknoten-Weg (seit 0.4.20), dupliziert aber die eigene Engine." - }, - { - "key": "llamacpp-rocm-diskussion", - "typ": "url_zeilen", - "url": "https://raw.githubusercontent.com/hogeheer499-commits/strix-halo-guide/main/README.md", - "muster": "t/s", - "max_zeilen": 30, - "bedingung": "Community-Messwerte (Vulkan vs. ROCm, MTP, neue Beschleuniger) auf unserer Hardware", - "warum": "Der Strix-Halo-Community-Grid ist der ehrlichste externe Messpunkt. Auf tg-Trendwende Vulkan↔ROCm und neue Spec-Decoding-Verfahren achten." + "warum": "LM Studio Bionic: erst interessant bei Linux-Port (dieses Issue), Custom-Endpoint-Support oder CLI. Bis dahin bleibt die Box :9001 draussen." } ] } diff --git a/docs/wissen/STACK.md b/docs/wissen/STACK.md index f5c71ba..7566c50 100644 --- a/docs/wissen/STACK.md +++ b/docs/wissen/STACK.md @@ -52,7 +52,7 @@ Qwen3.6-35B-A3B + Qwen3-Embedding-0.6B + Qwen3-Reranker-0.6B. Sonst NICHTS dauer | `coder` | Qwen3.8-27B (Q4_K_M, mmproj BF16) | 5400 | **131072 ctx** — `--parallel 1`, der Coder bekommt den ganzen Slot (34a9862). Multimodal, ~12,7 t/s (dicht = bandbreitengebunden) | | `debugger` | Muse-Glimmer-30B (Q4_K_XL, DFlash) | 600 | 65536 ctx; Runtime-Diagnostik & Fehler-Debugger (multimodal) | | `vision` | Qwen3-VL-30B-A3B-Instruct (Q4_K_M) | 900 | 32768 ctx; Multimodal-Augen (On-Demand) | -| `heavy` | gpt-oss-120b (mxfp4) | 600 | 32768 ctx; Chef-Gutachter (nur nachts / gezielter On-Demand-Call) | +| `heavy` | **= Qwen3.8-27B** (zweiter Alias des Coders, seit 04.09.) | 5400 | Planen/Review (OpenChamber) + chat-Lane des Gateways. gpt-oss-120b (60 GB, AA-Index 24 vs 52) liegt ohne Rolle als Rollback bereit, direkt als `gpt-oss-120b` ansprechbar | | `reranker` | Qwen3-Reranker-0.6B (q8_0, Mungert) | 0 | Sortiert Suchtreffer nach echter Relevanz (`/v1/rerank`) | | `embed` | Qwen3-Embedding-0.6B (f16) | 0 | Vektorisierung für Suche/Sortierung |