""" feedback/diff_engine.py — difflib-based diff with change_ratio Computes structured diffs between original and edited draft text. Filters out trivially small edits (< 5% change) to avoid learning from minor typo fixes. """ import difflib from models import EditDiff class DiffEngine: """Compute structured diffs between original and edited text.""" def compute(self, original: str, edited: str) -> EditDiff: """ Compute the diff between original and edited text. Returns an EditDiff with additions, deletions, and change_ratio. change_ratio ranges from 0.0 (identical) to 1.0 (completely different). """ orig_lines = original.splitlines() edit_lines = edited.splitlines() opcodes = difflib.SequenceMatcher( None, orig_lines, edit_lines ).get_opcodes() additions: list[str] = [] deletions: list[str] = [] for tag, i1, i2, j1, j2 in opcodes: if tag == "insert": additions.append("\n".join(edit_lines[j1:j2])) elif tag == "delete": deletions.append("\n".join(orig_lines[i1:i2])) elif tag == "replace": deletions.append("\n".join(orig_lines[i1:i2])) additions.append("\n".join(edit_lines[j1:j2])) # Character-level similarity ratio change_ratio = 1.0 - difflib.SequenceMatcher( None, original, edited ).ratio() return EditDiff( additions=additions, deletions=deletions, original=original, edited=edited, change_ratio=round(change_ratio, 3), )