headless_lms_server/controllers/
mock_azure.rs

1use headless_lms_chatbot::{
2    llm_utils::{APIMessageKind, AzureCompletionRequest},
3    message_suggestion::USER_PROMPT,
4};
5
6use crate::prelude::*;
7
8const COMPLETION: &str = r#"
9data: {"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}]}
10
11data: {"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"}
12
13data: {"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"}
14
15data: {"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"}
16
17data: {"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"}
18
19data: {"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"}
20
21data: {"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"}
22
23data: {"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"}
24
25data: {"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"}
26
27data: {"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"}
28
29data: {"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"}
30
31data: {"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"}
32
33data: {"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"}
34
35data: {"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"}
36
37data: [DONE]"#;
38
39const SUGGESTION: &str = r#"
40{"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"}]}"#;
41
42// GET /api/v0/mock_azure/test/{deployment_name}/chat/completions
43// POST /api/v0/mock_azure/test/{deployment_name}/chat/completions
44async fn mock_azure_chat_completions(
45    app_conf: web::Data<ApplicationConfiguration>,
46    payload: web::Json<AzureCompletionRequest>,
47) -> ControllerResult<String> {
48    assert!(app_conf.test_chatbot && app_conf.test_mode);
49    let message_suggestion_user_prompt = USER_PROMPT;
50
51    let message_kind = &payload
52        .base
53        .messages
54        .last()
55        .ok_or(ControllerError::new(
56            ControllerErrorType::BadRequest,
57            "No messages in request, there should be at least one.",
58            None,
59        ))?
60        .fields;
61    let message = match message_kind {
62        APIMessageKind::Text(m) => Ok(m.content.to_owned()),
63        _ => Err(ControllerError::new(
64            ControllerErrorType::BadRequest,
65            "The request had a tool call or tool response. This shouldn't happen when using message suggestion LLM.",
66            None,
67        )),
68    }?;
69
70    let suggest_prompt_match = message
71        .matches(message_suggestion_user_prompt)
72        .collect::<Vec<&str>>();
73    let res = if !suggest_prompt_match.is_empty() {
74        SUGGESTION.to_string()
75    } else {
76        COMPLETION.to_string()
77    };
78    let token = skip_authorize();
79    token.authorized_ok(res)
80}
81
82pub fn _add_routes(cfg: &mut ServiceConfig) {
83    cfg.route(
84        "/test/{deployment_name}/chat/completions",
85        web::get().to(mock_azure_chat_completions),
86    )
87    .route(
88        "/test/{deployment_name}/chat/completions",
89        web::post().to(mock_azure_chat_completions),
90    );
91}