systemprompt-api 0.9.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::protocol::outbound::anthropic::AnthropicOutbound;
use super::protocol::outbound::openai_chat::OpenAiChatOutbound;
use super::protocol::outbound::openai_responses::OpenAiResponsesOutbound;
use super::protocol::outbound::{OutboundAdapter, OutboundAdapterRegistration};

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

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 OutboundAdapter>> {
        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 OutboundAdapter>> = HashMap::new();

        let anthropic: Arc<dyn OutboundAdapter> = Arc::new(AnthropicOutbound);
        let openai: Arc<dyn OutboundAdapter> = Arc::new(OpenAiChatOutbound);
        let responses: Arc<dyn OutboundAdapter> = Arc::new(OpenAiResponsesOutbound);

        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));
        entries.insert("openai-responses".to_string(), Arc::clone(&responses));

        for registration in inventory::iter::<OutboundAdapterRegistration> {
            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 }
    }
}