everruns_builtins/
framework_config.rs1use everruns_capability::{CapabilityRef, CapabilitySpec, IntoCapability};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7#[non_exhaustive]
8pub enum CompactionStrategy {
9 #[default]
11 Auto,
12 Native,
14 ObservationMasking,
16 Summarization,
18}
19
20impl CompactionStrategy {
21 fn as_str(self) -> &'static str {
22 match self {
23 Self::Auto => "auto",
24 Self::Native => "native",
25 Self::ObservationMasking => "observation_masking",
26 Self::Summarization => "summarization",
27 }
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq)]
39pub struct CompactionConfig {
40 strategy: CompactionStrategy,
41 proactive: bool,
42 budget_percent: f32,
43}
44
45impl CompactionConfig {
46 pub fn new() -> Self {
48 Self::default()
49 }
50
51 pub fn strategy(mut self, strategy: CompactionStrategy) -> Self {
53 self.strategy = strategy;
54 self
55 }
56
57 pub fn proactive(mut self, enabled: bool) -> Self {
59 self.proactive = enabled;
60 self
61 }
62
63 pub fn budget_percent(mut self, budget_percent: f32) -> Self {
67 self.budget_percent = budget_percent;
68 self
69 }
70}
71
72impl Default for CompactionConfig {
73 fn default() -> Self {
74 Self {
75 strategy: CompactionStrategy::Auto,
76 proactive: true,
77 budget_percent: 0.85,
78 }
79 }
80}
81
82impl IntoCapability for CompactionConfig {
83 fn into_capability(self) -> CapabilitySpec {
84 CapabilityRef::new(super::COMPACTION_CAPABILITY_ID)
85 .config(serde_json::json!({
86 "strategy": self.strategy.as_str(),
87 "proactive": self.proactive,
88 "budget_percent": self.budget_percent,
89 }))
90 .into()
91 }
92}
93
94#[derive(Clone, Debug, Default, PartialEq, Eq)]
96pub struct ToolSearch {
97 threshold: Option<usize>,
98 never_defer: Vec<String>,
99}
100
101impl ToolSearch {
102 pub fn automatic() -> Self {
105 Self::default()
106 }
107
108 pub fn threshold(mut self, threshold: usize) -> Self {
110 self.threshold = Some(threshold);
111 self
112 }
113
114 pub fn never_defer<I, S>(mut self, names: I) -> Self
116 where
117 I: IntoIterator<Item = S>,
118 S: Into<String>,
119 {
120 self.never_defer.extend(names.into_iter().map(Into::into));
121 self
122 }
123}
124
125impl IntoCapability for ToolSearch {
126 fn into_capability(self) -> CapabilitySpec {
127 let mut config = serde_json::Map::new();
128 if let Some(threshold) = self.threshold {
129 config.insert("threshold".to_string(), threshold.into());
130 }
131 if !self.never_defer.is_empty() {
132 config.insert("never_defer".to_string(), self.never_defer.into());
133 }
134 CapabilityRef::new(super::AUTO_TOOL_SEARCH_CAPABILITY_ID)
135 .config(serde_json::Value::Object(config))
136 .into()
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn typed_compaction_uses_the_stable_id_and_schema() {
146 let spec = CompactionConfig::new()
147 .strategy(CompactionStrategy::ObservationMasking)
148 .budget_percent(0.9)
149 .into_capability();
150 assert_eq!(spec.capability_ref().id(), "compaction");
151 let budget = spec.capability_ref().config_value()["budget_percent"]
152 .as_f64()
153 .unwrap();
154 assert!((budget - 0.9).abs() < 1e-6);
155 }
156
157 #[test]
158 fn typed_tool_search_uses_the_model_adaptive_capability() {
159 let spec = ToolSearch::automatic()
160 .threshold(12)
161 .never_defer(["read_file"])
162 .into_capability();
163 assert_eq!(spec.capability_ref().id(), "auto_tool_search");
164 assert_eq!(spec.capability_ref().config_value()["threshold"], 12);
165 }
166}