Skip to main content

systemprompt_models/wire/canonical/
safety.rs

1//! Disjoint current-request and historical safety surfaces.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use super::CanonicalRequest;
7use super::request::flatten_part;
8
9impl CanonicalRequest {
10    pub fn safety_parts(&self, history: bool) -> Vec<(String, String)> {
11        let leaves = self.forwarded_surface.leaves();
12        if !leaves.is_empty() {
13            let newest = leaves
14                .iter()
15                .filter_map(|leaf| message_index(&leaf.path))
16                .max();
17            return leaves
18                .iter()
19                .filter(|leaf| {
20                    let historical =
21                        message_index(&leaf.path).is_some_and(|index| Some(index) != newest);
22                    historical == history
23                })
24                .map(|leaf| (leaf.path.clone(), leaf.value.clone()))
25                .collect();
26        }
27        let mut parts = Vec::new();
28        if !history && let Some(system) = self.system_text() {
29            parts.push(("system".to_owned(), system));
30        }
31        for (index, message) in self.messages.iter().enumerate() {
32            if (index + 1 < self.messages.len()) != history {
33                continue;
34            }
35            let mut text = String::new();
36            for part in &message.content {
37                flatten_part(&mut text, part);
38            }
39            if !text.is_empty() {
40                parts.push((format!("messages[{index}]"), text));
41            }
42        }
43        parts
44    }
45}
46
47fn message_index(path: &str) -> Option<usize> {
48    ["$.messages[", "$.contents[", "$.input["]
49        .iter()
50        .find_map(|prefix| path.strip_prefix(prefix))?
51        .split_once(']')?
52        .0
53        .parse()
54        .ok()
55}