Matrix bot with E2EE (matrix-sdk 0.9) that passively archives all messages to OpenSearch and responds to queries via Mistral AI with function calling tools. Core systems: - Archive: bulk OpenSearch indexer with batch/flush, edit/redaction handling, embedding pipeline passthrough - Brain: rule-based engagement evaluator (mentions, DMs, name invocations), LLM-powered spontaneous engagement, per-room conversation context windows, response delay simulation - Tools: search_archive, get_room_context, list_rooms, get_room_members registered as Mistral function calling tools with iterative tool loop - Personality: templated system prompt with Sol's librarian persona 47 unit tests covering config, evaluator, conversation windowing, personality templates, schema serialization, and search query building.
90 lines
2.8 KiB
Rust
90 lines
2.8 KiB
Rust
use chrono::Utc;
|
|
|
|
pub struct Personality {
|
|
template: String,
|
|
}
|
|
|
|
impl Personality {
|
|
pub fn new(system_prompt: String) -> Self {
|
|
Self {
|
|
template: system_prompt,
|
|
}
|
|
}
|
|
|
|
pub fn build_system_prompt(
|
|
&self,
|
|
room_name: &str,
|
|
members: &[String],
|
|
) -> String {
|
|
let date = Utc::now().format("%Y-%m-%d").to_string();
|
|
let members_str = members.join(", ");
|
|
|
|
self.template
|
|
.replace("{date}", &date)
|
|
.replace("{room_name}", room_name)
|
|
.replace("{members}", &members_str)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_date_substitution() {
|
|
let p = Personality::new("Today is {date}.".to_string());
|
|
let result = p.build_system_prompt("general", &[]);
|
|
let today = Utc::now().format("%Y-%m-%d").to_string();
|
|
assert_eq!(result, format!("Today is {today}."));
|
|
}
|
|
|
|
#[test]
|
|
fn test_room_name_substitution() {
|
|
let p = Personality::new("You are in {room_name}.".to_string());
|
|
let result = p.build_system_prompt("design-chat", &[]);
|
|
assert!(result.contains("design-chat"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_members_substitution() {
|
|
let p = Personality::new("Members: {members}".to_string());
|
|
let members = vec!["Alice".to_string(), "Bob".to_string(), "Carol".to_string()];
|
|
let result = p.build_system_prompt("room", &members);
|
|
assert_eq!(result, "Members: Alice, Bob, Carol");
|
|
}
|
|
|
|
#[test]
|
|
fn test_empty_members() {
|
|
let p = Personality::new("Members: {members}".to_string());
|
|
let result = p.build_system_prompt("room", &[]);
|
|
assert_eq!(result, "Members: ");
|
|
}
|
|
|
|
#[test]
|
|
fn test_all_placeholders() {
|
|
let template = "Date: {date}, Room: {room_name}, People: {members}".to_string();
|
|
let p = Personality::new(template);
|
|
let members = vec!["Sienna".to_string(), "Lonni".to_string()];
|
|
let result = p.build_system_prompt("studio", &members);
|
|
|
|
let today = Utc::now().format("%Y-%m-%d").to_string();
|
|
assert!(result.starts_with(&format!("Date: {today}")));
|
|
assert!(result.contains("Room: studio"));
|
|
assert!(result.contains("People: Sienna, Lonni"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_no_placeholders_passthrough() {
|
|
let p = Personality::new("Static prompt with no variables.".to_string());
|
|
let result = p.build_system_prompt("room", &["Alice".to_string()]);
|
|
assert_eq!(result, "Static prompt with no variables.");
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_same_placeholder() {
|
|
let p = Personality::new("{room_name} is great. I love {room_name}.".to_string());
|
|
let result = p.build_system_prompt("lounge", &[]);
|
|
assert_eq!(result, "lounge is great. I love lounge.");
|
|
}
|
|
}
|