headless_lms_models/
chatbot_conversation_message_reasoning.rs1use crate::prelude::*;
2use utoipa::ToSchema;
3
4#[derive(Clone, PartialEq, Deserialize, Serialize, Debug, ToSchema)]
5pub struct ChatbotConversationMessageReasoning {
6 pub id: Uuid,
7 pub chatbot_conversation_message_id: Uuid,
8 pub created_at: DateTime<Utc>,
9 pub updated_at: DateTime<Utc>,
10 pub deleted_at: Option<DateTime<Utc>>,
11 pub summary: Option<String>,
12 pub reasoning_id: String,
13 pub response_id: String,
14 #[serde(skip)]
19 pub encrypted_content: Option<String>,
20}
21
22impl Default for ChatbotConversationMessageReasoning {
23 fn default() -> Self {
24 Self {
25 id: Uuid::nil(),
26 chatbot_conversation_message_id: Uuid::nil(),
27 created_at: Default::default(),
28 updated_at: Default::default(),
29 deleted_at: None,
30 summary: None,
31 reasoning_id: Default::default(),
32 response_id: Default::default(),
33 encrypted_content: None,
34 }
35 }
36}
37
38pub async fn insert(
39 conn: &mut PgConnection,
40 input: ChatbotConversationMessageReasoning,
41 msg_id: Uuid,
42) -> ModelResult<ChatbotConversationMessageReasoning> {
43 let res = sqlx::query_as!(
44 ChatbotConversationMessageReasoning,
45 r#"
46INSERT INTO chatbot_conversation_message_reasoning (
47 chatbot_conversation_message_id,
48 summary,
49 response_id,
50 reasoning_id,
51 encrypted_content
52 )
53VALUES ($1, $2, $3, $4, $5)
54RETURNING *
55 "#,
56 msg_id,
57 input.summary,
58 input.response_id,
59 input.reasoning_id,
60 input.encrypted_content,
61 )
62 .fetch_one(conn)
63 .await?;
64 Ok(res)
65}
66
67pub async fn get_by_id(
68 conn: &mut PgConnection,
69 id: Uuid,
70) -> ModelResult<ChatbotConversationMessageReasoning> {
71 let res = sqlx::query_as!(
72 ChatbotConversationMessageReasoning,
73 r#"
74SELECT *
75FROM chatbot_conversation_message_reasoning
76WHERE id = $1
77 AND deleted_at IS NULL
78 "#,
79 id
80 )
81 .fetch_one(conn)
82 .await?;
83 Ok(res)
84}
85
86pub async fn get_by_message_id(
87 conn: &mut PgConnection,
88 message_id: Uuid,
89) -> ModelResult<Option<ChatbotConversationMessageReasoning>> {
90 let res = sqlx::query_as!(
91 ChatbotConversationMessageReasoning,
92 r#"
93SELECT *
94FROM chatbot_conversation_message_reasoning
95WHERE chatbot_conversation_message_id = $1
96 AND deleted_at IS NULL
97 "#,
98 message_id
99 )
100 .fetch_optional(conn)
101 .await?;
102 Ok(res)
103}
104
105pub async fn delete(
106 conn: &mut PgConnection,
107 id: Uuid,
108) -> ModelResult<ChatbotConversationMessageReasoning> {
109 let res = sqlx::query_as!(
110 ChatbotConversationMessageReasoning,
111 r#"
112UPDATE chatbot_conversation_message_reasoning
113SET deleted_at = NOW()
114WHERE id = $1
115 AND deleted_at IS NULL
116RETURNING *
117 "#,
118 id
119 )
120 .fetch_one(conn)
121 .await?;
122 Ok(res)
123}
124
125#[cfg(test)]
126mod tests {
127 use crate::chatbot_conversation_messages::{ChatbotConversationMessage, Message};
128
129 use super::*;
130
131 #[test]
135 fn a_reasoning_message_never_serializes_its_payload() {
136 let message = ChatbotConversationMessage {
137 message: Message::Reasoning(ChatbotConversationMessageReasoning {
138 summary: Some("Thinking about loops".to_string()),
139 encrypted_content: Some("the-opaque-payload".to_string()),
140 ..Default::default()
141 }),
142 ..Default::default()
143 };
144
145 let json = serde_json::to_string(&message).expect("the message serializes");
146
147 assert!(json.contains("Thinking about loops"), "{json}");
148 assert!(!json.contains("encrypted_content"), "{json}");
149 assert!(!json.contains("the-opaque-payload"), "{json}");
150 }
151}