File size: 30,903 Bytes
f42bec3
 
48c69e0
de63ca4
1682db2
1e5c5ca
8aa3c67
f42bec3
 
 
 
 
 
1e5c5ca
 
 
1682db2
 
 
 
f42bec3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
010b4a2
eba51e9
 
 
90be99b
605e658
90be99b
eba51e9
 
 
cea9643
eba51e9
cc14729
eba51e9
1e5c5ca
cea9643
ec27414
 
90be99b
8504692
1e5c5ca
eba51e9
5428188
 
 
 
 
 
 
 
 
 
 
a1c169c
 
90be99b
 
 
 
a1c169c
90be99b
 
 
 
 
 
a1c169c
90be99b
 
 
41c0767
 
f42bec3
eba51e9
 
7330e27
eba51e9
7330e27
eba51e9
7330e27
cc14729
7330e27
 
 
eba51e9
f42bec3
eba51e9
 
c387cc0
 
1e5c5ca
c387cc0
 
 
 
6ec5c62
c387cc0
 
 
 
 
 
 
 
6ec5c62
 
 
c387cc0
6ec5c62
 
 
 
 
 
 
 
 
c387cc0
 
eba51e9
f42bec3
eba51e9
 
 
 
1e5c5ca
 
 
 
eba51e9
 
 
1e5c5ca
eba51e9
 
f42bec3
 
294337e
 
 
f0867ff
f42bec3
f0867ff
1ce45b3
 
 
 
 
 
 
 
 
 
 
294337e
1ce45b3
 
 
 
 
 
 
 
 
f42bec3
684a4d7
605e658
 
 
 
684a4d7
605e658
684a4d7
 
 
1e5c5ca
684a4d7
605e658
684a4d7
 
 
 
840a3d4
1e5c5ca
684a4d7
 
 
 
 
1e5c5ca
af69c64
684a4d7
 
af69c64
684a4d7
af69c64
 
 
 
 
684a4d7
 
 
76caa8a
684a4d7
af69c64
684a4d7
1e5c5ca
 
 
 
 
 
605e658
 
 
 
 
684a4d7
605e658
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f42bec3
f0867ff
294337e
 
 
 
f42bec3
294337e
f42bec3
f0867ff
294337e
 
f42bec3
294337e
 
 
 
 
f42bec3
294337e
 
f42bec3
2044419
 
b5fbdaa
 
 
 
 
 
4974a84
f42bec3
14d57b2
 
 
c2000d0
14d57b2
02085c5
 
14d57b2
 
 
 
 
 
 
 
 
 
f42bec3
4397085
1e5c5ca
4397085
1e5c5ca
4397085
f42bec3
b5fbdaa
 
 
 
73048aa
b5fbdaa
 
f42bec3
 
ec833a4
61303c0
 
ec833a4
 
27ac1dd
f42bec3
6f497e7
6a10897
6f497e7
 
 
 
f42bec3
6f497e7
 
 
 
f42bec3
6f497e7
 
f42bec3
6f497e7
 
f42bec3
 
 
6f497e7
 
 
 
f42bec3
6f497e7
 
f42bec3
6f497e7
 
 
 
 
 
f42bec3
6f497e7
 
 
 
 
 
f42bec3
6f497e7
 
 
 
f42bec3
6f497e7
 
 
f42bec3
 
 
 
 
 
 
 
 
 
 
 
 
 
27ac1dd
 
 
 
 
 
1ce45b3
 
98fec41
 
27ac1dd
98fec41
 
f42bec3
ec833a4
c1b4062
 
 
 
 
 
 
 
 
 
 
 
 
15aceff
ec833a4
 
 
 
98fec41
f42bec3
1ce45b3
 
 
 
98fec41
f42bec3
 
14d57b2
b5fbdaa
14d57b2
 
98fec41
 
14d57b2
 
 
 
 
 
 
 
 
 
 
 
 
98fec41
 
14d57b2
f42bec3
14d57b2
 
 
4397085
98fec41
 
f5bef0b
 
 
 
09b950e
 
 
 
 
 
 
 
 
98fec41
 
09b950e
f42bec3
b5fbdaa
 
73048aa
b5fbdaa
98fec41
 
f5bef0b
 
 
 
 
 
 
98fec41
f42bec3
b5fbdaa
 
 
98fec41
 
f5bef0b
 
 
 
 
 
 
98fec41
f42bec3
b5fbdaa
 
 
98fec41
 
f5bef0b
 
 
 
 
 
 
98fec41
b5fbdaa
f42bec3
709982a
ec833a4
fcd34e2
6244f87
98fec41
 
ec833a4
31e0198
ec833a4
 
 
 
 
 
98fec41
 
ec833a4
f42bec3
ec833a4
 
 
 
 
 
98fec41
 
ec833a4
48c69e0
c3ac407
 
 
 
f42bec3
c3ac407
 
 
cf7f256
d47a933
c3ac407
 
 
 
 
 
 
 
f42bec3
c3ac407
cf7f256
 
 
d47a933
c3ac407
 
 
 
 
 
 
 
f42bec3
c3ac407
cf7f256
 
 
d47a933
c3ac407
 
 
 
 
 
 
 
f42bec3
c3ac407
cf7f256
 
 
c3ac407
 
 
 
 
 
 
 
f42bec3
c3ac407
cf7f256
 
 
c3ac407
 
 
 
 
 
 
 
f42bec3
c3ac407
cf7f256
 
c3ac407
 
 
 
 
 
 
 
 
48c69e0
 
1e5c5ca
 
 
1682db2
1e5c5ca
1682db2
1e5c5ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1682db2
 
1e5c5ca
 
3f68ea0
f0867ff
 
 
4cef0e1
f0867ff
6a50c71
a6e71a4
 
 
 
 
 
4cef0e1
 
 
 
 
f0867ff
62c1d66
1e5c5ca
3f68ea0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
"""Gnosis Tutor - Chatbot-Guided Learning Platform (Refactored)."""

import gradio as gr
from pathlib import Path
import logging
import atexit

# Import state
from core.state import user_state

# Import helper functions
from core.helpers import save_api_key

# Import MCP client
from core.mcp_client import initialize_mcp_client, shutdown_mcp_client

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Import handler functions
from handlers.chat_handlers import chat_with_tutor
from handlers.quiz_handlers import (
    quick_generate_quiz,
    load_quiz_to_modal,
    handle_quiz_submit,
    next_question,
    prev_question,
    quick_generate_quiz_with_update
)
from handlers.content_handlers import (
    quick_generate_curriculum,
    quick_find_resources,
    refresh_sidebar_displays,
    refresh_all_panels
)
from handlers.mcp_handlers import (
    mcp_generate_quiz,
    mcp_generate_curriculum,
    mcp_generate_exercise,
    mcp_search_resources,
    mcp_ask_guiding_question,
    mcp_analyze_code
)
from ui.components import (
    submit_chat,
    set_example_and_submit
)

# Load CSS from external file
css_path = Path(__file__).parent / "ui" / "styles.css"
with open(css_path, "r") as f:
    custom_css = f.read()

# Build Gradio interface
with gr.Blocks(title="Gnosis Tutor") as app:
    # Header with tabbed info section
    with gr.Row(elem_id="header-section"):
        with gr.Column():
            gr.Markdown(
                """<h1 style="text-align: center; margin-bottom: 0.5rem; width: 100%;">Gnosis</h1>""")

            with gr.Tabs(elem_classes="header-tabs"):
                with gr.Tab("πŸ‘‹ Welcome"):
                    gr.Markdown("""
**Hello and welcome to Gnosis!** πŸ‘‹

Gnosis is your AI tutorβ€”built to make learning feel more interactive and hands on.

Whether you're diving into lecture slides, tackling research papers, or trying to wrap your head around complex data sheets, Gnosis is here to help. With its built-in MCP server, you can spin up coding playgrounds tailored specifically to the concepts you're learning.

---

To get started, please select your LLM provider and enter your API key below. Gnosis relies on LLMs to bring you a truly personalized learning experience, so this step is essential.

*Can't wait for you to try Genosis* ✨
                    """)
                    # Demo video in collapsible accordion
                    with gr.Accordion("πŸŽ₯ Demo Video", open=False):
                        gr.Markdown("Check out our demo video to see Gnosis in action:")
                        demo_video_path = Path(__file__).parent / "demo.mp4"
                        demo_video = gr.Video(
                            value=str(demo_video_path) if demo_video_path.exists() else None,
                            label="Gnosis Demo",
                            show_label=False,
                            height=400
                        )
                    gr.Markdown("---")
                    with gr.Column(elem_id="api-key-section"):
                        provider_dropdown = gr.Radio(
                            label="πŸ€– LLM Provider",
                            choices=["gemini", "openai", "anthropic", "huggingface"],
                            value="gemini",
                            interactive=True,
                            elem_classes=["provider-radio"]
                        )
                        api_key_input = gr.Textbox(
                            label="πŸ”‘ API Key",
                            placeholder="Enter your API key here...",
                            type="password",
                            show_label=True,
                            elem_id="api-key-input"
                        )
                    with gr.Row():
                        save_api_btn = gr.Button("πŸ’Ύ Save & Connect", variant="primary", scale=1)
                        with gr.Column(scale=2):
                            api_status = gr.Markdown("*Enter your API key to get started*")

                with gr.Tab("🎯 Why Gnosis?"):
                    gr.Markdown("""
Gnosis is my first ever hackathon project, and it comes from a place I think many of us know well.

As a university studentβ€”and hopefully a lifelong learnerβ€”I've felt the frustration of wanting to learn something new but not knowing where to start. Finding resources that actually let you *test* what you've learned is surprisingly hard. I'm not talking about watching another tutorial or reading documentation. I mean real interaction: quizzes, practice problems, something that makes you think and confirms you're actually getting it.

And then there's the coding side. Finding exercises that are simple enough to not overwhelm you, but meaningful enough to teach you the core concepts? That's even harder. Most of us just want a starting pointβ€”something to build confidence before we go off and build on our own.

That's what Gnosis is for. It's not meant to make you an expert. It's meant to make **starting** feel less daunting. To take that first step from "I have no idea what this is" to "okay, I think I get the basics."

I built this because I needed it myself. In fact, I used Gnosis to help me learn Svelte for this very projectβ€”I had zero frontend experience before this. If it helped me, maybe it can help you too.
                    """)

                with gr.Tab("πŸš€ Features"):
                    gr.Markdown("""
**Part 1: The Web App (You're here now)**

This web interface is best suited for learning concepts and testing your understanding. Upload a PDF or tell me what you want to learn, and I'll help you grasp the fundamentals through:

- **Interactive Quizzes** β€” Generate quizzes to test what you've learned (check the quiz panel on the right!)
- **Curriculum Builder** β€” Get a structured learning path tailored to your topic
- **Socratic Tutoring** β€” I'll guide you with questions instead of just giving answers
- **Resource Search** β€” Find documentation, tutorials, and learning materials

---

**Part 2: The Coding Playground (This is the fun part)**

Here's where it gets exciting. Connect Gnosis to **Cursor IDE** or **Claude Desktop** via MCP, and you unlock a whole new way to learn programming.

Instead of just reading about code, you can:
- **Generate coding exercises** β€” Complete exercise packages with starter code, tests, and hints
- **Get Socratic guidance** β€” Ask questions and get guided toward the answer, not spoonfed
- **Analyze your code** β€” Get feedback on what you understand and where to improve

**Available MCP Tools:**
| Tool | What it does |
|------|--------------|
| `generate_quiz` | Create quizzes on any topic |
| `generate_curriculum` | Build a learning path with progressive exercises |
| `generate_exercise` | Create a coding exercise with starter code and tests |
| `search_resources` | Find documentation and tutorials |
| `ask_guiding_question` | Get Socratic tutoring help |
| `analyze_code` | Get feedback on your code |

I highly recommend you try this. It's one thing to read about recursionβ€”it's another to write it yourself with an AI tutor watching over your shoulder. Check the **MCP Server** tab in the sidebar for setup instructions.
                    """)

                with gr.Tab("πŸ“– How to Use"):
                    gr.Markdown("""
**Getting Started:**

1. **Chat** - Just type what you want to learn (e.g., "Teach me about recursion")
2. **Upload** - Drop a PDF to extract and learn from its contents
3. **Quiz** - Ask me to generate a quiz on any topic
4. **MCP** - Connect Gnosis to Cursor for seamless integration

**Pro Tips:**
- Be specific about what you want to learn
- Upload lecture slides or textbook chapters for best results
- Use the quiz feature to test your understanding
                    """)


    with gr.Row():
        # Left Sidebar - Tools & Info
        with gr.Column(scale=1):
            with gr.Tabs(elem_classes="glass-tabs-container"):

                with gr.Tab("πŸ“Š Extracted Data", elem_classes="glass-tab-item"):
                    # Initial placeholder when no data is loaded
                    data_status = gr.Markdown(
                        """### πŸ“„ No Document Loaded

Upload a PDF or tell me what you want to learn to see extracted concepts here!

**How to get started:**
1. πŸ’¬ Chat with me about what you want to learn
2. πŸ“Ž Upload a PDF document
3. πŸ“ Then generate quizzes and exercises!""",
                        visible=True
                    )
                    # Concepts display - hidden until data is loaded
                    with gr.Accordion("🧠 Concepts", open=True, visible=False) as concepts_accordion:
                        concepts_display = gr.Markdown("")
                    # Topics display - hidden until data is loaded
                    with gr.Accordion("πŸ“š Topics", open=True, visible=False) as topics_accordion:
                        topics_display = gr.Markdown("")
                    # Key Points display - hidden until data is loaded
                    with gr.Accordion("πŸ’‘ Key Points", open=False, visible=False) as key_points_accordion:
                        key_points_display = gr.Markdown("")

                with gr.Tab("πŸ”Œ MCP Server", elem_classes="glass-tab-item"):
                    with gr.Tabs(elem_classes="glass-tabs-container"):
                        with gr.Tab("πŸ“‹ Cursor", elem_classes="glass-tab-item"):
                            gr.Markdown("""
## πŸš€ Use Gnosis as an MCP Server in Cursor

**Connect Gnosis to Cursor for seamless AI-powered tutoring!**

---

### πŸ“‹ Setup with SSE/HTTP

**Add to `~/.cursor/mcp.json`:**

```json
{
  "mcpServers": {
    "gnosis": {
      "url": "https://mcp-1st-birthday-gnosis.hf.space/gradio_api/mcp/sse"
    }
  }
}
```

**Restart Cursor** - The AI will automatically use Gnosis tools!

---

### πŸ’¬ Just Ask Naturally!

| What You Ask | What Happens |
|--------------|--------------|
| "Quiz me on Python decorators" | Generates interactive quiz |
| "Create a learning path for React hooks" | Builds progressive curriculum |
| "Give me a coding exercise on recursion" | Creates exercise with tests |

---

### πŸ”§ Available Tools

| Tool | Description |
|------|-------------|
| `generate_quiz` | Create quizzes on any topic |
| `generate_curriculum` | Build learning paths |
| `generate_exercise` | Create coding exercises |
| `search_resources` | Find docs & tutorials |
| `ask_guiding_question` | Socratic tutoring |
| `analyze_code` | Code review & feedback |
                            """)
                        
                        with gr.Tab("πŸ’» Claude Code", elem_classes="glass-tab-item"):
                            gr.Markdown("""
## πŸš€ Use Gnosis as an MCP Server in Claude Code

**Connect Gnosis to Claude Code for seamless AI-powered tutoring!**

---

### πŸ“‹ Setup with SSE/HTTP

**Add to your Claude Code configuration:**

1. Open Claude Code settings
2. Navigate to MCP Servers configuration
3. Add the following:

```json
{
  "mcpServers": {
    "gnosis": {
      "url": "https://mcp-1st-birthday-gnosis.hf.space/gradio_api/mcp/sse"
    }
  }
}
```

**Restart Claude Code** - The AI will automatically use Gnosis tools!

---

### πŸ’¬ Just Ask Naturally!

| What You Ask | What Happens |
|--------------|--------------|
| "Quiz me on Python decorators" | Generates interactive quiz |
| "Create a learning path for React hooks" | Builds progressive curriculum |
| "Give me a coding exercise on recursion" | Creates exercise with tests |

---

### πŸ”§ Available Tools

| Tool | Description |
|------|-------------|
| `generate_quiz` | Create quizzes on any topic |
| `generate_curriculum` | Build learning paths |
| `generate_exercise` | Create coding exercises |
| `search_resources` | Find docs & tutorials |
| `ask_guiding_question` | Socratic tutoring |
| `analyze_code` | Code review & feedback |
                            """)

                with gr.Tab("🎯 Quick Actions", elem_classes="glass-tab-item"):
                    gr.Markdown("**Generate content quickly:**")
                    quick_quiz_btn = gr.Button("πŸ“ Generate Quiz", variant="primary")
                    quick_curriculum_btn = gr.Button("πŸ“š Create Curriculum", variant="primary")
                    quick_resources_btn = gr.Button("πŸ” Find Resources", variant="primary")

                    quick_output = gr.Markdown(label="Quick Result")

                with gr.Tab("ℹ️ About", elem_classes="glass-tab-item"):
                    gr.Markdown("""
                    **Gnosis Tutor** helps you learn programming concepts through:

                    - πŸ“„ **Document Analysis**: Extract concepts from PDFs
                    - ❓ **Interactive Quizzes**: Test your understanding
                    - πŸ“š **Learning Curricula**: Progressive learning paths
                    - πŸ” **Resource Discovery**: Find tutorials and docs
                    - πŸ€” **Socratic Tutoring**: Guided learning through questions

                    **Powered by MCP Server Integration**
                    """)

        # Center - Main Chat Area
        with gr.Column(scale=3):
            # Manual Chatbot setup for better event control
            chatbot = gr.Chatbot(
                label="Chat",
                height=500,
                show_label=False,
                elem_classes=["glass-chatbot"]
            )

            # Input box with submit button in a Row
            with gr.Row(elem_id="input-row"):
                chat_input = gr.Textbox(
                    placeholder="Ask me anything",
                    show_label=False,
                    lines=1,
                    max_lines=1,
                    elem_id="main-input",
                    scale=9
                )
                submit_btn = gr.Button(
                    "Send",
                    variant="primary",
                    elem_id="submit-btn",
                    scale=1,
                    min_width=100
                )

            file_upload = gr.File(
                label="Upload PDF",
                file_types=[".pdf"],
                elem_id="pdf-upload"
            )

            # Example buttons
            with gr.Row():
                gr.Markdown("**Quick prompts:**", elem_classes=["example-label"])
            with gr.Row():
                example_btn1 = gr.Button("🎯 Learn Gradio 6", size="sm", elem_classes=["example-btn"])
                example_btn2 = gr.Button("πŸ“ Generate quiz", size="sm", elem_classes=["example-btn"])
                example_btn3 = gr.Button("πŸ“š Create curriculum", size="sm", elem_classes=["example-btn"])


        # Quiz Popup Modal (shown when quiz is generated) - positioned as floating panel
        # Initialize quiz_data_state with empty quiz
        quiz_data_state = gr.State(value=[])
        current_question_idx = gr.State(value=0)
        quiz_score_state = gr.State(value={"correct": 0, "total": 0})
        quiz_trigger = gr.State(value=0)  # Trigger to update quiz modal when quiz is generated

        # Quiz Block - Right Sidebar (EXACT same structure as left sidebar)
        with gr.Column(scale=1, visible=True, min_width=300) as quiz_modal:
            with gr.Tabs(elem_classes="glass-tabs-container"):
                with gr.Tab("πŸ“ Interactive Quiz", elem_classes="glass-tab-item"):
                    # Status message (shown when no quiz)
                    quiz_status = gr.Markdown("**No quiz available yet. Ask me to generate a quiz!**", visible=False)

                    # Quiz Header Row
                    with gr.Row():
                        quiz_question_num = gr.Markdown("**Question 1 of 5**")
                        quiz_score_display = gr.Markdown("**Score: 0/0**")

                    # Question Text
                    quiz_question_text = gr.Markdown("", label="")

                    # Answer Options
                    quiz_options = gr.Radio(
                        label="Select your answer",
                        choices=[],
                        interactive=True,
                        visible=True,
                        value=None,
                        show_label=True
                    )

                    # Submit Button
                    quiz_submit_btn = gr.Button("Submit Answer", variant="primary", visible=True, interactive=True)

                    # Feedback
                    quiz_feedback = gr.HTML(
                        value="",
                        visible=False,
                        elem_classes=["quiz-feedback"]
                    )

                    # Explanation
                    quiz_explanation = gr.HTML(
                        value="",
                        visible=False,
                        elem_classes=["quiz-explanation"]
                    )

                    # Navigation Buttons
                    with gr.Row():
                        quiz_prev_btn = gr.Button("← Previous", variant="secondary", visible=False)
                        quiz_next_btn = gr.Button("Next β†’", variant="secondary", visible=False)

                    # Completion Message
                    quiz_complete = gr.Markdown(label="", visible=False)
                    quiz_restart_btn = gr.Button("πŸ”„ Start Over", variant="primary", visible=False)

    # ============================================
    # EVENT BINDINGS
    # ============================================

    # API Key save handler
    save_api_btn.click(
        save_api_key,
        inputs=[provider_dropdown, api_key_input],
        outputs=[api_status],
        api_name="save_api_key"  # Keep this one exposed for MCP
    )

    # Quick action buttons
    quick_quiz_btn.click(
        quick_generate_quiz_with_update,
        None,
        outputs=[quick_output, quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
         quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
         quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
         current_question_idx, quiz_score_state, quiz_data_state,
         data_status, concepts_accordion, concepts_display,
         topics_accordion, topics_display, key_points_accordion, key_points_display],
        api_name=False
    )
    quick_curriculum_btn.click(quick_generate_curriculum, None, [quick_output], api_name=False)
    quick_resources_btn.click(quick_find_resources, None, [quick_output], api_name=False)

    # Quiz modal interactions
    # Clear quiz on page load/refresh (don't persist across refreshes)
    def clear_quiz_on_load():
        """Clear quiz state when page loads/refreshes."""
        from core.state import user_state
        user_state["current_quiz"] = []
        user_state["current_question_idx"] = 0
        user_state["show_quiz_modal"] = False
        return None
    
    # Clear quiz first, then load (which will show "no quiz" message)
    app.load(clear_quiz_on_load, None, [], api_name=False)
    
    # Load quiz when app loads (will show "no quiz" since we just cleared it)
    app.load(load_quiz_to_modal, None, [
        quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
        quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
        quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
        current_question_idx, quiz_score_state, quiz_data_state
    ], api_name=False)

    # Initialize sidebar displays on app load
    app.load(refresh_sidebar_displays, None, [
        data_status, concepts_accordion, concepts_display,
        topics_accordion, topics_display, key_points_accordion, key_points_display
    ], api_name=False)

    # Chat submission handlers
    # Submit on Enter key
    chat_input.submit(
        submit_chat,
        inputs=[chat_input, file_upload, chatbot],
        outputs=[chat_input, file_upload, chatbot],
        api_name="submit_chat"  # Main chat endpoint for MCP
    ).then(
        # After chat response, refresh panels
        refresh_all_panels,
        inputs=[],
        outputs=[
            # Quiz outputs (15)
            quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
            quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
            quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
            current_question_idx, quiz_score_state, quiz_data_state,
            # Sidebar outputs (7)
            data_status, concepts_accordion, concepts_display,
            topics_accordion, topics_display, key_points_accordion, key_points_display
        ],
        api_name=False
    )

    # Submit on button click
    submit_btn.click(
        submit_chat,
        inputs=[chat_input, file_upload, chatbot],
        outputs=[chat_input, file_upload, chatbot],
        api_name=False  # Don't duplicate the chat endpoint
    ).then(
        # After chat response, refresh panels
        refresh_all_panels,
        inputs=[],
        outputs=[
            # Quiz outputs (15)
            quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
            quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
            quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
            current_question_idx, quiz_score_state, quiz_data_state,
            # Sidebar outputs (7)
            data_status, concepts_accordion, concepts_display,
            topics_accordion, topics_display, key_points_accordion, key_points_display
        ],
        api_name=False
    )

    # Example button handlers
    example_btn1.click(
        lambda h: set_example_and_submit("I want to learn about Gradio 6", h),
        inputs=[chatbot],
        outputs=[chat_input, file_upload, chatbot],
        api_name=False
    ).then(refresh_all_panels, inputs=[], outputs=[
        quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
        quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
        quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
        current_question_idx, quiz_score_state, quiz_data_state,
        data_status, concepts_accordion, concepts_display,
        topics_accordion, topics_display, key_points_accordion, key_points_display
    ], api_name=False)

    example_btn2.click(
        lambda h: set_example_and_submit("Generate a quiz for me", h),
        inputs=[chatbot],
        outputs=[chat_input, file_upload, chatbot],
        api_name=False
    ).then(refresh_all_panels, inputs=[], outputs=[
        quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
        quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
        quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
        current_question_idx, quiz_score_state, quiz_data_state,
        data_status, concepts_accordion, concepts_display,
        topics_accordion, topics_display, key_points_accordion, key_points_display
    ], api_name=False)

    example_btn3.click(
        lambda h: set_example_and_submit("Create a learning curriculum", h),
        inputs=[chatbot],
        outputs=[chat_input, file_upload, chatbot],
        api_name=False
    ).then(refresh_all_panels, inputs=[], outputs=[
        quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
        quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
        quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
        current_question_idx, quiz_score_state, quiz_data_state,
        data_status, concepts_accordion, concepts_display,
        topics_accordion, topics_display, key_points_accordion, key_points_display
        ], api_name=False
    )

    # Quiz submit - updates feedback, explanation, chat, score, and quiz_data
    quiz_submit_btn.click(
        handle_quiz_submit,
        inputs=[quiz_options, current_question_idx, quiz_score_state, chatbot],
        outputs=[quiz_feedback, quiz_explanation, quiz_submit_btn, quiz_next_btn, chatbot, quiz_score_state, quiz_data_state],
        api_name="handle_quiz_submit"  # Quiz interaction for MCP
    )

    quiz_next_btn.click(
        next_question,
        inputs=[quiz_data_state, current_question_idx, quiz_score_state],
        outputs=[quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
                quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
                quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
                current_question_idx, quiz_score_state, quiz_data_state],
        api_name="next_question"  # Quiz navigation for MCP
    )

    quiz_prev_btn.click(
        prev_question,
        inputs=[quiz_data_state, current_question_idx, quiz_score_state],
        outputs=[quiz_status, quiz_question_num, quiz_score_display, quiz_question_text,
                quiz_options, quiz_submit_btn, quiz_feedback, quiz_explanation,
                quiz_prev_btn, quiz_next_btn, quiz_complete, quiz_restart_btn,
                current_question_idx, quiz_score_state, quiz_data_state],
        api_name="prev_question"  # Quiz navigation for MCP
    )

    # ============================================
    # MCP TOOL ENDPOINTS
    # These are the core tools exposed via MCP for AI assistants
    # ============================================

    # Hidden components for MCP tool inputs/outputs
    with gr.Row(visible=False):
        # Generate Quiz
        mcp_quiz_topic = gr.Textbox(label="topic")
        mcp_quiz_num = gr.Number(label="num_questions", value=5, precision=0)
        mcp_quiz_output = gr.Textbox(label="Quiz Output")
        mcp_quiz_btn = gr.Button("Generate Quiz")
        mcp_quiz_btn.click(
            mcp_generate_quiz,
            inputs=[mcp_quiz_topic, mcp_quiz_num],
            outputs=[mcp_quiz_output],
            api_name="generate_quiz"
        )

        # Generate Curriculum
        mcp_curr_concept = gr.Textbox(label="concept")
        mcp_curr_lang = gr.Textbox(label="language", value="python")
        mcp_curr_level = gr.Textbox(label="level", value="beginner")
        mcp_curr_num = gr.Number(label="num_exercises", value=5, precision=0)
        mcp_curr_output = gr.Textbox(label="Curriculum Output")
        mcp_curr_btn = gr.Button("Generate Curriculum")
        mcp_curr_btn.click(
            mcp_generate_curriculum,
            inputs=[mcp_curr_concept, mcp_curr_lang, mcp_curr_level, mcp_curr_num],
            outputs=[mcp_curr_output],
            api_name="generate_curriculum"
        )

        # Generate Exercise
        mcp_ex_concept = gr.Textbox(label="concept")
        mcp_ex_lang = gr.Textbox(label="language", value="python")
        mcp_ex_level = gr.Textbox(label="level", value="beginner")
        mcp_ex_time = gr.Number(label="time_minutes", value=15, precision=0)
        mcp_ex_output = gr.Textbox(label="Exercise Output")
        mcp_ex_btn = gr.Button("Generate Exercise")
        mcp_ex_btn.click(
            mcp_generate_exercise,
            inputs=[mcp_ex_concept, mcp_ex_lang, mcp_ex_level, mcp_ex_time],
            outputs=[mcp_ex_output],
            api_name="generate_exercise"
        )

        # Search Resources
        mcp_res_concept = gr.Textbox(label="concept")
        mcp_res_lang = gr.Textbox(label="language", value="python")
        mcp_res_type = gr.Textbox(label="resource_type", value="all")
        mcp_res_output = gr.Textbox(label="Resources Output")
        mcp_res_btn = gr.Button("Search Resources")
        mcp_res_btn.click(
            mcp_search_resources,
            inputs=[mcp_res_concept, mcp_res_lang, mcp_res_type],
            outputs=[mcp_res_output],
            api_name="search_resources"
        )

        # Ask Guiding Question
        mcp_guide_problem = gr.Textbox(label="problem")
        mcp_guide_code = gr.Textbox(label="student_code", value="")
        mcp_guide_question = gr.Textbox(label="student_question", value="")
        mcp_guide_output = gr.Textbox(label="Guiding Question Output")
        mcp_guide_btn = gr.Button("Ask Guiding Question")
        mcp_guide_btn.click(
            mcp_ask_guiding_question,
            inputs=[mcp_guide_problem, mcp_guide_code, mcp_guide_question],
            outputs=[mcp_guide_output],
            api_name="ask_guiding_question"
        )

        # Analyze Code
        mcp_analyze_problem = gr.Textbox(label="problem")
        mcp_analyze_code_input = gr.Textbox(label="student_code")
        mcp_analyze_output = gr.Textbox(label="Analysis Output")
        mcp_analyze_btn = gr.Button("Analyze Code")
        mcp_analyze_btn.click(
            mcp_analyze_code,
            inputs=[mcp_analyze_problem, mcp_analyze_code_input],
            outputs=[mcp_analyze_output],
            api_name="analyze_code"
        )


if __name__ == "__main__":
    # ============================================
    # INITIALIZE MCP SERVER CLIENT
    # ============================================
    logger.info("=" * 80)
    logger.info("πŸš€ INITIALIZING GNOSIS WITH MCP SERVER INTEGRATION")
    logger.info("=" * 80)

    # Get the path to mcp_server.py
    mcp_server_path = Path(__file__).parent / "mcp_server.py"

    try:
        # Initialize the MCP client - this starts the MCP server as a subprocess
        logger.info(f"πŸ“‚ MCP Server Path: {mcp_server_path}")
        initialize_mcp_client(str(mcp_server_path))
        logger.info("βœ… MCP server client initialized successfully!")
        logger.info("🎯 Gradio app will now communicate with the MCP server")

        # Register shutdown handler to stop MCP server when app exits
        atexit.register(shutdown_mcp_client)

    except Exception as e:
        logger.error(f"❌ Failed to initialize MCP client: {e}")
        logger.warning("⚠️  App will start but MCP features may not work")

    logger.info("=" * 80)

    # Enable MCP server functionality for hackathon compliance
    # This makes the Gradio app serve as an MCP server
    app.launch(
        theme=gr.themes.Soft(
            primary_hue="purple",
            secondary_hue="purple",
            neutral_hue="purple"
        ).set(
            # All backgrounds transparent/dark
            body_background_fill="#050010",
            body_background_fill_dark="#050010",
            block_background_fill="transparent",
            block_background_fill_dark="transparent",
            panel_background_fill="transparent",
            panel_background_fill_dark="transparent",
            # Input overrides to match chatbot
            input_background_fill="transparent",
            input_background_fill_dark="transparent",
            input_border_color="transparent",
            input_border_color_dark="transparent",
        ),
        css=custom_css,
        mcp_server=True  # Enable MCP server mode
    )