bench/qa_sweep: scrub lone surrogates from the NLI-shadow answer_text/context fields before json.dumps

ARBORIST_NLI_SHADOW=1 carries the raw verifier-input text into bench
rows; real Wikipedia context occasionally has U+D800–U+DFFF code points
(mangled source encoding) that json.dumps(..., ensure_ascii=False) then
refuses to UTF-8-encode → the run died at row 224/225. Scrub via
encode('utf-8','replace').decode() — U+FFFD is fine for a measurement
field. Only the two new shadow fields are touched.
This commit is contained in:
russell@unturf.com 2026-05-12 17:04:33 -04:00
parent f955082ebe
commit 42a0f933ff
No known key found for this signature in database

View file

@ -330,15 +330,25 @@ def _run_one(
# #000049 Phase 2 — when ARBORIST_NLI_SHADOW is set, query() surfaces
# the verifier-input text; carry it (+ the answer) into the row so a
# downstream `nli_shadow_sweep.py --input <this.jsonl>` can measure
# the would-demote rate on real traffic (§7 #12 gate item 4). Off by
# default — these two fields can be large; never persisted otherwise.
# the would-demote rate on bench-qa traffic (§7 #12 gate item 4). Off
# by default — these two fields can be large; never persisted
# otherwise. Scrub lone surrogates first: real Wikipedia context
# occasionally carries U+D800U+DFFF code points (mangled source
# encoding) that `json.dumps(..., ensure_ascii=False)` then refuses
# to UTF-8-encode — a measurement field, U+FFFD is fine.
vit = result.get("verifier_input_text")
if vit is not None:
row["answer_text"] = result.get("answer_text")
row["context"] = vit
row["answer_text"] = _scrub_surrogates(result.get("answer_text"))
row["context"] = _scrub_surrogates(vit)
return row
def _scrub_surrogates(s):
if not isinstance(s, str):
return s
return s.encode("utf-8", "replace").decode("utf-8")
def _directive_compliance(
answer_mode: str, result: dict, err: str | None
) -> dict: