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]
297 pub fn with_tenant_resolver(mut self, resolver: impl TenantResolver) -> Self {
298 self.tenant_resolver = Some(Arc::new(resolver));
299 self
300 }
301
302 #[must_use]
314 pub const fn require_resolved_tenant(mut self) -> Self {
315 self.require_resolved_tenant = true;
316 self
317 }
318
319 #[must_use]
332 pub const fn allow_unauthenticated_extended_card(mut self) -> Self {
333 self.allow_unauthenticated_extended_card = true;
334 self
335 }
336
337 #[must_use]
346 pub fn with_tenant_config(mut self, config: PerTenantConfig) -> Self {
347 self.tenant_config = Some(config);
348 self
349 }
350
351 #[allow(clippy::too_many_lines)]
359 pub fn build(self) -> ServerResult<RequestHandler> {
360 if let Some(ref card) = self.agent_card {
362 if card.supported_interfaces.is_empty() {
363 return Err(crate::error::ServerError::InvalidParams(
364 "agent card must have at least one supported interface".into(),
365 ));
366 }
367 }
368
369 if let Some(timeout) = self.executor_timeout {
371 if timeout.is_zero() {
372 return Err(crate::error::ServerError::InvalidParams(
373 "executor timeout must be greater than zero".into(),
374 ));
375 }
376 }
377
378 if self.handler_limits.max_id_length == 0 {
380 return Err(crate::error::ServerError::InvalidParams(
381 "max_id_length must be greater than zero".into(),
382 ));
383 }
384 if self.handler_limits.max_metadata_size == 0 {
385 return Err(crate::error::ServerError::InvalidParams(
386 "max_metadata_size must be greater than zero".into(),
387 ));
388 }
389 if self.handler_limits.push_delivery_timeout.is_zero() {
390 return Err(crate::error::ServerError::InvalidParams(
391 "push_delivery_timeout must be greater than zero".into(),
392 ));
393 }
394 let (required_extensions, declared_extensions) = self
397 .agent_card
398 .as_ref()
399 .and_then(|c| c.capabilities.extensions.as_ref())
400 .map(|exts| {
401 let declared: Vec<String> = exts.iter().map(|e| e.uri.clone()).collect();
402 let required: Vec<String> = exts
403 .iter()
404 .filter(|e| e.required == Some(true))
405 .map(|e| e.uri.clone())
406 .collect();
407 (required, declared)
408 })
409 .unwrap_or_default();
410
411 Ok(RequestHandler {
412 executor: self.executor,
413 task_store: self.task_store.unwrap_or_else(|| {
414 Arc::new(InMemoryTaskStore::with_config(self.task_store_config))
415 }),
416 push_config_store: self
417 .push_config_store
418 .unwrap_or_else(|| Arc::new(InMemoryPushConfigStore::new())),
419 push_sender: self.push_sender,
420 event_queue_manager: {
421 let mut mgr = self
422 .event_queue_capacity
423 .map_or_else(EventQueueManager::new, EventQueueManager::with_capacity);
424 if let Some(max_size) = self.max_event_size {
425 mgr = mgr.with_max_event_size(max_size);
426 }
427 mgr = mgr.with_max_concurrent_queues(
428 self.max_concurrent_streams
429 .unwrap_or(DEFAULT_MAX_CONCURRENT_STREAMS),
430 );
431 mgr = mgr.with_metrics(Arc::clone(&self.metrics));
432 mgr
433 },
434 interceptors: self.interceptors,
435 agent_card: self.agent_card,
436 executor_timeout: self.executor_timeout,
437 metrics: self.metrics,
438 limits: self.handler_limits,
439 tenant_resolver: self.tenant_resolver,
440 require_resolved_tenant: self.require_resolved_tenant,
441 allow_unauthenticated_extended_card: self.allow_unauthenticated_extended_card,
442 required_extensions,
443 declared_extensions,
444 tenant_config: self.tenant_config,
445 cancellation_tokens: Arc::new(tokio::sync::RwLock::new(
446 std::collections::HashMap::new(),
447 )),
448 context_locks: Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new())),
449 })
450 }
451}
452
453impl std::fmt::Debug for RequestHandlerBuilder {
454 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
455 f.debug_struct("RequestHandlerBuilder")
456 .field("executor", &"<dyn AgentExecutor>")
457 .field("task_store", &self.task_store.is_some())
458 .field("task_store_config", &self.task_store_config)
459 .field("push_config_store", &self.push_config_store.is_some())
460 .field("push_sender", &self.push_sender.is_some())
461 .field("interceptors", &self.interceptors)
462 .field("agent_card", &self.agent_card.is_some())
463 .field("executor_timeout", &self.executor_timeout)
464 .field("event_queue_capacity", &self.event_queue_capacity)
465 .field("max_event_size", &self.max_event_size)
466 .field("max_concurrent_streams", &self.max_concurrent_streams)
467 .field("metrics", &"<dyn Metrics>")
468 .field("handler_limits", &self.handler_limits)
469 .field("tenant_resolver", &self.tenant_resolver.is_some())
470 .field("tenant_config", &self.tenant_config)
471 .field("require_resolved_tenant", &self.require_resolved_tenant)
472 .field(
473 "allow_unauthenticated_extended_card",
474 &self.allow_unauthenticated_extended_card,
475 )
476 .finish()
477 }
478}
479
480#[cfg(test)]
481mod tests {
482 use super::*;
483 use crate::agent_executor;
484
485 struct TestExecutor;
486
487 agent_executor!(TestExecutor, |_ctx, _queue| async { Ok(()) });
488
489 #[test]
490 fn builder_defaults_build_ok() {
491 let handler = RequestHandlerBuilder::new(TestExecutor).build();
492 let h = handler.expect("default builder should succeed");
493 assert!(
494 h.tenant_resolver().is_none(),
495 "default builder should have no tenant resolver"
496 );
497 assert!(
498 h.tenant_config().is_none(),
499 "default builder should have no tenant config"
500 );
501 }
502
503 #[test]
504 fn builder_zero_executor_timeout_errors() {
505 let result = RequestHandlerBuilder::new(TestExecutor)
506 .with_executor_timeout(Duration::ZERO)
507 .build();
508 assert!(result.is_err());
509 }
510
511 #[test]
512 fn builder_empty_agent_card_interfaces_errors() {
513 use a2a_protocol_types::{AgentCapabilities, AgentCard};
514
515 let card = AgentCard {
516 url: None,
517 name: "empty".into(),
518 version: "1.0".into(),
519 description: "No interfaces".into(),
520 supported_interfaces: vec![],
521 provider: None,
522 icon_url: None,
523 documentation_url: None,
524 capabilities: AgentCapabilities::none(),
525 security_schemes: None,
526 security_requirements: None,
527 default_input_modes: vec![],
528 default_output_modes: vec![],
529 skills: vec![],
530 signatures: None,
531 };
532
533 let result = RequestHandlerBuilder::new(TestExecutor)
534 .with_agent_card(card)
535 .build();
536 assert!(result.is_err());
537 }
538
539 #[test]
540 fn builder_with_all_options() {
541 use a2a_protocol_types::{AgentCapabilities, AgentCard, AgentInterface};
542
543 let card = AgentCard {
544 url: None,
545 name: "test".into(),
546 version: "1.0".into(),
547 description: "Test agent".into(),
548 supported_interfaces: vec![AgentInterface {
549 url: "http://localhost:8080".into(),
550 protocol_binding: "JSONRPC".into(),
551 protocol_version: "1.0.0".into(),
552 tenant: None,
553 }],
554 provider: None,
555 icon_url: None,
556 documentation_url: None,
557 capabilities: AgentCapabilities::none(),
558 security_schemes: None,
559 security_requirements: None,
560 default_input_modes: vec![],
561 default_output_modes: vec![],
562 skills: vec![],
563 signatures: None,
564 };
565
566 let result = RequestHandlerBuilder::new(TestExecutor)
567 .with_agent_card(card)
568 .with_executor_timeout(Duration::from_secs(30))
569 .with_event_queue_capacity(128)
570 .with_max_event_size(1024 * 1024)
571 .with_max_concurrent_streams(10)
572 .with_handler_limits(HandlerLimits::default().with_max_id_length(2048))
573 .build();
574 let h = result.expect("builder with all options should succeed");
575 assert!(h.tenant_resolver().is_none(), "no tenant resolver set");
576 }
577
578 #[test]
579 fn builder_with_tenant_resolver_and_config() {
580 use crate::tenant_config::{PerTenantConfig, TenantLimits};
581 use crate::tenant_resolver::HeaderTenantResolver;
582
583 let handler = RequestHandlerBuilder::new(TestExecutor)
584 .with_tenant_resolver(HeaderTenantResolver::default())
585 .with_tenant_config(
586 PerTenantConfig::builder()
587 .default_limits(TenantLimits::builder().rate_limit_rps(100).build())
588 .with_override(
589 "premium",
590 TenantLimits::builder().rate_limit_rps(1000).build(),
591 )
592 .build(),
593 )
594 .build();
595 let handler = handler.expect("builder with tenant resolver and config should succeed");
596 assert!(handler.tenant_resolver().is_some());
597 assert!(handler.tenant_config().is_some());
598 assert_eq!(
599 handler
600 .tenant_config()
601 .unwrap()
602 .get("premium")
603 .rate_limit_rps,
604 Some(1000)
605 );
606 assert_eq!(
607 handler
608 .tenant_config()
609 .unwrap()
610 .get("unknown")
611 .rate_limit_rps,
612 Some(100)
613 );
614 }
615
616 #[test]
617 fn builder_without_tenant_fields() {
618 let handler = RequestHandlerBuilder::new(TestExecutor).build().unwrap();
619 assert!(handler.tenant_resolver().is_none());
620 assert!(handler.tenant_config().is_none());
621 }
622
623 #[test]
627 fn builder_default_caps_concurrent_streams() {
628 let handler = RequestHandlerBuilder::new(TestExecutor).build().unwrap();
629 let debug = format!("{:?}", handler.event_queue_manager);
630 assert!(
631 debug.contains(&format!(
632 "max_concurrent_queues: Some({DEFAULT_MAX_CONCURRENT_STREAMS})"
633 )),
634 "default builder should cap concurrent streams at \
635 {DEFAULT_MAX_CONCURRENT_STREAMS}, got: {debug}"
636 );
637 }
638
639 #[test]
641 fn builder_max_concurrent_streams_override_wins() {
642 let handler = RequestHandlerBuilder::new(TestExecutor)
643 .with_max_concurrent_streams(7)
644 .build()
645 .unwrap();
646 let debug = format!("{:?}", handler.event_queue_manager);
647 assert!(
648 debug.contains("max_concurrent_queues: Some(7)"),
649 "explicit cap should override the default, got: {debug}"
650 );
651 }
652
653 #[test]
654 fn builder_debug_does_not_panic() {
655 let builder = RequestHandlerBuilder::new(TestExecutor);
656 let debug = format!("{builder:?}");
657 assert!(debug.contains("RequestHandlerBuilder"));
658 }
659
660 #[test]
661 fn builder_with_push_config_store_builds_ok() {
662 use crate::push::InMemoryPushConfigStore;
663 let result = RequestHandlerBuilder::new(TestExecutor)
664 .with_push_config_store(InMemoryPushConfigStore::new())
665 .build();
666 let _h = result.expect("builder with push config store should succeed");
667 }
668
669 #[test]
670 fn builder_zero_max_id_length_errors() {
671 let result = RequestHandlerBuilder::new(TestExecutor)
672 .with_handler_limits(HandlerLimits::default().with_max_id_length(0))
673 .build();
674 assert!(result.is_err(), "zero max_id_length should be rejected");
675 }
676
677 #[test]
678 fn builder_zero_max_metadata_size_errors() {
679 let result = RequestHandlerBuilder::new(TestExecutor)
680 .with_handler_limits(HandlerLimits::default().with_max_metadata_size(0))
681 .build();
682 assert!(result.is_err(), "zero max_metadata_size should be rejected");
683 }
684
685 #[test]
686 fn builder_zero_push_delivery_timeout_errors() {
687 let result = RequestHandlerBuilder::new(TestExecutor)
688 .with_handler_limits(
689 HandlerLimits::default().with_push_delivery_timeout(Duration::ZERO),
690 )
691 .build();
692 assert!(
693 result.is_err(),
694 "zero push_delivery_timeout should be rejected"
695 );
696 }
697}