Module: Legion::MCP::ContextGuard
- Extended by:
- Logging::Helper
- Defined in:
- lib/legion/mcp/context_guard.rb
Constant Summary collapse
- DEFAULT_MAX_STALE_SECONDS =
3600- DEFAULT_RAPID_FIRE_THRESHOLD =
5- DEFAULT_RAPID_FIRE_WINDOW_SECS =
600- DEFAULT_ANOMALY_MISS_THRESHOLD =
2
Class Method Summary collapse
- .anomalous?(pattern) ⇒ Boolean
- .anomaly_failure(pattern) ⇒ Object
- .anomaly_miss_threshold ⇒ Object
- .check(pattern, _params, _context) ⇒ Object
- .max_stale_seconds ⇒ Object
- .mutex ⇒ Object
- .rapid_fire?(intent_hash) ⇒ Boolean
- .rapid_fire_failure(_pattern) ⇒ Object
- .rapid_fire_threshold ⇒ Object
- .rapid_fire_window_seconds ⇒ Object
- .record_request(intent_hash) ⇒ Object
- .requests ⇒ Object
- .reset! ⇒ Object
- .setting(key) ⇒ Object
- .stale?(pattern) ⇒ Boolean
- .staleness_failure(pattern) ⇒ Object
Class Method Details
.anomalous?(pattern) ⇒ Boolean
41 42 43 |
# File 'lib/legion/mcp/context_guard.rb', line 41 def anomalous?(pattern) (pattern[:miss_count] || 0) >= anomaly_miss_threshold end |
.anomaly_failure(pattern) ⇒ Object
91 92 93 |
# File 'lib/legion/mcp/context_guard.rb', line 91 def anomaly_failure(pattern) { passed: false, guard: :anomaly, reason: "#{pattern[:miss_count]} consecutive misses" } end |
.anomaly_miss_threshold ⇒ Object
72 73 74 |
# File 'lib/legion/mcp/context_guard.rb', line 72 def anomaly_miss_threshold DEFAULT_ANOMALY_MISS_THRESHOLD end |
.check(pattern, _params, _context) ⇒ Object
15 16 17 18 19 20 21 |
# File 'lib/legion/mcp/context_guard.rb', line 15 def check(pattern, _params, _context) return staleness_failure(pattern) if stale?(pattern) return anomaly_failure(pattern) if anomalous?(pattern) return rapid_fire_failure(pattern) if rapid_fire?(pattern[:intent_hash]) { passed: true } end |
.max_stale_seconds ⇒ Object
60 61 62 |
# File 'lib/legion/mcp/context_guard.rb', line 60 def max_stale_seconds setting(:max_stale_seconds) || DEFAULT_MAX_STALE_SECONDS end |
.mutex ⇒ Object
104 105 106 |
# File 'lib/legion/mcp/context_guard.rb', line 104 def mutex @mutex ||= Mutex.new end |
.rapid_fire?(intent_hash) ⇒ Boolean
45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/legion/mcp/context_guard.rb', line 45 def rapid_fire?(intent_hash) return false unless intent_hash window = Time.now - rapid_fire_window_seconds count = mutex.synchronize do entries = requests[intent_hash] return false unless entries entries.reject! { |t| t < window } entries.size end count > rapid_fire_threshold end |
.rapid_fire_failure(_pattern) ⇒ Object
95 96 97 98 |
# File 'lib/legion/mcp/context_guard.rb', line 95 def rapid_fire_failure(_pattern) { passed: false, guard: :rapid_fire, reason: "exceeded #{rapid_fire_threshold} requests in #{rapid_fire_window_seconds}s" } end |
.rapid_fire_threshold ⇒ Object
64 65 66 |
# File 'lib/legion/mcp/context_guard.rb', line 64 def rapid_fire_threshold setting(:rapid_fire_threshold) || DEFAULT_RAPID_FIRE_THRESHOLD end |
.rapid_fire_window_seconds ⇒ Object
68 69 70 |
# File 'lib/legion/mcp/context_guard.rb', line 68 def rapid_fire_window_seconds setting(:rapid_fire_window_seconds) || DEFAULT_RAPID_FIRE_WINDOW_SECS end |
.record_request(intent_hash) ⇒ Object
23 24 25 26 27 28 |
# File 'lib/legion/mcp/context_guard.rb', line 23 def record_request(intent_hash) mutex.synchronize do requests[intent_hash] ||= [] requests[intent_hash] << Time.now end end |
.requests ⇒ Object
100 101 102 |
# File 'lib/legion/mcp/context_guard.rb', line 100 def requests @requests ||= {} end |
.reset! ⇒ Object
30 31 32 |
# File 'lib/legion/mcp/context_guard.rb', line 30 def reset! mutex.synchronize { requests.clear } end |
.setting(key) ⇒ Object
76 77 78 79 80 81 82 83 84 |
# File 'lib/legion/mcp/context_guard.rb', line 76 def setting(key) return nil unless defined?(Legion::Settings) Legion::Settings.dig(:mcp, :tier0, :guards, key) rescue StandardError => e handle_exception(e, level: :warn, operation: 'legion.mcp.context_guard.setting') log.warn("ContextGuard#setting failed for key #{key}: #{e.}") nil end |
.stale?(pattern) ⇒ Boolean
34 35 36 37 38 39 |
# File 'lib/legion/mcp/context_guard.rb', line 34 def stale?(pattern) last_hit = pattern[:last_hit_at] return false unless last_hit (Time.now - last_hit) > max_stale_seconds end |
.staleness_failure(pattern) ⇒ Object
86 87 88 89 |
# File 'lib/legion/mcp/context_guard.rb', line 86 def staleness_failure(pattern) age = pattern[:last_hit_at] ? (Time.now - pattern[:last_hit_at]).round(0) : 0 { passed: false, guard: :staleness, reason: "pattern stale (#{age}s since last hit)" } end |