use actix_web::{HttpResponse, Result, web}; use service::error::AppError; use session::Session; use uuid::Uuid; use crate::ApiResponse; use crate::error::ApiError; #[derive(Debug, serde::Serialize, utoipa::ToSchema)] pub struct ForkConversationResponse { pub id: Uuid, pub title: Option, pub model: String, pub created_at: chrono::DateTime, } /// Fork a conversation from a specific message, creating a new conversation /// with all messages up to and including the source message. #[utoipa::path( post, path = "/api/ai/conversations/{conversation_id}/messages/{message_id}/fork", operation_id = "ai_conversation_fork", params( ("conversation_id" = Uuid, Path, description = "Conversation ID"), ("message_id" = Uuid, Path, description = "Source message ID to fork from"), ), responses( (status = 200, description = "Conversation forked", body = ApiResponse), ), tag = "AI Chat" )] pub async fn message_fork( service: web::Data, session: Session, path: web::Path<(Uuid, Uuid)>, ) -> Result { let user_id = session .user() .ok_or_else(|| ApiError::from(AppError::Unauthorized))?; let (conversation_id, source_message_id) = path.into_inner(); let new_conv = service .fork_conversation_from_message(user_id, conversation_id, source_message_id) .await?; let resp = ForkConversationResponse { id: new_conv.id, title: new_conv.title, model: new_conv.model, created_at: new_conv.created_at, }; Ok(ApiResponse::ok(resp).to_response()) } #[derive(Debug, serde::Serialize, utoipa::ToSchema)] pub struct ForkListResponse { pub forks: Vec, } /// List all forks created from a specific message. pub async fn message_forks( service: web::Data, session: Session, path: web::Path<(Uuid, Uuid)>, ) -> Result { let user_id = session .user() .ok_or_else(|| ApiError::from(AppError::Unauthorized))?; let (conversation_id, source_message_id) = path.into_inner(); let forks = service .list_forks(conversation_id, user_id, source_message_id) .await?; let fork_responses: Vec = forks .into_iter() .map(|f| ForkConversationResponse { id: f.fork_message_id, title: None, model: String::new(), created_at: f.created_at, }) .collect(); Ok(ApiResponse::ok(ForkListResponse { forks: fork_responses, }) .to_response()) }