pub type CompactStr = compact_str::CompactString;
pub mod tool_names {
pub const UNIFIED_SEARCH: &str = "unified_search";
pub const UNIFIED_EXEC: &str = "unified_exec";
pub const RUN_PTY_CMD: &str = "run_pty_cmd";
}
pub const fn canonical_tool_name(name: &str) -> &str {
name
}
pub const ERROR_DETECTION_PATTERNS: &[&str] = &[
"error",
"failed",
"exception",
"permission denied",
"not found",
"no such file",
"cannot",
"could not",
"panic",
"crash",
"unhandled",
"fatal",
"timeout",
"connection refused",
"access denied",
"stack trace",
"traceback",
"abort",
"terminate",
];
pub const NETWORK_ERROR_PATTERNS: &[&str] = &[
"connection",
"timeout",
"network",
"http",
"ssl",
"tls",
"dns",
"proxy",
];
pub const DEFAULT_VEC_CAPACITY: usize = 32;
pub const DEFAULT_HASHMAP_CAPACITY: usize = 16;
pub const DEFAULT_STRING_CAPACITY: usize = 256;
pub const MAX_SEARCH_RESULTS: usize = 5;
pub const MAX_LIST_ITEMS_SUMMARY: usize = 5;
pub const OVERFLOW_INDICATOR_PREFIX: &str = "[+]";
pub const OVERFLOW_INDICATOR_SUFFIX: &str = "more items]";
pub const MAX_FILE_SIZE_FOR_PROCESSING: usize = 100 * 1024 * 1024; pub const MAX_CONTEXT_LINES: usize = 20;
pub const MAX_OUTPUT_TOKENS: usize = 4000;
pub fn empty_object_schema() -> Value {
serde_json::json!({"type": "object"})
}
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fmt;
use std::time::SystemTime;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum ResultCompleteness {
Complete,
Partial,
Truncated,
Empty,
}
impl ResultCompleteness {
pub fn to_static_str(&self) -> &'static str {
match self {
Self::Complete => "complete",
Self::Partial => "partial",
Self::Truncated => "truncated",
Self::Empty => "empty",
}
}
}
impl fmt::Display for ResultCompleteness {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.to_static_str())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResultMetadata {
#[serde(default = "default_confidence")]
pub confidence: f32,
#[serde(default = "default_relevance")]
pub relevance: f32,
pub completeness: ResultCompleteness,
#[serde(default)]
pub result_count: usize,
#[serde(default)]
pub false_positive_likelihood: f32,
#[serde(default)]
pub content_types: Vec<String>,
#[serde(default)]
pub tool_metrics: HashMap<String, Value>,
}
fn default_confidence() -> f32 {
0.5
}
fn default_relevance() -> f32 {
0.5
}
impl Default for ResultMetadata {
fn default() -> Self {
Self {
confidence: 0.5,
relevance: 0.5,
completeness: ResultCompleteness::Complete,
result_count: 0,
false_positive_likelihood: 0.1,
content_types: vec![],
tool_metrics: HashMap::new(),
}
}
}
impl ResultMetadata {
#[inline]
pub fn quality_score(&self) -> f32 {
let weighted = (self.confidence * 0.4)
+ (self.relevance * 0.4)
+ (self.false_positive_likelihood * -0.2);
weighted.clamp(0.0, 1.0)
}
#[inline]
pub fn success(confidence: f32, relevance: f32) -> Self {
Self {
confidence: confidence.clamp(0.0, 1.0),
relevance: relevance.clamp(0.0, 1.0),
completeness: ResultCompleteness::Complete,
result_count: 1,
false_positive_likelihood: 0.05,
..Default::default()
}
}
#[inline]
pub fn empty() -> Self {
Self {
completeness: ResultCompleteness::Empty,
result_count: 0,
confidence: 1.0, ..Default::default()
}
}
pub fn error() -> Self {
Self {
confidence: 0.2,
completeness: ResultCompleteness::Empty,
..Default::default()
}
}
pub fn merge(&mut self, other: &ResultMetadata) {
self.result_count += other.result_count;
self.confidence = (self.confidence + other.confidence) / 2.0;
self.relevance = (self.relevance + other.relevance) / 2.0;
for ct in &other.content_types {
if !self.content_types.contains(ct) {
self.content_types.push(ct.clone());
}
}
self.tool_metrics.extend(
other
.tool_metrics
.iter()
.map(|(k, v)| (k.clone(), v.clone())),
);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnhancedToolResult {
pub value: Value,
pub metadata: ResultMetadata,
pub timestamp: u64,
pub tool_name: CompactStr,
#[serde(default)]
pub from_cache: bool,
}
impl EnhancedToolResult {
pub fn new(value: Value, metadata: ResultMetadata, tool_name: impl Into<CompactStr>) -> Self {
Self {
value,
metadata,
timestamp: SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
tool_name: tool_name.into(),
from_cache: false,
}
}
pub fn from_cache(
value: Value,
metadata: ResultMetadata,
tool_name: impl Into<CompactStr>,
) -> Self {
Self {
value,
metadata,
timestamp: SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
tool_name: tool_name.into(),
from_cache: true,
}
}
#[inline]
pub fn is_useful(&self) -> bool {
self.metadata.quality_score() > 0.3
}
#[inline]
pub fn is_high_quality(&self) -> bool {
self.metadata.quality_score() > 0.7
}
#[allow(clippy::cast_sign_loss)] pub fn to_summary(&self) -> String {
let quality = ((self.metadata.quality_score() * 100.0).round().max(0.0) as u32).min(100);
match self.metadata.completeness {
ResultCompleteness::Complete => {
format!(
"{} found {} results (confidence: {}%)",
self.tool_name, self.metadata.result_count, quality
)
}
ResultCompleteness::Partial => {
format!(
"{} found {} results (truncated, confidence: {}%)",
self.tool_name, self.metadata.result_count, quality
)
}
ResultCompleteness::Empty => {
format!("{} found no results", self.tool_name)
}
ResultCompleteness::Truncated => {
format!(
"{} found results (truncated due to size, confidence: {}%)",
self.tool_name, quality
)
}
}
}
}
pub trait ResultScorer {
fn score(&self, result: &Value) -> ResultMetadata;
fn tool_name(&self) -> &str;
}