Build field suggestions from vision candidate fields

This commit is contained in:
Sean McElwain 2026-05-30 22:01:43 -05:00
parent b06e260dad
commit d542bcfdf8
1 changed files with 72 additions and 0 deletions

View File

@ -696,6 +696,76 @@ def build_vision_candidate_fields(classification: dict[str, Any]) -> list[dict[s
return fields
def build_vision_field_suggestions(
candidate_fields: list[dict[str, Any]],
existing_fields: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
"""
Convert vision candidate fields into simple add/update/ignore suggestions.
This intentionally stays conservative:
- high confidence merchant/time/item_count/money candidates are surfaced
- symbol/noise is ignored
- existing field comparison can be expanded later
"""
existing_fields = existing_fields or {}
suggestions: list[dict[str, Any]] = []
type_to_existing_key = {
"merchant_or_header": "merchant_raw",
"transaction_time": "transaction_time",
"item_count": "item_count",
"money_amounts": "amount_candidates",
}
for field in candidate_fields or []:
candidate_type = field.get("candidate_type")
if candidate_type == "symbol_or_noise":
continue
confidence = float(field.get("confidence") or 0)
ocr_confidence = field.get("ocr_confidence")
value = field.get("value")
if not value:
continue
min_conf = 0.40
if candidate_type in {"merchant_or_header", "transaction_time"}:
min_conf = 0.60
elif candidate_type == "money_amounts":
min_conf = 0.50
if confidence < min_conf:
continue
existing_key = type_to_existing_key.get(candidate_type, candidate_type)
existing_value = existing_fields.get(existing_key)
action = "add"
if existing_value:
action = "review_update" if str(existing_value).strip() != str(value).strip() else "already_present"
suggestions.append(
{
"suggestion_type": candidate_type,
"target_field": existing_key,
"action": action,
"value": value,
"existing_value": existing_value,
"confidence": confidence,
"ocr_confidence": ocr_confidence,
"source": "vision_candidate_fields",
"source_region_index": field.get("source_region_index"),
"source_bbox": field.get("source_bbox"),
"source_crop_path": field.get("source_crop_path"),
"raw_text": field.get("raw_text"),
}
)
return suggestions
def build_vision_assisted_layout(source_layout: dict[str, Any] | None, vision_result: dict[str, Any]) -> dict[str, Any]:
"""
Convert vision analysis into normal layout_json.
@ -716,6 +786,7 @@ def build_vision_assisted_layout(source_layout: dict[str, Any] | None, vision_re
region_score,
)
candidate_fields = build_vision_candidate_fields(region_classification)
field_suggestions = build_vision_field_suggestions(candidate_fields)
layout["vision_assisted"] = True
layout["vision_assisted_status"] = normalized_vision.get("status", "unknown")
@ -725,6 +796,7 @@ def build_vision_assisted_layout(source_layout: dict[str, Any] | None, vision_re
layout["vision_region_score"] = region_score
layout["vision_region_classification"] = region_classification
layout["vision_candidate_fields"] = candidate_fields
layout["vision_field_suggestions"] = field_suggestions
layout["layout_sync_source"] = "vision_assisted"
layout["layout_needs_review"] = True
return layout