File size: 45,442 Bytes
9932624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dd0af13
 
9932624
 
 
 
dd0af13
9932624
 
 
 
 
 
 
 
 
 
 
 
d0d1414
9932624
 
d0d1414
 
 
 
 
 
 
 
 
9932624
d0d1414
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9932624
 
 
 
 
 
2ccf1e3
d0d1414
e509d8a
 
 
2be7236
d0d1414
 
 
 
e509d8a
 
 
 
d0d1414
 
2be7236
d0d1414
 
dd0af13
d0d1414
 
 
 
 
 
dd0af13
d0d1414
dd0af13
d0d1414
dd0af13
9932624
d0d1414
dd0af13
d0d1414
dd0af13
d0d1414
dd0af13
d0d1414
 
dd0af13
d0d1414
dd0af13
d0d1414
dd0af13
d0d1414
 
 
 
 
9932624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ccf1e3
 
 
e509d8a
 
 
2ccf1e3
9932624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2be7236
9932624
 
2be7236
 
 
 
9932624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e5e1b4a
 
 
 
 
9932624
 
e5e1b4a
 
 
 
9932624
 
e5e1b4a
 
 
9932624
6be186b
 
 
9932624
04efa29
 
 
2be7236
 
52165d8
04efa29
2be7236
 
04efa29
 
2be7236
 
04efa29
 
2be7236
9932624
04efa29
 
9932624
 
04efa29
 
9932624
04efa29
 
 
 
9932624
 
52165d8
04efa29
9932624
 
04efa29
 
9932624
 
04efa29
 
9932624
 
52165d8
04efa29
2be7236
 
04efa29
 
 
 
 
 
 
9932624
 
e5e1b4a
04efa29
9932624
 
04efa29
 
9932624
 
04efa29
 
9932624
6be186b
04efa29
 
6be186b
 
04efa29
 
6be186b
 
04efa29
 
6be186b
04efa29
 
 
 
6be186b
 
04efa29
 
6be186b
 
04efa29
 
6be186b
 
 
04efa29
6be186b
 
 
04efa29
 
 
 
 
 
6be186b
 
04efa29
 
6be186b
 
04efa29
 
6be186b
 
04efa29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6be186b
9932624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14f69ef
 
9932624
 
 
 
 
 
 
 
 
 
 
 
d0d1414
9932624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d0d1414
9932624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4bbcd70
 
 
 
9932624
4bbcd70
9932624
 
 
 
4bbcd70
 
9932624
 
 
 
 
 
 
4bbcd70
 
9932624
 
 
 
 
 
4bbcd70
 
 
 
 
9932624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
import gradio as gr
import yaml
import json
import os
import traceback
import matplotlib.pyplot as plt
import numpy as np

from engine.loader import load_persona
from engine.drift import apply_context_shift
from engine.responder import generate_response, generate_response_local
from engine.utils import safe_log
from engine.logger import log_interaction

# Paths
persona_dir = "./personas"
contexts_path = "./contexts/scenarios.json"
error_log_path = "./ot_simulator_errors.log"

# Load available personas
def get_persona_choices():
    return [f for f in os.listdir(persona_dir) if f.endswith(".yml")]

# Load available contextual scenarios
def get_scenario_choices():
    try:
        with open(contexts_path, "r") as f:
            scenarios = json.load(f)
        return [s["scenario"] for s in scenarios]
    except Exception as e:
        safe_log("Scenarios load error", str(e))
        return []

# Generate radar chart for character emotional state
def plot_state(state, persona_name):
    if persona_name == "Jack":
        metrics = ["anxiety", "trust", "openness", "physical_discomfort"]
        labels = ["Intensity", "Openness", "Candor", "Physical State"]
        colors = ["#e74c3c", "#3498db", "#2ecc71", "#f39c12"]
    elif persona_name == "Maya":
        metrics = ["anxiety", "trust", "creative_engagement", "occupational_balance"]
        labels = ["Intensity", "Openness", "Creativity", "Balance"]
        colors = ["#e74c3c", "#3498db", "#9b59b6", "#1abc9c"]
    else:
        metrics = ["anxiety", "trust", "openness"]
        labels = ["Intensity", "Openness", "Candor"]
        colors = ["#e74c3c", "#3498db", "#2ecc71"]

    values = [state.get(m, 0.0) for m in metrics]
    angles = np.linspace(0, 2 * np.pi, len(metrics), endpoint=False).tolist()
    values += values[:1]
    angles += angles[:1]

    fig, ax = plt.subplots(figsize=(5, 5), subplot_kw=dict(polar=True))
    ax.plot(angles, values, color=colors[0], linewidth=2)
    ax.fill(angles, values, color=colors[0], alpha=0.25)
    ax.set_xticks(angles[:-1])
    ax.set_xticklabels(labels)
    ax.set_ylim(0, 1)
    ax.set_yticklabels(['0.0', '0.2', '0.4', '0.6', '0.8', '1.0'])
    ax.set_title(f"{persona_name}'s Character State", fontsize=14, pad=20)
    ax.grid(True)
    fig.tight_layout()

    chart_path = f"./state_chart_{persona_name}.png"
    fig.savefig(chart_path, dpi=100, bbox_inches='tight')
    plt.close(fig)
    return chart_path

# Generate interaction history visualization
def plot_interaction_history(history):
    if not history or len(history) < 2:
        return None

    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6))

    interactions = list(range(1, len(history) + 1))
    anxiety_vals = [h.get('anxiety', 0) for h in history]
    trust_vals = [h.get('trust', 0) for h in history]

    ax1.plot(interactions, anxiety_vals, marker='o', color='#e74c3c', linewidth=2, label='Emotional Intensity')
    ax1.set_ylabel('Intensity Level', fontsize=10)
    ax1.set_ylim(0, 1)
    ax1.grid(True, alpha=0.3)
    ax1.legend(loc='upper right')

    ax2.plot(interactions, trust_vals, marker='o', color='#3498db', linewidth=2, label='Openness')
    ax2.set_xlabel('Interaction Number', fontsize=10)
    ax2.set_ylabel('Openness Level', fontsize=10)
    ax2.set_ylim(0, 1)
    ax2.grid(True, alpha=0.3)
    ax2.legend(loc='upper right')

    fig.suptitle('Character Engagement Over Time', fontsize=14)
    fig.tight_layout()

    history_path = "./interaction_history.png"
    fig.savefig(history_path, dpi=100, bbox_inches='tight')
    plt.close(fig)
    return history_path

# Download session transcript
def download_session(conversation_history, state_history, selected_persona_file):
    """Generate downloadable transcript file."""
    if not conversation_history:
        return None

    try:
        persona_path = os.path.join(persona_dir, selected_persona_file)
        persona = load_persona(persona_path)

        from datetime import datetime
        timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
        name = persona.get("persona_name", "Character")

        # Header
        transcript = f"""================================================================
LIT DIGITAL TWIN - INTERACTION TRANSCRIPT
================================================================

Character: {name}
Date: {timestamp}
Number of Turns: {len(conversation_history)}

================================================================
CONVERSATION
================================================================
"""

        # Conversation history
        for i, turn in enumerate(conversation_history, 1):
            transcript += f"\n[Turn {i}]\n"
            if 'scenario' in turn:
                transcript += f"Scenario: {turn['scenario']}\n\n"
            transcript += f"Interviewer: {turn.get('student', '')}\n\n"
            transcript += f"{name}: {turn.get('client', '')}\n\n"
            transcript += "-" * 63 + "\n"

        # State progression
        if state_history:
            transcript += f"""================================================================
STATE PROGRESSION
================================================================

Initial State:
  Anxiety: {state_history[0].get('anxiety', 0):.2f}
  Trust: {state_history[0].get('trust', 0):.2f}
  Openness: {state_history[0].get('openness', 0):.2f}

Final State:
  Anxiety: {state_history[-1].get('anxiety', 0):.2f}
  Trust: {state_history[-1].get('trust', 0):.2f}
  Openness: {state_history[-1].get('openness', 0):.2f}

Change:
  Anxiety: {state_history[-1].get('anxiety', 0) - state_history[0].get('anxiety', 0):+.2f}
  Trust: {state_history[-1].get('trust', 0) - state_history[0].get('trust', 0):+.2f}
  Openness: {state_history[-1].get('openness', 0) - state_history[0].get('openness', 0):+.2f}

================================================================
"""

        # Save to file
        filename = f"{name}_{timestamp}.txt"
        filepath = os.path.join("lit_transcripts", filename)
        os.makedirs("lit_transcripts", exist_ok=True)

        with open(filepath, "w", encoding="utf-8") as f:
            f.write(transcript)

        return filepath

    except Exception as e:
        safe_log("Download error", str(e))
        return None

def simulate(prompt, selected_event, selected_persona_file, ai_mode,
             conversation_history, state_history, use_fast_mode):
    try:
        # Normalize inputs
        if hasattr(prompt, 'value'):
            prompt = prompt.value
        prompt = str(prompt) if prompt else ""

        if hasattr(selected_event, 'value'):
            selected_event = selected_event.value

        if hasattr(selected_persona_file, 'value'):
            selected_persona_file = selected_persona_file.value

        if hasattr(ai_mode, 'value'):
            ai_mode = ai_mode.value

        # Initialize histories
        conversation_history = conversation_history or []
        state_history = state_history or []

        # Load persona
        persona_path = os.path.join(persona_dir, selected_persona_file)
        persona = load_persona(persona_path)

        # Load contextual scenario
        with open(contexts_path, "r") as f:
            scenarios = json.load(f)
        scenario = next((s for s in scenarios if s["scenario"] == selected_event), None)

        if scenario:
            persona = apply_context_shift(persona, scenario)
            context_note = f"**Context:** {scenario.get('description', selected_event)}\n\n"
        else:
            context_note = ""

        # Generate response
        # Note: ai_mode selects which backend to use (HF API vs local model)
        # It should NOT be passed as force_mode (which controls emotional state)
        response, updated_state, teaching_note = generate_response(
            prompt,
            persona,
            conversation_history,
            force_mode=None,  # Let the system determine emotional mode naturally
            use_fast_mode=use_fast_mode
        )


        # Update histories
        conversation_history.append({
            "student": prompt,
            "client": response,
            "scenario": selected_event
        })
        state_history.append(updated_state.copy())

        # Conversation display with modern styling
        conversation_display = ""
        for i, turn in enumerate(conversation_history, 1):
            # Turn number with gradient badge
            conversation_display += f"""
<div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white; padding: 0.5rem 1rem; border-radius: 8px; display: inline-block;
            margin-bottom: 0.5rem; font-weight: 600; font-size: 0.9rem;'>
    Turn {i}
</div>\n\n"""

            # Scenario context if present
            if 'scenario' in turn:
                conversation_display += f"""
<div style='background: #f8f9fa; border-left: 4px solid #f5576c; padding: 0.5rem 1rem;
            margin-bottom: 1rem; border-radius: 4px; color: #495057; font-style: italic;'>
    ๐Ÿ“– Scene: {turn['scenario']}
</div>\n\n"""

            # Student question with modern styling
            conversation_display += f"""
<div style='background: #e8eaf6; border-radius: 12px; padding: 1rem;
            margin-bottom: 0.8rem; border-left: 4px solid #667eea;'>
    <div style='color: #667eea; font-weight: 700; margin-bottom: 0.3rem; font-size: 0.9rem;'>
        ๐Ÿ‘ค YOU ASKED:
    </div>
    <div style='color: #2d3748; font-size: 1rem; line-height: 1.5;'>
        {turn['student']}
    </div>
</div>\n\n"""

            # Character response with modern styling
            conversation_display += f"""
<div style='background: #fff3e0; border-radius: 12px; padding: 1rem;
            margin-bottom: 1.5rem; border-left: 4px solid #f5576c;'>
    <div style='color: #f5576c; font-weight: 700; margin-bottom: 0.3rem; font-size: 0.9rem;'>
        ๐ŸŽญ {persona['persona_name'].upper()} REPLIED:
    </div>
    <div style='color: #2d3748; font-size: 1rem; line-height: 1.6;'>
        {turn['client']}
    </div>
</div>\n\n"""

            conversation_display += "<hr style='border: none; border-top: 2px solid #e9ecef; margin: 1.5rem 0;'>\n\n"

        # Visualizations
        state_yaml = yaml.dump(updated_state, sort_keys=False)
        current_chart = plot_state(updated_state, persona['persona_name'])
        history_chart = plot_interaction_history(state_history)

        # Teaching feedback - clean, educational format with better contrast
        teaching_feedback = f"""
<div style='background: linear-gradient(135deg, #fff8dc 0%, #ffe4b5 100%);
            border-radius: 10px; padding: 1.5rem; margin-bottom: 1.5rem;
            border: 2px solid #ffd700; box-shadow: 0 4px 12px rgba(255, 215, 0, 0.2);'>
    <div style='color: #2d3748; font-size: 1.05rem; line-height: 1.8; font-weight: 600;'>
        {teaching_note}
    </div>
</div>

<div style='background: linear-gradient(135deg, #e0e7ff 0%, #ddd6fe 100%);
            border-radius: 10px; padding: 1.5rem; border-left: 6px solid #6366f1;
            box-shadow: 0 4px 12px rgba(99, 102, 241, 0.25);'>
    <div style='color: #4338ca; font-weight: 700; font-size: 1.1rem; margin-bottom: 1rem;'>
        ๐Ÿ“Š Character's Current State
    </div>
    <div style='color: #2d3748; font-size: 1rem; line-height: 1.8; font-weight: 600;'>
"""

        # Add literary analysis of character state
        intensity = updated_state.get('anxiety', 0)
        openness = updated_state.get('trust', 0)
        candor = updated_state.get('openness', 0)
        mode = updated_state.get('mode', 'baseline')

        if intensity > 0.7:
            teaching_feedback += "        โ€ข <strong>High Tension:</strong> Character is at a crisis point - authors use these moments to reveal core fears and motivations. What does this reveal about their inner conflict?<br>\n"
        elif intensity > 0.4:
            teaching_feedback += "        โ€ข <strong>Moderate Tension:</strong> Character is emotionally engaged but controlled. Good opportunity to explore the gap between what they say and what they feel (subtext).<br>\n"
        else:
            teaching_feedback += "        โ€ข <strong>Calm State:</strong> Character is composed. Authors often use calm moments to establish baseline personality before introducing conflict.<br>\n"

        if openness > 0.6:
            teaching_feedback += "        โ€ข <strong>Revealing Moment:</strong> Character is sharing deeply - this is when backstory, trauma, and hidden desires surface. Pay attention to what they choose to reveal.<br>\n"
        elif openness > 0.4:
            teaching_feedback += "        โ€ข <strong>Cautiously Engaged:</strong> Character is balancing self-protection with honesty. Notice what they avoid - often as revealing as what they share.<br>\n"
        else:
            teaching_feedback += "        โ€ข <strong>Guarded/Defensive:</strong> Character is protecting themselves. In literature, walls characters build often point to their deepest wounds. What are they protecting?<br>\n"

        if mode == "trusting":
            teaching_feedback += "        โ€ข <strong>โœจ Breakthrough!</strong> You've reached the character's authentic self - this is where theme emerges. How does their truth connect to the story's larger meaning?<br>\n"
        elif mode == "triggered":
            teaching_feedback += "        โ€ข <strong>โš ๏ธ Resistance:</strong> Character is defensive. In literary analysis, resistance often signals proximity to truth. What nerve did you touch?<br>\n"
        elif mode == "decompensating":
            teaching_feedback += "        โ€ข <strong>๐Ÿ”ฅ Crisis Point:</strong> Character breakdown reveals theme. Joyce, Hemingway, etc. use these moments to expose the forces crushing their characters. Analyze what's collapsing and why.<br>\n"

        teaching_feedback += """
    </div>
</div>
"""

        # Log interaction
        transcript_path = log_interaction(
            persona,
            prompt,
            selected_event,
            response,
            updated_state,
            teaching_note,
            
        )

        return (
            conversation_display,
            teaching_feedback,
            state_yaml,
            current_chart,
            history_chart,
            conversation_history,
            state_history
        )

    except Exception as e:
        error_msg = traceback.format_exc()
        safe_log("Simulation error", error_msg)
        print(f"ERROR: {error_msg}")
        return (
            "[ERROR] Simulation failed. Check logs.",
            "Error occurred",
            "",
            None,
            None,
            conversation_history,
            state_history
        )

# Custom CSS for modern, engaging design
custom_css = """
.gradio-container {
    max-width: 1400px !important;
}

.header-banner {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    border-radius: 20px;
    padding: 2.5rem;
    text-align: center;
    margin-bottom: 2rem;
    box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
}

.character-selector {
    background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
    border-radius: 15px;
    padding: 1.5rem;
    color: white;
}

.conversation-display {
    background: #f8f9fa;
    border-radius: 15px;
    padding: 1.5rem;
    min-height: 450px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}

.feedback-card {
    background: linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%);
    border-radius: 15px;
    padding: 1.5rem;
}

button.primary {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
    border: none !important;
    color: white !important;
    font-weight: 600 !important;
    font-size: 1.1rem !important;
    padding: 0.8rem 2rem !important;
    border-radius: 10px !important;
    transition: all 0.3s ease !important;
}

button.primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4) !important;
}

.chart-container {
    background: white;
    border-radius: 15px;
    padding: 1rem;
    box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}

.feedback-box {
    border-radius: 12px !important;
    padding: 0 !important;
    box-shadow: none !important;
    background: transparent !important;
}
"""

with gr.Blocks(
    title="๐Ÿ“š Literary Character Simulator",
    theme=gr.themes.Soft(
        primary_hue="purple",
        secondary_hue="pink",
        neutral_hue="slate",
        font=gr.themes.GoogleFont("Inter")
    ),
    css=custom_css
) as ui:

    # Modern Header
    gr.HTML("""
        <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                    border-radius: 20px; padding: 3rem 2rem; text-align: center;
                    margin-bottom: 2rem; box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);">
            <h1 style="color: white; font-size: 3rem; font-weight: 800; margin: 0;
                       text-shadow: 2px 2px 8px rgba(0,0,0,0.2);">
                ๐Ÿ“š Literary Character Simulator
            </h1>
            <p style="color: #f0f0ff; font-size: 1.3rem; margin: 0.8rem 0 0 0; font-weight: 300;">
                Step into the minds of iconic characters from classic literature
            </p>
            <p style="color: #e0e0ff; font-size: 1rem; margin: 0.5rem 0 0 0; font-weight: 400; font-style: italic;">
                An Interface Studio Production
            </p>
        </div>
    """)

    # Info Banner
    gr.Markdown(
        """
        <div style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
                    border-radius: 15px; padding: 1.5rem; text-align: center;
                    color: white; margin-bottom: 1.5rem; box-shadow: 0 4px 12px rgba(0,0,0,0.1);">
            <h3 style="margin: 0; font-size: 1.3rem; font-weight: 600;">โœจ How It Works</h3>
            <p style="margin: 0.8rem 0 0 0; font-size: 1.05rem; opacity: 0.95;">
                Select a character, choose a scene, and ask questions to explore their story,
                motivations, and inner conflicts
            </p>
        </div>
        """
    )

    # State management
    conversation_state = gr.State([])
    state_history = gr.State([])

    # Main row: left column (selectors + info + mode) and right column (conversation)
    with gr.Row():
        with gr.Column(scale=1, min_width=300):
            # Character Selection Card
            gr.HTML("""
                <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                            border-radius: 12px; padding: 1.2rem; margin-bottom: 1rem;
                            box-shadow: 0 4px 12px rgba(0,0,0,0.1);">
                    <h3 style="color: white; margin: 0; font-size: 1.2rem; font-weight: 600;">
                        ๐ŸŽญ Character Selection
                    </h3>
                </div>
            """)

            persona_files = get_persona_choices()
            default_persona = persona_files[0] if persona_files else None

            persona_selector = gr.Dropdown(
                label="Choose Your Character",
                choices=persona_files,
                value=default_persona,
                allow_custom_value=False,
                info="๐ŸŽญ Select which literary character to engage with",
                elem_classes=["dropdown-modern"]
            )

            scenario_choices = get_scenario_choices()
            scenario_selector = gr.Dropdown(
                label="Scene Context",
                choices=scenario_choices,
                value=scenario_choices[0] if scenario_choices else None,
                info="๐Ÿ“– Choose the narrative moment",
                elem_classes=["dropdown-modern"]
            )

            gr.HTML("<div style='margin: 1.5rem 0;'></div>")

            with gr.Accordion("๐Ÿ“š Character Library", open=False):
                gr.HTML("""
                    <div style="max-height: 400px; overflow-y: auto; padding: 0.5rem;">
                        <style>
                            .character-card {
                                background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important;
                                border-left: 4px solid #667eea !important;
                                border-radius: 8px !important;
                                padding: 1rem !important;
                                margin-bottom: 1rem !important;
                            }
                            .character-name {
                                color: #667eea !important;
                                font-weight: 700 !important;
                                font-size: 1.1rem !important;
                                margin-bottom: 0.5rem !important;
                            }
                            .character-desc {
                                color: #1e293b !important;
                                font-size: 0.95rem !important;
                                line-height: 1.5 !important;
                            }
                            .character-desc i {
                                color: #1e293b !important;
                            }
                        </style>
                        <!-- SHAKESPEARE: HAMLET -->
                        <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 0.5rem 1rem; border-radius: 6px; margin-bottom: 0.5rem; font-weight: 700;">
                            ๐Ÿ“š HAMLET by William Shakespeare
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ‘‘ Hamlet (23)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Prince of Denmark. Consumed by grief, betrayal, and existential doubt. Philosophical and volatile, struggling to avenge his father's murder.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ‘ธ Gertrude (45)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Queen of Denmark. Married Claudius shortly after King Hamlet's death. Caught between husband and son, loving both, blind to the poison around her.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ‘‘ Claudius (55)</div>
                            <div class="character-desc" style="color: #1e293b !important;">King of Denmark. Murdered his brother, married Gertrude. Smooth politician haunted by guilt. Kingly authority masking desperation and fear of exposure.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐ŸŒธ Ophelia (19)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Young noblewoman. Caught between father's commands and Hamlet's madness. Descends into grief-stricken insanity after Polonius's death, drowns tragically.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">โš”๏ธ Laertes (25)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Ophelia's brother. Returns from France to avenge father Polonius's murder. Passionate and impulsive, manipulated by Claudius into killing Hamlet.</div>
                        </div>

                        <!-- SOPHOCLES: OEDIPUS REX -->
                        <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 0.5rem 1rem; border-radius: 6px; margin: 1rem 0 0.5rem 0; font-weight: 700;">
                            ๐Ÿ›๏ธ OEDIPUS REX by Sophocles
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ‘‘ Oedipus (28)</div>
                            <div class="character-desc" style="color: #1e293b !important;">King of Thebes. Saved city from Sphinx, now investigating King Laius's murder. Confident seeker of truth, unwittingly investigating himself.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ‘ธ Jocasta (45)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Queen of Thebes. Married Oedipus after King Laius's death, unknowingly her own son. Realizes the truth and takes her own life.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ‘๏ธ Creon (50)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Brother-in-law to Oedipus. Rational and loyal advisor, wrongly accused of conspiracy. Represents justice and restraint, becomes ruler after Oedipus's fall.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ”ฎ Tiresias (65)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Blind prophet. Knows the truth about Oedipus but speaks in riddles. Angrily reveals Oedipus is the plague's cause when pressed.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ‘ Shepherd (60)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Former servant. Pitied baby Oedipus and gave him to Corinthian shepherd. His testimony reveals the tragic truth to Oedipus.</div>
                        </div>

                        <!-- CLASSIC SHORT STORIES -->
                        <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 0.5rem 1rem; border-radius: 6px; margin: 1rem 0 0.5rem 0; font-weight: 700;">
                            ๐Ÿ“– CLASSIC SHORT STORIES
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐ŸŒŠ Eveline (29)</div>
                            <div class="character-desc" style="color: #1e293b !important;"><i>Eveline</i> by James Joyce. Ten years after freezing at the dock and not leaving with Frank the sailor. Lives alone after father's death, wondering if she made right choice.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">โœ๏ธ Jane (30)</div>
                            <div class="character-desc" style="color: #1e293b !important;"><i>The Yellow Wallpaper</i> by Charlotte Perkins Gilman. Confined under rest cure for postpartum depression. Perceptive and imaginative, descending into madness while observing wallpaper patterns.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ‘จโ€โš•๏ธ John (35)</div>
                            <div class="character-desc" style="color: #1e293b !important;"><i>The Yellow Wallpaper</i> by Charlotte Perkins Gilman. Physician husband who prescribes rest cure, forbids wife from writing. Paternalistic and dismissive, calls her "little girl." Faints when he sees her madness.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐ŸŽ–๏ธ Harold Krebs (25)</div>
                            <div class="character-desc" style="color: #1e293b !important;"><i>Soldier's Home</i> by Ernest Hemingway. WWI veteran emotionally numb and disconnected from postwar Oklahoma. Struggles with reintegration and societal expectations.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ’” Louise Mallard (28)</div>
                            <div class="character-desc" style="color: #1e293b !important;"><i>The Story of an Hour</i> by Kate Chopin. Hears husband died, feels guilty joy at freedom. Heart condition. Dies when husband returns alive - "joy that kills."</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ›๏ธ Uncle Ben (50)</div>
                            <div class="character-desc" style="color: #1e293b !important;"><i>Uncle Ben's Choice</i> by Chinua Achebe. Civil servant navigating moral compromise in postcolonial Nigeria. Pragmatic and ethically conflicted, chooses silence over confrontation.</div>
                        </div>

                        <!-- MYSTIC RIVER -->
                        <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 0.5rem 1rem; border-radius: 6px; margin: 1rem 0 0.5rem 0; font-weight: 700;">
                            ๐ŸŒŠ MYSTIC RIVER by Dennis Lehane
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ“– Jimmy Markum (38)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Corner store owner and ex-convict driven by grief and vengeance after daughter Katie's murder. Uses street-level intuition and intimidation outside the law.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐ŸŽญ Sean Devine (38)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Homicide detective torn between duty and personal history. Methodical and guarded, investigating the case that haunts his childhood friendships.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ”ช Dave Boyle (38)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Childhood friend abducted as child, never recovered. Haunted and suspect in Katie's murder. Tragic victim of misplaced vengeance.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ’ Brendan Harris (19)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Katie's secret boyfriend. Plans to elope with her. Devastated by her death, wrongly suspected by both police and Jimmy.</div>
                        </div>

                        <!-- JOHN MISAK: JOHN KEEGAN SERIES -->
                        <div style="background: linear-gradient(135deg, #f5576c 0%, #f093fb 100%); color: white; padding: 0.5rem 1rem; border-radius: 6px; margin: 1rem 0 0.5rem 0; font-weight: 700;">
                            ๐Ÿš” JOHN KEEGAN MYSTERIES by John Misak
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿš” John Keegan (52)</div>
                            <div class="character-desc" style="color: #1e293b !important;"><i>Ghosts of Days Gone By</i> (Book #8). NYPD Deputy Inspector. Russian mob calls him "Okhotnik" (bogeyman). Haunted by killing Boris Yigevny, separated from wife Pauline, star of TV show "Dark Justice". Investigating Alice Duncan's murder in Central Park.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ‘ฎโ€โ™€๏ธ Pauline McCrory-Keegan (48)</div>
                            <div class="character-desc" style="color: #1e293b !important;"><i>To The Bone</i> (Pauline Series #1). NYPD officer on leave from work and marriage, staying in St. Michaels. Struggling with identity outside John's shadow. Investigating missing 12-year-old Melissa Carver to feel useful again.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ•ต๏ธ Karl Lavin (58)</div>
                            <div class="character-desc" style="color: #1e293b !important;"><i>Ghosts of Days Gone By</i>. NYPD Lieutenant, Keegan's partner and best friend. Shot by Russians 15 years ago. Known for crude humor, loyalty, and ball-busting. Planning retirement to Tennessee.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐Ÿ” Arianna Nunez (32)</div>
                            <div class="character-desc" style="color: #1e293b !important;"><i>Ghosts of Days Gone By</i>. NYPD Detective. Works with Keegan and Lavin. Sharp, witty, pop-culture savvy. Keegan calls her "Millennial". Ambitious and digitally fluent investigator.</div>
                        </div>

                        <!-- THE LAST OF US -->
                        <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 0.5rem 1rem; border-radius: 6px; margin: 1rem 0 0.5rem 0; font-weight: 700;">
                            ๐ŸŽฎ THE LAST OF US by Naughty Dog
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐ŸŽฎ Joel (50)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Survivor hardened by daughter Sarah's death. Saved Ellie from Fireflies, lied about it. Morally complex protector struggling with guilt and love.</div>
                        </div>
                        <div class="character-card">
                            <div class="character-name" style="color: #667eea !important;">๐ŸŽฎ Ellie (14-19)</div>
                            <div class="character-desc" style="color: #1e293b !important;">Survivor immune to infection. Lost friend Riley, bonded with Joel as father figure. Tough exterior hiding vulnerability and survivor's guilt.</div>
                        </div>
                    </div>
                """)

            gr.HTML("""
                <div style="background: linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%);
                            border-radius: 12px; padding: 1.2rem; margin-bottom: 1rem;">
                    <h3 style="color: #2d3748; margin: 0; font-size: 1.2rem; font-weight: 600;">
                        โš™๏ธ Settings
                    </h3>
                </div>
            """)

            ai_mode_selector = gr.Radio(
                label="Response Mode",
                choices=["AI (HuggingFace)", "Templates (Local)"],
                value="AI (HuggingFace)",
                info="๐Ÿค– Choose how responses are generated"
            )

            use_fast_mode_toggle = gr.Checkbox(
                label="โšก Fast Mode (Local AI)",
                value=True,
                info="Uses API if HF_TOKEN/ANTHROPIC_API_KEY set, otherwise local TinyLlama (~15-20s)",
                visible=False  # Hide since API tokens take priority anyway
            )

        with gr.Column(scale=2):
            gr.HTML("""
                <div style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
                            border-radius: 12px; padding: 1.2rem; margin-bottom: 1rem;">
                    <h3 style="color: white; margin: 0; font-size: 1.2rem; font-weight: 600;">
                        ๐Ÿ’ฌ Conversation
                    </h3>
                </div>
            """)

            conversation_display = gr.HTML(
                label="",
                value="""
                <div style='text-align: center; padding: 3rem; color: #6c757d;'>
                    <h3 style='font-size: 1.5rem; margin-bottom: 1rem;'>๐ŸŽญ Ready to Begin!</h3>
                    <p style='font-size: 1.1rem;'>Select a character and ask your first question to start exploring</p>
                </div>
                """,
                elem_classes=["conversation-display"]
            )


    # User input row with modern styling
    with gr.Row():
        with gr.Column():
            gr.HTML("""
                <div style="margin-top: 1.5rem; margin-bottom: 0.5rem;">
                    <h3 style="color: #495057; font-size: 1.2rem; font-weight: 600; margin: 0;">
                        โœ๏ธ Ask Your Question
                    </h3>
                </div>
            """)
            student_prompt = gr.Textbox(
                label="",
                lines=3,
                placeholder="๐Ÿ’ญ Example: How did that moment change you? What were you thinking when...?",
                value="",
                elem_classes=["input-box"]
            )

    # Buttons row with modern gradients
    with gr.Row():
        with gr.Column(scale=1):
            send_btn = gr.Button(
                "๐Ÿš€ Send Question",
                variant="primary",
                size="lg",
                elem_classes=["btn-primary"]
            )
        with gr.Column(scale=1):
            download_btn = gr.Button(
                "๐Ÿ’พ Download Session",
                variant="secondary",
                size="lg"
            )
        with gr.Column(scale=1):
            reset_btn = gr.Button(
                "๐Ÿ”„ New Conversation",
                variant="secondary",
                size="lg"
            )

    # Feedback + technical state row with gradient card
    with gr.Row():
        with gr.Column():
            gr.HTML("""
                <div style="background: linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%);
                            border-radius: 12px; padding: 1rem; margin: 1.5rem 0 0.5rem 0;">
                    <h3 style="color: #2d3748; margin: 0; font-size: 1.2rem; font-weight: 600;">
                        ๐Ÿ“ Learning Feedback
                    </h3>
                </div>
            """)
            teaching_output = gr.HTML(
                label="",
                elem_classes=["feedback-box"]
            )
        with gr.Column():
            state_output = gr.Textbox(
                label="Technical State (for debugging)",
                lines=8,
                visible=False
            )

    # Charts row with cards
    gr.HTML("""
        <div style="margin-top: 2rem; margin-bottom: 1rem;">
            <h2 style="color: #495057; font-size: 1.5rem; font-weight: 700; text-align: center;">
                ๐Ÿ“Š Character Analysis
            </h2>
        </div>
    """)

    with gr.Row():
        with gr.Column():
            gr.HTML("""
                <div style="text-align: center; margin-bottom: 0.5rem;">
                    <h4 style="color: #667eea; font-size: 1.1rem; font-weight: 600;">
                        Current Emotional State
                    </h4>
                </div>
            """)
            current_state_chart = gr.Image(
                label="",
                elem_classes=["chart-container"]
            )
        with gr.Column():
            gr.HTML("""
                <div style="text-align: center; margin-bottom: 0.5rem;">
                    <h4 style="color: #667eea; font-size: 1.1rem; font-weight: 600;">
                        Engagement Over Time
                    </h4>
                </div>
            """)
            history_chart = gr.Image(
                label="",
                elem_classes=["chart-container"]
            )

    # Hidden file output for downloads
    download_file = gr.File(
        label="Your session transcript (right-click to save)",
        visible=True
    )

    # Instructor guide with modern styling
    with gr.Accordion("๐Ÿ‘จโ€๐Ÿซ Instructor Guide", open=False):
        gr.HTML("""
            <div style="background: linear-gradient(135deg, #e0e7ff 0%, #ddd6fe 100%);
                        border-radius: 12px; padding: 2rem; border: 2px solid #6366f1;
                        box-shadow: 0 4px 12px rgba(99, 102, 241, 0.15);">
                <h3 style="color: #4338ca; margin-top: 0; font-size: 1.4rem; font-weight: 700;">๐Ÿ“š How to Use This Tool</h3>

                <p style="font-size: 1.05rem; line-height: 1.7; color: #1e293b; font-weight: 500;">
                    This simulator enables students to engage with literary characters through
                    interactive dialogue, fostering deeper understanding of narrative and character development.
                </p>

                <h4 style="color: #4338ca; margin-top: 1.5rem; font-size: 1.15rem; font-weight: 700;">Learning Objectives:</h4>
                <ul style="font-size: 1.05rem; line-height: 1.9; color: #0f172a; font-weight: 500;">
                    <li>โœ… Practice close reading and character analysis</li>
                    <li>โœ… Explore narrative perspectives and authentic voice</li>
                    <li>โœ… Understand character psychology and emotional depth</li>
                    <li>โœ… Develop critical thinking about literary texts</li>
                    <li>โœ… Learn effective questioning techniques for analysis</li>
                </ul>

                <h4 style="color: #4338ca; margin-top: 1.5rem; font-size: 1.15rem; font-weight: 700;">Features:</h4>
                <ul style="font-size: 1.05rem; line-height: 1.9; color: #0f172a; font-weight: 500;">
                    <li>๐Ÿ“Š <strong>Real-time Feedback:</strong> Students receive immediate guidance on their questions</li>
                    <li>๐Ÿ“ˆ <strong>Emotional Tracking:</strong> Visualizations show how characters respond emotionally</li>
                    <li>๐Ÿ’พ <strong>Session Transcripts:</strong> Download conversations for review and reflection</li>
                    <li>โšก <strong>Fast AI Responses:</strong> Local model provides quick, authentic character interactions</li>
                </ul>

                <div style="background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
                            border-left: 5px solid #f59e0b; padding: 1.25rem;
                            margin-top: 1.5rem; border-radius: 10px;
                            box-shadow: 0 2px 8px rgba(245, 158, 11, 0.2);">
                    <p style="margin: 0; color: #78350f; font-size: 1rem; font-weight: 600; line-height: 1.7;">
                        <strong>๐Ÿ’ก Tip:</strong> Encourage students to ask open-ended questions that
                        explore motivations, conflicts, and character development rather than simple
                        factual questions about plot.
                    </p>
                </div>
            </div>
        """)

    # Button actions
    send_btn.click(
        fn=simulate,
        inputs=[
            student_prompt,
            scenario_selector,
            persona_selector,
            ai_mode_selector,
            conversation_state,
            state_history,
            use_fast_mode_toggle
        ],
        outputs=[
            conversation_display,
            teaching_output,
            state_output,
            current_state_chart,
            history_chart,
            conversation_state,
            state_history
        ]
    )



    download_btn.click(
        fn=download_session,
        inputs=[
            conversation_state,
            state_history,
            persona_selector
        ],
        outputs=download_file
    )

    def reset_conversation():
        return (
            "*Conversation will appear here...*",
            "",
            "",
            None,
            None,
            [],
            []
        )

    reset_btn.click(
        fn=reset_conversation,
        inputs=[],
        outputs=[
            conversation_display,
            teaching_output,
            state_output,
            current_state_chart,
            history_chart,
            conversation_state,
            state_history
        ]
    )

if __name__ == "__main__":
    os.makedirs("personas", exist_ok=True)
    os.makedirs("contexts", exist_ok=True)
    os.makedirs("transcripts", exist_ok=True)
    os.makedirs("engine", exist_ok=True)

    ui.launch(share=True, server_name="0.0.0.0", server_port=7860)