Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
refactor(loop_guard): 为LoopGuard添加Clone派生 feat(capabilities): 实现CapabilityManager.validate()安全验证 fix(agentStore): 添加token用量追踪 chore: 删除未实现的Predictor/Lead HAND.toml文件 style(Credits): 移除假数据并标注开发中状态 refactor(Skills): 动态加载技能卡片 perf(configStore): 为定时任务添加localStorage降级 docs: 更新功能文档和版本变更记录
104 lines
2.6 KiB
Rust
104 lines
2.6 KiB
Rust
//! Loop guard to prevent infinite tool loops
|
|
|
|
use sha2::{Sha256, Digest};
|
|
use std::collections::HashMap;
|
|
|
|
/// Configuration for loop guard
|
|
#[derive(Debug, Clone)]
|
|
pub struct LoopGuardConfig {
|
|
/// Warn after this many repetitions
|
|
pub warn_threshold: u32,
|
|
/// Block tool call after this many repetitions
|
|
pub block_threshold: u32,
|
|
/// Terminate loop after this many total repetitions
|
|
pub circuit_breaker: u32,
|
|
}
|
|
|
|
impl Default for LoopGuardConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
warn_threshold: 3,
|
|
block_threshold: 5,
|
|
circuit_breaker: 30,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Loop guard state
|
|
#[derive(Debug, Clone)]
|
|
pub struct LoopGuard {
|
|
config: LoopGuardConfig,
|
|
/// Hash of (tool_name, params) -> count
|
|
call_counts: HashMap<String, u32>,
|
|
/// Total calls in this session
|
|
total_calls: u32,
|
|
}
|
|
|
|
impl LoopGuard {
|
|
pub fn new(config: LoopGuardConfig) -> Self {
|
|
Self {
|
|
config,
|
|
call_counts: HashMap::new(),
|
|
total_calls: 0,
|
|
}
|
|
}
|
|
|
|
/// Check if a tool call should be allowed
|
|
pub fn check(&mut self, tool_name: &str, params: &serde_json::Value) -> LoopGuardResult {
|
|
let hash = self.hash_call(tool_name, params);
|
|
let count = self.call_counts.entry(hash).or_insert(0);
|
|
|
|
self.total_calls += 1;
|
|
*count += 1;
|
|
|
|
// Check circuit breaker first
|
|
if self.total_calls > self.config.circuit_breaker {
|
|
return LoopGuardResult::CircuitBreaker;
|
|
}
|
|
|
|
// Check block threshold
|
|
if *count > self.config.block_threshold {
|
|
return LoopGuardResult::Blocked;
|
|
}
|
|
|
|
// Check warn threshold
|
|
if *count > self.config.warn_threshold {
|
|
return LoopGuardResult::Warn;
|
|
}
|
|
|
|
LoopGuardResult::Allowed
|
|
}
|
|
|
|
/// Reset the guard state
|
|
pub fn reset(&mut self) {
|
|
self.call_counts.clear();
|
|
self.total_calls = 0;
|
|
}
|
|
|
|
fn hash_call(&self, tool_name: &str, params: &serde_json::Value) -> String {
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(tool_name.as_bytes());
|
|
hasher.update(params.to_string().as_bytes());
|
|
format!("{:x}", hasher.finalize())
|
|
}
|
|
}
|
|
|
|
impl Default for LoopGuard {
|
|
fn default() -> Self {
|
|
Self::new(LoopGuardConfig::default())
|
|
}
|
|
}
|
|
|
|
/// Result of loop guard check
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum LoopGuardResult {
|
|
/// Call is allowed
|
|
Allowed,
|
|
/// Call is allowed but should warn
|
|
Warn,
|
|
/// Call should be blocked
|
|
Blocked,
|
|
/// Loop should be terminated
|
|
CircuitBreaker,
|
|
}
|