mod conversation;
use std::{path::PathBuf, sync::Arc};
use super::{AgentResolver, Resolved, Route, Taught, Unresolved};
pub(crate) use conversation::{Conversation, Outstanding, Remembered, Waiting, forget};
pub(crate) struct RemoteAgents {
resolvers: Vec<Arc<dyn AgentResolver>>,
conversations: conversation::Book,
hop: u32,
}
impl RemoteAgents {
pub(crate) fn over(
resolvers: Vec<Arc<dyn AgentResolver>>,
remembered_in: Option<PathBuf>,
) -> Self {
Self {
resolvers,
conversations: conversation::Book::new(remembered_in),
hop: 0,
}
}
pub(crate) fn with_hop(self, hop: u32) -> Self {
Self { hop, ..self }
}
pub(crate) fn hop(&self) -> u32 {
self.hop
}
pub(crate) fn route(&self, name: &str) -> Route {
self.resolvers
.iter()
.map(|resolver| resolver.route(name))
.find(|route| *route != Route::Unknown)
.unwrap_or(Route::Unknown)
}
pub(crate) async fn resolve(&self, name: &str) -> Result<Resolved, Unresolved> {
for resolver in &self.resolvers {
match resolver.resolve(name).await {
Err(Unresolved::Unknown) => continue,
answer => return answer,
}
}
Err(Unresolved::Unknown)
}
pub(crate) fn taught(&self) -> Taught {
let mut taught = Taught::default();
let mut notes = Vec::new();
let mut seen = std::collections::BTreeSet::new();
for resolver in &self.resolvers {
let each = resolver.taught();
taught.agents.extend(
each.agents
.into_iter()
.filter(|agent| seen.insert(agent.name.clone())),
);
notes.extend(each.note);
}
if !notes.is_empty() {
taught.note = Some(notes.join(" "));
}
taught
}
pub(crate) fn names(&self) -> Vec<String> {
self.taught()
.agents
.into_iter()
.map(|agent| agent.name)
.collect()
}
pub(crate) fn conversation(
&self,
caller: &str,
id: &str,
remembered: &Remembered,
) -> Conversation {
self.conversations.get(caller, id, remembered)
}
pub(crate) fn keyed_conversation(&self, key: &str, id: &str) -> Option<Conversation> {
self.conversations.keyed(key, id)
}
pub(crate) fn record(
&self,
caller: &str,
id: &str,
conversation: Conversation,
remembered: &Remembered,
) -> std::io::Result<()> {
self.conversations
.record(caller, id, conversation, remembered)
}
}
impl std::fmt::Debug for RemoteAgents {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.names()).finish()
}
}
#[cfg(test)]
impl RemoteAgents {
pub(crate) fn alone(dir: Option<&std::path::Path>) -> Self {
Self::over(Vec::new(), dir.map(std::path::Path::to_path_buf))
}
}
#[cfg(all(test, feature = "a2a"))]
impl RemoteAgents {
pub(crate) fn unreachable(names: &[&str]) -> Self {
Self::over(
vec![Arc::new(
crate::a2a::configured::ConfiguredAgents::unreachable(names),
)],
None,
)
}
pub(crate) fn over_configured(
configured: &Arc<crate::a2a::configured::ConfiguredAgents>,
) -> Self {
Self::over(vec![Arc::clone(configured) as Arc<dyn AgentResolver>], None)
}
}
#[cfg(test)]
mod tests;