85 lines
4.8 KiB
Python
85 lines
4.8 KiB
Python
"""Production graph-to-answer context adapter; no benchmark or oracle dependencies."""
|
|
from .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']:
|
|
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('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
|
|
|