entrenar/monitor/llm/
prompt.rs1use crate::monitor::llm::error::{LLMError, Result};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use sha2::{Digest, Sha256};
7use std::collections::HashMap;
8
9pub type PromptId = String;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct PromptVersion {
15 pub id: PromptId,
17 pub template: String,
19 pub variables: Vec<String>,
21 pub version: u32,
23 pub created_at: DateTime<Utc>,
25 pub sha256: String,
27 pub description: Option<String>,
29 pub tags: HashMap<String, String>,
31}
32
33impl PromptVersion {
34 pub fn new(template: &str, variables: Vec<String>) -> Self {
36 let sha256 = Self::compute_hash(template);
37 let id = sha256[..16].to_string(); Self {
40 id,
41 template: template.to_string(),
42 variables,
43 version: 1,
44 created_at: Utc::now(),
45 sha256,
46 description: None,
47 tags: HashMap::new(),
48 }
49 }
50
51 pub fn with_version(mut self, version: u32) -> Self {
53 self.version = version;
54 self
55 }
56
57 pub fn with_description(mut self, desc: &str) -> Self {
59 self.description = Some(desc.to_string());
60 self
61 }
62
63 pub fn with_tag(mut self, key: &str, value: &str) -> Self {
65 self.tags.insert(key.to_string(), value.to_string());
66 self
67 }
68
69 fn compute_hash(template: &str) -> String {
71 let mut hasher = Sha256::new();
72 hasher.update(template.as_bytes());
73 let result = hasher.finalize();
74 hex::encode(result)
75 }
76
77 pub fn render(&self, vars: &HashMap<String, String>) -> Result<String> {
79 let mut result = self.template.clone();
80 for var in &self.variables {
81 let placeholder = format!("{{{var}}}");
82 if let Some(value) = vars.get(var) {
83 result = result.replace(&placeholder, value);
84 } else {
85 return Err(LLMError::EvaluationFailed(format!("Missing variable: {var}")));
86 }
87 }
88 Ok(result)
89 }
90
91 pub fn extract_variables(template: &str) -> Vec<String> {
93 let mut vars = Vec::new();
94 let mut in_var = false;
95 let mut current = String::new();
96
97 for c in template.chars() {
98 match c {
99 '{' => {
100 in_var = true;
101 current.clear();
102 }
103 '}' if in_var => {
104 if !current.is_empty() && !vars.contains(¤t) {
105 vars.push(current.clone());
106 }
107 in_var = false;
108 }
109 _ if in_var => {
110 current.push(c);
111 }
112 _non_brace => {}
113 }
114 }
115 vars
116 }
117}