File size: 4,962 Bytes
8f6d79d
 
 
 
 
 
 
7ea9869
7b67b50
8f6d79d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31bce5e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7ea9869
31bce5e
 
 
 
7ea9869
 
8f6d79d
7b67b50
 
 
 
 
 
 
 
 
 
 
 
 
8f6d79d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7b67b50
 
 
 
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
"""Rewrite planning — decide safety and ranked template candidates."""

from __future__ import annotations

from app.engine.classify import classify_sentence, is_rewriteable_type
from app.engine.models import RewritePlan, SentenceSlots, TemplateCandidate
from app.engine.parse import extract_slots
from app.engine.templates import can_swap_initial_subordinate, rank_templates
from app.engine.voice import active_to_passive


def build_plan(text: str, *, min_confidence: float = 0.55) -> RewritePlan:
    """Create a rewrite plan without generating text yet."""
    raw = (text or "").strip()
    if not raw:
        return RewritePlan(safe=False, skip_reason="empty")

    kind = classify_sentence(raw)
    if kind == "because_clause":
        return RewritePlan(
            safe=True,
            template_id="because_front",
            candidates=[TemplateCandidate("because_front", 0.85)],
            confidence=0.85,
            slots=SentenceSlots(text=raw, sentence_type=kind, confidence=0.85),
            movable=["because_clause"],
            fixed_spans=[],
        )

    if kind == "complex":
        if can_swap_initial_subordinate(raw):
            slots = extract_slots(raw)
            slots.confidence = 0.76
            return RewritePlan(
                safe=True,
                template_id="complex_clause_swap",
                candidates=[TemplateCandidate("complex_clause_swap", 0.76)],
                confidence=0.76,
                slots=slots,
                movable=["subordinate_clause"],
                fixed_spans=list(slots.entities),
            )
        # Other complex prose still reaches paraphrase/lexical/ensure via finalize.
        passive_candidate = active_to_passive(raw)
        if passive_candidate:
            slots = extract_slots(raw)
            slots.confidence = 0.7
            return RewritePlan(
                safe=True,
                template_id="active_to_passive",
                candidates=[TemplateCandidate("active_to_passive", 0.7)],
                confidence=0.7,
                slots=slots,
                movable=["subject", "object"],
                fixed_spans=list(slots.entities),
            )
        return RewritePlan(
            safe=False,
            skip_reason="complex",
            confidence=0.0,
            slots=SentenceSlots(text=raw, sentence_type=kind),
        )

    if not is_rewriteable_type(kind):
        passive_candidate = active_to_passive(raw)
        if passive_candidate:
            slots = extract_slots(raw)
            slots.confidence = 0.7
            return RewritePlan(
                safe=True,
                template_id="active_to_passive",
                candidates=[TemplateCandidate("active_to_passive", 0.7)],
                confidence=0.7,
                slots=slots,
                movable=["subject", "object"],
                fixed_spans=list(slots.entities),
            )
        return RewritePlan(
            safe=False,
            skip_reason=kind,
            confidence=0.0,
            slots=SentenceSlots(text=raw, sentence_type=kind),
        )

    slots = extract_slots(raw)
    if "low_confidence" in slots.reasons or slots.confidence < min_confidence:
        return RewritePlan(
            safe=False,
            skip_reason="low_confidence",
            confidence=slots.confidence,
            slots=slots,
        )
    if any(r.startswith("skip:") for r in slots.reasons):
        return RewritePlan(
            safe=False,
            skip_reason=slots.reasons[0],
            confidence=slots.confidence,
            slots=slots,
        )

    candidates = rank_templates(slots)
    structural_ids = {candidate.template_id for candidate in candidates}
    if not structural_ids or structural_ids <= {"subj_verb_object"}:
        if active_to_passive(raw):
            candidates.append(TemplateCandidate("active_to_passive", 0.7))
    if not candidates:
        return RewritePlan(
            safe=False,
            skip_reason="no_template",
            confidence=slots.confidence,
            slots=slots,
        )

    # Filter by min confidence
    candidates = [c for c in candidates if c.confidence >= min_confidence]
    if not candidates:
        return RewritePlan(
            safe=False,
            skip_reason="low_template_confidence",
            confidence=slots.confidence,
            slots=slots,
        )

    fixed = list(slots.entities)
    if slots.negation:
        fixed.append(slots.negation)
    movable = [x for x in ("time", "manner", "place") if getattr(slots, x, "")]

    return RewritePlan(
        safe=True,
        slots=slots,
        template_id=candidates[0].template_id,
        candidates=candidates,
        fixed_spans=fixed,
        movable=movable,
        confidence=candidates[0].confidence,
    )