any2human / app /engine /plan /__init__.py
idnameraj's picture
Upload 89 files
31bce5e verified
Raw
History Blame Contribute Delete
4.96 kB
"""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,
)