Backend:
- Document + MemoryEntry models with Alembic migration (GIN FTS index)
- File upload endpoint with path traversal protection (sanitized filenames)
- Background document text extraction (PyMuPDF)
- Full-text search on extracted_text via PostgreSQL tsvector/tsquery
- Memory CRUD with enum-validated categories/importance, field allow-list
- AI tools: save_memory, search_documents, get_memory (Claude function calling)
- Tool execution loop in stream_ai_response (multi-turn tool use)
- Context assembly: injects critical memory + relevant doc excerpts
- File storage abstraction (local filesystem, S3-swappable)
- Secure file deletion (DB flush before disk delete)
Frontend:
- Document upload dialog (drag-and-drop + file picker)
- Document list with status badges, search, download (via authenticated blob)
- Document viewer with extracted text preview
- Memory list grouped by category with importance color coding
- Memory editor with category/importance dropdowns
- Documents + Memory pages with full CRUD
- Enabled sidebar navigation for both sections
Review fixes applied:
- Sanitized upload filenames (path traversal prevention)
- Download via axios blob (not bare <a href>, preserves auth)
- Route ordering: /search before /{id}/reindex
- Memory update allows is_active=False + field allow-list
- MemoryEditor form resets on mode switch
- Literal enum validation on category/importance schemas
- DB flush before file deletion for data integrity
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
44 lines
1.1 KiB
Python
44 lines
1.1 KiB
Python
import uuid
|
|
from datetime import datetime
|
|
from typing import Literal
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
CategoryType = Literal["condition", "medication", "allergy", "vital", "document_summary", "other"]
|
|
ImportanceType = Literal["critical", "high", "medium", "low"]
|
|
|
|
|
|
class CreateMemoryRequest(BaseModel):
|
|
category: CategoryType
|
|
title: str = Field(min_length=1, max_length=255)
|
|
content: str = Field(min_length=1)
|
|
source_document_id: uuid.UUID | None = None
|
|
importance: ImportanceType = "medium"
|
|
is_active: bool = True
|
|
|
|
|
|
class UpdateMemoryRequest(BaseModel):
|
|
category: CategoryType | None = None
|
|
title: str | None = None
|
|
content: str | None = None
|
|
importance: ImportanceType | None = None
|
|
is_active: bool | None = None
|
|
|
|
|
|
class MemoryEntryResponse(BaseModel):
|
|
id: uuid.UUID
|
|
user_id: uuid.UUID
|
|
category: str
|
|
title: str
|
|
content: str
|
|
source_document_id: uuid.UUID | None
|
|
importance: str
|
|
is_active: bool
|
|
created_at: datetime
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class MemoryEntryListResponse(BaseModel):
|
|
entries: list[MemoryEntryResponse]
|