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}