1use std::sync::Arc;
13use std::time::Duration;
14
15use a2a_protocol_types::agent_card::AgentCard;
16
17use crate::error::ServerResult;
18use crate::executor::AgentExecutor;
19use crate::handler::{HandlerLimits, RequestHandler};
20use crate::interceptor::{ServerInterceptor, ServerInterceptorChain};
21use crate::metrics::{Metrics, NoopMetrics};
22use crate::push::{InMemoryPushConfigStore, PushConfigStore, PushSender};
23use crate::store::{InMemoryTaskStore, TaskStore, TaskStoreConfig};
24use crate::streaming::EventQueueManager;
25use crate::tenant_config::PerTenantConfig;
26use crate::tenant_resolver::TenantResolver;
27
28pub const DEFAULT_MAX_CONCURRENT_STREAMS: usize = 1024;
37
38pub const DEFAULT_EXECUTOR_TIMEOUT: Duration = Duration::from_secs(3600);
70
71pub struct RequestHandlerBuilder {
95 executor: Arc<dyn AgentExecutor>,
96 task_store: Option<Arc<dyn TaskStore>>,
97 task_store_config: TaskStoreConfig,
98 push_config_store: Option<Arc<dyn PushConfigStore>>,
99 push_sender: Option<Arc<dyn PushSender>>,
100 interceptors: ServerInterceptorChain,
101 agent_card: Option<AgentCard>,
102 executor_timeout: Option<Duration>,
103 event_queue_capacity: Option<usize>,
104 max_event_size: Option<usize>,
105 max_concurrent_streams: Option<usize>,
106 metrics: Arc<dyn Metrics>,
107 handler_limits: HandlerLimits,
108 tenant_resolver: Option<Arc<dyn TenantResolver>>,
109 tenant_config: Option<PerTenantConfig>,
110 require_resolved_tenant: bool,
111 allow_unauthenticated_extended_card: bool,
112}
113
114impl RequestHandlerBuilder {
115 #[must_use]
119 pub fn new(executor: impl AgentExecutor) -> Self {
120 Self {
121 executor: Arc::new(executor),
122 task_store: None,
123 task_store_config: TaskStoreConfig::default(),
124 push_config_store: None,
125 push_sender: None,
126 interceptors: ServerInterceptorChain::new(),
127 agent_card: None,
128 executor_timeout: Some(DEFAULT_EXECUTOR_TIMEOUT),
129 event_queue_capacity: None,
130 max_event_size: None,
131 max_concurrent_streams: None,
132 metrics: Arc::new(NoopMetrics),
133 handler_limits: HandlerLimits::default(),
134 tenant_resolver: None,
135 tenant_config: None,
136 require_resolved_tenant: false,
137 allow_unauthenticated_extended_card: false,
138 }
139 }
140
141 #[must_use]
143 pub fn with_task_store(mut self, store: impl TaskStore + 'static) -> Self {
144 self.task_store = Some(Arc::new(store));
145 self
146 }
147
148 #[must_use]
153 pub fn with_task_store_arc(mut self, store: Arc<dyn TaskStore>) -> Self {
154 self.task_store = Some(store);
155 self
156 }
157
158 #[must_use]
166 pub fn with_task_store_config(mut self, config: TaskStoreConfig) -> Self {
167 debug_assert!(
168 self.task_store.is_none(),
169 "with_task_store_config() called after with_task_store(); \
170 the config will be ignored because a custom store was already set"
171 );
172 self.task_store_config = config;
173 self
174 }
175
176 #[must_use]
178 pub fn with_push_config_store(mut self, store: impl PushConfigStore + 'static) -> Self {
179 self.push_config_store = Some(Arc::new(store));
180 self
181 }
182
183 #[must_use]
185 pub fn with_push_sender(mut self, sender: impl PushSender + 'static) -> Self {
186 self.push_sender = Some(Arc::new(sender));
187 self
188 }
189
190 #[must_use]
192 pub fn with_interceptor(mut self, interceptor: impl ServerInterceptor + 'static) -> Self {
193 self.interceptors.push(Arc::new(interceptor));
194 self
195 }
196
197 #[must_use]
207 pub const fn with_executor_timeout(mut self, timeout: Duration) -> Self {
208 self.executor_timeout = Some(timeout);
209 self
210 }
211
212 #[must_use]
223 pub const fn without_executor_timeout(mut self) -> Self {
224 self.executor_timeout = None;
225 self
226 }
227
228 #[must_use]
230 pub fn with_agent_card(mut self, card: AgentCard) -> Self {
231 self.agent_card = Some(card);
232 self
233 }
234
235 #[must_use]
243 pub const fn with_event_queue_capacity(mut self, capacity: usize) -> Self {
244 self.event_queue_capacity = Some(capacity);
245 self
246 }
247
248 #[must_use]
253 pub const fn with_max_event_size(mut self, max_event_size: usize) -> Self {
254 self.max_event_size = Some(max_event_size);
255 self
256 }
257
258 #[must_use]
265 pub const fn with_max_concurrent_streams(mut self, max: usize) -> Self {
266 self.max_concurrent_streams = Some(max);
267 self
268 }
269
270 #[must_use]
274 pub const fn with_handler_limits(mut self, limits: HandlerLimits) -> Self {
275 self.handler_limits = limits;
276 self
277 }
278
279 #[must_use]
283 pub fn with_metrics(mut self, metrics: impl Metrics + 'static) -> Self {
284 self.metrics = Arc::new(metrics);
285 self
286 }
287
288 #[must_use]
304 pub fn with_tenant_resolver(mut self, resolver: impl TenantResolver) -> Self {
305 self.tenant_resolver = Some(Arc::new(resolver));
306 self
307 }
308
309 #[must_use]
321 pub const fn require_resolved_tenant(mut self) -> Self {
322 self.require_resolved_tenant = true;
323 self
324 }
325
326 #[must_use]
339 pub const fn allow_unauthenticated_extended_card(mut self) -> Self {
340 self.allow_unauthenticated_extended_card = true;
341 self
342 }
343
344 #[must_use]
358 pub fn with_tenant_config(mut self, config: PerTenantConfig) -> Self {
359 self.tenant_config = Some(config);
360 self
361 }
362
363 #[allow(clippy::too_many_lines)]
371 pub fn build(self) -> ServerResult<RequestHandler> {
372 if let Some(ref card) = self.agent_card {
374 if card.supported_interfaces.is_empty() {
375 return Err(crate::error::ServerError::InvalidParams(
376 "agent card must have at least one supported interface".into(),
377 ));
378 }
379 }
380
381 if let Some(timeout) = self.executor_timeout {
383 if timeout.is_zero() {
384 return Err(crate::error::ServerError::InvalidParams(
385 "executor timeout must be greater than zero".into(),
386 ));
387 }
388 }
389
390 if self.handler_limits.max_id_length == 0 {
392 return Err(crate::error::ServerError::InvalidParams(
393 "max_id_length must be greater than zero".into(),
394 ));
395 }
396 if self.handler_limits.max_metadata_size == 0 {
397 return Err(crate::error::ServerError::InvalidParams(
398 "max_metadata_size must be greater than zero".into(),
399 ));
400 }
401 if self.handler_limits.push_delivery_timeout.is_zero() {
402 return Err(crate::error::ServerError::InvalidParams(
403 "push_delivery_timeout must be greater than zero".into(),
404 ));
405 }
406 let (required_extensions, declared_extensions) = self
409 .agent_card
410 .as_ref()
411 .and_then(|c| c.capabilities.extensions.as_ref())
412 .map(|exts| {
413 let declared: Vec<String> = exts.iter().map(|e| e.uri.clone()).collect();
414 let required: Vec<String> = exts
415 .iter()
416 .filter(|e| e.required == Some(true))
417 .map(|e| e.uri.clone())
418 .collect();
419 (required, declared)
420 })
421 .unwrap_or_default();
422
423 Ok(RequestHandler {
424 executor: self.executor,
425 task_store: self.task_store.unwrap_or_else(|| {
426 Arc::new(InMemoryTaskStore::with_config(self.task_store_config))
427 }),
428 push_config_store: self
429 .push_config_store
430 .unwrap_or_else(|| Arc::new(InMemoryPushConfigStore::new())),
431 push_sender: self.push_sender,
432 event_queue_manager: {
433 let mut mgr = self
434 .event_queue_capacity
435 .map_or_else(EventQueueManager::new, EventQueueManager::with_capacity);
436 if let Some(max_size) = self.max_event_size {
437 mgr = mgr.with_max_event_size(max_size);
438 }
439 mgr = mgr.with_max_concurrent_queues(
440 self.max_concurrent_streams
441 .unwrap_or(DEFAULT_MAX_CONCURRENT_STREAMS),
442 );
443 mgr = mgr.with_metrics(Arc::clone(&self.metrics));
444 mgr
445 },
446 interceptors: self.interceptors,
447 agent_card: self.agent_card,
448 executor_timeout: self.executor_timeout,
449 metrics: self.metrics,
450 limits: self.handler_limits,
451 tenant_resolver: self.tenant_resolver,
452 require_resolved_tenant: self.require_resolved_tenant,
453 allow_unauthenticated_extended_card: self.allow_unauthenticated_extended_card,
454 required_extensions,
455 declared_extensions,
456 tenant_config: self.tenant_config,
457 cancellation_tokens: Arc::new(tokio::sync::RwLock::new(
458 std::collections::HashMap::new(),
459 )),
460 context_locks: Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new())),
461 tenant_slots: Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new())),
462 })
463 }
464}
465
466impl std::fmt::Debug for RequestHandlerBuilder {
467 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468 f.debug_struct("RequestHandlerBuilder")
469 .field("executor", &"<dyn AgentExecutor>")
470 .field("task_store", &self.task_store.is_some())
471 .field("task_store_config", &self.task_store_config)
472 .field("push_config_store", &self.push_config_store.is_some())
473 .field("push_sender", &self.push_sender.is_some())
474 .field("interceptors", &self.interceptors)
475 .field("agent_card", &self.agent_card.is_some())
476 .field("executor_timeout", &self.executor_timeout)
477 .field("event_queue_capacity", &self.event_queue_capacity)
478 .field("max_event_size", &self.max_event_size)
479 .field("max_concurrent_streams", &self.max_concurrent_streams)
480 .field("metrics", &"<dyn Metrics>")
481 .field("handler_limits", &self.handler_limits)
482 .field("tenant_resolver", &self.tenant_resolver.is_some())
483 .field("tenant_config", &self.tenant_config)
484 .field("require_resolved_tenant", &self.require_resolved_tenant)
485 .field(
486 "allow_unauthenticated_extended_card",
487 &self.allow_unauthenticated_extended_card,
488 )
489 .finish()
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496 use crate::agent_executor;
497
498 struct TestExecutor;
499
500 agent_executor!(TestExecutor, |_ctx, _queue| async { Ok(()) });
501
502 #[test]
503 fn builder_defaults_build_ok() {
504 let handler = RequestHandlerBuilder::new(TestExecutor).build();
505 let h = handler.expect("default builder should succeed");
506 assert!(
507 h.tenant_resolver().is_none(),
508 "default builder should have no tenant resolver"
509 );
510 assert!(
511 h.tenant_config().is_none(),
512 "default builder should have no tenant config"
513 );
514 }
515
516 #[test]
517 fn builder_zero_executor_timeout_errors() {
518 let result = RequestHandlerBuilder::new(TestExecutor)
519 .with_executor_timeout(Duration::ZERO)
520 .build();
521 assert!(result.is_err());
522 }
523
524 #[test]
525 fn builder_empty_agent_card_interfaces_errors() {
526 use a2a_protocol_types::{AgentCapabilities, AgentCard};
527
528 let card = AgentCard {
529 url: None,
530 name: "empty".into(),
531 version: "1.0".into(),
532 description: "No interfaces".into(),
533 supported_interfaces: vec![],
534 provider: None,
535 icon_url: None,
536 documentation_url: None,
537 capabilities: AgentCapabilities::none(),
538 security_schemes: None,
539 security_requirements: None,
540 default_input_modes: vec![],
541 default_output_modes: vec![],
542 skills: vec![],
543 signatures: None,
544 };
545
546 let result = RequestHandlerBuilder::new(TestExecutor)
547 .with_agent_card(card)
548 .build();
549 assert!(result.is_err());
550 }
551
552 #[test]
553 fn builder_with_all_options() {
554 use a2a_protocol_types::{AgentCapabilities, AgentCard, AgentInterface};
555
556 let card = AgentCard {
557 url: None,
558 name: "test".into(),
559 version: "1.0".into(),
560 description: "Test agent".into(),
561 supported_interfaces: vec![AgentInterface {
562 url: "http://localhost:8080".into(),
563 protocol_binding: "JSONRPC".into(),
564 protocol_version: "1.0.0".into(),
565 tenant: None,
566 }],
567 provider: None,
568 icon_url: None,
569 documentation_url: None,
570 capabilities: AgentCapabilities::none(),
571 security_schemes: None,
572 security_requirements: None,
573 default_input_modes: vec![],
574 default_output_modes: vec![],
575 skills: vec![],
576 signatures: None,
577 };
578
579 let result = RequestHandlerBuilder::new(TestExecutor)
580 .with_agent_card(card)
581 .with_executor_timeout(Duration::from_secs(30))
582 .with_event_queue_capacity(128)
583 .with_max_event_size(1024 * 1024)
584 .with_max_concurrent_streams(10)
585 .with_handler_limits(HandlerLimits::default().with_max_id_length(2048))
586 .build();
587 let h = result.expect("builder with all options should succeed");
588 assert!(h.tenant_resolver().is_none(), "no tenant resolver set");
589 }
590
591 #[test]
592 fn builder_with_tenant_resolver_and_config() {
593 use crate::tenant_config::{PerTenantConfig, TenantLimits};
594 use crate::tenant_resolver::HeaderTenantResolver;
595
596 let handler = RequestHandlerBuilder::new(TestExecutor)
597 .with_tenant_resolver(HeaderTenantResolver::default())
598 .with_tenant_config(
599 PerTenantConfig::builder()
600 .default_limits(TenantLimits::builder().rate_limit_rps(100).build())
601 .with_override(
602 "premium",
603 TenantLimits::builder().rate_limit_rps(1000).build(),
604 )
605 .build(),
606 )
607 .build();
608 let handler = handler.expect("builder with tenant resolver and config should succeed");
609 assert!(handler.tenant_resolver().is_some());
610 assert!(handler.tenant_config().is_some());
611 assert_eq!(
612 handler
613 .tenant_config()
614 .unwrap()
615 .get("premium")
616 .rate_limit_rps,
617 Some(1000)
618 );
619 assert_eq!(
620 handler
621 .tenant_config()
622 .unwrap()
623 .get("unknown")
624 .rate_limit_rps,
625 Some(100)
626 );
627 }
628
629 #[test]
630 fn builder_without_tenant_fields() {
631 let handler = RequestHandlerBuilder::new(TestExecutor).build().unwrap();
632 assert!(handler.tenant_resolver().is_none());
633 assert!(handler.tenant_config().is_none());
634 }
635
636 #[test]
640 fn builder_default_caps_concurrent_streams() {
641 let handler = RequestHandlerBuilder::new(TestExecutor).build().unwrap();
642 let debug = format!("{:?}", handler.event_queue_manager);
643 assert!(
644 debug.contains(&format!(
645 "max_concurrent_queues: Some({DEFAULT_MAX_CONCURRENT_STREAMS})"
646 )),
647 "default builder should cap concurrent streams at \
648 {DEFAULT_MAX_CONCURRENT_STREAMS}, got: {debug}"
649 );
650 }
651
652 #[test]
654 fn builder_max_concurrent_streams_override_wins() {
655 let handler = RequestHandlerBuilder::new(TestExecutor)
656 .with_max_concurrent_streams(7)
657 .build()
658 .unwrap();
659 let debug = format!("{:?}", handler.event_queue_manager);
660 assert!(
661 debug.contains("max_concurrent_queues: Some(7)"),
662 "explicit cap should override the default, got: {debug}"
663 );
664 }
665
666 #[test]
667 fn builder_debug_does_not_panic() {
668 let builder = RequestHandlerBuilder::new(TestExecutor);
669 let debug = format!("{builder:?}");
670 assert!(debug.contains("RequestHandlerBuilder"));
671 }
672
673 #[test]
674 fn builder_with_push_config_store_builds_ok() {
675 use crate::push::InMemoryPushConfigStore;
676 let result = RequestHandlerBuilder::new(TestExecutor)
677 .with_push_config_store(InMemoryPushConfigStore::new())
678 .build();
679 let _h = result.expect("builder with push config store should succeed");
680 }
681
682 #[test]
683 fn builder_zero_max_id_length_errors() {
684 let result = RequestHandlerBuilder::new(TestExecutor)
685 .with_handler_limits(HandlerLimits::default().with_max_id_length(0))
686 .build();
687 assert!(result.is_err(), "zero max_id_length should be rejected");
688 }
689
690 #[test]
691 fn builder_zero_max_metadata_size_errors() {
692 let result = RequestHandlerBuilder::new(TestExecutor)
693 .with_handler_limits(HandlerLimits::default().with_max_metadata_size(0))
694 .build();
695 assert!(result.is_err(), "zero max_metadata_size should be rejected");
696 }
697
698 #[test]
699 fn builder_zero_push_delivery_timeout_errors() {
700 let result = RequestHandlerBuilder::new(TestExecutor)
701 .with_handler_limits(
702 HandlerLimits::default().with_push_delivery_timeout(Duration::ZERO),
703 )
704 .build();
705 assert!(
706 result.is_err(),
707 "zero push_delivery_timeout should be rejected"
708 );
709 }
710}