142 lines
5.6 KiB
Python
142 lines
5.6 KiB
Python
"""Production graph-to-answer context adapter; no benchmark or oracle dependencies."""
|
|
|
|
from tools.answers.context_policy import joined_candidates
|
|
|
|
|
|
def graph_context(result: dict) -> dict:
|
|
"""Remove storage/profile metadata that cannot answer the actual question.
|
|
|
|
Uses only actual retrieval output and query intent, never gold annotations.
|
|
Values/subjects/source paths are copied without inventing or rewriting facts.
|
|
"""
|
|
facts = []
|
|
intent = result["constraints"]
|
|
shared_year_query = intent.get("mentions_shared") and intent.get("mentions_academic_year")
|
|
work_listing = (
|
|
intent.get("mentions_work_listing", False)
|
|
or shared_year_query
|
|
or (
|
|
intent.get("mentions_title")
|
|
and any(
|
|
row.get("fact_type") in {"person_thesis", "person_work"} and not row.get("title")
|
|
for row in result["facts"]
|
|
)
|
|
)
|
|
)
|
|
academic_records = []
|
|
for row in [*result["facts"], *result.get("supporting_facts", [])]:
|
|
kind = row.get("fact_type", "")
|
|
if kind.startswith("shared_"):
|
|
field = {
|
|
"shared_keyword": "keyword",
|
|
"shared_category": "category",
|
|
"shared_document_author": "document_author",
|
|
}.get(kind)
|
|
if field:
|
|
for side in ["left", "right"]:
|
|
facts.append(
|
|
{
|
|
"person": row[f"{side}_person"],
|
|
field: row[field],
|
|
"document_path": row.get(f"{side}_document_path"),
|
|
"source_url": row.get(f"{side}_source_url"),
|
|
}
|
|
)
|
|
continue
|
|
if not row.get("person"):
|
|
continue
|
|
allowed = {
|
|
"person",
|
|
"document_path",
|
|
"source_url",
|
|
"keyword",
|
|
"category",
|
|
"document_author",
|
|
}
|
|
if intent["mentions_title"] or work_listing:
|
|
allowed.add("title")
|
|
if (
|
|
intent["mentions_title"]
|
|
or intent["mentions_academic_year"]
|
|
or intent["years"]
|
|
or work_listing
|
|
):
|
|
allowed.add("year")
|
|
if intent["mentions_start_year"]:
|
|
allowed.add("start_year")
|
|
fact = {k: v for k, v in row.items() if k in allowed and v is not None}
|
|
if (intent.get("thesis_types") or intent.get("work_types")) and kind in {
|
|
"person_thesis",
|
|
"person_work",
|
|
}:
|
|
work_type = row.get("thesis_type") or row.get("work_type")
|
|
if work_type:
|
|
fact["work_type"] = work_type
|
|
if work_listing and kind in {"person_thesis", "person_work"}:
|
|
# Stable source record identity distinguishes two untitled projects
|
|
# by the same person in the same year. Never use Neo4j element IDs.
|
|
fact.update(
|
|
record_id=row.get("thesis_id") or row.get("work_id"),
|
|
work_type=row.get("thesis_type") or row.get("work_type"),
|
|
work_label=row.get("heading"),
|
|
title=row.get("title"),
|
|
)
|
|
fact = {k: v for k, v in fact.items() if v is not None or k == "title"}
|
|
if fact not in academic_records:
|
|
academic_records.append(fact)
|
|
if fact not in facts:
|
|
facts.append(fact)
|
|
sources = result.get("sources")
|
|
if sources is None:
|
|
sources = [
|
|
{k: d[k] for k in ["document_path", "source_url"] if d.get(k)}
|
|
for d in result.get("documents", [])
|
|
]
|
|
context = {
|
|
"status": result["status"],
|
|
"reason": result["reason"],
|
|
"facts": facts,
|
|
"sources": sources,
|
|
"aggregation": result["aggregation"],
|
|
"complete": result.get("complete"),
|
|
"truncated": result.get("truncated", False),
|
|
"truncation_reasons": result.get("truncation_reasons", []),
|
|
"index_version": result.get("index_version"),
|
|
}
|
|
context["answer_candidates"] = joined_candidates(facts, intent)
|
|
context["question"] = result.get("resolved_query") or result.get("query", "")
|
|
context["requested_years"] = (
|
|
intent.get("years", []) if not intent.get("mentions_start_year") else []
|
|
)
|
|
context["requested_work_types"] = [
|
|
*intent.get("thesis_types", []),
|
|
*intent.get("work_types", []),
|
|
]
|
|
context["clarification"] = result.get("clarification")
|
|
context["requested_people"] = list(
|
|
dict.fromkeys(
|
|
row["name"]
|
|
for row in result.get("resolved_entities", [])
|
|
if row.get("kind") == "Person" and row.get("name")
|
|
)
|
|
)
|
|
shared_values = {}
|
|
for fact in facts:
|
|
for field in ["keyword", "category", "document_author"]:
|
|
if intent.get("mentions_shared") and fact.get(field) is not None:
|
|
shared_values.setdefault((field, fact[field]), set()).add(fact["person"])
|
|
people = context["requested_people"]
|
|
context["shared_properties"] = [
|
|
{"field": field, "value": value, "people": people}
|
|
for (field, value), witnesses in shared_values.items()
|
|
if len(people) >= 2 and set(people) <= witnesses
|
|
]
|
|
comparison = result.get("academic_year_comparison") or next(
|
|
(row for row in result["facts"] if row.get("fact_type") == "academic_year_comparison"), None
|
|
)
|
|
if comparison is not None:
|
|
context["academic_year_comparison"] = comparison
|
|
if work_listing:
|
|
context["required_academic_records"] = academic_records
|
|
return context
|