use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tool {
pub name: String,
pub description: String,
pub input_schema: serde_json::Value,
}
impl Tool {
pub fn new(name: String, description: String, input_schema: serde_json::Value) -> Self {
Self {
name,
description,
input_schema,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ExecutionResult {
Success {
output: String,
stdout: String,
stderr: String,
execution_time_ms: u64,
},
Error {
message: String,
error_type: ErrorType,
stdout: String,
stderr: String,
},
Timeout {
elapsed_ms: u64,
partial_output: Option<String>,
},
SecurityViolation {
reason: String,
violation_type: SecurityViolationType,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ErrorType {
Syntax,
Runtime,
Permission,
Resource,
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SecurityViolationType {
FileSystemAccess,
NetworkAccess,
ProcessExecution,
MemoryLimit,
InfiniteLoop,
MaliciousCode,
}
#[derive(Debug, Clone)]
pub struct ResourceLimits {
pub max_cpu_percent: f32,
pub max_memory_mb: usize,
pub max_execution_time_ms: u64,
pub max_file_operations: usize,
pub max_network_requests: usize,
}
impl Default for ResourceLimits {
fn default() -> Self {
Self {
max_cpu_percent: 50.0,
max_memory_mb: 128,
max_execution_time_ms: 5000,
max_file_operations: 0,
max_network_requests: 0,
}
}
}
impl ResourceLimits {
pub fn restrictive() -> Self {
Self {
max_cpu_percent: 30.0,
max_memory_mb: 64,
max_execution_time_ms: 3000,
max_file_operations: 0,
max_network_requests: 0,
}
}
pub fn permissive() -> Self {
Self {
max_cpu_percent: 80.0,
max_memory_mb: 256,
max_execution_time_ms: 10000,
max_file_operations: 100,
max_network_requests: 10,
}
}
}
#[derive(Debug, Clone)]
pub struct SandboxConfig {
pub max_execution_time_ms: u64,
pub max_memory_mb: usize,
pub max_cpu_percent: u8,
pub allowed_paths: Vec<String>,
pub allowed_network: Vec<String>,
pub allow_network: bool,
pub allow_filesystem: bool,
pub allow_subprocesses: bool,
pub resource_limits: ResourceLimits,
pub process_uid: Option<u32>,
pub read_only_mode: bool,
}
impl Default for SandboxConfig {
fn default() -> Self {
let limits = ResourceLimits::default();
Self {
max_execution_time_ms: limits.max_execution_time_ms,
max_memory_mb: limits.max_memory_mb,
max_cpu_percent: limits.max_cpu_percent as u8,
allowed_paths: vec![],
allowed_network: vec![],
allow_network: false,
allow_filesystem: false,
allow_subprocesses: false,
resource_limits: limits,
process_uid: None,
read_only_mode: true,
}
}
}
impl SandboxConfig {
pub fn restrictive() -> Self {
let limits = ResourceLimits::restrictive();
Self {
max_execution_time_ms: limits.max_execution_time_ms,
max_memory_mb: limits.max_memory_mb,
max_cpu_percent: limits.max_cpu_percent as u8,
allowed_paths: vec![],
allowed_network: vec![],
allow_network: false,
allow_filesystem: false,
allow_subprocesses: false,
resource_limits: limits,
process_uid: None,
read_only_mode: true,
}
}
pub fn permissive() -> Self {
let limits = ResourceLimits::permissive();
Self {
max_execution_time_ms: limits.max_execution_time_ms,
max_memory_mb: limits.max_memory_mb,
max_cpu_percent: limits.max_cpu_percent as u8,
allowed_paths: vec!["/tmp".to_string()],
allowed_network: vec![],
allow_network: false,
allow_filesystem: true,
allow_subprocesses: false,
resource_limits: limits,
process_uid: None,
read_only_mode: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionContext {
pub task: String,
pub input: serde_json::Value,
pub env: HashMap<String, String>,
pub metadata: HashMap<String, String>,
}
impl ExecutionContext {
pub fn new(task: String, input: serde_json::Value) -> Self {
Self {
task,
input,
env: HashMap::new(),
metadata: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionStats {
pub total_executions: u64,
pub successful_executions: u64,
pub failed_executions: u64,
pub timeout_count: u64,
pub security_violations: u64,
pub avg_execution_time_ms: f64,
}
impl Default for ExecutionStats {
fn default() -> Self {
Self {
total_executions: 0,
successful_executions: 0,
failed_executions: 0,
timeout_count: 0,
security_violations: 0,
avg_execution_time_ms: 0.0,
}
}
}
impl ExecutionStats {
pub fn record_execution(&mut self, result: &ExecutionResult, duration_ms: u64) {
self.total_executions += 1;
match result {
ExecutionResult::Success { .. } => {
self.successful_executions += 1;
}
ExecutionResult::Error { .. } => {
self.failed_executions += 1;
}
ExecutionResult::Timeout { .. } => {
self.timeout_count += 1;
self.failed_executions += 1;
}
ExecutionResult::SecurityViolation { .. } => {
self.security_violations += 1;
self.failed_executions += 1;
}
}
let total = self.total_executions as f64;
self.avg_execution_time_ms =
(self.avg_execution_time_ms * (total - 1.0) + duration_ms as f64) / total;
}
pub fn success_rate(&self) -> f64 {
if self.total_executions == 0 {
0.0
} else {
(self.successful_executions as f64 / self.total_executions as f64) * 100.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_execution_stats() {
let mut stats = ExecutionStats::default();
assert_eq!(stats.success_rate(), 0.0);
let success = ExecutionResult::Success {
output: "result".to_string(),
stdout: "out".to_string(),
stderr: "".to_string(),
execution_time_ms: 100,
};
stats.record_execution(&success, 100);
assert_eq!(stats.total_executions, 1);
assert_eq!(stats.successful_executions, 1);
assert_eq!(stats.success_rate(), 100.0);
let error = ExecutionResult::Error {
message: "error".to_string(),
error_type: ErrorType::Runtime,
stdout: "".to_string(),
stderr: "err".to_string(),
};
stats.record_execution(&error, 50);
assert_eq!(stats.total_executions, 2);
assert_eq!(stats.successful_executions, 1);
assert_eq!(stats.failed_executions, 1);
assert_eq!(stats.success_rate(), 50.0);
}
#[test]
fn test_sandbox_config_defaults() {
let default = SandboxConfig::default();
assert_eq!(default.max_execution_time_ms, 5000);
assert!(!default.allow_network);
assert!(!default.allow_filesystem);
let restrictive = SandboxConfig::restrictive();
assert_eq!(restrictive.max_execution_time_ms, 3000);
assert_eq!(restrictive.max_memory_mb, 64);
let permissive = SandboxConfig::permissive();
assert!(permissive.allow_filesystem);
assert_eq!(permissive.allowed_paths.len(), 1);
}
#[test]
fn test_execution_context() {
let ctx =
ExecutionContext::new("test task".to_string(), serde_json::json!({"key": "value"}));
assert_eq!(ctx.task, "test task");
assert_eq!(ctx.input["key"], "value");
assert!(ctx.env.is_empty());
}
}