agent_trace/llm/
prompts.rs1use crate::llm::trace_insights::TraceDocument;
2use crate::types::DocType;
3
4const MAX_SNIPPET_CHARS: usize = 2000;
5
6#[derive(Debug, Clone)]
7pub struct DocSummary {
8 pub path: String,
9 pub doc_type: DocType,
10 pub content_snippet: String,
11}
12
13fn truncate(s: &str, max: usize) -> &str {
14 if s.len() <= max {
15 s
16 } else {
17 &s[..max]
18 }
19}
20
21pub fn summarize_change_prompt(path: &str, doc_type: &str, diff: &str) -> String {
22 format!(
23 "Summarize this diff of a {doc_type} document '{path}' in one sentence (max 20 words).\n\n{}",
24 truncate(diff, MAX_SNIPPET_CHARS)
25 )
26}
27
28pub fn synthesize_context_prompt(documents: &[DocSummary], updates: &[String]) -> String {
29 let mut docs_str = String::new();
30 for doc in documents {
31 docs_str.push_str(&format!(
32 "[{}] {}: {}\n",
33 doc.doc_type, doc.path, doc.content_snippet
34 ));
35 }
36 format!(
37 "Given these project documents and user updates, produce a concise context document \
38 covering: goals, status, key decisions, architecture, open questions.\n\
39 Start with '# Project Context'.\n\n\
40 Documents:\n{}\n\nUser updates:\n{}",
41 truncate(&docs_str, MAX_SNIPPET_CHARS),
42 truncate(&updates.join("\n"), MAX_SNIPPET_CHARS / 2)
43 )
44}
45
46pub fn summarize_session_prompt(session_id: &str, events: &[String]) -> String {
47 format!(
48 "Summarize this agent session in 2-3 sentences for a reconnecting agent.\n\
49 Session: {session_id}\n\nEvents:\n{}",
50 truncate(&events.join("\n"), MAX_SNIPPET_CHARS)
51 )
52}
53
54pub fn summarize_event_history_prompt(events: &str) -> String {
55 format!(
56 "Summarize this agent work history in one paragraph (4-6 sentences).\n\
57 Focus on themes and progress, not per-file enumeration.\n\n{}",
58 truncate(events, MAX_SNIPPET_CHARS)
59 )
60}
61
62pub fn update_running_summary_prompt(previous: &str, events: &str, plan: &str) -> String {
63 format!(
64 "Update the running project summary given the previous version and new events.\n\
65 Keep sections: Current Status, Recent Activity, Resume Here, Key Documents, Open Items.\n\
66 Under 800 words.\n\n\
67 Previous summary:\n{}\n\nNew events:\n{}\n\nPlan excerpt:\n{}",
68 truncate(previous, 3000),
69 truncate(events, 2000),
70 truncate(plan, 2000)
71 )
72}
73
74pub fn trace_to_doc_summaries(documents: &[TraceDocument]) -> Vec<DocSummary> {
75 documents
76 .iter()
77 .map(|d| DocSummary {
78 path: d.path.clone(),
79 doc_type: d.doc_type.clone(),
80 content_snippet: d.content_snippet.clone(),
81 })
82 .collect()
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn prompts_are_non_empty() {
91 assert!(!summarize_change_prompt("a.md", "plan", "+1").is_empty());
92 assert!(!update_running_summary_prompt("prev", "ev", "plan").is_empty());
93 assert!(!summarize_session_prompt("s1", &["event".into()]).is_empty());
94 }
95}