Store security risk scores as a snapshot (#38567)

## What changed

- Replace the single `category` and `score` fields in `SecurityRiskScore` with a
  deterministic map of category names to scores.
- Validate the full Guardian V2 classifier response before storing it as one
  thread extension value and, for non-ephemeral threads, one rollout item.
- Cover serialization and rollout loading with snapshots containing multiple
  score categories.

GitOrigin-RevId: 24525de691f96bd34ccfe04acab2f01aca309837
This commit is contained in:
jif authored and copyberry committed 2026-08-14 12:16:51 +00:00
1 parent 08c48e3700
commit aa905bb962
10 files changed
+41 -32

No files matched your search

+17 -16
View File
@@ -202,23 +202,24 @@ impl ToolLifecycleContributor for GuardianV2Extension {
.await
.map_err(|error| error.to_string())?;
let ephemeral = thread.config_snapshot().await.ephemeral;
for (category, value) in scores {
let score = value
.as_f64()
.filter(|score| (0.0..=1.0).contains(score))
.ok_or_else(|| format!("invalid security risk score for {category}"))?;
let score = SecurityRiskScore {
category: category.clone(),
score,
};
if !ephemeral {
thread
.append_rollout_items(&[RolloutItem::SecurityRiskScore(score.clone())])
.await
.map_err(|error| error.to_string())?;
}
thread.thread_extension_data().insert(score);
let scores = scores
.iter()
.map(|(category, value)| {
value
.as_f64()
.filter(|score| (0.0..=1.0).contains(score))
.map(|score| (category.clone(), score))
.ok_or_else(|| format!("invalid security risk score for {category}"))
})
.collect::<Result<_, _>>()?;
let score = SecurityRiskScore { scores };
if !ephemeral {
thread
.append_rollout_items(&[RolloutItem::SecurityRiskScore(score.clone())])
.await
.map_err(|error| error.to_string())?;
}
thread.thread_extension_data().insert(score);
Ok(())
}
.await;
@@ -1,3 +1,4 @@
use std::collections::BTreeMap;
use std::sync::Arc;
use std::time::Duration;
@@ -275,8 +276,7 @@ async fn contributor_samples_tool_calls_with_the_existing_luna_pool() -> Result<
assert_eq!(
score.as_ref(),
&SecurityRiskScore {
category: "action_risk".to_string(),
score: 0.25,
scores: BTreeMap::from([("action_risk".to_string(), 0.25)]),
}
);
test.codex.ensure_rollout_materialized().await;