Skip to main content

openrouter2/
types.rs

1use std::collections::BTreeMap;
2
3use bytes::Bytes;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use serde_json::Value;
6
7pub type JsonObject = BTreeMap<String, Value>;
8pub type QueryParams = crate::transport::QueryParams;
9
10macro_rules! string_enum {
11    ($name:ident { $($variant:ident => $wire:literal,)* }) => {
12        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
13        pub enum $name {
14            $($variant,)*
15            Unknown(String),
16        }
17
18        impl $name {
19            pub fn as_str(&self) -> &str {
20                match self {
21                    $(Self::$variant => $wire,)*
22                    Self::Unknown(value) => value,
23                }
24            }
25        }
26
27        impl From<&str> for $name {
28            fn from(value: &str) -> Self {
29                match value {
30                    $($wire => Self::$variant,)*
31                    other => Self::Unknown(other.to_owned()),
32                }
33            }
34        }
35
36        impl From<String> for $name {
37            fn from(value: String) -> Self {
38                Self::from(value.as_str())
39            }
40        }
41
42        impl Serialize for $name {
43            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
44            where
45                S: Serializer,
46            {
47                serializer.serialize_str(self.as_str())
48            }
49        }
50
51        impl<'de> Deserialize<'de> for $name {
52            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
53            where
54                D: Deserializer<'de>,
55            {
56                let value = String::deserialize(deserializer)?;
57                Ok(Self::from(value))
58            }
59        }
60    };
61}
62
63macro_rules! json_object_type {
64    ($name:ident) => {
65        #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
66        pub struct $name {
67            #[serde(flatten)]
68            pub extra: JsonObject,
69        }
70
71        impl $name {
72            pub fn new() -> Self {
73                Self::default()
74            }
75
76            pub fn with_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
77                self.extra.insert(key.into(), value.into());
78                self
79            }
80        }
81    };
82}
83
84macro_rules! query_struct {
85    ($name:ident { $($field:ident : $typ:ty => $wire:literal,)* }) => {
86        #[derive(Debug, Clone, Default, PartialEq)]
87        pub struct $name {
88            $(pub $field: Option<$typ>,)*
89            pub extra: QueryParams,
90        }
91
92        impl $name {
93            pub fn new() -> Self {
94                Self::default()
95            }
96
97            pub fn with_extra_query(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
98                self.extra.push((key.into(), value.into()));
99                self
100            }
101        }
102
103        impl crate::transport::IntoQueryParams for $name {
104            fn into_query_params(self) -> QueryParams {
105                let mut query = Vec::new();
106                $(
107                    if let Some(value) = self.$field {
108                        query.push(($wire.to_owned(), value.to_string()));
109                    }
110                )*
111                query.extend(self.extra);
112                query
113            }
114        }
115    };
116}
117
118query_struct!(PaginationQuery {
119    offset: u32 => "offset",
120    limit: u32 => "limit",
121});
122
123query_struct!(ActivityQuery {
124    date: String => "date",
125    api_key_hash: String => "api_key_hash",
126    user_id: String => "user_id",
127});
128
129query_struct!(BenchmarksQuery {
130    source: String => "source",
131    task_type: String => "task_type",
132    arena: String => "arena",
133    category: String => "category",
134    max_results: u32 => "max_results",
135});
136
137query_struct!(TaskClassificationsQuery {
138    window: String => "window",
139});
140
141query_struct!(AppRankingsQuery {
142    category: String => "category",
143    subcategory: String => "subcategory",
144    sort: String => "sort",
145    start_date: String => "start_date",
146    end_date: String => "end_date",
147    limit: u32 => "limit",
148    offset: u32 => "offset",
149});
150
151query_struct!(RankingsDailyQuery {
152    start_date: String => "start_date",
153    end_date: String => "end_date",
154});
155
156query_struct!(FilesQuery {
157    limit: u32 => "limit",
158    cursor: String => "cursor",
159    workspace_id: String => "workspace_id",
160});
161
162query_struct!(WorkspaceScopedPaginationQuery {
163    offset: u32 => "offset",
164    limit: u32 => "limit",
165    workspace_id: String => "workspace_id",
166});
167
168query_struct!(KeysQuery {
169    include_disabled: bool => "include_disabled",
170    offset: u32 => "offset",
171    workspace_id: String => "workspace_id",
172});
173
174query_struct!(ModelsQuery {
175    category: String => "category",
176    supported_parameters: String => "supported_parameters",
177    output_modalities: String => "output_modalities",
178    sort: String => "sort",
179    q: String => "q",
180    input_modalities: String => "input_modalities",
181    context: u32 => "context",
182    min_price: f64 => "min_price",
183    max_price: f64 => "max_price",
184    arch: String => "arch",
185    model_authors: String => "model_authors",
186    providers: String => "providers",
187    distillable: String => "distillable",
188    zdr: String => "zdr",
189    region: String => "region",
190    min_output_price: f64 => "min_output_price",
191    max_output_price: f64 => "max_output_price",
192    min_age_days: u32 => "min_age_days",
193    max_age_days: u32 => "max_age_days",
194    min_intelligence_index: f64 => "min_intelligence_index",
195    max_intelligence_index: f64 => "max_intelligence_index",
196    min_coding_index: f64 => "min_coding_index",
197    max_coding_index: f64 => "max_coding_index",
198    min_agentic_index: f64 => "min_agentic_index",
199    max_agentic_index: f64 => "max_agentic_index",
200    min_tool_success_rate: f64 => "min_tool_success_rate",
201    max_tool_success_rate: f64 => "max_tool_success_rate",
202});
203
204query_struct!(ModelCountQuery {
205    output_modalities: String => "output_modalities",
206});
207
208query_struct!(VideoContentQuery {
209    index: u32 => "index",
210});
211
212pub type ByokKeysQuery = WorkspaceScopedPaginationQuery;
213pub type GuardrailsQuery = WorkspaceScopedPaginationQuery;
214pub type ObservabilityDestinationsQuery = WorkspaceScopedPaginationQuery;
215pub type KeyAssignmentsQuery = PaginationQuery;
216pub type MemberAssignmentsQuery = PaginationQuery;
217pub type OrganizationMembersQuery = PaginationQuery;
218pub type PresetsQuery = PaginationQuery;
219pub type PresetVersionsQuery = PaginationQuery;
220pub type WorkspacesQuery = PaginationQuery;
221pub type WorkspaceMembersQuery = PaginationQuery;
222
223string_enum!(ChatRole {
224    System => "system",
225    User => "user",
226    Assistant => "assistant",
227    Developer => "developer",
228    Tool => "tool",
229});
230
231string_enum!(FinishReason {
232    Stop => "stop",
233    Length => "length",
234    ToolCalls => "tool_calls",
235    ContentFilter => "content_filter",
236    Error => "error",
237});
238
239string_enum!(ServiceTier {
240    Auto => "auto",
241    Default => "default",
242    Flex => "flex",
243    Priority => "priority",
244    Scale => "scale",
245});
246
247string_enum!(DataCollection {
248    Allow => "allow",
249    Deny => "deny",
250});
251
252string_enum!(ProviderSort {
253    Price => "price",
254    Throughput => "throughput",
255    Latency => "latency",
256});
257
258#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
259#[serde(untagged)]
260pub enum StringOrList {
261    String(String),
262    List(Vec<String>),
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
266#[serde(untagged)]
267pub enum ChatContent {
268    Text(String),
269    Parts(Vec<ContentPart>),
270    Null,
271}
272
273impl Default for ChatContent {
274    fn default() -> Self {
275        Self::Text(String::new())
276    }
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
280pub struct ContentPart {
281    #[serde(rename = "type")]
282    pub type_: String,
283    #[serde(default, flatten)]
284    pub extra: JsonObject,
285}
286
287impl ContentPart {
288    pub fn text(text: impl Into<String>) -> Self {
289        let mut extra = JsonObject::new();
290        extra.insert("text".to_owned(), Value::String(text.into()));
291        Self {
292            type_: "text".to_owned(),
293            extra,
294        }
295    }
296
297    pub fn image_url(url: impl Into<String>) -> Self {
298        let mut image_url = JsonObject::new();
299        image_url.insert("url".to_owned(), Value::String(url.into()));
300        let mut extra = JsonObject::new();
301        extra.insert(
302            "image_url".to_owned(),
303            Value::Object(image_url.into_iter().collect()),
304        );
305        Self {
306            type_: "image_url".to_owned(),
307            extra,
308        }
309    }
310
311    pub fn with_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
312        self.extra.insert(key.into(), value.into());
313        self
314    }
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
318pub struct ChatMessage {
319    pub role: ChatRole,
320    #[serde(default, skip_serializing_if = "Option::is_none")]
321    pub content: Option<ChatContent>,
322    #[serde(skip_serializing_if = "Option::is_none")]
323    pub name: Option<String>,
324    #[serde(skip_serializing_if = "Option::is_none")]
325    pub tool_call_id: Option<String>,
326    #[serde(default, flatten)]
327    pub extra: JsonObject,
328}
329
330impl ChatMessage {
331    pub fn new(role: ChatRole, content: ChatContent) -> Self {
332        Self {
333            role,
334            content: Some(content),
335            name: None,
336            tool_call_id: None,
337            extra: JsonObject::new(),
338        }
339    }
340
341    pub fn system(content: impl Into<String>) -> Self {
342        Self::new(ChatRole::System, ChatContent::Text(content.into()))
343    }
344
345    pub fn user(content: impl Into<String>) -> Self {
346        Self::new(ChatRole::User, ChatContent::Text(content.into()))
347    }
348
349    pub fn assistant(content: impl Into<String>) -> Self {
350        Self::new(ChatRole::Assistant, ChatContent::Text(content.into()))
351    }
352
353    pub fn tool(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
354        let mut msg = Self::new(ChatRole::Tool, ChatContent::Text(content.into()));
355        msg.tool_call_id = Some(tool_call_id.into());
356        msg
357    }
358
359    pub fn with_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
360        self.extra.insert(key.into(), value.into());
361        self
362    }
363}
364
365#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
366pub struct ProviderPriceLimit {
367    #[serde(skip_serializing_if = "Option::is_none")]
368    pub prompt: Option<String>,
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub completion: Option<String>,
371    #[serde(skip_serializing_if = "Option::is_none")]
372    pub request: Option<String>,
373    #[serde(skip_serializing_if = "Option::is_none")]
374    pub image: Option<String>,
375    #[serde(skip_serializing_if = "Option::is_none")]
376    pub audio: Option<String>,
377    #[serde(default, flatten)]
378    pub extra: JsonObject,
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
382pub struct ProviderPreferences {
383    #[serde(skip_serializing_if = "Option::is_none")]
384    pub allow_fallbacks: Option<bool>,
385    #[serde(skip_serializing_if = "Option::is_none")]
386    pub data_collection: Option<DataCollection>,
387    #[serde(skip_serializing_if = "Option::is_none")]
388    pub enforce_distillable_text: Option<bool>,
389    #[serde(skip_serializing_if = "Option::is_none")]
390    pub ignore: Option<Vec<String>>,
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub max_price: Option<ProviderPriceLimit>,
393    #[serde(skip_serializing_if = "Option::is_none")]
394    pub only: Option<Vec<String>>,
395    #[serde(skip_serializing_if = "Option::is_none")]
396    pub order: Option<Vec<String>>,
397    #[serde(skip_serializing_if = "Option::is_none")]
398    pub preferred_max_latency: Option<f64>,
399    #[serde(skip_serializing_if = "Option::is_none")]
400    pub preferred_min_throughput: Option<f64>,
401    #[serde(skip_serializing_if = "Option::is_none")]
402    pub quantizations: Option<Vec<String>>,
403    #[serde(skip_serializing_if = "Option::is_none")]
404    pub require_parameters: Option<bool>,
405    #[serde(skip_serializing_if = "Option::is_none")]
406    pub sort: Option<Value>,
407    #[serde(skip_serializing_if = "Option::is_none")]
408    pub zdr: Option<bool>,
409    #[serde(default, flatten)]
410    pub extra: JsonObject,
411}
412
413impl ProviderPreferences {
414    pub fn new() -> Self {
415        Self::default()
416    }
417
418    pub fn require_parameters(mut self, value: bool) -> Self {
419        self.require_parameters = Some(value);
420        self
421    }
422
423    pub fn only(mut self, providers: impl IntoIterator<Item = impl Into<String>>) -> Self {
424        self.only = Some(providers.into_iter().map(Into::into).collect());
425        self
426    }
427
428    pub fn order(mut self, providers: impl IntoIterator<Item = impl Into<String>>) -> Self {
429        self.order = Some(providers.into_iter().map(Into::into).collect());
430        self
431    }
432
433    pub fn with_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
434        self.extra.insert(key.into(), value.into());
435        self
436    }
437}
438
439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
440pub struct ChatCompletionRequest {
441    #[serde(skip_serializing_if = "Option::is_none")]
442    pub cache_control: Option<Value>,
443    #[serde(skip_serializing_if = "Option::is_none")]
444    pub debug: Option<bool>,
445    #[serde(skip_serializing_if = "Option::is_none")]
446    pub image_config: Option<Value>,
447    #[serde(skip_serializing_if = "Option::is_none")]
448    pub logit_bias: Option<Value>,
449    #[serde(skip_serializing_if = "Option::is_none")]
450    pub model: Option<String>,
451    #[serde(skip_serializing_if = "Option::is_none")]
452    pub models: Option<Vec<String>>,
453    pub messages: Vec<ChatMessage>,
454    #[serde(skip_serializing_if = "Option::is_none")]
455    pub temperature: Option<f64>,
456    #[serde(skip_serializing_if = "Option::is_none")]
457    pub max_tokens: Option<u32>,
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub max_completion_tokens: Option<u32>,
460    #[serde(skip_serializing_if = "Option::is_none")]
461    pub min_p: Option<f64>,
462    #[serde(skip_serializing_if = "Option::is_none")]
463    pub modalities: Option<Vec<String>>,
464    #[serde(skip_serializing_if = "Option::is_none")]
465    pub response_format: Option<Value>,
466    #[serde(skip_serializing_if = "Option::is_none")]
467    pub provider: Option<ProviderPreferences>,
468    #[serde(skip_serializing_if = "Option::is_none")]
469    pub plugins: Option<Value>,
470    #[serde(skip_serializing_if = "Option::is_none")]
471    pub route: Option<Value>,
472    #[serde(skip_serializing_if = "Option::is_none")]
473    pub service_tier: Option<ServiceTier>,
474    #[serde(skip_serializing_if = "Option::is_none")]
475    pub stream: Option<bool>,
476    #[serde(skip_serializing_if = "Option::is_none")]
477    pub stream_options: Option<Value>,
478    #[serde(skip_serializing_if = "Option::is_none")]
479    pub tools: Option<Value>,
480    #[serde(skip_serializing_if = "Option::is_none")]
481    pub tool_choice: Option<Value>,
482    #[serde(skip_serializing_if = "Option::is_none")]
483    pub parallel_tool_calls: Option<bool>,
484    #[serde(skip_serializing_if = "Option::is_none")]
485    pub reasoning: Option<Value>,
486    #[serde(skip_serializing_if = "Option::is_none")]
487    pub reasoning_effort: Option<String>,
488    #[serde(skip_serializing_if = "Option::is_none")]
489    pub stop: Option<Value>,
490    #[serde(skip_serializing_if = "Option::is_none")]
491    pub stop_server_tools_when: Option<Value>,
492    #[serde(skip_serializing_if = "Option::is_none")]
493    pub top_a: Option<f64>,
494    #[serde(skip_serializing_if = "Option::is_none")]
495    pub top_p: Option<f64>,
496    #[serde(skip_serializing_if = "Option::is_none")]
497    pub top_k: Option<u32>,
498    #[serde(skip_serializing_if = "Option::is_none")]
499    pub top_logprobs: Option<u32>,
500    #[serde(skip_serializing_if = "Option::is_none")]
501    pub logprobs: Option<bool>,
502    #[serde(skip_serializing_if = "Option::is_none")]
503    pub frequency_penalty: Option<f64>,
504    #[serde(skip_serializing_if = "Option::is_none")]
505    pub presence_penalty: Option<f64>,
506    #[serde(skip_serializing_if = "Option::is_none")]
507    pub repetition_penalty: Option<f64>,
508    #[serde(skip_serializing_if = "Option::is_none")]
509    pub seed: Option<i64>,
510    #[serde(skip_serializing_if = "Option::is_none")]
511    pub user: Option<String>,
512    #[serde(skip_serializing_if = "Option::is_none")]
513    pub session_id: Option<String>,
514    #[serde(skip_serializing_if = "Option::is_none")]
515    pub metadata: Option<JsonObject>,
516    #[serde(skip_serializing_if = "Option::is_none")]
517    pub trace: Option<Value>,
518    #[serde(default, flatten)]
519    pub extra: JsonObject,
520}
521
522impl ChatCompletionRequest {
523    pub fn new(model: impl Into<String>, messages: Vec<ChatMessage>) -> Self {
524        Self::from_model_fields(Some(model.into()), None, messages)
525    }
526
527    pub fn new_with_models(
528        models: impl IntoIterator<Item = impl Into<String>>,
529        messages: Vec<ChatMessage>,
530    ) -> Self {
531        Self::from_model_fields(
532            None,
533            Some(models.into_iter().map(Into::into).collect()),
534            messages,
535        )
536    }
537
538    fn from_model_fields(
539        model: Option<String>,
540        models: Option<Vec<String>>,
541        messages: Vec<ChatMessage>,
542    ) -> Self {
543        Self {
544            cache_control: None,
545            debug: None,
546            image_config: None,
547            logit_bias: None,
548            model,
549            models,
550            messages,
551            temperature: None,
552            max_tokens: None,
553            max_completion_tokens: None,
554            min_p: None,
555            modalities: None,
556            response_format: None,
557            provider: None,
558            plugins: None,
559            route: None,
560            service_tier: None,
561            stream: None,
562            stream_options: None,
563            tools: None,
564            tool_choice: None,
565            parallel_tool_calls: None,
566            reasoning: None,
567            reasoning_effort: None,
568            stop: None,
569            stop_server_tools_when: None,
570            top_a: None,
571            top_p: None,
572            top_k: None,
573            top_logprobs: None,
574            logprobs: None,
575            frequency_penalty: None,
576            presence_penalty: None,
577            repetition_penalty: None,
578            seed: None,
579            user: None,
580            session_id: None,
581            metadata: None,
582            trace: None,
583            extra: JsonObject::new(),
584        }
585    }
586
587    pub fn message(mut self, message: ChatMessage) -> Self {
588        self.messages.push(message);
589        self
590    }
591
592    pub fn temperature(mut self, value: f64) -> Self {
593        self.temperature = Some(value);
594        self
595    }
596
597    pub fn max_tokens(mut self, value: u32) -> Self {
598        self.max_tokens = Some(value);
599        self
600    }
601
602    pub fn stream(mut self, value: bool) -> Self {
603        self.stream = Some(value);
604        self
605    }
606
607    pub fn provider(mut self, value: ProviderPreferences) -> Self {
608        self.provider = Some(value);
609        self
610    }
611
612    pub fn with_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
613        self.extra.insert(key.into(), value.into());
614        self
615    }
616}
617
618#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
619pub struct Usage {
620    #[serde(default)]
621    pub prompt_tokens: Option<i64>,
622    #[serde(default)]
623    pub completion_tokens: Option<i64>,
624    #[serde(default)]
625    pub total_tokens: Option<i64>,
626    #[serde(default, flatten)]
627    pub extra: JsonObject,
628}
629
630#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
631pub struct ChatChoice {
632    #[serde(default)]
633    pub index: Option<i64>,
634    #[serde(default)]
635    pub finish_reason: Option<FinishReason>,
636    #[serde(default)]
637    pub message: Option<ChatMessage>,
638    #[serde(default)]
639    pub delta: Option<Value>,
640    #[serde(default)]
641    pub logprobs: Option<Value>,
642    #[serde(default, flatten)]
643    pub extra: JsonObject,
644}
645
646#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
647pub struct ChatCompletionResponse {
648    #[serde(default)]
649    pub id: Option<String>,
650    #[serde(default)]
651    pub choices: Vec<ChatChoice>,
652    #[serde(default)]
653    pub created: Option<i64>,
654    #[serde(default)]
655    pub model: Option<String>,
656    #[serde(default)]
657    pub object: Option<String>,
658    #[serde(default)]
659    pub usage: Option<Usage>,
660    #[serde(default)]
661    pub system_fingerprint: Option<String>,
662    #[serde(default)]
663    pub service_tier: Option<ServiceTier>,
664    #[serde(default)]
665    pub openrouter_metadata: Option<Value>,
666    #[serde(default, flatten)]
667    pub extra: JsonObject,
668}
669
670pub type ChatStreamChunk = ChatCompletionResponse;
671
672#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
673pub struct ResponsesRequest {
674    #[serde(skip_serializing_if = "Option::is_none")]
675    pub background: Option<bool>,
676    #[serde(skip_serializing_if = "Option::is_none")]
677    pub cache_control: Option<Value>,
678    #[serde(skip_serializing_if = "Option::is_none")]
679    pub debug: Option<bool>,
680    #[serde(skip_serializing_if = "Option::is_none")]
681    pub model: Option<String>,
682    #[serde(skip_serializing_if = "Option::is_none")]
683    pub models: Option<Vec<String>>,
684    #[serde(skip_serializing_if = "Option::is_none")]
685    pub input: Option<Value>,
686    #[serde(skip_serializing_if = "Option::is_none")]
687    pub instructions: Option<String>,
688    #[serde(skip_serializing_if = "Option::is_none")]
689    pub previous_response_id: Option<String>,
690    #[serde(skip_serializing_if = "Option::is_none")]
691    pub temperature: Option<f64>,
692    #[serde(skip_serializing_if = "Option::is_none")]
693    pub frequency_penalty: Option<f64>,
694    #[serde(skip_serializing_if = "Option::is_none")]
695    pub image_config: Option<Value>,
696    #[serde(skip_serializing_if = "Option::is_none")]
697    pub include: Option<Value>,
698    #[serde(skip_serializing_if = "Option::is_none")]
699    pub max_output_tokens: Option<u32>,
700    #[serde(skip_serializing_if = "Option::is_none")]
701    pub max_tool_calls: Option<u32>,
702    #[serde(skip_serializing_if = "Option::is_none")]
703    pub metadata: Option<JsonObject>,
704    #[serde(skip_serializing_if = "Option::is_none")]
705    pub modalities: Option<Vec<String>>,
706    #[serde(skip_serializing_if = "Option::is_none")]
707    pub parallel_tool_calls: Option<bool>,
708    #[serde(skip_serializing_if = "Option::is_none")]
709    pub plugins: Option<Value>,
710    #[serde(skip_serializing_if = "Option::is_none")]
711    pub presence_penalty: Option<f64>,
712    #[serde(skip_serializing_if = "Option::is_none")]
713    pub prompt: Option<Value>,
714    #[serde(skip_serializing_if = "Option::is_none")]
715    pub prompt_cache_key: Option<String>,
716    #[serde(skip_serializing_if = "Option::is_none")]
717    pub reasoning: Option<Value>,
718    #[serde(skip_serializing_if = "Option::is_none")]
719    pub route: Option<Value>,
720    #[serde(skip_serializing_if = "Option::is_none")]
721    pub safety_identifier: Option<String>,
722    #[serde(skip_serializing_if = "Option::is_none")]
723    pub service_tier: Option<ServiceTier>,
724    #[serde(skip_serializing_if = "Option::is_none")]
725    pub session_id: Option<String>,
726    #[serde(skip_serializing_if = "Option::is_none")]
727    pub stop_server_tools_when: Option<Value>,
728    #[serde(skip_serializing_if = "Option::is_none")]
729    pub text: Option<Value>,
730    #[serde(skip_serializing_if = "Option::is_none")]
731    pub tools: Option<Value>,
732    #[serde(skip_serializing_if = "Option::is_none")]
733    pub tool_choice: Option<Value>,
734    #[serde(skip_serializing_if = "Option::is_none")]
735    pub provider: Option<ProviderPreferences>,
736    #[serde(skip_serializing_if = "Option::is_none")]
737    pub stream: Option<bool>,
738    #[serde(skip_serializing_if = "Option::is_none")]
739    pub store: Option<bool>,
740    #[serde(skip_serializing_if = "Option::is_none")]
741    pub top_k: Option<u32>,
742    #[serde(skip_serializing_if = "Option::is_none")]
743    pub top_logprobs: Option<u32>,
744    #[serde(skip_serializing_if = "Option::is_none")]
745    pub top_p: Option<f64>,
746    #[serde(skip_serializing_if = "Option::is_none")]
747    pub trace: Option<Value>,
748    #[serde(skip_serializing_if = "Option::is_none")]
749    pub truncation: Option<String>,
750    #[serde(skip_serializing_if = "Option::is_none")]
751    pub user: Option<String>,
752    #[serde(default, flatten)]
753    pub extra: JsonObject,
754}
755
756impl ResponsesRequest {
757    pub fn new(model: impl Into<String>, input: impl Into<Value>) -> Self {
758        Self::from_model_fields(Some(model.into()), None, input.into())
759    }
760
761    pub fn new_with_models(
762        models: impl IntoIterator<Item = impl Into<String>>,
763        input: impl Into<Value>,
764    ) -> Self {
765        Self::from_model_fields(
766            None,
767            Some(models.into_iter().map(Into::into).collect()),
768            input.into(),
769        )
770    }
771
772    fn from_model_fields(model: Option<String>, models: Option<Vec<String>>, input: Value) -> Self {
773        Self {
774            background: None,
775            cache_control: None,
776            debug: None,
777            model,
778            models,
779            input: Some(input),
780            instructions: None,
781            previous_response_id: None,
782            temperature: None,
783            frequency_penalty: None,
784            image_config: None,
785            include: None,
786            max_output_tokens: None,
787            max_tool_calls: None,
788            metadata: None,
789            modalities: None,
790            parallel_tool_calls: None,
791            plugins: None,
792            presence_penalty: None,
793            prompt: None,
794            prompt_cache_key: None,
795            reasoning: None,
796            route: None,
797            safety_identifier: None,
798            service_tier: None,
799            session_id: None,
800            stop_server_tools_when: None,
801            text: None,
802            tools: None,
803            tool_choice: None,
804            provider: None,
805            stream: None,
806            store: None,
807            top_k: None,
808            top_logprobs: None,
809            top_p: None,
810            trace: None,
811            truncation: None,
812            user: None,
813            extra: JsonObject::new(),
814        }
815    }
816
817    pub fn stream(mut self, value: bool) -> Self {
818        self.stream = Some(value);
819        self
820    }
821
822    pub fn with_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
823        self.extra.insert(key.into(), value.into());
824        self
825    }
826}
827
828#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
829pub struct ResponsesResponse {
830    #[serde(default)]
831    pub id: Option<String>,
832    #[serde(default)]
833    pub object: Option<String>,
834    #[serde(default)]
835    pub status: Option<String>,
836    #[serde(default)]
837    pub model: Option<String>,
838    #[serde(default)]
839    pub output: Option<Value>,
840    #[serde(default)]
841    pub output_text: Option<String>,
842    #[serde(default)]
843    pub usage: Option<Usage>,
844    #[serde(default, flatten)]
845    pub extra: JsonObject,
846}
847
848#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
849pub struct StreamedResponsesEvent {
850    #[serde(default, rename = "type")]
851    pub type_: Option<String>,
852    #[serde(default)]
853    pub response: Option<ResponsesResponse>,
854    #[serde(default)]
855    pub item: Option<Value>,
856    #[serde(default)]
857    pub delta: Option<Value>,
858    #[serde(default, flatten)]
859    pub extra: JsonObject,
860}
861
862#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
863pub struct MessagesRequest {
864    #[serde(skip_serializing_if = "Option::is_none")]
865    pub cache_control: Option<Value>,
866    #[serde(skip_serializing_if = "Option::is_none")]
867    pub context_management: Option<Value>,
868    #[serde(skip_serializing_if = "Option::is_none")]
869    pub fallbacks: Option<Value>,
870    #[serde(skip_serializing_if = "Option::is_none")]
871    pub model: Option<String>,
872    #[serde(skip_serializing_if = "Option::is_none")]
873    pub models: Option<Vec<String>>,
874    pub messages: Vec<ChatMessage>,
875    #[serde(skip_serializing_if = "Option::is_none")]
876    pub metadata: Option<JsonObject>,
877    #[serde(skip_serializing_if = "Option::is_none")]
878    pub max_tokens: Option<u32>,
879    #[serde(skip_serializing_if = "Option::is_none")]
880    pub output_config: Option<Value>,
881    #[serde(skip_serializing_if = "Option::is_none")]
882    pub plugins: Option<Value>,
883    #[serde(skip_serializing_if = "Option::is_none")]
884    pub system: Option<Value>,
885    #[serde(skip_serializing_if = "Option::is_none")]
886    pub temperature: Option<f64>,
887    #[serde(skip_serializing_if = "Option::is_none")]
888    pub tools: Option<Value>,
889    #[serde(skip_serializing_if = "Option::is_none")]
890    pub tool_choice: Option<Value>,
891    #[serde(skip_serializing_if = "Option::is_none")]
892    pub thinking: Option<Value>,
893    #[serde(skip_serializing_if = "Option::is_none")]
894    pub provider: Option<ProviderPreferences>,
895    #[serde(skip_serializing_if = "Option::is_none")]
896    pub route: Option<Value>,
897    #[serde(skip_serializing_if = "Option::is_none")]
898    pub service_tier: Option<ServiceTier>,
899    #[serde(skip_serializing_if = "Option::is_none")]
900    pub session_id: Option<String>,
901    #[serde(skip_serializing_if = "Option::is_none")]
902    pub speed: Option<f64>,
903    #[serde(skip_serializing_if = "Option::is_none")]
904    pub stop_sequences: Option<Vec<String>>,
905    #[serde(skip_serializing_if = "Option::is_none")]
906    pub stop_server_tools_when: Option<Value>,
907    #[serde(skip_serializing_if = "Option::is_none")]
908    pub stream: Option<bool>,
909    #[serde(skip_serializing_if = "Option::is_none")]
910    pub top_k: Option<u32>,
911    #[serde(skip_serializing_if = "Option::is_none")]
912    pub top_p: Option<f64>,
913    #[serde(skip_serializing_if = "Option::is_none")]
914    pub trace: Option<Value>,
915    #[serde(skip_serializing_if = "Option::is_none")]
916    pub user: Option<String>,
917    #[serde(default, flatten)]
918    pub extra: JsonObject,
919}
920
921impl MessagesRequest {
922    pub fn new(model: impl Into<String>, messages: Vec<ChatMessage>) -> Self {
923        Self::from_model_fields(Some(model.into()), None, messages)
924    }
925
926    pub fn new_with_models(
927        models: impl IntoIterator<Item = impl Into<String>>,
928        messages: Vec<ChatMessage>,
929    ) -> Self {
930        Self::from_model_fields(
931            None,
932            Some(models.into_iter().map(Into::into).collect()),
933            messages,
934        )
935    }
936
937    fn from_model_fields(
938        model: Option<String>,
939        models: Option<Vec<String>>,
940        messages: Vec<ChatMessage>,
941    ) -> Self {
942        Self {
943            cache_control: None,
944            context_management: None,
945            fallbacks: None,
946            model,
947            models,
948            messages,
949            metadata: None,
950            max_tokens: None,
951            output_config: None,
952            plugins: None,
953            system: None,
954            temperature: None,
955            tools: None,
956            tool_choice: None,
957            thinking: None,
958            provider: None,
959            route: None,
960            service_tier: None,
961            session_id: None,
962            speed: None,
963            stop_sequences: None,
964            stop_server_tools_when: None,
965            stream: None,
966            top_k: None,
967            top_p: None,
968            trace: None,
969            user: None,
970            extra: JsonObject::new(),
971        }
972    }
973
974    pub fn max_tokens(mut self, value: u32) -> Self {
975        self.max_tokens = Some(value);
976        self
977    }
978
979    pub fn stream(mut self, value: bool) -> Self {
980        self.stream = Some(value);
981        self
982    }
983
984    pub fn with_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
985        self.extra.insert(key.into(), value.into());
986        self
987    }
988}
989
990#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
991pub struct MessagesResponse {
992    #[serde(default)]
993    pub id: Option<String>,
994    #[serde(default, rename = "type")]
995    pub type_: Option<String>,
996    #[serde(default)]
997    pub role: Option<String>,
998    #[serde(default)]
999    pub content: Option<Value>,
1000    #[serde(default)]
1001    pub model: Option<String>,
1002    #[serde(default)]
1003    pub stop_reason: Option<String>,
1004    #[serde(default)]
1005    pub usage: Option<Usage>,
1006    #[serde(default, flatten)]
1007    pub extra: JsonObject,
1008}
1009
1010pub type MessagesStreamEvent = MessagesResponse;
1011
1012#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1013pub struct EmbeddingsRequest {
1014    pub model: String,
1015    pub input: Value,
1016    #[serde(skip_serializing_if = "Option::is_none")]
1017    pub dimensions: Option<u32>,
1018    #[serde(skip_serializing_if = "Option::is_none")]
1019    pub encoding_format: Option<String>,
1020    #[serde(skip_serializing_if = "Option::is_none")]
1021    pub input_type: Option<String>,
1022    #[serde(skip_serializing_if = "Option::is_none")]
1023    pub provider: Option<ProviderPreferences>,
1024    #[serde(skip_serializing_if = "Option::is_none")]
1025    pub user: Option<String>,
1026    #[serde(default, flatten)]
1027    pub extra: JsonObject,
1028}
1029
1030impl EmbeddingsRequest {
1031    pub fn new(model: impl Into<String>, input: impl Into<Value>) -> Self {
1032        Self {
1033            model: model.into(),
1034            input: input.into(),
1035            dimensions: None,
1036            encoding_format: None,
1037            input_type: None,
1038            provider: None,
1039            user: None,
1040            extra: JsonObject::new(),
1041        }
1042    }
1043}
1044
1045json_object_type!(EmbeddingsResponse);
1046json_object_type!(EmbeddingModelsResponse);
1047
1048#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1049#[serde(untagged)]
1050pub enum RerankDocument {
1051    Text(String),
1052    Object(Value),
1053}
1054
1055impl From<String> for RerankDocument {
1056    fn from(value: String) -> Self {
1057        Self::Text(value)
1058    }
1059}
1060
1061impl From<&str> for RerankDocument {
1062    fn from(value: &str) -> Self {
1063        Self::Text(value.to_owned())
1064    }
1065}
1066
1067#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1068pub struct RerankRequest {
1069    pub model: String,
1070    pub query: String,
1071    pub documents: Vec<RerankDocument>,
1072    #[serde(skip_serializing_if = "Option::is_none")]
1073    pub provider: Option<ProviderPreferences>,
1074    #[serde(skip_serializing_if = "Option::is_none")]
1075    pub top_n: Option<u32>,
1076    #[serde(default, flatten)]
1077    pub extra: JsonObject,
1078}
1079
1080impl RerankRequest {
1081    pub fn new(
1082        model: impl Into<String>,
1083        query: impl Into<String>,
1084        documents: impl IntoIterator<Item = impl Into<RerankDocument>>,
1085    ) -> Self {
1086        Self {
1087            model: model.into(),
1088            query: query.into(),
1089            documents: documents.into_iter().map(Into::into).collect(),
1090            provider: None,
1091            top_n: None,
1092            extra: JsonObject::new(),
1093        }
1094    }
1095}
1096
1097json_object_type!(RerankResponse);
1098
1099#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1100pub struct SpeechRequest {
1101    pub model: String,
1102    pub input: String,
1103    pub voice: String,
1104    #[serde(skip_serializing_if = "Option::is_none")]
1105    pub response_format: Option<String>,
1106    #[serde(skip_serializing_if = "Option::is_none")]
1107    pub speed: Option<f64>,
1108    #[serde(default, flatten)]
1109    pub extra: JsonObject,
1110}
1111
1112impl SpeechRequest {
1113    pub fn new(
1114        model: impl Into<String>,
1115        input: impl Into<String>,
1116        voice: impl Into<String>,
1117    ) -> Self {
1118        Self {
1119            model: model.into(),
1120            input: input.into(),
1121            voice: voice.into(),
1122            response_format: None,
1123            speed: None,
1124            extra: JsonObject::new(),
1125        }
1126    }
1127}
1128
1129#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1130pub struct TranscriptionRequest {
1131    pub model: String,
1132    pub input_audio: Value,
1133    #[serde(skip_serializing_if = "Option::is_none")]
1134    pub language: Option<String>,
1135    #[serde(skip_serializing_if = "Option::is_none")]
1136    pub provider: Option<ProviderPreferences>,
1137    #[serde(skip_serializing_if = "Option::is_none")]
1138    pub temperature: Option<f64>,
1139    #[serde(default, flatten)]
1140    pub extra: JsonObject,
1141}
1142
1143impl TranscriptionRequest {
1144    pub fn new(model: impl Into<String>, input_audio: impl Into<Value>) -> Self {
1145        Self {
1146            model: model.into(),
1147            input_audio: input_audio.into(),
1148            language: None,
1149            provider: None,
1150            temperature: None,
1151            extra: JsonObject::new(),
1152        }
1153    }
1154}
1155
1156#[derive(Debug, Clone, PartialEq)]
1157pub struct TranscriptionFileRequest {
1158    pub model: String,
1159    pub bytes: Bytes,
1160    pub file_name: Option<String>,
1161    pub content_type: Option<String>,
1162    pub language: Option<String>,
1163    pub response_format: Option<String>,
1164    pub temperature: Option<f64>,
1165}
1166
1167impl TranscriptionFileRequest {
1168    pub fn new(model: impl Into<String>, bytes: impl Into<Bytes>) -> Self {
1169        Self {
1170            model: model.into(),
1171            bytes: bytes.into(),
1172            file_name: None,
1173            content_type: None,
1174            language: None,
1175            response_format: None,
1176            temperature: None,
1177        }
1178    }
1179
1180    pub fn with_file_name(mut self, value: impl Into<String>) -> Self {
1181        self.file_name = Some(value.into());
1182        self
1183    }
1184
1185    pub fn with_content_type(mut self, value: impl Into<String>) -> Self {
1186        self.content_type = Some(value.into());
1187        self
1188    }
1189}
1190
1191json_object_type!(TranscriptionResponse);
1192
1193#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1194pub struct ImageGenerationRequest {
1195    pub model: String,
1196    #[serde(skip_serializing_if = "Option::is_none")]
1197    pub prompt: Option<String>,
1198    #[serde(skip_serializing_if = "Option::is_none")]
1199    pub aspect_ratio: Option<String>,
1200    #[serde(skip_serializing_if = "Option::is_none")]
1201    pub background: Option<String>,
1202    #[serde(skip_serializing_if = "Option::is_none")]
1203    pub input_references: Option<Value>,
1204    #[serde(skip_serializing_if = "Option::is_none")]
1205    pub n: Option<u32>,
1206    #[serde(skip_serializing_if = "Option::is_none")]
1207    pub output_compression: Option<u32>,
1208    #[serde(skip_serializing_if = "Option::is_none")]
1209    pub output_format: Option<String>,
1210    #[serde(skip_serializing_if = "Option::is_none")]
1211    pub provider: Option<ProviderPreferences>,
1212    #[serde(skip_serializing_if = "Option::is_none")]
1213    pub quality: Option<String>,
1214    #[serde(skip_serializing_if = "Option::is_none")]
1215    pub resolution: Option<String>,
1216    #[serde(skip_serializing_if = "Option::is_none")]
1217    pub seed: Option<i64>,
1218    #[serde(skip_serializing_if = "Option::is_none")]
1219    pub size: Option<String>,
1220    #[serde(skip_serializing_if = "Option::is_none")]
1221    pub stream: Option<bool>,
1222    #[serde(default, flatten)]
1223    pub extra: JsonObject,
1224}
1225
1226impl ImageGenerationRequest {
1227    pub fn new(model: impl Into<String>) -> Self {
1228        Self {
1229            model: model.into(),
1230            prompt: None,
1231            aspect_ratio: None,
1232            background: None,
1233            input_references: None,
1234            n: None,
1235            output_compression: None,
1236            output_format: None,
1237            provider: None,
1238            quality: None,
1239            resolution: None,
1240            seed: None,
1241            size: None,
1242            stream: None,
1243            extra: JsonObject::new(),
1244        }
1245    }
1246
1247    pub fn prompt(mut self, prompt: impl Into<String>) -> Self {
1248        self.prompt = Some(prompt.into());
1249        self
1250    }
1251}
1252
1253json_object_type!(ImageGenerationResponse);
1254json_object_type!(ImageModelsResponse);
1255json_object_type!(ImageModelEndpointsResponse);
1256
1257#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1258pub struct VideoGenerationRequest {
1259    pub model: String,
1260    #[serde(skip_serializing_if = "Option::is_none")]
1261    pub prompt: Option<String>,
1262    #[serde(skip_serializing_if = "Option::is_none")]
1263    pub aspect_ratio: Option<String>,
1264    #[serde(skip_serializing_if = "Option::is_none")]
1265    pub callback_url: Option<String>,
1266    #[serde(skip_serializing_if = "Option::is_none")]
1267    pub duration: Option<f64>,
1268    #[serde(skip_serializing_if = "Option::is_none")]
1269    pub frame_images: Option<Value>,
1270    #[serde(skip_serializing_if = "Option::is_none")]
1271    pub generate_audio: Option<bool>,
1272    #[serde(skip_serializing_if = "Option::is_none")]
1273    pub input_references: Option<Value>,
1274    #[serde(skip_serializing_if = "Option::is_none")]
1275    pub provider: Option<ProviderPreferences>,
1276    #[serde(skip_serializing_if = "Option::is_none")]
1277    pub resolution: Option<String>,
1278    #[serde(skip_serializing_if = "Option::is_none")]
1279    pub seed: Option<i64>,
1280    #[serde(skip_serializing_if = "Option::is_none")]
1281    pub size: Option<String>,
1282    #[serde(default, flatten)]
1283    pub extra: JsonObject,
1284}
1285
1286impl VideoGenerationRequest {
1287    pub fn new(model: impl Into<String>) -> Self {
1288        Self {
1289            model: model.into(),
1290            prompt: None,
1291            aspect_ratio: None,
1292            callback_url: None,
1293            duration: None,
1294            frame_images: None,
1295            generate_audio: None,
1296            input_references: None,
1297            provider: None,
1298            resolution: None,
1299            seed: None,
1300            size: None,
1301            extra: JsonObject::new(),
1302        }
1303    }
1304
1305    pub fn prompt(mut self, prompt: impl Into<String>) -> Self {
1306        self.prompt = Some(prompt.into());
1307        self
1308    }
1309}
1310
1311json_object_type!(VideoGenerationResponse);
1312json_object_type!(VideoStatusResponse);
1313json_object_type!(VideoModelsResponse);
1314
1315#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1316pub struct BinaryResponse {
1317    #[serde(skip)]
1318    pub bytes: Bytes,
1319    pub content_type: Option<String>,
1320    pub content_disposition: Option<String>,
1321}
1322
1323#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1324pub struct FileUploadRequest {
1325    #[serde(skip)]
1326    pub bytes: Bytes,
1327    pub file_name: Option<String>,
1328    pub content_type: Option<String>,
1329}
1330
1331impl FileUploadRequest {
1332    pub fn new(bytes: impl Into<Bytes>) -> Self {
1333        Self {
1334            bytes: bytes.into(),
1335            file_name: None,
1336            content_type: None,
1337        }
1338    }
1339
1340    pub fn with_file_name(mut self, value: impl Into<String>) -> Self {
1341        self.file_name = Some(value.into());
1342        self
1343    }
1344
1345    pub fn with_content_type(mut self, value: impl Into<String>) -> Self {
1346        self.content_type = Some(value.into());
1347        self
1348    }
1349}
1350
1351json_object_type!(FileListResponse);
1352json_object_type!(FileMetadataResponse);
1353json_object_type!(FileDeleteResponse);
1354json_object_type!(FileUploadResponse);
1355
1356#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1357pub struct GenerationResponse {
1358    #[serde(default)]
1359    pub data: Option<GenerationData>,
1360    #[serde(default, flatten)]
1361    pub extra: JsonObject,
1362}
1363
1364#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1365pub struct GenerationData {
1366    #[serde(default)]
1367    pub id: Option<String>,
1368    #[serde(default)]
1369    pub model: Option<String>,
1370    #[serde(default)]
1371    pub total_cost: Option<f64>,
1372    #[serde(default)]
1373    pub usage: Option<f64>,
1374    #[serde(default)]
1375    pub provider_name: Option<String>,
1376    #[serde(default)]
1377    pub latency: Option<f64>,
1378    #[serde(default)]
1379    pub tokens_prompt: Option<i64>,
1380    #[serde(default)]
1381    pub tokens_completion: Option<i64>,
1382    #[serde(default)]
1383    pub finish_reason: Option<String>,
1384    #[serde(default, flatten)]
1385    pub extra: JsonObject,
1386}
1387
1388impl GenerationResponse {
1389    pub fn total_cost(&self) -> Option<f64> {
1390        self.data.as_ref().and_then(|data| data.total_cost)
1391    }
1392}
1393
1394json_object_type!(GenerationContentResponse);
1395
1396#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1397pub struct DataResponse<T> {
1398    #[serde(default)]
1399    pub data: Option<T>,
1400    #[serde(default, flatten)]
1401    pub extra: JsonObject,
1402}
1403
1404#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1405pub struct ListResponse<T> {
1406    #[serde(default)]
1407    pub data: Vec<T>,
1408    #[serde(default)]
1409    pub total_count: Option<i64>,
1410    #[serde(default, flatten)]
1411    pub extra: JsonObject,
1412}
1413
1414#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1415pub struct DeleteResponse {
1416    #[serde(default)]
1417    pub deleted: Option<bool>,
1418    #[serde(default, flatten)]
1419    pub extra: JsonObject,
1420}
1421
1422#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1423pub struct ActivityResponse {
1424    #[serde(default)]
1425    pub data: Vec<ActivityItem>,
1426    #[serde(default, flatten)]
1427    pub extra: JsonObject,
1428}
1429
1430#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1431pub struct ActivityItem {
1432    #[serde(default)]
1433    pub date: Option<String>,
1434    #[serde(default)]
1435    pub model: Option<String>,
1436    #[serde(default)]
1437    pub provider_name: Option<String>,
1438    #[serde(default)]
1439    pub usage: Option<f64>,
1440    #[serde(default)]
1441    pub tokens_prompt: Option<i64>,
1442    #[serde(default)]
1443    pub tokens_completion: Option<i64>,
1444    #[serde(default)]
1445    pub requests: Option<i64>,
1446    #[serde(default, flatten)]
1447    pub extra: JsonObject,
1448}
1449
1450#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1451pub struct AnalyticsMetaResponse {
1452    #[serde(default)]
1453    pub data: Option<AnalyticsMetaData>,
1454    #[serde(default, flatten)]
1455    pub extra: JsonObject,
1456}
1457
1458#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1459pub struct AnalyticsMetaData {
1460    #[serde(default)]
1461    pub metrics: Option<Value>,
1462    #[serde(default)]
1463    pub dimensions: Option<Value>,
1464    #[serde(default)]
1465    pub operators: Option<Value>,
1466    #[serde(default)]
1467    pub granularities: Option<Value>,
1468    #[serde(default, flatten)]
1469    pub extra: JsonObject,
1470}
1471
1472#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1473pub struct AnalyticsQueryRequest {
1474    #[serde(skip_serializing_if = "Option::is_none")]
1475    pub metrics: Option<Vec<String>>,
1476    #[serde(skip_serializing_if = "Option::is_none")]
1477    pub dimensions: Option<Vec<String>>,
1478    #[serde(skip_serializing_if = "Option::is_none")]
1479    pub filters: Option<Value>,
1480    #[serde(skip_serializing_if = "Option::is_none")]
1481    pub granularity: Option<String>,
1482    #[serde(skip_serializing_if = "Option::is_none")]
1483    pub time_range: Option<Value>,
1484    #[serde(skip_serializing_if = "Option::is_none")]
1485    pub order_by: Option<Value>,
1486    #[serde(skip_serializing_if = "Option::is_none")]
1487    pub limit: Option<u32>,
1488    #[serde(skip_serializing_if = "Option::is_none")]
1489    pub group_limit: Option<u32>,
1490    #[serde(default, flatten)]
1491    pub extra: JsonObject,
1492}
1493
1494impl AnalyticsQueryRequest {
1495    pub fn new() -> Self {
1496        Self::default()
1497    }
1498
1499    pub fn with_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1500        self.extra.insert(key.into(), value.into());
1501        self
1502    }
1503}
1504
1505#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1506pub struct AnalyticsQueryResponse {
1507    #[serde(default)]
1508    pub data: Option<AnalyticsQueryData>,
1509    #[serde(default, flatten)]
1510    pub extra: JsonObject,
1511}
1512
1513#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1514pub struct AnalyticsQueryData {
1515    #[serde(default)]
1516    pub data: Option<Value>,
1517    #[serde(default)]
1518    pub metadata: Option<Value>,
1519    #[serde(default)]
1520    pub warnings: Option<Value>,
1521    #[serde(default, rename = "cachedAt")]
1522    pub cached_at: Option<String>,
1523    #[serde(default, flatten)]
1524    pub extra: JsonObject,
1525}
1526json_object_type!(AuthKeyExchangeRequest);
1527json_object_type!(AuthKeyExchangeResponse);
1528json_object_type!(AuthKeyCodeRequest);
1529json_object_type!(AuthKeyCodeResponse);
1530json_object_type!(BenchmarksResponse);
1531json_object_type!(TaskClassificationResponse);
1532#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1533pub struct CreditsResponse {
1534    #[serde(default)]
1535    pub data: Option<CreditsData>,
1536    #[serde(default, flatten)]
1537    pub extra: JsonObject,
1538}
1539
1540#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1541pub struct CreditsData {
1542    #[serde(default)]
1543    pub total_credits: Option<f64>,
1544    #[serde(default)]
1545    pub total_usage: Option<f64>,
1546    #[serde(default, flatten)]
1547    pub extra: JsonObject,
1548}
1549json_object_type!(AppRankingsResponse);
1550json_object_type!(RankingsDailyResponse);
1551json_object_type!(EndpointsZdrResponse);
1552pub type CurrentKeyResponse = DataResponse<KeyData>;
1553pub type KeyListResponse = ListResponse<KeyData>;
1554pub type KeyResponse = DataResponse<KeyData>;
1555pub type KeyDeleteResponse = DeleteResponse;
1556
1557#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1558pub struct KeyCreateRequest {
1559    #[serde(skip_serializing_if = "Option::is_none")]
1560    pub name: Option<String>,
1561    #[serde(skip_serializing_if = "Option::is_none")]
1562    pub limit: Option<f64>,
1563    #[serde(skip_serializing_if = "Option::is_none")]
1564    pub limit_reset: Option<String>,
1565    #[serde(skip_serializing_if = "Option::is_none")]
1566    pub include_byok_in_limit: Option<bool>,
1567    #[serde(skip_serializing_if = "Option::is_none")]
1568    pub expires_at: Option<String>,
1569    #[serde(skip_serializing_if = "Option::is_none")]
1570    pub creator_user_id: Option<String>,
1571    #[serde(skip_serializing_if = "Option::is_none")]
1572    pub workspace_id: Option<String>,
1573    #[serde(default, flatten)]
1574    pub extra: JsonObject,
1575}
1576
1577#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1578pub struct KeyUpdateRequest {
1579    #[serde(skip_serializing_if = "Option::is_none")]
1580    pub name: Option<String>,
1581    #[serde(skip_serializing_if = "Option::is_none")]
1582    pub limit: Option<f64>,
1583    #[serde(skip_serializing_if = "Option::is_none")]
1584    pub limit_reset: Option<String>,
1585    #[serde(skip_serializing_if = "Option::is_none")]
1586    pub include_byok_in_limit: Option<bool>,
1587    #[serde(skip_serializing_if = "Option::is_none")]
1588    pub expires_at: Option<String>,
1589    #[serde(skip_serializing_if = "Option::is_none")]
1590    pub disabled: Option<bool>,
1591    #[serde(default, flatten)]
1592    pub extra: JsonObject,
1593}
1594
1595#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1596pub struct KeyCreateResponse {
1597    #[serde(default)]
1598    pub data: Option<KeyData>,
1599    #[serde(default)]
1600    pub key: Option<String>,
1601    #[serde(default, flatten)]
1602    pub extra: JsonObject,
1603}
1604
1605pub type KeyUpdateResponse = DataResponse<KeyData>;
1606
1607#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1608pub struct KeyData {
1609    #[serde(default)]
1610    pub hash: Option<String>,
1611    #[serde(default)]
1612    pub name: Option<String>,
1613    #[serde(default)]
1614    pub label: Option<String>,
1615    #[serde(default)]
1616    pub disabled: Option<bool>,
1617    #[serde(default)]
1618    pub limit: Option<f64>,
1619    #[serde(default)]
1620    pub limit_reset: Option<String>,
1621    #[serde(default)]
1622    pub include_byok_in_limit: Option<bool>,
1623    #[serde(default)]
1624    pub usage: Option<f64>,
1625    #[serde(default)]
1626    pub usage_limit: Option<f64>,
1627    #[serde(default)]
1628    pub byok_usage: Option<f64>,
1629    #[serde(default)]
1630    pub byok_usage_limit: Option<f64>,
1631    #[serde(default)]
1632    pub created_at: Option<String>,
1633    #[serde(default)]
1634    pub updated_at: Option<String>,
1635    #[serde(default)]
1636    pub creator_user_id: Option<String>,
1637    #[serde(default)]
1638    pub workspace_id: Option<String>,
1639    #[serde(default)]
1640    pub expires_at: Option<String>,
1641    #[serde(default, flatten)]
1642    pub extra: JsonObject,
1643}
1644json_object_type!(ModelListResponse);
1645json_object_type!(ModelResponse);
1646json_object_type!(ModelEndpointsResponse);
1647json_object_type!(ModelCountResponse);
1648json_object_type!(UserModelsResponse);
1649json_object_type!(ProviderListResponse);
1650pub type ByokListResponse = ListResponse<ByokKey>;
1651pub type ByokCreateResponse = DataResponse<ByokKey>;
1652pub type ByokResponse = DataResponse<ByokKey>;
1653pub type ByokUpdateResponse = DataResponse<ByokKey>;
1654pub type ByokDeleteResponse = DeleteResponse;
1655
1656#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1657pub struct ByokCreateRequest {
1658    #[serde(skip_serializing_if = "Option::is_none")]
1659    pub provider: Option<String>,
1660    #[serde(skip_serializing_if = "Option::is_none")]
1661    pub key: Option<String>,
1662    #[serde(skip_serializing_if = "Option::is_none")]
1663    pub name: Option<String>,
1664    #[serde(skip_serializing_if = "Option::is_none")]
1665    pub workspace_id: Option<String>,
1666    #[serde(skip_serializing_if = "Option::is_none")]
1667    pub disabled: Option<bool>,
1668    #[serde(skip_serializing_if = "Option::is_none")]
1669    pub is_fallback: Option<bool>,
1670    #[serde(skip_serializing_if = "Option::is_none")]
1671    pub allowed_models: Option<Vec<String>>,
1672    #[serde(skip_serializing_if = "Option::is_none")]
1673    pub allowed_user_ids: Option<Vec<String>>,
1674    #[serde(default, flatten)]
1675    pub extra: JsonObject,
1676}
1677
1678pub type ByokUpdateRequest = ByokCreateRequest;
1679
1680#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1681pub struct ByokKey {
1682    #[serde(default)]
1683    pub id: Option<String>,
1684    #[serde(default)]
1685    pub provider: Option<String>,
1686    #[serde(default)]
1687    pub workspace_id: Option<String>,
1688    #[serde(default)]
1689    pub label: Option<String>,
1690    #[serde(default)]
1691    pub name: Option<String>,
1692    #[serde(default)]
1693    pub disabled: Option<bool>,
1694    #[serde(default)]
1695    pub is_fallback: Option<bool>,
1696    #[serde(default)]
1697    pub allowed_models: Option<Vec<String>>,
1698    #[serde(default)]
1699    pub allowed_api_key_hashes: Option<Vec<String>>,
1700    #[serde(default)]
1701    pub allowed_user_ids: Option<Vec<String>>,
1702    #[serde(default)]
1703    pub sort_order: Option<i64>,
1704    #[serde(default)]
1705    pub created_at: Option<String>,
1706    #[serde(default, flatten)]
1707    pub extra: JsonObject,
1708}
1709
1710pub type GuardrailListResponse = ListResponse<Guardrail>;
1711pub type GuardrailCreateResponse = DataResponse<Guardrail>;
1712pub type GuardrailResponse = DataResponse<Guardrail>;
1713pub type GuardrailUpdateResponse = DataResponse<Guardrail>;
1714pub type GuardrailDeleteResponse = DeleteResponse;
1715
1716#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1717pub struct GuardrailCreateRequest {
1718    #[serde(flatten)]
1719    pub fields: GuardrailFields,
1720}
1721
1722pub type GuardrailUpdateRequest = GuardrailCreateRequest;
1723
1724#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1725pub struct GuardrailFields {
1726    #[serde(skip_serializing_if = "Option::is_none")]
1727    pub name: Option<String>,
1728    #[serde(skip_serializing_if = "Option::is_none")]
1729    pub description: Option<String>,
1730    #[serde(skip_serializing_if = "Option::is_none")]
1731    pub workspace_id: Option<String>,
1732    #[serde(skip_serializing_if = "Option::is_none")]
1733    pub limit_usd: Option<f64>,
1734    #[serde(skip_serializing_if = "Option::is_none")]
1735    pub reset_interval: Option<String>,
1736    #[serde(skip_serializing_if = "Option::is_none")]
1737    pub allowed_models: Option<Vec<String>>,
1738    #[serde(skip_serializing_if = "Option::is_none")]
1739    pub ignored_models: Option<Vec<String>>,
1740    #[serde(skip_serializing_if = "Option::is_none")]
1741    pub allowed_providers: Option<Vec<String>>,
1742    #[serde(skip_serializing_if = "Option::is_none")]
1743    pub ignored_providers: Option<Vec<String>>,
1744    #[serde(skip_serializing_if = "Option::is_none")]
1745    pub content_filters: Option<Value>,
1746    #[serde(skip_serializing_if = "Option::is_none")]
1747    pub content_filter_builtins: Option<Value>,
1748    #[serde(skip_serializing_if = "Option::is_none")]
1749    pub enforce_zdr: Option<bool>,
1750    #[serde(skip_serializing_if = "Option::is_none")]
1751    pub enforce_zdr_models: Option<Vec<String>>,
1752    #[serde(default, flatten)]
1753    pub extra: JsonObject,
1754}
1755
1756#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1757pub struct Guardrail {
1758    #[serde(default)]
1759    pub id: Option<String>,
1760    #[serde(flatten)]
1761    pub fields: GuardrailFields,
1762    #[serde(default)]
1763    pub created_at: Option<String>,
1764    #[serde(default)]
1765    pub updated_at: Option<String>,
1766}
1767
1768pub type KeyAssignmentsResponse = ListResponse<KeyAssignment>;
1769pub type MemberAssignmentsResponse = ListResponse<MemberAssignment>;
1770
1771#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1772pub struct KeyAssignment {
1773    #[serde(default)]
1774    pub key_hash: Option<String>,
1775    #[serde(default)]
1776    pub guardrail_id: Option<String>,
1777    #[serde(default, flatten)]
1778    pub extra: JsonObject,
1779}
1780
1781#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1782pub struct MemberAssignment {
1783    #[serde(default)]
1784    pub member_user_id: Option<String>,
1785    #[serde(default)]
1786    pub guardrail_id: Option<String>,
1787    #[serde(default, flatten)]
1788    pub extra: JsonObject,
1789}
1790
1791#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1792pub struct BulkAssignKeysRequest {
1793    #[serde(default)]
1794    pub key_hashes: Vec<String>,
1795    #[serde(default, flatten)]
1796    pub extra: JsonObject,
1797}
1798
1799pub type BulkUnassignKeysRequest = BulkAssignKeysRequest;
1800
1801#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1802pub struct BulkAssignMembersRequest {
1803    #[serde(default)]
1804    pub member_user_ids: Vec<String>,
1805    #[serde(default, flatten)]
1806    pub extra: JsonObject,
1807}
1808
1809pub type BulkUnassignMembersRequest = BulkAssignMembersRequest;
1810
1811#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1812pub struct AssignmentCountResponse {
1813    #[serde(default)]
1814    pub assigned_count: Option<i64>,
1815    #[serde(default)]
1816    pub unassigned_count: Option<i64>,
1817    #[serde(default, flatten)]
1818    pub extra: JsonObject,
1819}
1820
1821pub type BulkAssignKeysResponse = AssignmentCountResponse;
1822pub type BulkUnassignKeysResponse = AssignmentCountResponse;
1823pub type BulkAssignMembersResponse = AssignmentCountResponse;
1824pub type BulkUnassignMembersResponse = AssignmentCountResponse;
1825
1826pub type ObservabilityDestinationListResponse = ListResponse<ObservabilityDestination>;
1827pub type ObservabilityDestinationCreateResponse = DataResponse<ObservabilityDestination>;
1828pub type ObservabilityDestinationResponse = DataResponse<ObservabilityDestination>;
1829pub type ObservabilityDestinationUpdateResponse = DataResponse<ObservabilityDestination>;
1830pub type ObservabilityDestinationDeleteResponse = DeleteResponse;
1831
1832#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1833pub struct ObservabilityDestinationCreateRequest {
1834    #[serde(flatten)]
1835    pub fields: ObservabilityDestinationFields,
1836}
1837
1838pub type ObservabilityDestinationUpdateRequest = ObservabilityDestinationCreateRequest;
1839
1840#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1841pub struct ObservabilityDestinationFields {
1842    #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
1843    pub type_: Option<String>,
1844    #[serde(skip_serializing_if = "Option::is_none")]
1845    pub name: Option<String>,
1846    #[serde(skip_serializing_if = "Option::is_none")]
1847    pub config: Option<Value>,
1848    #[serde(skip_serializing_if = "Option::is_none")]
1849    pub workspace_id: Option<String>,
1850    #[serde(skip_serializing_if = "Option::is_none")]
1851    pub enabled: Option<bool>,
1852    #[serde(skip_serializing_if = "Option::is_none")]
1853    pub privacy_mode: Option<String>,
1854    #[serde(skip_serializing_if = "Option::is_none")]
1855    pub sampling_rate: Option<f64>,
1856    #[serde(skip_serializing_if = "Option::is_none")]
1857    pub api_key_hashes: Option<Vec<String>>,
1858    #[serde(skip_serializing_if = "Option::is_none")]
1859    pub filter_rules: Option<Value>,
1860    #[serde(default, flatten)]
1861    pub extra: JsonObject,
1862}
1863
1864#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1865pub struct ObservabilityDestination {
1866    #[serde(default)]
1867    pub id: Option<String>,
1868    #[serde(flatten)]
1869    pub fields: ObservabilityDestinationFields,
1870    #[serde(default)]
1871    pub created_at: Option<String>,
1872    #[serde(default)]
1873    pub updated_at: Option<String>,
1874}
1875
1876pub type OrganizationMembersResponse = ListResponse<OrganizationMember>;
1877
1878#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1879pub struct OrganizationMember {
1880    #[serde(default)]
1881    pub id: Option<String>,
1882    #[serde(default)]
1883    pub first_name: Option<String>,
1884    #[serde(default)]
1885    pub last_name: Option<String>,
1886    #[serde(default)]
1887    pub email: Option<String>,
1888    #[serde(default)]
1889    pub role: Option<String>,
1890    #[serde(default, flatten)]
1891    pub extra: JsonObject,
1892}
1893json_object_type!(PresetListResponse);
1894json_object_type!(PresetResponse);
1895json_object_type!(PresetCreateFromInferenceResponse);
1896json_object_type!(PresetVersionListResponse);
1897json_object_type!(PresetVersionResponse);
1898pub type WorkspaceListResponse = ListResponse<Workspace>;
1899pub type WorkspaceCreateResponse = DataResponse<Workspace>;
1900pub type WorkspaceResponse = DataResponse<Workspace>;
1901pub type WorkspaceUpdateResponse = DataResponse<Workspace>;
1902pub type WorkspaceDeleteResponse = DeleteResponse;
1903
1904#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1905pub struct WorkspaceCreateRequest {
1906    #[serde(flatten)]
1907    pub fields: WorkspaceFields,
1908}
1909
1910pub type WorkspaceUpdateRequest = WorkspaceCreateRequest;
1911
1912#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1913pub struct WorkspaceFields {
1914    #[serde(skip_serializing_if = "Option::is_none")]
1915    pub name: Option<String>,
1916    #[serde(skip_serializing_if = "Option::is_none")]
1917    pub slug: Option<String>,
1918    #[serde(skip_serializing_if = "Option::is_none")]
1919    pub description: Option<String>,
1920    #[serde(skip_serializing_if = "Option::is_none")]
1921    pub default_text_model: Option<String>,
1922    #[serde(skip_serializing_if = "Option::is_none")]
1923    pub default_image_model: Option<String>,
1924    #[serde(skip_serializing_if = "Option::is_none")]
1925    pub default_provider_sort: Option<Value>,
1926    #[serde(skip_serializing_if = "Option::is_none")]
1927    pub is_logging_enabled: Option<bool>,
1928    #[serde(skip_serializing_if = "Option::is_none")]
1929    pub is_observability_enabled: Option<bool>,
1930    #[serde(skip_serializing_if = "Option::is_none")]
1931    pub io_logging_enabled: Option<bool>,
1932    #[serde(skip_serializing_if = "Option::is_none")]
1933    pub io_logging_sampling_rate: Option<f64>,
1934    #[serde(skip_serializing_if = "Option::is_none")]
1935    pub io_logging_api_key_ids: Option<Vec<String>>,
1936    #[serde(default, flatten)]
1937    pub extra: JsonObject,
1938}
1939
1940#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1941pub struct Workspace {
1942    #[serde(default)]
1943    pub id: Option<String>,
1944    #[serde(flatten)]
1945    pub fields: WorkspaceFields,
1946    #[serde(default)]
1947    pub created_at: Option<String>,
1948    #[serde(default)]
1949    pub updated_at: Option<String>,
1950    #[serde(default)]
1951    pub created_by: Option<String>,
1952}
1953
1954pub type WorkspaceBudgetListResponse = ListResponse<WorkspaceBudget>;
1955pub type WorkspaceBudgetUpsertResponse = DataResponse<WorkspaceBudget>;
1956pub type WorkspaceBudgetDeleteResponse = DeleteResponse;
1957
1958#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1959pub struct WorkspaceBudgetUpsertRequest {
1960    #[serde(skip_serializing_if = "Option::is_none")]
1961    pub limit_usd: Option<f64>,
1962    #[serde(default, flatten)]
1963    pub extra: JsonObject,
1964}
1965
1966#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1967pub struct WorkspaceBudget {
1968    #[serde(default)]
1969    pub id: Option<String>,
1970    #[serde(default)]
1971    pub workspace_id: Option<String>,
1972    #[serde(default)]
1973    pub limit_usd: Option<f64>,
1974    #[serde(default)]
1975    pub reset_interval: Option<String>,
1976    #[serde(default)]
1977    pub created_at: Option<String>,
1978    #[serde(default)]
1979    pub updated_at: Option<String>,
1980    #[serde(default, flatten)]
1981    pub extra: JsonObject,
1982}
1983
1984pub type WorkspaceMemberListResponse = ListResponse<WorkspaceMember>;
1985
1986#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1987pub struct WorkspaceMember {
1988    #[serde(default)]
1989    pub id: Option<String>,
1990    #[serde(default)]
1991    pub workspace_id: Option<String>,
1992    #[serde(default)]
1993    pub user_id: Option<String>,
1994    #[serde(default)]
1995    pub role: Option<String>,
1996    #[serde(default)]
1997    pub created_at: Option<String>,
1998    #[serde(default, flatten)]
1999    pub extra: JsonObject,
2000}
2001
2002#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
2003pub struct BulkAddWorkspaceMembersRequest {
2004    #[serde(default)]
2005    pub user_ids: Vec<String>,
2006    #[serde(default, flatten)]
2007    pub extra: JsonObject,
2008}
2009
2010pub type BulkRemoveWorkspaceMembersRequest = BulkAddWorkspaceMembersRequest;
2011
2012#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
2013pub struct BulkAddWorkspaceMembersResponse {
2014    #[serde(default)]
2015    pub data: Vec<WorkspaceMember>,
2016    #[serde(default)]
2017    pub added_count: Option<i64>,
2018    #[serde(default, flatten)]
2019    pub extra: JsonObject,
2020}
2021
2022#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
2023pub struct BulkRemoveWorkspaceMembersResponse {
2024    #[serde(default)]
2025    pub removed_count: Option<i64>,
2026    #[serde(default, flatten)]
2027    pub extra: JsonObject,
2028}
2029
2030#[cfg(test)]
2031mod tests {
2032    use super::{
2033        ChatCompletionRequest, ChatMessage, ChatRole, EmbeddingsRequest, ImageGenerationRequest,
2034        MessagesRequest, ProviderPreferences, RerankDocument, RerankRequest, ResponsesRequest,
2035        TranscriptionRequest, VideoGenerationRequest,
2036    };
2037    use serde_json::json;
2038
2039    #[test]
2040    fn unknown_enums_round_trip() {
2041        let role: ChatRole = serde_json::from_str("\"new_role\"").unwrap();
2042        assert_eq!(role.as_str(), "new_role");
2043        assert_eq!(serde_json::to_string(&role).unwrap(), "\"new_role\"");
2044    }
2045
2046    #[test]
2047    fn provider_preferences_serialize_stable_and_extra_fields() {
2048        let prefs = ProviderPreferences::new()
2049            .require_parameters(true)
2050            .only(["openai"])
2051            .with_field("custom", true);
2052        let value = serde_json::to_value(prefs).unwrap();
2053        assert_eq!(value["require_parameters"], true);
2054        assert_eq!(value["only"][0], "openai");
2055        assert_eq!(value["custom"], true);
2056    }
2057
2058    #[test]
2059    fn multi_model_request_constructors_serialize_models_without_model() {
2060        let chat = ChatCompletionRequest::new_with_models(
2061            ["openai/gpt-4o-mini", "anthropic/claude-haiku"],
2062            vec![ChatMessage::user("hi")],
2063        );
2064        let chat = serde_json::to_value(chat).unwrap();
2065        assert!(chat.get("model").is_none());
2066        assert_eq!(chat["models"][0], "openai/gpt-4o-mini");
2067
2068        let responses = ResponsesRequest::new_with_models(
2069            ["openai/gpt-4o-mini", "anthropic/claude-haiku"],
2070            "hi",
2071        );
2072        let responses = serde_json::to_value(responses).unwrap();
2073        assert!(responses.get("model").is_none());
2074        assert_eq!(responses["models"][1], "anthropic/claude-haiku");
2075
2076        let messages = MessagesRequest::new_with_models(
2077            ["anthropic/claude-sonnet", "anthropic/claude-haiku"],
2078            vec![ChatMessage::user("hi")],
2079        );
2080        let messages = serde_json::to_value(messages).unwrap();
2081        assert!(messages.get("model").is_none());
2082        assert_eq!(messages["models"][0], "anthropic/claude-sonnet");
2083    }
2084
2085    #[test]
2086    fn expanded_request_fields_serialize_with_openrouter_wire_names() {
2087        let mut chat =
2088            ChatCompletionRequest::new("openai/gpt-4o-mini", vec![ChatMessage::user("hello")]);
2089        chat.cache_control = Some(json!({"type": "ephemeral"}));
2090        chat.modalities = Some(vec!["text".to_owned(), "image".to_owned()]);
2091        chat.stop_server_tools_when = Some(json!("first_tool_call"));
2092        chat.top_a = Some(0.4);
2093        chat.trace = Some(json!({"enabled": true}));
2094        let chat = serde_json::to_value(chat).unwrap();
2095        assert_eq!(chat["cache_control"]["type"], "ephemeral");
2096        assert_eq!(chat["modalities"][1], "image");
2097        assert_eq!(chat["stop_server_tools_when"], "first_tool_call");
2098        assert_eq!(chat["top_a"], 0.4);
2099        assert_eq!(chat["trace"]["enabled"], true);
2100
2101        let mut responses = ResponsesRequest::new("openai/gpt-4o-mini", "hello");
2102        responses.background = Some(true);
2103        responses.max_tool_calls = Some(3);
2104        responses.prompt_cache_key = Some("cache-key".to_owned());
2105        responses.safety_identifier = Some("user-1".to_owned());
2106        responses.truncation = Some("auto".to_owned());
2107        let responses = serde_json::to_value(responses).unwrap();
2108        assert_eq!(responses["background"], true);
2109        assert_eq!(responses["max_tool_calls"], 3);
2110        assert_eq!(responses["prompt_cache_key"], "cache-key");
2111        assert_eq!(responses["safety_identifier"], "user-1");
2112        assert_eq!(responses["truncation"], "auto");
2113
2114        let mut messages =
2115            MessagesRequest::new("anthropic/claude-haiku", vec![ChatMessage::user("hello")]);
2116        messages.context_management = Some(json!({"clear_function_results": true}));
2117        messages.stop_sequences = Some(vec!["END".to_owned()]);
2118        messages.speed = Some(1.1);
2119        messages.top_p = Some(0.9);
2120        let messages = serde_json::to_value(messages).unwrap();
2121        assert_eq!(
2122            messages["context_management"]["clear_function_results"],
2123            true
2124        );
2125        assert_eq!(messages["stop_sequences"][0], "END");
2126        assert_eq!(messages["speed"], 1.1);
2127        assert_eq!(messages["top_p"], 0.9);
2128
2129        let mut image = ImageGenerationRequest::new("openai/gpt-image-1").prompt("a chair");
2130        image.aspect_ratio = Some("1:1".to_owned());
2131        image.n = Some(2);
2132        image.output_format = Some("png".to_owned());
2133        image.stream = Some(true);
2134        let image = serde_json::to_value(image).unwrap();
2135        assert_eq!(image["aspect_ratio"], "1:1");
2136        assert_eq!(image["n"], 2);
2137        assert_eq!(image["output_format"], "png");
2138        assert_eq!(image["stream"], true);
2139
2140        let mut video = VideoGenerationRequest::new("provider/video").prompt("waves");
2141        video.callback_url = Some("https://example.test/callback".to_owned());
2142        video.generate_audio = Some(false);
2143        video.frame_images = Some(json!(["image-1"]));
2144        let video = serde_json::to_value(video).unwrap();
2145        assert_eq!(video["callback_url"], "https://example.test/callback");
2146        assert_eq!(video["generate_audio"], false);
2147        assert_eq!(video["frame_images"][0], "image-1");
2148
2149        let mut embeddings = EmbeddingsRequest::new("openai/text-embedding-3-small", "hello");
2150        embeddings.dimensions = Some(128);
2151        embeddings.encoding_format = Some("float".to_owned());
2152        embeddings.input_type = Some("query".to_owned());
2153        let embeddings = serde_json::to_value(embeddings).unwrap();
2154        assert_eq!(embeddings["dimensions"], 128);
2155        assert_eq!(embeddings["encoding_format"], "float");
2156        assert_eq!(embeddings["input_type"], "query");
2157
2158        let mut rerank = RerankRequest::new(
2159            "cohere/rerank",
2160            "query",
2161            [
2162                RerankDocument::Text("plain".to_owned()),
2163                RerankDocument::Object(json!({"text": "rich"})),
2164            ],
2165        );
2166        rerank.top_n = Some(1);
2167        let rerank = serde_json::to_value(rerank).unwrap();
2168        assert_eq!(rerank["documents"][0], "plain");
2169        assert_eq!(rerank["documents"][1]["text"], "rich");
2170        assert_eq!(rerank["top_n"], 1);
2171
2172        let mut transcription =
2173            TranscriptionRequest::new("openai/whisper-1", json!({"data": "base64"}));
2174        transcription.temperature = Some(0.2);
2175        let transcription = serde_json::to_value(transcription).unwrap();
2176        assert_eq!(transcription["temperature"], 0.2);
2177        assert_eq!(transcription["input_audio"]["data"], "base64");
2178    }
2179}