use std::time::{Duration, SystemTime};
use async_trait::async_trait;
use mentra::runtime::RunOptions;
#[async_trait]
pub trait Peer: Send + Sync {
async fn exchange(
&self,
request: Request,
bounds: &ExchangeBounds<'_>,
) -> Result<Outcome, PeerError>;
fn max_output_bytes(&self) -> usize;
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Request {
Send(Message),
Wait { task_id: String },
}
impl Request {
pub fn wait(task_id: impl Into<String>) -> Self {
Self::Wait {
task_id: task_id.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct Message {
pub text: String,
pub context_id: Option<String>,
pub answering: Option<String>,
pub hop: u32,
pub deadline: Option<SystemTime>,
}
impl Message {
pub fn new(text: impl Into<String>) -> Self {
Self {
text: text.into(),
context_id: None,
answering: None,
hop: 1,
deadline: None,
}
}
#[must_use]
pub fn in_context(self, context_id: Option<String>) -> Self {
Self { context_id, ..self }
}
#[must_use]
pub fn answering(self, task_id: Option<String>) -> Self {
Self {
answering: task_id,
..self
}
}
#[must_use]
pub fn at_hop(self, hop: u32, deadline: Option<SystemTime>) -> Self {
Self {
hop,
deadline,
..self
}
}
}
pub struct ExchangeBounds<'a> {
pub options: &'a RunOptions,
pub progress: &'a (dyn Fn(String) + Send + Sync),
pub started: &'a (dyn Fn(Started) + Send + Sync),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct Started {
pub task_id: String,
pub context_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Outcome {
Replied {
context_id: Option<String>,
text: String,
},
Finished {
task_id: String,
context_id: Option<String>,
text: String,
},
Working {
task_id: String,
context_id: Option<String>,
text: String,
why: StillWorking,
},
NeedsInput {
task_id: String,
context_id: Option<String>,
text: String,
},
NeedsAuthorization {
task_id: String,
context_id: Option<String>,
text: String,
},
Failed {
task_id: String,
context_id: Option<String>,
text: String,
how: Failure,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum StillWorking {
WaitBound(Duration),
RunStopping,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Failure {
Failed,
Rejected,
Canceled,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum PeerError {
Send(String),
Check { task_id: String, error: String },
UnknownTask,
Abandoned {
task_id: String,
canceled: Result<(), String>,
},
Unrecognized,
}
impl std::fmt::Display for PeerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Send(error) => write!(f, "the request failed: {error}"),
Self::Check { task_id, error } => {
write!(f, "checking on task {task_id} failed: {error}")
}
Self::UnknownTask => write!(f, "the agent does not know the task"),
Self::Abandoned { task_id, .. } => write!(f, "task {task_id} was abandoned"),
Self::Unrecognized => {
write!(f, "the agent answered in a way spawn does not understand")
}
}
}
}
impl std::error::Error for PeerError {}