1use std::sync::Arc;
29use std::time::Duration;
30
31use async_trait::async_trait;
32use gemini_genai_rs::prelude::FunctionCall;
33
34use gemini_adk_rs::context::AgentEvent;
35use gemini_adk_rs::error::{AgentError, ToolError};
36use gemini_adk_rs::middleware::{LatencyMiddleware, LogMiddleware, Middleware};
37
38#[derive(Clone)]
43#[non_exhaustive]
44pub struct MiddlewareComposite {
45 pub layers: Vec<Arc<dyn Middleware>>,
47}
48
49impl MiddlewareComposite {
50 pub fn new(layer: Arc<dyn Middleware>) -> Self {
52 Self {
53 layers: vec![layer],
54 }
55 }
56
57 pub fn len(&self) -> usize {
59 self.layers.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.layers.is_empty()
65 }
66}
67
68impl From<Arc<dyn Middleware>> for MiddlewareComposite {
69 fn from(layer: Arc<dyn Middleware>) -> Self {
70 Self::new(layer)
71 }
72}
73
74impl std::ops::Shr for MiddlewareComposite {
76 type Output = MiddlewareComposite;
77
78 fn shr(mut self, rhs: MiddlewareComposite) -> Self::Output {
79 self.layers.extend(rhs.layers);
80 self
81 }
82}
83
84pub struct M;
86
87impl M {
88 pub fn log() -> MiddlewareComposite {
90 MiddlewareComposite::new(Arc::new(LogMiddleware::new()))
91 }
92
93 pub fn latency() -> MiddlewareComposite {
95 MiddlewareComposite::new(Arc::new(LatencyMiddleware::new()))
96 }
97
98 pub fn timeout(duration: Duration) -> MiddlewareComposite {
102 MiddlewareComposite::new(Arc::new(TimeoutMiddleware {
103 name: "timeout".to_string(),
104 duration,
105 }))
106 }
107
108 pub fn retry(max_retries: u32) -> MiddlewareComposite {
110 MiddlewareComposite::new(Arc::new(gemini_adk_rs::middleware::RetryMiddleware::new(
111 max_retries,
112 )))
113 }
114
115 pub fn tap(f: impl Fn(&AgentEvent) + Send + Sync + 'static) -> MiddlewareComposite {
117 MiddlewareComposite::new(Arc::new(TapMiddleware {
118 handler: Arc::new(f),
119 }))
120 }
121
122 pub fn before_tool(
124 f: impl Fn(&FunctionCall) -> Result<(), String> + Send + Sync + 'static,
125 ) -> MiddlewareComposite {
126 MiddlewareComposite::new(Arc::new(BeforeToolMiddleware {
127 handler: Arc::new(f),
128 }))
129 }
130
131 pub fn after_tool(
133 f: impl Fn(&FunctionCall, &serde_json::Value) -> Result<(), String> + Send + Sync + 'static,
134 ) -> MiddlewareComposite {
135 MiddlewareComposite::new(Arc::new(AfterToolMiddleware {
136 handler: Arc::new(f),
137 }))
138 }
139
140 pub fn on_error(
142 f: impl Fn(&AgentError) -> Result<(), String> + Send + Sync + 'static,
143 ) -> MiddlewareComposite {
144 MiddlewareComposite::new(Arc::new(OnErrorMiddleware {
145 handler: Arc::new(f),
146 }))
147 }
148
149 pub fn cost() -> MiddlewareComposite {
151 MiddlewareComposite::new(Arc::new(CostMiddleware {
152 tool_calls: std::sync::atomic::AtomicU64::new(0),
153 }))
154 }
155
156 pub fn rate_limit(rps: u32) -> MiddlewareComposite {
160 MiddlewareComposite::new(Arc::new(RateLimitMiddleware::new(rps)))
161 }
162
163 pub fn circuit_breaker(threshold: u32) -> MiddlewareComposite {
165 MiddlewareComposite::new(Arc::new(CircuitBreakerMiddleware {
166 threshold,
167 consecutive_failures: std::sync::atomic::AtomicU32::new(0),
168 }))
169 }
170
171 pub fn trace() -> MiddlewareComposite {
173 MiddlewareComposite::new(Arc::new(TraceMiddleware))
174 }
175
176 pub fn audit() -> MiddlewareComposite {
178 MiddlewareComposite::new(Arc::new(AuditMiddleware {
179 log: parking_lot::Mutex::new(Vec::new()),
180 }))
181 }
182
183 #[doc(hidden)]
189 pub fn scope(_names: &[&str], inner: MiddlewareComposite) -> MiddlewareComposite {
190 inner
191 }
192
193 pub fn structured_log() -> MiddlewareComposite {
195 MiddlewareComposite::new(Arc::new(StructuredLogMiddleware))
196 }
197
198 pub fn dispatch_log() -> MiddlewareComposite {
200 MiddlewareComposite::new(Arc::new(DispatchLogMiddleware))
201 }
202
203 pub fn topology_log() -> MiddlewareComposite {
205 MiddlewareComposite::new(Arc::new(TopologyLogMiddleware))
206 }
207
208 pub fn validate(
210 f: impl Fn(&FunctionCall) -> Result<(), String> + Send + Sync + 'static,
211 ) -> MiddlewareComposite {
212 MiddlewareComposite::new(Arc::new(ValidateMiddleware {
213 validator: Arc::new(f),
214 }))
215 }
216
217 #[doc(hidden)]
223 pub fn fallback_model(model: &str) -> MiddlewareComposite {
224 MiddlewareComposite::new(Arc::new(FallbackModelMiddleware {
225 model: model.to_string(),
226 }))
227 }
228
229 pub fn cache() -> MiddlewareComposite {
231 MiddlewareComposite::new(Arc::new(CacheMiddleware {
232 cache: parking_lot::Mutex::new(std::collections::HashMap::new()),
233 }))
234 }
235
236 pub fn dedup() -> MiddlewareComposite {
238 MiddlewareComposite::new(Arc::new(DedupMiddleware {
239 last_request_hash: parking_lot::Mutex::new(None),
240 }))
241 }
242
243 pub fn sample(rate: f64) -> MiddlewareComposite {
245 MiddlewareComposite::new(Arc::new(SampleMiddleware {
246 rate: rate.clamp(0.0, 1.0),
247 }))
248 }
249
250 pub fn metrics() -> MiddlewareComposite {
252 MiddlewareComposite::new(Arc::new(MetricsMiddleware {
253 request_count: std::sync::atomic::AtomicU64::new(0),
254 error_count: std::sync::atomic::AtomicU64::new(0),
255 }))
256 }
257
258 pub fn before_agent(
260 f: impl Fn(&gemini_adk_rs::context::InvocationContext) -> Result<(), String>
261 + Send
262 + Sync
263 + 'static,
264 ) -> MiddlewareComposite {
265 MiddlewareComposite::new(Arc::new(BeforeAgentMiddleware {
266 handler: Arc::new(f),
267 }))
268 }
269
270 pub fn after_agent(
272 f: impl Fn(&gemini_adk_rs::context::InvocationContext) -> Result<(), String>
273 + Send
274 + Sync
275 + 'static,
276 ) -> MiddlewareComposite {
277 MiddlewareComposite::new(Arc::new(AfterAgentMiddleware {
278 handler: Arc::new(f),
279 }))
280 }
281
282 pub fn before_model(
284 f: impl Fn(&gemini_adk_rs::llm::LlmRequest) -> Result<(), String> + Send + Sync + 'static,
285 ) -> MiddlewareComposite {
286 MiddlewareComposite::new(Arc::new(BeforeModelMiddleware {
287 handler: Arc::new(f),
288 }))
289 }
290
291 pub fn after_model(
293 f: impl Fn(
294 &gemini_adk_rs::llm::LlmRequest,
295 &gemini_adk_rs::llm::LlmResponse,
296 ) -> Result<(), String>
297 + Send
298 + Sync
299 + 'static,
300 ) -> MiddlewareComposite {
301 MiddlewareComposite::new(Arc::new(AfterModelMiddleware {
302 handler: Arc::new(f),
303 }))
304 }
305
306 pub fn on_loop(f: impl Fn(u32) + Send + Sync + 'static) -> MiddlewareComposite {
308 MiddlewareComposite::new(Arc::new(OnLoopMiddleware {
309 handler: Arc::new(f),
310 }))
311 }
312
313 pub fn on_timeout(f: impl Fn() + Send + Sync + 'static) -> MiddlewareComposite {
315 MiddlewareComposite::new(Arc::new(OnTimeoutMiddleware {
316 handler: Arc::new(f),
317 }))
318 }
319
320 pub fn on_route(f: impl Fn(&str) + Send + Sync + 'static) -> MiddlewareComposite {
322 MiddlewareComposite::new(Arc::new(OnRouteMiddleware {
323 handler: Arc::new(f),
324 }))
325 }
326
327 pub fn on_fallback(f: impl Fn(&str) + Send + Sync + 'static) -> MiddlewareComposite {
329 MiddlewareComposite::new(Arc::new(OnFallbackMiddleware {
330 handler: Arc::new(f),
331 }))
332 }
333}
334
335#[allow(dead_code)]
337struct TimeoutMiddleware {
338 name: String,
339 duration: Duration,
340}
341
342#[async_trait::async_trait]
343impl Middleware for TimeoutMiddleware {
344 fn name(&self) -> &str {
345 &self.name
346 }
347
348 fn timeout(&self) -> Option<Duration> {
349 Some(self.duration)
350 }
351}
352
353struct TapMiddleware {
356 #[allow(clippy::type_complexity)]
357 handler: Arc<dyn Fn(&AgentEvent) + Send + Sync>,
358}
359
360#[async_trait]
361impl Middleware for TapMiddleware {
362 fn name(&self) -> &str {
363 "tap"
364 }
365
366 async fn on_event(&self, event: &AgentEvent) -> Result<(), AgentError> {
367 (self.handler)(event);
368 Ok(())
369 }
370}
371
372struct BeforeToolMiddleware {
375 #[allow(clippy::type_complexity)]
376 handler: Arc<dyn Fn(&FunctionCall) -> Result<(), String> + Send + Sync>,
377}
378
379#[async_trait]
380impl Middleware for BeforeToolMiddleware {
381 fn name(&self) -> &str {
382 "before_tool"
383 }
384
385 async fn before_tool(&self, call: &FunctionCall) -> Result<(), AgentError> {
386 (self.handler)(call).map_err(AgentError::Other)
387 }
388}
389
390struct AfterToolMiddleware {
393 #[allow(clippy::type_complexity)]
394 handler: Arc<dyn Fn(&FunctionCall, &serde_json::Value) -> Result<(), String> + Send + Sync>,
395}
396
397#[async_trait]
398impl Middleware for AfterToolMiddleware {
399 fn name(&self) -> &str {
400 "after_tool"
401 }
402
403 async fn after_tool(
404 &self,
405 call: &FunctionCall,
406 result: &serde_json::Value,
407 ) -> Result<(), AgentError> {
408 (self.handler)(call, result).map_err(AgentError::Other)
409 }
410}
411
412struct OnErrorMiddleware {
415 #[allow(clippy::type_complexity)]
416 handler: Arc<dyn Fn(&AgentError) -> Result<(), String> + Send + Sync>,
417}
418
419#[async_trait]
420impl Middleware for OnErrorMiddleware {
421 fn name(&self) -> &str {
422 "on_error"
423 }
424
425 async fn on_error(&self, err: &AgentError) -> Result<(), AgentError> {
426 (self.handler)(err).map_err(AgentError::Other)
427 }
428}
429
430pub struct CostMiddleware {
434 tool_calls: std::sync::atomic::AtomicU64,
435}
436
437impl CostMiddleware {
438 pub fn tool_call_count(&self) -> u64 {
440 self.tool_calls.load(std::sync::atomic::Ordering::SeqCst)
441 }
442}
443
444#[async_trait]
445impl Middleware for CostMiddleware {
446 fn name(&self) -> &str {
447 "cost"
448 }
449
450 async fn after_tool(
451 &self,
452 _call: &FunctionCall,
453 _result: &serde_json::Value,
454 ) -> Result<(), AgentError> {
455 self.tool_calls
456 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
457 Ok(())
458 }
459}
460
461#[allow(dead_code)]
464struct RateLimitMiddleware {
465 min_interval: Duration,
467 last: parking_lot::Mutex<Option<std::time::Instant>>,
469}
470
471impl RateLimitMiddleware {
472 fn new(rps: u32) -> Self {
473 let rps = rps.max(1);
474 Self {
475 min_interval: Duration::from_secs_f64(1.0 / rps as f64),
476 last: parking_lot::Mutex::new(None),
477 }
478 }
479}
480
481#[async_trait]
482impl Middleware for RateLimitMiddleware {
483 fn name(&self) -> &str {
484 "rate_limit"
485 }
486
487 async fn before_tool(&self, _call: &FunctionCall) -> Result<(), AgentError> {
488 let wait = {
492 let mut last = self.last.lock();
493 let now = std::time::Instant::now();
494 let scheduled = match *last {
495 Some(prev) if prev + self.min_interval > now => prev + self.min_interval,
496 _ => now,
497 };
498 *last = Some(scheduled);
499 scheduled.saturating_duration_since(now)
500 };
501 if !wait.is_zero() {
502 tokio::time::sleep(wait).await;
503 }
504 Ok(())
505 }
506}
507
508struct CircuitBreakerMiddleware {
511 threshold: u32,
512 consecutive_failures: std::sync::atomic::AtomicU32,
513}
514
515#[async_trait]
516impl Middleware for CircuitBreakerMiddleware {
517 fn name(&self) -> &str {
518 "circuit_breaker"
519 }
520
521 async fn before_tool(&self, _call: &FunctionCall) -> Result<(), AgentError> {
522 let failures = self
523 .consecutive_failures
524 .load(std::sync::atomic::Ordering::SeqCst);
525 if failures >= self.threshold {
526 return Err(AgentError::Other(format!(
527 "Circuit breaker open: {} consecutive failures (threshold: {})",
528 failures, self.threshold
529 )));
530 }
531 Ok(())
532 }
533
534 async fn after_tool(
535 &self,
536 _call: &FunctionCall,
537 _result: &serde_json::Value,
538 ) -> Result<(), AgentError> {
539 self.consecutive_failures
540 .store(0, std::sync::atomic::Ordering::SeqCst);
541 Ok(())
542 }
543
544 async fn on_tool_error(
545 &self,
546 _call: &FunctionCall,
547 _err: &ToolError,
548 ) -> Result<(), AgentError> {
549 self.consecutive_failures
550 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
551 Ok(())
552 }
553}
554
555struct TraceMiddleware;
561
562#[async_trait]
563impl Middleware for TraceMiddleware {
564 fn name(&self) -> &str {
565 "trace"
566 }
567
568 async fn before_agent(
569 &self,
570 ctx: &gemini_adk_rs::context::InvocationContext,
571 ) -> Result<(), AgentError> {
572 let sid = ctx.session_id.as_deref().unwrap_or("unknown");
573 gemini_adk_rs::telemetry::logging::log_agent_started(sid, 0);
574 Ok(())
575 }
576
577 async fn before_tool(&self, call: &FunctionCall) -> Result<(), AgentError> {
578 gemini_adk_rs::telemetry::logging::log_tool_dispatch("fluent", &call.name, "function");
579 Ok(())
580 }
581
582 async fn after_tool(
583 &self,
584 call: &FunctionCall,
585 _result: &serde_json::Value,
586 ) -> Result<(), AgentError> {
587 gemini_adk_rs::telemetry::logging::log_tool_result("fluent", &call.name, true, 0.0);
588 Ok(())
589 }
590
591 async fn on_tool_error(&self, call: &FunctionCall, _err: &ToolError) -> Result<(), AgentError> {
592 gemini_adk_rs::telemetry::logging::log_tool_result("fluent", &call.name, false, 0.0);
593 Ok(())
594 }
595
596 async fn on_error(&self, err: &AgentError) -> Result<(), AgentError> {
597 gemini_adk_rs::telemetry::logging::log_agent_error("fluent", &err.to_string());
598 Ok(())
599 }
600}
601
602pub struct AuditMiddleware {
606 log: parking_lot::Mutex<Vec<AuditEntry>>,
607}
608
609#[derive(Debug, Clone)]
611pub struct AuditEntry {
612 pub tool_name: String,
614 pub args: serde_json::Value,
616 pub success: Option<bool>,
618}
619
620impl AuditMiddleware {
621 pub fn entries(&self) -> Vec<AuditEntry> {
623 self.log.lock().clone()
624 }
625}
626
627#[async_trait]
628impl Middleware for AuditMiddleware {
629 fn name(&self) -> &str {
630 "audit"
631 }
632
633 async fn before_tool(&self, call: &FunctionCall) -> Result<(), AgentError> {
634 let mut log = self.log.lock();
635 if log.len() >= 10_000 {
636 log.drain(..1_000);
637 }
638 log.push(AuditEntry {
639 tool_name: call.name.clone(),
640 args: call.args.clone(),
641 success: None,
642 });
643 Ok(())
644 }
645
646 async fn after_tool(
647 &self,
648 call: &FunctionCall,
649 _result: &serde_json::Value,
650 ) -> Result<(), AgentError> {
651 let mut log = self.log.lock();
652 if let Some(entry) = log.iter_mut().rev().find(|e| e.tool_name == call.name) {
653 entry.success = Some(true);
654 }
655 Ok(())
656 }
657
658 async fn on_tool_error(&self, call: &FunctionCall, _err: &ToolError) -> Result<(), AgentError> {
659 let mut log = self.log.lock();
660 if let Some(entry) = log.iter_mut().rev().find(|e| e.tool_name == call.name) {
661 entry.success = Some(false);
662 }
663 Ok(())
664 }
665}
666
667struct ValidateMiddleware {
670 #[allow(clippy::type_complexity)]
671 validator: Arc<dyn Fn(&FunctionCall) -> Result<(), String> + Send + Sync>,
672}
673
674#[async_trait]
675impl Middleware for ValidateMiddleware {
676 fn name(&self) -> &str {
677 "validate"
678 }
679
680 async fn before_tool(&self, call: &FunctionCall) -> Result<(), AgentError> {
681 (self.validator)(call).map_err(|e| AgentError::Tool(ToolError::InvalidArgs(e)))
682 }
683}
684
685#[allow(dead_code)]
689struct FallbackModelMiddleware {
690 model: String,
691}
692
693#[async_trait]
694impl Middleware for FallbackModelMiddleware {
695 fn name(&self) -> &str {
696 "fallback_model"
697 }
698
699 async fn on_error(&self, _err: &AgentError) -> Result<(), AgentError> {
700 Ok(())
702 }
703}
704
705pub struct CacheMiddleware {
709 cache: parking_lot::Mutex<std::collections::HashMap<u64, gemini_adk_rs::llm::LlmResponse>>,
710}
711
712impl CacheMiddleware {
713 pub fn len(&self) -> usize {
715 self.cache.lock().len()
716 }
717
718 pub fn is_empty(&self) -> bool {
720 self.cache.lock().is_empty()
721 }
722
723 pub fn clear(&self) {
725 self.cache.lock().clear();
726 }
727}
728
729#[async_trait]
730impl Middleware for CacheMiddleware {
731 fn name(&self) -> &str {
732 "cache"
733 }
734
735 async fn before_model(
736 &self,
737 request: &gemini_adk_rs::llm::LlmRequest,
738 ) -> Result<Option<gemini_adk_rs::llm::LlmResponse>, AgentError> {
739 use std::hash::{Hash, Hasher};
740 let mut hasher = std::collections::hash_map::DefaultHasher::new();
741 format!("{request:?}").hash(&mut hasher);
742 let key = hasher.finish();
743 let cache = self.cache.lock();
744 Ok(cache.get(&key).cloned())
745 }
746
747 async fn after_model(
748 &self,
749 request: &gemini_adk_rs::llm::LlmRequest,
750 response: &gemini_adk_rs::llm::LlmResponse,
751 ) -> Result<Option<gemini_adk_rs::llm::LlmResponse>, AgentError> {
752 use std::hash::{Hash, Hasher};
753 let mut hasher = std::collections::hash_map::DefaultHasher::new();
754 format!("{request:?}").hash(&mut hasher);
755 let key = hasher.finish();
756 self.cache.lock().insert(key, response.clone());
757 Ok(None) }
759}
760
761#[allow(dead_code)]
765struct DedupMiddleware {
766 last_request_hash: parking_lot::Mutex<Option<u64>>,
767}
768
769#[async_trait]
770impl Middleware for DedupMiddleware {
771 fn name(&self) -> &str {
772 "dedup"
773 }
774
775 async fn before_model(
776 &self,
777 request: &gemini_adk_rs::llm::LlmRequest,
778 ) -> Result<Option<gemini_adk_rs::llm::LlmResponse>, AgentError> {
779 use std::hash::{Hash, Hasher};
780 let mut hasher = std::collections::hash_map::DefaultHasher::new();
781 format!("{request:?}").hash(&mut hasher);
782 let hash = hasher.finish();
783 let mut last = self.last_request_hash.lock();
784 if *last == Some(hash) {
785 return Err(AgentError::Other(
787 "Duplicate consecutive request".to_string(),
788 ));
789 }
790 *last = Some(hash);
791 Ok(None)
792 }
793}
794
795#[allow(dead_code)]
799struct SampleMiddleware {
800 rate: f64,
801}
802
803#[async_trait]
804impl Middleware for SampleMiddleware {
805 fn name(&self) -> &str {
806 "sample"
807 }
808
809 async fn before_model(
810 &self,
811 _request: &gemini_adk_rs::llm::LlmRequest,
812 ) -> Result<Option<gemini_adk_rs::llm::LlmResponse>, AgentError> {
813 use std::hash::{Hash, Hasher};
814 let mut hasher = std::collections::hash_map::DefaultHasher::new();
816 std::time::Instant::now().hash(&mut hasher);
817 let hash = hasher.finish();
818 let normalized = (hash as f64) / (u64::MAX as f64);
819 if normalized > self.rate {
820 return Err(AgentError::Other("Sampled out".to_string()));
821 }
822 Ok(None)
823 }
824}
825
826pub struct MetricsMiddleware {
830 request_count: std::sync::atomic::AtomicU64,
831 error_count: std::sync::atomic::AtomicU64,
832}
833
834impl MetricsMiddleware {
835 pub fn request_count(&self) -> u64 {
837 self.request_count.load(std::sync::atomic::Ordering::SeqCst)
838 }
839
840 pub fn error_count(&self) -> u64 {
842 self.error_count.load(std::sync::atomic::Ordering::SeqCst)
843 }
844}
845
846#[async_trait]
847impl Middleware for MetricsMiddleware {
848 fn name(&self) -> &str {
849 "metrics"
850 }
851
852 async fn before_agent(
853 &self,
854 _ctx: &gemini_adk_rs::context::InvocationContext,
855 ) -> Result<(), AgentError> {
856 self.request_count
857 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
858 Ok(())
859 }
860
861 async fn on_error(&self, _err: &AgentError) -> Result<(), AgentError> {
862 self.error_count
863 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
864 Ok(())
865 }
866}
867
868struct BeforeAgentMiddleware {
871 #[allow(clippy::type_complexity)]
872 handler:
873 Arc<dyn Fn(&gemini_adk_rs::context::InvocationContext) -> Result<(), String> + Send + Sync>,
874}
875
876#[async_trait]
877impl Middleware for BeforeAgentMiddleware {
878 fn name(&self) -> &str {
879 "before_agent"
880 }
881
882 async fn before_agent(
883 &self,
884 ctx: &gemini_adk_rs::context::InvocationContext,
885 ) -> Result<(), AgentError> {
886 (self.handler)(ctx).map_err(AgentError::Other)
887 }
888}
889
890struct AfterAgentMiddleware {
893 #[allow(clippy::type_complexity)]
894 handler:
895 Arc<dyn Fn(&gemini_adk_rs::context::InvocationContext) -> Result<(), String> + Send + Sync>,
896}
897
898#[async_trait]
899impl Middleware for AfterAgentMiddleware {
900 fn name(&self) -> &str {
901 "after_agent"
902 }
903
904 async fn after_agent(
905 &self,
906 ctx: &gemini_adk_rs::context::InvocationContext,
907 ) -> Result<(), AgentError> {
908 (self.handler)(ctx).map_err(AgentError::Other)
909 }
910}
911
912struct BeforeModelMiddleware {
915 #[allow(clippy::type_complexity)]
916 handler: Arc<dyn Fn(&gemini_adk_rs::llm::LlmRequest) -> Result<(), String> + Send + Sync>,
917}
918
919#[async_trait]
920impl Middleware for BeforeModelMiddleware {
921 fn name(&self) -> &str {
922 "before_model"
923 }
924
925 async fn before_model(
926 &self,
927 request: &gemini_adk_rs::llm::LlmRequest,
928 ) -> Result<Option<gemini_adk_rs::llm::LlmResponse>, AgentError> {
929 (self.handler)(request).map_err(AgentError::Other)?;
930 Ok(None)
931 }
932}
933
934struct AfterModelMiddleware {
937 #[allow(clippy::type_complexity)]
938 handler: Arc<
939 dyn Fn(
940 &gemini_adk_rs::llm::LlmRequest,
941 &gemini_adk_rs::llm::LlmResponse,
942 ) -> Result<(), String>
943 + Send
944 + Sync,
945 >,
946}
947
948#[async_trait]
949impl Middleware for AfterModelMiddleware {
950 fn name(&self) -> &str {
951 "after_model"
952 }
953
954 async fn after_model(
955 &self,
956 request: &gemini_adk_rs::llm::LlmRequest,
957 response: &gemini_adk_rs::llm::LlmResponse,
958 ) -> Result<Option<gemini_adk_rs::llm::LlmResponse>, AgentError> {
959 (self.handler)(request, response).map_err(AgentError::Other)?;
960 Ok(None)
961 }
962}
963
964struct OnLoopMiddleware {
967 handler: Arc<dyn Fn(u32) + Send + Sync>,
968}
969
970#[async_trait]
971impl Middleware for OnLoopMiddleware {
972 fn name(&self) -> &str {
973 "on_loop"
974 }
975
976 async fn on_event(&self, event: &AgentEvent) -> Result<(), AgentError> {
977 if let AgentEvent::LoopIteration { iteration } = event {
978 (self.handler)(*iteration);
979 }
980 Ok(())
981 }
982}
983
984struct OnTimeoutMiddleware {
987 handler: Arc<dyn Fn() + Send + Sync>,
988}
989
990#[async_trait]
991impl Middleware for OnTimeoutMiddleware {
992 fn name(&self) -> &str {
993 "on_timeout"
994 }
995
996 async fn on_event(&self, event: &AgentEvent) -> Result<(), AgentError> {
997 if let AgentEvent::Timeout = event {
998 (self.handler)();
999 }
1000 Ok(())
1001 }
1002}
1003
1004struct OnRouteMiddleware {
1007 handler: Arc<dyn Fn(&str) + Send + Sync>,
1008}
1009
1010#[async_trait]
1011impl Middleware for OnRouteMiddleware {
1012 fn name(&self) -> &str {
1013 "on_route"
1014 }
1015
1016 async fn on_event(&self, event: &AgentEvent) -> Result<(), AgentError> {
1017 if let AgentEvent::RouteSelected { agent_name } = event {
1018 (self.handler)(agent_name);
1019 }
1020 Ok(())
1021 }
1022}
1023
1024struct OnFallbackMiddleware {
1027 handler: Arc<dyn Fn(&str) + Send + Sync>,
1028}
1029
1030#[async_trait]
1031impl Middleware for OnFallbackMiddleware {
1032 fn name(&self) -> &str {
1033 "on_fallback"
1034 }
1035
1036 async fn on_event(&self, event: &AgentEvent) -> Result<(), AgentError> {
1037 if let AgentEvent::FallbackActivated { agent_name } = event {
1038 (self.handler)(agent_name);
1039 }
1040 Ok(())
1041 }
1042}
1043
1044struct StructuredLogMiddleware;
1047
1048#[async_trait]
1049impl Middleware for StructuredLogMiddleware {
1050 fn name(&self) -> &str {
1051 "structured_log"
1052 }
1053
1054 async fn on_event(&self, event: &AgentEvent) -> Result<(), AgentError> {
1055 let _ = event;
1057 Ok(())
1058 }
1059}
1060
1061struct DispatchLogMiddleware;
1064
1065#[async_trait]
1066impl Middleware for DispatchLogMiddleware {
1067 fn name(&self) -> &str {
1068 "dispatch_log"
1069 }
1070}
1071
1072struct TopologyLogMiddleware;
1075
1076#[async_trait]
1077impl Middleware for TopologyLogMiddleware {
1078 fn name(&self) -> &str {
1079 "topology_log"
1080 }
1081}
1082
1083#[cfg(test)]
1084mod tests {
1085 use super::*;
1086
1087 #[test]
1088 fn log_creates_composite() {
1089 let m = M::log();
1090 assert_eq!(m.len(), 1);
1091 }
1092
1093 #[test]
1094 fn latency_creates_composite() {
1095 let m = M::latency();
1096 assert_eq!(m.len(), 1);
1097 }
1098
1099 #[test]
1100 fn timeout_creates_composite() {
1101 let m = M::timeout(Duration::from_secs(30));
1102 assert_eq!(m.len(), 1);
1103 }
1104
1105 #[test]
1106 fn compose_with_bitor() {
1107 let m = M::log() >> M::latency() >> M::timeout(Duration::from_secs(5));
1108 assert_eq!(m.len(), 3);
1109 }
1110
1111 #[test]
1112 fn retry_creates_composite() {
1113 let m = M::retry(3);
1114 assert_eq!(m.len(), 1);
1115 }
1116
1117 #[test]
1118 fn tap_creates_composite() {
1119 let m = M::tap(|_event| {});
1120 assert_eq!(m.len(), 1);
1121 }
1122
1123 #[test]
1124 fn before_tool_creates_composite() {
1125 let m = M::before_tool(|_call| Ok(()));
1126 assert_eq!(m.len(), 1);
1127 }
1128
1129 #[test]
1130 fn cost_creates_composite() {
1131 let m = M::cost();
1132 assert_eq!(m.len(), 1);
1133 }
1134
1135 #[test]
1136 fn rate_limit_creates_composite() {
1137 let m = M::rate_limit(10);
1138 assert_eq!(m.len(), 1);
1139 }
1140
1141 #[test]
1142 fn circuit_breaker_creates_composite() {
1143 let m = M::circuit_breaker(5);
1144 assert_eq!(m.len(), 1);
1145 }
1146
1147 #[test]
1148 fn trace_creates_composite() {
1149 let m = M::trace();
1150 assert_eq!(m.len(), 1);
1151 }
1152
1153 #[test]
1154 fn audit_creates_composite() {
1155 let m = M::audit();
1156 assert_eq!(m.len(), 1);
1157 }
1158
1159 #[test]
1160 fn validate_creates_composite() {
1161 let m = M::validate(|_call| Ok(()));
1162 assert_eq!(m.len(), 1);
1163 }
1164
1165 #[test]
1166 fn fallback_model_creates_composite() {
1167 let m = M::fallback_model("gemini-1.5-flash");
1168 assert_eq!(m.len(), 1);
1169 }
1170
1171 #[test]
1172 fn cache_creates_composite() {
1173 let m = M::cache();
1174 assert_eq!(m.len(), 1);
1175 }
1176
1177 #[test]
1178 fn dedup_creates_composite() {
1179 let m = M::dedup();
1180 assert_eq!(m.len(), 1);
1181 }
1182
1183 #[test]
1184 fn sample_creates_composite() {
1185 let m = M::sample(0.5);
1186 assert_eq!(m.len(), 1);
1187 }
1188
1189 #[test]
1190 fn sample_clamps_rate() {
1191 let m = M::sample(2.0);
1192 assert_eq!(m.len(), 1);
1193 let m = M::sample(-1.0);
1194 assert_eq!(m.len(), 1);
1195 }
1196
1197 #[test]
1198 fn metrics_creates_composite() {
1199 let m = M::metrics();
1200 assert_eq!(m.len(), 1);
1201 }
1202
1203 #[test]
1204 fn before_agent_creates_composite() {
1205 let m = M::before_agent(|_ctx| Ok(()));
1206 assert_eq!(m.len(), 1);
1207 }
1208
1209 #[test]
1210 fn after_agent_creates_composite() {
1211 let m = M::after_agent(|_ctx| Ok(()));
1212 assert_eq!(m.len(), 1);
1213 }
1214
1215 #[test]
1216 fn before_model_creates_composite() {
1217 let m = M::before_model(|_req| Ok(()));
1218 assert_eq!(m.len(), 1);
1219 }
1220
1221 #[test]
1222 fn after_model_creates_composite() {
1223 let m = M::after_model(|_req, _resp| Ok(()));
1224 assert_eq!(m.len(), 1);
1225 }
1226
1227 #[test]
1228 fn on_loop_creates_composite() {
1229 let m = M::on_loop(|_iteration| {});
1230 assert_eq!(m.len(), 1);
1231 }
1232
1233 #[test]
1234 fn on_timeout_creates_composite() {
1235 let m = M::on_timeout(|| {});
1236 assert_eq!(m.len(), 1);
1237 }
1238
1239 #[test]
1240 fn on_route_creates_composite() {
1241 let m = M::on_route(|_name| {});
1242 assert_eq!(m.len(), 1);
1243 }
1244
1245 #[test]
1246 fn on_fallback_creates_composite() {
1247 let m = M::on_fallback(|_name| {});
1248 assert_eq!(m.len(), 1);
1249 }
1250
1251 #[test]
1252 fn compose_all_middleware() {
1253 let m = M::log()
1254 >> M::latency()
1255 >> M::timeout(Duration::from_secs(30))
1256 >> M::retry(3)
1257 >> M::cost()
1258 >> M::rate_limit(10)
1259 >> M::circuit_breaker(5)
1260 >> M::trace()
1261 >> M::audit()
1262 >> M::validate(|_| Ok(()))
1263 >> M::fallback_model("gemini-1.5-flash")
1264 >> M::cache()
1265 >> M::dedup()
1266 >> M::sample(0.5)
1267 >> M::metrics()
1268 >> M::before_agent(|_| Ok(()))
1269 >> M::after_agent(|_| Ok(()))
1270 >> M::before_model(|_| Ok(()))
1271 >> M::after_model(|_, _| Ok(()))
1272 >> M::on_loop(|_| {})
1273 >> M::on_timeout(|| {})
1274 >> M::on_route(|_| {})
1275 >> M::on_fallback(|_| {});
1276 assert_eq!(m.len(), 23);
1277 }
1278}