rainy_sdk/
models.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5/// Represents a single message in a chat conversation.
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7pub struct ChatMessage {
8    /// The role of the message author.
9    pub role: MessageRole,
10    /// The content of the message.
11    pub content: String,
12}
13
14/// The role of a message's author.
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
16#[serde(rename_all = "lowercase")]
17pub enum MessageRole {
18    /// A message from the system, setting the context or instructions for the assistant.
19    System,
20    /// A message from the user.
21    User,
22    /// A message from the assistant.
23    Assistant,
24}
25
26/// Represents a request to create a chat completion.
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ChatCompletionRequest {
29    /// The identifier of the model to use for the completion (e.g., "gpt-4o", "claude-sonnet-4").
30    pub model: String,
31
32    /// A list of messages that form the conversation history.
33    pub messages: Vec<ChatMessage>,
34
35    /// The sampling temperature to use, between 0.0 and 2.0. Higher values will make the output
36    /// more random, while lower values will make it more focused and deterministic.
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub temperature: Option<f32>,
39
40    /// The maximum number of tokens to generate in the completion.
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub max_tokens: Option<u32>,
43
44    /// The nucleus sampling parameter. The model considers the results of the tokens with `top_p`
45    /// probability mass. So, 0.1 means only the tokens comprising the top 10% probability mass are considered.
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub top_p: Option<f32>,
48
49    /// A penalty applied to new tokens based on their frequency in the text so far.
50    /// It decreases the model's likelihood to repeat the same line verbatim.
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub frequency_penalty: Option<f32>,
53
54    /// A penalty applied to new tokens based on whether they appear in the text so far.
55    /// It increases the model's likelihood to talk about new topics.
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub presence_penalty: Option<f32>,
58
59    /// A list of sequences that will cause the model to stop generating further tokens.
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub stop: Option<Vec<String>>,
62
63    /// A unique identifier representing your end-user, which can help in monitoring and
64    /// tracking conversations.
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub user: Option<String>,
67
68    /// A hint to the router about which provider to use for the model.
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub provider: Option<String>,
71
72    /// If set to `true`, the response will be streamed as a series of events.
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub stream: Option<bool>,
75
76    /// Modify the likelihood of specified tokens appearing in the completion.
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub logit_bias: Option<serde_json::Value>,
79
80    /// Whether to return log probabilities of the output tokens.
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub logprobs: Option<bool>,
83
84    /// An integer between 0 and 20 specifying the number of most likely tokens to return at each token position.
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub top_logprobs: Option<u32>,
87
88    /// How many chat completion choices to generate for each input message.
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub n: Option<u32>,
91
92    /// An object specifying the format that the model must output.
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub response_format: Option<ResponseFormat>,
95
96    /// A list of tools the model may call.
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub tools: Option<Vec<Tool>>,
99
100    /// Controls which (if any) tool is called by the model.
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub tool_choice: Option<ToolChoice>,
103}
104
105/// Represents the response from a chat completion request.
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct ChatCompletionResponse {
108    /// A unique identifier for the chat completion.
109    pub id: String,
110
111    /// The type of object, which is always "chat.completion".
112    pub object: String,
113
114    /// The Unix timestamp (in seconds) of when the completion was created.
115    pub created: u64,
116
117    /// The model that was used for the completion.
118    pub model: String,
119
120    /// A list of chat completion choices.
121    pub choices: Vec<ChatChoice>,
122
123    /// Information about the token usage for this completion.
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub usage: Option<Usage>,
126}
127
128/// Represents a single choice in a chat completion response.
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct ChatChoice {
131    /// The index of the choice in the list of choices.
132    pub index: u32,
133
134    /// The message generated by the model.
135    pub message: ChatMessage,
136
137    /// The reason the model stopped generating tokens.
138    pub finish_reason: String,
139}
140
141/// Represents the token usage statistics for a chat completion.
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct Usage {
144    /// The number of tokens in the prompt.
145    pub prompt_tokens: u32,
146
147    /// The number of tokens in the generated completion.
148    pub completion_tokens: u32,
149
150    /// The total number of tokens used in the request (prompt + completion).
151    pub total_tokens: u32,
152}
153
154/// Represents the health status of the Rainy API.
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct HealthStatus {
157    /// The overall status of the API (e.g., "healthy", "degraded").
158    pub status: String,
159
160    /// The timestamp of when the health check was performed.
161    pub timestamp: String,
162
163    /// The uptime of the system in seconds.
164    pub uptime: f64,
165
166    /// The status of individual services.
167    pub services: ServiceStatus,
168}
169
170/// Represents the status of individual backend services.
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct ServiceStatus {
173    /// The status of the database connection.
174    pub database: bool,
175
176    /// The status of the Redis connection, if applicable.
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub redis: Option<bool>,
179
180    /// The overall status of the connections to AI providers.
181    pub providers: bool,
182}
183
184/// Represents the available models and providers.
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct AvailableModels {
187    /// A map where keys are provider names and values are lists of model names.
188    pub providers: HashMap<String, Vec<String>>,
189
190    /// The total number of available models across all providers.
191    pub total_models: usize,
192
193    /// A list of provider names that are currently active and available.
194    pub active_providers: Vec<String>,
195}
196
197/// Represents information about credit usage for a request.
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct CreditInfo {
200    /// The number of credits available before the request.
201    pub current_credits: f64,
202
203    /// The estimated number of credits that the request will cost.
204    pub estimated_cost: f64,
205
206    /// The estimated number of credits remaining after the request.
207    pub credits_after_request: f64,
208
209    /// The date when the credit balance is next scheduled to be reset.
210    pub reset_date: String,
211}
212
213/// Represents metadata extracted from the response headers of an API request.
214#[derive(Debug, Clone)]
215pub struct RequestMetadata {
216    /// The time taken for the request to complete, in milliseconds.
217    pub response_time: Option<u64>,
218
219    /// The AI provider that handled the request.
220    pub provider: Option<String>,
221
222    /// The number of tokens used in the request.
223    pub tokens_used: Option<u32>,
224
225    /// The number of credits used for the request.
226    pub credits_used: Option<f64>,
227
228    /// The number of credits remaining after the request.
229    pub credits_remaining: Option<f64>,
230
231    /// The unique ID of the request, for tracking and debugging.
232    pub request_id: Option<String>,
233}
234
235/// A collection of predefined model constants for convenience.
236/// All models listed here are confirmed to be 100% OpenAI-compatible without parameter adaptations.
237pub mod model_constants {
238    // OpenAI models (fully compatible)
239    /// Constant for the GPT-4o model.
240    pub const OPENAI_GPT_4O: &str = "gpt-4o";
241    /// Constant for the GPT-5 model.
242    pub const OPENAI_GPT_5: &str = "gpt-5";
243    /// Constant for the GPT-5 Pro model.
244    pub const OPENAI_GPT_5_PRO: &str = "gpt-5-pro";
245    /// Constant for the O3 model.
246    pub const OPENAI_O3: &str = "o3";
247    /// Constant for the O4 Mini model.
248    pub const OPENAI_O4_MINI: &str = "o4-mini";
249
250    // Google Gemini models (fully compatible via official compatibility layer)
251    /// Constant for the Gemini 2.5 Pro model.
252    pub const GOOGLE_GEMINI_2_5_PRO: &str = "gemini-2.5-pro";
253    /// Constant for the Gemini 2.5 Flash model.
254    pub const GOOGLE_GEMINI_2_5_FLASH: &str = "gemini-2.5-flash";
255    /// Constant for the Gemini 2.5 Flash Lite model.
256    pub const GOOGLE_GEMINI_2_5_FLASH_LITE: &str = "gemini-2.5-flash-lite";
257
258    // Groq models (fully compatible)
259    /// Constant for the Llama 3.1 8B Instant model.
260    pub const GROQ_LLAMA_3_1_8B_INSTANT: &str = "llama-3.1-8b-instant";
261    /// Constant for the Llama 3.3 70B Versatile model.
262    pub const GROQ_LLAMA_3_3_70B_VERSATILE: &str = "llama-3.3-70b-versatile";
263    /// Constant for the moonshotai/kimi-k2-instruct-0905 Instant model.
264    pub const KIMI_K2_0925: &str = "moonshotai/kimi-k2-instruct-0905";
265
266    // Cerebras models (fully compatible)
267    /// Constant for the Llama3.1 8B model.
268    pub const CEREBRAS_LLAMA3_1_8B: &str = "cerebras/llama3.1-8b";
269
270    // Enosis Labs models (fully compatible)
271    /// Constant for the Astronomer 1 model.
272    pub const ASTRONOMER_1: &str = "astronomer-1";
273    /// Constant for the Astronomer 1 Max model.
274    pub const ASTRONOMER_1_MAX: &str = "astronomer-1-max";
275    /// Constant for the Astronomer 1.5 model.
276    pub const ASTRONOMER_1_5: &str = "astronomer-1.5";
277    /// Constant for the Astronomer 2 model.
278    pub const ASTRONOMER_2: &str = "astronomer-2";
279    /// Constant for the Astronomer 2 Pro model.
280    pub const ASTRONOMER_2_PRO: &str = "astronomer-2-pro";
281
282    // Legacy aliases for backward compatibility (deprecated - use provider-prefixed versions above)
283    /// Legacy constant for the GPT-4o model (use OPENAI_GPT_4O instead).
284    #[deprecated(note = "Use OPENAI_GPT_4O instead for OpenAI compatibility")]
285    pub const GPT_4O: &str = "openai/gpt-4o";
286    /// Legacy constant for the GPT-5 model (use OPENAI_GPT_5 instead).
287    #[deprecated(note = "Use OPENAI_GPT_5 instead for OpenAI compatibility")]
288    pub const GPT_5: &str = "openai/gpt-5";
289    /// Legacy constant for the Gemini 2.5 Pro model (use GOOGLE_GEMINI_2_5_PRO instead).
290    #[deprecated(note = "Use GOOGLE_GEMINI_2_5_PRO instead for OpenAI compatibility")]
291    pub const GEMINI_2_5_PRO: &str = "google/gemini-2.5-pro";
292    /// Legacy constant for the Gemini 2.5 Flash model (use GOOGLE_GEMINI_2_5_FLASH instead).
293    #[deprecated(note = "Use GOOGLE_GEMINI_2_5_FLASH instead for OpenAI compatibility")]
294    pub const GEMINI_2_5_FLASH: &str = "google/gemini-2.5-flash";
295    /// Legacy constant for the Gemini 2.5 Flash Lite model (use GOOGLE_GEMINI_2_5_FLASH_LITE instead).
296    #[deprecated(note = "Use GOOGLE_GEMINI_2_5_FLASH_LITE instead for OpenAI compatibility")]
297    pub const GEMINI_2_5_FLASH_LITE: &str = "google/gemini-2.5-flash-lite";
298    /// Legacy constant for the Llama 3.1 8B Instant model (use GROQ_LLAMA_3_1_8B_INSTANT instead).
299    #[deprecated(note = "Use GROQ_LLAMA_3_1_8B_INSTANT instead for OpenAI compatibility")]
300    pub const LLAMA_3_1_8B_INSTANT: &str = "groq/llama-3.1-8b-instant";
301    /// Legacy constant for the Llama3.1 8B model (use CEREBRAS_LLAMA3_1_8B instead).
302    #[deprecated(note = "Use CEREBRAS_LLAMA3_1_8B instead for OpenAI compatibility")]
303    pub const LLAMA3_1_8B: &str = "cerebras/llama3.1-8b";
304}
305
306/// A collection of predefined provider name constants for convenience.
307pub mod providers {
308    /// Constant for the OpenAI provider.
309    pub const OPENAI: &str = "openai";
310    /// Constant for the Anthropic provider.
311    pub const ANTHROPIC: &str = "anthropic";
312    /// Constant for the Groq provider.
313    pub const GROQ: &str = "groq";
314    /// Constant for the Cerebras provider.
315    pub const CEREBRAS: &str = "cerebras";
316    /// Constant for the Gemini provider.
317    pub const GEMINI: &str = "gemini";
318    /// Constant for the Enosis Labs provider.
319    pub const ENOSISLABS: &str = "enosislabs";
320}
321
322impl ChatCompletionRequest {
323    /// Creates a new `ChatCompletionRequest` with the given model and messages.
324    ///
325    /// # Arguments
326    ///
327    /// * `model` - The identifier of the model to use.
328    /// * `messages` - The list of messages for the conversation.
329    pub fn new(model: impl Into<String>, messages: Vec<ChatMessage>) -> Self {
330        Self {
331            model: model.into(),
332            messages,
333            temperature: None,
334            max_tokens: None,
335            top_p: None,
336            frequency_penalty: None,
337            presence_penalty: None,
338            stop: None,
339            user: None,
340            provider: None,
341            stream: None,
342            logit_bias: None,
343            logprobs: None,
344            top_logprobs: None,
345            n: None,
346            response_format: None,
347            tools: None,
348            tool_choice: None,
349        }
350    }
351
352    /// Sets the temperature for the chat completion.
353    ///
354    /// The temperature is clamped between 0.0 and 2.0.
355    ///
356    /// # Arguments
357    ///
358    /// * `temperature` - The sampling temperature.
359    pub fn with_temperature(mut self, temperature: f32) -> Self {
360        self.temperature = Some(temperature.clamp(0.0, 2.0));
361        self
362    }
363
364    /// Sets the maximum number of tokens to generate.
365    ///
366    /// # Arguments
367    ///
368    /// * `max_tokens` - The maximum number of tokens.
369    pub fn with_max_tokens(mut self, max_tokens: u32) -> Self {
370        self.max_tokens = Some(max_tokens);
371        self
372    }
373
374    /// Sets the user identifier for the chat completion.
375    ///
376    /// # Arguments
377    ///
378    /// * `user` - A unique identifier for the end-user.
379    pub fn with_user(mut self, user: impl Into<String>) -> Self {
380        self.user = Some(user.into());
381        self
382    }
383
384    /// Sets a provider hint for the request.
385    ///
386    /// # Arguments
387    ///
388    /// * `provider` - The name of the provider to use.
389    pub fn with_provider(mut self, provider: impl Into<String>) -> Self {
390        self.provider = Some(provider.into());
391        self
392    }
393
394    /// Enables or disables streaming for the response.
395    ///
396    /// # Arguments
397    ///
398    /// * `stream` - `true` to enable streaming, `false` to disable.
399    pub fn with_stream(mut self, stream: bool) -> Self {
400        self.stream = Some(stream);
401        self
402    }
403
404    /// Sets the logit bias for the chat completion.
405    ///
406    /// # Arguments
407    ///
408    /// * `logit_bias` - A map of token IDs to bias values.
409    pub fn with_logit_bias(mut self, logit_bias: serde_json::Value) -> Self {
410        self.logit_bias = Some(logit_bias);
411        self
412    }
413
414    /// Enables or disables log probabilities for the response.
415    ///
416    /// # Arguments
417    ///
418    /// * `logprobs` - `true` to include log probabilities.
419    pub fn with_logprobs(mut self, logprobs: bool) -> Self {
420        self.logprobs = Some(logprobs);
421        self
422    }
423
424    /// Sets the number of most likely tokens to return at each position.
425    ///
426    /// # Arguments
427    ///
428    /// * `top_logprobs` - The number of top log probabilities to return.
429    pub fn with_top_logprobs(mut self, top_logprobs: u32) -> Self {
430        self.top_logprobs = Some(top_logprobs);
431        self
432    }
433
434    /// Sets the number of chat completion choices to generate.
435    ///
436    /// # Arguments
437    ///
438    /// * `n` - The number of completions to generate.
439    pub fn with_n(mut self, n: u32) -> Self {
440        self.n = Some(n);
441        self
442    }
443
444    /// Sets the response format for the chat completion.
445    ///
446    /// # Arguments
447    ///
448    /// * `response_format` - The format the model must output.
449    pub fn with_response_format(mut self, response_format: ResponseFormat) -> Self {
450        self.response_format = Some(response_format);
451        self
452    }
453
454    /// Sets the tools available to the model.
455    ///
456    /// # Arguments
457    ///
458    /// * `tools` - A list of tools the model can use.
459    pub fn with_tools(mut self, tools: Vec<Tool>) -> Self {
460        self.tools = Some(tools);
461        self
462    }
463
464    /// Sets the tool choice for the chat completion.
465    ///
466    /// # Arguments
467    ///
468    /// * `tool_choice` - Controls which tool the model uses.
469    pub fn with_tool_choice(mut self, tool_choice: ToolChoice) -> Self {
470        self.tool_choice = Some(tool_choice);
471        self
472    }
473
474    /// Validates that the request parameters are compatible with OpenAI standards.
475    ///
476    /// This method checks parameter ranges and values to ensure they match OpenAI's API specifications.
477    ///
478    /// # Returns
479    ///
480    /// A `Result` indicating whether the request is valid for OpenAI compatibility.
481    pub fn validate_openai_compatibility(&self) -> Result<(), String> {
482        // Validate temperature
483        if let Some(temp) = self.temperature {
484            if !(0.0..=2.0).contains(&temp) {
485                return Err(format!(
486                    "Temperature must be between 0.0 and 2.0, got {}",
487                    temp
488                ));
489            }
490        }
491
492        // Validate top_p
493        if let Some(top_p) = self.top_p {
494            if !(0.0..=1.0).contains(&top_p) {
495                return Err(format!("Top-p must be between 0.0 and 1.0, got {}", top_p));
496            }
497        }
498
499        // Validate frequency_penalty
500        if let Some(fp) = self.frequency_penalty {
501            if !(-2.0..=2.0).contains(&fp) {
502                return Err(format!(
503                    "Frequency penalty must be between -2.0 and 2.0, got {}",
504                    fp
505                ));
506            }
507        }
508
509        // Validate presence_penalty
510        if let Some(pp) = self.presence_penalty {
511            if !(-2.0..=2.0).contains(&pp) {
512                return Err(format!(
513                    "Presence penalty must be between -2.0 and 2.0, got {}",
514                    pp
515                ));
516            }
517        }
518
519        // Validate max_tokens
520        if let Some(mt) = self.max_tokens {
521            if mt == 0 {
522                return Err("Max tokens must be greater than 0".to_string());
523            }
524        }
525
526        // Validate top_logprobs
527        if let Some(tlp) = self.top_logprobs {
528            if !(0..=20).contains(&tlp) {
529                return Err(format!(
530                    "Top logprobs must be between 0 and 20, got {}",
531                    tlp
532                ));
533            }
534        }
535
536        // Validate n
537        if let Some(n) = self.n {
538            if n == 0 {
539                return Err("n must be greater than 0".to_string());
540            }
541        }
542
543        // Validate stop sequences
544        if let Some(stop) = &self.stop {
545            if stop.len() > 4 {
546                return Err("Cannot have more than 4 stop sequences".to_string());
547            }
548            for seq in stop {
549                if seq.is_empty() {
550                    return Err("Stop sequences cannot be empty".to_string());
551                }
552                if seq.len() > 64 {
553                    return Err("Stop sequences cannot be longer than 64 characters".to_string());
554                }
555            }
556        }
557
558        Ok(())
559    }
560}
561
562impl ChatMessage {
563    /// Creates a new message with the `System` role.
564    ///
565    /// # Arguments
566    ///
567    /// * `content` - The content of the system message.
568    pub fn system(content: impl Into<String>) -> Self {
569        Self {
570            role: MessageRole::System,
571            content: content.into(),
572        }
573    }
574
575    /// Creates a new message with the `User` role.
576    ///
577    /// # Arguments
578    ///
579    /// * `content` - The content of the user message.
580    pub fn user(content: impl Into<String>) -> Self {
581        Self {
582            role: MessageRole::User,
583            content: content.into(),
584        }
585    }
586
587    /// Creates a new message with the `Assistant` role.
588    ///
589    /// # Arguments
590    ///
591    /// * `content` - The content of the assistant message.
592    pub fn assistant(content: impl Into<String>) -> Self {
593        Self {
594            role: MessageRole::Assistant,
595            content: content.into(),
596        }
597    }
598}
599
600// Legacy compatibility types - keep existing types for backward compatibility
601use uuid::Uuid;
602
603/// Represents a user account (legacy).
604#[derive(Debug, Clone, Serialize, Deserialize)]
605pub struct User {
606    /// The unique ID of the user.
607    pub id: Uuid,
608    /// The user's identifier string.
609    pub user_id: String,
610    /// The name of the user's subscription plan.
611    pub plan_name: String,
612    /// The user's current credit balance.
613    pub current_credits: f64,
614    /// The amount of credits the user has used in the current month.
615    pub credits_used_this_month: f64,
616    /// The date when the user's credits will reset.
617    pub credits_reset_date: DateTime<Utc>,
618    /// Indicates if the user account is active.
619    pub is_active: bool,
620    /// The timestamp of when the user account was created.
621    pub created_at: DateTime<Utc>,
622}
623
624/// Represents an API key (legacy).
625#[derive(Debug, Clone, Serialize, Deserialize)]
626pub struct ApiKey {
627    /// The unique ID of the API key.
628    pub id: Uuid,
629    /// The API key string.
630    pub key: String,
631    /// The ID of the user who owns the key.
632    pub owner_id: Uuid,
633    /// Indicates if the API key is active.
634    pub is_active: bool,
635    /// The timestamp of when the key was created.
636    pub created_at: DateTime<Utc>,
637    /// The expiration date of the key, if any.
638    pub expires_at: Option<DateTime<Utc>>,
639    /// A description of the key.
640    pub description: Option<String>,
641    /// The timestamp of when the key was last used.
642    pub last_used_at: Option<DateTime<Utc>>,
643}
644
645/// Represents usage statistics over a period (legacy).
646#[derive(Debug, Clone, Serialize, Deserialize)]
647pub struct UsageStats {
648    /// The number of days in the usage period.
649    pub period_days: u32,
650    /// A list of daily usage data.
651    pub daily_usage: Vec<DailyUsage>,
652    /// A list of recent credit transactions.
653    pub recent_transactions: Vec<CreditTransaction>,
654    /// The total number of requests made in the period.
655    pub total_requests: u64,
656    /// The total number of tokens used in the period.
657    pub total_tokens: u64,
658}
659
660/// Represents usage data for a single day (legacy).
661#[derive(Debug, Clone, Serialize, Deserialize)]
662pub struct DailyUsage {
663    /// The date for the usage data.
664    pub date: String,
665    /// The number of credits used on this day.
666    pub credits_used: f64,
667    /// The number of requests made on this day.
668    pub requests: u64,
669    /// The number of tokens used on this day.
670    pub tokens: u64,
671}
672
673/// Represents a single credit transaction (legacy).
674#[derive(Debug, Clone, Serialize, Deserialize)]
675pub struct CreditTransaction {
676    /// The unique ID of the transaction.
677    pub id: Uuid,
678    /// The type of the transaction.
679    pub transaction_type: TransactionType,
680    /// The amount of credits involved in the transaction.
681    pub credits_amount: f64,
682    /// The credit balance after the transaction.
683    pub credits_balance_after: f64,
684    /// The provider associated with the transaction, if any.
685    pub provider: Option<String>,
686    /// The model associated with the transaction, if any.
687    pub model: Option<String>,
688    /// A description of the transaction.
689    pub description: String,
690    /// The timestamp of when the transaction occurred.
691    pub created_at: DateTime<Utc>,
692}
693
694/// The type of credit transaction (legacy).
695#[derive(Debug, Clone, Serialize, Deserialize)]
696#[serde(rename_all = "lowercase")]
697pub enum TransactionType {
698    /// A transaction for API usage.
699    Usage,
700    /// A transaction for a credit reset.
701    Reset,
702    /// A transaction for a credit purchase.
703    Purchase,
704    /// A transaction for a credit refund.
705    Refund,
706}
707
708// Legacy aliases for backward compatibility
709/// A legacy type alias for `MessageRole`.
710pub type ChatRole = MessageRole;
711/// A legacy type alias for `Usage`.
712pub type ChatUsage = Usage;
713/// A legacy type alias for `HealthStatus`.
714pub type HealthCheck = HealthStatus;
715
716/// Represents the status of backend services (legacy).
717#[derive(Debug, Clone, Serialize, Deserialize)]
718pub struct HealthServices {
719    /// The status of the database connection.
720    pub database: bool,
721    /// The status of the Redis connection.
722    pub redis: bool,
723    /// The overall status of AI providers.
724    pub providers: bool,
725}
726
727/// The health status of the API (legacy).
728#[derive(Debug, Clone, Serialize, Deserialize)]
729#[serde(rename_all = "lowercase")]
730pub enum HealthStatusEnum {
731    /// The API is healthy.
732    Healthy,
733    /// The API is in a degraded state.
734    Degraded,
735    /// The API is unhealthy.
736    Unhealthy,
737    /// The API needs initialization.
738    NeedsInit,
739}
740
741/// Represents the format that the model must output.
742#[derive(Debug, Clone, Serialize, Deserialize)]
743#[serde(rename_all = "snake_case")]
744pub enum ResponseFormat {
745    /// The model can return text.
746    Text,
747    /// The model must return a valid JSON object.
748    JsonObject,
749    /// The model must return a JSON object that matches the provided schema.
750    JsonSchema { json_schema: serde_json::Value },
751}
752
753/// Represents a tool that the model can use.
754#[derive(Debug, Clone, Serialize, Deserialize)]
755pub struct Tool {
756    /// The type of the tool.
757    pub r#type: ToolType,
758    /// The function definition for the tool.
759    pub function: FunctionDefinition,
760}
761
762/// The type of tool.
763#[derive(Debug, Clone, Serialize, Deserialize)]
764#[serde(rename_all = "snake_case")]
765pub enum ToolType {
766    /// A function tool.
767    Function,
768}
769
770/// Represents a function definition for a tool.
771#[derive(Debug, Clone, Serialize, Deserialize)]
772pub struct FunctionDefinition {
773    /// The name of the function.
774    pub name: String,
775    /// A description of what the function does.
776    #[serde(skip_serializing_if = "Option::is_none")]
777    pub description: Option<String>,
778    /// The parameters the function accepts, described as a JSON Schema object.
779    #[serde(skip_serializing_if = "Option::is_none")]
780    pub parameters: Option<serde_json::Value>,
781}
782
783/// Controls which tool is called by the model.
784#[derive(Debug, Clone, Serialize, Deserialize)]
785#[serde(untagged)]
786pub enum ToolChoice {
787    /// No tool is called.
788    None,
789    /// The model chooses which tool to call.
790    Auto,
791    /// A specific tool is called.
792    Tool {
793        r#type: ToolType,
794        function: ToolFunction,
795    },
796}
797
798/// Represents a tool function call.
799#[derive(Debug, Clone, Serialize, Deserialize)]
800pub struct ToolFunction {
801    /// The name of the function to call.
802    pub name: String,
803}
804
805/// Represents a streaming chat completion response (OpenAI delta format).
806#[derive(Debug, Clone, Serialize, Deserialize)]
807pub struct ChatCompletionStreamResponse {
808    /// A unique identifier for the chat completion.
809    pub id: String,
810    /// The type of object, which is always "chat.completion.chunk".
811    pub object: String,
812    /// The Unix timestamp (in seconds) of when the completion was created.
813    pub created: u64,
814    /// The model that was used for the completion.
815    pub model: String,
816    /// A list of chat completion choices.
817    pub choices: Vec<ChatCompletionStreamChoice>,
818    /// Information about the token usage for this completion (only present in the final chunk).
819    #[serde(skip_serializing_if = "Option::is_none")]
820    pub usage: Option<Usage>,
821}
822
823/// Represents a single choice in a streaming chat completion response.
824#[derive(Debug, Clone, Serialize, Deserialize)]
825pub struct ChatCompletionStreamChoice {
826    /// The index of the choice in the list of choices.
827    pub index: u32,
828    /// The delta containing the new content for this choice.
829    pub delta: ChatCompletionStreamDelta,
830    /// The reason the model stopped generating tokens (only present in the final chunk).
831    #[serde(skip_serializing_if = "Option::is_none")]
832    pub finish_reason: Option<String>,
833}
834
835/// Represents the delta (change) in a streaming chat completion response.
836#[derive(Debug, Clone, Serialize, Deserialize)]
837pub struct ChatCompletionStreamDelta {
838    /// The role of the message (only present in the first chunk).
839    #[serde(skip_serializing_if = "Option::is_none")]
840    pub role: Option<String>,
841    /// The new content for this chunk.
842    #[serde(skip_serializing_if = "Option::is_none")]
843    pub content: Option<String>,
844    /// Tool calls for this chunk (if any).
845    #[serde(skip_serializing_if = "Option::is_none")]
846    pub tool_calls: Option<Vec<ToolCall>>,
847}
848
849/// Represents a tool call in a streaming response.
850#[derive(Debug, Clone, Serialize, Deserialize)]
851pub struct ToolCall {
852    /// The index of the tool call.
853    pub index: u32,
854    /// The ID of the tool call.
855    #[serde(skip_serializing_if = "Option::is_none")]
856    pub id: Option<String>,
857    /// The type of the tool call.
858    #[serde(skip_serializing_if = "Option::is_none")]
859    pub r#type: Option<String>,
860    /// The function being called.
861    #[serde(skip_serializing_if = "Option::is_none")]
862    pub function: Option<ToolCallFunction>,
863}
864
865/// Represents a function call in a tool call.
866#[derive(Debug, Clone, Serialize, Deserialize)]
867pub struct ToolCallFunction {
868    /// The name of the function.
869    #[serde(skip_serializing_if = "Option::is_none")]
870    pub name: Option<String>,
871    /// The arguments for the function.
872    #[serde(skip_serializing_if = "Option::is_none")]
873    pub arguments: Option<String>,
874}