rig_memory_policy/
metadata.rs1use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum FrameKind {
20 DemotedMessage,
22 CompactionSummary,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37pub struct FrameMetadata {
38 pub schema_version: u8,
40 pub kind: FrameKind,
42 pub conversation_id: String,
44 pub chat_role: String,
46 pub dedup_key: String,
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub scope: Option<String>,
51}
52
53impl FrameMetadata {
54 pub fn try_from_map(
57 map: &std::collections::BTreeMap<String, String>,
58 ) -> Result<Self, serde_json::Error> {
59 let mut obj = serde_json::Map::new();
60 #[allow(clippy::collapsible_if)]
61 for (k, v) in map {
62 if let Ok(num) = v.parse::<u8>() {
63 if k == "schema_version" {
64 obj.insert(k.clone(), serde_json::Value::Number(num.into()));
65 continue;
66 }
67 }
68 obj.insert(k.clone(), serde_json::Value::String(v.clone()));
69 }
70 serde_json::from_value(serde_json::Value::Object(obj))
71 }
72
73 pub fn into_map(self) -> std::collections::BTreeMap<String, String> {
75 let mut map = std::collections::BTreeMap::new();
76 map.insert(
77 "schema_version".to_string(),
78 self.schema_version.to_string(),
79 );
80 map.insert("kind".to_string(), self.kind.as_str().to_string());
81 map.insert("conversation_id".to_string(), self.conversation_id);
82 map.insert("chat_role".to_string(), self.chat_role);
83 map.insert("dedup_key".to_string(), self.dedup_key);
84 if let Some(scope) = self.scope {
85 map.insert("scope".to_string(), scope);
86 }
87 map
88 }
89}
90
91impl FrameKind {
92 pub fn as_str(self) -> &'static str {
94 match self {
95 Self::DemotedMessage => "demoted_message",
96 Self::CompactionSummary => "compaction_summary",
97 }
98 }
99}
100
101#[cfg(test)]
102#[allow(clippy::unwrap_used, clippy::panic, clippy::indexing_slicing)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn round_trips_through_map() {
108 let original = FrameMetadata {
109 schema_version: 1,
110 kind: FrameKind::DemotedMessage,
111 conversation_id: "conv-1".to_string(),
112 chat_role: "user".to_string(),
113 dedup_key: "deadbeef".to_string(),
114 scope: Some("scope-a".to_string()),
115 };
116 let map = original.clone().into_map();
117 let parsed = FrameMetadata::try_from_map(&map).unwrap();
118 assert_eq!(parsed, original);
119 }
120
121 #[test]
122 fn scope_round_trips_when_absent() {
123 let original = FrameMetadata {
124 schema_version: 1,
125 kind: FrameKind::CompactionSummary,
126 conversation_id: "conv-2".to_string(),
127 chat_role: "assistant".to_string(),
128 dedup_key: "cafebabe".to_string(),
129 scope: None,
130 };
131 let map = original.clone().into_map();
132 assert!(!map.contains_key("scope"));
133 let parsed = FrameMetadata::try_from_map(&map).unwrap();
134 assert_eq!(parsed, original);
135 }
136}