Skip to main content

systemprompt_models/profile/providers/
surface.rs

1//! Client-facing API surface for an upstream AI provider.
2//!
3//! [`ApiSurface`] names the *vendor API family* a provider's models are
4//! advertised under, independent of the [`WireProtocol`] the gateway speaks to
5//! reach it. A provider can speak the Anthropic wire yet not be the Anthropic
6//! vendor: `minimax` declares `wire: anthropic` (so the gateway reuses the
7//! Anthropic codec) and `surface: backend` (so its `MiniMax-M2` model is never
8//! advertised to any client API, only reached through an explicit gateway
9//! route). Keeping these two facts orthogonal is what stops a backend provider
10//! from masquerading as an Anthropic model in a client's catalog.
11//!
12//! Copyright (c) systemprompt.io — Business Source License 1.1.
13//! See <https://systemprompt.io> for licensing details.
14
15use serde::{Deserialize, Serialize};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, schemars::JsonSchema)]
18pub enum ApiSurface {
19    #[serde(rename = "anthropic")]
20    Anthropic,
21    #[serde(rename = "openai")]
22    OpenAi,
23    #[serde(rename = "gemini")]
24    Gemini,
25    #[serde(rename = "backend")]
26    Backend,
27}
28
29impl ApiSurface {
30    #[must_use]
31    pub const fn as_tag(self) -> &'static str {
32        match self {
33            Self::Anthropic => "anthropic",
34            Self::OpenAi => "openai",
35            Self::Gemini => "gemini",
36            Self::Backend => "backend",
37        }
38    }
39
40    /// `backend` parses here (it is a valid declared surface) but callers that
41    /// resolve a *client* selection must reject it: a backend provider is never
42    /// a front door.
43    #[must_use]
44    pub fn from_tag(tag: &str) -> Option<Self> {
45        match tag {
46            "anthropic" => Some(Self::Anthropic),
47            "openai" => Some(Self::OpenAi),
48            "gemini" => Some(Self::Gemini),
49            "backend" => Some(Self::Backend),
50            _ => None,
51        }
52    }
53
54    /// The single definition of the advertisement rule.
55    ///
56    /// Every host-facing catalog (`/v1/models`, `/v1/bridge/profile`, the admin
57    /// profile page) derives its exclusion of backend providers from here
58    /// rather than re-checking the variant per call site.
59    #[must_use]
60    pub const fn is_advertised(self) -> bool {
61        !matches!(self, Self::Backend)
62    }
63}
64
65impl std::fmt::Display for ApiSurface {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        f.write_str(self.as_tag())
68    }
69}