bamboo_server/
prompt_defaults.rs1use std::path::{Path, PathBuf};
9
10const SYSTEM_PROMPT_FILE_NAME: &str = "system-prompt.md";
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum GlobalSystemPromptSource {
14 BambooFile,
15 BuiltinDefault,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct GlobalSystemPromptTemplate {
20 pub content: String,
21 pub source: GlobalSystemPromptSource,
22 pub path: Option<PathBuf>,
23}
24
25fn bamboo_system_prompt_file_path() -> PathBuf {
26 bamboo_infrastructure::paths::bamboo_dir().join(SYSTEM_PROMPT_FILE_NAME)
27}
28
29fn read_non_empty_prompt_file(path: &Path) -> Option<String> {
30 std::fs::read_to_string(path).ok().and_then(|content| {
31 let trimmed = content.trim();
32 if trimmed.is_empty() {
33 None
34 } else {
35 Some(trimmed.to_string())
36 }
37 })
38}
39
40fn builtin_default_prompt() -> String {
41 crate::app_state::DEFAULT_BASE_PROMPT.to_string()
42}
43
44fn resolve_with_paths(bamboo_path: &Path) -> GlobalSystemPromptTemplate {
45 if let Some(content) = read_non_empty_prompt_file(bamboo_path) {
46 return GlobalSystemPromptTemplate {
47 content,
48 source: GlobalSystemPromptSource::BambooFile,
49 path: Some(bamboo_path.to_path_buf()),
50 };
51 }
52
53 GlobalSystemPromptTemplate {
54 content: builtin_default_prompt(),
55 source: GlobalSystemPromptSource::BuiltinDefault,
56 path: None,
57 }
58}
59
60pub fn read_global_default_system_prompt_template() -> String {
66 resolve_global_default_system_prompt_template().content
67}
68
69pub fn resolve_global_default_system_prompt_template() -> GlobalSystemPromptTemplate {
71 let bamboo_path = bamboo_system_prompt_file_path();
72 resolve_with_paths(&bamboo_path)
73}
74
75#[cfg(test)]
76mod tests {
77 use super::{
78 read_global_default_system_prompt_template, resolve_with_paths, GlobalSystemPromptSource,
79 };
80 use std::fs;
81 use tempfile::tempdir;
82
83 #[test]
84 fn global_default_template_is_never_empty() {
85 assert!(!read_global_default_system_prompt_template()
86 .trim()
87 .is_empty());
88 }
89
90 #[test]
91 fn resolution_prefers_bamboo_file_when_present() {
92 let temp = tempdir().expect("temp dir");
93 let bamboo_prompt = temp.path().join("bamboo-system-prompt.md");
94
95 fs::write(&bamboo_prompt, "from bamboo").expect("write bamboo prompt");
96
97 let resolved = resolve_with_paths(&bamboo_prompt);
98 assert_eq!(resolved.content, "from bamboo");
99 assert_eq!(resolved.source, GlobalSystemPromptSource::BambooFile);
100 assert_eq!(resolved.path.as_deref(), Some(bamboo_prompt.as_path()));
101 }
102
103 #[test]
104 fn resolution_falls_back_to_builtin_when_bamboo_missing() {
105 let temp = tempdir().expect("temp dir");
106 let bamboo_prompt = temp.path().join("missing-bamboo-system-prompt.md");
107
108 let resolved = resolve_with_paths(&bamboo_prompt);
109 assert_eq!(resolved.source, GlobalSystemPromptSource::BuiltinDefault);
110 assert!(resolved.path.is_none());
111 assert!(!resolved.content.trim().is_empty());
112 }
113}