headless_lms_models/
chatbot_conversations.rs

1use futures::future::OptionFuture;
2
3use crate::{chatbot_conversation_messages::ChatbotConversationMessage, prelude::*};
4
5#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
6#[cfg_attr(feature = "ts_rs", derive(TS))]
7pub struct ChatbotConversation {
8    pub id: Uuid,
9    pub created_at: DateTime<Utc>,
10    pub updated_at: DateTime<Utc>,
11    pub deleted_at: Option<DateTime<Utc>>,
12    pub course_id: Uuid,
13    pub user_id: Uuid,
14    pub chatbot_configuration_id: Uuid,
15}
16
17#[derive(Serialize, Deserialize, PartialEq, Clone)]
18#[cfg_attr(feature = "ts_rs", derive(TS))]
19/** Should contain all information required to display the chatbot to the user. */
20pub struct ChatbotConversationInfo {
21    pub current_conversation: Option<ChatbotConversation>,
22    pub current_conversation_messages: Option<Vec<ChatbotConversationMessage>>,
23    pub chatbot_name: String,
24    pub hide_citations: bool,
25}
26
27pub async fn insert(
28    conn: &mut PgConnection,
29    input: ChatbotConversation,
30) -> ModelResult<ChatbotConversation> {
31    let res = sqlx::query_as!(
32        ChatbotConversation,
33        r#"
34INSERT INTO chatbot_conversations (course_id, user_id, chatbot_configuration_id)
35VALUES ($1, $2, $3)
36RETURNING *
37        "#,
38        input.course_id,
39        input.user_id,
40        input.chatbot_configuration_id
41    )
42    .fetch_one(conn)
43    .await?;
44    Ok(res)
45}
46
47pub async fn get_latest_conversation_for_user(
48    conn: &mut PgConnection,
49    user_id: Uuid,
50    chatbot_configuration_id: Uuid,
51) -> ModelResult<ChatbotConversation> {
52    let res = sqlx::query_as!(
53        ChatbotConversation,
54        r#"
55SELECT *
56FROM chatbot_conversations
57WHERE user_id = $1
58  AND chatbot_configuration_id = $2
59  AND deleted_at IS NULL
60ORDER BY created_at DESC
61LIMIT 1
62        "#,
63        user_id,
64        chatbot_configuration_id
65    )
66    .fetch_one(conn)
67    .await?;
68    Ok(res)
69}
70
71/// Gets the current conversation for the user, if any. Also inlcudes information about the chatbot so that the chatbot ui can be rendered using the information.
72pub async fn get_current_conversation_info(
73    tx: &mut PgConnection,
74    user_id: Uuid,
75    chatbot_configuration_id: Uuid,
76) -> ModelResult<ChatbotConversationInfo> {
77    let chatbot_configuration =
78        crate::chatbot_configurations::get_by_id(tx, chatbot_configuration_id).await?;
79    let current_conversation =
80        get_latest_conversation_for_user(tx, user_id, chatbot_configuration_id)
81            .await
82            .optional()?;
83    let current_conversation_messages = OptionFuture::from(
84        current_conversation
85            .clone()
86            .map(|c| crate::chatbot_conversation_messages::get_by_conversation_id(tx, c.id)),
87    )
88    .await
89    .transpose()?;
90
91    Ok(ChatbotConversationInfo {
92        current_conversation,
93        current_conversation_messages,
94        // Don't want to expose everything from the chatbot configuration to the user because it contains private information like the prompt.
95        chatbot_name: chatbot_configuration.chatbot_name,
96        hide_citations: chatbot_configuration.hide_citations,
97    })
98}