headless_lms_server/controllers/cms/
ai_suggestions.rs1use headless_lms_models::application_task_default_language_models::{self, ApplicationTask};
3use headless_lms_models::cms_ai::ParagraphSuggestionAction;
4
5use crate::prelude::*;
6
7#[derive(Debug, Serialize, Deserialize)]
8#[cfg_attr(feature = "ts_rs", derive(TS))]
9pub struct ParagraphSuggestionMeta {
10 pub tone: Option<String>,
11 pub language: Option<String>,
12 pub setting_type: Option<String>,
13}
14
15#[derive(Debug, Serialize, Deserialize)]
16#[cfg_attr(feature = "ts_rs", derive(TS))]
17pub struct ParagraphSuggestionContext {
18 pub page_id: Option<Uuid>,
19 pub course_id: Option<Uuid>,
20 pub locale: Option<String>,
21}
22
23#[derive(Debug, Serialize, Deserialize)]
24#[cfg_attr(feature = "ts_rs", derive(TS))]
25pub struct ParagraphSuggestionRequest {
26 pub action: ParagraphSuggestionAction,
27 pub content: String,
28 pub is_html: bool,
29 pub meta: Option<ParagraphSuggestionMeta>,
30 pub context: Option<ParagraphSuggestionContext>,
31}
32
33#[derive(Serialize, Deserialize)]
34#[cfg_attr(feature = "ts_rs", derive(TS))]
35pub struct ParagraphSuggestionResponse {
36 pub suggestions: Vec<String>,
37}
38
39#[instrument(skip(pool, app_conf))]
47async fn suggest_paragraph(
48 pool: web::Data<PgPool>,
49 app_conf: web::Data<ApplicationConfiguration>,
50 user: AuthUser,
51 payload: web::Json<ParagraphSuggestionRequest>,
52) -> ControllerResult<web::Json<ParagraphSuggestionResponse>> {
53 let mut conn = pool.acquire().await?;
54
55 if payload.content.trim().is_empty() {
57 return Err(ControllerError::new(
58 ControllerErrorType::BadRequest,
59 "Paragraph content must not be empty.".to_string(),
60 None,
61 ));
62 }
63
64 let token = if let Some(ParagraphSuggestionContext {
67 page_id: Some(page_id),
68 ..
69 }) = &payload.context
70 {
71 authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?
72 } else {
73 authorize(&mut conn, Act::Teach, Some(user.id), Res::AnyCourse).await?
74 };
75
76 let task_lm = application_task_default_language_models::get_for_task(
77 &mut conn,
78 ApplicationTask::CmsParagraphSuggestion,
79 )
80 .await?;
81
82 let meta = payload.meta.as_ref();
83 let generator_input = headless_lms_chatbot::cms_ai_suggestion::CmsParagraphSuggestionInput {
84 action: payload.action,
85 content: payload.content.clone(),
86 is_html: payload.is_html,
87 meta_tone: meta.and_then(|m| m.tone.clone()),
88 meta_language: meta.and_then(|m| m.language.clone()),
89 meta_setting_type: meta.and_then(|m| m.setting_type.clone()),
90 };
91
92 drop(conn);
94
95 let suggestions = headless_lms_chatbot::cms_ai_suggestion::generate_paragraph_suggestions(
96 &app_conf,
97 task_lm,
98 &generator_input,
99 )
100 .await?;
101
102 token.authorized_ok(web::Json(ParagraphSuggestionResponse { suggestions }))
103}
104
105pub fn _add_routes(cfg: &mut ServiceConfig) {
113 cfg.route("/paragraph", web::post().to(suggest_paragraph));
114}