Skip to content

feat(sdk): auto-calibrate scanner thresholds per backend - #65

Merged
tharindupr merged 2 commits into
c2siorg:mainfrom
Pranjal0410:feat/sentence-transformer-backend
Aug 2, 2026
Merged

feat(sdk): auto-calibrate scanner thresholds per backend#65
tharindupr merged 2 commits into
c2siorg:mainfrom
Pranjal0410:feat/sentence-transformer-backend

Conversation

@Pranjal0410

Copy link
Copy Markdown
Contributor

Sentence-transformer backend was already reachable (PR #60 review fix) but unusable — the scanner's default thresholds were calibrated for TF-IDF's inflated score range. Sentence-transformer produces lower but much cleaner scores, so everything was getting filtered out.

What changed

Auto-calibrate scanner config based on which backend is selected:

Setting TF-IDF Sentence-transformer
default_threshold 0.75 0.45
block_threshold 0.90 0.75
signal_threshold 0.85 0.50

User-provided threshold still wins over the per-backend default.

Head-to-head on 58 adversarial payloads

Backend Attacks caught False positives
TF-IDF 12/47 (26%) 1/11 (9%)
Sentence-transformer 20/47 (43%) 0/11 (0%)

67% more attacks caught. Zero false positives. The paraphrase case (ap-093) that TF-IDF scored at 0.81 now produces clean signals at 0.64 with no benign overlap.

Why the scores are lower but better

TF-IDF matches surface words. "What is the weather" scores 0.83 because it shares "what is" with "what is your system prompt." Sentence-transformer understands meaning — weather question scores 0.14, attack paraphrase scores 0.64. The gap is what matters, not the absolute number.

109/109 tests passing.

Sentence-transformer: 20/47 attacks caught, 0 false positives. TF-IDF: 12/47, 1 false positive. 67% more attacks, zero noise.
@tharindupr

Copy link
Copy Markdown
Collaborator

Good improvement overall.

One bug in the threshold override logic:

if semantic_signal_threshold != 0.85:
self._semantic_signal_threshold = semantic_signal_threshold
else:
self._semantic_signal_threshold = default_signal_threshold

This uses 0.85 as a sentinel to detect "user didn't pass a value", but there's no way to distinguish that from a user who explicitly passes semantic_signal_threshold=0.85. If someone migrates a TF-IDF config to sentence-transformer and keeps 0.85 in their config file, their value gets silently replaced with 0.50.

Fix: change the constructor default to None and only fall back when it's None:

constructor signature

semantic_signal_threshold: float | None = None,

threshold resolution

self._semantic_signal_threshold = semantic_signal_threshold if semantic_signal_threshold is not None else default_s

Avoids conflating 'user passed 0.85 explicitly' with 'user didn't pass anything'. Reported by @tharindupr.
@Pranjal0410

Copy link
Copy Markdown
Contributor Author

fixed @tharindupr, thanks for the catch. changed the default to None so explicit 0.85 is respected. 109/109 passing.

@tharindupr
tharindupr merged commit 429ed4d into c2siorg:main Aug 2, 2026
2 checks passed
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.

2 participants