fix: integrate LLM verifier into proxy handler, remove dead dependency

- Integrate LlmVerifier into scan_and_redact(): when classification is
  sensitive_partial, split entities by confidence, verify low-confidence
  ones via RKLLM, rebuild summary from verified entities
- Pass verifier from proxy.rs process_upload() to scanner
- Remove #[allow(dead_code)] from llm_verifier field
- Remove unused multer dependency from Cargo.toml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 06:30:46 +00:00
co-authored by Claude Opus 4.6
parent ec464e3d9e
commit e069249512
3 changed files with 35 additions and 8 deletions
-3
View File
@@ -26,9 +26,6 @@ axum = { version = "0.8", features = ["multipart"] }
# HTTP client for info-privacy-rs, RKLLM, KVM backend
reqwest = { version = "0.12", features = ["json", "multipart"] }
# Multipart parsing
multer = "3"
# MariaDB audit logging
sqlx = { version = "0.8", features = ["mysql", "runtime-tokio", "chrono"] }
+1 -1
View File
@@ -33,7 +33,6 @@ pub struct ProxyState {
pub info_privacy_url: String,
/// LLM verifier for false-positive reduction (used when scanner
/// returns sensitive_partial classification)
#[allow(dead_code)]
pub llm_verifier: Arc<LlmVerifier>,
/// Audit logger (optional — None if MariaDB is unavailable)
pub audit: Option<AuditLogger>,
@@ -194,6 +193,7 @@ impl PrivacyProxyHandler {
&self.state.info_privacy_url,
&f.filename,
&f.data,
Some(self.state.llm_verifier.as_ref()),
)
.await;
+34 -4
View File
@@ -76,7 +76,8 @@ struct AnalyzeResponse {
/// Steps:
/// 1. Call POST /api/v1/analyze with the file as multipart upload
/// 2. Parse the detection report
/// 3. If PII found, call POST /api/v1/redact to get redacted file bytes
/// 3. If sensitive_partial AND verifier provided: LLM-verify low-confidence entities
/// 4. If PII found, call POST /api/v1/redact to get redacted file bytes
///
/// Returns `Err` on network/HTTP errors (caller decides whether to block or allow).
pub async fn scan_and_redact(
@@ -84,6 +85,7 @@ pub async fn scan_and_redact(
base_url: &str,
filename: &str,
data: &[u8],
verifier: Option<&crate::llm_verifier::LlmVerifier>,
) -> Result<ScanResult, ScanError> {
let original_hash = hex_sha256(data);
@@ -128,9 +130,37 @@ pub async fn scan_and_redact(
});
}
// Use the summary from the report (LLM verification is done by llm_verifier
// at a higher level if needed)
let summary = report.summary;
// LLM verification for sensitive_partial with low-confidence entities
let summary = if report.classification == "sensitive_partial" {
if let Some(v) = verifier {
let (high_conf, low_conf) = split_by_confidence(&report.entities);
if !low_conf.is_empty() {
let context = if report.doc_id.is_empty() {
filename.to_string()
} else {
report.doc_id.clone()
};
let verified = v.verify_detections(&context, &low_conf).await;
let mut all_verified = high_conf;
all_verified.extend(verified);
if all_verified.is_empty() {
return Ok(ScanResult {
pii_found: false,
pii_types: HashMap::new(),
redacted_bytes: None,
original_hash,
});
}
build_summary(&all_verified)
} else {
report.summary
}
} else {
report.summary
}
} else {
report.summary
};
// Step 2: Redact
let redact_types: Vec<String> = summary.keys().cloned().collect();