unigateway 1.2.1

Lightweight, local-first LLM gateway for developers. A stable, single-binary unified entry point for all your AI tools and models.
use llm_providers::{get_model_for_endpoint, get_providers_data, list_models_for_endpoint};

#[derive(Clone)]
pub(super) struct RegistryProviderOption {
    pub(super) display_name: String,
    pub(super) family_id: String,
    pub(super) provider_type: String,
    pub(super) endpoint_id: String,
    pub(super) default_base_url: String,
    pub(super) model_ids: Vec<String>,
}

fn registry_provider_type(family_id: &str, base_url: &str) -> Option<&'static str> {
    if family_id == "anthropic" {
        return Some("anthropic");
    }
    if base_url.contains("/v1") {
        return Some("openai");
    }
    None
}

pub(super) fn preferred_model_for_endpoint(
    endpoint_id: &str,
    model_ids: &[String],
) -> Option<String> {
    model_ids
        .iter()
        .filter_map(|model_id| {
            get_model_for_endpoint(endpoint_id, model_id).map(|model| {
                (
                    model.supports_tools,
                    model.context_length.unwrap_or(0),
                    model.id.to_string(),
                )
            })
        })
        .max_by(|left, right| left.0.cmp(&right.0).then(left.1.cmp(&right.1)))
        .map(|(_, _, model_id)| model_id)
        .or_else(|| model_ids.first().cloned())
}

pub(super) fn registry_provider_options() -> Vec<RegistryProviderOption> {
    let mut options = Vec::new();

    for (family_id, provider) in get_providers_data().entries() {
        let Some(endpoint) = provider.endpoints.get("global") else {
            continue;
        };
        if endpoint.region != "global" {
            continue;
        }

        // Logic check: only proceed if we can determine a provider_type (openai/anthropic)
        let Some(provider_type) = registry_provider_type(family_id, endpoint.base_url) else {
            continue;
        };

        let endpoint_id = format!("{}:global", family_id);
        let model_ids = list_models_for_endpoint(&endpoint_id).unwrap_or_default();

        options.push(RegistryProviderOption {
            display_name: endpoint.label.to_string(),
            family_id: family_id.to_string(),
            provider_type: provider_type.to_string(),
            endpoint_id,
            // Keep empty to indicate we should use llm_providers truth at runtime
            default_base_url: String::new(),
            model_ids,
        });
    }

    options.sort_by(|left, right| left.display_name.cmp(&right.display_name));
    options.push(RegistryProviderOption {
        display_name: "Other (OpenAI-compatible)".to_string(),
        family_id: "custom".to_string(),
        provider_type: "openai".to_string(),
        endpoint_id: String::new(),
        default_base_url: String::new(),
        model_ids: Vec::new(),
    });
    options
}