"""Select literal evidence by opaque IDs; never repair a model's factual prose.""" from __future__ import annotations from copy import deepcopy import hashlib import json import re class EvidenceSelectionError(ValueError): pass def identify(row: dict, *, required: bool = False) -> dict: payload = { k: row.get(k) for k in [ "source_id", "passage_id", "person", "work_type", "year", "work_label", "text", "kind", "phase", ] } digest = hashlib.sha256( json.dumps(payload, sort_keys=True, ensure_ascii=False).encode() ).hexdigest()[:24] return {**row, "evidence_id": "E" + digest, "required": required} def overview_candidates(context: dict) -> list[dict]: """Keep every main assignment item; do not confuse a backlog with goals.""" from tools.answers.content_selection import ( evidence_units, administrative_only, unit_score, scope_groups, matches_scope, ) rows = evidence_units(context) selected = [] for person, label in scope_groups(context): own = [row for row in rows if matches_scope(row, person, label)] main = [row for row in own if row["kind"] == "assignment"] # Without an explicit assignment, retain a bounded descriptive sample. # A plan or meeting history cannot become the main assignment. fallback = [ row for row in own if row["kind"] in {"other", "progress"} and not administrative_only(row["text"]) ] fallback.sort(key=lambda row: -unit_score(row["text"], row["kind"])) for row in main or fallback[:2]: if len(row["text"]) > 2500: context["complete"] = False context["truncated"] = True context["truncation_reasons"] = list( dict.fromkeys( [*(context.get("truncation_reasons") or []), "evidence_unit_limit"] ) ) continue selected.append(identify(row, required=bool(main))) return selected def resolve_selection(answer: dict, context: dict) -> dict: """Expand only IDs from the actual budgeted model input, keeping raw data intact.""" out = deepcopy(answer) ids = out.pop("evidence_ids", []) if not ids: return out if out.get("evidence_quotes"): raise EvidenceSelectionError("mixed_evidence_selection") choices = { row["evidence_id"]: row for row in context.get("content_candidates", []) if row.get("evidence_id") } if any(identifier not in choices for identifier in ids): raise EvidenceSelectionError("unknown_evidence_id") wanted = set(ids) chosen = [ row for row in context.get("content_candidates", []) if row.get("evidence_id") in wanted ] out["evidence_quotes"] = [ {k: row[k] for k in ["source_id", "passage_id"]} | {"quote": row["text"]} for row in chosen ] sources = {row["source_id"]: row for row in context.get("sources", [])} urls = [ sources[row["source_id"]]["source_url"] for row in chosen if sources.get(row["source_id"], {}).get("source_url") ] # Extra model-supplied URLs remain visible to the citation guard. out["source_urls"] = list(dict.fromkeys([*out.get("source_urls", []), *urls])) return out def missing_required_evidence(answer: dict, context: dict) -> list[str]: """A short overview must still cover the supplied main assignment items.""" def plain(text): return " ".join(re.sub(r"^[ \t]*(?:\d+[.)]|[-+*])\s+", "", text, flags=re.M).split()) quotes = answer.get("evidence_quotes", []) return [ row["evidence_id"] for row in context.get("content_candidates", []) if row.get("required") and not any( q["source_id"] == row["source_id"] and (not q.get("passage_id") or q["passage_id"] == row["passage_id"]) and plain(row["text"]) in plain(q["quote"]) for q in quotes ) ] def overview_units(answer: dict, context: dict) -> list[dict]: """Render all checked assignment items and at most two fallback units per person.""" from tools.answers.content_selection import evidence_units, scope_groups, matches_scope rows = evidence_units(context, answer.get("evidence_quotes", [])) selected = [] for person, label in scope_groups(context): own = [row for row in rows if matches_scope(row, person, label)] main = [row for row in own if row["kind"] == "assignment"] for row in main or own[:2]: quote = next( q for q in answer["evidence_quotes"] if q["source_id"] == row["source_id"] and " ".join(row["text"].split()) in " ".join(q["quote"].split()) and (not q.get("passage_id") or q["passage_id"] == row["passage_id"]) ) selected.append({"evidence": quote, "text": row["text"]}) return selected