neuron_context/persistent.rs
1//! Persistent context sections for structured system prompt construction.
2
3/// A single named section within a [`PersistentContext`].
4///
5/// Sections are rendered in ascending `priority` order (lower number = higher
6/// priority = rendered first).
7#[derive(Debug, Clone)]
8pub struct ContextSection {
9 /// Human-readable label for this section (used as a heading).
10 pub label: String,
11 /// The text content of this section.
12 pub content: String,
13 /// Render order — lower values appear first.
14 pub priority: usize,
15}
16
17/// Aggregates named context sections and renders them into a single string.
18///
19/// Sections are sorted by `priority` (ascending) before rendering. Use this to
20/// build structured system prompts from multiple independent sources.
21///
22/// # Example
23///
24/// ```
25/// use neuron_context::{PersistentContext, ContextSection};
26///
27/// let mut ctx = PersistentContext::new();
28/// ctx.add_section(ContextSection { label: "Role".into(), content: "You are helpful.".into(), priority: 0 });
29/// ctx.add_section(ContextSection { label: "Rules".into(), content: "Be concise.".into(), priority: 10 });
30/// let rendered = ctx.render();
31/// assert!(rendered.contains("Role"));
32/// assert!(rendered.contains("Rules"));
33/// ```
34#[derive(Debug, Default)]
35pub struct PersistentContext {
36 sections: Vec<ContextSection>,
37}
38
39impl PersistentContext {
40 /// Creates an empty `PersistentContext`.
41 #[must_use]
42 pub fn new() -> Self {
43 Self::default()
44 }
45
46 /// Adds a section to this context.
47 pub fn add_section(&mut self, section: ContextSection) {
48 self.sections.push(section);
49 }
50
51 /// Renders all sections into a single string, sorted by `priority`.
52 ///
53 /// Each section is formatted as:
54 /// ```text
55 /// ## <label>
56 /// <content>
57 /// ```
58 #[must_use]
59 pub fn render(&self) -> String {
60 let mut sorted = self.sections.clone();
61 sorted.sort_by_key(|s| s.priority);
62
63 sorted
64 .into_iter()
65 .map(|s| format!("## {}\n{}", s.label, s.content))
66 .collect::<Vec<_>>()
67 .join("\n\n")
68 }
69}