use std::collections::HashMap;
use crate::agent::{Agent, AgentId};
use crate::error::ProserpinaError;
use crate::graph::InteractionGraph;
use crate::message::{Message, MessageKind};
use crate::subject::Subject;
use crate::transcript::Transcript;
pub const SYSTEM_AGENT: &str = "system";
pub struct Runner {
graph: InteractionGraph,
agents: HashMap<AgentId, Box<dyn Agent>>,
}
impl Runner {
pub fn new(graph: InteractionGraph) -> Self {
Self {
graph,
agents: HashMap::new(),
}
}
#[must_use]
pub fn with_agent(mut self, agent: impl Agent + 'static) -> Self {
let id = agent.id().clone();
self.agents.insert(id, Box::new(agent));
self
}
pub fn execute(&mut self, subject: &Subject) -> Result<Transcript, ProserpinaError> {
let system = AgentId::new(SYSTEM_AGENT);
match self.graph.clone() {
InteractionGraph::Parallel { .. } => self.execute_parallel(subject, &system),
InteractionGraph::Rounds { max_rounds, .. } => {
self.execute_rounds(subject, &system, max_rounds)
}
}
}
fn agent_mut(&mut self, id: &AgentId) -> Result<&mut Box<dyn Agent>, ProserpinaError> {
self.agents
.get_mut(id)
.ok_or_else(|| ProserpinaError::missing_agent(id.clone()))
}
fn execute_parallel(
&mut self,
subject: &Subject,
system: &AgentId,
) -> Result<Transcript, ProserpinaError> {
let mut transcript = Transcript::new();
let critics: Vec<AgentId> = self.graph.critics().to_vec();
for critic_id in &critics {
let prompt = Message::new(
system.clone(),
Some(critic_id.clone()),
MessageKind::Prompt,
subject.text().to_owned(),
);
let response = self.agent_mut(critic_id)?.respond(&prompt)?;
transcript.push(response);
}
Ok(transcript)
}
fn execute_rounds(
&mut self,
subject: &Subject,
system: &AgentId,
max_rounds: usize,
) -> Result<Transcript, ProserpinaError> {
let mut transcript = Transcript::new();
if max_rounds == 0 {
return Ok(transcript);
}
let critics: Vec<AgentId> = self.graph.critics().to_vec();
for critic_id in &critics {
let prompt = Message::new(
system.clone(),
Some(critic_id.clone()),
MessageKind::Prompt,
subject.text().to_owned(),
);
let response = self.agent_mut(critic_id)?.respond(&prompt)?;
transcript.push(response);
}
let mut prev_round_start = 0usize;
for _round in 2..=max_rounds {
let prior_round: Vec<Message> =
transcript.iter().skip(prev_round_start).cloned().collect();
let current_round_start = transcript.len();
let mut rebuttals_this_round = 0usize;
for critic_id in &critics {
for prior_msg in &prior_round {
if prior_msg.sender() == critic_id {
continue; }
let response = self.agent_mut(critic_id)?.respond(prior_msg)?;
if matches!(response.kind(), MessageKind::Rebuttal) {
rebuttals_this_round += 1;
}
transcript.push(response);
}
}
if rebuttals_this_round == 0 {
break; }
prev_round_start = current_round_start;
}
Ok(transcript)
}
}