Skip to main content

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}