lean_ctx/core/config/
response_shaping.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7#[serde(default)]
8pub struct ResponseShapingConfig {
9 pub enabled: bool,
11 pub mode: String,
13 pub preamble: PreambleConfig,
14 pub code_repetition: CodeRepetitionConfig,
15 pub narration: NarrationConfig,
16 pub confirmation: ConfirmationConfig,
17}
18
19impl Default for ResponseShapingConfig {
20 fn default() -> Self {
21 Self {
22 enabled: true,
23 mode: "gentle".into(),
24 preamble: PreambleConfig::default(),
25 code_repetition: CodeRepetitionConfig::default(),
26 narration: NarrationConfig::default(),
27 confirmation: ConfirmationConfig::default(),
28 }
29 }
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34#[serde(default)]
35pub struct PreambleConfig {
36 pub strip: bool,
37 pub max_pattern_length: usize,
39}
40
41impl Default for PreambleConfig {
42 fn default() -> Self {
43 Self {
44 strip: true,
45 max_pattern_length: 50,
46 }
47 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
53#[serde(default)]
54pub struct CodeRepetitionConfig {
55 pub enabled: bool,
56 pub similarity_threshold: f64,
57 pub min_block_lines: usize,
58}
59
60impl Default for CodeRepetitionConfig {
61 fn default() -> Self {
62 Self {
63 enabled: false,
64 similarity_threshold: 0.8,
65 min_block_lines: 10,
66 }
67 }
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
72#[serde(default)]
73pub struct NarrationConfig {
74 pub compress_post_action: bool,
75 pub max_summary_tokens: usize,
76}
77
78impl Default for NarrationConfig {
79 fn default() -> Self {
80 Self {
81 compress_post_action: true,
82 max_summary_tokens: 30,
83 }
84 }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
89#[serde(default)]
90pub struct ConfirmationConfig {
91 pub strip_trailing: bool,
92}
93
94impl Default for ConfirmationConfig {
95 fn default() -> Self {
96 Self {
97 strip_trailing: true,
98 }
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn defaults_are_enabled_and_gentle() {
108 let cfg = ResponseShapingConfig::default();
109 assert!(cfg.enabled);
110 assert_eq!(cfg.mode, "gentle");
111 assert!(cfg.preamble.strip);
112 assert!(cfg.confirmation.strip_trailing);
113 }
114
115 #[test]
116 fn proposed_toml_shape_round_trips() {
117 let cfg: ResponseShapingConfig = toml::from_str(
118 r#"
119 enabled = true
120 mode = "aggressive"
121 [preamble]
122 max_pattern_length = 42
123 [code_repetition]
124 enabled = true
125 similarity_threshold = 0.9
126 min_block_lines = 12
127 [narration]
128 max_summary_tokens = 24
129 [confirmation]
130 strip_trailing = false
131 "#,
132 )
133 .expect("response-shaping TOML");
134 assert_eq!(cfg.mode, "aggressive");
135 assert_eq!(cfg.preamble.max_pattern_length, 42);
136 assert!(cfg.code_repetition.enabled);
137 assert_eq!(cfg.narration.max_summary_tokens, 24);
138 assert!(!cfg.confirmation.strip_trailing);
139 }
140
141 #[test]
142 fn omitted_nested_values_use_safe_defaults() {
143 let cfg: ResponseShapingConfig = toml::from_str("mode = \"off\"").unwrap();
144 assert!(!cfg.enabled || cfg.mode == "off");
145 assert_eq!(cfg.code_repetition, CodeRepetitionConfig::default());
146 assert_eq!(cfg.narration, NarrationConfig::default());
147 }
148}