16
Structured model output
validate_edits(payload) -> list[str] schema only, no document access load_edits(payload, strict=True) -> list[RedlineEdit | ReviewNote] strict=True raises rather than silently dropping items: a dropped finding is a review that quietly did less than it claimed.
examples/16_model_output.py โ ran in 0.23s ยท exit 0
Console output
============================================================================
16 ยท Model output
============================================================================
--- kind defaults to 'edit'; every field is optional except para_id/target ---
RedlineEdit p19 'thirty (30) days' [payment-terms]
RedlineEdit p19 'of the invoice date' [payment-terms]
ReviewNote p15 'reverse engineer, decompile, or di' [IP]
--- straight into apply() ---
3 applied, 0 rejected
APPLIED [payment-terms] p19: 'thirty (30) days'
APPLIED [payment-terms] p19: 'of the invoice date'
APPLIED [IP] p15: 'reverse engineer, decompile, or disassemble'
--- validate_edits โ every problem it catches ---
#0: 'edit' does not accept 'replacment'
#0: 'edit' requires 'replacement' (use "" to delete)
#1: 'edit' requires 'replacement' (use "" to delete)
#2: 'note' requires 'body'
#3: unknown kind 'annotation', expected 'edit' or 'note'
#4: 'edit' requires 'target'
#5: severity 'urgent' not in ('low', 'medium', 'high', 'critical')
#6: occurrence must be >= 0
#7: occurrence must be an integer
--- strict=True (the default) refuses the whole batch ---
RedlineError: malformed edit payload:
9 problems listed
--- strict=False keeps what parsed, drops what did not ---
4 in -> 3 out: ['RedlineEdit', 'RedlineEdit', 'ReviewNote']
--- a clean payload validates to nothing ---
validate_edits(GOOD) -> no problems
--- round-tripping the report back to JSON ---
{
"applied": 3,
"rejected": 1,
"results": [
{
"para_id": 19,
"target": "thirty (30) days",
"agent": "payment-terms",
"applied": true,
"reason": null,
"detail": "",
"spans": 1
},
{
"para_id": 19,
"target": "of the invoice date",
"agent": "payment-terms",
"applied": true,
"reason": null,
"detail": "",
"spans": 1
},
{
"para_id": 15,
"target": "reverse engineer, decompile, or disassemble",
"ag ...
Source
"""16 ยท Structured model output.
validate_edits(payload) -> list[str] schema only, no document access
load_edits(payload, strict=True) -> list[RedlineEdit | ReviewNote]
strict=True raises rather than silently dropping items: a dropped finding is a
review that quietly did less than it claimed.
"""
import json
from _shared import banner, fresh, section
from docx_redline import (
ParagraphIndex,
RedlineError,
ReviewNote,
load_edits,
validate_edits,
)
banner("16 ยท Model output")
GOOD = json.loads("""[
{"kind": "edit", "para_id": 19,
"target": "thirty (30) days", "replacement": "forty-five (45) days",
"agent": "payment-terms", "severity": "high",
"rationale": "Net 45 matches our AP cycle."},
{"kind": "edit", "para_id": 19,
"target": "of the invoice date", "replacement": "of receipt of a valid invoice",
"agent": "payment-terms", "severity": "low", "occurrence": 1,
"insertion_first": false, "rationale": "Invoices are dated before they are sent."},
{"kind": "note", "para_id": 15,
"target": "reverse engineer, decompile, or disassemble",
"body": "No carve-out for interoperability. Check local law.",
"agent": "IP", "severity": "medium", "occurrence": 1}
]""")
section("kind defaults to 'edit'; every field is optional except para_id/target")
items = load_edits(GOOD)
for item in items:
print(f" {type(item).__name__:<12} p{item.para_id} {item.target[:34]!r} [{item.agent}]")
section("straight into apply()")
rl = fresh()
index = ParagraphIndex(rl)
print(index.apply(items).summary())
section("validate_edits โ every problem it catches")
BAD = [
{"para_id": 0, "target": "x", "replacment": "y"}, # typo'd key
{"para_id": 0, "target": "x"}, # no replacement
{"kind": "note", "para_id": 0, "target": "x"}, # note with no body
{"kind": "annotation", "para_id": 0, "target": "x"}, # unknown kind
{"para_id": 0, "target": "", "replacement": "y"}, # empty target
{"para_id": 0, "target": "x", "replacement": "y", "severity": "urgent"},
{"para_id": 0, "target": "x", "replacement": "y", "occurrence": -1},
{"para_id": 0, "target": "x", "replacement": "y", "occurrence": "two"},
]
for problem in validate_edits(BAD):
print(" ", problem)
section("strict=True (the default) refuses the whole batch")
try:
load_edits(BAD)
except RedlineError as exc:
print(" RedlineError:", str(exc).splitlines()[0])
print(" ", len(str(exc).splitlines()) - 1, "problems listed")
section("strict=False keeps what parsed, drops what did not")
mixed = [*GOOD, {"kind": "annotation", "para_id": 0, "target": "x"}]
kept = load_edits(mixed, strict=False)
print(f" {len(mixed)} in -> {len(kept)} out:", [type(i).__name__ for i in kept])
section("a clean payload validates to nothing")
print(" validate_edits(GOOD) ->", validate_edits(GOOD) or "no problems")
section("round-tripping the report back to JSON")
rl = fresh()
index = ParagraphIndex(rl)
report = index.apply([*load_edits(GOOD), ReviewNote(19, "no such phrase", "orphan note")])
print(json.dumps(report.to_dict(), indent=2)[:520], "...")
What it wrote
This example prints its result rather than saving a document โ read the
console output beside it.