use crate::agent::AgentId;
use crate::backend::EchoAgent;
use crate::error::ProserpinaError;
use crate::graph::{InteractionGraph, Topology};
use crate::persona::Persona;
use crate::report::Report;
use crate::runner::Runner;
use crate::subject::Subject;
pub fn default_personas() -> Vec<Persona> {
crate::persona::Persona::default_panel()
}
pub fn run_critique_echo(input: &str, source: &str) -> Result<String, ProserpinaError> {
let personas = default_personas();
let critic_ids: Vec<AgentId> = personas.iter().map(|p| AgentId::new(p.name())).collect();
let graph = InteractionGraph::from(Topology::parallel(critic_ids));
let mut runner = Runner::new(graph);
for persona in personas {
let id = AgentId::new(persona.name());
runner = runner.with_agent(EchoAgent::new(id, persona));
}
let subject = Subject::from_markdown(input, source);
let transcript = runner.execute(&subject)?;
let report = Report::from_transcript(&transcript);
Ok(report.to_markdown_with_source(subject.source()))
}
#[cfg(all(feature = "cli", feature = "backend-http"))]
pub fn run_critique(
input: &str,
source: &str,
seed: u64,
config_path: Option<&std::path::Path>,
json: bool,
panel: Option<&str>,
policy: crate::backend::http::RetryPolicy,
) -> Result<String, ProserpinaError> {
use crate::backend::credentials::{authed_configs_with, Credentials};
use crate::backend::http::HttpAgent;
use crate::backend::roster::{random_roster, Provider};
use crate::persona::resolve_panel;
use crate::summary::summarize;
use rand::SeedableRng;
let credentials = Credentials::discover_or(config_path)?;
let personas = resolve_panel(panel.unwrap_or("default"), &credentials)?;
let configs = authed_configs_with(config_path)?;
if configs.is_empty() {
return Err(ProserpinaError::no_authed_providers(
Provider::registry()
.iter()
.map(|p| p.name().to_owned())
.collect(),
));
}
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let roster = random_roster(&personas, &configs, &mut rng);
let critic_ids: Vec<AgentId> = roster.iter().map(|(p, _)| AgentId::new(p.name())).collect();
let graph = InteractionGraph::from(Topology::parallel(critic_ids));
let mut runner = Runner::new(graph);
for (persona, config) in roster {
let id = AgentId::new(persona.name());
runner = runner.with_agent(HttpAgent::new_with_policy(id, persona, config, policy));
}
let subject = Subject::from_markdown(input, source);
let transcript = runner.execute(&subject)?;
let findings = summarize(&subject, &transcript, &configs[0], &policy).unwrap_or_default();
let mut report = Report::new();
for f in findings {
report.push_finding(f);
}
let mut body = if json {
#[cfg(feature = "json")]
{
report.to_json()
}
#[cfg(not(feature = "json"))]
{
report.to_markdown_with_source(subject.source())
}
} else {
report.to_markdown_with_source(subject.source())
};
if !json {
body.push_str(&format!("\n_Reproducibility: seed `{seed}`_\n"));
}
Ok(body)
}
#[cfg(all(feature = "cli", feature = "backend-http"))]
pub fn plan_critique(
_input: &str,
_source: &str,
seed: u64,
config_path: Option<&std::path::Path>,
_json: bool,
panel: Option<&str>,
) -> Result<String, ProserpinaError> {
use crate::agent_info::Plan;
use crate::backend::credentials::{authed_configs_with, Credentials};
use crate::backend::roster::Provider;
use crate::persona::resolve_panel;
let credentials = Credentials::discover_or(config_path)?;
let configs = authed_configs_with(config_path)?;
if configs.is_empty() {
return Err(ProserpinaError::no_authed_providers(
Provider::registry()
.iter()
.map(|p| p.name().to_owned())
.collect(),
));
}
#[cfg(feature = "json")]
{
let personas = resolve_panel(panel.unwrap_or("default"), &credentials)?;
let plan = Plan::for_parallel(&personas, &configs, seed);
Ok(serde_json::to_string_pretty(&plan).unwrap_or_else(|_| "{}".to_owned()))
}
#[cfg(not(feature = "json"))]
{
let _ = panel;
Ok(format!(
"Dry-run would use seed {seed} with {} provider config(s).",
configs.len()
))
}
}