vv_agent/prompt/builder/
system_builder.rs1use serde_json::{json, Value};
2
3use super::hash::sha256_hex;
4use super::options::BuiltSystemPrompt;
5use super::section::PromptSection;
6
7#[derive(Clone, Default)]
8pub struct SystemPromptBuilder {
9 sections: Vec<PromptSection>,
10}
11
12impl SystemPromptBuilder {
13 pub fn add_section(&mut self, section: PromptSection) {
14 self.sections.push(section);
15 }
16
17 pub fn build(&self) -> String {
18 self.sections
19 .iter()
20 .filter_map(|section| {
21 let value = section.get_value().trim().to_string();
22 (!value.is_empty()).then_some(value)
23 })
24 .collect::<Vec<_>>()
25 .join("\n\n")
26 }
27
28 pub fn metadata_sections(&self) -> Vec<Value> {
29 self.sections
30 .iter()
31 .filter_map(PromptSection::to_metadata)
32 .collect()
33 }
34
35 pub fn invalidate_all(&self) {
36 for section in &self.sections {
37 section.invalidate();
38 }
39 }
40
41 pub fn invalidate_volatile(&self) {
42 for section in &self.sections {
43 if !section.stable() {
44 section.invalidate();
45 }
46 }
47 }
48
49 pub fn stable_hash(&self) -> String {
50 let stable_text = self
51 .sections
52 .iter()
53 .filter(|section| section.stable())
54 .map(|section| section.get_value().trim().to_string())
55 .collect::<String>();
56 sha256_hex(stable_text.as_bytes())
57 }
58
59 pub fn build_result(&self) -> BuiltSystemPrompt {
60 let mut prompt_parts = Vec::new();
61 let mut sections = Vec::new();
62 let mut stable_parts = Vec::new();
63 for section in &self.sections {
64 let value = section.get_value().trim().to_string();
65 if value.is_empty() {
66 continue;
67 }
68 prompt_parts.push(value.clone());
69 sections.push(json!({
70 "id": section.id,
71 "text": value,
72 "stable": section.stable(),
73 }));
74 if section.stable() {
75 stable_parts.push(value);
76 }
77 }
78 BuiltSystemPrompt {
79 prompt: prompt_parts.join("\n\n"),
80 sections,
81 stable_hash: sha256_hex(stable_parts.join("").as_bytes()),
82 }
83 }
84}