Skip to main content

gproxy_protocol/
kinds.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5/// Protocol-agnostic operation family derived from an API route.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub enum OperationFamily {
8    #[serde(rename = "model_list")]
9    ModelList,
10    #[serde(rename = "model_get")]
11    ModelGet,
12    #[serde(rename = "count_tokens")]
13    CountToken,
14    #[serde(rename = "compact")]
15    Compact,
16    #[serde(rename = "generate_content")]
17    GenerateContent,
18    #[serde(rename = "stream_generate_content")]
19    StreamGenerateContent,
20    #[serde(rename = "create_image")]
21    CreateImage,
22    #[serde(rename = "stream_create_image")]
23    StreamCreateImage,
24    #[serde(rename = "create_image_edit")]
25    CreateImageEdit,
26    #[serde(rename = "stream_create_image_edit")]
27    StreamCreateImageEdit,
28    #[serde(rename = "openai_response_websocket")]
29    OpenAiResponseWebSocket,
30    #[serde(rename = "gemini_live")]
31    GeminiLive,
32    #[serde(rename = "embeddings")]
33    Embedding,
34    #[serde(rename = "file_upload")]
35    FileUpload,
36    #[serde(rename = "file_list")]
37    FileList,
38    #[serde(rename = "file_get")]
39    FileGet,
40    #[serde(rename = "file_content")]
41    FileContent,
42    #[serde(rename = "file_delete")]
43    FileDelete,
44}
45
46impl OperationFamily {
47    pub const fn is_stream(self) -> bool {
48        matches!(
49            self,
50            Self::StreamGenerateContent | Self::StreamCreateImage | Self::StreamCreateImageEdit
51        )
52    }
53
54    pub const fn can_be_stream_driven(self) -> bool {
55        matches!(
56            self,
57            Self::GenerateContent | Self::Compact | Self::CreateImage | Self::CreateImageEdit
58        )
59    }
60}
61
62impl fmt::Display for OperationFamily {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        let label = match self {
65            Self::ModelList => "model_list",
66            Self::ModelGet => "model_get",
67            Self::CountToken => "count_tokens",
68            Self::Compact => "compact",
69            Self::GenerateContent => "generate_content",
70            Self::StreamGenerateContent => "stream_generate_content",
71            Self::CreateImage => "create_image",
72            Self::StreamCreateImage => "stream_create_image",
73            Self::CreateImageEdit => "create_image_edit",
74            Self::StreamCreateImageEdit => "stream_create_image_edit",
75            Self::OpenAiResponseWebSocket => "openai_response_websocket",
76            Self::GeminiLive => "gemini_live",
77            Self::Embedding => "embeddings",
78            Self::FileUpload => "file_upload",
79            Self::FileList => "file_list",
80            Self::FileGet => "file_get",
81            Self::FileContent => "file_content",
82            Self::FileDelete => "file_delete",
83        };
84        f.write_str(label)
85    }
86}
87
88impl TryFrom<&str> for OperationFamily {
89    type Error = &'static str;
90
91    fn try_from(value: &str) -> Result<Self, Self::Error> {
92        match value {
93            "model_list" => Ok(Self::ModelList),
94            "model_get" => Ok(Self::ModelGet),
95            "count_tokens" => Ok(Self::CountToken),
96            "compact" => Ok(Self::Compact),
97            "generate_content" => Ok(Self::GenerateContent),
98            "stream_generate_content" => Ok(Self::StreamGenerateContent),
99            "create_image" => Ok(Self::CreateImage),
100            "stream_create_image" => Ok(Self::StreamCreateImage),
101            "create_image_edit" => Ok(Self::CreateImageEdit),
102            "stream_create_image_edit" => Ok(Self::StreamCreateImageEdit),
103            "openai_response_websocket" => Ok(Self::OpenAiResponseWebSocket),
104            "gemini_live" => Ok(Self::GeminiLive),
105            "embeddings" => Ok(Self::Embedding),
106            "file_upload" => Ok(Self::FileUpload),
107            "file_list" => Ok(Self::FileList),
108            "file_get" => Ok(Self::FileGet),
109            "file_content" => Ok(Self::FileContent),
110            "file_delete" => Ok(Self::FileDelete),
111            _ => Err("unknown operation family"),
112        }
113    }
114}
115
116impl TryFrom<String> for OperationFamily {
117    type Error = &'static str;
118
119    fn try_from(value: String) -> Result<Self, Self::Error> {
120        Self::try_from(value.as_str())
121    }
122}
123
124/// Canonical protocol kind used across routing, transforms, and provider dispatch.
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
126pub enum ProtocolKind {
127    #[serde(rename = "openai")]
128    OpenAi,
129    #[serde(rename = "claude")]
130    Claude,
131    #[serde(rename = "gemini")]
132    Gemini,
133    #[serde(rename = "openai_chat_completions")]
134    OpenAiChatCompletion,
135    #[serde(rename = "gemini_ndjson")]
136    GeminiNDJson,
137    #[serde(rename = "openai_response")]
138    OpenAiResponse,
139}
140
141impl ProtocolKind {
142    pub const fn normalize_gemini_stream(self) -> Self {
143        match self {
144            Self::GeminiNDJson => Self::Gemini,
145            _ => self,
146        }
147    }
148}
149
150impl fmt::Display for ProtocolKind {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        let label = match self {
153            Self::OpenAi => "openai",
154            Self::Claude => "claude",
155            Self::Gemini => "gemini",
156            Self::OpenAiChatCompletion => "openai_chat_completions",
157            Self::GeminiNDJson => "gemini_ndjson",
158            Self::OpenAiResponse => "openai_response",
159        };
160        f.write_str(label)
161    }
162}
163
164impl TryFrom<&str> for ProtocolKind {
165    type Error = &'static str;
166
167    fn try_from(value: &str) -> Result<Self, Self::Error> {
168        match value {
169            "openai" => Ok(Self::OpenAi),
170            "claude" => Ok(Self::Claude),
171            "gemini" => Ok(Self::Gemini),
172            "openai_chat_completions" => Ok(Self::OpenAiChatCompletion),
173            "gemini_ndjson" => Ok(Self::GeminiNDJson),
174            "openai_response" => Ok(Self::OpenAiResponse),
175            _ => Err("unknown protocol kind"),
176        }
177    }
178}
179
180impl TryFrom<String> for ProtocolKind {
181    type Error = &'static str;
182
183    fn try_from(value: String) -> Result<Self, Self::Error> {
184        Self::try_from(value.as_str())
185    }
186}