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    Minimal,
9    Low,
10    Medium,
11    High,
12}
13
14#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Type, ToSchema)]
15#[sqlx(type_name = "verbosity_level", rename_all = "snake_case")]
16#[serde(rename_all = "snake_case")]
17pub enum VerbosityLevel {
18    Low,
19    Medium,
20    High,
21}
22
23#[derive(Clone, PartialEq, Deserialize, Serialize, ToSchema)]
24
25pub struct ChatbotConfiguration {
26    pub id: Uuid,
27    pub created_at: DateTime<Utc>,
28    pub updated_at: DateTime<Utc>,
29    pub deleted_at: Option<DateTime<Utc>>,
30    pub course_id: Uuid,
31    pub enabled_to_students: bool,
32    pub chatbot_name: String,
33    pub model_id: Uuid,
34    pub thinking_model: bool,
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 response_max_tokens: i32,
40    pub temperature: f32,
41    pub top_p: f32,
42    pub frequency_penalty: f32,
43    pub presence_penalty: f32,
44    pub max_completion_tokens: i32,
45    pub verbosity: VerbosityLevel,
46    pub reasoning_effort: ReasoningEffortLevel,
47    pub use_azure_search: bool,
48    pub maintain_azure_search_index: bool,
49    pub hide_citations: bool,
50    pub use_semantic_reranking: bool,
51    pub use_tools: bool,
52    pub default_chatbot: bool,
53    pub suggest_next_messages: bool,
54    pub initial_suggested_messages: Option<Vec<String>>,
55}
56
57impl Default for ChatbotConfiguration {
58    fn default() -> Self {
59        Self {
60            id: Uuid::nil(),
61            created_at: Default::default(),
62            updated_at: Default::default(),
63            deleted_at: None,
64            course_id: Default::default(),
65            enabled_to_students: false,
66            chatbot_name: Default::default(),
67            model_id: Uuid::nil(),
68            thinking_model: false,
69            prompt: Default::default(),
70            initial_message: Default::default(),
71            weekly_tokens_per_user: 20000 * 5,
72            daily_tokens_per_user: 20000,
73            response_max_tokens: 500,
74            temperature: 0.7,
75            top_p: 1.0,
76            frequency_penalty: 0.0,
77            presence_penalty: 0.0,
78            max_completion_tokens: 600,
79            reasoning_effort: ReasoningEffortLevel::Minimal,
80            verbosity: VerbosityLevel::Medium,
81            use_azure_search: false,
82            maintain_azure_search_index: false,
83            hide_citations: false,
84            use_semantic_reranking: false,
85            use_tools: false,
86            default_chatbot: false,
87            suggest_next_messages: false,
88            initial_suggested_messages: None,
89        }
90    }
91}
92
93#[derive(Clone, PartialEq, Deserialize, Serialize, Debug, ToSchema)]
94
95pub struct NewChatbotConf {
96    pub course_id: Uuid,
97    pub enabled_to_students: bool,
98    pub chatbot_name: String,
99    pub model_id: Uuid,
100    pub thinking_model: bool,
101    pub prompt: String,
102    pub initial_message: String,
103    pub weekly_tokens_per_user: i32,
104    pub daily_tokens_per_user: i32,
105    pub response_max_tokens: i32,
106    pub temperature: f32,
107    pub top_p: f32,
108    pub frequency_penalty: f32,
109    pub presence_penalty: f32,
110    pub max_completion_tokens: i32,
111    pub verbosity: VerbosityLevel,
112    pub reasoning_effort: ReasoningEffortLevel,
113    pub use_azure_search: bool,
114    pub maintain_azure_search_index: bool,
115    pub hide_citations: bool,
116    pub use_semantic_reranking: bool,
117    pub use_tools: bool,
118    pub default_chatbot: bool,
119    pub chatbotconf_id: Option<Uuid>,
120    pub suggest_next_messages: bool,
121    pub initial_suggested_messages: Option<Vec<String>>,
122}
123
124impl Default for NewChatbotConf {
125    fn default() -> Self {
126        let chatbot_conf: ChatbotConfiguration = ChatbotConfiguration::default();
127        Self {
128            course_id: chatbot_conf.course_id,
129            enabled_to_students: chatbot_conf.enabled_to_students,
130            chatbot_name: chatbot_conf.chatbot_name,
131            model_id: chatbot_conf.model_id,
132            thinking_model: chatbot_conf.thinking_model,
133            prompt: chatbot_conf.prompt,
134            initial_message: chatbot_conf.initial_message,
135            weekly_tokens_per_user: chatbot_conf.weekly_tokens_per_user,
136            daily_tokens_per_user: chatbot_conf.daily_tokens_per_user,
137            response_max_tokens: chatbot_conf.response_max_tokens,
138            temperature: chatbot_conf.temperature,
139            top_p: chatbot_conf.top_p,
140            frequency_penalty: chatbot_conf.frequency_penalty,
141            presence_penalty: chatbot_conf.presence_penalty,
142            max_completion_tokens: chatbot_conf.max_completion_tokens,
143            verbosity: chatbot_conf.verbosity,
144            reasoning_effort: chatbot_conf.reasoning_effort,
145            use_azure_search: chatbot_conf.use_azure_search,
146            maintain_azure_search_index: chatbot_conf.maintain_azure_search_index,
147            hide_citations: chatbot_conf.hide_citations,
148            use_semantic_reranking: chatbot_conf.use_semantic_reranking,
149            use_tools: chatbot_conf.use_tools,
150            default_chatbot: chatbot_conf.default_chatbot,
151            chatbotconf_id: None,
152            suggest_next_messages: chatbot_conf.suggest_next_messages,
153            initial_suggested_messages: chatbot_conf.initial_suggested_messages,
154        }
155    }
156}
157
158pub async fn get_by_id(conn: &mut PgConnection, id: Uuid) -> ModelResult<ChatbotConfiguration> {
159    let res = sqlx::query_as!(
160        ChatbotConfiguration,
161        r#"
162SELECT
163    id,
164    created_at,
165    updated_at,
166    deleted_at,
167    course_id,
168    enabled_to_students,
169    chatbot_name,
170    model_id,
171    thinking_model,
172    prompt,
173    initial_message,
174    weekly_tokens_per_user,
175    daily_tokens_per_user,
176    temperature,
177    top_p,
178    frequency_penalty,
179    presence_penalty,
180    response_max_tokens,
181    max_completion_tokens,
182    use_azure_search,
183    maintain_azure_search_index,
184    hide_citations,
185    use_semantic_reranking,
186    use_tools,
187    default_chatbot,
188    suggest_next_messages,
189    verbosity as "verbosity: VerbosityLevel",
190    reasoning_effort as "reasoning_effort: ReasoningEffortLevel",
191    initial_suggested_messages
192FROM chatbot_configurations
193WHERE id = $1
194AND deleted_at IS NULL
195        "#,
196        id
197    )
198    .fetch_one(conn)
199    .await?;
200    Ok(res)
201}
202
203pub async fn insert(
204    conn: &mut PgConnection,
205    pkey_policy: PKeyPolicy<Uuid>,
206    input: NewChatbotConf,
207) -> ModelResult<ChatbotConfiguration> {
208    let maintain_azure_search_index = input.use_azure_search;
209    let res = sqlx::query_as!(
210        ChatbotConfiguration,
211        r#"
212INSERT INTO chatbot_configurations (
213    id,
214    course_id,
215    enabled_to_students,
216    chatbot_name,
217    model_id,
218    thinking_model,
219    prompt,
220    initial_message,
221    weekly_tokens_per_user,
222    daily_tokens_per_user,
223    temperature,
224    top_p,
225    hide_citations,
226    frequency_penalty,
227    presence_penalty,
228    max_completion_tokens,
229    verbosity,
230    reasoning_effort,
231    response_max_tokens,
232    use_azure_search,
233    use_tools,
234    maintain_azure_search_index,
235    default_chatbot,
236    suggest_next_messages,
237    initial_suggested_messages
238  )
239VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)
240RETURNING
241    id,
242    created_at,
243    updated_at,
244    deleted_at,
245    course_id,
246    enabled_to_students,
247    chatbot_name,
248    model_id,
249    thinking_model,
250    prompt,
251    initial_message,
252    weekly_tokens_per_user,
253    daily_tokens_per_user,
254    temperature,
255    top_p,
256    frequency_penalty,
257    presence_penalty,
258    response_max_tokens,
259    max_completion_tokens,
260    use_azure_search,
261    maintain_azure_search_index,
262    hide_citations,
263    use_semantic_reranking,
264    use_tools,
265    default_chatbot,
266    suggest_next_messages,
267    verbosity as "verbosity: VerbosityLevel",
268    reasoning_effort as "reasoning_effort: ReasoningEffortLevel",
269    initial_suggested_messages
270        "#,
271        pkey_policy.into_uuid(),
272        input.course_id,
273        input.enabled_to_students,
274        input.chatbot_name,
275        input.model_id,
276        input.thinking_model,
277        input.prompt,
278        input.initial_message,
279        input.weekly_tokens_per_user,
280        input.daily_tokens_per_user,
281        input.temperature,
282        input.top_p,
283        input.hide_citations,
284        input.frequency_penalty,
285        input.presence_penalty,
286        input.max_completion_tokens,
287        input.verbosity as VerbosityLevel,
288        input.reasoning_effort as ReasoningEffortLevel,
289        input.response_max_tokens,
290        input.use_azure_search,
291        input.use_tools,
292        maintain_azure_search_index,
293        input.default_chatbot,
294        input.suggest_next_messages,
295        input.initial_suggested_messages.as_deref()
296    )
297    .fetch_one(conn)
298    .await?;
299    Ok(res)
300}
301
302pub async fn edit(
303    conn: &mut PgConnection,
304    input: NewChatbotConf,
305    chatbot_configuration_id: Uuid,
306) -> ModelResult<ChatbotConfiguration> {
307    let res = sqlx::query_as!(
308        ChatbotConfiguration,
309        r#"
310UPDATE chatbot_configurations
311SET
312    enabled_to_students = $1,
313    chatbot_name = $2,
314    prompt = $3,
315    initial_message = $4,
316    weekly_tokens_per_user = $5,
317    daily_tokens_per_user = $6,
318    temperature = $7,
319    top_p = $8,
320    frequency_penalty = $9,
321    presence_penalty = $10,
322    response_max_tokens = $11,
323    use_azure_search = $12,
324    maintain_azure_search_index = $13,
325    hide_citations = $14,
326    use_semantic_reranking = $15,
327    default_chatbot = $16,
328    model_id = $17,
329    thinking_model = $18,
330    max_completion_tokens = $19,
331    verbosity = $20,
332    reasoning_effort = $21,
333    use_tools = $22,
334    suggest_next_messages = $23,
335    initial_suggested_messages = $24
336WHERE id = $25
337    AND deleted_at IS NULL
338RETURNING
339    id,
340    created_at,
341    updated_at,
342    deleted_at,
343    course_id,
344    enabled_to_students,
345    chatbot_name,
346    model_id,
347    thinking_model,
348    prompt,
349    initial_message,
350    weekly_tokens_per_user,
351    daily_tokens_per_user,
352    temperature,
353    top_p,
354    frequency_penalty,
355    presence_penalty,
356    response_max_tokens,
357    max_completion_tokens,
358    use_azure_search,
359    maintain_azure_search_index,
360    hide_citations,
361    use_semantic_reranking,
362    default_chatbot,
363    suggest_next_messages,
364    verbosity as "verbosity: VerbosityLevel",
365    reasoning_effort as "reasoning_effort: ReasoningEffortLevel",
366    use_tools,
367    initial_suggested_messages
368"#,
369        input.enabled_to_students,
370        input.chatbot_name,
371        input.prompt,
372        input.initial_message,
373        input.weekly_tokens_per_user,
374        input.daily_tokens_per_user,
375        input.temperature,
376        input.top_p,
377        input.frequency_penalty,
378        input.presence_penalty,
379        input.response_max_tokens,
380        input.use_azure_search,
381        input.maintain_azure_search_index,
382        input.hide_citations,
383        input.use_semantic_reranking,
384        input.default_chatbot,
385        input.model_id,
386        input.thinking_model,
387        input.max_completion_tokens,
388        input.verbosity as VerbosityLevel,
389        input.reasoning_effort as ReasoningEffortLevel,
390        input.use_tools,
391        input.suggest_next_messages,
392        input.initial_suggested_messages.as_deref(),
393        chatbot_configuration_id
394    )
395    .fetch_one(conn)
396    .await?;
397    Ok(res)
398}
399
400pub async fn delete(conn: &mut PgConnection, chatbot_configuration_id: Uuid) -> ModelResult<()> {
401    sqlx::query!(
402        r#"
403UPDATE chatbot_configurations
404SET deleted_at = now()
405WHERE id = $1
406AND deleted_at IS NULL
407        "#,
408        chatbot_configuration_id
409    )
410    .execute(conn)
411    .await?;
412    Ok(())
413}
414
415pub async fn get_for_course(
416    conn: &mut PgConnection,
417    course_id: Uuid,
418) -> ModelResult<Vec<ChatbotConfiguration>> {
419    let res = sqlx::query_as!(
420        ChatbotConfiguration,
421        r#"
422SELECT
423    id,
424    created_at,
425    updated_at,
426    deleted_at,
427    course_id,
428    enabled_to_students,
429    chatbot_name,
430    model_id,
431    thinking_model,
432    prompt,
433    initial_message,
434    weekly_tokens_per_user,
435    daily_tokens_per_user,
436    temperature,
437    top_p,
438    frequency_penalty,
439    presence_penalty,
440    response_max_tokens,
441    max_completion_tokens,
442    use_azure_search,
443    maintain_azure_search_index,
444    hide_citations,
445    use_semantic_reranking,
446    use_tools,
447    default_chatbot,
448    suggest_next_messages,
449    verbosity as "verbosity: VerbosityLevel",
450    reasoning_effort as "reasoning_effort: ReasoningEffortLevel",
451    initial_suggested_messages
452FROM chatbot_configurations
453WHERE course_id = $1
454AND deleted_at IS NULL
455"#,
456        course_id
457    )
458    .fetch_all(conn)
459    .await?;
460    Ok(res)
461}
462
463pub async fn get_enabled_nondefault_for_course(
464    conn: &mut PgConnection,
465    course_id: Uuid,
466) -> ModelResult<Vec<ChatbotConfiguration>> {
467    let res = sqlx::query_as!(
468        ChatbotConfiguration,
469        r#"
470SELECT
471    id,
472    created_at,
473    updated_at,
474    deleted_at,
475    course_id,
476    enabled_to_students,
477    chatbot_name,
478    model_id,
479    thinking_model,
480    prompt,
481    initial_message,
482    weekly_tokens_per_user,
483    daily_tokens_per_user,
484    temperature,
485    top_p,
486    frequency_penalty,
487    presence_penalty,
488    response_max_tokens,
489    max_completion_tokens,
490    use_azure_search,
491    maintain_azure_search_index,
492    hide_citations,
493    use_semantic_reranking,
494    use_tools,
495    default_chatbot,
496    suggest_next_messages,
497    verbosity as "verbosity: VerbosityLevel",
498    reasoning_effort as "reasoning_effort: ReasoningEffortLevel",
499    initial_suggested_messages
500FROM chatbot_configurations
501WHERE course_id = $1
502AND default_chatbot IS false
503AND enabled_to_students IS true
504AND deleted_at IS NULL
505"#,
506        course_id
507    )
508    .fetch_all(conn)
509    .await?;
510    Ok(res)
511}
512
513pub async fn get_for_azure_search_maintenance(
514    conn: &mut PgConnection,
515) -> ModelResult<Vec<ChatbotConfiguration>> {
516    let res = sqlx::query_as!(
517        ChatbotConfiguration,
518        r#"
519SELECT
520    id,
521    created_at,
522    updated_at,
523    deleted_at,
524    course_id,
525    enabled_to_students,
526    chatbot_name,
527    model_id,
528    thinking_model,
529    prompt,
530    initial_message,
531    weekly_tokens_per_user,
532    daily_tokens_per_user,
533    temperature,
534    top_p,
535    frequency_penalty,
536    presence_penalty,
537    response_max_tokens,
538    max_completion_tokens,
539    use_azure_search,
540    maintain_azure_search_index,
541    hide_citations,
542    use_semantic_reranking,
543    use_tools,
544    default_chatbot,
545    suggest_next_messages,
546    verbosity as "verbosity: VerbosityLevel",
547    reasoning_effort as "reasoning_effort: ReasoningEffortLevel",
548    initial_suggested_messages
549FROM chatbot_configurations
550WHERE maintain_azure_search_index = true
551AND deleted_at IS NULL
552"#,
553    )
554    .fetch_all(conn)
555    .await?;
556    Ok(res)
557}
558
559pub async fn remove_default_chatbot_from_course(
560    conn: &mut PgConnection,
561    course_id: Uuid,
562) -> ModelResult<()> {
563    sqlx::query!(
564        r#"
565UPDATE chatbot_configurations
566SET default_chatbot = false
567WHERE course_id = $1
568AND default_chatbot = true
569AND deleted_at IS NULL
570"#,
571        course_id,
572    )
573    .execute(conn)
574    .await?;
575    Ok(())
576}
577
578pub async fn set_default_chatbot_for_course(
579    conn: &mut PgConnection,
580    chatbot_configuration_id: Uuid,
581) -> ModelResult<ChatbotConfiguration> {
582    let res = sqlx::query_as!(
583        ChatbotConfiguration,
584        r#"
585UPDATE chatbot_configurations
586SET default_chatbot = true
587WHERE id = $1
588RETURNING
589    id,
590    created_at,
591    updated_at,
592    deleted_at,
593    course_id,
594    enabled_to_students,
595    chatbot_name,
596    model_id,
597    thinking_model,
598    prompt,
599    initial_message,
600    weekly_tokens_per_user,
601    daily_tokens_per_user,
602    temperature,
603    top_p,
604    frequency_penalty,
605    presence_penalty,
606    response_max_tokens,
607    max_completion_tokens,
608    use_azure_search,
609    maintain_azure_search_index,
610    hide_citations,
611    use_semantic_reranking,
612    use_tools,
613    default_chatbot,
614    suggest_next_messages,
615    verbosity as "verbosity: VerbosityLevel",
616    reasoning_effort as "reasoning_effort: ReasoningEffortLevel",
617    initial_suggested_messages
618"#,
619        chatbot_configuration_id,
620    )
621    .fetch_one(conn)
622    .await?;
623    Ok(res)
624}