diff --git a/app/api/documents.py b/app/api/documents.py index b7ffbcb..3dec397 100644 --- a/app/api/documents.py +++ b/app/api/documents.py @@ -1167,9 +1167,9 @@ async def upload_document( canonical.duplicate_count = (canonical.duplicate_count or 0) + 1 # document + processing_queue 는 단일 트랜잭션으로 묶어 원자적 정리. - # G2 (PR-G2-2): 첫 stage = presegment (extract 前). 非PDF/단일문서는 presegment 가 - # 변경 없이 통과시켜 extract 로 흐르고, 번들 PDF 만 자식 분할된다 (worker-side gating). - await enqueue_stage(session, doc.id, "presegment") + # ★ G2 presegment 인제스트 비활성 (2026-06-18): Option A(자식이 부모 file_path 공유)가 + # uq_documents_file_path UNIQUE 제약과 충돌 — 자식파일 전략 재설계 후 재활성. 현재=직접 extract. + await enqueue_stage(session, doc.id, "extract") await session.commit() except Exception: # DB 예외 시 session 은 get_session 컨텍스트 종료로 자동 rollback. diff --git a/app/workers/file_watcher.py b/app/workers/file_watcher.py index 8abba74..6c7c9d9 100644 --- a/app/workers/file_watcher.py +++ b/app/workers/file_watcher.py @@ -118,18 +118,18 @@ def _route_media(path: Path, expected_category: str | None) -> tuple[str | None, if expected_category == "library": # 외부 작성 학습 자료 (KGS Code, 시행규칙 등). 문서 확장자만 수락. # frontmatter 해석은 classify_worker (옵션 C) 가 담당. file_watcher 는 라우팅만. - # G2 (PR-G2-2): 문서 첫 stage = presegment (extract 前). 非PDF/단일은 통과, 번들 PDF 만 분할. + # ★ G2 presegment 인제스트 비활성(2026-06-18, uq_documents_file_path 충돌) → 직접 extract. if ext in LIBRARY_DOC_EXTS: - return ("library", False, "presegment") + return ("library", False, "extract") if ext in AUDIO_EXTS or ext in VIDEO_DIRECT_EXTS or ext in VIDEO_QUARANTINE_EXTS: return (None, False, None) # audio/video 잘못 들어오면 skip return (None, False, None) # 기타 알 수 없는 확장자 skip # Inbox: 문서 파이프 (기존). audio/video 확장자가 실수로 여기 들어오면 skip. - # G2 (PR-G2-2): 문서 첫 stage = presegment (extract 前). 非PDF/단일은 통과, 번들 PDF 만 분할. + # ★ G2 presegment 인제스트 비활성(2026-06-18, uq_documents_file_path 충돌) → 직접 extract. if ext in AUDIO_EXTS or ext in VIDEO_DIRECT_EXTS or ext in VIDEO_QUARANTINE_EXTS: return (None, False, None) - return (None, False, "presegment") + return (None, False, "extract") # ─── Web/Blog ingest (devonagent 트랙) 헬퍼 ────────────────────────────────── @@ -228,9 +228,8 @@ async def _ingest_web_file(session, file_path: Path, rel_path: str) -> tuple[int ) session.add(doc) await session.flush() - # G2 (PR-G2-2): 모든 문서가 presegment 로 진입(단일 entry-point). HTML(非PDF)은 presegment 가 - # 변경 없이 통과시켜 extract 로 흐른다 (worker-side gating). - await enqueue_stage(session, doc.id, "presegment") + # ★ G2 presegment 인제스트 비활성(2026-06-18, uq_documents_file_path 충돌) → 직접 extract. + await enqueue_stage(session, doc.id, "extract") return (1, 0) diff --git a/app/workers/presegment_worker.py b/app/workers/presegment_worker.py index b3700e0..869c1f9 100644 --- a/app/workers/presegment_worker.py +++ b/app/workers/presegment_worker.py @@ -18,6 +18,12 @@ document_lineage(relation_type='segmented_from'). 부모(presegment_role='parent 멱등: 재실행 시 같은 부모로 이미 자식이 있으면(document_lineage segmented_from) 재생성하지 않고 수렴(각 자식이 extract 활성/완료 상태인지만 보장)한다. +★★ BLOCKER (2026-06-18 실측): Option A(자식이 부모 file_path 공유)는 `uq_documents_file_path` +UNIQUE 제약과 충돌 → 자식 INSERT UniqueViolation 으로 실패한다. 따라서 **현재 인제스트는 presegment +를 enqueue 하지 않음**(documents.py/file_watcher.py 가 직접 extract). 본 워커는 재설계 전까지 미사용. +재설계 후보: 자식 file_path=unique 합성값(`{parent}#p{s}-{e}`)+실파일은 부모(lineage source)에서 해석 +/ 또는 file_path NULL + 별도 bundle_source_path 컬럼. 결정 후 인제스트 재활성. + plan: G2 pre-segmentation (PR-G2-2 deterministic ToC segmentation) """