vv_agent/prompt/builder/
section.rs1use std::sync::{Arc, Mutex};
2
3use serde_json::{Map, Value};
4
5#[derive(Clone)]
6pub struct PromptSection {
7 pub id: String,
8 stable: bool,
9 source: String,
10 cache_hint: Option<String>,
11 metadata: Map<String, Value>,
12 compute: Arc<dyn Fn() -> String + Send + Sync>,
13 cached_value: Arc<Mutex<Option<String>>>,
14}
15
16impl PromptSection {
17 pub fn new(
18 id: impl Into<String>,
19 compute: impl Fn() -> String + Send + Sync + 'static,
20 stable: bool,
21 ) -> Self {
22 Self {
23 id: id.into(),
24 stable,
25 source: String::new(),
26 cache_hint: None,
27 metadata: Map::new(),
28 compute: Arc::new(compute),
29 cached_value: Arc::new(Mutex::new(None)),
30 }
31 }
32
33 pub fn constant(id: impl Into<String>, text: impl Into<String>, stable: bool) -> Self {
34 let text = text.into();
35 Self::new(id, move || text.clone(), stable)
36 }
37
38 pub fn source(mut self, source: impl Into<String>) -> Self {
39 self.source = source.into();
40 self
41 }
42
43 pub fn cache_hint(mut self, cache_hint: impl Into<String>) -> Self {
44 self.cache_hint = Some(cache_hint.into());
45 self
46 }
47
48 pub fn metadata(mut self, key: impl Into<String>, value: Value) -> Self {
49 self.metadata.insert(key.into(), value);
50 self
51 }
52
53 pub fn get_value(&self) -> String {
54 if self.stable {
55 let mut cached = self.cached_value.lock().expect("prompt section cache");
56 if let Some(value) = cached.as_ref() {
57 return value.clone();
58 }
59 let value = (self.compute)();
60 *cached = Some(value.clone());
61 return value;
62 }
63 (self.compute)()
64 }
65
66 pub fn invalidate(&self) {
67 let mut cached = self.cached_value.lock().expect("prompt section cache");
68 *cached = None;
69 }
70
71 pub fn to_metadata(&self) -> Option<Value> {
72 let text = self.get_value().trim().to_string();
73 if text.is_empty() {
74 return None;
75 }
76 let mut payload = Map::from_iter([
77 ("id".to_string(), Value::String(self.id.clone())),
78 ("text".to_string(), Value::String(text)),
79 ("stable".to_string(), Value::Bool(self.stable)),
80 ]);
81 if !self.source.is_empty() {
82 payload.insert("source".to_string(), Value::String(self.source.clone()));
83 }
84 if let Some(cache_hint) = &self.cache_hint {
85 payload.insert("cache_hint".to_string(), Value::String(cache_hint.clone()));
86 }
87 if !self.metadata.is_empty() {
88 payload.insert("metadata".to_string(), Value::Object(self.metadata.clone()));
89 }
90 Some(Value::Object(payload))
91 }
92
93 pub fn stable(&self) -> bool {
94 self.stable
95 }
96}