Spaces:
Sleeping
Sleeping
| """ | |
| Data models for the segmentation pipeline. | |
| All dataclasses are pure Python β no BeautifulSoup or Neo4j dependency. | |
| This lets cross_reference/ and the application layer import them freely. | |
| UID conventions (must match Neo4j schema from T1.4 / NgΖ°α»i A): | |
| Article.uid = "doc_{doc_id}_dieu_{index}" | |
| Clause.uid = "doc_{doc_id}_dieu_{dieu_idx}_khoan_{idx}" | |
| Point.uid = "doc_{doc_id}_dieu_{dieu_idx}_khoan_{khoan_idx}_diem_{letter}" | |
| Chapter has no uid β identified by (doc_id, roman_index) | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass, field | |
| from enum import Enum | |
| from typing import Optional | |
| # --------------------------------------------------------------------------- | |
| # Enums | |
| # --------------------------------------------------------------------------- | |
| class HierarchyType(str, Enum): | |
| """Structural level of a parsed segment.""" | |
| PHAN = "PhαΊ§n" # Part β above ChΖ°Ζ‘ng, used in Bα» luαΊt (gap in spec) | |
| CHUONG = "ChΖ°Ζ‘ng" # Chapter | |
| MUC = "Mα»₯c" # Section β between ChΖ°Ζ‘ng and Δiα»u | |
| DIEU = "Δiα»u" # Article | |
| KHOAN = "KhoαΊ£n" # Clause | |
| DIEM = "Δiα»m" # Point | |
| UNKNOWN = "unknown" | |
| class ConfidenceLevel(str, Enum): | |
| HIGH = "high" # β₯ 0.9 | |
| MEDIUM = "medium" # 0.6 β 0.9 | |
| LOW = "low" # < 0.6 | |
| # --------------------------------------------------------------------------- | |
| # Core segment dataclass | |
| # --------------------------------------------------------------------------- | |
| class Segment: | |
| """ | |
| One structural node produced by the parser. | |
| This is the primary output unit of LegalDocumentParser and the | |
| primary input unit of SegmentWriter and ArticleEmbedder. | |
| Interface note for NgΖ°α»i A: | |
| - doc_id must match Document.id already in Neo4j (from T1.4 / T1.5) | |
| Interface note for cross_reference/ (NgΖ°α»i B, Phase 2): | |
| - article_uid, clause_uid, point_uid are the stable IDs used in | |
| InternalRef.source_article_uid / target_article_uid | |
| """ | |
| # Identity | |
| doc_id: str | |
| hierarchy_type: HierarchyType | |
| index: any # position or label (int or str) | |
| # Hierarchy path β human-readable, e.g. "ChΖ°Ζ‘ng I / Δiα»u 5 / KhoαΊ£n 2" | |
| path: str = "" | |
| # Content | |
| text_content: str = "" # raw HTML of this segment | |
| clean_text: str = "" # plain text, no HTML tags | |
| # Parent linkage | |
| parent_uid: Optional[str] = None # UID of parent segment; None for top-level | |
| # Computed UIDs (set after parsing, before Neo4j write) | |
| uid: Optional[str] = None # own stable UID | |
| # Chapter-specific | |
| roman_index: Optional[str] = None # "I", "II", "III"... | |
| title: Optional[str] = None # heading text (ChΖ°Ζ‘ng/Δiα»u/Mα»₯c title) | |
| section: Optional[str] = None # section context (Mα»₯c), stored as metadata instead of separate node | |
| # Embedding (set by ArticleEmbedder, only for DIEU level) | |
| embedding: Optional[list[float]] = None # 1024-dim vector | |
| # Parser metadata | |
| parse_notes: list[str] = field(default_factory=list) # warnings / edge cases | |
| # ββ Computed properties ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def article_uid(self) -> Optional[str]: | |
| """Convenience accessor used by cross_reference/.""" | |
| return self.uid if self.hierarchy_type == HierarchyType.DIEU else None | |
| class ParseResult: | |
| """ | |
| Full output for one document from LegalDocumentParser. | |
| Consumed by: | |
| - SegmentWriter (T1.5) β Neo4j ingest | |
| - ArticleEmbedder (T1.6) β embedding generation | |
| - ConfidenceScorer (T1.2) β quality check | |
| - cross_reference/extractor.py (T2.1-T2.3) β reference extraction | |
| """ | |
| doc_id: str | |
| segments: list[Segment] = field(default_factory=list) | |
| # Confidence (set by ConfidenceScorer after parsing) | |
| confidence_score: float = 0.0 | |
| confidence_level: ConfidenceLevel = ConfidenceLevel.LOW | |
| confidence_notes: list[str] = field(default_factory=list) | |
| # Parse statistics | |
| chapter_count: int = 0 | |
| article_count: int = 0 | |
| clause_count: int = 0 | |
| point_count: int = 0 | |
| parse_errors: list[str] = field(default_factory=list) | |
| # ββ Convenience accessors ββββββββββββββββββββββββββββββββββββββββββββββ | |
| def articles(self) -> list[Segment]: | |
| return [s for s in self.segments if s.hierarchy_type == HierarchyType.DIEU] | |
| def clauses_of(self, article_uid: str) -> list[Segment]: | |
| return [s for s in self.segments | |
| if s.hierarchy_type == HierarchyType.KHOAN | |
| and s.parent_uid == article_uid] | |
| def points_of(self, clause_uid: str) -> list[Segment]: | |
| return [s for s in self.segments | |
| if s.hierarchy_type == HierarchyType.DIEM | |
| and s.parent_uid == clause_uid] | |