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:
root
2026-05-14 03:11:30 +00:00
parent 98cf9eb47e
commit 3a9881d36f
18 changed files with 479 additions and 145 deletions
+5 -4
View File
@@ -36,13 +36,14 @@ async def run_pipeline(job_id: int) -> None:
return
audio_path = settings.upload_dir / job.filename
title = job.title or audio_path.stem
profile = job.profile or "meeting"
job_dir = settings.result_dir / str(job_id)
job_dir.mkdir(parents=True, exist_ok=True)
try:
_update(job_id, status=JobStatus.TRANSCRIBING, progress=10)
log.info("[job %s] transcribe", job_id)
log.info("[job %s] transcribe (profile=%s)", job_id, profile)
tx = await client.transcribe(audio_path, language="de")
transcript_path = job_dir / "transcript.json"
transcript_path.write_text(json.dumps(tx, ensure_ascii=False, indent=2), encoding="utf-8")
@@ -52,21 +53,21 @@ async def run_pipeline(job_id: int) -> None:
_update(job_id, status=JobStatus.SUMMARIZING, progress=55)
log.info("[job %s] summarize", job_id)
summary = await client.summarize(transcript_text, title=title)
summary = await client.summarize(transcript_text, title=title, profile=profile)
summary_path = job_dir / "summary.json"
summary_path.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
_update(job_id, summary_path=str(summary_path), progress=70)
_update(job_id, status=JobStatus.PROTOCOLLING, progress=75)
log.info("[job %s] protocol", job_id)
protocol = await client.protocol(transcript_text, summary, title=title)
protocol = await client.protocol(transcript_text, summary, title=title, profile=profile)
protocol_path = job_dir / "protocol.json"
protocol_path.write_text(json.dumps(protocol, ensure_ascii=False, indent=2), encoding="utf-8")
_update(job_id, protocol_path=str(protocol_path), progress=85)
_update(job_id, status=JobStatus.EXPORTING, progress=90)
log.info("[job %s] export docx", job_id)
docx_bytes = await client.export_docx(protocol)
docx_bytes = await client.export_docx(protocol, profile=profile)
docx_path = job_dir / "protocol.docx"
docx_path.write_bytes(docx_bytes)
_update(job_id, docx_path=str(docx_path), status=JobStatus.DONE, progress=100)