headless_lms_models/
chatbot_configurations.rs

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