use anyhow::{Context, Result};
use super::types::{RegistryManifest, RegistryProvider, RegistryTranscriptionProvider};
include!(concat!(env!("OUT_DIR"), "/embedded_registry/embedded.rs"));
pub fn embedded_base_files() -> &'static [(&'static str, &'static str)] {
BASE_FILES
}
pub fn embedded_manifest() -> Result<RegistryManifest> {
serde_json::from_str(MANIFEST_JSON).context("failed to parse embedded manifest")
}
pub fn embedded_model_catalog() -> Result<super::resolve::ModelCatalog> {
let mut catalog = std::collections::HashMap::new();
for (id, json) in MODEL_CATALOG_FILES {
let model: super::types::CanonicalModel = serde_json::from_str(json)
.with_context(|| format!("failed to parse embedded canonical model '{id}'"))?;
catalog.insert(id.to_string(), model);
}
Ok(catalog)
}
pub fn embedded_providers() -> Result<Vec<RegistryProvider>> {
let catalog = embedded_model_catalog()?;
let bases = super::extends::base_map_from_embedded(BASE_FILES, PROVIDER_FILES)?;
let mut providers = Vec::with_capacity(PROVIDER_FILES.len());
for (id, json) in PROVIDER_FILES {
let mut provider = super::extends::parse_provider_json(json, &bases)
.with_context(|| format!("failed to parse embedded provider '{id}'"))?;
super::resolve::resolve_provider_refs(&mut provider, &catalog);
providers.push(provider);
}
Ok(providers)
}
pub fn embedded_transcription_providers() -> Result<Vec<RegistryTranscriptionProvider>> {
let mut providers = Vec::with_capacity(TRANSCRIPTION_PROVIDER_FILES.len());
for (id, json) in TRANSCRIPTION_PROVIDER_FILES {
let provider: RegistryTranscriptionProvider = serde_json::from_str(json)
.with_context(|| format!("failed to parse embedded transcription provider '{id}'"))?;
providers.push(provider);
}
Ok(providers)
}
pub fn embedded_provider_schema() -> Option<&'static str> {
Some(include_str!(concat!(
env!("OUT_DIR"),
"/embedded_registry/schemas/provider.schema.json"
)))
}
pub fn embedded_transcription_provider_schema() -> Option<&'static str> {
Some(include_str!(concat!(
env!("OUT_DIR"),
"/embedded_registry/schemas/transcription-provider.schema.json"
)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn embedded_manifest_parses() {
let manifest = embedded_manifest().expect("manifest should parse");
assert!(!manifest.providers.is_empty());
}
#[test]
fn embedded_manifest_version_is_at_least_2() {
let manifest = embedded_manifest().expect("manifest should parse");
assert!(
manifest.version >= 2,
"manifest version should be >= 2 (bumped when GLM-5.2 was added), got {}",
manifest.version
);
}
#[test]
fn embedded_providers_parse_cleanly() {
let providers = embedded_providers().expect("providers should parse");
assert!(!providers.is_empty());
for p in &providers {
assert!(
!p.models.is_empty(),
"embedded provider '{}' has no models",
p.id
);
}
}
#[test]
fn embedded_manifest_matches_provider_files() {
let manifest = embedded_manifest().expect("manifest");
let providers = embedded_providers().expect("providers");
assert_eq!(
manifest.providers.len(),
providers.len(),
"manifest provider count != embedded provider file count"
);
}
#[test]
fn embedded_transcription_providers_parse_cleanly() {
let providers =
embedded_transcription_providers().expect("transcription providers should parse");
assert!(
!providers.is_empty(),
"expected at least one embedded transcription provider"
);
for p in &providers {
assert!(
!p.models.is_empty(),
"transcription provider '{}' has no models",
p.id
);
assert!(
p.kind_enum().is_some(),
"transcription provider '{}' has unknown kind '{}'",
p.id,
p.kind
);
assert!(!p.api_key_env.is_empty());
assert!(!p.base_url.is_empty());
assert!(p.resolved_default_model().is_some());
}
let ids: Vec<_> = providers.iter().map(|p| p.id.as_str()).collect();
assert!(ids.contains(&"openai"));
assert!(ids.contains(&"groq"));
}
#[test]
fn embedded_manifest_matches_transcription_provider_files() {
let manifest = embedded_manifest().expect("manifest");
let providers = embedded_transcription_providers().expect("transcription providers");
assert_eq!(
manifest.transcription_providers.len(),
providers.len(),
"manifest transcription_providers count != embedded file count"
);
}
#[test]
fn embedded_transcription_schema_is_present() {
assert!(embedded_transcription_provider_schema().is_some());
}
#[test]
fn embedded_multimodal_models_are_flagged_by_modality() {
fn provider<'a>(providers: &'a [RegistryProvider], id: &str) -> &'a RegistryProvider {
providers
.iter()
.find(|provider| provider.id == id)
.unwrap_or_else(|| panic!("missing provider {id}"))
}
fn model<'a>(
provider: &'a RegistryProvider,
name: &str,
) -> &'a super::super::types::RegistryModel {
provider
.models
.iter()
.find(|model| model.name == name)
.unwrap_or_else(|| panic!("missing model {}:{}", provider.id, name))
}
fn provider_config<'a>(
providers: &'a [crate::config::types::ProviderConfig],
id: &str,
) -> &'a crate::config::types::ProviderConfig {
providers
.iter()
.find(|provider| provider.id == id)
.unwrap_or_else(|| panic!("missing provider config {id}"))
}
fn config_model<'a>(
provider: &'a crate::config::types::ProviderConfig,
name: &str,
) -> &'a crate::config::types::ProviderModelConfig {
provider
.models
.iter()
.find(|model| model.name == name)
.unwrap_or_else(|| panic!("missing model config {}:{}", provider.id, name))
}
let providers = embedded_providers().expect("providers");
let gemini = provider(&providers, "google-gemini");
assert_eq!(gemini.defaults.attachments.images, Some(true));
assert_eq!(gemini.defaults.attachments.audio, Some(true));
assert_eq!(gemini.defaults.attachments.video, Some(true));
assert_eq!(gemini.defaults.attachments.documents, Some(true));
assert_eq!(
model(gemini, "gemini-2.5-flash").attachments.images,
Some(true)
);
assert_eq!(
model(gemini, "gemini-2.5-flash").attachments.audio,
Some(true)
);
let anthropic = provider(&providers, "anthropic");
assert_eq!(anthropic.defaults.attachments.images, Some(true));
assert_eq!(anthropic.defaults.attachments.audio, Some(false));
assert_eq!(anthropic.defaults.attachments.video, Some(false));
assert_eq!(anthropic.defaults.attachments.documents, Some(true));
let openai = provider(&providers, "openai");
assert_eq!(openai.defaults.attachments.images, Some(true));
assert_eq!(model(openai, "o3-mini").attachments.images, Some(false));
let configs = providers
.into_iter()
.map(super::super::store::registry_provider_to_config)
.collect::<Vec<_>>();
let gemini_flash = config_model(
provider_config(&configs, "google-gemini"),
"gemini-2.5-flash",
);
assert_eq!(gemini_flash.supports_images, Some(true));
assert_eq!(gemini_flash.supports_audio, Some(true));
assert_eq!(gemini_flash.supports_video, Some(true));
assert_eq!(gemini_flash.supports_documents, Some(true));
let claude = config_model(provider_config(&configs, "anthropic"), "claude-sonnet-4");
assert_eq!(claude.supports_images, Some(true));
assert_eq!(claude.supports_audio, Some(false));
assert_eq!(claude.supports_video, Some(false));
assert_eq!(claude.supports_documents, Some(true));
let gpt_4o = config_model(provider_config(&configs, "openai"), "gpt-4o");
assert_eq!(gpt_4o.supports_images, Some(true));
let o3_mini = config_model(provider_config(&configs, "openai"), "o3-mini");
assert_eq!(o3_mini.supports_images, Some(false));
}
}