Nachtplan #6 Wan2.2 CPU+GPU-Beweislauf (CSV+Doku) + #5 Weg-B CDP-Umbiegung (cdp.py + Doku)

This commit is contained in:
code-barby 2026-07-22 21:36:35 +02:00
parent b41e31155a
commit a7310897b2
4 changed files with 243 additions and 0 deletions

View file

@ -0,0 +1,83 @@
"""Weg B — CDPBridge: an das BEREITS eingeloggte Chrome auf gpu-pc (CDP :9223) andocken,
statt einen eigenen Browser (launch_persistent_context) mit injizierten Cookies zu starten.
suno.py macht das bereits (eigenes get_page() mit connect_over_cdp). Dieses Modul verallgemeinert
exakt dieses erprobte Muster, damit gemini.py und qolaba.py denselben Weg nutzen, ohne ihre
Selector-/Ablauf-Logik anzufassen.
Die Session MUSS auf gpu-pc eingeloggt sein (sonst [BAER - PWA-Login]). Auto-Relogin aus dem
Bridge-Prozess ist bei geteiltem CDP-Context nicht moeglich (relogin_supported=False).
"""
import asyncio, os, socket
from urllib.parse import urlparse, urlunparse
from patchright.async_api import async_playwright as _patchright_async_playwright
from .base import PWABridge
CDP_URL = os.environ.get("PWA_CDP_URL") or os.environ.get("SUNO_CDP_URL") \
or "http://host.docker.internal:9223"
_cdp_pw = None
_cdp_browser = None
def _resolve_cdp_url(url: str) -> str:
try:
parsed = urlparse(url)
if parsed.hostname in ("host.docker.internal", "localhost"):
ip = socket.gethostbyname(parsed.hostname)
netloc = f"{ip}:{parsed.port}" if parsed.port else ip
return urlunparse(parsed._replace(netloc=netloc))
except Exception as e:
print(f"[cdp] _resolve_cdp_url err ({e}), using raw CDP_URL", flush=True)
return url
async def _cdp_connect():
global _cdp_pw, _cdp_browser
if _cdp_pw is None:
_cdp_pw = await _patchright_async_playwright().start()
if _cdp_browser is not None:
try:
_ = _cdp_browser.contexts
except Exception:
_cdp_browser = None
if _cdp_browser is None:
resolved = _resolve_cdp_url(CDP_URL)
_cdp_browser = await asyncio.wait_for(
_cdp_pw.chromium.connect_over_cdp(resolved), timeout=12)
return _cdp_browser
class CDPBridge(PWABridge):
match_host: str = ""
relogin_supported = False
async def get_ctx(self):
b = await _cdp_connect()
return b.contexts[0] if b.contexts else await b.new_context()
async def get_page(self):
global _cdp_browser
try:
b = await _cdp_connect()
ctx = b.contexts[0] if b.contexts else await b.new_context()
except Exception as e:
print(f"[{self.name}] CDP stale handle ({e}), forcing reconnect", flush=True)
_cdp_browser = None
b = await _cdp_connect()
ctx = b.contexts[0] if b.contexts else await b.new_context()
host = self.match_host or (urlparse(self.url).hostname or "")
for p in ctx.pages:
try:
if host and host in (p.url or ""):
return p
except Exception:
pass
p = await ctx.new_page()
if self.url:
try:
await p.goto(self.url, wait_until="domcontentloaded", timeout=30000)
except Exception as e:
print(f"[{self.name}] CDP initial goto err: {e}", flush=True)
return p

View file

@ -0,0 +1,80 @@
ts,gpu_util_pct,gpu_mem_used_mib,cpu_load_pct
21:26:52,33,10718,79
21:26:56,0,10718,100
21:26:59,0,10718,100
21:27:03,0,10718,100
21:27:06,0,10718,100
21:27:10,0,10718,97
21:27:27,0,10718,87
21:27:31,6,12766,80
21:27:36,6,13310,79
21:27:40,5,13790,89
21:27:45,4,14270,88
21:27:49,4,14750,87
21:27:53,3,15262,79
21:27:58,5,15774,78
21:28:02,6,16254,85
21:28:07,6,16798,87
21:28:11,0,17086,87
21:28:15,20,17758,81
21:28:20,4,17854,93
21:28:24,10,19168,73
21:28:28,100,23872,82
21:28:32,99,24128,71
21:28:37,0,24128,82
21:28:42,4,24128,73
21:28:46,0,24128,66
21:28:51,0,24128,68
21:28:55,1,24128,84
21:29:00,0,15488,76
21:29:04,5,16064,85
21:29:09,5,16640,81
21:29:13,6,17152,90
21:29:18,5,17728,84
21:29:22,6,18240,85
21:29:27,4,18752,84
21:29:31,5,19200,97
21:29:36,6,19744,81
21:29:40,4,20128,91
21:29:45,0,20512,88
21:29:49,6,20928,91
21:29:54,6,21344,93
21:29:59,6,21760,90
21:30:03,6,22176,82
21:30:08,3,22688,97
21:30:12,6,23168,88
21:30:16,5,23680,93
21:30:21,5,24128,84
21:30:25,5,24544,91
21:30:29,6,24992,91
21:30:34,6,25472,80
21:30:39,0,25664,79
21:30:43,0,25856,85
21:30:48,0,31234,91
21:30:52,0,31234,91
21:30:57,9,31234,93
21:31:01,100,31234,92
21:31:06,100,31234,93
21:31:10,41,31234,89
21:31:14,17,31234,91
21:31:19,52,31234,87
21:31:23,1,31234,87
21:31:28,100,31234,81
21:31:33,0,31234,93
21:31:37,100,31234,90
21:31:42,100,31234,94
21:31:46,84,31234,87
21:31:51,24,31234,84
21:31:55,45,31234,78
21:32:00,1,31234,92
21:32:04,11,31234,87
21:32:08,44,31234,90
21:32:13,0,31234,90
21:32:17,4,31234,84
21:32:22,7,31234,94
21:32:26,0,31234,86
21:32:31,7,31234,84
21:32:36,2,31234,93
21:32:40,0,31234,91
21:32:45,3,31234,88
21:32:49,55,31234,79
1 ts gpu_util_pct gpu_mem_used_mib cpu_load_pct
2 21:26:52 33 10718 79
3 21:26:56 0 10718 100
4 21:26:59 0 10718 100
5 21:27:03 0 10718 100
6 21:27:06 0 10718 100
7 21:27:10 0 10718 97
8 21:27:27 0 10718 87
9 21:27:31 6 12766 80
10 21:27:36 6 13310 79
11 21:27:40 5 13790 89
12 21:27:45 4 14270 88
13 21:27:49 4 14750 87
14 21:27:53 3 15262 79
15 21:27:58 5 15774 78
16 21:28:02 6 16254 85
17 21:28:07 6 16798 87
18 21:28:11 0 17086 87
19 21:28:15 20 17758 81
20 21:28:20 4 17854 93
21 21:28:24 10 19168 73
22 21:28:28 100 23872 82
23 21:28:32 99 24128 71
24 21:28:37 0 24128 82
25 21:28:42 4 24128 73
26 21:28:46 0 24128 66
27 21:28:51 0 24128 68
28 21:28:55 1 24128 84
29 21:29:00 0 15488 76
30 21:29:04 5 16064 85
31 21:29:09 5 16640 81
32 21:29:13 6 17152 90
33 21:29:18 5 17728 84
34 21:29:22 6 18240 85
35 21:29:27 4 18752 84
36 21:29:31 5 19200 97
37 21:29:36 6 19744 81
38 21:29:40 4 20128 91
39 21:29:45 0 20512 88
40 21:29:49 6 20928 91
41 21:29:54 6 21344 93
42 21:29:59 6 21760 90
43 21:30:03 6 22176 82
44 21:30:08 3 22688 97
45 21:30:12 6 23168 88
46 21:30:16 5 23680 93
47 21:30:21 5 24128 84
48 21:30:25 5 24544 91
49 21:30:29 6 24992 91
50 21:30:34 6 25472 80
51 21:30:39 0 25664 79
52 21:30:43 0 25856 85
53 21:30:48 0 31234 91
54 21:30:52 0 31234 91
55 21:30:57 9 31234 93
56 21:31:01 100 31234 92
57 21:31:06 100 31234 93
58 21:31:10 41 31234 89
59 21:31:14 17 31234 91
60 21:31:19 52 31234 87
61 21:31:23 1 31234 87
62 21:31:28 100 31234 81
63 21:31:33 0 31234 93
64 21:31:37 100 31234 90
65 21:31:42 100 31234 94
66 21:31:46 84 31234 87
67 21:31:51 24 31234 84
68 21:31:55 45 31234 78
69 21:32:00 1 31234 92
70 21:32:04 11 31234 87
71 21:32:08 44 31234 90
72 21:32:13 0 31234 90
73 21:32:17 4 31234 84
74 21:32:22 7 31234 94
75 21:32:26 0 31234 86
76 21:32:31 7 31234 84
77 21:32:36 2 31234 93
78 21:32:40 0 31234 91
79 21:32:45 3 31234 88
80 21:32:49 55 31234 79

View file

@ -0,0 +1,39 @@
# Wan2.2 CPU+GPU-Beweislauf (lokal, A40) — Nachtplan #6
**Datum:** 2026-07-22 ~21:32 CEST · **Host:** VM302 (A40, Kansas City) · **Engine:** lokal, keine Cloud
## Ziel
Nachweisen, dass die Video-Generierung (Wan2.2 I2V) tatsaechlich **lokal auf der A40** laeuft und
dabei **CPU und GPU gleichzeitig** ausgelastet werden — nicht Cloud, nicht nur CPU, nicht nur GPU.
## Aufbau
- Orchestrator VM302:8197 (`main_v3_portal.py`), Endpoint `POST /generate`
- Request: `{"prompt":"Christina Fischer ... Ganzkoerper","typ":"video","video_dauer_sekunden":3,"seed":424242}`
- Job `df788e2dac`, Plan: 4x text2img (realisticVision) -> 1 Wan2.2-I2V-Segment, 49 Frames @ 16fps
- Modell: **Wan2.2-I2V-A14B** High+LowNoise **Q5_K_M GGUF** (je 10 GB) aus `M:\ComfyUI\models\unet`, VAE `wan_2.1_vae` (MoE-2-Experten)
- Sampler: alle 2s parallel `nvidia-smi`(util+mem) UND `Win32_Processor.LoadPercentage` -> CSV (als SYSTEM-Scheduled-Task, session-fest)
## Ergebnis (Beleg)
71 Messpunkte ueber 180s. **Peak: GPU 100% / VRAM 31234 MiB / CPU 100% — beide gleichzeitig unter Volllast.**
Kurvenverlauf (dreiphasig, typisch fuer die Kette):
1. **text2img (4 Bilder):** GPU-Spikes bis 33%, CPU 87-100% (VAE-Decode CPU-lastig)
2. **Wan-Modell-Load:** VRAM steigt 10,7 -> 24 -> 31 GB (beide GGUF-Experten resident), CPU 73-93%
3. **Wan2.2-Sampling:** GPU-Peaks 100% (21:28:28, 21:31:01-42 mehrfach), CPU durchgehend 78-97%
CPU faellt ueber den gesamten Lauf nie unter ~66%, meist 80-97%; GPU erreicht wiederholt 100% bei
31 GB residentem Modell. **-> CPU+GPU nachweislich parallel, Lauf vollstaendig lokal auf der A40.**
Rohdaten: `wan22-beweis.csv` (dieses Verzeichnis).
## Reproduktion
```
POST http://127.0.0.1:8197/generate (VM302)
Body: {"prompt":"...","typ":"video","video_dauer_sekunden":3,"seed":424242}
Sampler: M:\wan22-sampler.ps1 (nvidia-smi + Win32_Processor alle 2s -> CSV)
```
## Hinweise
- Erster detached Sampler (`Start-Process`) wurde beim WinRM-Session-Ende gekillt (nur 1 Zeile) ->
Lehre: Langlaeufer als **Scheduled Task** (SYSTEM), nicht als WinRM-Kindprozess.
- Kein Reboot durchgefuehrt (Nachtplan-Regel).

View file

@ -0,0 +1,41 @@
# Weg B — Bridge-Adapter auf gpu-pc-CDP :9223 umbiegen (Nachtplan #5)
**Datum:** 2026-07-22 · **Status:** Code fertig + py_compile-sauber · **Nutzbarkeit:** [BÄR — PWA-Login gpu-pc]
## Kontext
Der dedizierte Bridge-Server `bridge.ki1.it` wurde am 2026-07-22 abgebaut. Einzige Quelle dauerhaft
eingeloggter PWA-Sessions = **gpu-pc (100.109.177.88)**, dort Chrome mit remote-debugging **:9223**
(Suno/Gemini/Qolaba live). "Weg B" = Adapter greifen diese Tabs per CDP ab, statt einen zweiten
Browser mit kopierten Cookies zu betreiben (bei Google-OAuth ohnehin fragil).
## Befund
- **suno.py macht Weg B bereits** — eigenes `get_page()` mit `connect_over_cdp` (host.docker.internal:9223
via SSH-Tunnel, Hostname->IP wegen Chrome-CDP-Host-Header-Check).
- **gemini.py / qolaba.py** nutzten bisher `base.get_page()` = `launch_persistent_context` (eigenes Chrome
+ injizierte Cookies). Diese sind auf CDP umgestellt.
- **mammouth.py** = Firefox + SOCKS5 (gpu-pc-Egress). Firefox hat kein Chromium-CDP -> `connect_over_cdp`
nicht anwendbar. Zusaetzlich Session aktuell ausgeloggt. **Bleibt Firefox-Sonderfall.**
## Umsetzung
Neues Modul `app/bridges/cdp.py` mit `CDPBridge(PWABridge)` — verallgemeinert exakt suno's erprobtes
Muster (gemeinsamer CDP-Browser-Handle mit Reconnect, Hostname->IP-Aufloesung, Tab-Wiederverwendung
nach Domain). Aenderungen an gemini.py/qolaba.py minimal:
- `class Gemini(CDPBridge)` + `match_host="gemini.google.com"`, `relogin_supported=True` entfernt
- `class Qolaba(CDPBridge)` + `match_host="qolaba.ai"`, `relogin_supported=True` entfernt
- Die gesamte Selector-/Ablauf-Logik (chat/image/audio/video/veo3) **unveraendert**.
Sicherheit: `CDPBridge.get_page()` oeffnet bei fehlendem Domain-Tab einen **eigenen neuen Tab**
(kein Hijack von `pages[0]` eines fremden Dienstes). `relogin_supported=False` (geteilter CDP-Context).
## Nutzbarkeit (ehrlich)
Rein technische Umbiegung. Die Session MUSS auf gpu-pc eingeloggt sein. Am 2026-07-22 ist
**Mammouth ausgeloggt** (rigoros geprueft: /login-Redirect, kein Auth-Cookie); Gemini-Google-OAuth
kann ebenfalls ablaufen. Dann meldet `login_status()` ehrlich `logged_in=False` und die Methoden
liefern eine Login-Wall. **-> einmaliger Human-Login auf gpu-pc noetig ([BÄR — PWA-Login gpu-pc]).**
Auto-Relogin aus dem Bridge-Prozess ist bei geteiltem CDP nicht moeglich.
## Deployment (offen, [BÄR]/Folgeschritt)
bridge.ki1.it ist weg — der Bridge-Prozess braucht einen neuen Host MIT SSH-Tunnel zu gpu-pc:9223
(oder Betrieb direkt auf gpu-pc gegen localhost:9223). Env: `PWA_CDP_URL=http://host.docker.internal:9223`.
Code: `bridges/cdp.py` (dieses Repo), Adapter-Backup `D:\claude\backups\bridges-adapter-code-2026-07-22.tgz`.