rohitsar567 Claude Opus 4.7 (1M context) commited on
Commit
835f745
Β·
1 Parent(s): 52b0b5d

fix(upload-extract): LLM output now merges INTO heuristic record (not replace)

Browse files

Live evidence on commit 52b0b5d showed Gemini extraction completing
in ~14s but with data_completeness_pct=17.4% β€” LOWER than the pure
heuristic baseline (47.8%). Cause: the marketplace catalogue prefers
the rag/extracted/<pid>.json (LLM payload) over the persisted
record.json (heuristic baseline), so a sparse LLM extraction was
overwriting the richer heuristic fields.

Fix: after Gemini writes rag/extracted/<pid>.json, also merge the
LLM scalar values INTO UPLOADED_DOCS_DIR/<pid>/record.json. Heuristic
stays as the floor; LLM fields override where non-empty. This is the
same "extracted + curated overlay" pattern the catalogued 148 use
via 40-data/policy_facts/. Schema-shape preserved: heuristic cells
({value, source_pdf_path, source_quote, _confidence}) get their
value lifted; bare-scalar heuristic fields get replaced wholesale.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. backend/uploaded_docs.py +47 -0
backend/uploaded_docs.py CHANGED
@@ -933,6 +933,53 @@ async def extract_one_for_upload(
933
  out_json = _settings.EXTRACTED_DIR / f"{policy_id}.json"
934
  out_json.write_text(policy.model_dump_json(indent=2))
935
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
936
  # Persist into DuckDB so admin / re-render paths see the new card.
937
  try:
938
  upsert_policy(
 
933
  out_json = _settings.EXTRACTED_DIR / f"{policy_id}.json"
934
  out_json.write_text(policy.model_dump_json(indent=2))
935
 
936
+ # ALSO merge the LLM output INTO the persisted record.json so the
937
+ # marketplace catalogue's _load_curated_facts() pass sees the
938
+ # combined heuristic-baseline + LLM-extracted fields (rather than
939
+ # just the LLM payload, which may be sparser than the heuristic
940
+ # for non-standard PDFs). Heuristic stays as the fallback; LLM
941
+ # values override where present + non-empty. This is the same
942
+ # "curated overlay" model the catalogued 148 use via
943
+ # 40-data/policy_facts/.
944
+ try:
945
+ doc_dir = _doc_dir(policy_id)
946
+ rec_path = doc_dir / "record.json"
947
+ if rec_path.exists():
948
+ existing = json.loads(rec_path.read_text())
949
+ llm_dump = policy.model_dump()
950
+ # Carry over LLM scalar values + verbatim source_quotes
951
+ # into the heuristic record. Skip null/empty/empty-list
952
+ # so heuristic stays intact where the LLM was silent.
953
+ for k, v in llm_dump.items():
954
+ if k in ("policy_id", "policy_name", "insurer_slug", "insurer_name"):
955
+ continue
956
+ if v in (None, "", [], {}):
957
+ continue
958
+ # Already in cell-shape ({value, source_quote, ...}) on
959
+ # the heuristic side; lift the LLM scalar into the
960
+ # value field, preserving the heuristic's source_quote
961
+ # / source_pdf_path if the LLM didn't supply one.
962
+ if isinstance(existing.get(k), dict) and "value" in existing[k]:
963
+ existing[k] = {**existing[k], "value": v}
964
+ else:
965
+ existing[k] = v
966
+ # Also carry over the LLM's confidence + insurer_name if
967
+ # detected, both for downstream provenance.
968
+ if getattr(policy, "extraction_confidence_pct", None) is not None:
969
+ existing["_llm_extraction_confidence_pct"] = policy.extraction_confidence_pct
970
+ tmp = rec_path.with_suffix(".json.tmp")
971
+ tmp.write_text(json.dumps(existing, indent=2, ensure_ascii=False, default=str))
972
+ tmp.replace(rec_path)
973
+ _log.info(
974
+ "[upload-extract] merged LLM extraction into record.json for %s",
975
+ policy_id,
976
+ )
977
+ except Exception as _merge_err: # noqa: BLE001
978
+ _log.warning(
979
+ "[upload-extract] record.json merge failed for %s: %s",
980
+ policy_id, _merge_err,
981
+ )
982
+
983
  # Persist into DuckDB so admin / re-render paths see the new card.
984
  try:
985
  upsert_policy(