pub mod deployment;
pub mod metrics;
pub mod openai;
pub mod team;
pub mod user;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Metadata {
pub id: Uuid,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub version: i64,
#[serde(default)]
pub extra: HashMap<String, serde_json::Value>,
}
impl Default for Metadata {
fn default() -> Self {
let now = chrono::Utc::now();
Self {
id: Uuid::new_v4(),
created_at: now,
updated_at: now,
version: 1,
extra: HashMap::new(),
}
}
}
impl Metadata {
pub fn new() -> Self {
Self::default()
}
pub fn touch(&mut self) {
self.updated_at = chrono::Utc::now();
self.version += 1;
}
pub fn set_extra<K: Into<String>, V: Into<serde_json::Value>>(&mut self, key: K, value: V) {
self.extra.insert(key.into(), value.into());
}
pub fn get_extra(&self, key: &str) -> Option<&serde_json::Value> {
self.extra.get(key)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiKey {
#[serde(flatten)]
pub metadata: Metadata,
pub name: String,
pub key_hash: String,
pub key_prefix: String,
pub user_id: Option<Uuid>,
pub team_id: Option<Uuid>,
pub permissions: Vec<String>,
pub rate_limits: Option<RateLimits>,
pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
pub is_active: bool,
pub last_used_at: Option<chrono::DateTime<chrono::Utc>>,
pub usage_stats: UsageStats,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimits {
pub rpm: Option<u32>,
pub tpm: Option<u32>,
pub rpd: Option<u32>,
pub tpd: Option<u32>,
pub concurrent: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UsageStats {
pub total_requests: u64,
pub total_tokens: u64,
pub total_cost: f64,
pub requests_today: u32,
pub tokens_today: u32,
pub cost_today: f64,
pub last_reset: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelInfo {
pub id: String,
pub name: String,
pub provider: String,
pub model_type: ModelType,
pub context_window: Option<u32>,
pub max_output_tokens: Option<u32>,
pub input_cost_per_token: Option<f64>,
pub output_cost_per_token: Option<f64>,
pub features: Vec<String>,
pub description: Option<String>,
pub is_available: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ModelType {
Chat,
Completion,
Embedding,
ImageGeneration,
AudioTranscription,
AudioTranslation,
Moderation,
FineTuning,
Rerank,
}
pub type RequestContext = crate::core::types::context::RequestContext;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderHealth {
pub provider: String,
pub status: HealthStatus,
pub last_check: chrono::DateTime<chrono::Utc>,
pub response_time_ms: Option<u64>,
pub error_message: Option<String>,
pub success_rate: f64,
pub total_requests: u64,
pub failed_requests: u64,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum HealthStatus {
Healthy,
Degraded,
Unhealthy,
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderRegistryHealth {
pub total_count: usize,
pub healthy_count: usize,
pub degraded_count: usize,
pub unhealthy_count: usize,
pub providers: Vec<ProviderHealth>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metadata_creation() {
let metadata = Metadata::new();
assert_eq!(metadata.version, 1);
assert!(metadata.created_at <= chrono::Utc::now());
}
#[test]
fn test_metadata_touch() {
let mut metadata = Metadata::new();
let original_version = metadata.version;
let original_updated = metadata.updated_at;
std::thread::sleep(std::time::Duration::from_millis(1));
metadata.touch();
assert_eq!(metadata.version, original_version + 1);
assert!(metadata.updated_at > original_updated);
}
#[test]
fn test_request_context_creation() {
let context = RequestContext::new();
assert!(!context.request_id.is_empty());
assert!(context.user_id.is_none());
}
#[test]
fn test_request_context_builder() {
let user_id = Uuid::new_v4();
let team_id = Uuid::new_v4();
let context = RequestContext::new()
.with_user(user_id, Some(team_id))
.with_client_info(
Some("127.0.0.1".to_string()),
Some("test-agent".to_string()),
)
.add_header("X-Custom", "value");
assert_eq!(context.user_id, Some(user_id.to_string()));
assert_eq!(context.team_id(), Some(team_id));
assert_eq!(context.client_ip, Some("127.0.0.1".to_string()));
assert_eq!(context.headers.get("X-Custom"), Some(&"value".to_string()));
}
}