- services/lucy_autonomie.py: Stufe je Aktionsart (nur lesen, erst fragen, selbst handeln), Start „mehr Freiheit“;
Meldung danach (POST /api/lucy/gehandelt)
- services/rueckfragen.py: nummerierte Rückfragen, nur Knöpfe der Oberfläche (ERLAUBT), „ja“ führt genau die
gespeicherte Aktion aus, 24 h gültig, hinfällig wenn der Hinweis weg ist, Nachprüfung danach
- notify.sh -k: Antworttasten (Antworttastatur über den Direktweg, ohne Eingriff in Hermes)
- services/ermittlung.py: Wächter meldet rote Hinweise an Lucy; Knopf wird Rückfrage, Lucy sieht über den
api_server nach und schickt Ursache und Weg als eine Nachricht
- services/wochenbericht.py + mc2-wochenbericht.timer (So 07:30): feste Sätze, Absatz vom Modell, offene
App-Updates als Rückfragen
- MCP: Werkzeuge fragen die Freiheiten (auf_bitte), neu: freiheiten, rueckfragen, rueckfrage_beantworten,
sicherheitsupdates_einspielen, snapshot_zurueckspielen, snapshots, geraet_logs, dienst_logs
- Ausführer-Aktion journal (nur lesend), POST /api/homelab/ziele/{id}/sicherheitsupdates
- Läufe des Homelabs mit flock (Oberfläche und Steward schreiben beide)
- Skill homelab-orchestrator, SOUL-Abschnitt als Kopie in deploy/nur-box
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
"""Lucys Freiheiten, Rückfragen, Untersuchungen und Wochenbericht (Ausbauplan Welle 3, seit 25.09.2026; nur KI-Box).
|
|
|
|
GET /api/lucy/autonomie Stufe je Aktionsart (nur lesen, erst fragen, selbst handeln)
|
|
POST /api/lucy/autonomie {"stufen": {art: stufe}}
|
|
POST /api/lucy/gehandelt Meldung danach: {"was", "grund", "ergebnis"} (Lucys Werkzeuge)
|
|
POST /api/lucy/befund roter Hinweis vom Wächter → Zeilen und Antworttasten für seine Nachricht
|
|
POST /api/lucy/ermitteln Lucy untersucht einen Hinweis {"bereich", "id", "titel", "text"}
|
|
GET /api/lucy/wochenbericht der letzte und wann der nächste kommt
|
|
POST /api/lucy/wochenbericht jetzt schreiben und schicken (Timer sonntags 07:30, Knopf)
|
|
GET /api/rueckfragen offene und zuletzt beantwortete
|
|
POST /api/rueckfragen stellen: {"frage", "pfad", "daten", "text", "von", "stufe", "befund", "still"}
|
|
POST /api/rueckfragen/antwort {"nr": 7 oder null, "ja": bool, "wer"} — ohne Nummer nur, wenn genau eine offen ist
|
|
POST /api/rueckfragen/{nr}/ja|nein die Knöpfe der Oberfläche
|
|
Was nicht geht, beantworten sie mit HTTP 409 und detail (wie die Update-Knöpfe).
|
|
"""
|
|
|
|
from fastapi import APIRouter, Body, HTTPException
|
|
from services import ermittlung, lucy_autonomie, rueckfragen, wochenbericht
|
|
|
|
router = APIRouter(prefix="/api", tags=["lucy"])
|
|
|
|
|
|
def _antwort(ergebnis: dict) -> dict:
|
|
if not ergebnis.get("ok"):
|
|
raise HTTPException(status_code=409, detail=ergebnis.get("detail") or "Das geht gerade nicht.")
|
|
return ergebnis
|
|
|
|
|
|
@router.get("/lucy/autonomie")
|
|
def autonomie() -> dict:
|
|
return lucy_autonomie.stand()
|
|
|
|
|
|
@router.post("/lucy/autonomie")
|
|
def autonomie_setzen(daten: dict = Body(...)) -> dict:
|
|
return _antwort(lucy_autonomie.setzen(daten))
|
|
|
|
|
|
@router.post("/lucy/gehandelt")
|
|
def gehandelt(daten: dict = Body(...)) -> dict:
|
|
return _antwort(lucy_autonomie.gehandelt(daten))
|
|
|
|
|
|
@router.post("/lucy/befund")
|
|
def befund(daten: dict = Body(...)) -> dict:
|
|
return _antwort(ermittlung.befund_gemeldet(daten))
|
|
|
|
|
|
@router.post("/lucy/ermitteln")
|
|
def ermitteln(daten: dict = Body(...)) -> dict:
|
|
return _antwort(ermittlung.starten(daten))
|
|
|
|
|
|
@router.get("/lucy/wochenbericht")
|
|
def wochenbericht_stand() -> dict:
|
|
return wochenbericht.stand()
|
|
|
|
|
|
@router.post("/lucy/wochenbericht")
|
|
def wochenbericht_senden() -> dict:
|
|
return _antwort(wochenbericht.senden())
|
|
|
|
|
|
@router.get("/rueckfragen")
|
|
def rueckfragen_liste(grenze: int = 20) -> dict:
|
|
return rueckfragen.liste(grenze)
|
|
|
|
|
|
@router.post("/rueckfragen")
|
|
def rueckfrage_stellen(daten: dict = Body(...)) -> dict:
|
|
return _antwort(rueckfragen.stellen(
|
|
str(daten.get("frage") or ""), str(daten.get("pfad") or ""),
|
|
daten.get("daten") if isinstance(daten.get("daten"), dict) else None,
|
|
text=str(daten.get("text") or ""), von=str(daten.get("von") or "lucy"),
|
|
stufe=str(daten.get("stufe") or "normal"), befund=daten.get("befund"), still=bool(daten.get("still"))))
|
|
|
|
|
|
@router.post("/rueckfragen/antwort")
|
|
def rueckfrage_antwort(daten: dict = Body(...)) -> dict:
|
|
nr = daten.get("nr")
|
|
if nr is not None and not isinstance(nr, int):
|
|
raise HTTPException(status_code=422, detail="nr muss eine Zahl sein.")
|
|
return _antwort(rueckfragen.beantworten(nr, bool(daten.get("ja")), wer=str(daten.get("wer") or "lucy")))
|
|
|
|
|
|
@router.post("/rueckfragen/{nr}/ja")
|
|
def rueckfrage_ja(nr: int) -> dict:
|
|
return _antwort(rueckfragen.beantworten(nr, True, wer="oberflaeche"))
|
|
|
|
|
|
@router.post("/rueckfragen/{nr}/nein")
|
|
def rueckfrage_nein(nr: int) -> dict:
|
|
return _antwort(rueckfragen.beantworten(nr, False, wer="oberflaeche"))
|