Meet Patel commited on
Commit
8372659
·
1 Parent(s): 0228818

Step 5: Added Gradio web interface and run script for the TutorX MCP server

Browse files
Files changed (3) hide show
  1. app.py +280 -0
  2. pyproject.toml +4 -1
  3. run.py +93 -0
app.py ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Gradio web interface for the TutorX MCP Server
3
+ """
4
+
5
+ import gradio as gr
6
+ import numpy as np
7
+ import json
8
+ import base64
9
+ from io import BytesIO
10
+ from PIL import Image
11
+ from datetime import datetime
12
+
13
+ # Import MCP server tools
14
+ from main import (
15
+ # Core features
16
+ assess_skill,
17
+ get_concept_graph,
18
+ get_learning_path,
19
+ generate_quiz,
20
+ analyze_error_patterns,
21
+
22
+ # Advanced features
23
+ analyze_cognitive_state,
24
+ get_curriculum_standards,
25
+ align_content_to_standard,
26
+ generate_lesson,
27
+
28
+ # User experience
29
+ get_student_dashboard,
30
+ get_accessibility_settings,
31
+ update_accessibility_settings,
32
+
33
+ # Multi-modal
34
+ text_interaction,
35
+ voice_interaction,
36
+ handwriting_recognition,
37
+
38
+ # Assessment
39
+ create_assessment,
40
+ grade_assessment,
41
+ get_student_analytics,
42
+ check_submission_originality
43
+ )
44
+
45
+ # Utility functions
46
+ def image_to_base64(img):
47
+ """Convert a PIL image or numpy array to base64 string"""
48
+ if isinstance(img, np.ndarray):
49
+ img = Image.fromarray(img)
50
+
51
+ buffered = BytesIO()
52
+ img.save(buffered, format="PNG")
53
+ img_str = base64.b64encode(buffered.getvalue()).decode()
54
+ return img_str
55
+
56
+ def format_json(data):
57
+ """Format JSON data for display"""
58
+ return json.dumps(data, indent=2)
59
+
60
+ # Create Gradio interface
61
+ with gr.Blocks(title="TutorX Educational AI", theme=gr.themes.Soft()) as demo:
62
+ gr.Markdown("# 📚 TutorX Educational AI Platform")
63
+ gr.Markdown("""
64
+ An adaptive, multi-modal, and collaborative AI tutoring platform built with MCP.
65
+
66
+ This interface demonstrates the functionality of the TutorX MCP server.
67
+ """)
68
+
69
+ # Set a default student ID for the demo
70
+ student_id = "student_12345"
71
+
72
+ with gr.Tabs() as tabs:
73
+ # Tab 1: Core Features
74
+ with gr.Tab("Core Features"):
75
+ gr.Markdown("## Adaptive Learning Engine")
76
+
77
+ with gr.Row():
78
+ with gr.Column():
79
+ concept_id_input = gr.Dropdown(
80
+ choices=["math_algebra_basics", "math_algebra_linear_equations", "math_algebra_quadratic_equations"],
81
+ label="Select Concept",
82
+ value="math_algebra_linear_equations"
83
+ )
84
+ assess_btn = gr.Button("Assess Skill")
85
+
86
+ with gr.Column():
87
+ assessment_output = gr.JSON(label="Skill Assessment")
88
+
89
+ assess_btn.click(
90
+ fn=lambda concept: assess_skill(student_id, concept),
91
+ inputs=[concept_id_input],
92
+ outputs=[assessment_output]
93
+ )
94
+
95
+ gr.Markdown("## Concept Graph")
96
+ concept_graph_btn = gr.Button("Show Concept Graph")
97
+ concept_graph_output = gr.JSON(label="Concept Graph")
98
+
99
+ concept_graph_btn.click(
100
+ fn=lambda: get_concept_graph(),
101
+ inputs=[],
102
+ outputs=[concept_graph_output]
103
+ )
104
+
105
+ gr.Markdown("## Assessment Generation")
106
+ with gr.Row():
107
+ with gr.Column():
108
+ concepts_input = gr.CheckboxGroup(
109
+ choices=["math_algebra_basics", "math_algebra_linear_equations", "math_algebra_quadratic_equations"],
110
+ label="Select Concepts",
111
+ value=["math_algebra_linear_equations"]
112
+ )
113
+ diff_input = gr.Slider(minimum=1, maximum=5, value=2, step=1, label="Difficulty")
114
+ gen_quiz_btn = gr.Button("Generate Quiz")
115
+
116
+ with gr.Column():
117
+ quiz_output = gr.JSON(label="Generated Quiz")
118
+
119
+ gen_quiz_btn.click(
120
+ fn=lambda concepts, diff: generate_quiz(concepts, diff),
121
+ inputs=[concepts_input, diff_input],
122
+ outputs=[quiz_output]
123
+ )
124
+
125
+ # Tab 2: Advanced Features
126
+ with gr.Tab("Advanced Features"):
127
+ gr.Markdown("## Lesson Generation")
128
+
129
+ with gr.Row():
130
+ with gr.Column():
131
+ topic_input = gr.Textbox(label="Lesson Topic", value="Solving Quadratic Equations")
132
+ grade_input = gr.Slider(minimum=1, maximum=12, value=9, step=1, label="Grade Level")
133
+ duration_input = gr.Slider(minimum=15, maximum=90, value=45, step=5, label="Duration (minutes)")
134
+ gen_lesson_btn = gr.Button("Generate Lesson Plan")
135
+
136
+ with gr.Column():
137
+ lesson_output = gr.JSON(label="Lesson Plan")
138
+
139
+ gen_lesson_btn.click(
140
+ fn=lambda topic, grade, duration: generate_lesson(topic, grade, duration),
141
+ inputs=[topic_input, grade_input, duration_input],
142
+ outputs=[lesson_output]
143
+ )
144
+
145
+ gr.Markdown("## Curriculum Standards")
146
+
147
+ with gr.Row():
148
+ with gr.Column():
149
+ country_input = gr.Dropdown(
150
+ choices=["us", "uk"],
151
+ label="Country",
152
+ value="us"
153
+ )
154
+ standards_btn = gr.Button("Get Standards")
155
+
156
+ with gr.Column():
157
+ standards_output = gr.JSON(label="Curriculum Standards")
158
+
159
+ standards_btn.click(
160
+ fn=lambda country: get_curriculum_standards(country),
161
+ inputs=[country_input],
162
+ outputs=[standards_output]
163
+ )
164
+
165
+ # Tab 3: Multi-Modal Interaction
166
+ with gr.Tab("Multi-Modal Interaction"):
167
+ gr.Markdown("## Text Interaction")
168
+
169
+ with gr.Row():
170
+ with gr.Column():
171
+ text_input = gr.Textbox(label="Ask a Question", value="How do I solve a quadratic equation?")
172
+ text_btn = gr.Button("Submit")
173
+
174
+ with gr.Column():
175
+ text_output = gr.JSON(label="Response")
176
+
177
+ text_btn.click(
178
+ fn=lambda query: text_interaction(query, student_id),
179
+ inputs=[text_input],
180
+ outputs=[text_output]
181
+ )
182
+
183
+ gr.Markdown("## Handwriting Recognition")
184
+
185
+ with gr.Row():
186
+ with gr.Column():
187
+ drawing_input = gr.Sketchpad(label="Draw an Equation")
188
+ drawing_btn = gr.Button("Recognize")
189
+
190
+ with gr.Column():
191
+ drawing_output = gr.JSON(label="Recognition Results")
192
+
193
+ # Convert drawing to base64 then process
194
+ drawing_btn.click(
195
+ fn=lambda img: handwriting_recognition(image_to_base64(img), student_id),
196
+ inputs=[drawing_input],
197
+ outputs=[drawing_output]
198
+ )
199
+
200
+ # Tab 4: Analytics
201
+ with gr.Tab("Analytics"):
202
+ gr.Markdown("## Student Performance")
203
+ analytics_btn = gr.Button("Generate Analytics Report")
204
+ timeframe = gr.Slider(minimum=7, maximum=90, value=30, step=1, label="Timeframe (days)")
205
+ analytics_output = gr.JSON(label="Performance Analytics")
206
+
207
+ analytics_btn.click(
208
+ fn=lambda days: get_student_analytics(student_id, days),
209
+ inputs=[timeframe],
210
+ outputs=[analytics_output]
211
+ )
212
+
213
+ gr.Markdown("## Error Pattern Analysis")
214
+
215
+ error_concept = gr.Dropdown(
216
+ choices=["math_algebra_basics", "math_algebra_linear_equations", "math_algebra_quadratic_equations"],
217
+ label="Select Concept for Error Analysis",
218
+ value="math_algebra_linear_equations"
219
+ )
220
+ error_btn = gr.Button("Analyze Errors")
221
+ error_output = gr.JSON(label="Error Pattern Analysis")
222
+
223
+ error_btn.click(
224
+ fn=lambda concept: analyze_error_patterns(student_id, concept),
225
+ inputs=[error_concept],
226
+ outputs=[error_output]
227
+ )
228
+
229
+ # Tab 5: Assessment Tools
230
+ with gr.Tab("Assessment Tools"):
231
+ gr.Markdown("## Create Assessment")
232
+
233
+ with gr.Row():
234
+ with gr.Column():
235
+ assess_concepts = gr.CheckboxGroup(
236
+ choices=["math_algebra_basics", "math_algebra_linear_equations", "math_algebra_quadratic_equations"],
237
+ label="Select Concepts",
238
+ value=["math_algebra_linear_equations"]
239
+ )
240
+ assess_questions = gr.Slider(minimum=1, maximum=10, value=3, step=1, label="Number of Questions")
241
+ assess_diff = gr.Slider(minimum=1, maximum=5, value=3, step=1, label="Difficulty")
242
+ create_assess_btn = gr.Button("Create Assessment")
243
+
244
+ with gr.Column():
245
+ assessment_output = gr.JSON(label="Generated Assessment")
246
+
247
+ create_assess_btn.click(
248
+ fn=lambda concepts, num, diff: create_assessment(concepts, num, diff),
249
+ inputs=[assess_concepts, assess_questions, assess_diff],
250
+ outputs=[assessment_output]
251
+ )
252
+
253
+ gr.Markdown("## Plagiarism Detection")
254
+
255
+ with gr.Row():
256
+ with gr.Column():
257
+ submission_input = gr.Textbox(
258
+ label="Student Submission",
259
+ lines=5,
260
+ value="The quadratic formula states that if ax² + bx + c = 0, then x = (-b ± √(b² - 4ac)) / 2a."
261
+ )
262
+ reference_input = gr.Textbox(
263
+ label="Reference Source",
264
+ lines=5,
265
+ value="According to the quadratic formula, for any equation in the form ax² + bx + c = 0, the solutions are x = (-b ± √(b² - 4ac)) / 2a."
266
+ )
267
+ plagiarism_btn = gr.Button("Check Originality")
268
+
269
+ with gr.Column():
270
+ plagiarism_output = gr.JSON(label="Originality Report")
271
+
272
+ plagiarism_btn.click(
273
+ fn=lambda sub, ref: check_submission_originality(sub, [ref]),
274
+ inputs=[submission_input, reference_input],
275
+ outputs=[plagiarism_output]
276
+ )
277
+
278
+ # Launch the app
279
+ if __name__ == "__main__":
280
+ demo.launch()
pyproject.toml CHANGED
@@ -1,9 +1,12 @@
1
  [project]
2
  name = "tutorx-mcp"
3
  version = "0.1.0"
4
- description = "Add your description here"
5
  readme = "README.md"
6
  requires-python = ">=3.12"
7
  dependencies = [
8
  "mcp[cli]>=1.9.3",
 
 
 
9
  ]
 
1
  [project]
2
  name = "tutorx-mcp"
3
  version = "0.1.0"
4
+ description = "Educational AI Tutor MCP Server"
5
  readme = "README.md"
6
  requires-python = ">=3.12"
7
  dependencies = [
8
  "mcp[cli]>=1.9.3",
9
+ "gradio>=4.19.0",
10
+ "numpy>=1.24.0",
11
+ "pillow>=10.0.0",
12
  ]
run.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Script to run either the MCP server or the Gradio interface
3
+ """
4
+
5
+ import argparse
6
+ import importlib.util
7
+ import os
8
+ import sys
9
+
10
+ def load_module(name, path):
11
+ """Load a module from path"""
12
+ spec = importlib.util.spec_from_file_location(name, path)
13
+ module = importlib.util.module_from_spec(spec)
14
+ spec.loader.exec_module(module)
15
+ return module
16
+
17
+ def run_mcp_server():
18
+ """Run the MCP server"""
19
+ print("Starting TutorX MCP Server...")
20
+ main_module = load_module("main", "main.py")
21
+
22
+ # Access the mcp instance and run it
23
+ if hasattr(main_module, "mcp"):
24
+ main_module.mcp.run()
25
+ else:
26
+ print("Error: MCP server instance not found in main.py")
27
+ sys.exit(1)
28
+
29
+ def run_gradio_interface():
30
+ """Run the Gradio interface"""
31
+ print("Starting TutorX Gradio Interface...")
32
+ app_module = load_module("app", "app.py")
33
+
34
+ # Run the Gradio demo
35
+ if hasattr(app_module, "demo"):
36
+ app_module.demo.launch()
37
+ else:
38
+ print("Error: Gradio demo not found in app.py")
39
+ sys.exit(1)
40
+
41
+ if __name__ == "__main__":
42
+ parser = argparse.ArgumentParser(description="Run TutorX MCP Server or Gradio Interface")
43
+ parser.add_argument(
44
+ "--mode",
45
+ choices=["mcp", "gradio", "both"],
46
+ default="mcp",
47
+ help="Run mode: 'mcp' for MCP server, 'gradio' for Gradio interface, 'both' for both"
48
+ )
49
+ parser.add_argument(
50
+ "--host",
51
+ default="127.0.0.1",
52
+ help="Host address to use"
53
+ )
54
+ parser.add_argument(
55
+ "--port",
56
+ type=int,
57
+ default=8000,
58
+ help="Port to use"
59
+ )
60
+
61
+ args = parser.parse_args()
62
+
63
+ if args.mode == "mcp":
64
+ # Set environment variables for MCP server
65
+ os.environ["MCP_HOST"] = args.host
66
+ os.environ["MCP_PORT"] = str(args.port)
67
+ run_mcp_server()
68
+ elif args.mode == "gradio":
69
+ run_gradio_interface()
70
+ elif args.mode == "both":
71
+ # For 'both' mode, we'll start MCP server in a separate process
72
+ import subprocess
73
+ import time
74
+
75
+ # Start MCP server in a background process
76
+ mcp_process = subprocess.Popen(
77
+ [sys.executable, "run.py", "--mode", "mcp", "--host", args.host, "--port", str(args.port)],
78
+ stdout=subprocess.PIPE,
79
+ stderr=subprocess.PIPE
80
+ )
81
+
82
+ # Give the MCP server a moment to start up
83
+ print("Starting MCP server in background...")
84
+ time.sleep(2)
85
+
86
+ try:
87
+ # Then start Gradio interface
88
+ run_gradio_interface()
89
+ finally:
90
+ # Make sure to terminate the MCP server process when exiting
91
+ print("Shutting down MCP server...")
92
+ mcp_process.terminate()
93
+ mcp_process.wait(timeout=5)