use std::collections::HashMap;
use std::time::Instant;
use a2a_protocol_types::error::A2aResult;
use a2a_protocol_types::events::StreamResponse;
use a2a_protocol_types::params::MessageSendParams;
use a2a_protocol_types::responses::SendMessageResponse;
use a2a_protocol_types::task::Task;
use tokio::sync::OwnedSemaphorePermit;
use tokio::task::JoinHandle;
use crate::error::ServerResult;
use crate::streaming::InMemoryQueueReader;
use super::helpers::build_call_context;
use super::{RequestHandler, SendMessageResult};
mod admission;
mod continuation;
mod create;
mod decisions;
mod eviction;
mod execute;
mod validation;
pub use decisions::MAX_TASK_HISTORY_MESSAGES;
use decisions::shape_response_history;
#[derive(Clone, Copy)]
struct SendMode {
use_background: bool,
response_history_length: Option<u32>,
}
impl SendMode {
fn of(params: &MessageSendParams, streaming: bool) -> Self {
let configuration = params.configuration.as_ref();
let return_immediately = configuration
.and_then(|c| c.return_immediately)
.unwrap_or(false);
Self {
use_background: streaming || return_immediately,
response_history_length: configuration.and_then(|c| c.history_length),
}
}
}
struct Started {
task: Task,
reader: InMemoryQueueReader,
persistence_rx: Option<tokio::sync::mpsc::Receiver<A2aResult<StreamResponse>>>,
executor_handle: JoinHandle<()>,
}
impl RequestHandler {
pub async fn on_send_message(
&self,
params: MessageSendParams,
streaming: bool,
headers: Option<&HashMap<String, String>>,
) -> ServerResult<SendMessageResult> {
let method_name = if streaming {
"SendStreamingMessage"
} else {
"SendMessage"
};
let start = Instant::now();
trace_info!(method = method_name, streaming, "handling send message");
self.metrics.on_request(method_name);
let tenant = self
.resolve_tenant(method_name, headers, params.tenant.as_deref())
.await?;
let result = crate::store::tenant::TenantContext::scope(tenant, async {
self.send_message_inner(params, streaming, method_name, headers)
.await
})
.await;
let elapsed = start.elapsed();
match &result {
Ok(_) => {
self.metrics.on_response(method_name);
self.metrics.on_latency(method_name, elapsed);
}
Err(e) => {
self.metrics.on_error(method_name, e.metric_label());
self.metrics.on_latency(method_name, elapsed);
}
}
result
}
async fn send_message_inner(
&self,
params: MessageSendParams,
streaming: bool,
method_name: &str,
headers: Option<&HashMap<String, String>>,
) -> ServerResult<SendMessageResult> {
let call_ctx = build_call_context(method_name, headers);
self.interceptors.run_before(&call_ctx).await?;
self.ensure_required_extensions(&call_ctx)?;
let (mode, started) = self.validate_and_commit(params, streaming).await?;
self.interceptors.run_after(&call_ctx).await?;
if mode.use_background {
Ok(self.respond_in_background(started, streaming, mode.response_history_length))
} else {
self.respond_blocking(started, mode.response_history_length)
.await
}
}
async fn validate_and_commit(
&self,
mut params: MessageSendParams,
streaming: bool,
) -> ServerResult<(SendMode, Started)> {
let tenant_slot = self.acquire_tenant_slot().await?;
if streaming {
self.ensure_streaming_supported()?;
}
self.validate_send_params(&mut params)?;
let mode = SendMode::of(¶ms, streaming);
self.commit_task(params, mode.use_background, tenant_slot)
.await
.map(|started| (mode, started))
}
async fn commit_task(
&self,
params: MessageSendParams,
use_background: bool,
tenant_slot: Option<OwnedSemaphorePermit>,
) -> ServerResult<Started> {
let context_id = self.resolve_context_id(¶ms.message).await?;
let context_lock = self.keyed_lock(&context_id).await;
let context_guard = context_lock.lock().await;
let stored_task = self.find_task_by_context(&context_id).await?;
let task_id = self
.resolve_task_id(¶ms.message, stored_task.as_ref())
.await?;
self.reject_in_flight_send(&task_id).await?;
trace_debug!(
task_id = %task_id,
context_id = %context_id,
"creating task"
);
let task = create::build_initial_task(
&task_id,
&context_id,
stored_task.as_ref(),
¶ms.message,
);
let ctx = create::build_request_context(
params.message,
task_id.clone(),
context_id,
stored_task,
params.metadata,
);
let (writer, reader, persistence_rx) =
self.lease_event_queue(&task_id, use_background).await?;
self.register_cancellation_token(&task_id, ctx.cancellation_token.clone())
.await;
self.persist_initial_task(&task).await?;
drop(context_guard);
Box::pin(self.register_inline_push_config(params.configuration.as_ref(), &task_id)).await?;
let executor_handle = self.spawn_executor(ctx, writer, tenant_slot);
Ok(Started {
task,
reader,
persistence_rx,
executor_handle,
})
}
fn respond_in_background(
&self,
started: Started,
streaming: bool,
response_history_length: Option<u32>,
) -> SendMessageResult {
let Started {
task,
reader,
persistence_rx,
executor_handle,
} = started;
self.spawn_background_event_processor(
task.id.clone(),
executor_handle,
persistence_rx,
task.clone(),
);
let mut snapshot = task;
shape_response_history(&mut snapshot, response_history_length);
if streaming {
let mut reader = reader;
reader.set_first_event(StreamResponse::Task(snapshot));
SendMessageResult::Stream(reader)
} else {
drop(reader);
SendMessageResult::Response(SendMessageResponse::Task(snapshot))
}
}
async fn respond_blocking(
&self,
started: Started,
response_history_length: Option<u32>,
) -> ServerResult<SendMessageResult> {
let Started {
task,
reader,
executor_handle,
..
} = started;
let collected = self
.collect_events(reader, task.id, executor_handle)
.await?;
if let Some(message) = collected.direct_message {
return Ok(SendMessageResult::Response(SendMessageResponse::Message(
message,
)));
}
let mut final_task = collected.task;
shape_response_history(&mut final_task, response_history_length);
Ok(SendMessageResult::Response(SendMessageResponse::Task(
final_task,
)))
}
}
#[cfg(test)]
mod tests;