Spaces:
Running
Running
| from typing import List, Literal, Optional | |
| from fastapi import APIRouter, HTTPException, Query | |
| from loguru import logger | |
| from api.models import NoteCreate, NoteResponse, NoteUpdate | |
| from open_notebook.domain.notebook import Note | |
| from open_notebook.exceptions import InvalidInputError | |
| router = APIRouter() | |
| async def get_notes( | |
| notebook_id: Optional[str] = Query(None, description="Filter by notebook ID") | |
| ): | |
| """Get all notes with optional notebook filtering.""" | |
| try: | |
| if notebook_id: | |
| # Get notes for a specific notebook | |
| from open_notebook.domain.notebook import Notebook | |
| notebook = await Notebook.get(notebook_id) | |
| if not notebook: | |
| raise HTTPException(status_code=404, detail="Notebook not found") | |
| notes = await notebook.get_notes() | |
| else: | |
| # Get all notes | |
| notes = await Note.get_all(order_by="updated desc") | |
| return [ | |
| NoteResponse( | |
| id=note.id or "", | |
| title=note.title, | |
| content=note.content, | |
| note_type=note.note_type, | |
| created=str(note.created), | |
| updated=str(note.updated), | |
| ) | |
| for note in notes | |
| ] | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Error fetching notes: {str(e)}") | |
| raise HTTPException(status_code=500, detail=f"Error fetching notes: {str(e)}") | |
| async def create_note(note_data: NoteCreate): | |
| """Create a new note.""" | |
| try: | |
| # Auto-generate title if not provided and it's an AI note | |
| title = note_data.title | |
| if not title and note_data.note_type == "ai" and note_data.content: | |
| from open_notebook.graphs.prompt import graph as prompt_graph | |
| prompt = "Based on the Note below, please provide a Title for this content, with max 15 words" | |
| result = await prompt_graph.ainvoke( | |
| { # type: ignore[arg-type] | |
| "input_text": note_data.content, | |
| "prompt": prompt | |
| } | |
| ) | |
| title = result.get("output", "Untitled Note") | |
| # Validate note_type | |
| note_type: Optional[Literal["human", "ai"]] = None | |
| if note_data.note_type in ("human", "ai"): | |
| note_type = note_data.note_type # type: ignore[assignment] | |
| elif note_data.note_type is not None: | |
| raise HTTPException(status_code=400, detail="note_type must be 'human' or 'ai'") | |
| new_note = Note( | |
| title=title, | |
| content=note_data.content, | |
| note_type=note_type, | |
| ) | |
| await new_note.save() | |
| # Add to notebook if specified | |
| if note_data.notebook_id: | |
| from open_notebook.domain.notebook import Notebook | |
| notebook = await Notebook.get(note_data.notebook_id) | |
| if not notebook: | |
| raise HTTPException(status_code=404, detail="Notebook not found") | |
| await new_note.add_to_notebook(note_data.notebook_id) | |
| return NoteResponse( | |
| id=new_note.id or "", | |
| title=new_note.title, | |
| content=new_note.content, | |
| note_type=new_note.note_type, | |
| created=str(new_note.created), | |
| updated=str(new_note.updated), | |
| ) | |
| except HTTPException: | |
| raise | |
| except InvalidInputError as e: | |
| raise HTTPException(status_code=400, detail=str(e)) | |
| except Exception as e: | |
| logger.error(f"Error creating note: {str(e)}") | |
| raise HTTPException(status_code=500, detail=f"Error creating note: {str(e)}") | |
| async def get_note(note_id: str): | |
| """Get a specific note by ID.""" | |
| try: | |
| note = await Note.get(note_id) | |
| if not note: | |
| raise HTTPException(status_code=404, detail="Note not found") | |
| return NoteResponse( | |
| id=note.id or "", | |
| title=note.title, | |
| content=note.content, | |
| note_type=note.note_type, | |
| created=str(note.created), | |
| updated=str(note.updated), | |
| ) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Error fetching note {note_id}: {str(e)}") | |
| raise HTTPException(status_code=500, detail=f"Error fetching note: {str(e)}") | |
| async def update_note(note_id: str, note_update: NoteUpdate): | |
| """Update a note.""" | |
| try: | |
| note = await Note.get(note_id) | |
| if not note: | |
| raise HTTPException(status_code=404, detail="Note not found") | |
| # Update only provided fields | |
| if note_update.title is not None: | |
| note.title = note_update.title | |
| if note_update.content is not None: | |
| note.content = note_update.content | |
| if note_update.note_type is not None: | |
| if note_update.note_type in ("human", "ai"): | |
| note.note_type = note_update.note_type # type: ignore[assignment] | |
| else: | |
| raise HTTPException(status_code=400, detail="note_type must be 'human' or 'ai'") | |
| await note.save() | |
| return NoteResponse( | |
| id=note.id or "", | |
| title=note.title, | |
| content=note.content, | |
| note_type=note.note_type, | |
| created=str(note.created), | |
| updated=str(note.updated), | |
| ) | |
| except HTTPException: | |
| raise | |
| except InvalidInputError as e: | |
| raise HTTPException(status_code=400, detail=str(e)) | |
| except Exception as e: | |
| logger.error(f"Error updating note {note_id}: {str(e)}") | |
| raise HTTPException(status_code=500, detail=f"Error updating note: {str(e)}") | |
| async def delete_note(note_id: str): | |
| """Delete a note.""" | |
| try: | |
| note = await Note.get(note_id) | |
| if not note: | |
| raise HTTPException(status_code=404, detail="Note not found") | |
| await note.delete() | |
| return {"message": "Note deleted successfully"} | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Error deleting note {note_id}: {str(e)}") | |
| raise HTTPException(status_code=500, detail=f"Error deleting note: {str(e)}") |