headless_lms_models/
chatbot_conversations.rs1use futures::future::OptionFuture;
2use utoipa::ToSchema;
3
4use crate::{
5 chatbot_conversation_messages::ChatbotConversationMessage,
6 chatbot_conversation_messages_citations::ChatbotConversationMessageCitation,
7 chatbot_conversation_suggested_messages::ChatbotConversationSuggestedMessage, prelude::*,
8};
9
10#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
11
12pub struct ChatbotConversation {
13 pub id: Uuid,
14 pub created_at: DateTime<Utc>,
15 pub updated_at: DateTime<Utc>,
16 pub deleted_at: Option<DateTime<Utc>>,
17 pub course_id: Uuid,
18 pub user_id: Uuid,
19 pub chatbot_configuration_id: Uuid,
20}
21
22#[derive(Serialize, Deserialize, PartialEq, Clone, ToSchema)]
23
24pub struct ChatbotConversationInfo {
26 pub current_conversation: Option<ChatbotConversation>,
27 pub current_conversation_messages: Option<Vec<ChatbotConversationMessage>>,
28 pub current_conversation_message_citations: Option<Vec<ChatbotConversationMessageCitation>>,
29 pub chatbot_name: String,
30 pub course_name: String,
31 pub hide_citations: bool,
32 pub suggested_messages: Option<Vec<ChatbotConversationSuggestedMessage>>,
33}
34
35pub async fn insert(
36 conn: &mut PgConnection,
37 input: ChatbotConversation,
38) -> ModelResult<ChatbotConversation> {
39 let res = sqlx::query_as!(
40 ChatbotConversation,
41 r#"
42INSERT INTO chatbot_conversations (course_id, user_id, chatbot_configuration_id)
43VALUES ($1, $2, $3)
44RETURNING *
45 "#,
46 input.course_id,
47 input.user_id,
48 input.chatbot_configuration_id
49 )
50 .fetch_one(conn)
51 .await?;
52 Ok(res)
53}
54
55pub async fn get_by_id(conn: &mut PgConnection, id: Uuid) -> ModelResult<ChatbotConversation> {
56 let res = sqlx::query_as!(
57 ChatbotConversation,
58 r#"
59SELECT *
60FROM chatbot_conversations
61WHERE id = $1
62 AND deleted_at IS NULL
63 "#,
64 id
65 )
66 .fetch_one(conn)
67 .await?;
68 Ok(res)
69}
70
71pub async fn create_for_user_and_configuration(
72 conn: &mut PgConnection,
73 pkey_policy: PKeyPolicy<Uuid>,
74 user_id: Uuid,
75 chatbot_configuration_id: Uuid,
76) -> ModelResult<ChatbotConversation> {
77 let res = sqlx::query_as!(
78 ChatbotConversation,
79 r#"
80INSERT INTO chatbot_conversations (
81 id,
82 course_id,
83 user_id,
84 chatbot_configuration_id
85)
86SELECT
87 $1,
88 chatbot_configurations.course_id,
89 $2,
90 chatbot_configurations.id
91FROM chatbot_configurations
92WHERE chatbot_configurations.id = $3
93 AND chatbot_configurations.deleted_at IS NULL
94RETURNING *
95 "#,
96 pkey_policy.into_uuid(),
97 user_id,
98 chatbot_configuration_id
99 )
100 .fetch_one(conn)
101 .await?;
102 Ok(res)
103}
104
105pub async fn get_latest_conversation_for_user(
106 conn: &mut PgConnection,
107 user_id: Uuid,
108 chatbot_configuration_id: Uuid,
109) -> ModelResult<ChatbotConversation> {
110 let res = sqlx::query_as!(
111 ChatbotConversation,
112 r#"
113SELECT *
114FROM chatbot_conversations
115WHERE user_id = $1
116 AND chatbot_configuration_id = $2
117 AND deleted_at IS NULL
118ORDER BY created_at DESC
119LIMIT 1
120 "#,
121 user_id,
122 chatbot_configuration_id
123 )
124 .fetch_one(conn)
125 .await?;
126 Ok(res)
127}
128
129pub async fn get_current_conversation_info(
131 tx: &mut PgConnection,
132 user_id: Uuid,
133 chatbot_configuration_id: Uuid,
134) -> ModelResult<ChatbotConversationInfo> {
135 let chatbot_configuration =
136 crate::chatbot_configurations::get_by_id(tx, chatbot_configuration_id).await?;
137 let course = crate::courses::get_course(tx, chatbot_configuration.course_id).await?;
138 let current_conversation =
139 get_latest_conversation_for_user(tx, user_id, chatbot_configuration_id)
140 .await
141 .optional()?;
142 let current_conversation_messages = OptionFuture::from(
144 current_conversation
145 .clone()
146 .map(|c| crate::chatbot_conversation_messages::get_by_conversation_id(tx, c.id)),
147 )
148 .await
149 .transpose()?;
150
151 let current_conversation_message_citations =
152 OptionFuture::from(current_conversation.clone().map(|c| {
153 crate::chatbot_conversation_messages_citations::get_by_conversation_id(tx, c.id)
154 }))
155 .await
156 .transpose()?;
157
158 let suggested_messages = if chatbot_configuration.suggest_next_messages
159 && let Some(ccm) = ¤t_conversation_messages
160 && let Some(last_ccm) = ccm.last()
161 {
162 let sm = crate::chatbot_conversation_suggested_messages::get_by_conversation_message_id(
163 tx,
164 last_ccm.id.to_owned(),
165 )
166 .await?;
167 Some(sm)
169 } else {
170 None
171 };
172
173 Ok(ChatbotConversationInfo {
174 current_conversation,
175 current_conversation_messages,
176 current_conversation_message_citations,
177 suggested_messages,
178 chatbot_name: chatbot_configuration.chatbot_name,
180 course_name: course.name,
181 hide_citations: chatbot_configuration.hide_citations,
182 })
183}