From 9a6cc871dd05961492b249333bc0402627c84eb4 Mon Sep 17 00:00:00 2001 From: neagualexa Date: Thu, 19 Mar 2026 16:29:50 +0000 Subject: [PATCH 01/10] mued API schema for response and request --- index.py | 44 +- src/agent/agent.py | 3 +- .../utils/example_inputs/example_input_1.json | 286 ++++---- .../utils/example_inputs/example_input_2.json | 235 +++---- .../utils/example_inputs/example_input_3.json | 663 ++++++------------ .../utils/parse_json_context_to_prompt.py | 11 +- src/agent/utils/prompt_context_templates.py | 8 +- src/module.py | 203 ++++-- tests/manual_agent_run.py | 37 +- tests/test_example_inputs.py | 171 +++++ tests/test_index.py | 56 +- tests/test_module.py | 82 +-- 12 files changed, 855 insertions(+), 944 deletions(-) create mode 100644 tests/test_example_inputs.py diff --git a/index.py b/index.py index 0ad738d..d0610fe 100644 --- a/index.py +++ b/index.py @@ -1,52 +1,36 @@ import json +from pydantic import ValidationError + +from lf_toolkit.chat import ChatRequest from src.module import chat_module -from src.agent.utils.types import JsonType -def handler(event: JsonType, context): + +def handler(event, context): """ Lambda handler function """ - # Log the input event for debugging purposes - # print("Received event:", " ".join(json.dumps(event, indent=2).splitlines())) - if "body" in event: try: event = json.loads(event["body"]) except json.JSONDecodeError: return { "statusCode": 400, - "body": "Invalid JSON format in the body or body not found. Please check the input." + "body": "Invalid JSON format in the body. Please check the input.", } - if "message" not in event: - return { - "statusCode": 400, - "body": "Missing 'message' key in event. Please confirm the key in the json body." - } - if "params" not in event: - return { - "statusCode": 400, - "body": "Missing 'params' key in event. Please confirm the key in the json body. Make sure it contains the necessary conversation_id." - } - - message = event.get("message") - params = event.get("params") + try: + request = ChatRequest.model_validate(event) + except ValidationError as e: + return {"statusCode": 400, "body": e.json()} try: - chatbot_response = chat_module(message, params) + result = chat_module(request) except Exception as e: return { "statusCode": 500, - "body": f"An error occurred within the chat_module(): {str(e)}" + "body": f"An error occurred within the chat_module(): {str(e)}", } - # Create a response - response = { - "statusCode": 200, - "body": json.dumps(chatbot_response) - } - - # Log the response for debugging purposes + response = {"statusCode": 200, "body": result.model_dump_json()} print("Returning response:", " ".join(json.dumps(response, indent=2).splitlines())) - - return response \ No newline at end of file + return response diff --git a/src/agent/agent.py b/src/agent/agent.py index 31e23cb..b79dcd7 100644 --- a/src/agent/agent.py +++ b/src/agent/agent.py @@ -181,7 +181,8 @@ def invoke_base_agent(query: str, conversation_history: list, summary: str, conv print(f'in invoke_base_agent(), thread_id = {session_id}') config = {"configurable": {"thread_id": session_id, "summary": summary, "conversational_style": conversationalStyle, "question_response_details": question_response_details}} - response_events = agent.app.invoke({"messages": conversation_history, "summary": summary, "conversational_style": conversationalStyle}, config=config, stream_mode="values") #updates + messages = conversation_history + [HumanMessage(content=query)] + response_events = agent.app.invoke({"messages": messages, "summary": summary, "conversational_style": conversationalStyle}, config=config, stream_mode="values") #updates pretty_printed_response = agent.pretty_response_value(response_events) # get last event/ai answer in the response # Gather Metadata from the agent diff --git a/src/agent/utils/example_inputs/example_input_1.json b/src/agent/utils/example_inputs/example_input_1.json index 5bd8071..baebc3e 100644 --- a/src/agent/utils/example_inputs/example_input_1.json +++ b/src/agent/utils/example_inputs/example_input_1.json @@ -1,168 +1,138 @@ { - "message": "i dont remember anything", - "params": { - "include_test_data": true, - "conversation_history": [ - { "type": "user", "content": "what should I do?" }, - { - "type": "assistant", - "content": "It seems like you're currently working on Part (a) of the dot product question. Since you haven't submitted an answer yet, let's take a moment to break it down together. \n\nWhat do you remember about how to calculate the dot product of two vectors? Can you describe the steps you would take?" - }, - { "type": "user", "content": "i dont remember anything" } - ], - "summary": "", - "conversational_style": "", - "question_response_details": { - "questionSubmissionSummary": [ - { - "publishedPartId": "0e0432e4-90a2-47a1-b597-55f76596b7d5", - "publishedPartPosition": 0, - "publishedResponseAreaId": "ce28b12b-f583-4d83-a0e4-36b17127746a", - "publishedResponseAreaPosition": 0, - "responseAreaUniversalId": "f8857876-482d-411c-8ffc-734fb07712cf", - "publishedResponseAreaPreResponseText": "$\\vec{a} \\ \\cdot \\ \\vec{b} \\ =$", - "publishedResponseType": "NUMBER", - "publishedResponseConfig": null, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 - }, - { - "publishedPartId": "a74a6fef-8c94-474c-b381-8d97a4b54725", - "publishedPartPosition": 1, - "publishedResponseAreaId": "f3be55ed-af9b-4483-ba45-636ccbad7a24", - "publishedResponseAreaPosition": 0, - "responseAreaUniversalId": "054bbcce-facb-4aaa-a1d2-7bec7627a6ef", - "publishedResponseAreaPreResponseText": "$\\left(\\vec{a} - \\vec{b}\\right)\\cdot \\vec{c}\\ =$", - "publishedResponseType": "NUMBER", - "publishedResponseConfig": null, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 - }, - { - "publishedPartId": "66160c45-0554-470a-88ae-82f36e755ef6", - "publishedPartPosition": 2, - "publishedResponseAreaId": "79260525-1edb-4f19-bcdd-d827b5b692f3", - "publishedResponseAreaPosition": 0, - "responseAreaUniversalId": "bba9308c-55c7-4733-9315-37bc26fc8ec1", - "publishedResponseAreaPreResponseText": "$\\left( \\ \\vec{a} \\cdot \\vec{c} \\ \\right) \\vec{b}\\ =$", - "publishedResponseType": "MATRIX", - "publishedResponseConfig": { "cols": 1, "rows": 3 }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 - } - ], - "questionInformation": { - "questionTitle": "Dot Product", - "questionGuidance": "", - "questionContent": "$$\n\\vec{a}=\\begin{bmatrix}1 \\\\ 3\\\\ -2\\end{bmatrix} \\quad \\vec{b}=\\begin{bmatrix}0 \\\\ 3\\\\ 1\\end{bmatrix} \\quad \\vec{c}=\\begin{bmatrix} 1 \\\\\\ -1\\\\ -3\\end{bmatrix}\n$$", - "durationLowerBound": 1, - "durationUpperBound": 4, - "parts": [ + "conversationId": "9f4d40af-5f65-4f90-ac8e-9b92fea92f9f", + "messages": [ + { "role": "USER", "content": "How can I start solving this question?" } + ], + "user": { + "userId": "a6632248-578b-4976-9d42-7daea459a905", + "type": "LEARNER", + "preference": { "conversationalStyle": "" }, + "taskProgress": { + "currentQuestionId": "c5b258ec-0165-4595-a7ee-cbe0cd5dc012", + "timeSpentOnQuestion": "58 minutes", + "accessStatus": "too much time spent on this question today.", + "markedDone": "This question is still being worked on.", + "currentPart": { + "partId": "bbfcc2e4-3bf7-4016-a300-f09e3204ed8e", + "position": 0, + "timeSpentOnPart": "37 minutes", + "markedDone": "This part is not marked done.", + "responseAreas": [ { - "publishedPartId": "0e0432e4-90a2-47a1-b597-55f76596b7d5", - "publishedPartPosition": 0, - "publishedPartContent": "", - "publishedPartAnswerContent": "", - "publishedWorkedSolutionSections": [ - { - "id": "a7b1150a-05b7-4337-b902-5c6b1835cc74", - "position": 0, - "title": "", - "content": "Assuming the given basis is orthonormal, the dot product between two vectors, $\\vec{a}$ and $\\vec{b}$, can be calculated simply by multiplying their corresponding components and summing them up:\n\n \n\n$$\n\\begin{array}{rl}\\vec{a} \\cdot \\vec{b} &=\\begin{bmatrix}1 \\\\ 3\\\\ -2\\end{bmatrix}•\\begin{bmatrix}0 \\\\ 3\\\\ 1\\end{bmatrix} \\\\\\\\ &= (1 \\cdot 0) + (3 \\cdot 3) + (-2 \\cdot 1) \\\\\\\\ &= 0 + 9 + (-2) \\\\\\\\ &= 7\\end{array}\n$$\n\n \n\nTherefore, the dot product between vectors $\\vec{a}$ and $\\vec{b}$ is \\$7\\$" - } - ], - "publishedResponseAreas": [ - { - "id": "ce28b12b-f583-4d83-a0e4-36b17127746a", - "position": 0, - "universalResponseAreaId": "f8857876-482d-411c-8ffc-734fb07712cf", - "preResponseText": "$\\vec{a} \\ \\cdot \\ \\vec{b} \\ =$", - "Response": { - "id": "4d0dfa85-fc34-4649-bb6e-0f194e8a5a04", - "responseType": "NUMBER", - "config": null, - "answer": 7 - }, - "responseType": "NUMBER", - "answer": 7 - } - ] + "responseAreaId": "4ceb8d4b-b892-4388-a1b0-d80219e5b86e", + "responseType": "NUMBER", + "totalSubmissions": 1, + "wrongSubmissions": 1, + "latestSubmission": { + "submission": 1, + "feedback": "Incorrect", + "answer": "26" + } }, { - "publishedPartId": "a74a6fef-8c94-474c-b381-8d97a4b54725", - "publishedPartPosition": 1, - "publishedPartContent": "", - "publishedPartAnswerContent": "", - "publishedWorkedSolutionSections": [ - { - "id": "0e91a91d-c536-4120-a70f-5cfea57b4235", - "position": 0, - "title": "", - "content": "To calculate $\\left(\\vec{a} - \\vec{b}\\right) \\cdot \\vec{c}$ , we first need to find the vector resulting from the subtraction of $\\vec{b}$ from $\\vec{a}$:\n\n \n\n$$\n\\begin{array}{rl}\\vec{a} - \\vec{b} &= \\begin{bmatrix}1 \\\\ 3 \\\\ -2\\end{bmatrix} - \\begin{bmatrix}0 \\\\ 3 \\\\ 1\\end{bmatrix} \\\\\\\\&= \\begin{bmatrix}1 - 0 \\\\ 3 - 3 \\\\ -2 - 1\\end{bmatrix} \\\\\\\\&= \\begin{bmatrix}1 \\\\ 0 \\\\ -3\\end{bmatrix}\\end{array}\n$$\n\n\n\nNext, we can compute the dot product of $\\left(\\vec{a} - \\vec{b}\\right)$ and $\\vec{c}$\n\n \n\n$$\n\\begin{array}{rl}\\left(\\vec{a} - \\vec{b}\\right) \\cdot \\vec{c} &= \\begin{bmatrix}1 \\\\ 0 \\\\ -3\\end{bmatrix} \\cdot \\begin{bmatrix}1 \\\\ -1 \\\\ -3\\end{bmatrix} \\\\\\\\ &= (1 \\cdot 1) + (0 \\cdot -1) + (-3 \\cdot -3) \\\\\\\\&= 1 + 0 + 9 \\\\\\\\&= 10\\end{array}\n$$\n\n\n\nTherefore, $\\left(\\vec{a} - \\vec{b}\\right) \\cdot \\vec{c}$ is equal to \\$10" - } - ], - "publishedResponseAreas": [ - { - "id": "f3be55ed-af9b-4483-ba45-636ccbad7a24", - "position": 0, - "universalResponseAreaId": "054bbcce-facb-4aaa-a1d2-7bec7627a6ef", - "preResponseText": "$\\left(\\vec{a} - \\vec{b}\\right)\\cdot \\vec{c}\\ =$", - "Response": { - "id": "01edd054-b1fd-4049-8551-a8551d7a63ea", - "responseType": "NUMBER", - "config": null, - "answer": 10 - }, - "responseType": "NUMBER", - "answer": 10 - } - ] - }, - { - "publishedPartId": "66160c45-0554-470a-88ae-82f36e755ef6", - "publishedPartPosition": 2, - "publishedPartContent": "", - "publishedPartAnswerContent": "", - "publishedWorkedSolutionSections": [ - { - "id": "c3225276-7bf8-4962-a459-d9583442c26e", - "position": 0, - "title": "", - "content": "To calculate $\\left( \\ \\vec{a} \\cdot \\vec{c} \\ \\right) \\vec{b}$ , we first need to find the dot product of vectors $\\vec{a}$ and $\\vec{c}$\n\n \n\n$$\n\\begin{array}{rl}\\vec{a} \\cdot \\vec{c} &= \\begin{bmatrix}1 \\\\ 3 \\\\ -2\\end{bmatrix} \\cdot \\begin{bmatrix}1 \\\\ -1 \\\\ -3\\end{bmatrix} \\\\\\\\ &= (1 \\cdot 1) + (3 \\cdot -1) + (-2 \\cdot -3) \\\\\\\\&= 1 - 3 + 6 \\\\\\\\&= 4\\end{array}\n$$\n\n \n\nNext, we can scale $\\vec{b}$ by $\\vec{a} \\cdot \\vec{c}$\n\n \n\n$$\n\\begin{array}{rl}\\left( \\ \\vec{a} \\cdot \\vec{c} \\ \\right) \\vec{b} &= 4\\begin{bmatrix}0 \\\\ 3 \\\\ 1\\end{bmatrix} \\\\\\\\ &= \\begin{bmatrix}4\\cdot0 \\\\ 4\\cdot3 \\\\ 4\\cdot1\\end{bmatrix} \\\\\\\\&= \\begin{bmatrix}0 \\\\ 12 \\\\ 4\\end{bmatrix} \\end{array}\n$$\n\n \n" - } - ], - "publishedResponseAreas": [ - { - "id": "79260525-1edb-4f19-bcdd-d827b5b692f3", - "position": 0, - "universalResponseAreaId": "bba9308c-55c7-4733-9315-37bc26fc8ec1", - "preResponseText": "$\\left( \\ \\vec{a} \\cdot \\vec{c} \\ \\right) \\vec{b}\\ =$", - "Response": { - "id": "cc759374-175f-40be-8481-2c81e34795b6", - "responseType": "MATRIX", - "config": { "cols": 1, "rows": 3 }, - "answer": [["0"], ["12"], ["4"]] - }, - "responseType": "MATRIX", - "answer": [["0"], ["12"], ["4"]] - } - ] + "responseAreaId": "dbff8fab-b129-4a6c-a768-52f5c5923934", + "responseType": "NUMBER", + "totalSubmissions": 1, + "wrongSubmissions": 1, + "latestSubmission": { + "submission": 20, + "feedback": "Incorrect", + "answer": "53" + } } ] - }, - "questionAccessInformation": { - "estimatedMinimumTime": "1 minute", - "estimaredMaximumTime": "4 minutes", - "timeTaken": "20 minutes", - "accessStatus": "too much time spent on this question.", - "markedDone": "", - "currentPart": { - "id": "0e0432e4-90a2-47a1-b597-55f76596b7d5", - "position": 0 - } } + } + }, + "context": { + "summary": "", + "set": { + "title": "Computing", + "number": 8, + "description": "Exercises on number systems and binary arithmetic." }, - "conversation_id": "7a65b6ed-85d1-4621-8efb-4fc8e9c5a8de", - "agent_type": "base" + "question": { + "title": "Binary Numbers", + "number": 0, + "guidance": "", + "content": "", + "estimatedTime": "5-10 minutes", + "parts": [ + { + "partId": "bbfcc2e4-3bf7-4016-a300-f09e3204ed8e", + "position": 0, + "content": "Convert the following numbers to decimal: $11010_2$, $110101_2$.", + "answerContent": "$11010_2 = 26_{10}$\\n\\n$110101_2 = 53_{10}$ \\n\\n ", + "workedSolutionSections": [ + { + "id": "df588439-f183-4161-ade3-41559113e447", + "position": 0, + "title": "", + "content": "$11010_2 = (0\\\\times1) + (1\\\\times2) + (0\\\\times4) + (1\\\\times8) + (1\\\\times16) = \\\\boxed{26_{10}}$ \\n\\n***\\n\\n$110101_2 = (1\\\\times1) + (0\\\\times2) + (1\\\\times4) + (0\\\\times8) + (1\\\\times16) + (1\\\\times32) = \\\\boxed{53_{10}}$ \\n\\n " + } + ], + "structuredTutorialSections": [], + "responseAreas": [ + { + "responseAreaId": "4ceb8d4b-b892-4388-a1b0-d80219e5b86e", + "position": 0, + "responseType": "NUMBER", + "answer": 26, + "preResponseText": "$11010_2 =$" + }, + { + "responseAreaId": "dbff8fab-b129-4a6c-a768-52f5c5923934", + "position": 1, + "responseType": "NUMBER", + "answer": 53, + "preResponseText": "$110101_2$" + } + ] + }, + { + "partId": "cdf7de05-0bea-43e4-8c9a-5d47e835e6bd", + "position": 1, + "content": "Convert the following numbers to binary: $101_{10}$, $16_{10}$.", + "answerContent": "$101_{10} = 1100101_2$ \\n\\n$16_{10} = 10000_2$ \\n\\n ", + "workedSolutionSections": [ + { + "id": "aff42475-f0c0-4416-b1da-5cbae086d3f4", + "position": 0, + "title": "", + "content": "" + }, + { + "id": "3bb12dbb-f558-4df3-9db7-a31733143d71", + "position": 0, + "title": "By repeated division", + "content": "$101_{10}$ can be divided by 2 to give a quotient of 50 and a remainder of 1. The remainder is the first binary digit: $1_2$.\\n\\n***\\n\\n$50_{10}$ divided by 2 gives a quotient of 25 and a remainder of zero, so the second binary digit is 0: $01_2$.\\n\\n***\\n\\n$25_{10}$ divided by 2 gives a quotient of 12 and a remainder of 1, so the third binary digit is 1: $101_2$\\n\\n***\\n\\n$12_{10}$ divided by 2 gives a quotient of 6 and a remainder of 0, so the fourth binary digit is 0: $0101_2$\\n\\n***\\n\\n$6_{10}$ divided by 2 gives a quotient of 3 and a remainder of 0, so the fifth binary digit is 0: $00101_2$\\n\\n***\\n\\n$3_{10}$ divided by 2 gives a quotient of 1 and a remainder of 1, so the sixth binary digit is 1: $100101_2$\\n\\n***\\n\\n$1_{10}$ divided by 2 gives a quotient of zero and a remainder of 1, so the seventh binary digit is 1: $\\\\boxed{1100101_2}$. \\n\\n " + }, + { + "id": "d95da3de-eafa-4cab-98d8-005b1268393b", + "position": 1, + "title": "By inspection", + "content": "$101_{10}$ can be written in terms of powers of 2 as $101 = 64 + 32 + 4 + 1 = 2^6 + 2^5 + 2^2 + 2^0$, so its binary representation is $\\\\boxed{1100101_2}$.\\n\\n***\\n\\n$16_{10}$ is already a power of 2 ($2^4$), so its binary representation will have 1 bit set: $\\\\boxed{10000_2}$. \\n\\n " + } + ], + "structuredTutorialSections": [], + "responseAreas": [ + { + "responseAreaId": "9a307730-7525-4b6e-976a-d8a434c6503a", + "position": 0, + "responseType": "NUMBER", + "answer": 1100101, + "preResponseText": "$101_{10} = $" + }, + { + "responseAreaId": "05df22cc-4afd-499c-8533-5f34dfec7844", + "position": 1, + "responseType": "NUMBER", + "answer": 10000, + "preResponseText": "$16_{10} = $" + } + ] + } + ] + } } } diff --git a/src/agent/utils/example_inputs/example_input_2.json b/src/agent/utils/example_inputs/example_input_2.json index 503ece0..a1a17e0 100644 --- a/src/agent/utils/example_inputs/example_input_2.json +++ b/src/agent/utils/example_inputs/example_input_2.json @@ -1,143 +1,108 @@ { - "message": "what is the function value when x is between -1 0", - "params": { - "include_test_data": true, - "conversation_history": [ - { - "type": "user", - "content": "what is the function value when x is between -1 0" + "conversationId": "9e6b84b8-9aed-4145-ae1a-2f4d315e7601", + "messages": [{ "role": "USER", "content": "help" }], + "user": { + "userId": "a6632248-578b-4976-9d42-7daea459a905", + "type": "LEARNER", + "preference": { + "conversationalStyle": "student prefers direct and short responses" + }, + "taskProgress": { + "currentQuestionId": "f30c29ad-a7f6-45d0-8713-cb0686ca6a0e", + "timeSpentOnQuestion": "less than one minute", + "accessStatus": "", + "markedDone": "This question is still being worked on.", + "currentPart": { + "partId": "a51a1908-03cd-4bd9-8bbe-62d94ec2b3d1", + "position": 0, + "timeSpentOnPart": "less than one minute", + "markedDone": "This part is marked done.", + "responseAreas": [ + { + "responseAreaId": "a30d1dd3-1255-42c6-a1ac-37437476f816", + "responseType": "NUMERIC_UNITS", + "totalSubmissions": 3, + "wrongSubmissions": 2, + "latestSubmission": { + "submission": "939.7 J", + "feedback": "Correct", + "answer": "\"939.7 J\"" + } + } + ] } - ], - "summary": "", - "conversational_style": "", - "question_response_details": { - "questionSubmissionSummary": [ - { - "publishedPartId": "08e6f713-def7-4f97-83d4-34b0e67f5222", - "publishedPartPosition": 0, - "publishedResponseAreaId": "b8fc25d6-7cf4-4afc-984a-fa75fb0af7e7", - "publishedResponseAreaPosition": 0, - "responseAreaUniversalId": "e849584a-f58c-4330-858b-b507b9d8c56c", - "publishedResponseAreaPreResponseText": "$a_0=$", - "publishedResponseType": "EXPRESSION", - "publishedResponseConfig": { - "allowPhoto": true, - "allowHandwrite": true - }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 - }, + } + }, + "context": { + "summary": "noting was discussed yet", + "set": { "title": "Physics", "number": 7, "description": "" }, + "question": { + "title": "Work done", + "number": 0, + "guidance": "See section 1.11.", + "content": "**(L3)** A father pulls a child on a sled with a rope that has a constant tension of $F =100\\\\text{N}$ and makes an angle of $20^\\\\circ$ to the horizontal. Calculate the work he performs after pulling the child for $10\\\\text{m}$. \\n\\n![](image)\\n", + "estimatedTime": "10 minutes", + "parts": [ { - "publishedPartId": "08e6f713-def7-4f97-83d4-34b0e67f5222", - "publishedPartPosition": 0, - "publishedResponseAreaId": "2afd4463-ffbd-4b6b-8244-19c9c27e00f1", - "publishedResponseAreaPosition": 1, - "responseAreaUniversalId": "f20abf75-15ee-46ee-bee5-2897c71b2493", - "publishedResponseAreaPreResponseText": "$a_n=$", - "publishedResponseType": "EXPRESSION", - "publishedResponseConfig": { - "allowPhoto": true, - "allowHandwrite": true - }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 + "partId": "a51a1908-03cd-4bd9-8bbe-62d94ec2b3d1", + "position": 0, + "content": "Calculate the work he performs after pulling the child for $10\\\\text{m}$. \\n", + "answerContent": "$W = 939.7\\\\text{J}$\\n", + "workedSolutionSections": [ + { + "id": "ad3060af-4040-45d7-b0d5-ce47497b86f3", + "position": 0, + "title": "", + "content": "We require the force component parallel to the direction of travel, and so $W = \\\\vec{F}\\\\cdot\\\\vec{d}$ (the projection of $\\\\vec{F}$ onto $\\\\vec{d}$) (**section 1.11**). Hence applying the dot product definition to evaluate this (**section 1.8**):\\n\\n***\\n\\n$$\\n\\\\begin{aligned}\\nW & = \\\\vec{F}\\\\cdot \\\\vec{d} \\\\\\\\\\n& = 100\\\\cdot10\\\\cos\\\\theta \\\\\\\\\\n& = 939.7\\\\text{J} \\n\\\\end{aligned}\\n$$\\n" + } + ], + "structuredTutorialSections": [ + { + "id": "a6044ac0-f7f3-4a07-ac94-cb3e00cc732e", + "position": 0, + "title": null, + "content": "Can you express the work done as a dot product (**section 1.11**)?\\n\\n\\n***\\n\\nEvaluate the dot product using the dot product definition (**section 1.8**).\\n" + } + ], + "responseAreas": [ + { + "responseAreaId": "a30d1dd3-1255-42c6-a1ac-37437476f816", + "position": 0, + "responseType": "NUMERIC_UNITS", + "answer": "939.7 J", + "preResponseText": "$W=$" + } + ] }, { - "publishedPartId": "08e6f713-def7-4f97-83d4-34b0e67f5222", - "publishedPartPosition": 0, - "publishedResponseAreaId": "1477280a-64c7-43f3-a31d-091b95fd0900", - "publishedResponseAreaPosition": 2, - "responseAreaUniversalId": "f3cb33ee-419b-4b60-ab2a-2b7425710b5a", - "publishedResponseAreaPreResponseText": "$b_n=$", - "publishedResponseType": "EXPRESSION", - "publishedResponseConfig": { - "allowPhoto": true, - "allowHandwrite": true - }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 + "partId": "af21b774-5a07-48aa-9c90-398718f6b831", + "position": 1, + "content": "Why does he not pull at a shallower angle (closer to the horizontal)?\\n", + "answerContent": "", + "workedSolutionSections": [ + { + "id": "4f00f976-044b-4d40-9a14-0aa8d8c52221", + "position": 0, + "title": "", + "content": "Pulling at a shallower angle (closer to horizontal) would require less work or allow him to pull less strongly, but it reduces the reaction force that he feels too, which reduces his maximum pulling force, and also allow him to gain more leverage as he pulls (he can lean forward and use his weight to pull whilst pivoted at his feet). \\n" + } + ], + "structuredTutorialSections": [], + "responseAreas": [ + { + "responseAreaId": "202a362a-382e-4093-bd31-5531f9f677e1", + "position": 0, + "responseType": "MULTIPLE_CHOICE", + "answer": [ + "[true] ... reduces the reaction force acting on him, hence reducing the maximum pulling force.", + "[false] ... is a more awkward angle for the man to maintain, causing him to strain his muscles.", + "[false] ... increases the friction between the sledge and the ground, making it more difficult to move." + ], + "preResponseText": "Pulling at a shallower angle..." + } + ] } - ], - "questionInformation": { - "questionTitle": "Piecewise function Fourier series", - "questionGuidance": "", - "questionContent": "Find $a_0$, $a_n$ and $b_n$ for the Fourier series of $f(x)$, which is assumed to have period $4$.\n\n \n\n$$\nf(x)= \\begin{cases}0, & -2 \\leq x<-1 \\\\\\ \\frac{2 k}{3}, & -1 \\leq x<1 \\\\\\ -\\frac{k}{2}, & 1 \\leq x<2.\\end{cases}\n$$\n", - "durationLowerBound": 2, - "durationUpperBound": 10, - "parts": [ - { - "publishedPartId": "08e6f713-def7-4f97-83d4-34b0e67f5222", - "publishedPartPosition": 0, - "publishedPartContent": "", - "publishedPartAnswerContent": "$$\na_0=\\frac{5k}{12}\n$$\n\n \n\n \n\n$$\n\\begin{align*}\na_n &= \\dfrac{11k}{6n \\pi} \\sin \\left( \\dfrac{n \\pi}{2} \\right) \\\\[1em]\n &= \\dfrac{11k}{6n \\pi} \\frac{1-(-1)^n}{2} (-1)^{^{\\frac{n+3}{2}}} \\\\[1em]\n&= (-1)^{n+1}\\frac{11k}{6(2n-1)\\pi}\n\\end{align*}\n$$\n\n \n\n \n\n$$\n\\begin{align*}\nb_n &= \\frac{k}{2n \\pi} \\left( \\cos \\left( \\frac{n \\pi}{2} \\right) - \\cos(n \\pi) \\right) \\\\[1em]\nb_n &= \\frac{k}{2n \\pi} \\left( \\frac{1+(-1)^n}{2}(-1)^{\\frac{n}{2}} - (-1)^n \\right)\n\\end{align*}\n\n$$\n", - "publishedWorkedSolutionSections": [ - { - "id": "d41e73da-331d-4897-b6f6-15031469502c", - "position": 0, - "title": "", - "content": "Recall the Fourier series equations for period $2L$:\n\n \n\n$$\na_0 = \\frac{1}{L} \\int_{-L}^L {f(x)} \\, \\text{d}x\n\n$$\n\n \n\n$$\na_n = \\frac{1}{L} \\int_{-L}^L {f(x)} \\cos\\left(\\frac{n \\pi x}{L}\\right) \\, \\text{d}x\n$$\n\n \n\n$$\nb_n = \\frac{1}{L} \\int_{-L}^L {f(x)} \\sin\\left(\\frac{n \\pi x}{L}\\right) \\, \\text{d}x\n$$\n\n***\n\n$$\n2L=4\n$$\n\n$$\nL=2\n$$\n\n***\n\n### **Finding $a_0$:**\n\n$$\na_0 = \\frac{1}{2} \\int_{-2}^2 {f(x)} \\, \\text{d}x\n$$\n\n \n\n$$\na_0 = \\frac{1}{2} \\left( \\int_{-2}^{-1} 0 \\, \\text{d}x + \\int_{-1}^1 \\frac{2k}{3} \\, \\text{d}x + \\int_1^2 -\\frac{k}{2} \\, \\text{d}x \\right)\n\n$$\n\n \n\n$$\na_0=\\frac{5k}{12}\n$$\n\n***\n\n### **Finding $a_n$:**\n\n$$\na_n = \\frac{1}{2} \\int_{-2}^2 {f(x)} \\cos \\left( \\frac{n \\pi x}{L} \\right) \\, \\text{d}x\n\n$$\n\n \n\n$$\na_n = \\frac{1}{2} \\left( \\int_{-2}^{-1} 0 \\, \\text{d}x + \\int_{-1}^1 \\frac{2k}{3} \\cos \\left( \\frac{n \\pi x}{2} \\right) \\, \\text{d}x + \\int_1^2 -\\frac{k}{2} \\cos \\left( \\frac{n \\pi x}{2} \\right) \\, \\text{d}x \\right)\n\n$$\n\n \n\nAfter evaluating the integrals, the following is obtained:\n\n \n\n$$\na_n=\\frac{k}{3}\\left(\\frac{2}{n \\pi} \\sin \\left(\\frac{n \\pi}{2}\\right)-\\frac{2}{n \\pi} \\sin \\left(-\\frac{n \\pi}{2}\\right)\\right)-\\frac{k}{4}\\left(\\frac{2}{n \\pi} \\sin (n \\pi)-\\frac{2}{n \\pi} \\sin \\left(\\frac{n \\pi}{2}\\right)\\right)\n$$\n\n \n\n* The second $\\sin$ term can be written as: $-\\frac{2}{n \\pi} \\sin \\left(-\\frac{n \\pi}{2}\\right)=\\frac{2}{n \\pi} \\sin \\left(\\frac{n \\pi}{2}\\right)$\n* The third $\\sin$ term is always zero.\n\n \n\n$$\na_n=\\frac{k}{3}\\left(\\frac{4}{n \\pi} \\sin \\left(\\frac{n \\pi}{2}\\right)\\right)-\\frac{k}{4}\\left(-\\frac{2}{n \\pi} \\sin \\left(\\frac{n \\pi}{2}\\right)\\right)\n$$\n\n \n\nSome manipulation results in:\n\n \n\n$$\na_n = \\dfrac{11k}{6n \\pi} \\sin \\left( \\dfrac{n \\pi}{2} \\right)\n$$\n\n**Further simplification of $a_n$:**\n\nThe $\\sin \\left( \\dfrac{n \\pi}{2} \\right)$ term can be simplified by considering the pattern with increasing $n$:\n\n \n\n$$\n\\sin\\left(\\frac{n\\pi}{2}\\right) = \\begin{cases}\n0 & \\text{if $n=0$} \\\\\n1 & \\text{if $n=1$} \\\\\n0 & \\text{if $n=2$} \\\\\n-1 & \\text{if $n=3$} \\\\\n0 & \\text{if $n=4$} \\\\\n\\vdots & \\vdots\n\\end{cases}\n$$\n\nThis can be achieved as follows:\n\n$$\n\\sin \\left( \\dfrac{n \\pi}{2} \\right)=\\frac{1-(-1)^n}{2} (-1)^{^{\\frac{n+3}{2}}}\n$$\n\n \n\n$$\na_n = \\dfrac{11k}{6n \\pi} \\frac{1-(-1)^n}{2} (-1)^{^{\\frac{n+3}{2}}}\n$$\n\n$$\n\n\n$$\n\n \n\nHowever, since $\\sin \\left( \\dfrac{n \\pi}{2} \\right)$ is zero for all even $n$, the expression can alternatively be written as:\n\n \n\n$$\na_n=(-1)^{n+1}\\frac{11k}{6(2n-1)\\pi} \n$$\n\n***\n\n### **Finding $b_n$:**\n\n$$\nb_n = \\frac{1}{2} \\int_{-2}^2 {f(x)} \\sin\\left(\\frac{n \\pi x}{L}\\right) \\, \\text{d}x\n$$\n\n \n\n$$\nb_n = \\frac{1}{2} \\left( \\int_{-2}^{-1} 0 \\, \\text{d}x + \\int_{-1}^1 \\frac{2k}{3} \\sin \\left( \\frac{n \\pi x}{2} \\right) \\, \\text{d}x + \\int_1^2 -\\frac{k}{2} \\sin \\left( \\frac{n \\pi x}{2} \\right) \\, \\text{d}x \\right)\n$$\n\n \n\nAfter evaluating the integrals, the following is obtained:\n\n \n\n$$\nb_n=\\frac{k}{3}\\left(-\\frac{2}{n \\pi} \\cos \\left(\\frac{n \\pi}{2}\\right)+\\frac{2}{n \\pi} \\cos \\left(\\frac{n \\pi}{2}\\right)\\right)-\\frac{k}{4}\\left(-\\frac{2}{n \\pi} \\cos (n \\pi)+\\frac{2}{n \\pi} \\cos \\left(\\frac{n \\pi}{2}\\right)\\right)\n$$\n\n(note that the second $\\cos$ term has positive argument, because $\\cos$ is an even function.)\n\n \n\nSimplifying this expression yields the answer. Note that $\\cos(n\\pi)$ has been replaced with $(-1)^n$.\n\n \n\n$$\nb_n = \\frac{k}{2n \\pi} \\left( \\cos \\left( \\frac{n \\pi}{2} \\right) - \\cos(n \\pi) \\right)\n$$\n\n \n\n**Further simplification of** $b_n$**:**\n\n* The $\\cos(n\\pi)$ term can be replaced by $(-1)^n$.\n* The $\\cos\\left(\\frac{n\\pi}{2}\\right)$ term can be simplified by considering the pattern with increasing $n$:\n\n \n\n$$\n\\cos\\left(\\frac{n\\pi}{2}\\right) = \\begin{cases}\n1 & \\text{if $n=0$} \\\\\n0 & \\text{if $n=1$} \\\\\n-1 & \\text{if $n=2$} \\\\\n0 & \\text{if $n=3$} \\\\\n1 & \\text{if $n=4$} \\\\\n\\vdots & \\vdots\n\\end{cases}\n$$\n\nThis can be achieved as follows:\n\n$$\n\\cos\\left(\\frac{n\\pi}{2}\\right) =\\frac{1+(-1)^n}{2}(-1)^{\\frac{n}{2}}\n$$\n\nFinally, this yields:\n\n \n\n$$\nb_n = \\frac{k}{2n \\pi} \\left( \\frac{1+(-1)^n}{2}(-1)^{\\frac{n}{2}} - (-1)^n \\right)\n$$\n\n \n\n(Note that this expression may seem less concise that simply including the cos(n\\*pi/2) but is much more desirable and efficient in a numerical algorithm).\n" - } - ], - "publishedResponseAreas": [ - { - "id": "b8fc25d6-7cf4-4afc-984a-fa75fb0af7e7", - "position": 0, - "universalResponseAreaId": "e849584a-f58c-4330-858b-b507b9d8c56c", - "preResponseText": "$a_0=$", - "Response": { - "id": "d5d9f641-0724-4fa9-b3a7-2139c551c0a5", - "responseType": "EXPRESSION", - "config": { "allowPhoto": true, "allowHandwrite": true }, - "answer": "5k/12" - }, - "responseType": "EXPRESSION", - "answer": "5k/12" - }, - { - "id": "2afd4463-ffbd-4b6b-8244-19c9c27e00f1", - "position": 1, - "universalResponseAreaId": "f20abf75-15ee-46ee-bee5-2897c71b2493", - "preResponseText": "$a_n=$", - "Response": { - "id": "c7cb9bae-d67e-42db-9d71-3b4d2c3c8b44", - "responseType": "EXPRESSION", - "config": { "allowPhoto": true, "allowHandwrite": true }, - "answer": "(-1)^(n+1) 11k/(6(2n-1)pi)" - }, - "responseType": "EXPRESSION", - "answer": "(-1)^(n+1) 11k/(6(2n-1)pi)" - }, - { - "id": "1477280a-64c7-43f3-a31d-091b95fd0900", - "position": 2, - "universalResponseAreaId": "f3cb33ee-419b-4b60-ab2a-2b7425710b5a", - "preResponseText": "$b_n=$", - "Response": { - "id": "9b868669-feb4-4c33-977e-2297bf4f7948", - "responseType": "EXPRESSION", - "config": { "allowPhoto": true, "allowHandwrite": true }, - "answer": "k/(2n pi) ( (1+(-1)^n)/(2) (-1)^(n/2) - (-1)^n)" - }, - "responseType": "EXPRESSION", - "answer": "k/(2n pi) ( (1+(-1)^n)/(2) (-1)^(n/2) - (-1)^n)" - } - ] - } - ] - }, - "questionAccessInformation": { - "estimatedMinimumTime": "2 minutes", - "estimaredMaximumTime": "10 minutes", - "timeTaken": "less than one minute", - "accessStatus": "too little time spent on this question.", - "markedDone": "", - "currentPart": { - "id": "08e6f713-def7-4f97-83d4-34b0e67f5222", - "position": 0 - } - } - }, - "conversation_id": "6779b184-41b5-4384-ade9-1c06d2cd91a5", - "agent_type": "base" + ] + } } } diff --git a/src/agent/utils/example_inputs/example_input_3.json b/src/agent/utils/example_inputs/example_input_3.json index d93ac92..f422dbf 100644 --- a/src/agent/utils/example_inputs/example_input_3.json +++ b/src/agent/utils/example_inputs/example_input_3.json @@ -1,460 +1,235 @@ { - "message": "hi", - "params": { - "include_test_data": true, - "conversation_history": [ - { "type": "user", "content": "hi" }, - { "type": "ai", "content": "Hello! How can I help you today?" } - ], - "summary": "", - "conversational_style": "", - "question_response_details": { - "questionSubmissionSummary": [ - { - "publishedPartId": "04d2cab2-eeca-4c1f-bcef-6401fe4ec635", - "publishedPartPosition": 0, - "publishedResponseAreaId": "1194c5b3-1831-433e-b78e-c96c94f99117", - "publishedResponseAreaPosition": 0, - "responseAreaUniversalId": "ee1df8dd-bccb-46d2-b816-1a707115495e", - "publishedResponseAreaPreResponseText": "$\\vec{a}+\\vec{b}=$", - "publishedResponseType": "MATRIX", - "publishedResponseConfig": { "cols": 1, "rows": 3 }, - "totalSubmissions": 1, - "totalWrongSubmissions": 0, - "latestSubmission": { - "universalResponseAreaId": "ee1df8dd-bccb-46d2-b816-1a707115495e", - "answer": "[[\"-1\"],[\"5\"],[\"-1\"]]", - "submission": [["-1"], ["5"], ["-1"]], - "feedback": "Correct", - "rawResponse": { - "result": { - "feedback": "", - "is_correct": true, - "detailed_feedback": [ - [ - { - "feedback": "", - "is_correct": true, - "response_latex": "-1", - "response_simplified": "-1" - } - ], - [ - { - "feedback": "", - "is_correct": true, - "response_latex": "5", - "response_simplified": "5" - } - ], - [ - { - "feedback": "", - "is_correct": true, - "response_latex": "-1", - "response_simplified": "-1" - } - ] - ] - }, - "command": "eval" + "conversationId": "2f95c229-af43-4c1f-918a-0fd428178acf", + "messages": [ + { + "role": "USER", + "content": "hi" + } + ], + "user": { + "userId": "c7e19a84-2b56-4f01-9a3e-1d08bc762345", + "type": "LEARNER", + "preference": { + "conversationalStyle": "The student prefers concise answers with worked examples." + }, + "taskProgress": { + "currentQuestionId": "a1b2c3d4-5e6f-7890-abcd-ef1234567890", + "timeSpentOnQuestion": "13 minutes", + "accessStatus": "too much time spent on this question.", + "markedDone": "Part (a) was marked done.", + "currentPart": { + "partId": "04d2cab2-eeca-4c1f-bcef-6401fe4ec635", + "position": 0, + "timeSpentOnPart": "10 minutes", + "markedDone": "This part is not marked done.", + "responseAreas": [ + { + "responseAreaId": "1194c5b3-1831-433e-b78e-c96c94f99117", + "responseType": "MATRIX", + "totalSubmissions": 1, + "wrongSubmissions": 0, + "latestSubmission": { + "submission": [["-1"], ["5"], ["-1"]], + "feedback": "Correct", + "answer": [["-1"], ["5"], ["-1"]] } - } - }, - { - "publishedPartId": "04d2cab2-eeca-4c1f-bcef-6401fe4ec635", - "publishedPartPosition": 0, - "publishedResponseAreaId": "4e354eda-fb78-4c26-a3ae-f8d1c06297a2", - "publishedResponseAreaPosition": 1, - "responseAreaUniversalId": "7f7ad90a-0cfb-44e0-b5d7-6be585206508", - "publishedResponseAreaPreResponseText": "$\\vec{b}+\\vec{a}=$", - "publishedResponseType": "MATRIX", - "publishedResponseConfig": { "cols": 1, "rows": 3 }, - "totalSubmissions": 1, - "totalWrongSubmissions": 0, - "latestSubmission": { - "universalResponseAreaId": "7f7ad90a-0cfb-44e0-b5d7-6be585206508", - "answer": "[[\"-1\"],[\"5\"],[\"-1\"]]", - "submission": [["-1"], ["5"], ["-1"]], - "feedback": "Correct", - "rawResponse": { - "result": { - "feedback": "", - "is_correct": true, - "detailed_feedback": [ - [ - { - "feedback": "", - "is_correct": true, - "response_latex": "-1", - "response_simplified": "-1" - } - ], - [ - { - "feedback": "", - "is_correct": true, - "response_latex": "5", - "response_simplified": "5" - } - ], - [ - { - "feedback": "", - "is_correct": true, - "response_latex": "-1", - "response_simplified": "-1" - } - ] - ] - }, - "command": "eval" + }, + { + "responseAreaId": "4e354eda-fb78-4c26-a3ae-f8d1c06297a2", + "responseType": "MATRIX", + "totalSubmissions": 1, + "wrongSubmissions": 1, + "latestSubmission": { + "submission": [["-1"], ["5"], ["1"]], + "feedback": "Missing negative sign in the last element.", + "answer": [["-1"], ["5"], ["-1"]] } } - }, - { - "publishedPartId": "dbe25bc9-6c42-4e58-aac7-d15a783ef337", - "publishedPartPosition": 1, - "publishedResponseAreaId": "85c33011-0e12-435c-944e-399043e92779", - "publishedResponseAreaPosition": 0, - "responseAreaUniversalId": "bcf1b636-f188-4c68-a59a-c153fb4419d2", - "publishedResponseAreaPreResponseText": "$3\\vec{c}=$", - "publishedResponseType": "MATRIX", - "publishedResponseConfig": { "cols": 1, "rows": 3 }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 - }, - { - "publishedPartId": "dbe25bc9-6c42-4e58-aac7-d15a783ef337", - "publishedPartPosition": 1, - "publishedResponseAreaId": "ae4a9db7-ff54-489d-9a21-143621e6e7aa", - "publishedResponseAreaPosition": 1, - "responseAreaUniversalId": "bfab6be0-11bd-4799-ba10-1e9a0182da36", - "publishedResponseAreaPreResponseText": "$-\\vec{a}=$", - "publishedResponseType": "MATRIX", - "publishedResponseConfig": { "cols": 1, "rows": 3 }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 - }, + ] + } + } + }, + "context": { + "summary": "", + "set": { + "title": "Linear Algebra", + "number": 1, + "description": "Vectors, matrices, and linear transformations." + }, + "question": { + "title": "Vector Arithmetics", + "number": 1, + "guidance": null, + "content": "$$\n\\vec{a}=\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix} \\quad \\vec{b}=\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix} \\quad \\vec{c}=\\begin{bmatrix} 0 \\\\\\ 4\\\\ -1\\end{bmatrix}\n$$", + "estimatedTime": "1-3 minutes", + "parts": [ { - "publishedPartId": "dbe25bc9-6c42-4e58-aac7-d15a783ef337", - "publishedPartPosition": 1, - "publishedResponseAreaId": "3ae38184-9dbc-4c5f-872b-75ddfd691b20", - "publishedResponseAreaPosition": 2, - "responseAreaUniversalId": "170e2cf2-9d0d-438e-a406-cafe7ff1017b", - "publishedResponseAreaPreResponseText": "$\\frac{\\vec{b}}{2}=$", - "publishedResponseType": "MATRIX", - "publishedResponseConfig": { "cols": 1, "rows": 3 }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 + "partId": "04d2cab2-eeca-4c1f-bcef-6401fe4ec635", + "position": 0, + "content": "", + "answerContent": "", + "workedSolutionSections": [ + { + "id": "9cb3a347-c05f-4101-b522-a0c6d2fbc6bf", + "position": 1, + "title": "", + "content": "$$\n\\vec{a}+\\vec{b} = \\begin{bmatrix}-1 \\\\ 5\\\\ -1\\end{bmatrix}\n$$\n\nSince vector addition is commutative, $\\vec{b}+\\vec{a}=\\vec{a}+\\vec{b}$.\n" + } + ], + "structuredTutorialSections": [], + "responseAreas": [ + { + "responseAreaId": "1194c5b3-1831-433e-b78e-c96c94f99117", + "position": 0, + "responseType": "MATRIX", + "answer": [["-1"], ["5"], ["-1"]], + "preResponseText": "$\\vec{a}+\\vec{b}=$" + }, + { + "responseAreaId": "4e354eda-fb78-4c26-a3ae-f8d1c06297a2", + "position": 1, + "responseType": "MATRIX", + "answer": [["-1"], ["5"], ["-1"]], + "preResponseText": "$\\vec{b}+\\vec{a}=$" + } + ] }, { - "publishedPartId": "2cb9d565-8f1c-4901-bf2b-0b89f12ec9e2", - "publishedPartPosition": 2, - "publishedResponseAreaId": "6c64c869-358d-477d-b835-7c5e268b08b2", - "publishedResponseAreaPosition": 0, - "responseAreaUniversalId": "dfa4be9e-c9e2-442d-95a4-c2c61d25fe51", - "publishedResponseAreaPreResponseText": "$3\\vec{a}-3\\vec{c}=$", - "publishedResponseType": "MATRIX", - "publishedResponseConfig": { "cols": 1, "rows": 3 }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 + "partId": "dbe25bc9-6c42-4e58-aac7-d15a783ef337", + "position": 1, + "content": "", + "answerContent": "", + "workedSolutionSections": [ + { + "id": "7d696718-c6f5-4e11-a5fb-bd122c1274cf", + "position": 1, + "title": "$3\\vec{c}$", + "content": "$$\n3\\vec{c} = \\begin{bmatrix}0 \\\\ 12\\\\ -3\\end{bmatrix}\n$$" + }, + { + "id": "010b312d-2699-439f-bcab-9f8ac4b96114", + "position": 2, + "title": "$-\\vec{a}$", + "content": "$$\n-\\vec{a} = \\begin{bmatrix}-1 \\\\ -2\\\\ -3\\end{bmatrix}\n$$" + }, + { + "id": "20602e93-de57-4e69-afcd-e05f454155de", + "position": 3, + "title": "$\\frac{\\vec{b}}{2}$", + "content": "$$\n\\frac{\\vec{b}}{2} = \\begin{bmatrix}-1 \\\\ 1.5\\\\ -2\\end{bmatrix}\n$$" + } + ], + "structuredTutorialSections": [], + "responseAreas": [ + { + "responseAreaId": "85c33011-0e12-435c-944e-399043e92779", + "position": 0, + "responseType": "MATRIX", + "answer": [["0"], ["12"], ["-3"]], + "preResponseText": "$3\\vec{c}=$" + }, + { + "responseAreaId": "ae4a9db7-ff54-489d-9a21-143621e6e7aa", + "position": 1, + "responseType": "MATRIX", + "answer": [["-1"], ["-2"], ["-3"]], + "preResponseText": "$-\\vec{a}=$" + }, + { + "responseAreaId": "3ae38184-9dbc-4c5f-872b-75ddfd691b20", + "position": 2, + "responseType": "MATRIX", + "answer": [["-1"], ["1.5"], ["-2"]], + "preResponseText": "$\\frac{\\vec{b}}{2}=$" + } + ] }, { - "publishedPartId": "2cb9d565-8f1c-4901-bf2b-0b89f12ec9e2", - "publishedPartPosition": 2, - "publishedResponseAreaId": "0b5d2dab-7c6d-4694-8c3b-56df1afaba96", - "publishedResponseAreaPosition": 1, - "responseAreaUniversalId": "e695440a-65b8-4ebd-b055-6173d9d84148", - "publishedResponseAreaPreResponseText": "$3(\\vec{a}-\\vec{c})=$", - "publishedResponseType": "MATRIX", - "publishedResponseConfig": { "cols": 1, "rows": 3 }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 + "partId": "2cb9d565-8f1c-4901-bf2b-0b89f12ec9e2", + "position": 2, + "content": "", + "answerContent": "", + "workedSolutionSections": [ + { + "id": "8162a17d-bed7-48a9-b487-0bd6280a3988", + "position": 1, + "title": "$3\\vec{a}-3\\vec{c}$", + "content": "$$\n3\\vec{a}-3\\vec{c} = \\begin{bmatrix}3 \\\\ -6\\\\ 12\\end{bmatrix}\n$$" + }, + { + "id": "9d88d02a-e63d-40d0-8200-5d81718e2259", + "position": 2, + "title": "$3(\\vec{a}-\\vec{c})$", + "content": "$$\n3(\\vec{a}-\\vec{c}) = \\begin{bmatrix}3 \\\\ -6\\\\ 12\\end{bmatrix}\n$$" + } + ], + "structuredTutorialSections": [], + "responseAreas": [ + { + "responseAreaId": "6c64c869-358d-477d-b835-7c5e268b08b2", + "position": 0, + "responseType": "MATRIX", + "answer": [["3"], ["-6"], ["12"]], + "preResponseText": "$3\\vec{a}-3\\vec{c}=$" + }, + { + "responseAreaId": "0b5d2dab-7c6d-4694-8c3b-56df1afaba96", + "position": 1, + "responseType": "MATRIX", + "answer": [["3"], ["-6"], ["12"]], + "preResponseText": "$3(\\vec{a}-\\vec{c})=$" + } + ] }, { - "publishedPartId": "3ee15335-1bfc-466a-8fff-66abd742dc33", - "publishedPartPosition": 3, - "publishedResponseAreaId": "8a4d3fed-e450-475d-a09b-1e58f8dbbbe2", - "publishedResponseAreaPosition": 0, - "responseAreaUniversalId": "4fd2eb4f-dec2-4695-9148-09104935d431", - "publishedResponseAreaPreResponseText": "$-5(\\vec{a}+\\vec{c})+\\vec{b}=$", - "publishedResponseType": "MATRIX", - "publishedResponseConfig": { "cols": 1, "rows": 3 }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 + "partId": "3ee15335-1bfc-466a-8fff-66abd742dc33", + "position": 3, + "content": "", + "answerContent": "", + "workedSolutionSections": [ + { + "id": "916cf686-7a41-4169-8fa2-b2e9d6924375", + "position": 1, + "title": "", + "content": "$$\n-5(\\vec{a}+\\vec{c})+\\vec{b} = \\begin{bmatrix}-7 \\\\ -27\\\\ -14\\end{bmatrix}\n$$" + } + ], + "structuredTutorialSections": [], + "responseAreas": [ + { + "responseAreaId": "8a4d3fed-e450-475d-a09b-1e58f8dbbbe2", + "position": 0, + "responseType": "MATRIX", + "answer": [["-7"], ["-27"], ["-14"]], + "preResponseText": "$-5(\\vec{a}+\\vec{c})+\\vec{b}=$" + } + ] }, { - "publishedPartId": "b2620eb0-9de6-404e-a82c-b4ad8e015d40", - "publishedPartPosition": 4, - "publishedResponseAreaId": "1a5fb3fb-001d-4ed5-9606-2a73fcae036c", - "publishedResponseAreaPosition": 0, - "responseAreaUniversalId": "c98844ba-3fd2-4154-8713-1d76bfaf610c", - "publishedResponseAreaPreResponseText": "$3\\vec{a}-2\\vec{c}+3\\vec{b}=$", - "publishedResponseType": "MATRIX", - "publishedResponseConfig": { "cols": 1, "rows": 3 }, - "totalSubmissions": 0, - "totalWrongSubmissions": 0 - } - ], - "questionInformation": { - "questionTitle": "Vector Arithmetics", - "questionGuidance": null, - "questionContent": "$$\n\\vec{a}=\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix} \\quad \\vec{b}=\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix} \\quad \\vec{c}=\\begin{bmatrix} 0 \\\\\\ 4\\\\ -1\\end{bmatrix}\n$$", - "durationLowerBound": 1, - "durationUpperBound": 3, - "parts": [ - { - "publishedPartId": "04d2cab2-eeca-4c1f-bcef-6401fe4ec635", - "publishedPartPosition": 0, - "publishedPartContent": "", - "publishedPartAnswerContent": "", - "publishedWorkedSolutionSections": [ - { - "id": "9cb3a347-c05f-4101-b522-a0c6d2fbc6bf", - "position": 0, - "title": "", - "content": "As we made no mention that the vectors are represented in different basis, we can assume that they all share the same basis set.\n\n \n\n$$\n\\begin{array}{rl}\n\\vec{a}+\\vec{b} &=\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix}+\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}1-2 \\\\ 2+3\\\\ 3-4\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-1 \\\\ 5\\\\ -1\\end{bmatrix}\n\\end{array}\n$$\n\n \n\n***\n\n \n\nSince vector addition is commutative, $\\vec{b}+\\vec{a}=\\vec{a}+\\vec{b}$\n" - } - ], - "publishedResponseAreas": [ - { - "id": "1194c5b3-1831-433e-b78e-c96c94f99117", - "position": 0, - "universalResponseAreaId": "ee1df8dd-bccb-46d2-b816-1a707115495e", - "preResponseText": "$\\vec{a}+\\vec{b}=$", - "Response": { - "id": "0c40d7de-15a0-4916-af34-5ee2b330f3dd", - "responseType": "MATRIX", - "config": { "cols": 1, "rows": 3 }, - "answer": [["-1"], ["5"], ["-1"]] - }, - "responseType": "MATRIX", - "answer": [["-1"], ["5"], ["-1"]] - }, - { - "id": "4e354eda-fb78-4c26-a3ae-f8d1c06297a2", - "position": 1, - "universalResponseAreaId": "7f7ad90a-0cfb-44e0-b5d7-6be585206508", - "preResponseText": "$\\vec{b}+\\vec{a}=$", - "Response": { - "id": "a01003b5-f11d-4d06-9ec2-35efc11b7b64", - "responseType": "MATRIX", - "config": { "cols": 1, "rows": 3 }, - "answer": [["-1"], ["5"], ["-1"]] - }, - "responseType": "MATRIX", - "answer": [["-1"], ["5"], ["-1"]] - } - ] - }, - { - "publishedPartId": "dbe25bc9-6c42-4e58-aac7-d15a783ef337", - "publishedPartPosition": 1, - "publishedPartContent": "", - "publishedPartAnswerContent": "", - "publishedWorkedSolutionSections": [ - { - "id": "7d696718-c6f5-4e11-a5fb-bd122c1274cf", - "position": 0, - "title": "$3\\vec{c}$", - "content": "$$\n\\begin{array}{rl}\n3\\vec{c}&=3\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}3\\cdot0 \\\\ 3\\cdot4\\\\ 3\\cdot-1\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}0 \\\\ 12\\\\ -3\\end{bmatrix}\n\\end{array}\n$$" - }, - { - "id": "8134095d-f49e-4834-b8de-12baa9625d7c", - "position": 0, - "title": "", - "content": "" - }, - { - "id": "010b312d-2699-439f-bcab-9f8ac4b96114", - "position": 1, - "title": "$-\\vec{a}$", - "content": "$$\n\\begin{array}{rl}\n-\\vec{a}&=3\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-1\\cdot1 \\\\ -1\\cdot2\\\\ -1\\cdot3\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-1 \\\\ -2\\\\ -3\\end{bmatrix}\n\\end{array}\n$$" - }, - { - "id": "20602e93-de57-4e69-afcd-e05f454155de", - "position": 2, - "title": "$\\frac{\\vec{b}}{2}$", - "content": "$$\n\\begin{array}{rl}\n\\frac{\\vec{b}}{2}&=\\frac{1}{2}\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}\\frac{1}{2}\\cdot-2 \\\\ \\frac{1}{2}\\cdot3\\\\ \\frac{1}{2}\\cdot-4\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-1 \\\\ 1.5\\\\ -2\\end{bmatrix}\n\\end{array}\n$$" - } - ], - "publishedResponseAreas": [ - { - "id": "85c33011-0e12-435c-944e-399043e92779", - "position": 0, - "universalResponseAreaId": "bcf1b636-f188-4c68-a59a-c153fb4419d2", - "preResponseText": "$3\\vec{c}=$", - "Response": { - "id": "8623c885-b49d-476e-b316-f45a5052cacd", - "responseType": "MATRIX", - "config": { "cols": 1, "rows": 3 }, - "answer": [["0"], ["12"], ["-3"]] - }, - "responseType": "MATRIX", - "answer": [["0"], ["12"], ["-3"]] - }, - { - "id": "ae4a9db7-ff54-489d-9a21-143621e6e7aa", - "position": 1, - "universalResponseAreaId": "bfab6be0-11bd-4799-ba10-1e9a0182da36", - "preResponseText": "$-\\vec{a}=$", - "Response": { - "id": "150494b9-1ed6-44f3-8d52-8bdbd74f7c75", - "responseType": "MATRIX", - "config": { "cols": 1, "rows": 3 }, - "answer": [["-1"], ["-2"], ["-3"]] - }, - "responseType": "MATRIX", - "answer": [["-1"], ["-2"], ["-3"]] - }, - { - "id": "3ae38184-9dbc-4c5f-872b-75ddfd691b20", - "position": 2, - "universalResponseAreaId": "170e2cf2-9d0d-438e-a406-cafe7ff1017b", - "preResponseText": "$\\frac{\\vec{b}}{2}=$", - "Response": { - "id": "ed65b833-5cb6-4198-aa2f-a5b74d86b3f1", - "responseType": "MATRIX", - "config": { "cols": 1, "rows": 3 }, - "answer": [["-1"], ["1.5"], ["-2"]] - }, - "responseType": "MATRIX", - "answer": [["-1"], ["1.5"], ["-2"]] - } - ] - }, - { - "publishedPartId": "2cb9d565-8f1c-4901-bf2b-0b89f12ec9e2", - "publishedPartPosition": 2, - "publishedPartContent": "", - "publishedPartAnswerContent": "", - "publishedWorkedSolutionSections": [ - { - "id": "8162a17d-bed7-48a9-b487-0bd6280a3988", - "position": 0, - "title": "$3\\vec{a}-3\\vec{c}$", - "content": "$$\n\\begin{array}{rl}\n3\\vec{a}-3\\vec{c} &=3\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix}-3\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}3 \\\\ 6\\\\ 9\\end{bmatrix}-\\begin{bmatrix}0 \\\\ 12\\\\ -3\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}3-0 \\\\ 6-12\\\\ 9-(-3)\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}3 \\\\ -6\\\\ 12\\end{bmatrix}\n\\end{array}\n$$" - }, - { - "id": "7526f2d6-baf1-4aea-bb9f-6486f7233f97", - "position": 0, - "title": "", - "content": "As we made no mention that the vectors are represented in different basis, we can assume that they all share the same basis set.\n\n \n\n$$\n\\begin{array}{rl}\n3\\vec{a}-2\\vec{c}+3\\vec{b} &=3\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix}-2\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}+3\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix}\n\\\\\\\\\n&= 3\\left(\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix}+\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix}\\right)-2\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}\n\\\\\\\\\n&= 3\\begin{bmatrix}1-2 \\\\ 2+3\\\\ 3-4\\end{bmatrix}-2\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}\n\\\\\\\\\n&= 3\\begin{bmatrix}-1 \\\\ 5\\\\ -1\\end{bmatrix}-2\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-3 \\\\ 15\\\\ -3\\end{bmatrix}+\\begin{bmatrix}0 \\\\ -8\\\\ 2\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-3+0 \\\\ 15-8\\\\ -3+2\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-3 \\\\ 7\\\\ -1\\end{bmatrix}\n\\end{array}\n$$" - }, - { - "id": "9d88d02a-e63d-40d0-8200-5d81718e2259", - "position": 1, - "title": "$3(\\vec{a}-\\vec{c})$", - "content": "$$\n\\begin{array}{rl}\n3(\\vec{a}-\\vec{c}) &=3\\left(\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix}-\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}\\right)\n\\\\\\\\\n&= 3\\begin{bmatrix}1-0 \\\\ 2-4\\\\ 3-(-1)\\end{bmatrix}\n\\\\\\\\\n&= 3\\begin{bmatrix}1 \\\\ -2\\\\ 4\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}3 \\\\ -6\\\\ 12\\end{bmatrix}\n\\end{array}\n$$" - } - ], - "publishedResponseAreas": [ - { - "id": "6c64c869-358d-477d-b835-7c5e268b08b2", - "position": 0, - "universalResponseAreaId": "dfa4be9e-c9e2-442d-95a4-c2c61d25fe51", - "preResponseText": "$3\\vec{a}-3\\vec{c}=$", - "Response": { - "id": "53199f12-d660-4ffe-95ae-9d28c9f260ba", - "responseType": "MATRIX", - "config": { "cols": 1, "rows": 3 }, - "answer": [["3"], ["-6"], ["12"]] - }, - "responseType": "MATRIX", - "answer": [["3"], ["-6"], ["12"]] - }, - { - "id": "0b5d2dab-7c6d-4694-8c3b-56df1afaba96", - "position": 1, - "universalResponseAreaId": "e695440a-65b8-4ebd-b055-6173d9d84148", - "preResponseText": "$3(\\vec{a}-\\vec{c})=$", - "Response": { - "id": "c189d436-d5a4-4c61-b26e-95ac30c281b5", - "responseType": "MATRIX", - "config": { "cols": 1, "rows": 3 }, - "answer": [["3"], ["-6"], ["12"]] - }, - "responseType": "MATRIX", - "answer": [["3"], ["-6"], ["12"]] - } - ] - }, - { - "publishedPartId": "3ee15335-1bfc-466a-8fff-66abd742dc33", - "publishedPartPosition": 3, - "publishedPartContent": "", - "publishedPartAnswerContent": "", - "publishedWorkedSolutionSections": [ - { - "id": "916cf686-7a41-4169-8fa2-b2e9d6924375", - "position": 0, - "title": "", - "content": "As we made no mention that the vectors are represented in different basis, we can assume that they all share the same basis set.\n\n \n\n$$\n\\begin{array}{rl}\n-5(\\vec{a}+\\vec{c})+\\vec{b} &=-5\\left(\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix}+\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}\\right)+\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix}\n\\\\\\\\\n&= -5\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix}-5\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}+\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-5 \\\\ -10\\\\ -15\\end{bmatrix}+\\begin{bmatrix}0 \\\\ -20\\\\ 5\\end{bmatrix}+\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-5+0-2 \\\\ -10-20+3\\\\ -15+5-4\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-7 \\\\ -27\\\\ -14\\end{bmatrix}\n\\end{array}\n$$" - } - ], - "publishedResponseAreas": [ - { - "id": "8a4d3fed-e450-475d-a09b-1e58f8dbbbe2", - "position": 0, - "universalResponseAreaId": "4fd2eb4f-dec2-4695-9148-09104935d431", - "preResponseText": "$-5(\\vec{a}+\\vec{c})+\\vec{b}=$", - "Response": { - "id": "eeb448a9-81b9-4f4d-836f-d04029aa449c", - "responseType": "MATRIX", - "config": { "cols": 1, "rows": 3 }, - "answer": [["-7"], ["-27"], ["-14"]] - }, - "responseType": "MATRIX", - "answer": [["-7"], ["-27"], ["-14"]] - } - ] - }, - { - "publishedPartId": "b2620eb0-9de6-404e-a82c-b4ad8e015d40", - "publishedPartPosition": 4, - "publishedPartContent": "", - "publishedPartAnswerContent": "", - "publishedWorkedSolutionSections": [ - { - "id": "3d15fcbc-5173-4d89-a13a-3296c25680ec", - "position": 0, - "title": "", - "content": "As we made no mention that the vectors are represented in different basis, we can assume that they all share the same basis set.\n\n \n\n$$\n\\begin{array}{rl}\n3\\vec{a}-2\\vec{c}+3\\vec{b} &=3\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix}-2\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}+3\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix}\n\\\\\\\\\n&= 3\\left(\\begin{bmatrix}1 \\\\ 2\\\\ 3\\end{bmatrix}+\\begin{bmatrix}-2 \\\\ 3\\\\ -4\\end{bmatrix}\\right)-2\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}\n\\\\\\\\\n&= 3\\begin{bmatrix}1-2 \\\\ 2+3\\\\ 3-4\\end{bmatrix}-2\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}\n\\\\\\\\\n&= 3\\begin{bmatrix}-1 \\\\ 5\\\\ -1\\end{bmatrix}-2\\begin{bmatrix}0 \\\\ 4\\\\ -1\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-3 \\\\ 15\\\\ -3\\end{bmatrix}+\\begin{bmatrix}0 \\\\ -8\\\\ 2\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-3+0 \\\\ 15-8\\\\ -3+2\\end{bmatrix}\n\\\\\\\\\n&= \\begin{bmatrix}-3 \\\\ 7\\\\ -1\\end{bmatrix}\n\\end{array}\n$$" - } - ], - "publishedResponseAreas": [ - { - "id": "1a5fb3fb-001d-4ed5-9606-2a73fcae036c", - "position": 0, - "universalResponseAreaId": "c98844ba-3fd2-4154-8713-1d76bfaf610c", - "preResponseText": "$3\\vec{a}-2\\vec{c}+3\\vec{b}=$", - "Response": { - "id": "c5f1c9a9-eb13-4b2e-83ad-f9a3bd87a056", - "responseType": "MATRIX", - "config": { "cols": 1, "rows": 3 }, - "answer": [["-3"], ["7"], ["-1"]] - }, - "responseType": "MATRIX", - "answer": [["-3"], ["7"], ["-1"]] - } - ] - } - ] - }, - "questionAccessInformation": { - "estimatedMinimumTime": "1 minute", - "estimaredMaximumTime": "3 minutes", - "timeTaken": "13 minutes", - "accessStatus": "too much time spent on this question.", - "markedDone": "Part (a) was marked done", - "currentPart": { - "id": "04d2cab2-eeca-4c1f-bcef-6401fe4ec635", - "position": 0 + "partId": "b2620eb0-9de6-404e-a82c-b4ad8e015d40", + "position": 4, + "content": "", + "answerContent": "", + "workedSolutionSections": [ + { + "id": "3d15fcbc-5173-4d89-a13a-3296c25680ec", + "position": 1, + "title": "", + "content": "$$\n3\\vec{a}-2\\vec{c}+3\\vec{b} = \\begin{bmatrix}-3 \\\\ 7\\\\ -1\\end{bmatrix}\n$$" + } + ], + "structuredTutorialSections": [], + "responseAreas": [ + { + "responseAreaId": "1a5fb3fb-001d-4ed5-9606-2a73fcae036c", + "position": 0, + "responseType": "MATRIX", + "answer": [["-3"], ["7"], ["-1"]], + "preResponseText": "$3\\vec{a}-2\\vec{c}+3\\vec{b}=$" + } + ] } - } - }, - "conversation_id": "2f95c229-af43-4c1f-918a-0fd428178acf", - "agent_type": "base" + ] + } } } diff --git a/src/agent/utils/parse_json_context_to_prompt.py b/src/agent/utils/parse_json_context_to_prompt.py index a233126..a74314e 100644 --- a/src/agent/utils/parse_json_context_to_prompt.py +++ b/src/agent/utils/parse_json_context_to_prompt.py @@ -165,11 +165,10 @@ def parse_json_to_structured_prompt( if not question_information: return PromptFormatter.format_error_message() - - # Convert to proper objects - submission_summary = [StudentWorkResponseArea(**summary) for summary in question_submission_summary] - question_info = QuestionDetails(**question_information) - access_info = QuestionAccessInformation(**question_access_information) if question_access_information else None + + submission_summary = question_submission_summary + question_info = question_information + access_info = question_access_information # TODO: EXPERIMENTAL - Remove later # if question_info.setNumber is not None: @@ -181,7 +180,7 @@ def parse_json_to_structured_prompt( # 1. Question Header current_part_letter = None - if access_info and access_info.currentPart: + if access_info and access_info.currentPart and access_info.currentPart.position is not None: current_part_letter = PromptFormatter.get_part_letter(access_info.currentPart.position) set_info = { diff --git a/src/agent/utils/prompt_context_templates.py b/src/agent/utils/prompt_context_templates.py index e77a208..037b5ba 100644 --- a/src/agent/utils/prompt_context_templates.py +++ b/src/agent/utils/prompt_context_templates.py @@ -174,18 +174,18 @@ def format_single_response_area( def _format_student_submissions(student_work: Dict[str, Any]) -> str: """Format student submission history.""" if not student_work.get('has_submissions'): - return "- Your Work: No responses submitted yet" + return "- Your Work on this response area: No response submitted yet" latest = student_work.get('latest_response', 'None') feedback = student_work.get('latest_feedback', 'None') total = student_work.get('total_submissions', 0) wrong = student_work.get('total_wrong', 0) - return f"""- Your Work: + return f"""- Your Work on this response area: - Latest response: {latest} - Latest feedback: {feedback} - - Total attempts: {total} - - Incorrect attempts: {wrong}""" + - Total attempts on this response area: {total} + - Incorrect attempts on this response area: {wrong}""" @staticmethod def format_part_answer(answer_content: Optional[str]) -> str: diff --git a/src/module.py b/src/module.py index 922979f..8e278d9 100755 --- a/src/module.py +++ b/src/module.py @@ -1,85 +1,168 @@ import time -from typing import Any -from lf_toolkit.chat.result import ChatResult as Result -from lf_toolkit.chat.params import ChatParams as Params +from langchain_core.messages import HumanMessage, AIMessage, SystemMessage -from src.agent.utils.parse_json_context_to_prompt import parse_json_to_prompt +from lf_toolkit.chat import ChatRequest, ChatResponse, Message +from lf_toolkit.shared.mued_api_v0_1_0 import Role + +from src.agent.utils.parse_json_context_to_prompt import ( + parse_json_to_prompt, + QuestionDetails, + StudentWorkResponseArea, + QuestionAccessInformation, +) from src.agent.agent import invoke_base_agent -from src.agent.utils.types import JsonType -def chat_module(message: Any, params: Params) -> JsonType: - """ - Function used by student to converse with a chatbot. - --- - The handler function passes three arguments to module(): - - `message` which is the message sent by the student. - - `params` which are any extra parameters that may be useful, - e.g., conversation history and summary, conversational style of user, conversation id. +def chat_module(request: ChatRequest) -> ChatResponse: + # EXTRACT FIELDS FROM MUED REQUEST + conversation_id = request.conversationId + if conversation_id is None: + raise Exception("Internal Error: The conversation id is required in the request.") + + context = request.context or {} + task_progress = (request.user.taskProgress or {}) if request.user else {} + current_part_progress = task_progress.get("currentPart", {}) if task_progress else {} - The output of this function is what is returned as the API response - and therefore must be JSON-encodable. It must also conform to the - response schema. + summary = context.get("summary", "") or "" - Any standard python library may be used, as well as any package - available on pip (provided it is added to requirements.txt). + conversationalStyle = "" + if request.user and request.user.preference: + pref = request.user.preference.model_dump() + conversationalStyle = pref.get("conversationalStyle", "") or "" - The way you wish to structure you code (all in this function, or - split into many) is entirely up to you. All that matters are the - return types and that module() is the main function used - to output the Chatbot response. - """ + # All messages except the last are history; last is the current message + conversation_history = _to_langchain_messages(request.messages[:-1]) + message = request.messages[-1].content - result = Result() + # Transform mued context/taskProgress into old parse_json_to_prompt format + question_information = QuestionDetails(**_build_question_information(context)) if context.get("question") else None + question_submission_summary = [StudentWorkResponseArea(**s) for s in _build_submission_summary(current_part_progress.get("responseAreas", []))] + question_access_information = QuestionAccessInformation(**_build_access_information(task_progress)) if task_progress else None - # EXTRACT PARAMETERS - conversation_id = params.get("conversation_id", None) - if conversation_id is None: - raise Exception("Internal Error: The conversation id is required in the parameters of the chat module.") - - include_test_data = params.get("include_test_data", False) or False - conversation_history = params.get("conversation_history", []) or [] - summary = params.get("summary", "") or "" - conversationalStyle = params.get("conversational_style", "") or "" - - question_response_details = params.get("question_response_details", {}) - if isinstance(question_response_details, dict): - question_submission_summary = question_response_details.get("questionSubmissionSummary", []) - question_information = question_response_details.get("questionInformation", {}) - question_access_information = question_response_details.get("questionAccessInformation", {}) - else: - print("ERROR:: question_response_details is not a dict") - raise Exception("Internal Error: The question response details parameter is malformed.") - - # PARSE QUESTION RESPONSE DETAILS TO PROMPT + # PARSE QUESTION CONTEXT TO PROMPT try: question_response_details_prompt = parse_json_to_prompt( question_submission_summary, question_information, - question_access_information + question_access_information, ) except Exception as e: print("ERROR:: ", e) raise Exception("Internal Error: The question response details could not be parsed.") - # RUN THE AGENT AND MEASURE PROCESSING TIME start_time = time.time() + chatbot_response = invoke_base_agent( + query=message, + conversation_history=conversation_history, + summary=summary, + conversationalStyle=conversationalStyle, + question_response_details=question_response_details_prompt, + session_id=conversation_id, + ) + end_time = time.time() - chatbot_response = invoke_base_agent(query=message, \ - conversation_history=conversation_history, \ - summary=summary, \ - conversationalStyle=conversationalStyle, \ - question_response_details=question_response_details_prompt, \ - session_id=conversation_id) + return ChatResponse( + output=Message(role=Role.ASSISTANT, content=chatbot_response["output"]), + metadata={ + "summary": chatbot_response["intermediate_steps"][0], + "conversationalStyle": chatbot_response["intermediate_steps"][1], + "processingTimeMs": round((end_time - start_time) * 1000), + }, + ) - end_time = time.time() - result._processing_time = end_time - start_time - result.add_response("chatbot_response", chatbot_response["output"]) - result.add_metadata("summary", chatbot_response["intermediate_steps"][0]) - result.add_metadata("conversational_style", chatbot_response["intermediate_steps"][1]) - result.add_metadata("conversation_history", chatbot_response["intermediate_steps"][2]) - result.add_processing_time(end_time - start_time) +def _to_langchain_messages(messages): + result = [] + for m in messages: + role = str(m.role).upper() + if role == "USER": + result.append(HumanMessage(content=m.content)) + elif role == "ASSISTANT": + result.append(AIMessage(content=m.content)) + elif role == "SYSTEM": + result.append(SystemMessage(content=m.content)) + return result + + +def _build_question_information(context: dict) -> dict: + set_data = context.get("set", {}) + question_data = context.get("question", {}) + return { + "setNumber": set_data.get("number"), + "setName": set_data.get("title"), + "setDescription": set_data.get("description"), + "questionNumber": question_data.get("number"), + "questionTitle": question_data.get("title"), + "questionGuidance": question_data.get("guidance"), + "questionContent": question_data.get("content"), + "durationLowerBound": None, + "durationUpperBound": None, + "parts": [ + _transform_part(p, i) for i, p in enumerate(question_data.get("parts", []), 1) + ], + } + + +def _transform_part(p: dict, position: int) -> dict: + return { + "publishedPartId": p.get("partId"), + "publishedPartPosition": p.get("position", position), + "publishedPartContent": p.get("content"), + "publishedPartAnswerContent": p.get("answerContent"), + "publishedWorkedSolutionSections": p.get("workedSolutionSections", []), + "publishedStructuredTutorialSections": p.get("structuredTutorialSections", []), + "publishedResponseAreas": [ + _transform_response_area(ra, j) + for j, ra in enumerate(p.get("responseAreas", []), 1) + ], + } + + +def _transform_response_area(ra: dict, position: int) -> dict: + ra_id = ra.get("responseAreaId") + return { + "id": ra_id, + "position": ra.get("position", position), + "universalResponseAreaId": ra_id, + "preResponseText": ra.get("preResponseText"), + "responseType": ra.get("responseType"), + "answer": ra.get("answer"), + } + + +def _build_submission_summary(submissions: list) -> list: + return [ + { + "publishedPartId": None, + "publishedPartPosition": None, + "publishedResponseAreaId": s.get("responseAreaId"), + "publishedResponseAreaPosition": None, + "responseAreaUniversalId": s.get("responseAreaId"), + "publishedResponseAreaPreResponseText": None, + "publishedResponseType": s.get("responseType"), + "publishedResponseConfig": None, + "totalSubmissions": s.get("totalSubmissions"), + "totalWrongSubmissions": s.get("wrongSubmissions"), + "latestSubmission": s.get("latestSubmission"), + } + for s in submissions + ] + - return result.to_dict(include_test_data=include_test_data) \ No newline at end of file +def _build_access_information(task_progress: dict) -> dict: + current_part = task_progress.get("currentPart", {}) + return { + "estimatedMinimumTime": None, + "estimaredMaximumTime": None, + "timeTaken": task_progress.get("timeSpentOnQuestion"), + "accessStatus": task_progress.get("accessStatus"), + "markedDone": task_progress.get("markedDone"), + "currentPart": { + "id": current_part.get("partId"), + "position": current_part.get("position"), + "universalPartId": current_part.get("partId"), + "timeTakenPart": current_part.get("timeSpentOnPart"), + "markedDonePart": current_part.get("markedDone"), + }, + } diff --git a/tests/manual_agent_run.py b/tests/manual_agent_run.py index b69f609..5089c7d 100644 --- a/tests/manual_agent_run.py +++ b/tests/manual_agent_run.py @@ -1,46 +1,37 @@ """ - Conversation turn-based Testbench of the agent's performance. - Select an example input file and write your query. Then run the agent to get the response. +Conversation turn-based Testbench of the agent's performance. +Select an example input file and write your query. Then run the agent to get the response. """ import json +from lf_toolkit.chat import ChatRequest from src.module import chat_module # File path for the input text path = "src/agent/utils/example_inputs/" -input_file = path + "example_input_1.json" +input_file = path + "example_input_3.json" # Step 1: Read the input file with open(input_file, "r") as file: raw_text = file.read() - -# Step 5: Parse into JSON + try: parsed_json = json.loads(raw_text) - """ - STEP 2: Extract the parameters from the JSON - """ # NOTE: #### This is the testing message ##### - message = "Hi, how do I solve this problem?" + message = "What are my submission attempts and feedback for the current part?" # NOTE: ######################################## - # In the JSON, replace "mock" in the message and conversation history with the testing message - parsed_json["message"] = message - parsed_json["params"]["conversation_history"][-1]["content"] = message + # Replace the last user message with the testing message + parsed_json["messages"][-1]["content"] = message + + # Step 2: Build and validate the ChatRequest + request = ChatRequest.model_validate(parsed_json) - params = parsed_json["params"] + # Step 3: Call the chat module + response = chat_module(request) - """ - STEP 3: Call the chat module to get a response to the user's message - """ - response = chat_module(message, params) - - print(json.dumps(response, indent=4)) - + print(response.model_dump_json(indent=4)) except json.JSONDecodeError as e: print("Error decoding JSON:", e) - - - diff --git a/tests/test_example_inputs.py b/tests/test_example_inputs.py new file mode 100644 index 0000000..f384f35 --- /dev/null +++ b/tests/test_example_inputs.py @@ -0,0 +1,171 @@ +""" +Verifies that all key attributes from the example input JSON files are correctly +loaded through the parsing pipeline before being sent to the LLM. + +These tests do NOT call the LLM — they only exercise the data transformation +layer (module.py helpers → typed objects → prompt string). +""" + +import json +import pytest +from pathlib import Path + +from lf_toolkit.chat import ChatRequest +from src.agent.utils.parse_json_context_to_prompt import ( + QuestionDetails, + StudentWorkResponseArea, + QuestionAccessInformation, + parse_json_to_prompt, +) +from src.module import ( + _build_question_information, + _build_submission_summary, + _build_access_information, +) + +EXAMPLE_INPUTS_DIR = Path("src/agent/utils/example_inputs") +EXAMPLE_FILES = sorted(EXAMPLE_INPUTS_DIR.glob("example_input_*.json")) + + +def load_example(path: Path) -> dict: + with open(path) as f: + return json.load(f) + + +def _get_task_progress(data: dict) -> dict: + return ((data.get("user") or {}).get("taskProgress") or {}) + + +def _get_submissions(data: dict) -> list: + return _get_task_progress(data).get("currentPart", {}).get("responseAreas", []) + + +# --------------------------------------------------------------------------- +# Parametrize over all example files +# --------------------------------------------------------------------------- + +@pytest.mark.parametrize("path", EXAMPLE_FILES, ids=[p.name for p in EXAMPLE_FILES]) +class TestExampleInputLoading: + + def test_parses_as_valid_chat_request(self, path): + """JSON can be validated as a ChatRequest without errors.""" + data = load_example(path) + request = ChatRequest.model_validate(data) + assert len(request.messages) >= 1 + + def test_question_information_fields(self, path): + """question.title, content, and part count survive the transform.""" + data = load_example(path) + context = data.get("context", {}) + if not context.get("question"): + pytest.skip("no question context") + + q_data = context["question"] + q_info = QuestionDetails(**_build_question_information(context)) + + assert q_info.questionTitle == q_data.get("title") + assert q_info.questionContent == q_data.get("content") + assert len(q_info.parts) == len(q_data.get("parts", [])) + + def test_parts_load_with_correct_ids_and_positions(self, path): + """Each part's partId and position are preserved after transform.""" + data = load_example(path) + context = data.get("context", {}) + if not context.get("question"): + pytest.skip("no question context") + + q_info = QuestionDetails(**_build_question_information(context)) + for part_obj, part_raw in zip(q_info.parts, context["question"].get("parts", [])): + assert part_obj.publishedPartId == part_raw["partId"], \ + f"partId mismatch: {part_obj.publishedPartId} != {part_raw['partId']}" + assert part_obj.publishedPartPosition == part_raw["position"], \ + f"position mismatch for part {part_raw['partId']}" + + def test_response_areas_load_with_correct_ids_and_answers(self, path): + """Each responseArea's id, position, and answer are preserved after transform.""" + data = load_example(path) + context = data.get("context", {}) + if not context.get("question"): + pytest.skip("no question context") + + q_info = QuestionDetails(**_build_question_information(context)) + for part_obj, part_raw in zip(q_info.parts, context["question"]["parts"]): + for ra_obj, ra_raw in zip(part_obj.publishedResponseAreas, part_raw.get("responseAreas", [])): + expected_id = ra_raw["responseAreaId"] + assert ra_obj.universalResponseAreaId == expected_id, \ + f"responseAreaId mismatch in part {part_raw['partId']}: got {ra_obj.universalResponseAreaId}" + assert ra_obj.position == ra_raw["position"], \ + f"responseArea position mismatch for {expected_id}" + assert ra_obj.answer == ra_raw["answer"], \ + f"answer mismatch for {expected_id}" + + def test_submission_summary_fields(self, path): + """Submission responseAreaId, counts, and latestSubmission feedback survive the transform.""" + submissions_raw = _get_submissions(load_example(path)) + if not submissions_raw: + pytest.skip("no submissions in this example") + + summaries = [StudentWorkResponseArea(**s) for s in _build_submission_summary(submissions_raw)] + for summary, s_raw in zip(summaries, submissions_raw): + expected_id = s_raw["responseAreaId"] + assert summary.publishedResponseAreaId == expected_id, \ + f"responseAreaId mismatch: got {summary.publishedResponseAreaId}" + assert summary.totalSubmissions == s_raw["totalSubmissions"], \ + f"totalSubmissions mismatch for {expected_id}" + assert summary.totalWrongSubmissions == s_raw["wrongSubmissions"], \ + f"wrongSubmissions mismatch for {expected_id}" + + ls_raw = s_raw.get("latestSubmission") + if ls_raw: + assert summary.latestSubmission is not None + assert summary.latestSubmission.feedback == ls_raw.get("feedback") + assert summary.latestSubmission.answer == ls_raw.get("answer") + else: + assert summary.latestSubmission is None + + def test_access_information_fields(self, path): + """taskProgress fields (timeTaken, accessStatus, currentPart id/position) are preserved.""" + data = load_example(path) + task_progress = _get_task_progress(data) + if not task_progress: + pytest.skip("no taskProgress in this example") + + access_info = QuestionAccessInformation(**_build_access_information(task_progress)) + current_part_raw = task_progress.get("currentPart", {}) + + assert access_info.timeTaken == task_progress.get("timeSpentOnQuestion") + assert access_info.accessStatus == task_progress.get("accessStatus") + assert access_info.currentPart.id == current_part_raw.get("partId") + assert access_info.currentPart.position == current_part_raw.get("position") + assert access_info.currentPart.timeTakenPart == current_part_raw.get("timeSpentOnPart") + + def test_prompt_is_generated_and_contains_question_title(self, path): + """Full pipeline produces a non-empty prompt that includes the question title.""" + data = load_example(path) + context = data.get("context", {}) + if not context.get("question"): + pytest.skip("no question context") + + task_progress = _get_task_progress(data) + current_part_progress = task_progress.get("currentPart", {}) + + question_information = QuestionDetails(**_build_question_information(context)) + question_submission_summary = [ + StudentWorkResponseArea(**s) + for s in _build_submission_summary(current_part_progress.get("responseAreas", [])) + ] + question_access_information = ( + QuestionAccessInformation(**_build_access_information(task_progress)) + if task_progress else None + ) + + prompt = parse_json_to_prompt( + question_submission_summary, + question_information, + question_access_information, + ) + + assert isinstance(prompt, str) and len(prompt) > 0 + title = context["question"].get("title", "") + if title: + assert title in prompt, f"Question title '{title}' not found in prompt" diff --git a/tests/test_index.py b/tests/test_index.py index 822049d..4cf4ea8 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -2,6 +2,17 @@ import json from index import handler + +def make_event(body: dict) -> dict: + return {"body": json.dumps(body)} + + +BASE_BODY = { + "messages": [{"role": "USER", "content": "Hello, World"}], + "conversationId": "1234Test", +} + + class TestChatIndexFunction(unittest.TestCase): """ TestCase Class used to test the algorithm. @@ -20,43 +31,26 @@ class TestChatIndexFunction(unittest.TestCase): Use module() to check your algorithm works as it should. - The expected input of the hander is a JsonType. + The expected input of the handler is a JsonType matching ChatRequest. """ - def test_missing_argument(self): - arguments = ["message", "params"] + def test_missing_messages(self): + # messages is required — omitting it should return 400 + body = {k: v for k, v in BASE_BODY.items() if k != "messages"} + result = handler(make_event(body), None) + self.assertEqual(result.get("statusCode"), 400) - for arg in arguments: - event = { - "message": "Hello, World", - "params": {"conversation_id": "1234Test", "conversation_history": [{"type": "user", "content": "Hello, World"}]} - } - event.pop(arg) - event = {"body":json.dumps(event)} + def test_invalid_json_body(self): + result = handler({"body": "not valid json"}, None) + self.assertEqual(result.get("statusCode"), 400) - result = handler(event, None) - - self.assertEqual(result.get("statusCode"), 400) - def test_correct_arguments(self): - event = { - "message": "Hello, World", - "params": {"conversation_id": "1234Test", "conversation_history": [{"type": "user", "content": "Hello, World"}]} - } - event = {"body":json.dumps(event)} - - result = handler(event, None) - + result = handler(make_event(BASE_BODY), None) self.assertEqual(result.get("statusCode"), 200) def test_correct_response(self): - event = { - "message": "Hello, World", - "params": {"conversation_id": "1234Test", "conversation_history": [{"type": "user", "content": "Hello, World"}]} - } - event = {"body":json.dumps(event)} - - result = handler(event, None) - + result = handler(make_event(BASE_BODY), None) self.assertEqual(result.get("statusCode"), 200) - \ No newline at end of file + body = json.loads(result["body"]) + self.assertIn("output", body) + self.assertIn("content", body["output"]) diff --git a/tests/test_module.py b/tests/test_module.py index 26c96de..dff68bb 100755 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -1,8 +1,17 @@ import unittest -from lf_toolkit.chat.result import ChatResult as Result -from lf_toolkit.chat.params import ChatParams as Params +from lf_toolkit.chat import ChatRequest, ChatResponse from src.module import chat_module + +def make_request(**kwargs): + defaults = { + "messages": [{"role": "USER", "content": "Hello, World"}], + "conversationId": "1234Test", + } + defaults.update(kwargs) + return ChatRequest.model_validate(defaults) + + class TestChatModuleFunction(unittest.TestCase): """ TestCase Class used to test the algorithm. @@ -22,63 +31,32 @@ class TestChatModuleFunction(unittest.TestCase): as it should. """ - def test_missing_parameters(self): - # Checking state for missing parameters on default agent - response = "Hello, World" - expected_params = Params(include_test_data=True, conversation_history=['{ "type": "user", "content": response }'], \ - summary="", conversational_style="", \ - question_response_details={}, conversation_id="1234Test") - - for p in expected_params: - params = expected_params.copy() - # except for the special parameters - if p not in ["include_test_data", "conversation_id", "conversation_history"]: - params.pop(p) - - result = chat_module(response, params) - - self.assertIsNotNone(result) - self.assertEqual("error" in result, False) - elif p == "include_test_data": - params.pop(p) - - result = chat_module(response, params) - - # check if result has nothing except for the chatbot_response - self.assertIsNotNone(result.get("chatbot_response")) - self.assertEqual(len(result), 1) - elif p == "conversation_id": - params.pop(p) - - with self.assertRaises(Exception) as cm: - chat_module(response, params) - - self.assertTrue("Internal Error" in str(cm.exception)) - self.assertTrue("conversation id" in str(cm.exception)) - elif p == "conversation_history": - params.pop(p) + def test_missing_conversation_id(self): + # conversationId is required by chat_module even though it's optional in ChatRequest + request = make_request(conversationId=None) - with self.assertRaises(Exception) as cm: - chat_module(response, params) + with self.assertRaises(Exception) as cm: + chat_module(request) - self.assertTrue("Internal Error" in str(cm.exception)) - self.assertTrue("conversation history" in str(cm.exception)) + self.assertIn("Internal Error", str(cm.exception)) + self.assertIn("conversation id", str(cm.exception)) def test_agent_output(self): # Checking the output of the agent - response = "Hello, World" - params = Params(conversation_id="1234Test", conversation_history=['{ "type": "user", "content": response }']) + request = make_request() - result = chat_module(response, params) + result = chat_module(request) - self.assertIsNotNone(result.get("chatbot_response")) + self.assertIsInstance(result, ChatResponse) + self.assertIsNotNone(result.output) + self.assertIsNotNone(result.output.content) - def test_processing_time_calc(self): - # Checking the processing time calculation - response = "Hello, World" - params = Params(include_test_data=True, conversation_id="1234Test", conversation_history=['{ "type": "user", "content": response }']) + def test_processing_time_in_metadata(self): + # Checking the processing time is included in the response metadata + request = make_request() - result = chat_module(response, params) + result = chat_module(request) - self.assertIsNotNone(result.get("processing_time")) - self.assertGreaterEqual(result.get("processing_time"), 0) \ No newline at end of file + self.assertIsNotNone(result.metadata) + self.assertIn("processingTimeMs", result.metadata) + self.assertGreaterEqual(result.metadata["processingTimeMs"], 0) From 694288d196232520d6e6ece39e666b4495fd571b Mon Sep 17 00:00:00 2001 From: neagualexa Date: Thu, 19 Mar 2026 17:08:43 +0000 Subject: [PATCH 02/10] fix estimated time --- src/agent/utils/parse_json_context_to_prompt.py | 3 +++ src/agent/utils/prompt_context_templates.py | 4 +++- src/module.py | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/agent/utils/parse_json_context_to_prompt.py b/src/agent/utils/parse_json_context_to_prompt.py index a74314e..f7c3039 100644 --- a/src/agent/utils/parse_json_context_to_prompt.py +++ b/src/agent/utils/parse_json_context_to_prompt.py @@ -97,6 +97,7 @@ def __init__( questionTitle: Optional[str] = None, questionGuidance: Optional[str] = None, questionContent: Optional[str] = None, + estimatedTime: Optional[str] = None, durationLowerBound: Optional[int] = None, durationUpperBound: Optional[int] = None, parts: Optional[List[PartDetails]] = [], @@ -108,6 +109,7 @@ def __init__( self.questionTitle = questionTitle self.questionGuidance = questionGuidance self.questionContent = questionContent + self.estimatedTime = estimatedTime self.durationLowerBound = durationLowerBound self.durationUpperBound = durationUpperBound self.parts = [PartDetails(**part) for part in parts] @@ -193,6 +195,7 @@ def parse_json_to_structured_prompt( 'title': question_info.questionTitle, 'guidance': question_info.questionGuidance, 'content': question_info.questionContent, + 'estimated_time': question_info.estimatedTime, 'duration_lower': question_info.durationLowerBound, 'duration_upper': question_info.durationUpperBound } diff --git a/src/agent/utils/prompt_context_templates.py b/src/agent/utils/prompt_context_templates.py index 037b5ba..94442e4 100644 --- a/src/agent/utils/prompt_context_templates.py +++ b/src/agent/utils/prompt_context_templates.py @@ -56,7 +56,9 @@ def format_question_header( # Duration formatting duration_text = "- Expected Duration: " - if question_info.get('duration_lower') and question_info.get('duration_upper'): + if question_info.get('estimated_time'): + duration_text += question_info['estimated_time'] + elif question_info.get('duration_lower') and question_info.get('duration_upper'): duration_text += f"{question_info['duration_lower']}-{question_info['duration_upper']} minutes" else: duration_text += "Not specified" diff --git a/src/module.py b/src/module.py index 8e278d9..72d174c 100755 --- a/src/module.py +++ b/src/module.py @@ -96,6 +96,7 @@ def _build_question_information(context: dict) -> dict: "questionTitle": question_data.get("title"), "questionGuidance": question_data.get("guidance"), "questionContent": question_data.get("content"), + "estimatedTime": question_data.get("estimatedTime"), "durationLowerBound": None, "durationUpperBound": None, "parts": [ From 52be49d0ff1559da1ac246fef368410bac04af4c Mon Sep 17 00:00:00 2001 From: neagualexa Date: Fri, 20 Mar 2026 14:04:20 +0000 Subject: [PATCH 03/10] update readme with mued --- README.md | 108 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1bf3503..b1892c1 100755 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ You agent can be based on an LLM hosted anywhere, you have available currently O └── tests/ # contains all tests for the chat function ├── manual_agent_requests.py # allows testing of the docker container through API requests ├── manual_agent_run.py # allows testing of any LLM agent on a couple of example inputs + ├── test_example_inputs.py # pytests for the example input files ├── test_index.py # pytests └── test_module.py # pytests ``` @@ -165,7 +166,7 @@ This will start the chat function and expose it on port `8080` and it will be op ```bash curl --location 'http://localhost:8080/2015-03-31/functions/function/invocations' \ --header 'Content-Type: application/json' \ ---data '{"body":"{\"message\": \"hi\", \"params\": {\"conversation_id\": \"12345Test\", \"conversation_history\": [{\"type\": \"user\", +--data '{"body":"{\"conversationId\": \"12345Test\", \"messages\": [{\"role\": \"USER\", \"content\": \"hi\"}], \"user\": {\"type\": \"LEARNER\"}}"}' ``` #### Call Docker Container @@ -184,21 +185,104 @@ http://localhost:8080/2015-03-31/functions/function/invocations Body (stringified within body for API request): ```JSON -{"body":"{\"message\": \"hi\", \"params\": {\"conversation_id\": \"12345Test\", \"conversation_history\": [{\"type\": \"user\", \"content\": \"hi\"}]}}"} +{"body":"{\"conversationId\": \"12345Test\", \"messages\": [{\"role\": \"USER\", \"content\": \"hi\"}], \"user\": {\"type\": \"LEARNER\"}}"} ``` -Body with optional Params: -```JSON +Body with optional fields: +```json { - "message":"hi", - "params":{ - "conversation_id":"12345Test", - "conversation_history":[{"type":"user","content":"hi"}], - "summary":" ", - "conversational_style":" ", - "question_response_details": "", - "include_test_data": true, + "conversationId": "", + "messages": [ + { "role": "USER", "content": "" }, + { "role": "ASSISTANT", "content": "" }, + { "role": "USER", "content": "" } + ], + "user": { + "userId": "", + "type": "LEARNER", + "preference": { + "conversationalStyle": "" + }, + "taskProgress": { + "currentQuestionId": "", + "timeSpentOnQuestion": "30 minutes", + "accessStatus": "a good amount of time spent on this question today.", + "markedDone": "This question is still being worked on.", + "currentPart": { + "partId": "", + "position": 0, + "timeSpentOnPart": "10 minutes", + "markedDone": "This part is not marked done.", + "responseAreas": [ + { + "responseAreaId": "", + "responseType": "EXPRESSION", + "totalSubmissions": 3, + "wrongSubmissions": 2, + "latestSubmission": { + "submission": "", + "feedback": "", + "answer": "" + } + } + ] + } + } + }, + "context": { + "summary": "", + "set": { + "title": "Fundamentals", + "number": 2, + "description": "" + }, + "question": { + "title": "Understanding Polymorphism", + "number": 3, + "guidance": "", + "content": "", + "estimatedTime": "15-25 minutes", + "parts": [ + { + "partId": "", + "position": 0, + "content": "", + "answerContent": "", + "workedSolutionSections": [ + { "id": "", "position": 0, "title": "Step 1", "content": "..." } + ], + "structuredTutorialSections": [ + { "id": "", "position": 0, "title": "Hint", "content": "..." } + ], + "responseAreas": [ + { + "responseAreaId": "", + "position": 0, + "responseType": "EXPRESSION", + "answer": "", + "preResponseText": "