headless_lms_server/controllers/
mock_azure.rs

1use headless_lms_chatbot::{
2    cms_ai_suggestion::USER_PROMPT_PREFIX,
3    llm_utils::{APIMessageKind, AzureCompletionRequest},
4    message_suggestion::USER_PROMPT,
5};
6
7use crate::prelude::*;
8
9const COMPLETION: &str = r#"
10data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","context":{"citations":[{"content": "This chunk is a snippet from page {} of the course {}. ,|||,Mock test page content\n This is test content blah.", "title": "Cited course page", "url": "http://project-331.local/org/uh-mathstat/courses/advanced-chatbot-course/chapter-1", "filepath": "null", "chunk_id": "0"}, {"content": "This chunk is a snippet from page {} of the course {}. ,|||,Mock test page content 2\n This is another test page.", "title": "Cited course page 2", "url": "http://project-331.local/org/uh-mathstat/courses/advanced-chatbot-course", "filepath": "null", "chunk_id": "0"},{"content": "This chunk is a snippet from page {} of the course {}. ,|||,More content on the same mock course page. Another snippet. Long. More content on the same mock course page. Another snippet. Long. More content on the same mock course page. Another snippet. Long. More content on the same mock course page. Another snippet. Long. More content on the same mock course page. Another snippet. Long. More content on the same mock course page. Another snippet. Long. More content on the same mock course page. Another snippet. Long. More content on the same mock course page. Another snippet. Long. More content on the same mock course page. Another snippet. Long. More content on the same mock course page. Another snippet. Long. More content on the same mock course page. Another snippet. Long.", "title": "Cited course page", "url": "http://project-331.local/org/uh-mathstat/courses/advanced-chatbot-course/chapter-1", "filepath": "null", "chunk_id": "0"}],"intent":"[]"}},"end_turn":false,"finish_reason":null}]}
11
12data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Hello"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
13
14data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":"!"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
15
16data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":" How"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
17
18data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":" can"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
19
20data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":" I"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
21
22data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":" assist"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
23
24data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":" [doc1]"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
25
26data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":" you"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
27
28data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":" [doc2]"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
29
30data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":" today"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
31
32data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":"?"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
33
34data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{"content":" [doc3]"},"end_turn":false,"finish_reason":null}],"system_fingerprint":"mock_fingerprint"}
35
36data: {"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"delta":{},"end_turn":true,"finish_reason":"stop"}],"system_fingerprint":"mock_fingerprint"}
37
38data: [DONE]"#;
39
40const SUGGESTION: &str = r#"
41{"id":"mock_id","model":"gpt-4o","created":1757592280,  "object":"extensions.chat.completion.chunk","choices":[{"index":0,"message":{"role":"assistant","content": "{\"suggestions\":[\"Can you pls help me?\",\"Nice weather we're having.\",\"Hello?\"]}"},"end_turn": true,"finish_reason":"stop"}]}"#;
42
43const CMS_SUGGESTION: &str = r#"{"id":"mock_id","model":"gpt-4o","created":1757592280,"object":"extensions.chat.completion.chunk","choices":[{"index":0,"message":{"role":"assistant","content":"{\"suggestions\":[\"Mock suggestion 1: The paragraph has been improved.\",\"Mock suggestion 2: Here is an alternative version of the paragraph.\",\"Mock suggestion 3: A third distinct rewrite of the paragraph.\"]}"},"end_turn":true,"finish_reason":"stop"}]}"#;
44
45// GET /api/v0/mock_azure/test/{deployment_name}/chat/completions
46// POST /api/v0/mock_azure/test/{deployment_name}/chat/completions
47async fn mock_azure_chat_completions(
48    app_conf: web::Data<ApplicationConfiguration>,
49    payload: web::Json<AzureCompletionRequest>,
50) -> ControllerResult<String> {
51    assert!(app_conf.test_chatbot && app_conf.test_mode);
52    let message_suggestion_user_prompt = USER_PROMPT;
53
54    let message_kind = &payload
55        .base
56        .messages
57        .last()
58        .ok_or(ControllerError::new(
59            ControllerErrorType::BadRequest,
60            "No messages in request, there should be at least one.",
61            None,
62        ))?
63        .fields;
64    let message = match message_kind {
65        APIMessageKind::Text(m) => Ok(m.content.to_owned()),
66        _ => Err(ControllerError::new(
67            ControllerErrorType::BadRequest,
68            "The request had a tool call or tool response. This shouldn't happen when using message suggestion LLM.",
69            None,
70        )),
71    }?;
72
73    let suggest_prompt_match = message
74        .matches(message_suggestion_user_prompt)
75        .collect::<Vec<&str>>();
76    let cms_suggest_match = message.contains(USER_PROMPT_PREFIX);
77    let res = if !suggest_prompt_match.is_empty() {
78        SUGGESTION.to_string()
79    } else if cms_suggest_match {
80        CMS_SUGGESTION.to_string()
81    } else {
82        COMPLETION.to_string()
83    };
84    let token = skip_authorize();
85    token.authorized_ok(res)
86}
87
88pub fn _add_routes(cfg: &mut ServiceConfig) {
89    cfg.route(
90        "/test/{deployment_name}/chat/completions",
91        web::get().to(mock_azure_chat_completions),
92    )
93    .route(
94        "/test/{deployment_name}/chat/completions",
95        web::post().to(mock_azure_chat_completions),
96    );
97}