unknown
Claude Opus 5.5
Add meaning check, Groq model fallback and subject test run; close guard gaps
689da70
Raw History Blame Contribute Delete
4.23 kB
"""Smart Humanize on one sample per subject (real models + Groq), with an independent audit of every sentence:
python devtools/batch.py [--passes 3] [--only medicine,law] [--out devtools/logs/batch.md]
Per subject: AI score before -> after, sentences rewritten, lost numbers / citations / terms, meaning similarity,
publication-rule violations, time. The full before/after text of every sentence goes to the --out file."""
import argparse
import os
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(Path(__file__).resolve().parent))
import env # noqa: E402
env.load()
os.environ.setdefault("LT_MODE", "off")
import e2e # noqa: E402
from samples_domains import DOMAINS # noqa: E402
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--passes", type=int, default=3)
ap.add_argument("--only", default="")
ap.add_argument("--out", default=str(ROOT / "devtools" / "logs" / "batch.md"))
a = ap.parse_args()
import humanizer as H
import meaning as M
import pubrules as PR
import smart as S
import textparse as TP
want = [x.strip() for x in a.only.split(",") if x.strip()]
names = [n for n in DOMAINS if not want or any(w in n for w in want)]
rows, md = [], ["# Smart Humanize by subject\n"]
quiet = lambda frac, desc=None: None
for name in names:
text = DOMAINS[name]
t = time.time()
try:
r = S.smart_text(text, 0, a.passes, 0.9, "Auto", 0.80, 0.50, quiet)
except Exception as err: # one subject failing must not hide the others
print(f"{name}: FAILED {type(err).__name__}: {err}", flush=True)
rows.append((name, "FAILED", str(err)[:60]))
continue
secs = time.time() - t
so, sn = H.split_sentences(r["orig"]), H.split_sentences(r["new"])
lost = e2e.integrity(r["orig"], r["new"])
n_lost = sum(len(v) for v in lost.values())
md.append(f"\n## {name}: {r['before']['pct']}% -> {r['after']['pct']}% ({secs:.0f}s)\n")
md += [f"- {line}" for line in r["log"]]
md.append(f"- {r['note'].strip()}")
issues, sims, changed = [], [], 0
if len(so) != len(sn):
issues.append(f"sentence count {len(so)} -> {len(sn)}")
md.append(f"\n**Sentence count changed.** Output:\n\n{r['new']}\n")
else:
sm = M.similarities(list(zip(so, sn)))
for k, (o, n, s) in enumerate(zip(so, sn, sm), 1):
if o.strip() == n.strip():
md.append(f"\n{k}. (unchanged) {o}")
continue
changed += 1
sims.append(s)
v = PR.violations(o, n) + ([TP.symbol_issue(o, n)] if TP.symbol_issue(o, n) else [])
if v:
issues.append(f"s{k}: " + "; ".join(v))
md.append(f"\n{k}. meaning {s:.2f}" + (f" | RULES: {'; '.join(v)}" if v else "")
+ f"\n - {o}\n + {n}")
if n_lost:
issues.append(f"lost {lost}")
rows.append((name, r["before"]["pct"], r["after"]["pct"], f"{changed}/{len(so)}", n_lost,
f"{min(sims):.2f}" if sims else "-", f"{sum(sims) / len(sims):.2f}" if sims else "-",
len([i for i in issues if i.startswith('s')]), f"{secs:.0f}s", issues))
print(f"{name:26} AI {r['before']['pct']:3}% -> {r['after']['pct']:3}% | rewritten {changed}/{len(so)} | "
f"lost {n_lost} | min sim {rows[-1][5]} | {secs:.0f}s" + (f" | {issues}" if issues else ""), flush=True)
md.insert(1, "\n| Subject | AI before | AI after | Rewritten | Lost items | Min meaning | Mean meaning | Rule flags | Time |"
"\n|---|---|---|---|---|---|---|---|---|\n"
+ "\n".join("| " + " | ".join(str(x) for x in row[:9]) + " |" for row in rows if len(row) > 3) + "\n")
Path(a.out).parent.mkdir(parents=True, exist_ok=True)
Path(a.out).write_text("\n".join(md), encoding="utf-8")
import groqai as G
print("groq:", G.status())
print("report:", a.out)
if __name__ == "__main__":
main()