mod data;
pub(crate) use data::GENERATED_AT;
use serde::de::DeserializeOwned;
use yapi_types::model::{ClassifierModel, ImageModel, Model};
pub fn builtin_providers() -> impl Iterator<Item = &'static str> {
data::PROVIDERS.iter().map(|(id, _)| *id)
}
pub fn builtin_models(provider: &str) -> Vec<Model> {
of_provider(data::PROVIDERS, provider)
}
pub fn builtin_image_models(provider: &str) -> Vec<ImageModel> {
of_provider(data::IMAGES, provider)
}
pub fn builtin_classifiers(provider: &str) -> Vec<ClassifierModel> {
of_provider(data::CLASSIFIERS, provider)
}
fn of_provider<T: DeserializeOwned>(data: &[(&str, &str)], provider: &str) -> Vec<T> {
data.iter()
.find(|(id, _)| *id == provider)
.map(|(_, json)| parse(json))
.unwrap_or_default()
}
pub fn all_builtin_models() -> Vec<Model> {
parse_all(data::PROVIDERS)
}
pub fn all_builtin_classifiers() -> Vec<ClassifierModel> {
parse_all(data::CLASSIFIERS)
}
pub fn all_builtin_image_models() -> Vec<ImageModel> {
parse_all(data::IMAGES)
}
fn parse_all<T: DeserializeOwned>(data: &[(&str, &str)]) -> Vec<T> {
data.iter().flat_map(|(_, json)| parse(json)).collect()
}
fn parse<T: DeserializeOwned>(json: &str) -> Vec<T> {
serde_json::from_str(json).unwrap_or_default()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_provider_parses() {
for (provider, json) in data::PROVIDERS {
let models: Vec<Model> = serde_json::from_str(json)
.unwrap_or_else(|err| panic!("catalog data for {provider}: {err}"));
let others = data::CLASSIFIERS
.iter()
.chain(data::IMAGES)
.any(|(id, _)| id == provider);
assert!(!models.is_empty() || others, "{provider} has no models");
assert!(models.iter().all(|model| model.provider == *provider));
}
for (provider, json) in data::CLASSIFIERS {
let models: Vec<ClassifierModel> = serde_json::from_str(json)
.unwrap_or_else(|err| panic!("classifier data for {provider}: {err}"));
assert!(models.iter().all(|model| model.provider == *provider));
}
for (provider, json) in data::IMAGES {
let models: Vec<ImageModel> = serde_json::from_str(json)
.unwrap_or_else(|err| panic!("image data for {provider}: {err}"));
assert!(models.iter().all(|model| model.provider == *provider));
}
assert!(
all_builtin_classifiers()
.iter()
.any(|model| model.provider == "typesafe" && model.id == "jev-latest")
);
assert!(!all_builtin_image_models().is_empty());
}
#[test]
fn finds_known_models() {
let anthropic = builtin_models("anthropic");
assert!(
anthropic
.iter()
.any(|model| model.id == "claude-sonnet-4-5")
);
let go = builtin_models("opencode-go");
let apis: std::collections::BTreeSet<_> =
go.iter().map(|model| model.api.as_str()).collect();
assert_eq!(
apis.into_iter().collect::<Vec<_>>(),
[
"anthropic-messages",
"openai-completions",
"openai-responses"
]
);
assert!(builtin_models("nope").is_empty());
}
}