Ruff-Cleanup: ganzes MC2-Repo lint-grün + projekt-passende ruff.toml
Die Ampel-Nachruestung deckte 317/337 vorbestehende ruff-Verstoesse im ganzen Repo auf. Aufgeraeumt: - ruff.toml: intentionale Muster als Projekt-Politik ausgenommen (BLE001 blind-except, S110/S112 try-except-pass/continue, PLW1510 subprocess-best-effort, B008 FastAPI- Depends/File-Idiom, EXE001 Shebang, + wenige Stil-Regeln). __init__.py-Re-Exports geschuetzt (F401). - ruff --fix: 128 mechanische (Import-Sortierung, PEP585/604-Annotationen, tote Imports, ueberfluessige noqa) auto-behoben. - 12 echte Reste von Hand: PERF402/102, PLC3002 (Lambda->walrus), ISC004 (String-Concat geklammert), F841/RUF059 (ungenutzte Vars), PIE810 (startswith-Tuple), UP031 (f-string), UP035 (veraltete typing-Imports). Ergebnis: 'ruff check .' = 0, 'compileall' grün. Kein Verhaltenswechsel (nur Stil/Modernisierung). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+5
-5
@@ -9,17 +9,16 @@ Server (proxyt /api hierher), daher CORS für localhost offen.
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
from collections.abc import AsyncGenerator, Awaitable, Callable
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any, AsyncGenerator, Awaitable, Callable
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from config import FRONTEND_DIST, V1_UPSTREAM, VERSION
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.requests import Request
|
||||
|
||||
from config import FRONTEND_DIST, V1_UPSTREAM, VERSION
|
||||
from routers import (
|
||||
agent,
|
||||
auftragsbuch,
|
||||
@@ -42,8 +41,9 @@ from routers import (
|
||||
zeitmaschine,
|
||||
)
|
||||
from routers import reminders as reminders_router
|
||||
from services import memory as memory_svc
|
||||
from services import ketten_digest, metrics_history, reminders, sentry, warmer
|
||||
from services import memory as memory_svc
|
||||
from starlette.requests import Request
|
||||
|
||||
# Zentrales Logging — Level via MC_LOG_LEVEL (INFO default). Eine Konfiguration
|
||||
# für alle Module (logging.getLogger(__name__)).
|
||||
|
||||
@@ -15,13 +15,12 @@ durchreicht (routers/gateway_forward.py, MC_V1_UPSTREAM).
|
||||
|
||||
import logging
|
||||
import os
|
||||
from collections.abc import AsyncGenerator
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncGenerator
|
||||
|
||||
import httpx
|
||||
from fastapi import FastAPI, Request
|
||||
|
||||
from config import LLAMA_SWAP_URL, VERSION
|
||||
from fastapi import FastAPI, Request
|
||||
from routers import gateway_proxy
|
||||
|
||||
logging.basicConfig(
|
||||
|
||||
@@ -4,8 +4,9 @@ from pathlib import Path
|
||||
# Add backend directory to sys.path so we can import services
|
||||
sys.path.append(str(Path(__file__).resolve().parent))
|
||||
|
||||
from services.llamaswap import read_config, write_config, spec_draft_flags, _PATH_RE
|
||||
from config import CONFIG_PATH
|
||||
from services.llamaswap import _PATH_RE, read_config, spec_draft_flags, write_config
|
||||
|
||||
|
||||
def migrate():
|
||||
print(f"Reading config from {CONFIG_PATH}...")
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
"""Agent-Endpoint: Hermes-Status + WebUI-Link (MC verlinkt nur, betreibt nicht)."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
from services.agent import agent_status, hermes_brain_info, set_agent_brain, update_brain_model
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -3,7 +3,6 @@ LAN-only wie alle MC2-Endpoints; das Gate ist der Klick des Commanders."""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from services import auftragsbuch
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -3,10 +3,8 @@ Quelle ist der persistente Melde-Briefkasten (services/announce.py): Health-Wäc
|
||||
Auto-Updates, Erinnerungen, Radar/Traum/Chef-Gutachter-Crons, Auftragsbuch — alle
|
||||
autonomen Kanäle laufen dort bereits durch. Hier wird nichts Neues erhoben, nur erzählt."""
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from pydantic import BaseModel
|
||||
from services import announce
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Connect-Endpoint: erzeugt IDE-/Agent-Snippets (auf den Gateway + Memory-MCP)."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from services.connect import DEFAULT_HOST, build_snippets, check_health
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -14,11 +14,10 @@ import logging
|
||||
|
||||
import httpx
|
||||
import websockets
|
||||
from config import BOX_CONSOLE_UPSTREAM
|
||||
from fastapi import APIRouter, Request, WebSocket
|
||||
from starlette.responses import Response
|
||||
|
||||
from config import BOX_CONSOLE_UPSTREAM
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
router = APIRouter()
|
||||
|
||||
@@ -28,7 +27,7 @@ def _register(base: str, upstream: str) -> None:
|
||||
ws_upstream = upstream.replace("http://", "ws://").replace("https://", "wss://")
|
||||
|
||||
@router.websocket(f"/{base}/ws")
|
||||
async def _proxy_ws(ws: WebSocket) -> None: # noqa: ANN001 — Closure je base
|
||||
async def _proxy_ws(ws: WebSocket) -> None:
|
||||
await ws.accept(subprotocol="tty")
|
||||
try:
|
||||
async with websockets.connect(
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
"""Eigenleben-Endpoints — „Von allein“-Ansicht (Skills + Vorschlags-Bilanz), read-only."""
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from pydantic import BaseModel
|
||||
from services import eigenleben
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -27,11 +27,10 @@ import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from config import MODELS_DIR
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
from config import MODELS_DIR
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -11,11 +11,10 @@ entfernen → app.py bindet wieder den lokalen Gateway ein.
|
||||
|
||||
import logging
|
||||
|
||||
from config import V1_UPSTREAM
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import JSONResponse, StreamingResponse
|
||||
|
||||
from config import V1_UPSTREAM
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/v1")
|
||||
|
||||
@@ -2,11 +2,9 @@ import logging
|
||||
import os
|
||||
|
||||
import httpx
|
||||
|
||||
from config import LLAMA_SWAP_URL
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import JSONResponse, StreamingResponse
|
||||
|
||||
from config import LLAMA_SWAP_URL
|
||||
from services.gateway_stream import record_stream_chunk, record_usage, warn_truncation
|
||||
from services.router_logic import IMAGE_PART_TYPES, VISION_CAPABLE, choose_for_lane, has_image
|
||||
from services.routing_policy import load_policy
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
"""Health-/Status-Endpoint — schlanker Lebenszeichen-Check für MC 2.0."""
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from config import VERSION
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from services import gateway, llamaswap
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -22,11 +22,10 @@ import re
|
||||
|
||||
import httpx
|
||||
import websockets
|
||||
from config import HERMES_BUILTIN_UI_UPSTREAM
|
||||
from fastapi import APIRouter, Request, WebSocket
|
||||
from starlette.responses import RedirectResponse, Response
|
||||
|
||||
from config import HERMES_BUILTIN_UI_UPSTREAM
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ LAN-only wie alle MC2-Endpoints; das Mensch-Gate bleibt die Karte im Auftragsbuc
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from services import ideen
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
"""Wartungs-Endpoints: Update-Badge, OS-/Engine-Update, Reboot, Restart, Logs."""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Header
|
||||
from fastapi import APIRouter, Header, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from services import maintenance
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -4,7 +4,6 @@ import time
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from services import memory
|
||||
from services.voice_metrics import park, record_stage
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
"""Modelle-Endpoints: Liste (mit Caps), Discover, Fit, Register, Groups."""
|
||||
|
||||
import psutil
|
||||
from config import HF_DOWNLOAD_ENV, MODELS_DIR
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from config import HF_DOWNLOAD_ENV, MODELS_DIR
|
||||
from services import budget, discover, hf, jobengine, llamaswap
|
||||
from services.fit import evaluate_fit, max_ctx_for
|
||||
|
||||
@@ -140,8 +139,7 @@ def install(req: InstallReq) -> dict:
|
||||
|
||||
# Download-Job: alle GGUF-Teile (+ mmproj) per --include holen.
|
||||
args = [hf.hf_bin(), "download", repo]
|
||||
for f in info["files"]:
|
||||
args.append(f)
|
||||
args.extend(info["files"])
|
||||
if info["mmproj"]:
|
||||
args.append(info["mmproj"])
|
||||
args += ["--local-dir", str(target)]
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
Konsument ist v.a. der Hermes-Agent via mcp_mc.py (reminder_create/list/delete);
|
||||
LAN-only wie alle MC2-Endpoints."""
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from pydantic import BaseModel
|
||||
from services import reminders
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from services import gateway
|
||||
from services.routing_policy import policy_meta, save_policy
|
||||
|
||||
|
||||
@@ -9,12 +9,10 @@ import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import httpx
|
||||
from config import GATEWAY_URL, HERMES_API_URL, LLAMA_SWAP_URL, MEM0_SERVICE_URL, V1_UPSTREAM, VOICE_SERVICE_URL
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
|
||||
import httpx
|
||||
|
||||
from config import GATEWAY_URL, HERMES_API_URL, LLAMA_SWAP_URL, MEM0_SERVICE_URL, V1_UPSTREAM, VOICE_SERVICE_URL
|
||||
from services import backup as backup_svc
|
||||
from services import maintenance
|
||||
from services.agent import agent_status
|
||||
@@ -67,7 +65,7 @@ def _user_unit_active(unit: str) -> bool:
|
||||
r = subprocess.run(["systemctl", "--user", "is-active", unit],
|
||||
capture_output=True, text=True, timeout=3)
|
||||
return r.stdout.strip() == "active"
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
@@ -135,7 +133,7 @@ def _run(cmd: list[str], cwd: str | None = None) -> dict:
|
||||
p = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, timeout=180)
|
||||
return {"ok": p.returncode == 0, "code": p.returncode,
|
||||
"out": (p.stdout or "")[-2000:], "err": (p.stderr or "")[-2000:]}
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
return {"ok": False, "code": -1, "out": "", "err": str(exc)}
|
||||
|
||||
|
||||
|
||||
@@ -11,14 +11,16 @@ LAN-only (kein Token in der 2.0-Phase), wie die übrigen MC2-Endpoints.
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
# Injection-Schutz (Stufe 0): guard.py liegt im mcp/-Verzeichnis. Per Pfad laden (eigene MC2-Venv).
|
||||
import sys as _sys
|
||||
import time
|
||||
|
||||
import httpx
|
||||
from config import HERMES_API_KEY, HERMES_API_MODEL, HERMES_API_URL, LLAMA_SWAP_URL, VOICE_SERVICE_URL
|
||||
from fastapi import APIRouter, File, Form, HTTPException, UploadFile
|
||||
from fastapi.responses import Response, StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
from config import HERMES_API_KEY, HERMES_API_MODEL, HERMES_API_URL, LLAMA_SWAP_URL, VOICE_SERVICE_URL
|
||||
from services import announce
|
||||
from services.voice_metrics import ( # Per-Stage-Latenz + Per-Turn-Trace (intern)
|
||||
Timer,
|
||||
@@ -29,8 +31,6 @@ from services.voice_metrics import ( # Per-Stage-Latenz + Per-Turn-Trace (inter
|
||||
record_stage,
|
||||
)
|
||||
|
||||
# Injection-Schutz (Stufe 0): guard.py liegt im mcp/-Verzeichnis. Per Pfad laden (eigene MC2-Venv).
|
||||
import sys as _sys
|
||||
_GUARD_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "mcp")
|
||||
if _GUARD_DIR not in _sys.path:
|
||||
_sys.path.insert(0, _GUARD_DIR)
|
||||
@@ -155,7 +155,7 @@ def voice_health() -> dict:
|
||||
r = httpx.get(f"{VOICE_SERVICE_URL}/health", timeout=httpx.Timeout(5.0))
|
||||
out["sidecar"] = r.status_code == 200
|
||||
out["detail"] = r.json() if r.status_code == 200 else None
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
out["error"] = str(exc)
|
||||
return out
|
||||
|
||||
@@ -180,7 +180,7 @@ def voice_voices() -> dict:
|
||||
r = httpx.get(f"{VOICE_SERVICE_URL}/voices", timeout=httpx.Timeout(10.0))
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
raise HTTPException(502, f"Voice-Sidecar nicht erreichbar: {exc}")
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ def voice_get_reference() -> dict:
|
||||
r = httpx.get(f"{VOICE_SERVICE_URL}/reference", timeout=httpx.Timeout(8.0))
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
return {"active": False, "error": str(exc)}
|
||||
|
||||
|
||||
|
||||
@@ -6,9 +6,8 @@ import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from services import backup as backup_svc
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -10,12 +10,10 @@ Permission errors are handled gracefully.
|
||||
Uses user journal (systemctl --user) to query logs.
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
LOG_DIR = Path.home() / "logs"
|
||||
LOG_FILE = LOG_DIR / "mc2-timeout.log"
|
||||
SERVICE_NAME = "mission-control-2.service"
|
||||
|
||||
@@ -9,11 +9,15 @@ import os
|
||||
import re
|
||||
|
||||
import httpx
|
||||
import psutil
|
||||
|
||||
from config import (BOX_CONSOLE_UPSTREAM,
|
||||
BOX_CONSOLE_PATH, HERMES_BUILTIN_UI_UPSTREAM, HERMES_BUILTIN_UI_PATH,
|
||||
HERMES_API_URL, HERMES_HOME, PC_EXECUTOR_URL)
|
||||
from config import (
|
||||
BOX_CONSOLE_PATH,
|
||||
BOX_CONSOLE_UPSTREAM,
|
||||
HERMES_API_URL,
|
||||
HERMES_BUILTIN_UI_PATH,
|
||||
HERMES_BUILTIN_UI_UPSTREAM,
|
||||
HERMES_HOME,
|
||||
PC_EXECUTOR_URL,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -194,7 +198,7 @@ def set_agent_brain(model_id: str) -> dict:
|
||||
new_members = [x for x in brains if x not in (old, model_id)] + [model_id]
|
||||
llamaswap.set_group("brains", new_members, swap=False, persist=True) # 2) warm
|
||||
# 2b) TTL härten: neues Hirn nie auto-entladen; altes Hirn auf Default entspannen.
|
||||
from services.llamaswap import set_ttl, DEFAULT_TTL
|
||||
from services.llamaswap import DEFAULT_TTL, set_ttl
|
||||
set_ttl(model_id, 0)
|
||||
if old:
|
||||
set_ttl(old, DEFAULT_TTL)
|
||||
@@ -253,7 +257,7 @@ def update_brain_model(new_model: str) -> bool:
|
||||
|
||||
# Restart the user-space service to apply changes
|
||||
try:
|
||||
import services.maintenance as maintenance
|
||||
from services import maintenance
|
||||
maintenance.restart_service("hermes-gateway")
|
||||
except Exception:
|
||||
log.warning("update_brain_model: hermes-gateway-Restart fehlgeschlagen", exc_info=True)
|
||||
|
||||
@@ -20,7 +20,6 @@ import time
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
|
||||
from config import MODELS_DIR
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -51,7 +51,7 @@ def backup_now() -> dict:
|
||||
r = subprocess.run(["/bin/bash", str(BACKUP_SH)], capture_output=True, text=True, timeout=180)
|
||||
if r.returncode != 0:
|
||||
return {"ok": False, "snapshot": "", "files": [], "error": (r.stderr or r.stdout).strip()[-300:]}
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
return {"ok": False, "snapshot": "", "files": [], "error": str(exc)}
|
||||
|
||||
latest = _latest()
|
||||
|
||||
@@ -10,7 +10,6 @@ die häufigste Fehlerquelle. Der Aufrufer übergibt den Host explizit.
|
||||
import json
|
||||
|
||||
import httpx
|
||||
|
||||
from config import HERMES_BUILTIN_UI_UPSTREAM, LLAMA_SWAP_URL, MEM0_SERVICE_URL, PORT, V1_UPSTREAM
|
||||
|
||||
DEFAULT_HOST = "192.168.178.151"
|
||||
@@ -139,7 +138,7 @@ def check_health() -> dict:
|
||||
gateway = {"ok": True, "detail": f"{n} Modelle verfügbar" if n else "bereit"}
|
||||
else:
|
||||
gateway = {"ok": False, "detail": f"HTTP {r.status_code}"}
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
memory = {"ok": False, "detail": "nicht erreichbar"}
|
||||
@@ -148,7 +147,7 @@ def check_health() -> dict:
|
||||
r = c.get(f"{MEM0_SERVICE_URL}/health")
|
||||
memory = ({"ok": True, "detail": "bereit"} if r.status_code == 200
|
||||
else {"ok": False, "detail": f"HTTP {r.status_code}"})
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
desktop_gateway = {"ok": False, "detail": "nicht erreichbar"}
|
||||
@@ -157,7 +156,7 @@ def check_health() -> dict:
|
||||
r = c.get(f"{HERMES_BUILTIN_UI_UPSTREAM}/api/status")
|
||||
desktop_gateway = ({"ok": True, "detail": "bereit"} if r.status_code == 200
|
||||
else {"ok": False, "detail": f"HTTP {r.status_code}"})
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {"gateway": gateway, "memory": memory, "desktop_gateway": desktop_gateway}
|
||||
|
||||
@@ -9,16 +9,15 @@ spätere Auto-Setups nutzen ihn, damit sie nie auseinanderlaufen.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
import httpx
|
||||
|
||||
import logging
|
||||
|
||||
from config import DISCOVER_CACHE_PATH, DISCOVER_TTL
|
||||
|
||||
from services import catalog
|
||||
from services.caps import capabilities
|
||||
from services.fit import evaluate_fit, extract_params_b, max_ctx_for
|
||||
|
||||
@@ -5,8 +5,8 @@ V1_UPSTREAM gilt der alte eingebaute Modus (MC2 serviert /v1 selbst).
|
||||
"""
|
||||
|
||||
import httpx
|
||||
|
||||
from config import PORT, V1_UPSTREAM
|
||||
|
||||
from services.llamaswap import engine_reachable
|
||||
from services.routing_policy import load_policy
|
||||
|
||||
@@ -50,7 +50,7 @@ def gateway_reachable() -> bool:
|
||||
if V1_UPSTREAM:
|
||||
try:
|
||||
return httpx.get(f"{V1_UPSTREAM}/v1/models", timeout=2.0).status_code == 200
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
return False
|
||||
# Eingebauter Modus: der Gateway lebt in MC selbst und proxyt llama-swap.
|
||||
return engine_reachable()
|
||||
|
||||
@@ -109,7 +109,7 @@ def _kurz(text: str, max_len: int = 80) -> str:
|
||||
return (schnitt or t[:max_len]) + "…"
|
||||
|
||||
|
||||
_TITEL_PRAEFIX_RX = re.compile(r"^\s*(idee|projekt|bitte)\s*[:\-–]?\s*", re.I)
|
||||
_TITEL_PRAEFIX_RX = re.compile(r"^\s*(idee|projekt|bitte)\s*[:\-–]?\s*", re.IGNORECASE)
|
||||
|
||||
|
||||
def _projekt_titel(titles: list[str]) -> str:
|
||||
@@ -220,7 +220,7 @@ def _ketten_anreichern(items: list[dict]) -> list[dict]:
|
||||
for it in items:
|
||||
familien.setdefault(boss(it["id"]), []).append(it)
|
||||
projekte = []
|
||||
for wurzel, mitglieder in familien.items():
|
||||
for mitglieder in familien.values():
|
||||
if len(mitglieder) < 2:
|
||||
continue
|
||||
mitglieder.sort(key=lambda i: i.get("erstellt") or 0)
|
||||
@@ -752,8 +752,8 @@ def _lokales_konzept(text: str, titel: str = "") -> tuple | None:
|
||||
if not kandidaten and titel and _KONZEPT_DIR.is_dir():
|
||||
worte = {w for w in re.findall(r"[a-z0-9]{3,}", titel.lower())}
|
||||
treffer = [p.name for p in _KONZEPT_DIR.glob("*.md")
|
||||
if (lambda g: len(g) >= 2 or any(len(w) >= 6 for w in g))(
|
||||
worte & set(re.findall(r"[a-z0-9]{3,}", p.stem.lower())))]
|
||||
if len(g := worte & set(re.findall(r"[a-z0-9]{3,}", p.stem.lower()))) >= 2
|
||||
or any(len(w) >= 6 for w in g)]
|
||||
if len(treffer) == 1:
|
||||
kandidaten.append(treffer[0])
|
||||
for name in kandidaten:
|
||||
@@ -935,7 +935,7 @@ def _konzept_und_name(task_id: str) -> tuple:
|
||||
quell_titel = _TITEL_PRAEFIX_RX.sub("", str(t.get("title") or "").strip())
|
||||
if not quell_titel:
|
||||
return {}, "", "Quell-Karte hat keinen Titel."
|
||||
h1 = re.search(r"^#\s+(.+)$", konz["konzept"], re.M)
|
||||
h1 = re.search(r"^#\s+(.+)$", konz["konzept"], re.MULTILINE)
|
||||
kurz = h1.group(1).strip() if h1 else re.split(r"(?<=[.!?])\s", quell_titel)[0]
|
||||
return konz, _kurz(kurz, 85), ""
|
||||
|
||||
@@ -1052,9 +1052,9 @@ def konzept_ueberarbeiten(task_id: str, hinweis: str) -> dict:
|
||||
"als `KONZEPT.md` hinein (klonen, schreiben, pushen, Push beweisen)."
|
||||
if konz.get("repo") else ""))
|
||||
|
||||
teile = [f"{_UEBERARBEITEN_KOPF}\n{quelle}\n"
|
||||
f"· SO SOLL ES ANDERS WERDEN — Wortlaut des Commanders:\n „{hinweis}“\n"
|
||||
f"{_UEBERARBEITEN_FUSS}",
|
||||
teile = [(f"{_UEBERARBEITEN_KOPF}\n{quelle}\n"
|
||||
f"· SO SOLL ES ANDERS WERDEN — Wortlaut des Commanders:\n „{hinweis}“\n"
|
||||
f"{_UEBERARBEITEN_FUSS}"),
|
||||
_INFRA_LXC]
|
||||
titel = f"Konzept nachschärfen: {kurz}"[:200]
|
||||
args = ["create", titel, "--body", "\n\n".join(teile)[:4000], "--assignee", "projektstart",
|
||||
@@ -1098,7 +1098,7 @@ def log_of(task_id: str) -> dict:
|
||||
|
||||
try:
|
||||
r = _hermes(["log", task_id])
|
||||
except Exception as exc:
|
||||
except Exception:
|
||||
log.warning("ideen: kanban log fehlgeschlagen", exc_info=True)
|
||||
return {"available": True, "lines": []}
|
||||
if r.returncode != 0:
|
||||
|
||||
@@ -90,7 +90,7 @@ def _run_job(job_id: str, args: list[str], env: dict | None = None, sudo_passwor
|
||||
log_str = "\n".join(job["log"])
|
||||
if "a password is required" in log_str or "password" in log_str.lower() or "sudo:" in log_str:
|
||||
job["sudo_failed"] = True
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
_append_log(job, f"[mc] Fehler: {exc}")
|
||||
job["state"] = "failed"
|
||||
job["returncode"] = -1
|
||||
@@ -101,7 +101,7 @@ def _run_job(job_id: str, args: list[str], env: dict | None = None, sudo_passwor
|
||||
if cb and job["state"] == "done":
|
||||
try:
|
||||
cb()
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
_append_log(job, f"[mc] Nachbearbeitung-Fehler: {exc}")
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ def attach_download_progress(job_id: str, local_dir: str, total_bytes: int) -> N
|
||||
j["rate_bps"] = rate
|
||||
j["eta_s"] = int((total_bytes - cur) / rate)
|
||||
prev_t, prev_b = now, cur
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(1.0)
|
||||
j = JOBS.get(job_id)
|
||||
@@ -182,7 +182,7 @@ def cancel_job(job_id: str) -> bool:
|
||||
if proc is not None:
|
||||
try:
|
||||
proc.terminate()
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
job["state"] = "canceled"
|
||||
|
||||
@@ -87,7 +87,6 @@ def _tick() -> None:
|
||||
return
|
||||
items = data.get("items") or []
|
||||
projekte = data.get("projekte") or []
|
||||
by_id = {i["id"]: i for i in items if i.get("id")}
|
||||
state = _load_state()
|
||||
gemeldete_fragen: dict = state.setdefault("fragen", {})
|
||||
projekt_state: dict = state.setdefault("projekte", {})
|
||||
|
||||
@@ -11,12 +11,17 @@ import os
|
||||
import re
|
||||
|
||||
import httpx
|
||||
from ruamel.yaml.scalarstring import LiteralScalarString
|
||||
|
||||
from config import (
|
||||
CMD_TEMPLATE, CONFIG_PATH, DEFAULT_TTL, DRAFTS_DIR, LLAMA_SWAP_URL,
|
||||
SPEC_DRAFT_MODEL_PATH, SPEC_DRAFT_N_MAX, SPEC_TYPE,
|
||||
CMD_TEMPLATE,
|
||||
CONFIG_PATH,
|
||||
DEFAULT_TTL,
|
||||
DRAFTS_DIR,
|
||||
LLAMA_SWAP_URL,
|
||||
SPEC_DRAFT_MODEL_PATH,
|
||||
SPEC_DRAFT_N_MAX,
|
||||
SPEC_TYPE,
|
||||
)
|
||||
from ruamel.yaml.scalarstring import LiteralScalarString
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -143,13 +148,13 @@ def model_id_from_path(model_path: str) -> str:
|
||||
Split-GGUFs liegen oft in einem Quant-Unterordner (…/Q4_K_M/file-00001-of-…) →
|
||||
dann eine Ebene höher (Repo-Ordner) nehmen, sonst hieße das Modell 'Q4_K_M'."""
|
||||
d = os.path.basename(os.path.dirname(model_path))
|
||||
if re.fullmatch(r"(I?Q\d[\w]*|UD-Q\d[\w]*|F16|BF16|FP16|F32)", d, flags=re.I):
|
||||
if re.fullmatch(r"(I?Q\d[\w]*|UD-Q\d[\w]*|F16|BF16|FP16|F32)", d, flags=re.IGNORECASE):
|
||||
d = os.path.basename(os.path.dirname(os.path.dirname(model_path)))
|
||||
name = re.sub(r"[-_]?GGUF$", "", d, flags=re.I).strip("-_")
|
||||
name = re.sub(r"[-_]?GGUF$", "", d, flags=re.IGNORECASE).strip("-_")
|
||||
if not name:
|
||||
fn = re.sub(r"\.gguf$", "", os.path.basename(model_path), flags=re.I)
|
||||
fn = re.sub(r"\.gguf$", "", os.path.basename(model_path), flags=re.IGNORECASE)
|
||||
fn = re.sub(r"-\d+-of-\d+$", "", fn)
|
||||
name = re.sub(r"[-_](Q\d[\w]*|IQ\d[\w]*|F16|BF16|FP16|F32)$", "", fn, flags=re.I)
|
||||
name = re.sub(r"[-_](Q\d[\w]*|IQ\d[\w]*|F16|BF16|FP16|F32)$", "", fn, flags=re.IGNORECASE)
|
||||
return name or "modell"
|
||||
|
||||
|
||||
@@ -222,7 +227,7 @@ def write_config(cfg: dict) -> None:
|
||||
try:
|
||||
from services import warmer
|
||||
warmer.nudge()
|
||||
except Exception: # noqa: BLE001 — Vorwärmen ist Komfort, nie ein Schreib-Blocker
|
||||
except Exception:
|
||||
pass
|
||||
except PermissionError as exc:
|
||||
raise PermissionError(
|
||||
|
||||
@@ -35,7 +35,7 @@ _engine_cache = {"ts": 0.0, "avail": False}
|
||||
# manchmal noch keine CI-Assets (0 Assets) → ihr Download-Link 404t. Sowohl der Update-Check
|
||||
# als auch der Download (update-engine.sh) müssen daher die neueste ASSET-tragende Release
|
||||
# nehmen, sonst zeigt das UI „Update verfügbar", das dann beim Einspielen scheitert.
|
||||
_ENGINE_ASSET_RX = re.compile(r"ubuntu-vulkan-x64\.tar\.gz$", re.I)
|
||||
_ENGINE_ASSET_RX = re.compile(r"ubuntu-vulkan-x64\.tar\.gz$", re.IGNORECASE)
|
||||
|
||||
|
||||
def _latest_engine_asset_release() -> dict | None:
|
||||
@@ -289,7 +289,7 @@ def model_upgrades() -> list[dict]:
|
||||
continue
|
||||
|
||||
base = rec.split("/")[-1].lower()
|
||||
stem = base[:-5] if base.endswith("-gguf") else base
|
||||
stem = base.removesuffix("-gguf")
|
||||
if base in cmds or (stem and stem in cmds):
|
||||
continue # schon installiert
|
||||
out.append({"role": role, "title": c["title"], "repo": rec})
|
||||
@@ -343,7 +343,7 @@ def _os_held_back() -> list[dict]:
|
||||
held.append({"name": name, "reason": reason})
|
||||
elif reason: # nicht eingerückt → Abschnitt zu Ende
|
||||
reason = None
|
||||
except Exception: # noqa: BLE001 — nur Zusatzinfo, nie ein Blocker
|
||||
except Exception:
|
||||
pass
|
||||
held.sort(key=lambda p: p["name"])
|
||||
return held
|
||||
@@ -368,7 +368,7 @@ def os_update_details() -> dict:
|
||||
out_pkgs.sort(key=lambda p: p["name"])
|
||||
return {"kind": "os", "count": len(out_pkgs), "packages": out_pkgs,
|
||||
"held_back": _os_held_back()}
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
return {"kind": "os", "count": len(out_pkgs), "packages": out_pkgs, "error": str(exc)}
|
||||
|
||||
|
||||
@@ -393,7 +393,7 @@ def engine_update_details() -> dict:
|
||||
ctx = ("Es geht um ein Update der Inferenz-Engine llama.cpp (Vulkan-Build, treibt alle "
|
||||
"Sprachmodelle der Box auf der AMD-Strix-Halo-GPU).")
|
||||
info.update(_summarize_release("engine", tag, ctx, body[:6000]))
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
info["error"] = str(exc)
|
||||
return info
|
||||
|
||||
@@ -418,7 +418,7 @@ def swap_update_details() -> dict:
|
||||
ctx = ("Es geht um ein Update von llama-swap (der Router, der Anfragen an die Box "
|
||||
"verteilt und Sprachmodelle heiß nachlädt).")
|
||||
info.update(_summarize_release("swap", tag, ctx, body[:6000]))
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
info["error"] = str(exc)
|
||||
return info
|
||||
|
||||
@@ -496,7 +496,7 @@ def _summarize_release(kind: str, key: str, context: str, changes: str) -> dict:
|
||||
if text:
|
||||
cache.update(key=key, data=data)
|
||||
return data
|
||||
except Exception as exc: # noqa: BLE001 — Zusammenfassung ist Komfort, nie Blocker
|
||||
except Exception as exc:
|
||||
return {"summary": f"(Zusammenfassung nicht verfügbar: {exc})",
|
||||
"action_needed": None, "action_text": ""}
|
||||
|
||||
@@ -534,7 +534,7 @@ def hermes_update_details() -> dict:
|
||||
info["behind"] = len(commits)
|
||||
if commits:
|
||||
info.update(_summarize_hermes_commits(commits))
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
info["error"] = str(exc)
|
||||
return info
|
||||
|
||||
@@ -575,7 +575,7 @@ def _run(cmd: list[str], sudo_password: str | None = None) -> dict:
|
||||
return {"ok": False, "status": "password_required", "out": p.stdout or "", "err": "Sudo-Passwort erforderlich."}
|
||||
|
||||
return {"ok": p.returncode == 0, "out": (p.stdout or "")[-4000:], "err": (p.stderr or "")[-2000:]}
|
||||
except Exception as exc: # noqa: BLE001
|
||||
except Exception as exc:
|
||||
return {"ok": False, "out": "", "err": str(exc)}
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import re
|
||||
from difflib import SequenceMatcher
|
||||
|
||||
import httpx
|
||||
|
||||
from config import MEM0_SERVICE_URL
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -51,7 +51,7 @@ def _load() -> None:
|
||||
log.info("metrics_history: %d Punkte aus %s geladen", len(_points), HISTORY_PATH)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except Exception: # noqa: BLE001 — kaputte Datei = leer starten, nie crashen
|
||||
except Exception:
|
||||
log.warning("metrics_history: %s nicht lesbar — starte leer", HISTORY_PATH, exc_info=True)
|
||||
|
||||
|
||||
@@ -77,19 +77,19 @@ def _sample() -> None:
|
||||
try:
|
||||
du = psutil.disk_usage(str(MODELS_DIR) if MODELS_DIR.exists() else os.getcwd())
|
||||
disk = du.percent
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
pass
|
||||
gpu = None
|
||||
try:
|
||||
g = _gpu_sysfs()
|
||||
gpu = g.get("busy_percent") if g else None
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
pass
|
||||
tp = tc = None
|
||||
try:
|
||||
ts = get_stats()
|
||||
tp, tc = ts.get("prompt_tokens"), ts.get("completion_tokens")
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
pass
|
||||
# interval=None: nicht-blockierende CPU-Messung seit dem letzten Aufruf (10 s her — ideal).
|
||||
_points.append([int(time.time()), psutil.cpu_percent(interval=None), vm.percent, gpu, disk, tp, tc])
|
||||
@@ -101,12 +101,12 @@ async def sampler_loop() -> None:
|
||||
while True:
|
||||
try:
|
||||
await asyncio.to_thread(_sample)
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
log.debug("metrics_history: Sample fehlgeschlagen", exc_info=True)
|
||||
if time.time() - _last_flush >= FLUSH_S:
|
||||
try:
|
||||
await asyncio.to_thread(_flush)
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception:
|
||||
pass
|
||||
await asyncio.sleep(SAMPLE_S)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from pathlib import Path
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from config import MODELS_DIR
|
||||
|
||||
from services import announce
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -20,8 +20,8 @@ import time
|
||||
|
||||
import httpx
|
||||
import psutil
|
||||
from config import HERMES_API_URL, MEM0_SERVICE_URL, MODELS_DIR, VOICE_SERVICE_URL
|
||||
|
||||
from config import HERMES_API_URL, LLAMA_SWAP_URL, MEM0_SERVICE_URL, MODELS_DIR, VOICE_SERVICE_URL
|
||||
from services import announce, llamaswap
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -100,7 +100,7 @@ if os.environ.get("MC_SENTRY_WATCH_MC2", "") == "1":
|
||||
|
||||
|
||||
class _Watch:
|
||||
__slots__ = ("fails", "alerted", "alert_ts")
|
||||
__slots__ = ("alert_ts", "alerted", "fails")
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.fails = 0 # Fehl-Ticks in Folge
|
||||
|
||||
@@ -12,7 +12,6 @@ import subprocess
|
||||
import threading
|
||||
|
||||
import psutil
|
||||
|
||||
from config import MODELS_DIR
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import json
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from config import HERMES_HOME
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import os
|
||||
import subprocess
|
||||
|
||||
import httpx
|
||||
|
||||
from config import LLAMA_SWAP_URL
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
Reference in New Issue
Block a user