Skip to main content

vv_agent/memory/session/
parse.rs

1use super::{SessionMemory, SessionMemoryEntry};
2use crate::memory::SessionMemoryOutputInvalidReason;
3
4impl SessionMemory {
5    pub fn parse_extraction_result(&self, raw: &str, cycle: i32) -> Vec<SessionMemoryEntry> {
6        self.parse_extraction_result_checked(raw, cycle)
7            .unwrap_or_default()
8    }
9
10    pub(crate) fn parse_extraction_result_checked(
11        &self,
12        raw: &str,
13        cycle: i32,
14    ) -> Result<Vec<SessionMemoryEntry>, SessionMemoryOutputInvalidReason> {
15        if raw.trim().is_empty() {
16            return Err(SessionMemoryOutputInvalidReason::EmptyOutput);
17        }
18        let Some(array_text) = extract_first_json_array(raw) else {
19            return Err(SessionMemoryOutputInvalidReason::JsonArrayMissing);
20        };
21        let Ok(value) = serde_json::from_str::<serde_json::Value>(array_text) else {
22            return Err(SessionMemoryOutputInvalidReason::JsonArrayMissing);
23        };
24        let Some(items) = value.as_array() else {
25            return Err(SessionMemoryOutputInvalidReason::JsonArrayMissing);
26        };
27        let entries = items
28            .iter()
29            .filter_map(|item| {
30                let object = item.as_object()?;
31                let content = object.get("content")?.as_str()?.trim();
32                if content.is_empty() {
33                    return None;
34                }
35                let category = object
36                    .get("category")
37                    .and_then(serde_json::Value::as_str)
38                    .unwrap_or("key_fact");
39                let importance = object
40                    .get("importance")
41                    .and_then(serde_json::Value::as_u64)
42                    .unwrap_or(5)
43                    .min(10) as u8;
44                Some(SessionMemoryEntry::new(
45                    category,
46                    content,
47                    cycle,
48                    importance.max(1),
49                ))
50            })
51            .collect::<Vec<_>>();
52        if entries.is_empty() {
53            Err(SessionMemoryOutputInvalidReason::NoValidEntries)
54        } else {
55            Ok(entries)
56        }
57    }
58}
59
60fn extract_first_json_array(raw: &str) -> Option<&str> {
61    let start = raw.find('[')?;
62    let mut depth = 0_i32;
63    let mut in_string = false;
64    let mut escaped = false;
65    for (offset, ch) in raw[start..].char_indices() {
66        if escaped {
67            escaped = false;
68            continue;
69        }
70        match ch {
71            '\\' if in_string => escaped = true,
72            '"' => in_string = !in_string,
73            '[' if !in_string => depth += 1,
74            ']' if !in_string => {
75                depth -= 1;
76                if depth == 0 {
77                    return Some(&raw[start..start + offset + ch.len_utf8()]);
78                }
79            }
80            _ => {}
81        }
82    }
83    None
84}