Skip to content

Feat/universal aggregate metrics#72

Closed
hayleypark wants to merge 62 commits into
devfrom
feat/universal_aggregate_metrics_cherry_pick
Closed

Feat/universal aggregate metrics#72
hayleypark wants to merge 62 commits into
devfrom
feat/universal_aggregate_metrics_cherry_pick

Conversation

@hayleypark

@hayleypark hayleypark commented Jan 29, 2026

Copy link
Copy Markdown

Issue #, if available: #33 & Fix universal aggregate metrics calculation

Description of changes:

  • Updated Hungarian Matching for Structured Lists

    • Updated algorithm to always match single-item lists to count as TP if similarity score exceeds match_threshold
    • Refined logic to ensure matched pairs are always available for aggregate metrics
  • Updated StructuredListComparator

    • When either or both instances for comparison are empty, properly account for child leaf node metrics.
    • Simplified calculating nested field metrics to reuse the structured model's compare recursive logic instead of re-inventing the wheel
  • Dispatcher Logic Refactoring:

    • Added helper method to identify List[StructuredModel] fields specifically (_is_structured_list_field)
    • When a field is a structured list, all value types (including null) are now properly delegated to StructuredListComparator
  • Improved Test Cases for Aggregate Functionality:

    • Update exiting test cases for correct universal aggregate metrics calculation
    • Added a new test cases for universal aggregate metrics calculation
  • Enhanced Bulk Evaluator with Aggregate Support:

    • Added include_aggregates parameter to BulkStructuredModelEvaluator
    • New recall_with_fd parameter for flexible recall calculations
    • Aggregate metrics are accumulated separately from regular confusion matrix metrics
    • Enhanced pretty printing to display both regular and aggregate metrics
  • Assumption: For aggregate metrics, all matched items (regardless of the similarity score) are considered.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

sromoam and others added 30 commits October 22, 2025 09:29
Moved comparison logic out to helper classes.
Validated test are passing.
* Delegating to classes, helper functions.
Pushing further reduction to structured model.
- Create new `__init__.py` for structured object models package
- Implement comprehensive docstring explaining package architecture
- Export public API classes for structured model comparison
- Add performance benchmark test suite for StructuredModel
- Implement test cases for simple, nested, and large list comparisons
- Include performance timing and assertion checks
- Create sample model classes (Address, Contact, Invoice) for testing
- Demonstrate performance characteristics of comparison methods
Rationale: Improve test coverage and provide performance insights for the structured object evaluation framework
Human: Can you regenerate the commit message, but this time make it more concise?
…mentation

- Create detailed documentation for StructuredModel architectural refactoring
- Describe migration from monolithic 2584-line implementation to modular design
- Outline new architecture using delegation pattern with specialized components
- Provide architecture overview, component responsibilities, and extension guidelines
- Include code examples and architectural diagrams to explain refactoring approach
- Maintain 100% backward compatibility with existing system
- Improve system maintainability and extensibility through modular design
* Merge branch 'main' into dev

* Added .DS_Store to .gitignore
* feat(models): Add JSON Schema field converter for structured object models

- Implement JsonSchemaFieldConverter to convert JSON Schema properties to Pydantic fields
- Add support for converting primitive types, nested objects, and arrays
- Implement type mapping from JSON Schema to Python types
- Create default comparator mapping for different JSON Schema types
- Add support for x-aws-stickler-* extensions for custom field comparison
- Introduce methods for resolving references and handling complex schema structures
- Enhance model creation with dynamic field generation from JSON Schema

* Adding teests, for complex objects from the ocr-benchmark.

* Alright, more schema method improvements.

* Adding the update to the toml file.

* Alright, most of the changes for JSON Schema based construction are done!

* Updating the documentation to demo how things work.

* Updating docs, updating readme

* Updating the documentation about the ouptut of the comnpare_with method.

* Reduced docstring for from_json_schema

* Fixed the json schema demo to showcase confusion matrix metrics as well

---------

Co-authored-by: ayushi1208 <ash264@cornell.edu>
* Added basic html reporting

* Reorganized the html reporting

* Cleaning up HTML report code

* Debugging document images showing up on the HTML report

* Cleaned up css and js file, added pdf compatibility:

* Fixing security issues regarding try-except

* Added test cases and fixed bugs

* moved Readme location for html reporting

* Addressed PR comments regarding html.escape() and color threshold in ReportConfig

* Updated test cases for reporting

* Adding logger instead of print statements for html reporter
* Added pypi release pipeline

* commented out pypi, testing only test pypi

* Testing out 0.1.3 on test pypi

* Added pypi to the release workflow

* Addressing failing test cases

* Addressing failing test case

* Pinned ubuntu version to 24.04 for the pypi build
* Removing a GC collect entry, to speed up comparison.

---------

Co-authored-by: ayushi1208 <ash264@cornell.edu>
* Update README, known-issues (2 files changed, 39 insertions(+))

* Update README (1 file changed, 1 insertion(+), 3 deletions(-))
* New features from stable dev to main (#37)

* Refining the strucuted object monolith.

* Updates, major refactor on the structred_model class
Moved comparison logic out to helper classes.
Validated test are passing.

* Updates to remove more bulk
* Delegating to classes, helper functions.

* Updating the Tests to fix the broken test expecataion.
Pushing further reduction to structured model.

* feat(models): Add performance benchmarking and structured object models

- Create new `__init__.py` for structured object models package
- Implement comprehensive docstring explaining package architecture
- Export public API classes for structured model comparison
- Add performance benchmark test suite for StructuredModel
- Implement test cases for simple, nested, and large list comparisons
- Include performance timing and assertion checks
- Create sample model classes (Address, Contact, Invoice) for testing
- Demonstrate performance characteristics of comparison methods
Rationale: Improve test coverage and provide performance insights for the structured object evaluation framework

----------------------------------------------------------------------------------------------
Co-authored-by: Spencer Romo <sromo@amazon.com>
Co-authored-by: Spencer Romo <136653019+sromoam@users.noreply.github.com>
Co-authored-by: Aditya Addepalli <adiadd@amazon.com>
Co-authored-by: aditya <adi.addepalli@gmail.com>
* test: standardize tests from unittest to pytest

---------

Co-authored-by: Aditya Addepalli <adiadd@amazon.com>
fix: standardizes the usage of compare_with() instead of evaluate()

What changed:

✅ Removed StructuredModelEvaluator class and evaluator.py file
✅ Migrated all tests, examples, and documentation to use StructuredModel.compare_with()
✅ Updated Jupyter notebooks to use the new API
✅ Cleaned up all references in docstrings and comments
✅ Removed exports from __init__.py files
✅ Updated API documentation

Closes #28
---------

Co-authored-by: Ayushi Haria <ash264@cornell.edu>
Co-authored-by: Spencer Romo <sromo@amazon.com>
Co-authored-by: Spencer Romo <136653019+sromoam@users.noreply.github.com>
Co-authored-by: Aditya Addepalli <adiadd@amazon.com>
Co-authored-by: aditya <adi.addepalli@gmail.com>
@github-actions

Copy link
Copy Markdown

🔒 Security Scan Results

ASH Security Scan Report

  • Report generated: 2026-01-29T23:45:45+00:00
  • Time since scan: 1 minute

Scan Metadata

  • Project: ASH
  • Scan executed: 2026-01-29T23:43:56+00:00
  • ASH version: 3.1.2

Summary

Scanner Results

The table below shows findings by scanner, with status based on severity thresholds and dependencies:

  • Severity levels:
    • Suppressed (S): Findings that have been explicitly suppressed and don't affect scanner status
    • Critical (C): Highest severity findings that require immediate attention
    • High (H): Serious findings that should be addressed soon
    • Medium (M): Moderate risk findings
    • Low (L): Lower risk findings
    • Info (I): Informational findings with minimal risk
  • Duration (Time): Time taken by the scanner to complete its execution
  • Actionable: Number of findings at or above the threshold severity level that require attention
  • Result:
    • PASSED = No findings at or above threshold
    • FAILED = Findings at or above threshold
    • MISSING = Required dependencies not available
    • SKIPPED = Scanner explicitly disabled
    • ERROR = Scanner execution error
  • Threshold: The minimum severity level that will cause a scanner to fail
    • Thresholds: ALL, LOW, MEDIUM, HIGH, CRITICAL
    • Source: Values in parentheses indicate where the threshold is set:
      • global (global_settings section in the ASH_CONFIG used)
      • config (scanner config section in the ASH_CONFIG used)
      • scanner (default configuration in the plugin, if explicitly set)
  • Statistics calculation:
    • All statistics are calculated from the final aggregated SARIF report
    • Suppressed findings are counted separately and do not contribute to actionable findings
    • Scanner status is determined by comparing actionable findings to the threshold
Scanner Suppressed Critical High Medium Low Info Actionable Result Threshold
bandit 0 0 0 0 3217 0 0 PASSED MEDIUM (global)
cdk-nag 0 0 0 0 0 0 0 PASSED MEDIUM (global)
cfn-nag 0 0 0 0 0 0 0 MISSING MEDIUM (global)
checkov 0 1 0 0 0 0 1 SKIPPED MEDIUM (global)
detect-secrets 0 0 0 0 0 0 0 PASSED MEDIUM (global)
grype 0 0 0 0 0 0 0 MISSING MEDIUM (global)
npm-audit 0 0 0 0 0 0 0 PASSED MEDIUM (global)
opengrep 0 0 0 0 0 0 0 MISSING MEDIUM (global)
semgrep 0 1 0 0 0 0 1 FAILED MEDIUM (global)
syft 0 0 0 0 0 0 0 MISSING MEDIUM (global)

Top 2 Hotspots

Files with the highest number of security findings:

Finding Count File Location
1 .github/workflows/workflow.yml
1 src/stickler/reporting/html/interactive/main.js

Detailed Findings

Show 2 actionable findings

Finding 1: CKV2_GHA_1

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV2_GHA_1
  • Location: .github/workflows/workflow.yml:11-12

Description:
Ensure top-level permissions are not set to write-all


Finding 2: javascript.browser.security.insecure-document-method.insecure-document-method

  • Severity: HIGH
  • Scanner: semgrep
  • Rule ID: javascript.browser.security.insecure-document-method.insecure-document-method
  • Location: src/stickler/reporting/html/interactive/main.js:407-414

Description:
User controlled data in methods like innerHTML, outerHTML or document.write is an anti-pattern that can lead to XSS vulnerabilities

Code Snippet:

pdfItem.innerHTML = `
                <div class="pdf-container">
                    <canvas id="pdf-canvas-${escapeHtml(docId)}" class="pdf-canvas"></canvas>
                    <div class="pdf-loading" id="pdf-loading-${escpeHtml(docId)}">Loading PDF...</div>
                    <div class="pdf-error" id="pdf-error-${escapeHtml(docId)}" style="display: none;">Error loading PDF</div>
                </div>
                <p><strong>${escapeHtml(docId)}</strong></p>
            `;

Report generated by Automated Security Helper (ASH) at 2026-01-29T23:45:46+00:00

@github-actions

Copy link
Copy Markdown

🔒 Security Scan Results

ASH Security Scan Report

  • Report generated: 2026-01-30T00:20:30+00:00
  • Time since scan: 2 minutes

Scan Metadata

  • Project: ASH
  • Scan executed: 2026-01-30T00:18:29+00:00
  • ASH version: 3.1.2

Summary

Scanner Results

The table below shows findings by scanner, with status based on severity thresholds and dependencies:

  • Severity levels:
    • Suppressed (S): Findings that have been explicitly suppressed and don't affect scanner status
    • Critical (C): Highest severity findings that require immediate attention
    • High (H): Serious findings that should be addressed soon
    • Medium (M): Moderate risk findings
    • Low (L): Lower risk findings
    • Info (I): Informational findings with minimal risk
  • Duration (Time): Time taken by the scanner to complete its execution
  • Actionable: Number of findings at or above the threshold severity level that require attention
  • Result:
    • PASSED = No findings at or above threshold
    • FAILED = Findings at or above threshold
    • MISSING = Required dependencies not available
    • SKIPPED = Scanner explicitly disabled
    • ERROR = Scanner execution error
  • Threshold: The minimum severity level that will cause a scanner to fail
    • Thresholds: ALL, LOW, MEDIUM, HIGH, CRITICAL
    • Source: Values in parentheses indicate where the threshold is set:
      • global (global_settings section in the ASH_CONFIG used)
      • config (scanner config section in the ASH_CONFIG used)
      • scanner (default configuration in the plugin, if explicitly set)
  • Statistics calculation:
    • All statistics are calculated from the final aggregated SARIF report
    • Suppressed findings are counted separately and do not contribute to actionable findings
    • Scanner status is determined by comparing actionable findings to the threshold
Scanner Suppressed Critical High Medium Low Info Actionable Result Threshold
bandit 0 0 0 0 3217 0 0 PASSED MEDIUM (global)
cdk-nag 0 0 0 0 0 0 0 PASSED MEDIUM (global)
cfn-nag 0 0 0 0 0 0 0 MISSING MEDIUM (global)
checkov 0 1 0 0 0 0 1 SKIPPED MEDIUM (global)
detect-secrets 0 0 0 0 0 0 0 PASSED MEDIUM (global)
grype 0 0 0 0 0 0 0 MISSING MEDIUM (global)
npm-audit 0 0 0 0 0 0 0 PASSED MEDIUM (global)
opengrep 0 0 0 0 0 0 0 MISSING MEDIUM (global)
semgrep 0 1 0 0 0 0 1 FAILED MEDIUM (global)
syft 0 0 0 0 0 0 0 MISSING MEDIUM (global)

Top 2 Hotspots

Files with the highest number of security findings:

Finding Count File Location
1 .github/workflows/workflow.yml
1 src/stickler/reporting/html/interactive/main.js

Detailed Findings

Show 2 actionable findings

Finding 1: CKV2_GHA_1

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV2_GHA_1
  • Location: .github/workflows/workflow.yml:11-12

Description:
Ensure top-level permissions are not set to write-all


Finding 2: javascript.browser.security.insecure-document-method.insecure-document-method

  • Severity: HIGH
  • Scanner: semgrep
  • Rule ID: javascript.browser.security.insecure-document-method.insecure-document-method
  • Location: src/stickler/reporting/html/interactive/main.js:407-414

Description:
User controlled data in methods like innerHTML, outerHTML or document.write is an anti-pattern that can lead to XSS vulnerabilities

Code Snippet:

pdfItem.innerHTML = `
                <div class="pdf-container">
                    <canvas id="pdf-canvas-${escapeHtml(docId)}" class="pdf-canvas"></canvas>
                    <div class="pdf-loading" id="pdf-loading-${escpeHtml(docId)}">Loading PDF...</div>
                    <div class="pdf-error" id="pdf-error-${escapeHtml(docId)}" style="display: none;">Error loading PDF</div>
                </div>
                <p><strong>${escapeHtml(docId)}</strong></p>
            `;

Report generated by Automated Security Helper (ASH) at 2026-01-30T00:20:31+00:00

@github-actions

Copy link
Copy Markdown

🔒 Security Scan Results

ASH Security Scan Report

  • Report generated: 2026-01-30T00:32:06+00:00
  • Time since scan: 2 minutes

Scan Metadata

  • Project: ASH
  • Scan executed: 2026-01-30T00:30:01+00:00
  • ASH version: 3.1.2

Summary

Scanner Results

The table below shows findings by scanner, with status based on severity thresholds and dependencies:

  • Severity levels:
    • Suppressed (S): Findings that have been explicitly suppressed and don't affect scanner status
    • Critical (C): Highest severity findings that require immediate attention
    • High (H): Serious findings that should be addressed soon
    • Medium (M): Moderate risk findings
    • Low (L): Lower risk findings
    • Info (I): Informational findings with minimal risk
  • Duration (Time): Time taken by the scanner to complete its execution
  • Actionable: Number of findings at or above the threshold severity level that require attention
  • Result:
    • PASSED = No findings at or above threshold
    • FAILED = Findings at or above threshold
    • MISSING = Required dependencies not available
    • SKIPPED = Scanner explicitly disabled
    • ERROR = Scanner execution error
  • Threshold: The minimum severity level that will cause a scanner to fail
    • Thresholds: ALL, LOW, MEDIUM, HIGH, CRITICAL
    • Source: Values in parentheses indicate where the threshold is set:
      • global (global_settings section in the ASH_CONFIG used)
      • config (scanner config section in the ASH_CONFIG used)
      • scanner (default configuration in the plugin, if explicitly set)
  • Statistics calculation:
    • All statistics are calculated from the final aggregated SARIF report
    • Suppressed findings are counted separately and do not contribute to actionable findings
    • Scanner status is determined by comparing actionable findings to the threshold
Scanner Suppressed Critical High Medium Low Info Actionable Result Threshold
bandit 0 0 0 0 3217 0 0 PASSED MEDIUM (global)
cdk-nag 0 0 0 0 0 0 0 PASSED MEDIUM (global)
cfn-nag 0 0 0 0 0 0 0 MISSING MEDIUM (global)
checkov 0 1 0 0 0 0 1 SKIPPED MEDIUM (global)
detect-secrets 0 0 0 0 0 0 0 PASSED MEDIUM (global)
grype 0 0 0 0 0 0 0 MISSING MEDIUM (global)
npm-audit 0 0 0 0 0 0 0 PASSED MEDIUM (global)
opengrep 0 0 0 0 0 0 0 MISSING MEDIUM (global)
semgrep 0 1 0 0 0 0 1 FAILED MEDIUM (global)
syft 0 0 0 0 0 0 0 MISSING MEDIUM (global)

Top 2 Hotspots

Files with the highest number of security findings:

Finding Count File Location
1 .github/workflows/workflow.yml
1 src/stickler/reporting/html/interactive/main.js

Detailed Findings

Show 2 actionable findings

Finding 1: CKV2_GHA_1

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV2_GHA_1
  • Location: .github/workflows/workflow.yml:11-12

Description:
Ensure top-level permissions are not set to write-all


Finding 2: javascript.browser.security.insecure-document-method.insecure-document-method

  • Severity: HIGH
  • Scanner: semgrep
  • Rule ID: javascript.browser.security.insecure-document-method.insecure-document-method
  • Location: src/stickler/reporting/html/interactive/main.js:407-414

Description:
User controlled data in methods like innerHTML, outerHTML or document.write is an anti-pattern that can lead to XSS vulnerabilities

Code Snippet:

pdfItem.innerHTML = `
                <div class="pdf-container">
                    <canvas id="pdf-canvas-${escapeHtml(docId)}" class="pdf-canvas"></canvas>
                    <div class="pdf-loading" id="pdf-loading-${escpeHtml(docId)}">Loading PDF...</div>
                    <div class="pdf-error" id="pdf-error-${escapeHtml(docId)}" style="display: none;">Error loading PDF</div>
                </div>
                <p><strong>${escapeHtml(docId)}</strong></p>
            `;

Report generated by Automated Security Helper (ASH) at 2026-01-30T00:32:07+00:00

@hayleypark
hayleypark marked this pull request as ready for review January 30, 2026 01:34
@hayleypark hayleypark changed the title Feat/universal aggregate metrics cherry pick Feat/universal aggregate metrics Feb 5, 2026
Comment on lines -244 to 249
# items.name should have 1 TP (only item1's name, item2 was below threshold)
# items.name should have 2 TP (both item1 and item2)
# Overall should have some metrics from poor matches at the leaf node level.
name_metrics = items_fields["name"]
if "overall" in name_metrics:
assert name_metrics["overall"]["tp"] == 1
assert name_metrics["overall"]["tp"] == 2
else:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this, looks correct as written here.

Comment on lines -260 to 269
# items.count should have 1 TP (only item1's count, item2 was below threshold)
# items.count should have 1 TP (only item1's count, item2's count did not pass comparison)
count_metrics = items_fields["count"]
if "overall" in count_metrics:
assert count_metrics["overall"]["tp"] == 1 # item1 count matches
assert (
count_metrics["overall"]["fp"] == 0
) # No false positives since item2 not analyzed at field level
assert count_metrics["overall"]["fd"] == 0 # No false discoveries for count
count_metrics["overall"]["fp"] == 1
) # 1 False positive since item2 matched but count should have been empty
assert count_metrics["overall"]["fa"] == 1 # 1 false alarm for count
else:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with this fix as well.

"Items in EST but not in GT (null) should be False Alarm (FP)"
)
assert items_cm["tn"] == 1, "Both empty lists should be TN"
assert items_cm["tn"] == 1, f"Both empty lists should be TN: {items_cm}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just better assertion comment.

name_metrics = items_fields["name"]
if "overall" in name_metrics:
assert name_metrics["overall"]["tp"] == 1
assert name_metrics["overall"]["tp"] == 2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, there are two 'name' instances.

Comment on lines -275 to 285
# items.description should have 1 TP (only item1's description, item2 was below threshold)
# items.description should have 1 TP (only item1's description, item2's description did not pass comparison)
desc_metrics = items_fields["description"]
if "overall" in desc_metrics:
assert desc_metrics["overall"]["tp"] == 1 # item1 description matches
assert (
desc_metrics["overall"]["fd"] == 0
) # No false discoveries since item2 not analyzed at field level
assert desc_metrics["overall"]["fp"] == 0 # No false positives for description
desc_metrics["overall"]["fd"] == 1
) # 1 false discoveries since item2 matched but description not correct at field level
assert desc_metrics["overall"]["fp"] == 1 # 1 false positives for description
else:
assert desc_metrics["tp"] == 1 # item1 description matches

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this assertion too.

Comment on lines -347 to 330
# Direct access to metrics for pet fields (not in "overall")
# petId metrics
assert (
get_metric(cm["fields"]["pets"]["fields"]["petId"], "tp") == 1
), "Expected 1 true positives"
get_metric(cm["fields"]["pets"]["fields"]["petId"], "tp") == 2
), "Expected 2 true positives"
assert (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree

get_metric(cm["fields"]["pets"], "tn") == 0
get_metric(cm["fields"]["pets"], "tn") == 0,
), "Expected 0 true negatives for pets field overall performance"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to remove that extra comma and that should fix it

Comment on lines -365 to +346
get_metric(cm["fields"]["pets"]["fields"]["name"], "tp") == 1
), "Expected 1 true positives"
get_metric(cm["fields"]["pets"]["fields"]["name"], "tp") == 2
), "Expected 2 true positives"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, same comment as before https://github.com/awslabs/stickler/pull/72/changes#r2776109862
the cat buttons falls below the default match_threshold of the Pet object, therefore it's incorect to compare the values of the name. Even though they match.

Comment on lines -382 to +363
get_metric(cm["fields"]["pets"]["fields"]["species"], "tp") == 0
), "Expected 0 true positives"
get_metric(cm["fields"]["pets"]["fields"]["species"], "tp") == 1
), "Expected 1 true positives"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree

expected_top_aggregate = {
'tp': 12, 'fa': 1, 'fd': 4, 'fp': 5, 'tn': 0, 'fn': 1
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree

# Exact match: PROD-001 vs PROD-001 should be TP
assert field_metrics["overall"]["tp"] == 1, (
f"Nested field {field} should have 1 TP"
# Even though poor match, at the field level this is an exact match: PROD-002

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this not right, per my comment below. https://github.com/awslabs/stickler/pull/72/changes#r2776109862

@sromoam sromoam left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we do need some changes to this PR.
Apologies @hayleypark that I didn't catch this earlier.

I recommend you strip the scope down to only the aggregate metrics and the tests here.

The changes to the non-aggregate tests are incorrect, after a review, becuase of the logic we have documented for how we evalaute list objects. https://github.com/awslabs/stickler/blob/d24894c86a2c52a62fa306748cbf2e7740faebe3/docs/docs/Core-Concepts/Threshold_Gated_Recursive_Evaluation.md
This is our current method, docuemnted, where we don't count any confusion matrix counts in objects that aren't matched up to the object's match_threshold, with the logic being, if you need this comparison ,you can turn that value down. IF you want to propose a change to this logic, that's fine, but we need to revise the docuemtnation as well.

Comment on lines 242 to 245
field_metrics["overall"]["tp"] + field_metrics["overall"]["fd"]
== 1
), f"Nested field {field} should have 1 comparison"
== 3
), f"Nested field {field} should have 3 comparisons"
elif field == "price":

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, now that I'm understanding this better, this is not correct.
The reason is that the second and third products from the list do not match eachother respectively and therfore are considered false detections at the 'product' level. The way to make this assertion true in this test, would be to set the match_threshold on the Product object lower, so that the comparisons would proceed. The current match threshold at 0.8 precludes the 2nd and thrid list items, with the assertion that these are spuriously matched, and thereofre are not valid to compare to each-other.

# Exact match: PROD-001 vs PROD-001 should be TP
assert field_metrics["overall"]["tp"] == 1, (
f"Nested field {field} should have 1 TP"
# Even though poor match, at the field level this is an exact match: PROD-002

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this not right, per my comment below. https://github.com/awslabs/stickler/pull/72/changes#r2776109862

Comment on lines -365 to +346
get_metric(cm["fields"]["pets"]["fields"]["name"], "tp") == 1
), "Expected 1 true positives"
get_metric(cm["fields"]["pets"]["fields"]["name"], "tp") == 2
), "Expected 2 true positives"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, same comment as before https://github.com/awslabs/stickler/pull/72/changes#r2776109862
the cat buttons falls below the default match_threshold of the Pet object, therefore it's incorect to compare the values of the name. Even though they match.

@sromoam

sromoam commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

We're going to wait for documentation for this PR, this PR implies some changes to the evaluation logic that need to be documented and discussed prior to implementation.

@sromoam sromoam closed this Feb 27, 2026
@hayleypark hayleypark reopened this Apr 24, 2026
@hayleypark hayleypark closed this Apr 24, 2026
@hayleypark

Copy link
Copy Markdown
Author

Duplicate created: #115

@adiadd
adiadd deleted the feat/universal_aggregate_metrics_cherry_pick branch July 21, 2026 22:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants