Skip to content

feat(backends): Add native Hugging Face transformers backend resolver - #168

Merged
Khanz9664 merged 9 commits into
Khanz9664:mainfrom
AaronProbha18:feat/transformers-resolver
Jul 20, 2026
Merged

Khanz9664 merged 9 commits into
Khanz9664:mainfrom
AaronProbha18:feat/transformers-resolver

Conversation

@AaronProbha18

@AaronProbha18 AaronProbha18 commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a native backend resolver so TrustLens.analyze() can audit transformers.TextClassificationPipeline models directly, passing raw string inputs instead of requiring users to manually extract logits, apply softmax, align labels, and convert everything to NumPy arrays. This mirrors the existing native resolvers for sklearn, xgboost, lightgbm, and catboost.

from trustlens import analyze

report = analyze(pipeline, texts, labels)

Related Issue

Type of Change

Please check the type of change:

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • 🚀 New feature (non-breaking change which adds functionality)
  • 📝 Documentation update (changes to docs or examples)
  • 🧹 Maintenance (refactoring, code cleanup, dependencies)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to not work as expected)

Changes Made

  • trustlens/backends/huggingface.py (new) - resolve() implementation matching sklearn.py's signature. Validates the model is a TextClassificationPipeline, runs it over raw text (unless y_prob is already supplied), parses the {'label', 'score'} output into a rectangular (n_samples, n_classes) probability matrix, and derives predictions. Column order is deterministic, sourced from the pipeline's model.config.id2label whenever available. y_pred is mapped back to semantic string labels (via id2label), not left as raw ordinal indices - mirrors sklearn.py's classes_arr[y_pred_indices] pattern and the existing raw-xgboost.Booster class_labels mapping, so equality-based downstream metrics (misclassification_summary, class_imbalance_report, subgroup_performance, ...) work correctly against a naturally string-labeled y_true. All transformers imports are inside resolve(), following the existing lazy-import convention.
  • trustlens/backends/registry.py - "transformers": "huggingface" added to FRAMEWORK_MAPPING, "huggingface" added to IMPLEMENTED_RESOLVERS, lazy-import dispatch branch added to get_resolver(). No other logic touched.
  • trustlens/backends/types.py - unchanged; resolver reuses PredictionBundle/UnsupportedModelError as-is.
  • pyproject.toml - new standalone transformers extra (single-package, matching the xgboost/lightgbm/catboost convention), added to full, reuses the existing torch extra rather than duplicating it. Install via pip install trustlens[nlp].
  • tests/test_backend_huggingface.py (new) - detection, basic resolver, end-to-end analyze() integration, manual override, and rejection-of-non-classification-pipeline tests, mirroring test_backend_xgboost.py's structure. All mocked (subclassed real pipeline classes with __init__ bypassed), no network access, no real model weights.

Testing

  • All unit tests pass locally.
  • Added new tests for the changes made.
pytest tests\test_backend_huggingface.py -v
tests/test_backend_huggingface.py::test_huggingface_detection PASSED
tests/test_backend_huggingface.py::test_huggingface_resolver_basic PASSED
tests/test_backend_huggingface.py::test_huggingface_integration_analyze PASSED
tests/test_backend_huggingface.py::test_huggingface_manual_override PASSED
tests/test_backend_huggingface.py::test_huggingface_non_classification_pipeline_rejection PASSED

Pre-submit Checklist

Before submitting, please ensure you have:

  • Run pre-commit run --all-files and all hooks passed.
  • Verified that all tests pass (make test).
  • Updated the CHANGELOG.md if applicable.
  • Verified that documentation has been updated for new features.

Notes for Reviewers

While building out the integration test, I ran into two behaviors in the core pipeline/metrics that seem unrelated to this backend but affect any framework. Not fixed here - flagging to check whether they're intentional or worth separate issues:

1. Binary calibration crashes on string y_true.
In trustlens/core/pipeline.py::_run_analysis_pipeline, the multiclass calibration branch (y_prob.shape[1] > 2) encodes y_true via _encode_labels_for_probability_columns before computing anything. The binary branch (shape[1] == 2) does not - it passes y_true straight into brier_score/expected_calibration_error/maximum_calibration_error/reliability_curve, all of which do np.asarray(y_true, dtype=float). A binary y_true like ["POSITIVE", "NEGATIVE"] raises ValueError: could not convert string to float. Is binary calibration expected to require pre-encoded 0/1 labels, or should the binary path also route through class_labels encoding like the multiclass path does?

2. confidence_gap divides by an empty set on 0%-accuracy predictions.
In trustlens/metrics/failure.py:

gap = float(correct_conf.mean() - incorrect_conf.mean()) if len(incorrect_conf) > 0 else 0.0

This only guards against incorrect_conf being empty (100% correct). It doesn't guard against correct_conf being empty (100% wrong) - .mean() on an empty array returns NaN, which propagates through to compute_trust_score's int(round(np.clip(raw_score, 0, 100))) and raises ValueError: cannot convert float NaN to integer. Net effect: any model with 0% accuracy on the eval set crashes analyze() entirely, regardless of framework or label type. Is this expected, or should this degrade gracefully the same way the len(incorrect_conf) > 0 branch already does for the opposite extreme?

Summary by CodeRabbit

  • New Features
    • Added support for Hugging Face Transformers text-classification pipelines, producing standardized class labels and probability outputs in analysis reports.
    • Added an nlp installation extra (and updated full) to include Transformers (transformers>=4.30).
  • Bug Fixes
    • Improved backend detection and consistent label ordering, including fallback behavior when label mappings are missing.
    • Added robust handling for caller-provided y_pred/y_prob (including 1D and 2D probability inputs).
  • Tests
    • Expanded Hugging Face backend tests for detection, resolution, normalization, and error cases.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AaronProbha18, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e365ec0-79b1-4c4e-a697-cb03a357029e

📥 Commits

Reviewing files that changed from the base of the PR and between ad653a1 and 6fa6080.

📒 Files selected for processing (1)
  • trustlens/backends/huggingface.py
📝 Walkthrough

Walkthrough

TrustLens adds Hugging Face text-classification pipeline support, including dependency extras, framework detection and dispatch, prediction resolution, metadata generation, and integration tests.

Changes

Hugging Face backend

Layer / File(s) Summary
Dependency and resolver registration
pyproject.toml, trustlens/backends/registry.py
Adds the nlp extra, extends full with transformers, and registers Hugging Face framework detection and dispatch.
Pipeline resolution and bundle construction
trustlens/backends/huggingface.py
Validates text-classification pipelines, parses probability outputs, derives class labels and predictions, and returns Hugging Face metadata.
Detection and integration tests
tests/test_backend_huggingface.py
Tests detection, resolution, analysis integration, manual prediction overrides, probability fallbacks, and rejection of non-text-classification pipelines.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

  • TrustLens issue 123 — Directly covers the Hugging Face backend resolver, registry integration, optional dependency, and tests added here.

Sequence Diagram(s)

sequenceDiagram
  participant Pipeline as Hugging Face Pipeline
  participant Resolver as huggingface.resolve
  participant Report as TrustReport
  Pipeline->>Resolver: Provide text inputs and classification outputs
  Resolver->>Resolver: Parse probabilities and derive labels
  Resolver->>Report: Return PredictionBundle metadata
  Report-->>Resolver: Preserve resolved or manual predictions
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Clearly summarizes the main change: adding a native Hugging Face transformers backend resolver.
Description check ✅ Passed Includes the required template sections and is mostly complete, with only minor gaps like the unfilled related issue link.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@llamapreview llamapreview Bot 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.

AI Code Review by LlamaPReview

🎯 TL;DR & Recommendation

Recommendation: Approve with suggestions
This PR adds a well-structured HuggingFace transformers backend resolver, closely following existing patterns. However, it exposes a pre-existing core pipeline bug in binary calibration that crashes on string labels, and has two untested code paths in the new resolver. See the table below for details.

🌟 Strengths

  • Solid test coverage with mocked pipelines and an integration test.
  • Lazy imports and optional dependency configuration correctly follow project conventions.
Priority File Category Impact Summary (≤12 words) Anchors
P2 trustlens/backends/huggingface.py Bug Exposes binary calibration crash on string labels path:trustlens/core/pipeline.py, symbol:_encode_labels_for_probability_columns
P2 trustlens/backends/huggingface.py Testing Untested fallback label order derivation path
P2 trustlens/backends/huggingface.py Testing No test for pre-supplied y_prob parameter

🔍 Notable Themes

  • Pre-existing core bug exposed: The new resolver returns string-encoded y_pred, triggering a crash in the binary calibration path of _run_analysis_pipeline (missing label encoding). This is a pre-existing issue that should be tracked separately.
  • Untested code paths: Two less common code paths in the resolver (fallback label ordering from output and pre-supplied y_prob) lack coverage, reducing confidence for edge cases.

💡 Have feedback? We'd love to hear it in our GitHub Discussions.
✨ This review was generated by LlamaPReview Advanced, which is free for all open-source projects. Learn more.

Comment thread trustlens/backends/huggingface.py
Comment thread trustlens/backends/huggingface.py
Comment thread trustlens/backends/huggingface.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
trustlens/backends/huggingface.py (1)

44-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Ordered-label derivation from id2label is duplicated three times.

The sorted(id2label) + [id2label[i] for i in ordered_ids] pattern appears in _parse_pipeline_output (lines 52-53), the y_pred branch of resolve (lines 139-140), and the resolved_class_labels branch (lines 157-158) — the last two compute the identical array twice when id2label is truthy.

♻️ Proposed refactor
+def _ordered_labels_from_id2label(id2label: dict) -> np.ndarray:
+    """Derive a stable, ordered label array from an id2label mapping."""
+    ordered_ids = sorted(id2label)
+    return np.asarray([id2label[i] for i in ordered_ids])
+
+
 def _parse_pipeline_output(model: Any, raw_output: list) -> tuple[np.ndarray, Optional[np.ndarray]]:
     ...
     if id2label:
-        ordered_ids = sorted(id2label)
-        ordered_labels = [id2label[i] for i in ordered_ids]
+        ordered_labels = list(_ordered_labels_from_id2label(id2label))
         label2col = {label: col for col, label in enumerate(ordered_labels)}

And in resolve, reuse the same helper (and cache the result once) instead of recomputing it for both y_pred and resolved_class_labels.

Also applies to: 84-178

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@trustlens/backends/huggingface.py` around lines 44 - 53, Centralize ordered
label derivation in a shared helper used by _parse_pipeline_output and resolve,
replacing each duplicated sorted(id2label) and label-list construction. In
resolve, compute the ordered labels once when id2label is available and reuse
that cached result for both the y_pred and resolved_class_labels branches,
preserving existing fallback behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@trustlens/backends/huggingface.py`:
- Around line 103-110: Update the ImportError message in the Hugging Face
dependency import block to reference the defined `nlp` extra instead of the
non-existent `transformers` extra, while preserving the existing torch
dependency hint and message context.

---

Nitpick comments:
In `@trustlens/backends/huggingface.py`:
- Around line 44-53: Centralize ordered label derivation in a shared helper used
by _parse_pipeline_output and resolve, replacing each duplicated
sorted(id2label) and label-list construction. In resolve, compute the ordered
labels once when id2label is available and reuse that cached result for both the
y_pred and resolved_class_labels branches, preserving existing fallback
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a601f962-d68d-4b41-8bc0-a80abffee85f

📥 Commits

Reviewing files that changed from the base of the PR and between a742909 and 124721b.

📒 Files selected for processing (4)
  • pyproject.toml
  • tests/test_backend_huggingface.py
  • trustlens/backends/huggingface.py
  • trustlens/backends/registry.py

Comment thread trustlens/backends/huggingface.py

@Khanz9664 Khanz9664 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@AaronProbha18

Great work on this PR! This is a well-designed addition that fits the existing backend architecture very naturally. I reviewed the implementation, the follow-up commits, and the automated review feedback.

The follow-up changes addressed all of the actionable review comments:

  • Fixed the installation instructions to use the correct trustlens[nlp] optional dependency.
  • Added coverage for the id2label fallback path, including the inconsistent-label failure case.
  • Added dedicated tests for the pre-supplied y_prob path (both 2D inputs and binary 1D normalization), ensuring the resolver correctly bypasses pipeline execution when probabilities are provided.

I also like that the resolver stays consistent with the existing backend design by:

  • using lazy imports,
  • returning a standard PredictionBundle,
  • preserving semantic class labels through id2label,
  • falling back to deterministic label ordering when id2label is unavailable, and
  • keeping Transformers as an optional dependency rather than expanding the core installation.

While testing this backend, you also uncovered two unrelated issues in the current TrustLens core:

  1. Binary calibration with string labels — the binary calibration path currently doesn't encode semantic labels before calling the calibration metrics, unlike the multiclass path. This is a pre-existing pipeline issue that this backend simply exposes.

  2. confidence_gap on 0%-accuracy predictions — the current implementation guards the 100%-accuracy case but not the symmetric 0%-accuracy case, allowing a NaN to propagate into Trust Score computation. Again, this is an existing core issue rather than something introduced by this PR.

I don't consider either of these blockers for the Hugging Face resolver itself, but I think they're worth tracking separately as follow-up issues.

From the backend perspective, this looks in very good shape.

One thing I'm still waiting on before approving is the GitHub Actions workflow. I haven't been able to verify the CI results yet, so I'll hold off on approval until the workflow completes successfully. Assuming the checks come back green, I don't have any further concerns with this implementation. Nice work!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_backend_huggingface.py`:
- Around line 194-205: Prevent shared mock-class mutation in both
test_huggingface_resolver_explicit_y_prob_2d and
test_huggingface_resolver_explicit_y_prob_1d_normalized: inject pytest’s
monkeypatch fixture and replace each direct model.__class__.__call__ assignment
with monkeypatch.setattr(model.__class__, "__call__", _boom). Apply this change
at tests/test_backend_huggingface.py lines 194-205 and 217-226.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7cc31921-d856-4ac8-8533-f170b8e6c3ea

📥 Commits

Reviewing files that changed from the base of the PR and between 124721b and 0c3e16d.

📒 Files selected for processing (2)
  • tests/test_backend_huggingface.py
  • trustlens/backends/huggingface.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • trustlens/backends/huggingface.py

Comment thread tests/test_backend_huggingface.py
@Khanz9664

Copy link
Copy Markdown
Owner

@AaronProbha18

Thanks! The implementation itself looks good from my review, but GitHub Actions is still reporting failures for Linting and Type Checking. Could you take a look at those CI failures? Once those are resolved and the checks are green, I'll be happy to give final approval.

@AaronProbha18

Copy link
Copy Markdown
Contributor Author

hey @Khanz9664
sorry for the delayed response was occupied with some work so couldn't lend time on this side.
There were some mypy tests failing, I have resolved all the issues and we are green and good to go now!

@Khanz9664 Khanz9664 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@AaronProbha18

Thanks for the update! I can see the linting issues are resolved now. However, the Type Checking workflow is still failing with one remaining mypy error:

trustlens/backends/huggingface.py:50:
Returning Any from function declared to return "ndarray[Any, dtype[Any]]"

Once that final type-checking issue is addressed and the workflow is green, I'll be happy to approve.

@AaronProbha18

Copy link
Copy Markdown
Contributor Author

@AaronProbha18

Thanks for the update! I can see the linting issues are resolved now. However, the Type Checking workflow is still failing with one remaining mypy error:

trustlens/backends/huggingface.py:50:
Returning Any from function declared to return "ndarray[Any, dtype[Any]]"

Once that final type-checking issue is addressed and the workflow is green, I'll be happy to approve.

Hey @Khanz9664
sorted this one out earlier this afternoon
can you please run the tests again to confirm if all the issues are resolved or not
Sorry for the repeated errors! (:

@Khanz9664 Khanz9664 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@AaronProbha18

Thanks for working through all the follow-up feedback and CI issues.

I pulled the latest changes into my review, and the remaining type-checking issue has now been resolved. With the implementation review complete and all GitHub Actions checks passing, I don't have any further concerns.

This backend integrates cleanly with the existing resolver architecture, keeps the optional dependency boundary intact, and preserves the standard PredictionBundle interface expected by the pipeline.

Nice work, and thanks for iterating on the feedback. Approved.

@Khanz9664
Khanz9664 merged commit fa5cfdc into Khanz9664:main Jul 20, 2026
11 checks passed
@Khanz9664

Copy link
Copy Markdown
Owner

@AaronProbha18

Thanks for the excellent work on this contribution and for sticking with the review process all the way through to the finish line.

This PR adds a substantial new capability to TrustLens by introducing native Hugging Face Transformers support while fitting cleanly into the backend abstraction introduced by the prediction resolver architecture. I particularly appreciated that the implementation remained consistent with the existing design principles:

  • keeping Transformers as an optional dependency rather than expanding the core installation,
  • using lazy imports,
  • returning the standard PredictionBundle interface expected by the pipeline,
  • preserving semantic labels through id2label where available,
  • providing a deterministic fallback when metadata is unavailable, and
  • supporting both automatic prediction resolution and user-supplied probability overrides.

Throughout the review process you were also very responsive to feedback. The follow-up commits addressed the installation documentation, expanded coverage for the id2label fallback behavior, added dedicated tests for the manual y_prob path (including binary normalization), and resolved the linting and type-checking issues until the entire GitHub Actions suite was green.

One thing I also appreciated was that, while validating this backend, it exposed two unrelated edge cases in the existing TrustLens core (binary calibration with semantic labels and the confidence_gap behavior for 0% accuracy). Those weren't introduced by this PR, but this implementation helped surface them. Fixing those independently ultimately made the core evaluation pipeline more robust for every backend, which is a nice example of how good feature work can improve the overall project.

After merging, I also pulled the latest main, created a clean virtual environment, installed the optional Hugging Face dependencies, reran the backend test suite locally, and exercised the integration against a real pretrained Transformers pipeline. Everything behaved as expected, including framework detection, resolver selection, semantic label handling, probability extraction, and end-to-end analysis.

Overall, this is a well-designed addition that integrates naturally with the existing architecture while maintaining backward compatibility and good test coverage. Thank you for your patience throughout the review iterations and for the quality of the implementation. Looking forward to your future contributions to TrustLens!

@Khanz9664 Khanz9664 linked an issue Jul 20, 2026 that may be closed by this pull request
Khanz9664 added a commit that referenced this pull request Jul 20, 2026
…ibutor to contributors block of the README.md (#171)
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.

[FEATURE]: Add native backend resolver for Hugging Face transformers

2 participants