use crate::backend::credentials::authed_configs_with;
use crate::backend::roster::Provider;
#[derive(Debug, Clone, serde::Serialize)]
pub struct ProviderInfo {
pub name: String,
pub model: String,
pub authed: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct Capabilities {
pub version: String,
pub subcommands: Vec<String>,
pub output_formats: Vec<String>,
pub topologies: Vec<String>,
pub providers: Vec<ProviderInfo>,
pub personas: Vec<crate::persona::Persona>,
pub panels: Vec<String>,
pub exit_codes: std::collections::BTreeMap<u8, &'static str>,
}
impl Capabilities {
pub fn static_info() -> Self {
let providers = Provider::registry()
.iter()
.map(|p| ProviderInfo {
name: p.name().to_owned(),
model: p.model().to_owned(),
authed: false,
})
.collect();
Self {
version: env!("CARGO_PKG_VERSION").to_owned(),
subcommands: vec!["critique".to_owned(), "capabilities".to_owned()],
output_formats: vec!["markdown".to_owned(), "json".to_owned()],
topologies: vec!["parallel".to_owned(), "rounds".to_owned()],
providers,
personas: crate::persona::Persona::default_panel(),
panels: vec!["default".to_owned(), "duo".to_owned(), "panel".to_owned()],
exit_codes: crate::error::exit_codes_map(),
}
}
pub fn with_current_auth() -> Self {
let authed = authed_configs_with(None).unwrap_or_default();
let authed_models: std::collections::HashSet<&str> =
authed.iter().map(|c| c.model.as_str()).collect();
let mut caps = Self::static_info();
for p in &mut caps.providers {
if authed_models.contains(p.model.as_str()) {
p.authed = true;
}
}
let registry_names: std::collections::HashSet<&str> =
Provider::registry().iter().map(|p| p.name()).collect();
for cfg in &authed {
let in_registry = caps.providers.iter().any(|p| p.model == cfg.model);
if !in_registry {
caps.providers.push(ProviderInfo {
name: "custom".to_owned(),
model: cfg.model.clone(),
authed: true,
});
}
let _ = registry_names;
}
if let Ok(creds) = crate::backend::credentials::Credentials::discover() {
for name in creds.panels().keys() {
caps.panels.push(name.clone());
}
}
caps
}
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct Plan {
pub seed: u64,
pub topology: String,
pub roster: Vec<PlanSlot>,
pub n_critic_calls: usize,
pub n_summarizer_calls: usize,
pub estimated_total_calls: usize,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct PlanSlot {
pub persona: String,
pub provider: String,
pub model: String,
}
impl Plan {
pub fn for_parallel(
personas: &[crate::persona::Persona],
configs: &[crate::backend::http::HttpConfig],
seed: u64,
) -> Self {
use crate::backend::roster::{random_roster, Provider};
use rand::SeedableRng;
let model_to_name: std::collections::HashMap<&str, &str> = Provider::registry()
.iter()
.map(|p| (p.model(), p.name()))
.collect();
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let roster = random_roster(personas, configs, &mut rng);
let slots = roster
.iter()
.map(|(persona, cfg)| {
let provider = model_to_name
.get(cfg.model.as_str())
.map(|s| s.to_string())
.unwrap_or_else(|| "custom".to_owned());
PlanSlot {
persona: persona.name().to_owned(),
provider,
model: cfg.model.clone(),
}
})
.collect();
let n_critic_calls = roster.len();
Self {
seed,
topology: "parallel".to_owned(),
roster: slots,
n_critic_calls,
n_summarizer_calls: 1,
estimated_total_calls: n_critic_calls + 1,
}
}
}