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}