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_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 .find(|c| c.enabled_to_students)
31 .map(|c| c.id);
32
33 token.authorized_ok(web::Json(res))
34}
35
36#[instrument(skip(pool, app_conf))]
42async fn send_message(
43 pool: web::Data<PgPool>,
44 params: web::Path<(Uuid, Uuid)>,
45 user: AuthUser,
46 app_conf: web::Data<ApplicationConfiguration>,
47 payload: web::Json<String>,
48) -> ControllerResult<HttpResponse> {
49 let message = payload.into_inner();
50 let chatbot_configuration_id = params.0;
51 let conversation_id = params.1;
52 let mut conn = pool.acquire().await?;
53 let mut tx: sqlx::Transaction<Postgres> = conn.begin().await?;
54 let token = skip_authorize();
55
56 let response_stream = send_chat_request_and_parse_stream(
57 &mut tx,
58 pool.get_ref().clone(),
60 &app_conf,
61 chatbot_configuration_id,
62 conversation_id,
63 &message,
64 )
65 .await?;
66
67 tx.commit().await?;
68
69 token.authorized_ok(
70 HttpResponse::Ok()
71 .content_type(ContentType::json())
72 .streaming(response_stream),
73 )
74}
75
76#[instrument(skip(pool))]
82async fn new_conversation(
83 pool: web::Data<PgPool>,
84 user: AuthUser,
85 params: web::Path<Uuid>,
86) -> ControllerResult<web::Json<ChatbotConversation>> {
87 let token = skip_authorize();
88
89 let mut conn = pool.acquire().await?;
90 let mut tx = conn.begin().await?;
91
92 let configuration = models::chatbot_configurations::get_by_id(&mut tx, *params).await?;
93
94 let conversation = models::chatbot_conversations::insert(
95 &mut tx,
96 ChatbotConversation {
97 id: Uuid::new_v4(),
98 created_at: Utc::now(),
99 updated_at: Utc::now(),
100 deleted_at: None,
101 course_id: configuration.course_id,
102 user_id: user.id,
103 chatbot_configuration_id: configuration.id,
104 },
105 )
106 .await?;
107
108 let _first_message = models::chatbot_conversation_messages::insert(
109 &mut tx,
110 models::chatbot_conversation_messages::ChatbotConversationMessage {
111 id: Uuid::new_v4(),
112 created_at: Utc::now(),
113 updated_at: Utc::now(),
114 deleted_at: None,
115 conversation_id: conversation.id,
116 message: Some(configuration.initial_message.clone()),
117 is_from_chatbot: true,
118 message_is_complete: true,
119 used_tokens: estimate_tokens(&configuration.initial_message),
120 order_number: 0,
121 },
122 )
123 .await?;
124
125 tx.commit().await?;
126
127 token.authorized_ok(web::Json(conversation))
128}
129
130#[instrument(skip(pool))]
136async fn current_conversation_info(
137 pool: web::Data<PgPool>,
138 user: AuthUser,
139 params: web::Path<Uuid>,
140) -> ControllerResult<web::Json<ChatbotConversationInfo>> {
141 let token = skip_authorize();
142
143 let mut conn = pool.acquire().await?;
144 let chatbot_configuration =
145 models::chatbot_configurations::get_by_id(&mut conn, *params).await?;
146 let res = chatbot_conversations::get_current_conversation_info(
147 &mut conn,
148 user.id,
149 chatbot_configuration.id,
150 )
151 .await?;
152
153 token.authorized_ok(web::Json(res))
154}
155
156pub fn _add_routes(cfg: &mut ServiceConfig) {
164 cfg.route(
165 "/{chatbot_configuration_id}/conversations/{conversation_id}/send-message",
166 web::post().to(send_message),
167 )
168 .route(
169 "/{chatbot_configuration_id}/conversations/current",
170 web::get().to(current_conversation_info),
171 )
172 .route(
173 "/{chatbot_configuration_id}/conversations/new",
174 web::post().to(new_conversation),
175 )
176 .route(
177 "/for-course/{course_id}",
178 web::get().to(get_chatbot_configuration_for_course),
179 );
180}