Skip to main content

systemprompt_models/profile/providers/
protocol.rs

1//! Wire-protocol family for an upstream AI provider.
2//!
3//! [`WireProtocol`] names the *request/response shape* a provider speaks, not
4//! the vendor. It is the single key that both the gateway's outbound adapters
5//! and the agent-flow provider clients resolve a wire codec from: `minimax`
6//! speaks [`WireProtocol::Anthropic`]; `moonshot` and `qwen` speak
7//! [`WireProtocol::OpenAiChat`]. Decoupling the protocol from the provider name
8//! is what lets a new vendor reuse an existing codec by declaring its protocol.
9
10use serde::{Deserialize, Serialize};
11
12use crate::schema::ProviderCapabilities;
13
14/// The wire-format family a provider's endpoint speaks.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, schemars::JsonSchema)]
16pub enum WireProtocol {
17    #[serde(rename = "anthropic")]
18    Anthropic,
19    #[serde(rename = "openai-chat", alias = "openai_chat", alias = "openai")]
20    OpenAiChat,
21    #[serde(rename = "openai-responses", alias = "openai_responses")]
22    OpenAiResponses,
23    #[serde(rename = "gemini")]
24    Gemini,
25}
26
27impl WireProtocol {
28    /// The stable string tag used to resolve a codec/adapter for this protocol.
29    #[must_use]
30    pub const fn as_tag(self) -> &'static str {
31        match self {
32            Self::Anthropic => "anthropic",
33            Self::OpenAiChat => "openai-chat",
34            Self::OpenAiResponses => "openai-responses",
35            Self::Gemini => "gemini",
36        }
37    }
38
39    /// Parse a wire-protocol tag back into the enum. Inverse of
40    /// [`Self::as_tag`], and accepts the same alias spellings serde allows
41    /// so a header value and a profile value resolve identically. Returns
42    /// `None` for an unknown tag.
43    #[must_use]
44    pub fn from_tag(tag: &str) -> Option<Self> {
45        match tag {
46            "anthropic" => Some(Self::Anthropic),
47            "openai-chat" | "openai_chat" | "openai" => Some(Self::OpenAiChat),
48            "openai-responses" | "openai_responses" => Some(Self::OpenAiResponses),
49            "gemini" => Some(Self::Gemini),
50            _ => None,
51        }
52    }
53
54    /// The JSON-Schema constructs this protocol's tool/output schema parser
55    /// accepts. The wire codecs feed this to [`crate::schema::SchemaSanitizer`]
56    /// so every tool schema is reduced to a shape the upstream will accept.
57    #[must_use]
58    pub const fn schema_capabilities(self) -> ProviderCapabilities {
59        match self {
60            Self::Anthropic => ProviderCapabilities::anthropic(),
61            Self::OpenAiChat | Self::OpenAiResponses => ProviderCapabilities::openai(),
62            Self::Gemini => ProviderCapabilities::gemini(),
63        }
64    }
65}
66
67impl std::fmt::Display for WireProtocol {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        f.write_str(self.as_tag())
70    }
71}