systemprompt-api 0.3.0

Axum-based HTTP server and API gateway for systemprompt.io AI governance infrastructure. Exposes governed agents, MCP, A2A, and admin endpoints with rate limiting and RBAC.
Documentation
use std::collections::HashMap;
use std::sync::{Arc, OnceLock};

use super::upstream::{
    AnthropicCompatibleUpstream, GatewayUpstream, GatewayUpstreamRegistration,
    OpenAiCompatibleUpstream,
};

pub struct GatewayUpstreamRegistry {
    entries: HashMap<String, Arc<dyn GatewayUpstream>>,
}

impl std::fmt::Debug for GatewayUpstreamRegistry {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("GatewayUpstreamRegistry")
            .field("tags", &self.tags())
            .finish()
    }
}

impl GatewayUpstreamRegistry {
    pub fn global() -> &'static Self {
        static REGISTRY: OnceLock<GatewayUpstreamRegistry> = OnceLock::new();
        REGISTRY.get_or_init(Self::build)
    }

    pub fn get(&self, tag: &str) -> Option<&Arc<dyn GatewayUpstream>> {
        self.entries.get(tag)
    }

    pub fn tags(&self) -> Vec<&str> {
        self.entries.keys().map(String::as_str).collect()
    }

    fn build() -> Self {
        let mut entries: HashMap<String, Arc<dyn GatewayUpstream>> = HashMap::new();

        let anthropic: Arc<dyn GatewayUpstream> = Arc::new(AnthropicCompatibleUpstream);
        let openai: Arc<dyn GatewayUpstream> = Arc::new(OpenAiCompatibleUpstream);

        entries.insert("anthropic".to_string(), Arc::clone(&anthropic));
        entries.insert("minimax".to_string(), Arc::clone(&anthropic));
        entries.insert("openai".to_string(), Arc::clone(&openai));
        entries.insert("moonshot".to_string(), Arc::clone(&openai));
        entries.insert("qwen".to_string(), Arc::clone(&openai));

        for registration in inventory::iter::<GatewayUpstreamRegistration> {
            let tag = registration.tag.to_string();
            if entries.contains_key(&tag) {
                tracing::warn!(
                    tag = %registration.tag,
                    "Extension-registered gateway upstream shadows a built-in"
                );
            }
            entries.insert(tag, (registration.factory)());
        }

        Self { entries }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn built_in_tags_present() {
        let registry = GatewayUpstreamRegistry::build();
        for tag in ["anthropic", "minimax", "openai", "moonshot", "qwen"] {
            assert!(registry.get(tag).is_some(), "missing built-in tag: {tag}");
        }
    }

    #[test]
    fn gemini_is_not_built_in() {
        let registry = GatewayUpstreamRegistry::build();
        assert!(registry.get("gemini").is_none());
    }
}