//! AI provider management — CRUD for model providers. //! //! All functions take `&DatabaseConnection` instead of `&AppService` //! so they can live in the agent crate. use chrono::Utc; use db::database::AppDatabase; use models::agents::model_provider; use models::agents::{ModelStatus, model_provider::Entity as ProviderEntity}; use sea_orm::*; use uuid::Uuid; use crate::error::AgentError; #[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)] pub struct CreateProviderRequest { pub name: String, pub display_name: String, pub website: Option, } #[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)] pub struct UpdateProviderRequest { pub display_name: Option, pub website: Option, pub status: Option, } #[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)] pub struct ProviderResponse { pub id: Uuid, pub name: String, pub display_name: String, pub website: Option, pub status: String, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, } impl From for ProviderResponse { fn from(p: model_provider::Model) -> Self { Self { id: p.id, name: p.name, display_name: p.display_name, website: p.website, status: p.status, created_at: p.created_at, updated_at: p.updated_at, } } } /// List all providers ordered by display name. pub async fn list_providers(db: &AppDatabase) -> Result, AgentError> { let providers = ProviderEntity::find() .order_by_asc(model_provider::Column::DisplayName) .all(db) .await?; Ok(providers.into_iter().map(ProviderResponse::from).collect()) } /// Get a single provider by ID. pub async fn get_provider(db: &AppDatabase, id: Uuid) -> Result { let provider = ProviderEntity::find_by_id(id) .one(db) .await? .ok_or_else(|| AgentError::NotFound(format!("Provider not found: {}", id)))?; Ok(ProviderResponse::from(provider)) } /// Create a new provider. pub async fn create_provider( db: &AppDatabase, request: CreateProviderRequest, ) -> Result { let now = Utc::now(); let active = model_provider::ActiveModel { id: Set(Uuid::now_v7()), name: Set(request.name), display_name: Set(request.display_name), website: Set(request.website), status: Set(ModelStatus::Active.to_string()), created_at: Set(now), updated_at: Set(now), ..Default::default() }; let model = active.insert(db).await?; Ok(ProviderResponse::from(model)) } /// Update an existing provider. pub async fn update_provider( db: &AppDatabase, id: Uuid, request: UpdateProviderRequest, ) -> Result { let provider = ProviderEntity::find_by_id(id) .one(db) .await? .ok_or_else(|| AgentError::NotFound(format!("Provider not found: {}", id)))?; let mut active: model_provider::ActiveModel = provider.into(); if let Some(display_name) = request.display_name { active.display_name = Set(display_name); } if let Some(website) = request.website { active.website = Set(Some(website)); } if let Some(status) = request.status { active.status = Set(status); } active.updated_at = Set(Utc::now()); let model = active.update(db).await?; Ok(ProviderResponse::from(model)) } /// Delete a provider by ID. pub async fn delete_provider(db: &AppDatabase, id: Uuid) -> Result<(), AgentError> { ProviderEntity::delete_by_id(id).exec(db).await?; Ok(()) }