feat: Profile-System für unterschiedliche Use-Cases (Sitzungsprotokoll, später Arzt-Diktat etc.)
Mac-Worker: - profiles/meeting.yaml — extrahiert die bisherigen Prompts + DOCX-Layout - core/profiles.py — YAML-Loader mit Cache und Fallback - ollama_client.summarize/make_protocol nehmen profile_name - docx_export: generischer Renderer aus profile.docx (meta + sections mit Typ text/list/tasks/transcript) - /api/profiles listet verfügbare Profile - pyyaml als Dependency LXC: - Job.profile + User.default_profile (Migration: ALTER TABLE) - /api/profiles proxy mit 60s-Cache und Fallback - Upload-Form akzeptiert profile (Server-Default: user.default_profile) - Pipeline gibt Profile bei summarize/protocol/docx an Mac weiter - PATCH /api/auth/me — User kann Standard-Profil ändern Frontend: - Profile-Dropdown im Upload (nur sichtbar wenn ≥2 Profile) - Settings-Karte "Mein Standard-Profil" (nur wenn ≥2 Profile) - Job-Zeile zeigt grünes Profile-Tag (nur wenn relevant) Neue Profile = neue YAML-Datei in mac-worker/profiles/ — kein Code-Deploy nötig, der Mac-Worker liest sie beim Start ein.
This commit is contained in:
@@ -33,7 +33,16 @@ class SetupIn(BaseModel):
|
||||
|
||||
|
||||
def _user_dict(u: User) -> dict:
|
||||
return {"id": u.id, "username": u.username, "is_admin": u.is_admin}
|
||||
return {
|
||||
"id": u.id,
|
||||
"username": u.username,
|
||||
"is_admin": u.is_admin,
|
||||
"default_profile": u.default_profile or "meeting",
|
||||
}
|
||||
|
||||
|
||||
class UpdateMeIn(BaseModel):
|
||||
default_profile: str | None = Field(default=None, max_length=64)
|
||||
|
||||
|
||||
@router.get("/me")
|
||||
@@ -41,6 +50,16 @@ def me(user: User = Depends(current_user)):
|
||||
return _user_dict(user)
|
||||
|
||||
|
||||
@router.patch("/me")
|
||||
def update_me(payload: UpdateMeIn, user: User = Depends(current_user), session: Session = Depends(get_session)):
|
||||
if payload.default_profile is not None:
|
||||
user.default_profile = payload.default_profile.strip() or "meeting"
|
||||
session.add(user)
|
||||
session.commit()
|
||||
session.refresh(user)
|
||||
return _user_dict(user)
|
||||
|
||||
|
||||
@router.get("/needs-setup")
|
||||
def needs_setup(session: Session = Depends(get_session)):
|
||||
"""Vor Login prüfen ob noch kein User existiert (erstes Deployment)."""
|
||||
|
||||
@@ -30,6 +30,7 @@ async def create_job(
|
||||
background: BackgroundTasks,
|
||||
audio: UploadFile = File(...),
|
||||
title: str = Form(""),
|
||||
profile: str = Form(""),
|
||||
session: Session = Depends(get_session),
|
||||
user: User = Depends(current_user),
|
||||
):
|
||||
@@ -54,6 +55,7 @@ async def create_job(
|
||||
|
||||
job = Job(
|
||||
owner_id=user.id,
|
||||
profile=(profile.strip() or user.default_profile or "meeting"),
|
||||
filename=safe,
|
||||
original_name=audio.filename,
|
||||
title=title.strip(),
|
||||
@@ -114,6 +116,7 @@ def _job_dict(j: Job, user: User, owner_username: str | None = None) -> dict:
|
||||
"status": j.status,
|
||||
"progress": j.progress,
|
||||
"error": j.error,
|
||||
"profile": j.profile or "meeting",
|
||||
"created_at": j.created_at.isoformat(),
|
||||
"updated_at": j.updated_at.isoformat(),
|
||||
"has": {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
"""Liefert die im Mac-Worker verfügbaren Verarbeitungs-Profile aus.
|
||||
|
||||
Cached die Liste 60 s, damit der Mac nicht bei jedem Reload getroffen wird.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
from app.core.auth import current_user
|
||||
from app.core.mac_client import MacClient
|
||||
from app.models.user import User
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["profiles"])
|
||||
log = logging.getLogger("voice-agent.profiles")
|
||||
|
||||
_CACHE: dict = {"profiles": None, "ts": 0.0}
|
||||
_TTL = 60.0
|
||||
_FALLBACK = [{"name": "meeting", "display_name": "Sitzungsprotokoll", "language": "de"}]
|
||||
|
||||
|
||||
@router.get("/profiles")
|
||||
async def list_profiles(_: User = Depends(current_user)):
|
||||
now = time.time()
|
||||
if _CACHE["profiles"] is not None and now - _CACHE["ts"] < _TTL:
|
||||
return {"profiles": _CACHE["profiles"]}
|
||||
try:
|
||||
profiles = await MacClient().list_profiles()
|
||||
if not profiles:
|
||||
profiles = _FALLBACK
|
||||
except Exception as e: # noqa: BLE001
|
||||
log.warning("Konnte Profile vom Mac nicht laden: %s — Fallback wird genutzt", e)
|
||||
profiles = _FALLBACK
|
||||
_CACHE["profiles"] = profiles
|
||||
_CACHE["ts"] = now
|
||||
return {"profiles": profiles}
|
||||
Reference in New Issue
Block a user