diff --git a/app/api/memos.py b/app/api/memos.py index a1b5316..9c43496 100644 --- a/app/api/memos.py +++ b/app/api/memos.py @@ -143,6 +143,11 @@ class MemoCreate(BaseModel): content: str title: str | None = None # 선택적 제목 (없으면 첫 줄 자동 생성) ask_includable: bool = True + # PR-Hermes-Docsrv-Bridge-1: 외부 채널 진입점 식별. default='memo' (web UI 호환). + # 허용 값: memo / voice / hermes / ... (app/models/document.py source_channel enum). + source_channel: str | None = None + # PR-Hermes-Docsrv-Bridge-1: channel/user/message_id/timestamp 등 채널 메타. + source_metadata: dict | None = None class MemoUpdate(BaseModel): @@ -175,7 +180,8 @@ class MemoResponse(BaseModel): # Memo Intake Upgrade PR-2B — AI 추천 분류 (사용자 1-click promote 의 hint) ai_event_kind: str | None = None ai_event_confidence: float | None = None - source_channel: str | None = None # voice/memo 등 진입점 식별 (UI 배지) + source_channel: str | None = None # voice/memo/hermes 등 진입점 식별 (UI 배지) + source_metadata: dict = {} # PR-Hermes-Docsrv-Bridge-1: channel/user/message_id/timestamp file_type: str | None = None # audio (voice 메모) vs note (text 메모) file_path: str | None = None # voice 메모의 NAS audio 경로 (audio player 용) created_at: datetime @@ -210,6 +216,7 @@ def _to_memo_response(doc: Document) -> MemoResponse: ai_event_kind=doc.ai_event_kind, ai_event_confidence=doc.ai_event_confidence, source_channel=doc.source_channel, + source_metadata=dict(doc.source_metadata or {}), file_type=doc.file_type, file_path=doc.file_path, created_at=doc.created_at, @@ -231,6 +238,13 @@ async def create_memo( if not content: raise HTTPException(status_code=400, detail="메모 내용이 비어있습니다") + # PR-Hermes-Docsrv-Bridge-1: source_channel/metadata override 가능. default='memo' (기존 web UI 호환). + channel = body.source_channel or "memo" + if channel not in ("memo", "voice", "hermes"): + raise HTTPException( + status_code=400, + detail=f"source_channel '{channel}' 허용 안 됨 (memo/voice/hermes 만)", + ) doc = Document( file_path=None, file_hash=_content_hash(content), @@ -240,7 +254,8 @@ async def create_memo( title=body.title.strip() if body.title and body.title.strip() else _auto_title(content), extracted_text=content, review_status="approved", - source_channel="memo", + source_channel=channel, + source_metadata=body.source_metadata or {}, user_tags=_parse_hashtags(content), pinned=False, archived=False, @@ -273,9 +288,10 @@ async def list_memos( PR-2C: source_channel='voice' (음성 메모) 도 포함. 사용자 의도 = 메모는 모든 입력의 inbox. voice 메모는 file_type='immutable' + category='audio' + source_channel='voice' 패턴. source_channel 만으로 분리 (file_type 필터는 immutable 다른 binary 까지 끌어옴 — 회피). + PR-Hermes-Docsrv-Bridge-1: source_channel='hermes' (Hermes Discord 등 외부 채널 진입) 도 inbox 포함. """ base = select(Document).where( - Document.source_channel.in_(("memo", "voice")), + Document.source_channel.in_(("memo", "voice", "hermes")), Document.deleted_at == None, # noqa: E711 Document.archived == archived, ) diff --git a/app/models/document.py b/app/models/document.py index a7cd6df..cf6d3cc 100644 --- a/app/models/document.py +++ b/app/models/document.py @@ -104,9 +104,12 @@ class Document(Base): source_channel: Mapped[str | None] = mapped_column( Enum("law_monitor", "devonagent", "email", "web_clip", "tksafety", "inbox_route", "manual", "drive_sync", "news", "memo", - "voice", + "voice", "hermes", name="source_channel") ) + # 외부 채널 (Hermes Discord 등) 의 channel/user/message_id/timestamp 메타. + # extract_meta (OCR 전용) 와 분리. + source_metadata: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict) data_origin: Mapped[str | None] = mapped_column( Enum("work", "external", name="data_origin") ) diff --git a/migrations/267_source_channel_hermes.sql b/migrations/267_source_channel_hermes.sql new file mode 100644 index 0000000..62c4321 --- /dev/null +++ b/migrations/267_source_channel_hermes.sql @@ -0,0 +1,4 @@ +-- 2026-05-16 PR-Hermes-Docsrv-Bridge-1: source_channel enum 에 'hermes' 추가. +-- Hermes Agent (Mac mini) 가 Discord 등 채널에서 받은 텍스트를 Document Server memo 로 +-- 저장할 때 source_channel='hermes' 로 표시. 기존 'memo'/'voice' 와 동등 inbox 진입점. +ALTER TYPE source_channel ADD VALUE IF NOT EXISTS 'hermes'; diff --git a/migrations/268_documents_source_metadata.sql b/migrations/268_documents_source_metadata.sql new file mode 100644 index 0000000..9958fbf --- /dev/null +++ b/migrations/268_documents_source_metadata.sql @@ -0,0 +1,5 @@ +-- 2026-05-16 PR-Hermes-Docsrv-Bridge-1: documents.source_metadata jsonb 컬럼 추가. +-- 외부 채널 (Hermes Discord 등) 에서 들어온 입력의 channel/user/message_id/timestamp +-- 메타데이터 보존. 기존 extract_meta (OCR 전용) 와 분리 — semantically 다른 도메인. +-- DEFAULT '{}'::jsonb 라 백필 X, 빠른 ADD COLUMN. +ALTER TABLE documents ADD COLUMN IF NOT EXISTS source_metadata jsonb DEFAULT '{}'::jsonb NOT NULL;