from fastapi import APIRouter, HTTPException, BackgroundTasks from pydantic import BaseModel, Field from typing import Optional, List, Dict, Any from app.services.note_store import get_note, update_note from app.jobs.async_enrichment_job import enrich_note from app.utils.time import now_ts router = APIRouter( prefix="/internal/notes", tags=["internal-notes"], ) class InternalUpdateNoteRequest(BaseModel): status: Optional[str] = Field( None, description="processing | transcribed | error | failed", ) raw_text: Optional[str] = None metadata: Optional[Dict[str, Any]] = None # pipeline triggers generate: Optional[List[str]] = None @router.patch("/{note_id}") def update_note_internal(note_id: str, req: InternalUpdateNoteRequest, bg: BackgroundTasks): note = get_note(note_id) if not note: raise HTTPException(status_code=404, detail="Note not found") updates = req.dict(exclude_unset=True) # cập nhật timestamp hệ thống updates["updated_at"] = now_ts() update_note(note_id, updates) # Decide whether to trigger enrichment: # - If client supplied `generate` in payload → run those tasks # - Or if status switched to `transcribed` and the existing note has a `generate` list → run that try: allowed = {"normalize", "keywords", "summary", "mindmap"} tasks = None if req.generate is not None: if not set(req.generate).issubset(allowed): raise HTTPException(400, "Invalid generate task") tasks = req.generate elif updates.get("status") == "transcribed": existing_generate = note.get("generate") if existing_generate and set(existing_generate).issubset(allowed): tasks = existing_generate if tasks: bg.add_task(enrich_note, note_id, tasks) except HTTPException: raise except Exception: # Don't fail the update if scheduling enrichment fails; just log in real app pass return { "note_id": note_id, "updated": True, "fields": list(updates.keys()), }