use std::collections::{BTreeMap, BTreeSet};
use super::catalog::{CatalogContent, CatalogContentId, CatalogSnapshot};
use super::catalog_projection::{
CallableId, CallableOffering, ModelProjection, ProjectionError, ProjectionId,
};
use crate::desired_state::Checksum;
use crate::desired_state::models::{CatalogOffering, ModelEnablementBody, OfferingId};
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum PinError {
#[error("the catalogue has no callable projection: {source}")]
Unprojectable {
#[source]
source: ProjectionError,
},
#[error("the offering `{published}` of provider `{provider}` has no derivable identity")]
Underivable { provider: String, published: String },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Resolution<'r, 'a> {
Callable(&'r CallableOffering<'a>),
Ambiguous { callables: &'r [CallableId] },
Withdrawn,
OtherSnapshot { pinned: Checksum },
}
impl<'r, 'a> Resolution<'r, 'a> {
pub const fn callable(&self) -> Option<&'r CallableOffering<'a>> {
match *self {
Self::Callable(offering) => Some(offering),
_ => None,
}
}
pub const fn is_about_this_snapshot(&self) -> bool {
!matches!(self, Self::OtherSnapshot { .. })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PinnedCatalog<'a> {
snapshot: Checksum,
projection: ModelProjection<'a>,
offerings: BTreeMap<OfferingId, Vec<CallableId>>,
}
impl<'a> PinnedCatalog<'a> {
pub fn of(content: &'a CatalogContent, snapshot: Checksum) -> Result<Self, PinError> {
let projection = ModelProjection::project(content)
.map_err(|source| PinError::Unprojectable { source })?;
let mut offerings: BTreeMap<OfferingId, Vec<CallableId>> = BTreeMap::new();
for callable in projection.callables() {
let offering = callable.offering();
let id = OfferingId::of(offering.provider.as_str(), offering.model.as_str()).map_err(
|_| PinError::Underivable {
provider: offering.provider.as_str().to_owned(),
published: offering.published_model_id.clone(),
},
)?;
offerings.entry(id).or_default().push(callable.id().clone());
}
Ok(Self {
snapshot,
projection,
offerings,
})
}
pub fn of_snapshot(snapshot: &'a CatalogSnapshot) -> Result<Self, PinError> {
Self::of(&snapshot.content, snapshot.source.raw.digest)
}
pub const fn snapshot(&self) -> Checksum {
self.snapshot
}
pub const fn projection(&self) -> &ModelProjection<'a> {
&self.projection
}
pub const fn content_id(&self) -> CatalogContentId {
self.projection.content_id()
}
pub const fn projection_id(&self) -> ProjectionId {
self.projection.projection_id()
}
pub fn published(&self) -> impl Iterator<Item = OfferingId> {
self.offerings.keys().copied()
}
pub fn resolve(&self, pin: CatalogOffering) -> Resolution<'_, 'a> {
if !pin.is_pinned_to(self.snapshot) {
return Resolution::OtherSnapshot {
pinned: pin.snapshot,
};
}
let Some(callables) = self.offerings.get(&pin.offering) else {
return Resolution::Withdrawn;
};
match callables.as_slice() {
[only] => self
.projection
.callable(only)
.map_or(Resolution::Withdrawn, Resolution::Callable),
several => Resolution::Ambiguous { callables: several },
}
}
pub fn withdrawn_from<'b>(
&self,
enablements: impl IntoIterator<Item = &'b ModelEnablementBody>,
) -> BTreeSet<OfferingId> {
enablements
.into_iter()
.map(ModelEnablementBody::offering)
.map(|pin| pin.offering)
.filter(|offering| !self.offerings.contains_key(offering))
.collect()
}
}
#[cfg(test)]
mod tests {
use std::time::SystemTime;
use super::*;
use crate::backends::catalog::SourceValidators;
use crate::backends::catalog_refresh::RefreshImpact;
use crate::backends::models_dev::ModelsDevAdapter;
use crate::desired_state::fixtures::{resource_id, tenant_id};
use crate::desired_state::models::{ModelOwner, WireFamily};
const IDENTITY: &str = include_str!("fixtures/models_dev/catalog.identity.json");
const ALIASES: &str = include_str!("fixtures/models_dev/catalog.aliases.json");
const ALIASES_REPRICED: &str =
include_str!("fixtures/models_dev/catalog.aliases-repriced.json");
fn imported(payload: &str) -> (CatalogContent, Checksum) {
let content = ModelsDevAdapter::default()
.parse(
payload.as_bytes(),
SourceValidators::etag("\"fixture\""),
SystemTime::UNIX_EPOCH,
)
.expect("the fixture parses")
.content;
(content, Checksum::of(payload.as_bytes()))
}
fn pin(provider: &str, model: &str, snapshot: Checksum) -> CatalogOffering {
CatalogOffering::new(
OfferingId::of(provider, model).expect("a fixture identity is derivable"),
snapshot,
)
}
fn enablement(sequence: u64, offering: CatalogOffering) -> ModelEnablementBody {
ModelEnablementBody::new(
resource_id(sequence),
ModelOwner::tenant(tenant_id(1)),
offering,
WireFamily::OpenaiChat,
)
}
#[test]
fn a_pin_resolves_to_the_id_a_request_would_send() {
let (content, snapshot) = imported(IDENTITY);
let pinned = PinnedCatalog::of(&content, snapshot).expect("the catalogue is keyable");
let resolved = pinned.resolve(pin("hpc-ai", "openai/gpt-5.5", snapshot));
let callable = resolved.callable().expect("the offering is published");
assert_eq!(callable.published_model_id(), "openai/gpt-5.5");
assert_eq!(callable.provider().as_str(), "hpc-ai");
assert!(callable.price().is_some(), "with that provider's own terms");
assert_eq!(
pinned.published().count(),
2,
"one model published by two providers is two pinnable offerings"
);
}
#[test]
fn each_provider_of_one_model_is_pinned_separately() {
let (content, snapshot) = imported(IDENTITY);
let pinned = PinnedCatalog::of(&content, snapshot).expect("the catalogue is keyable");
let first = pinned
.resolve(pin("openai", "openai/gpt-5.5", snapshot))
.callable()
.expect("openai publishes it");
let second = pinned
.resolve(pin("hpc-ai", "openai/gpt-5.5", snapshot))
.callable()
.expect("hpc-ai publishes it too");
assert_eq!(first.model(), second.model());
assert_ne!(first.id(), second.id());
assert_ne!(first.price(), second.price());
}
#[test]
fn an_offering_published_under_several_ids_is_ambiguous_rather_than_guessed() {
let (content, snapshot) = imported(ALIASES);
let pinned = PinnedCatalog::of(&content, snapshot).expect("the catalogue is keyable");
let resolved = pinned.resolve(pin("qiniu-ai", "xiaomi/mimo-v2-flash", snapshot));
let Resolution::Ambiguous { callables } = resolved else {
panic!("a pin reaching two callable ids must not resolve to one: {resolved:?}");
};
assert_eq!(
callables
.iter()
.map(CallableId::published_model_id)
.collect::<Vec<_>>(),
vec!["mimo-v2-flash", "xiaomi/mimo-v2-flash"],
"every candidate, so a caller with the authority to choose can"
);
assert!(resolved.callable().is_none());
assert!(resolved.is_about_this_snapshot());
}
#[test]
fn a_pin_the_catalogue_no_longer_publishes_is_withdrawn() {
let (content, snapshot) = imported(IDENTITY);
let pinned = PinnedCatalog::of(&content, snapshot).expect("the catalogue is keyable");
assert_eq!(
pinned.resolve(pin("openai", "openai/a-model-that-was-withdrawn", snapshot)),
Resolution::Withdrawn
);
assert_eq!(
pinned.resolve(pin("a-provider-that-left", "openai/gpt-5.5", snapshot)),
Resolution::Withdrawn,
"a pin names a provider's offering, not a model"
);
}
#[test]
fn a_pin_approved_against_another_snapshot_is_not_resolved_through_this_one() {
let (content, snapshot) = imported(ALIASES);
let (_, repriced) = imported(ALIASES_REPRICED);
let pinned = PinnedCatalog::of(&content, snapshot).expect("the catalogue is keyable");
let resolved = pinned.resolve(pin("qiniu-ai", "xiaomi/mimo-v2-flash", repriced));
assert_eq!(resolved, Resolution::OtherSnapshot { pinned: repriced });
assert!(!resolved.is_about_this_snapshot());
assert!(resolved.callable().is_none());
}
#[test]
fn an_identity_is_derived_from_the_catalogue_rather_than_the_payload() {
let (content, first) = imported(IDENTITY);
let second = Checksum::of(b"the same catalogue, served again");
let before = PinnedCatalog::of(&content, first).expect("the catalogue is keyable");
let after = PinnedCatalog::of(&content, second).expect("the catalogue is keyable");
assert_eq!(
before.published().collect::<Vec<_>>(),
after.published().collect::<Vec<_>>()
);
assert_eq!(before.content_id(), after.content_id());
assert_eq!(before.projection_id(), after.projection_id());
assert_ne!(before.snapshot(), after.snapshot());
}
#[test]
fn withdrawal_agrees_with_the_impact_a_refresh_reports() {
let (content, snapshot) = imported(IDENTITY);
let pinned = PinnedCatalog::of(&content, snapshot).expect("the catalogue is keyable");
let enablements = [
enablement(1, pin("openai", "openai/gpt-5.5", snapshot)),
enablement(2, pin("openai", "openai/gone", snapshot)),
];
let withdrawn = pinned.withdrawn_from(&enablements);
let impact = RefreshImpact::of(&enablements, &content, snapshot);
assert_eq!(withdrawn, impact.withdrawn);
assert_eq!(withdrawn.len(), 1);
}
#[test]
fn an_offering_pinned_elsewhere_is_still_reported_as_withdrawn() {
let (content, snapshot) = imported(IDENTITY);
let pinned = PinnedCatalog::of(&content, snapshot).expect("the catalogue is keyable");
let older = Checksum::of(b"an older catalogue payload");
let enablements = [
enablement(3, pin("openai", "openai/gone", older)),
enablement(4, pin("openai", "openai/gpt-5.5", older)),
];
let withdrawn = pinned.withdrawn_from(&enablements);
let impact = RefreshImpact::of(&enablements, &content, snapshot);
assert_eq!(withdrawn, impact.withdrawn);
assert_eq!(
withdrawn,
[OfferingId::of("openai", "openai/gone").expect("derivable")]
.into_iter()
.collect::<BTreeSet<_>>(),
"the offering this catalogue dropped, and only it"
);
assert_eq!(impact.pins_unmoved, 2);
assert_eq!(
pinned.resolve(pin("openai", "openai/gpt-5.5", older)),
Resolution::OtherSnapshot { pinned: older }
);
}
}