Skip to main content

loopsmith_core/config/
context.rs

1//! What each iteration is allowed to remember.
2//!
3//! A loop that re-sends every prior episode grows its own prompt without bound
4//! and bills accordingly; a loop that sends nothing produces the byte-identical
5//! prompt it already failed with. Neither is acceptable for a run measured in
6//! weeks, so each iteration is compressed to a summary and only the last few
7//! summaries are carried forward.
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12#[serde(deny_unknown_fields)]
13pub struct ContextPolicy {
14    /// How many previous iteration summaries a node's prompt carries.
15    ///
16    /// `0` disables carry-forward entirely. The default of 2 is enough for a
17    /// node to see what it just tried and what it tried before that, which is
18    /// what "do not repeat yourself" needs, without the prompt growing with the
19    /// run.
20    #[serde(default = "default_carry")]
21    pub carry_summaries: usize,
22    /// Provider id used to write the optional narrative half of a summary.
23    ///
24    /// Omit it and summaries are still written — the deterministic facts are
25    /// always there. This only buys prose, and prose costs tokens every
26    /// iteration, so it is opt-in.
27    #[serde(default)]
28    pub summary_provider: Option<String>,
29    /// Ceiling on the narrative, in characters. A summary that grows without
30    /// limit defeats the purpose of having one.
31    #[serde(default = "default_max_chars")]
32    pub max_summary_chars: usize,
33}
34
35impl Default for ContextPolicy {
36    fn default() -> Self {
37        Self {
38            carry_summaries: default_carry(),
39            summary_provider: None,
40            max_summary_chars: default_max_chars(),
41        }
42    }
43}
44
45fn default_carry() -> usize {
46    2
47}
48fn default_max_chars() -> usize {
49    1200
50}