use super::*;
use crate::{cancellation::AgentCancellation, config::EffectiveConfig};
pub(crate) fn refresh_provider_catalog(
config: &EffectiveConfig,
settings: &crate::config::Settings,
cancellation: &AgentCancellation,
) -> Result<(), &'static str> {
let check = || cancellation.check().map_err(|_| "operation_cancelled");
check()?;
let provider = config.provider_id();
let paths = &config.paths;
let auth = &config.auth_state();
let entries = match provider {
crate::provider_ids::OPENAI_CODEX_PROVIDER => {
let auth = crate::auth::refreshed_codex_auth_state(paths, auth)
.map_err(|_| "authentication_unavailable")?;
check()?;
let AuthState::Ready {
credential: ProviderCredential::OAuth { access, account_id },
..
} = auth
else {
return Err("authentication_unavailable");
};
OpenAiCodexProvider::new(
providers::DEFAULT_CODEX_MODEL,
access,
account_id,
ReqwestHttpTransport,
)
.discover_model_catalog_cancellable(cancellation)
.and_then(|entries| {
cancellation.check()?;
enrich_with_models_dev(entries)
})
}
crate::provider_ids::ANTHROPIC_PROVIDER => {
let AuthState::Ready {
credential: ProviderCredential::ApiKey { key },
..
} = auth
else {
return Err("authentication_unavailable");
};
AnthropicProvider::new(
providers::DEFAULT_ANTHROPIC_MODEL,
key.clone(),
ReqwestHttpTransport,
)
.discover_model_catalog_cancellable(cancellation)
.and_then(|entries| {
cancellation.check()?;
models_dev::enrich_anthropic_catalog(entries)
})
}
crate::provider_ids::CLAUDE_SUBSCRIPTION_PROVIDER => SharedModelsDevLookup::default()
.get()
.map_err(anyhow::Error::from)
.and_then(|value| claude_subscription_candidates(value).map_err(anyhow::Error::msg)),
_ => {
let custom = settings
.custom_providers
.get(provider)
.ok_or("provider_unavailable")?;
let credential = match auth {
AuthState::Ready { credential, .. } => Some(credential),
_ => None,
};
let api_key = custom_provider_catalog_api_key(provider, credential)
.map_err(|_| "authentication_unavailable")?;
let extra_models = crate::config::normalized_extra_models(&custom.extra_models)
.map_err(|_| "configuration_invalid")?;
crate::providers::OpenAiCompatibleProvider::custom(
provider.to_owned(),
providers::DEFAULT_CODEX_MODEL,
api_key,
custom.base_url.clone(),
custom.use_responses_endpoint,
ReqwestHttpTransport,
)
.discover_model_catalog_cancellable(cancellation)
.and_then(|entries| {
cancellation.check()?;
enrich_custom_provider_catalog(entries, provider, custom)
})
.and_then(|entries| {
merge_custom_provider_extra_models(entries, provider, &extra_models)
.map_err(anyhow::Error::msg)
})
}
};
check()?;
let entries = entries.map_err(|error| {
if let Some(failure) = error.downcast_ref::<crate::providers::ModelCatalogRequestFailure>()
{
return failure.code();
}
match error.downcast_ref::<models_dev::ModelsDevFetchError>() {
Some(models_dev::ModelsDevFetchError::Timeout) => "remote_timeout",
Some(models_dev::ModelsDevFetchError::HttpStatus(429)) => "rate_limited",
_ => "remote_unavailable",
}
})?;
if entries.is_empty() {
return Err("empty_catalog");
}
if entries.len() > 2048 {
return Err("catalog_limit");
}
let fingerprint = match settings.custom_providers.get(provider) {
Some(custom) => cache::custom_provider_catalog_cache_fingerprint(provider, custom)
.map_err(|_| "configuration_invalid")?,
None => cache::built_in_catalog_cache_fingerprint(provider),
};
let cached = cache::CachedCatalog {
schema_version: cache::CACHE_SCHEMA_VERSION,
provider: provider.to_owned(),
cache_fingerprint: fingerprint.clone(),
fetched_at: chrono::Utc::now(),
entries,
};
let cached =
cache::validate_catalog_cache(cached, provider, &fingerprint).ok_or("invalid_catalog")?;
let identities: std::collections::BTreeSet<_> =
cached.entries.iter().map(|entry| &entry.id).collect();
if identities.len() != cached.entries.len() {
return Err("invalid_catalog");
}
let bytes = serde_json::to_vec_pretty(&cached).map_err(|_| "catalog_limit")?;
if bytes.len() > 1024 * 1024 {
return Err("catalog_limit");
}
check()?;
let path = cache::catalog_cache_path(paths, provider).ok_or("configuration_invalid")?;
crate::persistence::atomic_write(&path, &bytes).map_err(|_| "cache_write_failed")
}