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