Auftragsbuch: 'feature/pydantic-response-models' angenommen (Ein-Klick-Gate)

This commit is contained in:
Auftragsbuch
2026-07-15 19:44:20 +02:00
5 changed files with 101 additions and 9 deletions
+16 -1
View File
@@ -3,6 +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 services import announce
@@ -10,6 +12,19 @@ from services import announce
router = APIRouter(prefix="/api")
@router.get("/chronik")
class ChronikItem(BaseModel):
id: int
ts: float
subject: str
text: str
source: str
priority: str
class ChronikResponse(BaseModel):
items: list[ChronikItem]
@router.get("/chronik", response_model=ChronikResponse)
def chronik(limit: int = 150) -> dict:
return {"items": announce.list_recent(limit)}
+34 -3
View File
@@ -1,4 +1,6 @@
"""Eigenleben-Endpoints — „Von allein"-Ansicht (Skills + Vorschlags-Bilanz), read-only."""
"""Eigenleben-Endpoints — „Von allein-Ansicht (Skills + Vorschlags-Bilanz), read-only."""
from pydantic import BaseModel
from fastapi import APIRouter, HTTPException
@@ -7,7 +9,36 @@ from services import eigenleben
router = APIRouter(prefix="/api")
@router.get("/eigenleben")
class SkillItem(BaseModel):
id: str
name: str
kategorie: str | None
aktiv: bool | None
beschreibung: str
version: str
autor: str
herkunft: str
herkunft_label: str
genutzt: int
zuletzt_genutzt: str | None
erstellt: str | None
status: str
geaendert: float
class EigenlebenOverviewResponse(BaseModel):
available: bool
skills: list[SkillItem]
selbst_erstellt: int
bilanz: dict
class SkillTextResponse(BaseModel):
id: str
text: str
@router.get("/eigenleben", response_model=EigenlebenOverviewResponse)
def get_overview() -> dict:
return eigenleben.overview()
@@ -17,7 +48,7 @@ def get_overview() -> dict:
# fällt in den SPA-Fallback (index.html, Status 200) und die UI zeigt „Konnte den
# Skill-Text nicht laden". Die Pfad-Validierung (max. eine Ebene, keine . /..) macht
# services.eigenleben.skill_text.
@router.get("/eigenleben/skill/{skill_id:path}")
@router.get("/eigenleben/skill/{skill_id:path}", response_model=SkillTextResponse)
def get_skill(skill_id: str) -> dict:
text = eigenleben.skill_text(skill_id)
if text is None:
+11 -1
View File
@@ -1,5 +1,7 @@
"""Health-/Status-Endpoint — schlanker Lebenszeichen-Check für MC 2.0."""
from pydantic import BaseModel
from fastapi import APIRouter
from config import VERSION
@@ -8,7 +10,15 @@ from services import gateway, llamaswap
router = APIRouter(prefix="/api")
@router.get("/health")
class HealthResponse(BaseModel):
status: str
version: str
engine_reachable: bool
gateway_reachable: bool
brain: dict
@router.get("/health", response_model=HealthResponse)
def health() -> dict:
return {
"status": "ok",
+16 -2
View File
@@ -2,9 +2,10 @@
Konsument ist v.a. der Hermes-Agent via mcp_mc.py (reminder_create/list/delete);
LAN-only wie alle MC2-Endpoints."""
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from fastapi import APIRouter, HTTPException
from services import reminders
router = APIRouter(prefix="/api")
@@ -16,7 +17,20 @@ class ReminderIn(BaseModel):
repeat: str = "" # '' einmalig | daily | weekdays | weekly
@router.get("/reminders")
class ReminderOut(BaseModel):
id: int
text: str
next_ts: float
repeat: str
created_ts: float
when_local: str
class ReminderListResponse(BaseModel):
items: list[ReminderOut]
@router.get("/reminders", response_model=ReminderListResponse)
def list_reminders() -> dict:
return {"items": reminders.list_all()}
+24 -2
View File
@@ -6,6 +6,8 @@ import os
import time
from pathlib import Path
from pydantic import BaseModel
from fastapi import APIRouter, HTTPException
router = APIRouter(prefix="/api")
@@ -30,7 +32,27 @@ def _title_of(p: Path) -> str:
return p.stem
@router.get("/wissen")
class VaultFile(BaseModel):
path: str
name: str
dir: str
title: str
mtime: float
neu: bool
class WissenListResponse(BaseModel):
available: bool
files: list[VaultFile]
class WissenFileResponse(BaseModel):
path: str
content: str
mtime: float
@router.get("/wissen", response_model=WissenListResponse)
def list_vault() -> dict:
"""Alle Markdown-Notizen des Vaults (relativer Pfad, Titel, Ordner, Alter)."""
if not _available():
@@ -54,7 +76,7 @@ def list_vault() -> dict:
return {"available": True, "files": files}
@router.get("/wissen/datei")
@router.get("/wissen/datei", response_model=WissenFileResponse)
def read_file(pfad: str) -> dict:
"""Inhalt einer Vault-Notiz — Traversal hart geblockt (resolve + is_relative_to)."""
if not _available():