1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
17pub enum OperationGroup {
18 Models,
19 CountTokens,
20 Memories,
21 GenerateContent,
22 Compact,
23 Conversation,
24 Embeddings,
25 Images,
26 Audio,
27 Video,
28 Files,
29 Search,
30 Rerank,
31 Realtime,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
37pub enum Operation {
38 ListModels,
39 GetModel,
40 CountTokens,
41 SummarizeMemory,
42 GenerateContent,
43 StreamGenerateContent,
44 GuardianReview,
45 GuardianClassify,
46 CompactContent,
47 CreateConversation,
48 CreateEmbedding,
49 BatchCreateEmbedding,
50 Rerank,
51 WebSearch,
52 CreateImage,
53 EditImage,
54 CreateSpeech,
55 CreateTranscription,
56 CreateTranslation,
57 CreateFile,
58 ListFiles,
59 RetrieveFile,
60 RetrieveFileContent,
61 DeleteFile,
62 CreateVideo,
63 RetrieveVideo,
64 ListVideos,
65 DeleteVideo,
66 DownloadVideoContent,
67 RemixVideo,
68 CreateVideoCharacter,
69 GetVideoCharacter,
70 EditVideo,
71 ExtendVideo,
72 CreateRealtimeCall,
76 ConnectRealtime,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
85pub enum WireFamily {
86 OpenAi,
87 Claude,
88 Gemini,
89}
90
91impl WireFamily {
92 pub const fn id(self) -> &'static str {
93 match self {
94 Self::OpenAi => "openai",
95 Self::Claude => "claude",
96 Self::Gemini => "gemini",
97 }
98 }
99
100 pub fn from_id(value: &str) -> Option<Self> {
101 Some(match value {
102 "openai" => Self::OpenAi,
103 "claude" => Self::Claude,
104 "gemini" => Self::Gemini,
105 _ => return None,
106 })
107 }
108
109 pub const fn client_markers(self) -> &'static [&'static str] {
115 match self {
116 Self::Claude => &["x-api-key", "anthropic-version", "anthropic-beta"],
117 Self::OpenAi | Self::Gemini => &[],
118 }
119 }
120}
121
122impl ContentGenerationKind {
123 pub const fn id(self) -> &'static str {
124 match self {
125 Self::OpenAiChat => "openai_chat",
126 Self::OpenAiResponses => "openai_responses",
127 Self::OpenAiResponsesWebSocket => "openai_responses_websocket",
128 Self::ClaudeMessages => "claude_messages",
129 Self::GeminiGenerateContent => "gemini_generate_content",
130 }
131 }
132
133 pub fn from_id(value: &str) -> Option<Self> {
134 Some(match value {
135 "openai_chat" => Self::OpenAiChat,
136 "openai_responses" => Self::OpenAiResponses,
137 "openai_responses_websocket" => Self::OpenAiResponsesWebSocket,
138 "claude_messages" => Self::ClaudeMessages,
139 "gemini_generate_content" => Self::GeminiGenerateContent,
140 _ => return None,
141 })
142 }
143}
144
145impl OperationKind {
146 pub const fn id(self) -> &'static str {
147 match self {
148 Self::ContentGeneration(kind) => kind.id(),
149 Self::Family(family) => family.id(),
150 }
151 }
152
153 pub fn from_id(value: &str) -> Option<Self> {
154 ContentGenerationKind::from_id(value)
155 .map(Self::ContentGeneration)
156 .or_else(|| WireFamily::from_id(value).map(Self::Family))
157 }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
162#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
163pub enum ContentGenerationKind {
164 OpenAiChat,
165 OpenAiResponses,
166 OpenAiResponsesWebSocket,
170 ClaudeMessages,
171 GeminiGenerateContent,
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
176#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
177pub enum OperationKind {
178 ContentGeneration(ContentGenerationKind),
179 Family(WireFamily),
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
184pub struct OperationKey {
185 operation: Operation,
186 kind: OperationKind,
187}
188
189impl OperationKey {
190 pub const fn content(operation: Operation, kind: ContentGenerationKind) -> Self {
191 assert!(
192 operation.is_content_generation(),
193 "content kind used with non-content operation"
194 );
195 Self {
196 operation,
197 kind: OperationKind::ContentGeneration(kind),
198 }
199 }
200
201 pub const fn family(operation: Operation, family: WireFamily) -> Self {
202 assert!(
203 !operation.is_content_generation(),
204 "wire family used with content operation"
205 );
206 Self {
207 operation,
208 kind: OperationKind::Family(family),
209 }
210 }
211
212 pub const fn try_new(
213 operation: Operation,
214 kind: OperationKind,
215 ) -> Result<Self, OperationKeyError> {
216 let consistent = matches!(kind, OperationKind::ContentGeneration(_))
217 == operation.is_content_generation();
218 if consistent {
219 Ok(Self { operation, kind })
220 } else {
221 Err(OperationKeyError { operation, kind })
222 }
223 }
224
225 pub const fn operation(self) -> Operation {
226 self.operation
227 }
228
229 pub const fn kind(self) -> OperationKind {
230 self.kind
231 }
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq)]
235#[non_exhaustive]
236pub struct OperationKeyError {
237 pub operation: Operation,
238 pub kind: OperationKind,
239}
240
241impl std::fmt::Display for OperationKeyError {
242 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 write!(
244 formatter,
245 "operation {:?} is inconsistent with kind {:?}",
246 self.operation, self.kind
247 )
248 }
249}
250
251impl std::error::Error for OperationKeyError {}
252
253impl OperationGroup {
254 pub const fn id(self) -> &'static str {
256 use OperationGroup::*;
257 match self {
258 Models => "models",
259 CountTokens => "count_tokens",
260 Memories => "memories",
261 GenerateContent => "generate_content",
262 Compact => "compact",
263 Conversation => "conversation",
264 Embeddings => "embeddings",
265 Images => "images",
266 Audio => "audio",
267 Video => "video",
268 Files => "files",
269 Search => "search",
270 Rerank => "rerank",
271 Realtime => "realtime",
272 }
273 }
274}
275
276impl Operation {
277 pub const fn is_content_generation(self) -> bool {
278 matches!(
279 self,
280 Self::GenerateContent
281 | Self::StreamGenerateContent
282 | Self::GuardianReview
283 | Self::GuardianClassify
284 )
285 }
286
287 pub const fn id(self) -> &'static str {
290 use Operation::*;
291 match self {
292 ListModels => "list_models",
293 GetModel => "get_model",
294 CountTokens => "count_tokens",
295 SummarizeMemory => "summarize_memory",
296 GenerateContent => "generate_content",
297 StreamGenerateContent => "stream_generate_content",
298 GuardianReview => "guardian_review",
299 GuardianClassify => "guardian_classify",
300 CompactContent => "compact_content",
301 CreateConversation => "create_conversation",
302 CreateEmbedding => "create_embedding",
303 BatchCreateEmbedding => "batch_create_embedding",
304 Rerank => "rerank",
305 WebSearch => "web_search",
306 CreateImage => "create_image",
307 EditImage => "edit_image",
308 CreateSpeech => "create_speech",
309 CreateTranscription => "create_transcription",
310 CreateTranslation => "create_translation",
311 CreateFile => "create_file",
312 ListFiles => "list_files",
313 RetrieveFile => "retrieve_file",
314 RetrieveFileContent => "retrieve_file_content",
315 DeleteFile => "delete_file",
316 CreateVideo => "create_video",
317 RetrieveVideo => "retrieve_video",
318 ListVideos => "list_videos",
319 DeleteVideo => "delete_video",
320 DownloadVideoContent => "download_video_content",
321 RemixVideo => "remix_video",
322 CreateVideoCharacter => "create_video_character",
323 GetVideoCharacter => "get_video_character",
324 EditVideo => "edit_video",
325 ExtendVideo => "extend_video",
326 CreateRealtimeCall => "create_realtime_call",
327 ConnectRealtime => "connect_realtime",
328 }
329 }
330
331 pub fn from_id(value: &str) -> Option<Self> {
332 use Operation::*;
333 Some(match value {
334 "list_models" => ListModels,
335 "get_model" => GetModel,
336 "count_tokens" => CountTokens,
337 "summarize_memory" => SummarizeMemory,
338 "generate_content" => GenerateContent,
339 "stream_generate_content" => StreamGenerateContent,
340 "guardian_review" => GuardianReview,
341 "guardian_classify" => GuardianClassify,
342 "compact_content" => CompactContent,
343 "create_conversation" => CreateConversation,
344 "create_embedding" => CreateEmbedding,
345 "batch_create_embedding" => BatchCreateEmbedding,
346 "rerank" => Rerank,
347 "web_search" => WebSearch,
348 "create_image" => CreateImage,
349 "edit_image" => EditImage,
350 "create_speech" => CreateSpeech,
351 "create_transcription" => CreateTranscription,
352 "create_translation" => CreateTranslation,
353 "create_file" => CreateFile,
354 "list_files" => ListFiles,
355 "retrieve_file" => RetrieveFile,
356 "retrieve_file_content" => RetrieveFileContent,
357 "delete_file" => DeleteFile,
358 "create_video" => CreateVideo,
359 "retrieve_video" => RetrieveVideo,
360 "list_videos" => ListVideos,
361 "delete_video" => DeleteVideo,
362 "download_video_content" => DownloadVideoContent,
363 "remix_video" => RemixVideo,
364 "create_video_character" => CreateVideoCharacter,
365 "get_video_character" => GetVideoCharacter,
366 "edit_video" => EditVideo,
367 "extend_video" => ExtendVideo,
368 "create_realtime_call" => CreateRealtimeCall,
369 "connect_realtime" => ConnectRealtime,
370 _ => return None,
371 })
372 }
373
374 pub const fn group(self) -> OperationGroup {
377 use Operation::*;
378 match self {
379 ListModels | GetModel => OperationGroup::Models,
380 CountTokens => OperationGroup::CountTokens,
381 SummarizeMemory => OperationGroup::Memories,
382 GenerateContent | StreamGenerateContent | GuardianReview | GuardianClassify => {
383 OperationGroup::GenerateContent
384 }
385 CompactContent => OperationGroup::Compact,
386 CreateConversation => OperationGroup::Conversation,
387 CreateEmbedding | BatchCreateEmbedding => OperationGroup::Embeddings,
388 Rerank => OperationGroup::Rerank,
389 WebSearch => OperationGroup::Search,
390 CreateImage | EditImage => OperationGroup::Images,
391 CreateSpeech | CreateTranscription | CreateTranslation => OperationGroup::Audio,
392 CreateFile | ListFiles | RetrieveFile | RetrieveFileContent | DeleteFile => {
393 OperationGroup::Files
394 }
395 CreateVideo | RetrieveVideo | ListVideos | DeleteVideo | DownloadVideoContent
396 | RemixVideo | CreateVideoCharacter | GetVideoCharacter | EditVideo | ExtendVideo => {
397 OperationGroup::Video
398 }
399 CreateRealtimeCall | ConnectRealtime => OperationGroup::Realtime,
400 }
401 }
402}