pub mod sink;
pub use sink::{ChildErrorForwarding, StreamingPassthroughSink};
use std::sync::Arc;
use awaken_runtime_contract::contract::event_sink::EventSink;
use awaken_runtime_contract::contract::message::Message;
use awaken_runtime_contract::state::PersistedState;
use crate::RuntimeError;
use crate::backend::{
BackendControl, BackendDelegatePolicy, BackendDelegateRunRequest, BackendParentContext,
BackendRunResult, BackendRunStatus, ExecutionBackendError, execute_resolved_delegate_execution,
};
use crate::cancellation::CancellationToken;
use crate::registry::AgentResolver;
#[non_exhaustive]
pub struct ChildAgentParams<'a> {
pub resolver: &'a dyn AgentResolver,
pub agent_id: &'a str,
pub initial_messages: Vec<Message>,
pub parent: BackendParentContext,
pub initial_state_seed: Option<PersistedState>,
pub sink: Arc<dyn EventSink>,
pub control: BackendControl,
pub policy: BackendDelegatePolicy,
}
impl<'a> ChildAgentParams<'a> {
#[must_use]
pub fn new(
resolver: &'a dyn AgentResolver,
agent_id: &'a str,
initial_messages: Vec<Message>,
parent: BackendParentContext,
sink: Arc<dyn EventSink>,
) -> Self {
Self {
resolver,
agent_id,
initial_messages,
parent,
initial_state_seed: None,
sink,
control: BackendControl::default(),
policy: BackendDelegatePolicy::default(),
}
}
#[must_use]
pub fn with_initial_state_seed(mut self, seed: PersistedState) -> Self {
self.initial_state_seed = Some(seed);
self
}
#[must_use]
pub fn with_control(mut self, control: BackendControl) -> Self {
self.control = control;
self
}
#[must_use]
pub fn with_cancellation_token(mut self, token: Option<CancellationToken>) -> Self {
self.control.cancellation_token = token;
self
}
#[must_use]
pub fn with_policy(mut self, policy: BackendDelegatePolicy) -> Self {
self.policy = policy;
self
}
}
#[derive(Debug, thiserror::Error)]
pub enum ChildAgentError {
#[error(transparent)]
Backend(#[from] ExecutionBackendError),
#[error("child agent did not complete: {}", .0.status)]
Terminal(Box<BackendRunResult>),
}
impl ChildAgentError {
#[must_use]
pub fn terminal_result(&self) -> Option<&BackendRunResult> {
match self {
Self::Backend(_) => None,
Self::Terminal(result) => Some(result),
}
}
}
pub async fn run_child_agent(
params: ChildAgentParams<'_>,
) -> Result<BackendRunResult, ExecutionBackendError> {
let ChildAgentParams {
resolver,
agent_id,
initial_messages,
parent,
initial_state_seed,
sink,
control,
policy,
} = params;
let resolved = resolver
.resolve_execution(agent_id)
.map_err(|error| map_resolve_error(agent_id, error))?;
let request = BackendDelegateRunRequest {
agent_id,
new_messages: initial_messages.clone(),
messages: initial_messages,
sink,
resolver,
parent,
control,
policy,
state_seed: initial_state_seed,
};
execute_resolved_delegate_execution(&resolved, request).await
}
fn map_resolve_error(agent_id: &str, error: RuntimeError) -> ExecutionBackendError {
match error {
RuntimeError::AgentNotFound { agent_id } => ExecutionBackendError::AgentNotFound(agent_id),
other => ExecutionBackendError::ExecutionFailed(format!(
"failed to resolve agent '{agent_id}': {other}"
)),
}
}
pub async fn run_child_agent_checked(
params: ChildAgentParams<'_>,
) -> Result<BackendRunResult, ChildAgentError> {
let result = run_child_agent(params).await?;
if matches!(result.status, BackendRunStatus::Completed) {
Ok(result)
} else {
Err(ChildAgentError::Terminal(Box::new(result)))
}
}