use std::collections::HashMap as StdHashMap;
use std::sync::{Mutex as StdMutex, OnceLock as StdOnceLock};
use super::auth::apply_auth_headers;
type ContextWindowKey = (String, String, String);
type ContextWindowCache = StdMutex<StdHashMap<ContextWindowKey, Option<usize>>>;
fn context_window_cache() -> &'static ContextWindowCache {
static CACHE: StdOnceLock<ContextWindowCache> = StdOnceLock::new();
CACHE.get_or_init(|| StdMutex::new(StdHashMap::new()))
}
pub async fn fetch_provider_max_context(
provider: &str,
model: &str,
api_key: &str,
) -> Option<usize> {
if crate::llm::capabilities::lookup(provider, model)
.message_wire_format
.is_ollama()
{
return usize::try_from(
super::ollama::OllamaRuntimeSettings::for_route(provider, Some(model), None).num_ctx,
)
.ok();
}
let (local, catalog_window, base_url) = {
let pdef = crate::llm_config::provider_config(provider);
let catalog = crate::llm_config::model_catalog_entry_for_route(provider, model);
let local = pdef
.as_ref()
.is_some_and(crate::llm_config::provider_is_local);
let catalog_window = catalog.as_ref().and_then(|entry| {
let window = if local {
entry.runtime_context_window?
} else {
entry.context_window
};
usize::try_from(window).ok().filter(|window| *window > 0)
});
let base_url = pdef
.as_ref()
.map(crate::llm_config::resolve_base_url)
.unwrap_or_else(|| "https://api.openai.com/v1".to_string());
(local, catalog_window, base_url)
};
if !local && catalog_window.is_some() {
return catalog_window;
}
let cache_key = (provider.to_string(), base_url.clone(), model.to_string());
if let Ok(cache) = context_window_cache().lock() {
if let Some(value) = cache.get(&cache_key) {
return value.or(catalog_window);
}
}
let fetched = fetch_provider_max_context_uncached(provider, model, api_key, &base_url).await;
if let Ok(mut cache) = context_window_cache().lock() {
cache.insert(cache_key, fetched);
}
fetched.or(catalog_window)
}
async fn fetch_openai_compatible_context_window(
provider: &str,
model: &str,
api_key: &str,
base_url: &str,
) -> Option<usize> {
let pdef = crate::llm_config::provider_config(provider);
let client = crate::llm::utility_client_for_base_url(base_url);
let url = pdef
.as_ref()
.and_then(|def| crate::llm::readiness::build_models_url(def).ok())
.unwrap_or_else(|| format!("{}/models", base_url.trim_end_matches('/')));
let req = client
.get(&url)
.header("Content-Type", "application/json")
.timeout(std::time::Duration::from_secs(10));
let req = apply_auth_headers(req, api_key, pdef.as_ref());
let response = req.send().await.ok()?;
if !response.status().is_success() {
return None;
}
let json: serde_json::Value = response.json().await.ok()?;
let data = json.get("data").and_then(|d| d.as_array())?;
for entry in data {
let id = entry.get("id").and_then(|v| v.as_str()).unwrap_or("");
if id != model {
continue;
}
if let Some(n) = entry.get("max_model_len").and_then(|v| v.as_u64()) {
return Some(n as usize);
}
if let Some(n) = entry.get("context_length").and_then(|v| v.as_u64()) {
return Some(n as usize);
}
if let Some(n) = entry.get("max_context_length").and_then(|v| v.as_u64()) {
return Some(n as usize);
}
if let Some(n) = entry.get("n_ctx").and_then(|v| v.as_u64()) {
return Some(n as usize);
}
if let Some(n) = entry
.get("top_provider")
.and_then(|tp| tp.get("context_length"))
.and_then(|v| v.as_u64())
{
return Some(n as usize);
}
break;
}
None
}
async fn fetch_provider_max_context_uncached(
provider: &str,
model: &str,
api_key: &str,
base_url: &str,
) -> Option<usize> {
let endpoint = crate::llm::helpers::ResolvedProvider::resolve(provider).endpoint;
let is_openai_compatible = endpoint.contains("/chat/completions")
|| endpoint.contains("/responses")
|| endpoint.contains("/v1/");
if is_openai_compatible {
return fetch_openai_compatible_context_window(provider, model, api_key, base_url).await;
}
None
}