use std::cmp::Ordering;
use std::collections::BTreeSet;
use argonauts_core::app::ModelChoice;
use argonauts_core::model_catalog::{ModelCatalog, display_name_from_id};
use argonauts_core::names::{ModelRef, RoleName};
use argonauts_core::registry::{ModelEntry, RegistryKind};
use argonauts_core::settings::Settings;
use argonauts_registry::models::{CLAUDE_CODE_ALIASES, usual_efforts};
pub(super) struct Labels<'a> {
catalog: ModelCatalog,
listed: &'a [ModelEntry],
defaults: &'a [(String, String)],
endpoints: BTreeSet<String>,
}
impl<'a> Labels<'a> {
pub(super) fn new(
settings: &Settings,
catalog: ModelCatalog,
listed: &'a [ModelEntry],
defaults: &'a [(String, String)],
) -> Self {
let endpoints = listed
.iter()
.filter(|entry| entry.kind == RegistryKind::Endpoint)
.map(|entry| entry.id.agent.clone())
.chain(settings.endpoints.keys().cloned())
.collect();
Self {
catalog,
listed,
defaults,
endpoints,
}
}
pub(super) fn describe(
&self,
settings: &Settings,
role: Option<RoleName>,
choice: &mut ModelChoice,
) {
let worker = choice.worker.clone();
let cli = worker.agent.as_str();
choice.alias = self.is_alias(&worker);
let target = self.target(&worker);
let (efforts, default_effort) = self.levels(Some(&worker), target.as_ref(), cli);
choice.efforts = efforts;
choice.default_effort = default_effort;
choice.effort = effort_set(settings, role, cli);
choice.runs = None;
choice.recommended = false;
choice.new = false;
match worker.model.as_deref() {
None => {
choice.name = String::new();
let own = self.catalog.models.iter().find(|entry| entry.id == worker);
choice.recommended = own.and_then(|entry| entry.recommended) == Some(true);
let runs_its_own = own
.and_then(|entry| entry.runs.as_deref())
.is_none_or(|runs| {
target.as_ref().and_then(|t| t.model.as_deref()) == Some(runs)
});
choice.description = own
.and_then(|entry| entry.description.clone())
.filter(|_| runs_its_own)
.or_else(|| self.description(target.as_ref()));
choice.runs = target
.as_ref()
.map(|target| self.catalog.label(target).name);
}
Some(alias) if choice.alias => {
choice.name = display_name_from_id(alias);
choice.description = self.description(target.as_ref());
choice.runs = target
.as_ref()
.map(|target| self.catalog.label(target).name);
}
Some(_) => {
let label = self.catalog.label(&worker);
choice.name = label.name;
choice.description = label.description;
choice.recommended = label.recommended;
choice.new = label.new && !choice.older && !self.endpoints.contains(cli);
}
}
}
pub(super) fn order(&self, a: &ModelChoice, b: &ModelChoice) -> Ordering {
let place = place_in_group(a);
place.cmp(&place_in_group(b)).then_with(|| {
if self.endpoints.contains(&a.worker.agent) || a.listed.is_some() {
return Ordering::Equal;
}
let newest = || self.catalog.newest_first(&a.worker, &b.worker);
match place.0 {
2 => newest(),
3 => self.ranked(b).cmp(&self.ranked(a)).then_with(newest),
_ => Ordering::Equal,
}
})
}
fn ranked(&self, choice: &ModelChoice) -> bool {
self.catalog
.entry(&choice.worker)
.is_some_and(|entry| entry.rank.is_some())
}
pub(super) fn levels(
&self,
worker: Option<&ModelRef>,
target: Option<&ModelRef>,
cli: &str,
) -> (Vec<String>, Option<String>) {
let own = worker
.filter(|worker| Some(*worker) != target)
.and_then(|worker| self.listed.iter().find(|entry| entry.id == *worker))
.filter(|entry| !entry.efforts.is_empty());
if let Some(own) = own {
let default = own
.default_effort
.clone()
.or_else(|| self.catalog.entry(target?)?.default_effort.clone())
.filter(|level| own.efforts.contains(level));
return (own.efforts.clone(), default);
}
let described = target.and_then(|target| self.catalog.entry(target));
let listed = target.and_then(|target| self.listed_entry(target));
if let Some(variants) = listed.and_then(|entry| entry.variants.clone()) {
return (variants, None);
}
let efforts = described
.and_then(|entry| entry.efforts.clone())
.or_else(|| {
listed
.map(|entry| entry.efforts.clone())
.filter(|levels| !levels.is_empty())
})
.unwrap_or_else(|| {
usual_efforts(cli)
.iter()
.map(|level| (*level).to_owned())
.collect()
});
let default = described
.and_then(|entry| entry.default_effort.clone())
.or_else(|| listed.and_then(|entry| entry.default_effort.clone()))
.filter(|level| efforts.contains(level));
(efforts, default)
}
pub(super) fn target(&self, worker: &ModelRef) -> Option<ModelRef> {
let model = match worker.model.as_deref() {
Some(_) if self.is_alias(worker) => return self.alias_target(worker),
Some(_) => return Some(worker.clone()),
None => self
.defaults
.iter()
.find(|(cli, _)| *cli == worker.agent)
.map(|(_, model)| model.clone())
.or_else(|| self.catalog.entry(worker)?.runs.clone())?,
};
let runs = ModelRef::new(worker.agent.clone(), Some(model));
if self.is_alias(&runs) {
return self.alias_target(&runs).or(Some(runs));
}
Some(runs)
}
fn is_alias(&self, worker: &ModelRef) -> bool {
let Some(model) = worker.model.as_deref() else {
return false;
};
if worker.agent == "claude-code" && CLAUDE_CODE_ALIASES.contains(&model) {
return true;
}
self.catalog.is_alias(worker)
&& !self
.listed
.iter()
.any(|entry| entry.id == *worker && entry.discovered)
}
fn alias_target(&self, alias: &ModelRef) -> Option<ModelRef> {
let name = alias.model.as_deref()?;
let current = |entry: &&ModelEntry| {
entry.id.agent == alias.agent
&& entry.id != *alias
&& entry.discovered
&& !entry.retired
&& entry.successor.is_none()
};
let provider_word = |entry: &&ModelEntry| {
entry
.id
.model
.as_deref()
.is_some_and(|model| model.split(['-', '.', ':']).any(|word| word == name))
};
let listed = self
.listed
.iter()
.filter(current)
.find(|entry| entry.aliases.iter().any(|known| known == name))
.or_else(|| self.listed.iter().filter(current).find(provider_word));
listed
.map(|entry| entry.id.clone())
.or_else(|| self.catalog.entry(alias).map(|entry| entry.id.clone()))
}
fn listed_entry(&self, worker: &ModelRef) -> Option<&ModelEntry> {
self.listed
.iter()
.find(|entry| entry.id == *worker)
.or_else(|| {
let model = worker.model.as_deref()?;
self.listed.iter().find(|entry| {
entry.id.agent == worker.agent && entry.aliases.iter().any(|a| a == model)
})
})
}
fn description(&self, target: Option<&ModelRef>) -> Option<String> {
self.catalog.label(target?).description
}
}
fn place_in_group(choice: &ModelChoice) -> (u8, usize) {
match choice.worker.model.as_deref() {
None => (0, 0),
Some(model) if choice.alias => {
let at = CLAUDE_CODE_ALIASES.iter().position(|alias| *alias == model);
(1, at.unwrap_or(CLAUDE_CODE_ALIASES.len()))
}
Some(_) if choice.older => (3, 0),
Some(_) => (2, 0),
}
}
pub(super) fn effort_set(settings: &Settings, role: Option<RoleName>, cli: &str) -> Option<String> {
settings
.roles
.get(&role.unwrap_or(RoleName::Main))
.and_then(|role| role.effort.clone())
.or_else(|| {
settings
.agents
.get(cli)
.and_then(|agent| agent.effort.clone())
})
}