"""Shared data models for the structural rewrite engine.""" from __future__ import annotations from dataclasses import dataclass, field from typing import Any @dataclass class DocumentBlock: """A contiguous document region with rewrite policy.""" text: str kind: str = "paragraph" # paragraph|heading|list|table|code|formula|bibliography|blank rewriteable: bool = True index: int = 0 meta: dict[str, Any] = field(default_factory=dict) @dataclass class SentenceSlots: """Constituent spans extracted for template fill.""" text: str subject: str = "" verb: str = "" verb_phrase: str = "" object: str = "" place: str = "" time: str = "" manner: str = "" negation: str = "" leftover: str = "" entities: list[str] = field(default_factory=list) auxiliaries: list[str] = field(default_factory=list) subject_is_proper: bool = False verb_starts_with_aux: bool = False confidence: float = 0.0 sentence_type: str = "unsupported" reasons: list[str] = field(default_factory=list) @dataclass class TemplateCandidate: template_id: str confidence: float @dataclass class LexicalChange: """One context-validated vocabulary substitution.""" original: str replacement: str token_index: int lemma: str = "" synset_id: str = "" confidence: float = 0.0 @dataclass class RewritePlan: """Decision object before generation.""" safe: bool slots: SentenceSlots | None = None template_id: str = "" candidates: list[TemplateCandidate] = field(default_factory=list) fixed_spans: list[str] = field(default_factory=list) movable: list[str] = field(default_factory=list) skip_reason: str = "" confidence: float = 0.0 @dataclass class SentenceRecord: """Per-sentence rewrite report.""" index: int original: str rewritten: str confidence: float status: str # rewritten|skipped|reverted|passthrough template_id: str = "" sentence_type: str = "" reasons: list[str] = field(default_factory=list) block_index: int = 0 lexical_changes: list[LexicalChange] = field(default_factory=list) @dataclass class EngineStats: batches: int = 0 blocks: int = 0 sentences: int = 0 rewritten: int = 0 skipped: int = 0 reverted: int = 0 passthrough: int = 0 lexical_refined: int = 0 forced_rewrites: int = 0 paraphrased: int = 0 seconds: float = 0.0 reasons: dict[str, int] = field(default_factory=dict) def bump(self, reason: str) -> None: key = (reason or "other").split(":")[0] self.reasons[key] = self.reasons.get(key, 0) + 1 @dataclass class EngineResult: text: str sentences: list[SentenceRecord] = field(default_factory=list) skipped: list[SentenceRecord] = field(default_factory=list) mapping: list[tuple[str, str]] = field(default_factory=list) stats: EngineStats = field(default_factory=EngineStats) notes: str = "" engine: str = "structural-reorder" input_words: int = 0 output_words: int = 0 changed: bool = False similarity: float = 1.0