import logging from fastapi import APIRouter, BackgroundTasks, HTTPException from pydantic import BaseModel from typing import List, Optional, Dict from app.services.note_store import create_note, get_note from app.jobs.async_enrichment_job import enrich_note from app.models.enums import NoteType, NoteStatus from app.utils.time import now_ts router = APIRouter(prefix="/internal/notes", tags=["internal"]) logger = logging.getLogger(__name__) class NoteTextInternalCreateRequest(BaseModel): note_id: str raw_text: str folder_id: Optional[str] = None generate: List[str] = [] @router.post("/text") async def create_text_note_internal(req: NoteTextInternalCreateRequest, bg: BackgroundTasks): """ Idempotent create for text notes: - If note exists -> return current status - Else create note with provided `note_id` and optionally enqueue enrichment """ now = now_ts() allowed = {"normalize", "keywords", "summary", "mindmap"} if not set(req.generate).issubset(allowed): raise HTTPException(400, "Invalid generate task") # IDEMPOTENT CHECK existing = get_note(req.note_id) if existing: logger.info("Note already exists, skip create note_id=%s", req.note_id) return { "note_id": req.note_id, "status": existing.get("status"), } has_enrichment = bool(req.generate) note = { "note_id": req.note_id, "type": NoteType.text, "raw_text": req.raw_text, "folder_id": req.folder_id, "status": NoteStatus.processing if has_enrichment else NoteStatus.created, "created_at": now, "updated_at": now, } # Prepare fields for enrichment if requested if req.generate: note.update({ "normalized_text": None, "title": None, "keywords": [], "summary": None, "mindmap": None, "generate": req.generate, }) try: create_note(note) except ValueError: # race: another worker created it first logger.warning("Race create_note, ignore note_id=%s", req.note_id) existing = get_note(req.note_id) return { "note_id": req.note_id, "status": existing.get("status") if existing else NoteStatus.created, } # enqueue enrichment if requested if req.generate: try: bg.add_task(enrich_note, req.note_id, req.generate) except Exception: # don't fail the request if enqueue fails; log and continue logger.exception("Failed to enqueue enrich_note for %s", req.note_id) return {"note_id": req.note_id, "status": note["status"]}