1pub type CompactStr = compact_str::CompactString;
21
22pub mod tool_names {
29 pub const CODE_SEARCH: &str = "code_search";
31 pub const EXEC_COMMAND: &str = "exec_command";
33}
34
35pub const fn canonical_tool_name(name: &str) -> &str {
39 name
40}
41
42pub const ERROR_DETECTION_PATTERNS: &[&str] = &[
48 "error",
49 "failed",
50 "exception",
51 "permission denied",
52 "not found",
53 "no such file",
54 "cannot",
55 "could not",
56 "panic",
57 "crash",
58 "unhandled",
59 "fatal",
60 "timeout",
61 "connection refused",
62 "access denied",
63 "stack trace",
64 "traceback",
65 "abort",
66 "terminate",
67];
68
69pub const NETWORK_ERROR_PATTERNS: &[&str] = &["connection", "timeout", "network", "http", "ssl", "tls", "dns", "proxy"];
71
72pub const DEFAULT_VEC_CAPACITY: usize = 32;
74pub const DEFAULT_HASHMAP_CAPACITY: usize = 16;
75pub const DEFAULT_STRING_CAPACITY: usize = 256;
76
77pub const MAX_SEARCH_RESULTS: usize = 5;
79pub const MAX_LIST_ITEMS_SUMMARY: usize = 5;
80pub const OVERFLOW_INDICATOR_PREFIX: &str = "[+]";
81pub const OVERFLOW_INDICATOR_SUFFIX: &str = "more items]";
82
83pub const MAX_FILE_SIZE_FOR_PROCESSING: usize = 100 * 1024 * 1024; pub const MAX_CONTEXT_LINES: usize = 20;
86pub const MAX_OUTPUT_TOKENS: usize = 4000;
87
88pub fn empty_object_schema() -> Value {
91 serde_json::json!({"type": "object"})
92}
93
94use hashbrown::HashMap;
99use serde::{Deserialize, Serialize};
100use serde_json::Value;
101use std::fmt;
102use std::time::SystemTime;
103
104#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
106pub enum ResultCompleteness {
107 Complete,
109 Partial,
111 Truncated,
113 Empty,
115}
116
117impl ResultCompleteness {
118 pub fn to_static_str(&self) -> &'static str {
120 match self {
121 Self::Complete => "complete",
122 Self::Partial => "partial",
123 Self::Truncated => "truncated",
124 Self::Empty => "empty",
125 }
126 }
127}
128
129impl fmt::Display for ResultCompleteness {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 f.write_str(self.to_static_str())
132 }
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct ResultMetadata {
138 #[serde(default = "default_confidence")]
140 pub confidence: f32,
141
142 #[serde(default = "default_relevance")]
144 pub relevance: f32,
145
146 pub completeness: ResultCompleteness,
148
149 #[serde(default)]
151 pub result_count: usize,
152
153 #[serde(default)]
155 pub false_positive_likelihood: f32,
156
157 #[serde(default)]
159 pub content_types: Vec<String>,
160
161 #[serde(default)]
163 pub tool_metrics: HashMap<String, Value>,
164}
165
166fn default_confidence() -> f32 {
167 0.5
168}
169
170fn default_relevance() -> f32 {
171 0.5
172}
173
174impl Default for ResultMetadata {
175 fn default() -> Self {
176 Self {
177 confidence: 0.5,
178 relevance: 0.5,
179 completeness: ResultCompleteness::Complete,
180 result_count: 0,
181 false_positive_likelihood: 0.1,
182 content_types: vec![],
183 tool_metrics: HashMap::new(),
184 }
185 }
186}
187
188impl ResultMetadata {
189 #[inline]
191 pub fn quality_score(&self) -> f32 {
192 let weighted = (self.confidence * 0.4) + (self.relevance * 0.4) + (self.false_positive_likelihood * -0.2);
193 weighted.clamp(0.0, 1.0)
194 }
195
196 #[inline]
198 pub fn success(confidence: f32, relevance: f32) -> Self {
199 Self {
200 confidence: confidence.clamp(0.0, 1.0),
201 relevance: relevance.clamp(0.0, 1.0),
202 completeness: ResultCompleteness::Complete,
203 result_count: 1,
204 false_positive_likelihood: 0.05,
205 ..Default::default()
206 }
207 }
208
209 #[inline]
211 pub fn empty() -> Self {
212 Self {
213 completeness: ResultCompleteness::Empty,
214 result_count: 0,
215 confidence: 1.0, ..Default::default()
217 }
218 }
219
220 pub fn error() -> Self {
222 Self {
223 confidence: 0.2,
224 completeness: ResultCompleteness::Empty,
225 ..Default::default()
226 }
227 }
228
229 pub fn merge(&mut self, other: &ResultMetadata) {
231 self.result_count += other.result_count;
232 self.confidence = (self.confidence + other.confidence) / 2.0;
233 self.relevance = (self.relevance + other.relevance) / 2.0;
234
235 for ct in &other.content_types {
237 if !self.content_types.contains(ct) {
238 self.content_types.push(ct.clone());
239 }
240 }
241
242 self.tool_metrics
244 .extend(other.tool_metrics.iter().map(|(k, v)| (k.clone(), v.clone())));
245 }
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct EnhancedToolResult {
251 pub value: Value,
253
254 pub metadata: ResultMetadata,
256
257 pub timestamp: u64,
259
260 pub tool_name: CompactStr,
262
263 #[serde(default)]
265 pub from_cache: bool,
266}
267
268impl EnhancedToolResult {
269 pub fn new(value: Value, metadata: ResultMetadata, tool_name: impl Into<CompactStr>) -> Self {
270 Self {
271 value,
272 metadata,
273 timestamp: SystemTime::now()
274 .duration_since(SystemTime::UNIX_EPOCH)
275 .unwrap_or_default()
276 .as_secs(),
277 tool_name: tool_name.into(),
278 from_cache: false,
279 }
280 }
281
282 pub fn from_cache(value: Value, metadata: ResultMetadata, tool_name: impl Into<CompactStr>) -> Self {
283 Self {
284 value,
285 metadata,
286 timestamp: SystemTime::now()
287 .duration_since(SystemTime::UNIX_EPOCH)
288 .unwrap_or_default()
289 .as_secs(),
290 tool_name: tool_name.into(),
291 from_cache: true,
292 }
293 }
294
295 #[inline]
297 pub fn is_useful(&self) -> bool {
298 self.metadata.quality_score() > 0.3
299 }
300
301 #[inline]
303 pub fn is_high_quality(&self) -> bool {
304 self.metadata.quality_score() > 0.7
305 }
306
307 #[allow(clippy::cast_sign_loss)] pub fn to_summary(&self) -> String {
310 let quality = ((self.metadata.quality_score() * 100.0).round().max(0.0) as u32).min(100);
311 match self.metadata.completeness {
312 ResultCompleteness::Complete => {
313 format!("{} found {} results (confidence: {}%)", self.tool_name, self.metadata.result_count, quality)
314 }
315 ResultCompleteness::Partial => {
316 format!(
317 "{} found {} results (truncated, confidence: {}%)",
318 self.tool_name, self.metadata.result_count, quality
319 )
320 }
321 ResultCompleteness::Empty => {
322 format!("{} found no results", self.tool_name)
323 }
324 ResultCompleteness::Truncated => {
325 format!("{} found results (truncated due to size, confidence: {}%)", self.tool_name, quality)
326 }
327 }
328 }
329}
330
331pub trait ResultScorer {
333 fn score(&self, result: &Value) -> ResultMetadata;
335
336 fn tool_name(&self) -> &str;
338}