File size: 3,267 Bytes
8f6d79d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39cfcd1
 
 
8f6d79d
 
 
 
 
 
 
 
 
 
 
74087c2
 
 
 
 
 
 
 
 
 
 
 
8f6d79d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74087c2
8f6d79d
 
 
 
 
 
 
 
 
 
 
74087c2
67f284e
0435b8d
8f6d79d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"""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