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 struct RequestHandlerBuilder {
60 executor: Arc<dyn AgentExecutor>,
61 task_store: Option<Arc<dyn TaskStore>>,
62 task_store_config: TaskStoreConfig,
63 push_config_store: Option<Arc<dyn PushConfigStore>>,
64 push_sender: Option<Arc<dyn PushSender>>,
65 interceptors: ServerInterceptorChain,
66 agent_card: Option<AgentCard>,
67 executor_timeout: Option<Duration>,
68 event_queue_capacity: Option<usize>,
69 max_event_size: Option<usize>,
70 max_concurrent_streams: Option<usize>,
71 metrics: Arc<dyn Metrics>,
72 handler_limits: HandlerLimits,
73 tenant_resolver: Option<Arc<dyn TenantResolver>>,
74 tenant_config: Option<PerTenantConfig>,
75 require_resolved_tenant: bool,
76 allow_unauthenticated_extended_card: bool,
77}
78
79impl RequestHandlerBuilder {
80 #[must_use]
84 pub fn new(executor: impl AgentExecutor) -> Self {
85 Self {
86 executor: Arc::new(executor),
87 task_store: None,
88 task_store_config: TaskStoreConfig::default(),
89 push_config_store: None,
90 push_sender: None,
91 interceptors: ServerInterceptorChain::new(),
92 agent_card: None,
93 executor_timeout: None,
94 event_queue_capacity: None,
95 max_event_size: None,
96 max_concurrent_streams: None,
97 metrics: Arc::new(NoopMetrics),
98 handler_limits: HandlerLimits::default(),
99 tenant_resolver: None,
100 tenant_config: None,
101 require_resolved_tenant: false,
102 allow_unauthenticated_extended_card: false,
103 }
104 }
105
106 #[must_use]
108 pub fn with_task_store(mut self, store: impl TaskStore + 'static) -> Self {
109 self.task_store = Some(Arc::new(store));
110 self
111 }
112
113 #[must_use]
118 pub fn with_task_store_arc(mut self, store: Arc<dyn TaskStore>) -> Self {
119 self.task_store = Some(store);
120 self
121 }
122
123 #[must_use]
131 pub fn with_task_store_config(mut self, config: TaskStoreConfig) -> Self {
132 debug_assert!(
133 self.task_store.is_none(),
134 "with_task_store_config() called after with_task_store(); \
135 the config will be ignored because a custom store was already set"
136 );
137 self.task_store_config = config;
138 self
139 }
140
141 #[must_use]
143 pub fn with_push_config_store(mut self, store: impl PushConfigStore + 'static) -> Self {
144 self.push_config_store = Some(Arc::new(store));
145 self
146 }
147
148 #[must_use]
150 pub fn with_push_sender(mut self, sender: impl PushSender + 'static) -> Self {
151 self.push_sender = Some(Arc::new(sender));
152 self
153 }
154
155 #[must_use]
157 pub fn with_interceptor(mut self, interceptor: impl ServerInterceptor + 'static) -> Self {
158 self.interceptors.push(Arc::new(interceptor));
159 self
160 }
161
162 #[must_use]
172 pub const fn with_executor_timeout(mut self, timeout: Duration) -> Self {
173 self.executor_timeout = Some(timeout);
174 self
175 }
176
177 #[must_use]
179 pub fn with_agent_card(mut self, card: AgentCard) -> Self {
180 self.agent_card = Some(card);
181 self
182 }
183
184 #[must_use]
192 pub const fn with_event_queue_capacity(mut self, capacity: usize) -> Self {
193 self.event_queue_capacity = Some(capacity);
194 self
195 }
196
197 #[must_use]
202 pub const fn with_max_event_size(mut self, max_event_size: usize) -> Self {
203 self.max_event_size = Some(max_event_size);
204 self
205 }
206
207 #[must_use]
214 pub const fn with_max_concurrent_streams(mut self, max: usize) -> Self {
215 self.max_concurrent_streams = Some(max);
216 self
217 }
218
219 #[must_use]
223 pub const fn with_handler_limits(mut self, limits: HandlerLimits) -> Self {
224 self.handler_limits = limits;
225 self
226 }
227
228 #[must_use]
232 pub fn with_metrics(mut self, metrics: impl Metrics + 'static) -> Self {
233 self.metrics = Arc::new(metrics);
234 self
235 }
236
237 #[must_use]
246 pub fn with_tenant_resolver(mut self, resolver: impl TenantResolver) -> Self {
247 self.tenant_resolver = Some(Arc::new(resolver));
248 self
249 }
250
251 #[must_use]
263 pub const fn require_resolved_tenant(mut self) -> Self {
264 self.require_resolved_tenant = true;
265 self
266 }
267
268 #[must_use]
281 pub const fn allow_unauthenticated_extended_card(mut self) -> Self {
282 self.allow_unauthenticated_extended_card = true;
283 self
284 }
285
286 #[must_use]
295 pub fn with_tenant_config(mut self, config: PerTenantConfig) -> Self {
296 self.tenant_config = Some(config);
297 self
298 }
299
300 #[allow(clippy::too_many_lines)]
308 pub fn build(self) -> ServerResult<RequestHandler> {
309 if let Some(ref card) = self.agent_card {
311 if card.supported_interfaces.is_empty() {
312 return Err(crate::error::ServerError::InvalidParams(
313 "agent card must have at least one supported interface".into(),
314 ));
315 }
316 }
317
318 if let Some(timeout) = self.executor_timeout {
320 if timeout.is_zero() {
321 return Err(crate::error::ServerError::InvalidParams(
322 "executor timeout must be greater than zero".into(),
323 ));
324 }
325 }
326
327 if self.handler_limits.max_id_length == 0 {
329 return Err(crate::error::ServerError::InvalidParams(
330 "max_id_length must be greater than zero".into(),
331 ));
332 }
333 if self.handler_limits.max_metadata_size == 0 {
334 return Err(crate::error::ServerError::InvalidParams(
335 "max_metadata_size must be greater than zero".into(),
336 ));
337 }
338 if self.handler_limits.push_delivery_timeout.is_zero() {
339 return Err(crate::error::ServerError::InvalidParams(
340 "push_delivery_timeout must be greater than zero".into(),
341 ));
342 }
343 let (required_extensions, declared_extensions) = self
346 .agent_card
347 .as_ref()
348 .and_then(|c| c.capabilities.extensions.as_ref())
349 .map(|exts| {
350 let declared: Vec<String> = exts.iter().map(|e| e.uri.clone()).collect();
351 let required: Vec<String> = exts
352 .iter()
353 .filter(|e| e.required == Some(true))
354 .map(|e| e.uri.clone())
355 .collect();
356 (required, declared)
357 })
358 .unwrap_or_default();
359
360 Ok(RequestHandler {
361 executor: self.executor,
362 task_store: self.task_store.unwrap_or_else(|| {
363 Arc::new(InMemoryTaskStore::with_config(self.task_store_config))
364 }),
365 push_config_store: self
366 .push_config_store
367 .unwrap_or_else(|| Arc::new(InMemoryPushConfigStore::new())),
368 push_sender: self.push_sender,
369 event_queue_manager: {
370 let mut mgr = self
371 .event_queue_capacity
372 .map_or_else(EventQueueManager::new, EventQueueManager::with_capacity);
373 if let Some(max_size) = self.max_event_size {
374 mgr = mgr.with_max_event_size(max_size);
375 }
376 mgr = mgr.with_max_concurrent_queues(
377 self.max_concurrent_streams
378 .unwrap_or(DEFAULT_MAX_CONCURRENT_STREAMS),
379 );
380 mgr = mgr.with_metrics(Arc::clone(&self.metrics));
381 mgr
382 },
383 interceptors: self.interceptors,
384 agent_card: self.agent_card,
385 executor_timeout: self.executor_timeout,
386 metrics: self.metrics,
387 limits: self.handler_limits,
388 tenant_resolver: self.tenant_resolver,
389 require_resolved_tenant: self.require_resolved_tenant,
390 allow_unauthenticated_extended_card: self.allow_unauthenticated_extended_card,
391 required_extensions,
392 declared_extensions,
393 tenant_config: self.tenant_config,
394 cancellation_tokens: Arc::new(tokio::sync::RwLock::new(
395 std::collections::HashMap::new(),
396 )),
397 context_locks: Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new())),
398 })
399 }
400}
401
402impl std::fmt::Debug for RequestHandlerBuilder {
403 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
404 f.debug_struct("RequestHandlerBuilder")
405 .field("executor", &"<dyn AgentExecutor>")
406 .field("task_store", &self.task_store.is_some())
407 .field("task_store_config", &self.task_store_config)
408 .field("push_config_store", &self.push_config_store.is_some())
409 .field("push_sender", &self.push_sender.is_some())
410 .field("interceptors", &self.interceptors)
411 .field("agent_card", &self.agent_card.is_some())
412 .field("executor_timeout", &self.executor_timeout)
413 .field("event_queue_capacity", &self.event_queue_capacity)
414 .field("max_event_size", &self.max_event_size)
415 .field("max_concurrent_streams", &self.max_concurrent_streams)
416 .field("metrics", &"<dyn Metrics>")
417 .field("handler_limits", &self.handler_limits)
418 .field("tenant_resolver", &self.tenant_resolver.is_some())
419 .field("tenant_config", &self.tenant_config)
420 .field("require_resolved_tenant", &self.require_resolved_tenant)
421 .field(
422 "allow_unauthenticated_extended_card",
423 &self.allow_unauthenticated_extended_card,
424 )
425 .finish()
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use super::*;
432 use crate::agent_executor;
433
434 struct TestExecutor;
435
436 agent_executor!(TestExecutor, |_ctx, _queue| async { Ok(()) });
437
438 #[test]
439 fn builder_defaults_build_ok() {
440 let handler = RequestHandlerBuilder::new(TestExecutor).build();
441 let h = handler.expect("default builder should succeed");
442 assert!(
443 h.tenant_resolver().is_none(),
444 "default builder should have no tenant resolver"
445 );
446 assert!(
447 h.tenant_config().is_none(),
448 "default builder should have no tenant config"
449 );
450 }
451
452 #[test]
453 fn builder_zero_executor_timeout_errors() {
454 let result = RequestHandlerBuilder::new(TestExecutor)
455 .with_executor_timeout(Duration::ZERO)
456 .build();
457 assert!(result.is_err());
458 }
459
460 #[test]
461 fn builder_empty_agent_card_interfaces_errors() {
462 use a2a_protocol_types::{AgentCapabilities, AgentCard};
463
464 let card = AgentCard {
465 url: None,
466 name: "empty".into(),
467 version: "1.0".into(),
468 description: "No interfaces".into(),
469 supported_interfaces: vec![],
470 provider: None,
471 icon_url: None,
472 documentation_url: None,
473 capabilities: AgentCapabilities::none(),
474 security_schemes: None,
475 security_requirements: None,
476 default_input_modes: vec![],
477 default_output_modes: vec![],
478 skills: vec![],
479 signatures: None,
480 };
481
482 let result = RequestHandlerBuilder::new(TestExecutor)
483 .with_agent_card(card)
484 .build();
485 assert!(result.is_err());
486 }
487
488 #[test]
489 fn builder_with_all_options() {
490 use a2a_protocol_types::{AgentCapabilities, AgentCard, AgentInterface};
491
492 let card = AgentCard {
493 url: None,
494 name: "test".into(),
495 version: "1.0".into(),
496 description: "Test agent".into(),
497 supported_interfaces: vec![AgentInterface {
498 url: "http://localhost:8080".into(),
499 protocol_binding: "JSONRPC".into(),
500 protocol_version: "1.0.0".into(),
501 tenant: None,
502 }],
503 provider: None,
504 icon_url: None,
505 documentation_url: None,
506 capabilities: AgentCapabilities::none(),
507 security_schemes: None,
508 security_requirements: None,
509 default_input_modes: vec![],
510 default_output_modes: vec![],
511 skills: vec![],
512 signatures: None,
513 };
514
515 let result = RequestHandlerBuilder::new(TestExecutor)
516 .with_agent_card(card)
517 .with_executor_timeout(Duration::from_secs(30))
518 .with_event_queue_capacity(128)
519 .with_max_event_size(1024 * 1024)
520 .with_max_concurrent_streams(10)
521 .with_handler_limits(HandlerLimits::default().with_max_id_length(2048))
522 .build();
523 let h = result.expect("builder with all options should succeed");
524 assert!(h.tenant_resolver().is_none(), "no tenant resolver set");
525 }
526
527 #[test]
528 fn builder_with_tenant_resolver_and_config() {
529 use crate::tenant_config::{PerTenantConfig, TenantLimits};
530 use crate::tenant_resolver::HeaderTenantResolver;
531
532 let handler = RequestHandlerBuilder::new(TestExecutor)
533 .with_tenant_resolver(HeaderTenantResolver::default())
534 .with_tenant_config(
535 PerTenantConfig::builder()
536 .default_limits(TenantLimits::builder().rate_limit_rps(100).build())
537 .with_override(
538 "premium",
539 TenantLimits::builder().rate_limit_rps(1000).build(),
540 )
541 .build(),
542 )
543 .build();
544 let handler = handler.expect("builder with tenant resolver and config should succeed");
545 assert!(handler.tenant_resolver().is_some());
546 assert!(handler.tenant_config().is_some());
547 assert_eq!(
548 handler
549 .tenant_config()
550 .unwrap()
551 .get("premium")
552 .rate_limit_rps,
553 Some(1000)
554 );
555 assert_eq!(
556 handler
557 .tenant_config()
558 .unwrap()
559 .get("unknown")
560 .rate_limit_rps,
561 Some(100)
562 );
563 }
564
565 #[test]
566 fn builder_without_tenant_fields() {
567 let handler = RequestHandlerBuilder::new(TestExecutor).build().unwrap();
568 assert!(handler.tenant_resolver().is_none());
569 assert!(handler.tenant_config().is_none());
570 }
571
572 #[test]
576 fn builder_default_caps_concurrent_streams() {
577 let handler = RequestHandlerBuilder::new(TestExecutor).build().unwrap();
578 let debug = format!("{:?}", handler.event_queue_manager);
579 assert!(
580 debug.contains(&format!(
581 "max_concurrent_queues: Some({DEFAULT_MAX_CONCURRENT_STREAMS})"
582 )),
583 "default builder should cap concurrent streams at \
584 {DEFAULT_MAX_CONCURRENT_STREAMS}, got: {debug}"
585 );
586 }
587
588 #[test]
590 fn builder_max_concurrent_streams_override_wins() {
591 let handler = RequestHandlerBuilder::new(TestExecutor)
592 .with_max_concurrent_streams(7)
593 .build()
594 .unwrap();
595 let debug = format!("{:?}", handler.event_queue_manager);
596 assert!(
597 debug.contains("max_concurrent_queues: Some(7)"),
598 "explicit cap should override the default, got: {debug}"
599 );
600 }
601
602 #[test]
603 fn builder_debug_does_not_panic() {
604 let builder = RequestHandlerBuilder::new(TestExecutor);
605 let debug = format!("{builder:?}");
606 assert!(debug.contains("RequestHandlerBuilder"));
607 }
608
609 #[test]
610 fn builder_with_push_config_store_builds_ok() {
611 use crate::push::InMemoryPushConfigStore;
612 let result = RequestHandlerBuilder::new(TestExecutor)
613 .with_push_config_store(InMemoryPushConfigStore::new())
614 .build();
615 let _h = result.expect("builder with push config store should succeed");
616 }
617
618 #[test]
619 fn builder_zero_max_id_length_errors() {
620 let result = RequestHandlerBuilder::new(TestExecutor)
621 .with_handler_limits(HandlerLimits::default().with_max_id_length(0))
622 .build();
623 assert!(result.is_err(), "zero max_id_length should be rejected");
624 }
625
626 #[test]
627 fn builder_zero_max_metadata_size_errors() {
628 let result = RequestHandlerBuilder::new(TestExecutor)
629 .with_handler_limits(HandlerLimits::default().with_max_metadata_size(0))
630 .build();
631 assert!(result.is_err(), "zero max_metadata_size should be rejected");
632 }
633
634 #[test]
635 fn builder_zero_push_delivery_timeout_errors() {
636 let result = RequestHandlerBuilder::new(TestExecutor)
637 .with_handler_limits(
638 HandlerLimits::default().with_push_delivery_timeout(Duration::ZERO),
639 )
640 .build();
641 assert!(
642 result.is_err(),
643 "zero push_delivery_timeout should be rejected"
644 );
645 }
646}