forge_guardrails/tool_output/
config.rs1use std::fmt;
2use std::str::FromStr;
3
4pub const DEFAULT_MAX_OUTPUT_BYTES: usize = 64 * 1024;
6pub const DEFAULT_MAX_DEDUP_ENTRIES_PER_SESSION: usize = 128;
8pub const DEFAULT_MAX_DEDUP_SESSIONS: usize = 64;
10
11pub const LZW_DICTIONARY_HEADER: &str = "[Forge LZW Dictionary]";
13pub const REPAIR_DICTIONARY_HEADER: &str = "[Forge RePair Dictionary]";
15pub const DICTIONARY_MAX_DICT_SIZE: usize = 20;
17pub const DICTIONARY_MAX_INPUT_BYTES: usize = 50_000;
19pub const DICTIONARY_MIN_OCCURRENCES: usize = 3;
21pub const DICTIONARY_MIN_NET_SAVINGS_BYTES: usize = 32;
23pub const DICTIONARY_MIN_ENTRY_SAVINGS_BYTES: usize = 16;
25pub const DICTIONARY_MIN_NET_SAVINGS_PERCENT: usize = 3;
27
28pub fn is_dictionary_compressed_output(output: &str) -> bool {
30 output.starts_with(LZW_DICTIONARY_HEADER) || output.starts_with(REPAIR_DICTIONARY_HEADER)
31}
32
33pub fn dictionary_has_meaningful_savings(original_len: usize, savings: usize) -> bool {
35 savings >= DICTIONARY_MIN_NET_SAVINGS_BYTES
36 && savings.saturating_mul(100) / original_len.max(1) >= DICTIONARY_MIN_NET_SAVINGS_PERCENT
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
41pub enum ToolOutputCompressionMode {
42 Disabled,
44 Safe,
46 #[default]
48 Standard,
49 Aggressive,
51}
52
53impl ToolOutputCompressionMode {
54 pub fn as_str(self) -> &'static str {
56 match self {
57 Self::Disabled => "disabled",
58 Self::Safe => "safe",
59 Self::Standard => "standard",
60 Self::Aggressive => "aggressive",
61 }
62 }
63
64 pub fn enabled(self) -> bool {
66 self != Self::Disabled
67 }
68}
69
70impl fmt::Display for ToolOutputCompressionMode {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 f.write_str(self.as_str())
73 }
74}
75
76impl FromStr for ToolOutputCompressionMode {
77 type Err = String;
78
79 fn from_str(value: &str) -> Result<Self, Self::Err> {
80 match value.trim().to_ascii_lowercase().as_str() {
81 "disabled" | "off" | "false" | "none" => Ok(Self::Disabled),
82 "safe" => Ok(Self::Safe),
83 "standard" | "on" | "true" => Ok(Self::Standard),
84 "aggressive" => Ok(Self::Aggressive),
85 other => Err(format!(
86 "tool output compression must be disabled, safe, standard, or aggressive, got '{other}'"
87 )),
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
94pub enum ToolOutputCompressionMethod {
95 #[default]
97 Lzw,
98 Repair,
100 Auto,
102}
103
104impl ToolOutputCompressionMethod {
105 pub fn as_str(self) -> &'static str {
107 match self {
108 Self::Lzw => "lzw",
109 Self::Repair => "repair",
110 Self::Auto => "auto",
111 }
112 }
113}
114
115impl fmt::Display for ToolOutputCompressionMethod {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 f.write_str(self.as_str())
118 }
119}
120
121impl FromStr for ToolOutputCompressionMethod {
122 type Err = String;
123
124 fn from_str(value: &str) -> Result<Self, Self::Err> {
125 match value.trim().to_ascii_lowercase().as_str() {
126 "lzw" => Ok(Self::Lzw),
127 "repair" | "re-pair" => Ok(Self::Repair),
128 "auto" => Ok(Self::Auto),
129 other => Err(format!(
130 "tool output compression method must be lzw, repair, or auto, got '{other}'"
131 )),
132 }
133 }
134}
135
136#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct ToolOutputCompressionConfig {
139 pub mode: ToolOutputCompressionMode,
141 pub method: ToolOutputCompressionMethod,
143 pub redact_secrets: bool,
145 pub enable_dedup: bool,
147 pub enable_memo: bool,
149 pub session_id: Option<String>,
151 pub max_output_bytes: usize,
153 pub max_dedup_entries_per_session: usize,
155 pub max_dedup_sessions: usize,
157}
158
159impl Default for ToolOutputCompressionConfig {
160 fn default() -> Self {
161 Self {
162 mode: ToolOutputCompressionMode::Standard,
163 method: ToolOutputCompressionMethod::Lzw,
164 redact_secrets: true,
165 enable_dedup: true,
166 enable_memo: true,
167 session_id: None,
168 max_output_bytes: DEFAULT_MAX_OUTPUT_BYTES,
169 max_dedup_entries_per_session: DEFAULT_MAX_DEDUP_ENTRIES_PER_SESSION,
170 max_dedup_sessions: DEFAULT_MAX_DEDUP_SESSIONS,
171 }
172 }
173}
174
175impl ToolOutputCompressionConfig {
176 pub fn disabled() -> Self {
178 Self {
179 mode: ToolOutputCompressionMode::Disabled,
180 ..Self::default()
181 }
182 }
183
184 pub fn from_mode(mode: ToolOutputCompressionMode) -> Self {
186 Self {
187 mode,
188 ..Self::default()
189 }
190 }
191
192 pub fn enabled(&self) -> bool {
194 self.mode.enabled()
195 }
196}
197
198#[derive(Debug, Clone, PartialEq)]
200pub struct ToolOutputCompressionResult {
201 pub output: String,
203 pub before_tokens: i64,
205 pub after_tokens: i64,
207 pub saved_tokens: i64,
209 pub saved_pct: i64,
211 pub canonical_tool: String,
213 pub family: String,
215 pub mode: ToolOutputCompressionMode,
217 pub redacted: bool,
219 pub capped: bool,
221 pub deduped: bool,
223 pub memo_reused: bool,
225 pub memo_changed: bool,
227 pub strategies: Vec<String>,
229}