headless_lms_server/controllers/course_material/
chatbot.rs1use actix_web::http::header::ContentType;
2use chrono::Utc;
3
4use headless_lms_chatbot::azure_chatbot::send_chat_request_and_parse_stream;
5use headless_lms_chatbot::llm_utils::estimate_tokens;
6use headless_lms_models::chatbot_conversations::{
7 self, ChatbotConversation, ChatbotConversationInfo,
8};
9
10use crate::prelude::*;
11
12#[instrument(skip(pool))]
18async fn get_default_chatbot_configuration_for_course(
19 pool: web::Data<PgPool>,
20 course_id: web::Path<Uuid>,
21) -> ControllerResult<web::Json<Option<Uuid>>> {
22 let token = skip_authorize();
23
24 let mut conn = pool.acquire().await?;
25 let chatbot_configurations =
26 models::chatbot_configurations::get_for_course(&mut conn, *course_id).await?;
27
28 let res = chatbot_configurations
29 .into_iter()
30 .filter(|c| c.enabled_to_students)
31 .find(|c| c.default_chatbot)
32 .map(|c| c.id);
33
34 token.authorized_ok(web::Json(res))
35}
36
37#[instrument(skip(pool, app_conf))]
43async fn send_message(
44 pool: web::Data<PgPool>,
45 params: web::Path<(Uuid, Uuid)>,
46 user: AuthUser,
47 app_conf: web::Data<ApplicationConfiguration>,
48 payload: web::Json<String>,
49) -> ControllerResult<HttpResponse> {
50 let message = payload.into_inner();
51 let chatbot_configuration_id = params.0;
52 let conversation_id = params.1;
53 let mut conn = pool.acquire().await?;
54 let mut tx: sqlx::Transaction<Postgres> = conn.begin().await?;
55 let token = skip_authorize();
56
57 let response_stream = send_chat_request_and_parse_stream(
58 &mut tx,
59 pool.get_ref().clone(),
61 &app_conf,
62 chatbot_configuration_id,
63 conversation_id,
64 &message,
65 )
66 .await?;
67
68 tx.commit().await?;
69
70 token.authorized_ok(
71 HttpResponse::Ok()
72 .content_type(ContentType::json())
73 .streaming(response_stream),
74 )
75}
76
77#[instrument(skip(pool))]
83async fn new_conversation(
84 pool: web::Data<PgPool>,
85 user: AuthUser,
86 params: web::Path<Uuid>,
87) -> ControllerResult<web::Json<ChatbotConversation>> {
88 let token = skip_authorize();
89
90 let mut conn = pool.acquire().await?;
91 let mut tx = conn.begin().await?;
92
93 let configuration = models::chatbot_configurations::get_by_id(&mut tx, *params).await?;
94
95 let conversation = models::chatbot_conversations::insert(
96 &mut tx,
97 ChatbotConversation {
98 id: Uuid::new_v4(),
99 created_at: Utc::now(),
100 updated_at: Utc::now(),
101 deleted_at: None,
102 course_id: configuration.course_id,
103 user_id: user.id,
104 chatbot_configuration_id: configuration.id,
105 },
106 )
107 .await?;
108
109 let _first_message = models::chatbot_conversation_messages::insert(
110 &mut tx,
111 models::chatbot_conversation_messages::ChatbotConversationMessage {
112 id: Uuid::new_v4(),
113 created_at: Utc::now(),
114 updated_at: Utc::now(),
115 deleted_at: None,
116 conversation_id: conversation.id,
117 message: Some(configuration.initial_message.clone()),
118 is_from_chatbot: true,
119 message_is_complete: true,
120 used_tokens: estimate_tokens(&configuration.initial_message),
121 order_number: 0,
122 },
123 )
124 .await?;
125
126 tx.commit().await?;
127
128 token.authorized_ok(web::Json(conversation))
129}
130
131#[instrument(skip(pool))]
137async fn current_conversation_info(
138 pool: web::Data<PgPool>,
139 user: AuthUser,
140 params: web::Path<Uuid>,
141) -> ControllerResult<web::Json<ChatbotConversationInfo>> {
142 let token = skip_authorize();
143
144 let mut conn = pool.acquire().await?;
145 let chatbot_configuration =
146 models::chatbot_configurations::get_by_id(&mut conn, *params).await?;
147 let res = chatbot_conversations::get_current_conversation_info(
148 &mut conn,
149 user.id,
150 chatbot_configuration.id,
151 )
152 .await?;
153
154 token.authorized_ok(web::Json(res))
155}
156
157pub fn _add_routes(cfg: &mut ServiceConfig) {
165 cfg.route(
166 "/{chatbot_configuration_id}/conversations/{conversation_id}/send-message",
167 web::post().to(send_message),
168 )
169 .route(
170 "/{chatbot_configuration_id}/conversations/current",
171 web::get().to(current_conversation_info),
172 )
173 .route(
174 "/{chatbot_configuration_id}/conversations/new",
175 web::post().to(new_conversation),
176 )
177 .route(
178 "/default-for-course/{course_id}",
179 web::get().to(get_default_chatbot_configuration_for_course),
180 );
181}