use tokio::sync::mpsc;
use agent_base::{AgentRuntime, RuntimeEvent, SessionId};
pub struct AgentHandle {
cmd_tx: mpsc::Sender<AgentCommand>,
event_rx: mpsc::UnboundedReceiver<RuntimeEvent>,
runtime: AgentRuntime,
default_session_id: Option<SessionId>,
}
enum AgentCommand {
RunTurn {
session_id: SessionId,
input: String,
},
}
#[derive(Debug)]
pub enum SendError {
ChannelClosed,
}
impl AgentHandle {
pub fn new(runtime: AgentRuntime) -> Self {
let (cmd_tx, cmd_rx) = mpsc::channel(32);
let (event_tx, event_rx) = mpsc::unbounded_channel();
let rt = runtime.clone();
tokio::spawn(async move {
let mut rx = cmd_rx;
while let Some(cmd) = rx.recv().await {
match cmd {
AgentCommand::RunTurn { session_id, input } => {
let tx = event_tx.clone();
let sid = session_id.clone();
let result = rt
.run_turn(session_id, &input, move |event| {
let _ = tx.send(event);
Ok(())
})
.await;
match &result {
Ok(_) => {}
Err(e) if e.is_cancelled() => {}
Err(e) => {
tracing::error!(error = %e, "run_turn failed");
let _ =
event_tx.send(RuntimeEvent::RunFinished { session_id: sid });
}
}
}
}
}
});
Self {
cmd_tx,
event_rx,
runtime,
default_session_id: None,
}
}
pub fn with_session(runtime: AgentRuntime, session_id: SessionId) -> Self {
let (cmd_tx, cmd_rx) = mpsc::channel(32);
let (event_tx, event_rx) = mpsc::unbounded_channel();
let rt = runtime.clone();
tokio::spawn(async move {
let mut rx = cmd_rx;
while let Some(cmd) = rx.recv().await {
match cmd {
AgentCommand::RunTurn { session_id, input } => {
let tx = event_tx.clone();
let sid = session_id.clone();
let result = rt
.run_turn(session_id, &input, move |event| {
let _ = tx.send(event);
Ok(())
})
.await;
match &result {
Ok(_) => {
}
Err(e) if e.is_cancelled() => {
}
Err(e) => {
tracing::error!(error = %e, "run_turn failed");
let _ =
event_tx.send(RuntimeEvent::RunFinished { session_id: sid });
}
}
}
}
}
});
Self {
cmd_tx,
event_rx,
runtime,
default_session_id: Some(session_id),
}
}
pub async fn send_input(&self, input: &str) -> Result<(), SendError> {
let session_id = match &self.default_session_id {
Some(id) => id.clone(),
None => self.runtime.create_session().await,
};
self.cmd_tx
.send(AgentCommand::RunTurn {
session_id,
input: input.to_string(),
})
.await
.map_err(|_| SendError::ChannelClosed)
}
pub async fn send_input_with_session(
&self,
input: &str,
session_id: SessionId,
) -> Result<(), SendError> {
self.cmd_tx
.send(AgentCommand::RunTurn {
session_id,
input: input.to_string(),
})
.await
.map_err(|_| SendError::ChannelClosed)
}
pub async fn recv_event(&mut self) -> Option<RuntimeEvent> {
self.event_rx.recv().await
}
pub fn try_recv_event(&mut self) -> Option<RuntimeEvent> {
self.event_rx.try_recv().ok()
}
pub fn cancel(&self) {
self.runtime.cancel();
}
pub fn runtime(&self) -> &AgentRuntime {
&self.runtime
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_agent_handle_creation() {
}
}