Compare line items between ground truth and agent output (legacy fallback).
(gt: dict, agent: dict, tolerance: float)
| 473 | |
| 474 | |
| 475 | def compare_line_items(gt: dict, agent: dict, tolerance: float) -> dict: |
| 476 | """Compare line items between ground truth and agent output (legacy fallback).""" |
| 477 | gt_items = parse_line_items(gt.get("Line Items")) |
| 478 | agent_items = parse_line_items(agent.get("Line Items")) |
| 479 | |
| 480 | gt_count = len(gt_items) |
| 481 | agent_count = len(agent_items) |
| 482 | count_match = gt_count == agent_count |
| 483 | |
| 484 | # Compare item codes |
| 485 | gt_codes = sorted([item.get("item_code", "UNKNOWN") for item in gt_items]) |
| 486 | agent_codes = sorted( |
| 487 | [item.get("item_code", "UNKNOWN") for item in agent_items] |
| 488 | ) |
| 489 | codes_match = gt_codes == agent_codes |
| 490 | |
| 491 | # Compare total line cost |
| 492 | gt_total = sum( |
| 493 | parse_amount(item.get("line_cost", "0")) or 0 for item in gt_items |
| 494 | ) |
| 495 | agent_total = sum( |
| 496 | parse_amount(item.get("line_cost", "0")) or 0 for item in agent_items |
| 497 | ) |
| 498 | total_match = abs(gt_total - agent_total) <= tolerance |
| 499 | |
| 500 | # Compare total tax |
| 501 | gt_tax = sum(parse_amount(item.get("tax", "0")) or 0 for item in gt_items) |
| 502 | agent_tax = sum( |
| 503 | parse_amount(item.get("tax", "0")) or 0 for item in agent_items |
| 504 | ) |
| 505 | tax_match = abs(gt_tax - agent_tax) <= tolerance |
| 506 | |
| 507 | # Per-line comparison (by line_number if counts match) |
| 508 | line_diffs = [] |
| 509 | if count_match: |
| 510 | # Sort both by line_number |
| 511 | gt_sorted = sorted(gt_items, key=lambda x: x.get("line_number", 0)) |
| 512 | agent_sorted = sorted( |
| 513 | agent_items, key=lambda x: x.get("line_number", 0) |
| 514 | ) |
| 515 | |
| 516 | for gt_line, agent_line in zip(gt_sorted, agent_sorted, strict=False): |
| 517 | diffs = {} |
| 518 | for field in [ |
| 519 | "item_code", |
| 520 | "description", |
| 521 | "quantity", |
| 522 | "unit_cost", |
| 523 | "line_cost", |
| 524 | "tax", |
| 525 | "tax_code", |
| 526 | ]: |
| 527 | gt_val = str(gt_line.get(field, "")).strip() |
| 528 | agent_val = str(agent_line.get(field, "")).strip() |
| 529 | |
| 530 | if field in ["line_cost", "unit_cost", "tax", "quantity"]: |
| 531 | match = amounts_match(gt_val, agent_val, tolerance) |
| 532 | else: |
no test coverage detected