strange_loop/nano_agent/
mod.rs

1//! Nano-agent system for ultra-low-latency agent orchestration
2//!
3//! This module implements deterministic nano-agents that run in sub-microsecond
4//! loops with explicit time budgets, zero allocations, and lock-free communication.
5
6use crossbeam::queue::ArrayQueue;
7use std::sync::Arc;
8use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
9use std::time::Instant;
10
11pub mod bus;
12pub mod scheduler;
13pub mod agents;
14pub mod budget;
15pub mod critic;
16pub mod optimization;
17
18pub use bus::{NanoBus, Message};
19pub use scheduler::{NanoScheduler, SchedulerConfig};
20pub use budget::{Budget, BudgetGuard};
21pub use critic::{CriticReflector, PolicyUpdate};
22
23/// Core trait for nano-agents
24pub trait NanoAgent: Send + Sync {
25    /// Agent name for tracing and debugging
26    fn name(&self) -> &'static str;
27
28    /// Execute one tick within the given time budget
29    #[inline(always)]
30    fn tick(&mut self, now_ns: u128, bus: &NanoBus) -> TickResult;
31
32    /// Get the per-tick budget in nanoseconds
33    fn budget_ns(&self) -> u128;
34
35    /// Optional reflection callback for policy updates
36    fn reflect(&mut self, _update: PolicyUpdate) {}
37}
38
39/// Result of a nano-agent tick
40#[derive(Debug, Clone, Copy)]
41pub struct TickResult {
42    pub cycles: u64,
43    pub messages_sent: u32,
44    pub messages_recv: u32,
45    pub budget_used_ns: u128,
46}
47
48impl Default for TickResult {
49    fn default() -> Self {
50        Self {
51            cycles: 0,
52            messages_sent: 0,
53            messages_recv: 0,
54            budget_used_ns: 0,
55        }
56    }
57}
58
59/// Get CPU timestamp counter (TSC) for precise timing
60#[cfg(target_arch = "x86_64")]
61#[inline(always)]
62pub fn rdtsc() -> u64 {
63    unsafe {
64        std::arch::x86_64::_rdtsc()
65    }
66}
67
68#[cfg(not(target_arch = "x86_64"))]
69#[inline(always)]
70pub fn rdtsc() -> u64 {
71    // Fallback to monotonic time on non-x86
72    std::time::Instant::now().elapsed().as_nanos() as u64
73}
74
75/// Spin loop hint for busy waiting
76#[inline(always)]
77pub fn spin() {
78    std::hint::spin_loop();
79}
80
81/// Cache-line aligned agent state for optimal performance
82#[repr(C, align(64))]
83pub struct AlignedState<T> {
84    pub data: T,
85    _padding: [u8; 0], // Ensure cache line alignment
86}
87
88impl<T> AlignedState<T> {
89    pub fn new(data: T) -> Self {
90        Self {
91            data,
92            _padding: [],
93        }
94    }
95}
96
97/// Metrics for nano-agent performance tracking
98pub struct NanoMetrics {
99    pub total_ticks: AtomicU64,
100    pub total_cycles: AtomicU64,
101    pub messages_sent: AtomicU64,
102    pub messages_recv: AtomicU64,
103    pub budget_violations: AtomicU64,
104    pub max_latency_ns: AtomicU64,
105}
106
107impl NanoMetrics {
108    pub fn new() -> Self {
109        Self {
110            total_ticks: AtomicU64::new(0),
111            total_cycles: AtomicU64::new(0),
112            messages_sent: AtomicU64::new(0),
113            messages_recv: AtomicU64::new(0),
114            budget_violations: AtomicU64::new(0),
115            max_latency_ns: AtomicU64::new(0),
116        }
117    }
118
119    pub fn record_tick(&self, result: &TickResult) {
120        self.total_ticks.fetch_add(1, Ordering::Relaxed);
121        self.total_cycles.fetch_add(result.cycles, Ordering::Relaxed);
122        self.messages_sent.fetch_add(result.messages_sent as u64, Ordering::Relaxed);
123        self.messages_recv.fetch_add(result.messages_recv as u64, Ordering::Relaxed);
124
125        // Update max latency
126        let mut current_max = self.max_latency_ns.load(Ordering::Relaxed);
127        while result.budget_used_ns as u64 > current_max {
128            match self.max_latency_ns.compare_exchange_weak(
129                current_max,
130                result.budget_used_ns as u64,
131                Ordering::Relaxed,
132                Ordering::Relaxed
133            ) {
134                Ok(_) => break,
135                Err(x) => current_max = x,
136            }
137        }
138    }
139}
140
141/// Topology for agent scheduling
142#[derive(Debug, Clone, Copy)]
143pub enum SchedulerTopology {
144    /// Round-robin scheduling with equal time slices
145    RoundRobin,
146    /// Priority-based scheduling
147    Priority,
148    /// Hierarchical tree-based delegation
149    Hierarchical,
150    /// Peer-to-peer mesh coordination
151    Mesh,
152    /// Quantum superposition scheduling
153    Quantum,
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    struct TestAgent {
161        counter: u64,
162    }
163
164    impl NanoAgent for TestAgent {
165        fn name(&self) -> &'static str { "test" }
166
167        fn tick(&mut self, _now_ns: u128, _bus: &NanoBus) -> TickResult {
168            self.counter += 1;
169            TickResult::default()
170        }
171
172        fn budget_ns(&self) -> u128 { 1000 } // 1 microsecond
173    }
174
175    #[test]
176    fn test_aligned_state() {
177        let state = AlignedState::new(42u64);
178        assert_eq!(state.data, 42);
179
180        // Check alignment
181        let ptr = &state as *const _ as usize;
182        assert_eq!(ptr % 64, 0, "State should be cache-line aligned");
183    }
184
185    #[test]
186    fn test_nano_metrics() {
187        let metrics = NanoMetrics::new();
188        let result = TickResult {
189            cycles: 1000,
190            messages_sent: 5,
191            messages_recv: 3,
192            budget_used_ns: 500,
193        };
194
195        metrics.record_tick(&result);
196
197        assert_eq!(metrics.total_ticks.load(Ordering::Relaxed), 1);
198        assert_eq!(metrics.total_cycles.load(Ordering::Relaxed), 1000);
199        assert_eq!(metrics.messages_sent.load(Ordering::Relaxed), 5);
200        assert_eq!(metrics.messages_recv.load(Ordering::Relaxed), 3);
201        assert_eq!(metrics.max_latency_ns.load(Ordering::Relaxed), 500);
202    }
203}