Skip to main content

headless_lms_models/
chatbot_configurations.rs

1use crate::prelude::*;
2use utoipa::ToSchema;
3
4#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Type, ToSchema)]
5#[sqlx(type_name = "reasoning_effort_level", rename_all = "snake_case")]
6#[serde(rename_all = "snake_case")]
7pub enum ReasoningEffortLevel {
8    None,
9    Minimal,
10    Low,
11    Medium,
12    High,
13    Xhigh,
14}
15
16#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Type, ToSchema)]
17#[sqlx(type_name = "verbosity_level", rename_all = "snake_case")]
18#[serde(rename_all = "snake_case")]
19pub enum VerbosityLevel {
20    Low,
21    Medium,
22    High,
23}
24
25#[derive(Clone, PartialEq, Deserialize, Serialize, ToSchema)]
26pub struct ChatbotConfiguration {
27    pub id: Uuid,
28    pub created_at: DateTime<Utc>,
29    pub updated_at: DateTime<Utc>,
30    pub deleted_at: Option<DateTime<Utc>>,
31    pub course_id: Uuid,
32    pub enabled_to_students: bool,
33    pub chatbot_name: String,
34    pub model_id: Uuid,
35    pub prompt: String,
36    pub initial_message: String,
37    pub weekly_tokens_per_user: i32,
38    pub daily_tokens_per_user: i32,
39    pub temperature: f32,
40    pub top_p: f32,
41    pub frequency_penalty: f32,
42    pub presence_penalty: f32,
43    pub max_output_tokens: i32,
44    pub verbosity: VerbosityLevel,
45    pub reasoning_effort: ReasoningEffortLevel,
46    pub use_azure_search: bool,
47    pub maintain_azure_search_index: bool,
48    pub hide_citations: bool,
49    pub use_semantic_reranking: bool,
50    pub use_tools: bool,
51    pub default_chatbot: bool,
52    pub suggest_next_messages: bool,
53    pub initial_suggested_messages: Option<Vec<String>>,
54}
55
56impl Default for ChatbotConfiguration {
57    fn default() -> Self {
58        Self {
59            id: Uuid::nil(),
60            created_at: Default::default(),
61            updated_at: Default::default(),
62            deleted_at: None,
63            course_id: Default::default(),
64            enabled_to_students: false,
65            chatbot_name: Default::default(),
66            model_id: Uuid::nil(),
67            prompt: Default::default(),
68            initial_message: Default::default(),
69            weekly_tokens_per_user: 20000 * 5,
70            daily_tokens_per_user: 20000,
71            max_output_tokens: 600,
72            temperature: 0.7,
73            top_p: 1.0,
74            frequency_penalty: 0.0,
75            presence_penalty: 0.0,
76            reasoning_effort: ReasoningEffortLevel::Minimal,
77            verbosity: VerbosityLevel::Medium,
78            use_azure_search: false,
79            maintain_azure_search_index: false,
80            hide_citations: false,
81            use_semantic_reranking: false,
82            use_tools: false,
83            default_chatbot: false,
84            suggest_next_messages: false,
85            initial_suggested_messages: None,
86        }
87    }
88}
89
90#[derive(Clone, PartialEq, Deserialize, Serialize, Debug, ToSchema)]
91
92pub struct NewChatbotConf {
93    pub course_id: Uuid,
94    pub enabled_to_students: bool,
95    pub chatbot_name: String,
96    pub model_id: Uuid,
97    pub prompt: String,
98    pub initial_message: String,
99    pub weekly_tokens_per_user: i32,
100    pub daily_tokens_per_user: i32,
101    pub temperature: f32,
102    pub top_p: f32,
103    pub frequency_penalty: f32,
104    pub presence_penalty: f32,
105    pub max_output_tokens: i32,
106    pub verbosity: VerbosityLevel,
107    pub reasoning_effort: ReasoningEffortLevel,
108    pub use_azure_search: bool,
109    pub maintain_azure_search_index: bool,
110    pub hide_citations: bool,
111    pub use_semantic_reranking: bool,
112    pub use_tools: bool,
113    pub default_chatbot: bool,
114    pub chatbotconf_id: Option<Uuid>,
115    pub suggest_next_messages: bool,
116    pub initial_suggested_messages: Option<Vec<String>>,
117}
118
119impl Default for NewChatbotConf {
120    fn default() -> Self {
121        let chatbot_conf: ChatbotConfiguration = ChatbotConfiguration::default();
122        Self {
123            course_id: chatbot_conf.course_id,
124            enabled_to_students: chatbot_conf.enabled_to_students,
125            chatbot_name: chatbot_conf.chatbot_name,
126            model_id: chatbot_conf.model_id,
127            prompt: chatbot_conf.prompt,
128            initial_message: chatbot_conf.initial_message,
129            weekly_tokens_per_user: chatbot_conf.weekly_tokens_per_user,
130            daily_tokens_per_user: chatbot_conf.daily_tokens_per_user,
131            temperature: chatbot_conf.temperature,
132            top_p: chatbot_conf.top_p,
133            frequency_penalty: chatbot_conf.frequency_penalty,
134            presence_penalty: chatbot_conf.presence_penalty,
135            max_output_tokens: chatbot_conf.max_output_tokens,
136            verbosity: chatbot_conf.verbosity,
137            reasoning_effort: chatbot_conf.reasoning_effort,
138            use_azure_search: chatbot_conf.use_azure_search,
139            maintain_azure_search_index: chatbot_conf.maintain_azure_search_index,
140            hide_citations: chatbot_conf.hide_citations,
141            use_semantic_reranking: chatbot_conf.use_semantic_reranking,
142            use_tools: chatbot_conf.use_tools,
143            default_chatbot: chatbot_conf.default_chatbot,
144            chatbotconf_id: None,
145            suggest_next_messages: chatbot_conf.suggest_next_messages,
146            initial_suggested_messages: chatbot_conf.initial_suggested_messages,
147        }
148    }
149}
150
151pub async fn get_by_id(conn: &mut PgConnection, id: Uuid) -> ModelResult<ChatbotConfiguration> {
152    let res = sqlx::query_as!(
153        ChatbotConfiguration,
154        r#"
155SELECT *
156FROM chatbot_configurations
157WHERE id = $1
158AND deleted_at IS NULL
159        "#,
160        id
161    )
162    .fetch_one(conn)
163    .await?;
164    Ok(res)
165}
166
167pub async fn insert(
168    conn: &mut PgConnection,
169    pkey_policy: PKeyPolicy<Uuid>,
170    input: NewChatbotConf,
171) -> ModelResult<ChatbotConfiguration> {
172    let maintain_azure_search_index = input.use_azure_search;
173    let res = sqlx::query_as!(
174        ChatbotConfiguration,
175        r#"
176INSERT INTO chatbot_configurations (
177    id,
178    course_id,
179    enabled_to_students,
180    chatbot_name,
181    model_id,
182    prompt,
183    initial_message,
184    weekly_tokens_per_user,
185    daily_tokens_per_user,
186    temperature,
187    top_p,
188    hide_citations,
189    frequency_penalty,
190    presence_penalty,
191    max_output_tokens,
192    verbosity,
193    reasoning_effort,
194    use_azure_search,
195    use_tools,
196    maintain_azure_search_index,
197    default_chatbot,
198    suggest_next_messages,
199    initial_suggested_messages
200  )
201VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)
202RETURNING *
203        "#,
204        pkey_policy.into_uuid(),
205        input.course_id,
206        input.enabled_to_students,
207        input.chatbot_name,
208        input.model_id,
209        input.prompt,
210        input.initial_message,
211        input.weekly_tokens_per_user,
212        input.daily_tokens_per_user,
213        input.temperature,
214        input.top_p,
215        input.hide_citations,
216        input.frequency_penalty,
217        input.presence_penalty,
218        input.max_output_tokens,
219        input.verbosity as VerbosityLevel,
220        input.reasoning_effort as ReasoningEffortLevel,
221        input.use_azure_search,
222        input.use_tools,
223        maintain_azure_search_index,
224        input.default_chatbot,
225        input.suggest_next_messages,
226        input.initial_suggested_messages.as_deref(),
227    )
228    .fetch_one(conn)
229    .await?;
230    Ok(res)
231}
232
233pub async fn edit(
234    conn: &mut PgConnection,
235    input: NewChatbotConf,
236    chatbot_configuration_id: Uuid,
237) -> ModelResult<ChatbotConfiguration> {
238    let res = sqlx::query_as!(
239        ChatbotConfiguration,
240        r#"
241UPDATE chatbot_configurations
242SET
243    enabled_to_students = $1,
244    chatbot_name = $2,
245    prompt = $3,
246    initial_message = $4,
247    weekly_tokens_per_user = $5,
248    daily_tokens_per_user = $6,
249    temperature = $7,
250    top_p = $8,
251    frequency_penalty = $9,
252    presence_penalty = $10,
253    max_output_tokens = $11,
254    use_azure_search = $12,
255    maintain_azure_search_index = $13,
256    hide_citations = $14,
257    use_semantic_reranking = $15,
258    default_chatbot = $16,
259    model_id = $17,
260    verbosity = $18,
261    reasoning_effort = $19,
262    use_tools = $20,
263    suggest_next_messages = $21,
264    initial_suggested_messages = $22
265WHERE id = $23
266    AND deleted_at IS NULL
267RETURNING *
268"#,
269        input.enabled_to_students,
270        input.chatbot_name,
271        input.prompt,
272        input.initial_message,
273        input.weekly_tokens_per_user,
274        input.daily_tokens_per_user,
275        input.temperature,
276        input.top_p,
277        input.frequency_penalty,
278        input.presence_penalty,
279        input.max_output_tokens,
280        input.use_azure_search,
281        input.maintain_azure_search_index,
282        input.hide_citations,
283        input.use_semantic_reranking,
284        input.default_chatbot,
285        input.model_id,
286        input.verbosity as VerbosityLevel,
287        input.reasoning_effort as ReasoningEffortLevel,
288        input.use_tools,
289        input.suggest_next_messages,
290        input.initial_suggested_messages.as_deref(),
291        chatbot_configuration_id,
292    )
293    .fetch_one(conn)
294    .await?;
295    Ok(res)
296}
297
298pub async fn delete(conn: &mut PgConnection, chatbot_configuration_id: Uuid) -> ModelResult<()> {
299    sqlx::query!(
300        r#"
301UPDATE chatbot_configurations
302SET deleted_at = now()
303WHERE id = $1
304AND deleted_at IS NULL
305        "#,
306        chatbot_configuration_id
307    )
308    .execute(conn)
309    .await?;
310    Ok(())
311}
312
313pub async fn get_for_course(
314    conn: &mut PgConnection,
315    course_id: Uuid,
316) -> ModelResult<Vec<ChatbotConfiguration>> {
317    let res = sqlx::query_as!(
318        ChatbotConfiguration,
319        r#"
320SELECT *
321FROM chatbot_configurations
322WHERE course_id = $1
323AND deleted_at IS NULL
324"#,
325        course_id
326    )
327    .fetch_all(conn)
328    .await?;
329    Ok(res)
330}
331
332pub async fn get_enabled_nondefault_for_course(
333    conn: &mut PgConnection,
334    course_id: Uuid,
335) -> ModelResult<Vec<ChatbotConfiguration>> {
336    let res = sqlx::query_as!(
337        ChatbotConfiguration,
338        r#"
339SELECT *
340FROM chatbot_configurations
341WHERE course_id = $1
342AND default_chatbot IS false
343AND enabled_to_students IS true
344AND deleted_at IS NULL
345"#,
346        course_id
347    )
348    .fetch_all(conn)
349    .await?;
350    Ok(res)
351}
352
353pub async fn get_for_azure_search_maintenance(
354    conn: &mut PgConnection,
355) -> ModelResult<Vec<ChatbotConfiguration>> {
356    let res = sqlx::query_as!(
357        ChatbotConfiguration,
358        r#"
359SELECT *
360FROM chatbot_configurations
361WHERE maintain_azure_search_index = true
362AND deleted_at IS NULL
363"#,
364    )
365    .fetch_all(conn)
366    .await?;
367    Ok(res)
368}
369
370pub async fn remove_default_chatbot_from_course(
371    conn: &mut PgConnection,
372    course_id: Uuid,
373) -> ModelResult<()> {
374    sqlx::query!(
375        r#"
376UPDATE chatbot_configurations
377SET default_chatbot = false
378WHERE course_id = $1
379AND default_chatbot = true
380AND deleted_at IS NULL
381"#,
382        course_id,
383    )
384    .execute(conn)
385    .await?;
386    Ok(())
387}
388
389pub async fn set_default_chatbot_for_course(
390    conn: &mut PgConnection,
391    chatbot_configuration_id: Uuid,
392) -> ModelResult<ChatbotConfiguration> {
393    let res = sqlx::query_as!(
394        ChatbotConfiguration,
395        r#"
396UPDATE chatbot_configurations
397SET default_chatbot = TRUE
398WHERE id = $1
399  AND deleted_at IS NULL
400RETURNING *
401"#,
402        chatbot_configuration_id,
403    )
404    .fetch_one(conn)
405    .await?;
406    Ok(res)
407}