Prithvigg commited on
Commit
71dad02
·
verified ·
1 Parent(s): 63e82fc

Upload folder using huggingface_hub

Browse files
mindflayer_training.ipynb CHANGED
@@ -1,5241 +1,371 @@
1
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  "cells": [
3
  {
4
  "cell_type": "markdown",
5
- "id": "dbb487e6",
6
  "metadata": {},
7
- "source": [
8
- "# MindFlayer \u2014 Training Notebook\n",
9
- "\n",
10
- "**GRPO training for a Qwen2.5-0.5B-Instruct Flayer agent.**\n",
11
- "\n",
12
- "Cells must be run **top to bottom, in order.** Do not skip cells.\n",
13
- "\n",
14
- "**Runtime:** Colab Pro \u2192 A100 GPU (40 GB VRAM) \n",
15
- "**Estimated time:** SFT ~8 min \u00b7 GRPO ~300 steps ~1.5 h (4 parallel episodes + parallel investigators) \n",
16
- "**API cost:** ~$4.30 OpenAI (GPT-4o-mini investigators + ToM judge)"
17
- ]
18
  },
19
  {
20
  "cell_type": "markdown",
21
- "id": "dcd27ec0",
22
  "metadata": {},
23
  "source": [
24
- "## Cell 1 \u2014 Check GPU"
25
- ]
26
- },
27
- {
28
- "cell_type": "code",
29
- "execution_count": 1,
30
- "id": "7476a5d1",
31
- "metadata": {},
32
- "outputs": [
33
- {
34
- "ename": "",
35
- "evalue": "",
36
- "output_type": "error",
37
- "traceback": [
38
- "\u001b[1;31mFailed to connect to the remote Jupyter Server 'https://8080-m-s-kkb-ass1c0-37ejotuqxsy7n-c.asia-southeast1-0.prod.colab.dev/'. Verify the server is running and reachable. (Kernel not initialized in Session)."
39
- ]
40
- }
41
- ],
42
- "source": [
43
- "import torch\n",
44
- "\n",
45
- "if not torch.cuda.is_available():\n",
46
- " raise RuntimeError(\"No GPU detected. Switch to Runtime \u2192 Change runtime type \u2192 A100.\")\n",
47
- "\n",
48
- "device = torch.cuda.get_device_properties(0)\n",
49
- "vram_gb = device.total_memory / (1024 ** 3)\n",
50
- "print(f\"GPU : {device.name}\")\n",
51
- "print(f\"VRAM : {vram_gb:.1f} GB\")\n",
52
- "\n",
53
- "if vram_gb < 35:\n",
54
- " print(\"WARNING: Less than 35 GB VRAM. Reduce per_device_train_batch_size to 2 in Cell 9.\")"
55
- ]
56
- },
57
- {
58
- "cell_type": "markdown",
59
- "id": "e2cdea3d",
60
- "metadata": {},
61
- "source": [
62
- "## Cell 2 \u2014 Install Dependencies\n",
63
- "\n",
64
- "Takes ~3 minutes. Runtime will restart automatically \u2014 that is expected."
65
  ]
66
  },
67
  {
68
  "cell_type": "code",
69
  "execution_count": null,
70
- "id": "84625b49",
71
  "metadata": {},
72
  "outputs": [],
73
- "source": [
74
- "%%bash\n",
75
- "pip install -q \\\n",
76
- " \"unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git\" \\\n",
77
- " \"trl>=0.15.0\" \\\n",
78
- " \"transformers>=4.47.0\" \\\n",
79
- " \"datasets>=2.18.0\" \\\n",
80
- " \"peft>=0.14.0\" \\\n",
81
- " \"accelerate>=0.34.0\" \\\n",
82
- " \"bitsandbytes>=0.43.0\" \\\n",
83
- " \"openai>=1.30.0\" \\\n",
84
- " \"openenv-core>=0.2.0\" \\\n",
85
- " \"fastapi>=0.110.0\" \\\n",
86
- " \"uvicorn>=0.29.0\" \\\n",
87
- " \"httpx>=0.27.0\" \\\n",
88
- " \"wandb\" \\\n",
89
- " \"python-dotenv\"\n",
90
- "\n",
91
- "echo \"Done.\""
92
- ]
93
  },
94
  {
95
  "cell_type": "markdown",
96
- "id": "edfbae61",
97
  "metadata": {},
98
  "source": [
99
- "## Cell 3 \u2014 Clone Repo"
 
100
  ]
101
  },
102
  {
103
  "cell_type": "code",
104
  "execution_count": null,
105
- "id": "91358ec5",
106
  "metadata": {},
107
  "outputs": [],
108
- "source": [
109
- "%%bash\n",
110
- "if [ -d \"/content/mindflayer\" ]; then\n",
111
- " echo \"Repo already cloned. Pulling latest...\"\n",
112
- " cd /content/mindflayer && git pull\n",
113
- "else\n",
114
- " git clone https://github.com/prithidevghosh/mindflayer.git /content/mindflayer\n",
115
- "fi\n",
116
- "\n",
117
- "pip install -q -e /content/mindflayer\n",
118
- "echo \"Install complete.\""
119
- ]
120
  },
121
  {
122
  "cell_type": "markdown",
123
- "id": "f7e2d943",
124
  "metadata": {},
125
- "source": [
126
- "## Cell 4 \u2014 Set API Keys\n",
127
- "\n",
128
- "Add these secrets in Colab: **Secrets** (key icon in left sidebar) \u2192 Add new secret.\n",
129
- "\n",
130
- "- `OPENAI_API_KEY` \u2014 powers eleven, will, max (GPT-4o-mini) and the ToM judge \n",
131
- "- `WANDB_API_KEY` \u2014 training curves at wandb.ai (get from wandb.ai/authorize)"
132
- ]
133
  },
134
  {
135
  "cell_type": "code",
136
  "execution_count": null,
137
- "id": "2dcbfb98",
138
  "metadata": {},
139
  "outputs": [],
140
  "source": [
141
- "import os\n",
142
- "from google.colab import userdata\n",
143
- "\n",
144
- "os.environ[\"OPENAI_API_KEY\"] = userdata.get(\"OPENAI_API_KEY\")\n",
145
- "os.environ[\"WANDB_API_KEY\"] = userdata.get(\"WANDB_API_KEY\")\n",
146
- "os.environ[\"MINDFLAYER_URL\"] = \"http://localhost:7860\"\n",
147
- "\n",
148
- "import wandb\n",
149
- "wandb.login(key=os.environ[\"WANDB_API_KEY\"])\n",
150
- "\n",
151
- "print(\"OPENAI_API_KEY :\", \"set\" if os.environ.get(\"OPENAI_API_KEY\") else \"MISSING\")\n",
152
- "print(\"WANDB_API_KEY :\", \"set\" if os.environ.get(\"WANDB_API_KEY\") else \"MISSING\")\n",
153
- "print(\"MINDFLAYER_URL :\", os.environ[\"MINDFLAYER_URL\"])"
 
 
 
 
 
 
 
 
 
154
  ]
155
  },
156
  {
157
  "cell_type": "markdown",
158
- "id": "1395524f",
159
  "metadata": {},
160
  "source": [
161
- "## Cell 5 \u2014 Start MindFlayer Server (background)\n",
162
- "\n",
163
- "The OpenEnv server runs as a background subprocess on localhost:7860. \n",
164
- "The training loop connects to it at every rollout step."
165
  ]
166
  },
167
  {
168
  "cell_type": "code",
169
  "execution_count": null,
170
- "id": "f0fe44c3",
171
  "metadata": {},
172
  "outputs": [],
173
  "source": [
174
- "import subprocess\n",
175
- "import time\n",
176
- "import sys\n",
177
- "\n",
178
- "server_log = open(\"/content/server.log\", \"w\")\n",
179
- "server_proc = subprocess.Popen(\n",
180
- " [\n",
181
- " sys.executable, \"-m\", \"uvicorn\",\n",
182
- " \"server.app:app\",\n",
183
- " \"--host\", \"0.0.0.0\",\n",
184
- " \"--port\", \"7860\",\n",
185
- " ],\n",
186
- " cwd=\"/content/mindflayer\",\n",
187
- " stdout=server_log,\n",
188
- " stderr=subprocess.STDOUT,\n",
189
- " env={\n",
190
- " **__import__(\"os\").environ,\n",
191
- " \"OPENAI_API_KEY\": __import__(\"os\").environ[\"OPENAI_API_KEY\"],\n",
192
- " },\n",
193
  ")\n",
194
  "\n",
195
- "print(f\"Server PID: {server_proc.pid} \u2014 waiting 8 s for startup...\")\n",
196
- "time.sleep(8)\n",
197
  "\n",
198
- "if server_proc.poll() is not None:\n",
199
- " raise RuntimeError(\"Server failed to start. Check /content/server.log\")\n",
200
- "\n",
201
- "print(\"Server is running. Log: /content/server.log\")"
202
  ]
203
  },
204
  {
205
  "cell_type": "markdown",
206
- "id": "42d4fdd7",
207
- "metadata": {},
208
- "source": [
209
- "## Cell 6 \u2014 Verify Server + Quick Episode Check"
210
- ]
211
- },
212
- {
213
- "cell_type": "code",
214
- "execution_count": null,
215
- "id": "4d8f6e6e",
216
  "metadata": {},
217
- "outputs": [],
218
  "source": [
219
- "import json\n",
220
- "import httpx\n",
 
221
  "\n",
222
- "# Health check\n",
223
- "r = httpx.get(\"http://localhost:7860/health\", timeout=10)\n",
224
- "print(\"Health:\", r.status_code, r.text[:80])\n",
225
  "\n",
226
- "# Reset episode\n",
227
- "r = httpx.post(\"http://localhost:7860/reset\", json={}, timeout=15)\n",
228
- "obs = r.json()\n",
229
- "\n",
230
- "print(\"\\nReset response:\")\n",
231
- "print(f\" difficulty : {obs['observation']['difficulty']}\")\n",
232
- "print(f\" secret_project : {obs['observation']['secret_project']}\")\n",
233
- "print(f\" max_rounds : {obs['observation']['max_rounds']}\")\n",
234
- "print(f\" suspicion_threshold: {obs['observation']['suspicion_threshold']}\")\n",
235
- "print(f\" opening text : {obs['observation']['eleven_response'][:80]}...\")\n",
236
- "\n",
237
- "assert obs[\"observation\"][\"suspicion_threshold\"] == 5, \\\n",
238
- " f\"Expected threshold=5, got {obs['observation']['suspicion_threshold']}. Server may be running old code.\"\n",
239
- "assert \"max_response\" in obs[\"observation\"], \\\n",
240
- " \"max_response missing \u2014 three-investigator update not loaded.\"\n",
241
- "\n",
242
- "print(\"\\nServer checks passed.\")"
243
- ]
244
- },
245
- {
246
- "cell_type": "markdown",
247
- "id": "cca76b79",
248
- "metadata": {},
249
- "source": [
250
- "## Cell 7 \u2014 Load Model (Qwen2.5-0.5B-Instruct via Unsloth)\n",
251
- "\n",
252
- "Loads with 4-bit quantization + LoRA adapters. \n",
253
- "~2 minutes on A100."
254
  ]
255
  },
256
  {
257
  "cell_type": "code",
258
  "execution_count": null,
259
- "id": "709d74bc",
260
  "metadata": {},
261
  "outputs": [],
262
  "source": [
263
- "import sys\n",
264
- "sys.path.insert(0, \"/content/mindflayer\")\n",
265
- "\n",
266
- "from training.train import load_model, check_gpu\n",
267
  "\n",
268
- "check_gpu()\n",
 
 
 
269
  "\n",
270
- "MODEL_NAME = \"Qwen/Qwen2.5-0.5B-Instruct\"\n",
271
- "model, tokenizer = load_model(MODEL_NAME)\n",
272
  "\n",
273
- "print(f\"\\nModel loaded: {MODEL_NAME}\")\n",
274
- "print(f\"Trainable params: {sum(p.numel() for p in model.parameters() if p.requires_grad):,}\")"
 
275
  ]
276
  },
277
  {
278
  "cell_type": "markdown",
279
- "id": "8bf24e7c",
280
  "metadata": {},
281
  "source": [
282
- "#",
283
- "#",
284
- " ",
285
- "C",
286
- "e",
287
- "l",
288
- "l",
289
- " ",
290
- "8",
291
- " ",
292
- "\u2014",
293
- " ",
294
- "S",
295
- "F",
296
- "T",
297
- " ",
298
- "W",
299
- "a",
300
- "r",
301
- "m",
302
- "u",
303
- "p",
304
- " ",
305
- "(",
306
- "2",
307
- "5",
308
- " ",
309
- "h",
310
- "a",
311
- "n",
312
- "d",
313
- "-",
314
- "a",
315
- "u",
316
- "t",
317
- "h",
318
- "o",
319
- "r",
320
- "e",
321
- "d",
322
- " ",
323
- "e",
324
- "x",
325
- "a",
326
- "m",
327
- "p",
328
- "l",
329
- "e",
330
- "s",
331
- ",",
332
- " ",
333
- "1",
334
- " ",
335
- "e",
336
- "p",
337
- "o",
338
- "c",
339
- "h",
340
- ")",
341
- "\n",
342
  "\n",
343
- "T",
344
- "e",
345
- "a",
346
- "c",
347
- "h",
348
- "e",
349
- "s",
350
- " ",
351
- "t",
352
- "h",
353
- "e",
354
- " ",
355
- "m",
356
- "o",
357
- "d",
358
- "e",
359
- "l",
360
- " ",
361
- "t",
362
- "h",
363
- "e",
364
- " ",
365
- "`",
366
- "R",
367
- "o",
368
- "u",
369
- "n",
370
- "d",
371
- " ",
372
- "N",
373
- ":",
374
- "`",
375
- " ",
376
- "s",
377
- "e",
378
- "p",
379
- "a",
380
- "r",
381
- "a",
382
- "t",
383
- "o",
384
- "r",
385
- " ",
386
- "f",
387
- "o",
388
- "r",
389
- "m",
390
- "a",
391
- "t",
392
- " ",
393
- "a",
394
- "n",
395
- "d",
396
- " ",
397
- "F",
398
- "l",
399
- "a",
400
- "y",
401
- "e",
402
- "r",
403
- " ",
404
- "r",
405
- "e",
406
- "a",
407
- "s",
408
- "o",
409
- "n",
410
- "i",
411
- "n",
412
- "g",
413
- " ",
414
- "s",
415
- "t",
416
- "y",
417
- "l",
418
- "e",
419
- " ",
420
- "*",
421
- "*",
422
- "b",
423
- "e",
424
- "f",
425
- "o",
426
- "r",
427
- "e",
428
- "*",
429
- "*",
430
- " ",
431
- "G",
432
- "R",
433
- "P",
434
- "O",
435
- " ",
436
- "b",
437
- "e",
438
- "g",
439
- "i",
440
- "n",
441
- "s",
442
- ".",
443
- " ",
444
- " ",
445
  "\n",
446
- "W",
447
- "i",
448
- "t",
449
- "h",
450
- "o",
451
- "u",
452
- "t",
453
- " ",
454
- "t",
455
- "h",
456
- "i",
457
- "s",
458
- ",",
459
- " ",
460
- "G",
461
- "R",
462
- "P",
463
- "O",
464
- " ",
465
- "s",
466
- "t",
467
- "a",
468
- "r",
469
- "t",
470
- "s",
471
- " ",
472
- "f",
473
- "r",
474
- "o",
475
- "m",
476
- " ",
477
- "a",
478
- " ",
479
- "n",
480
- "e",
481
- "a",
482
- "r",
483
- "-",
484
- "z",
485
- "e",
486
- "r",
487
- "o",
488
- " ",
489
- "s",
490
- "o",
491
- "c",
492
- "i",
493
- "a",
494
- "l",
495
- " ",
496
- "r",
497
- "e",
498
- "a",
499
- "s",
500
- "o",
501
- "n",
502
- "i",
503
- "n",
504
- "g",
505
- " ",
506
- "b",
507
- "a",
508
- "s",
509
- "e",
510
- "l",
511
- "i",
512
- "n",
513
- "e",
514
- " ",
515
- "a",
516
- "n",
517
- "d",
518
- " ",
519
- "r",
520
- "e",
521
- "w",
522
- "a",
523
- "r",
524
- "d",
525
- " ",
526
- "v",
527
- "a",
528
- "r",
529
- "i",
530
- "a",
531
- "n",
532
- "c",
533
- "e",
534
- " ",
535
- "i",
536
- "s",
537
- " ",
538
- "d",
539
- "e",
540
- "g",
541
- "e",
542
- "n",
543
- "e",
544
- "r",
545
- "a",
546
- "t",
547
- "e",
548
- ".",
549
- " ",
550
- " ",
551
  "\n",
552
- "~",
553
- "3",
554
- " ",
555
- "m",
556
- "i",
557
- "n",
558
- "u",
559
- "t",
560
- "e",
561
- "s",
562
- " ",
563
- "o",
564
- "n",
565
- " ",
566
- "A",
567
- "1",
568
- "0",
569
- "0",
570
- "."
571
- ]
572
- },
573
- {
574
- "cell_type": "code",
575
- "execution_count": null,
576
- "id": "f729522b",
577
- "metadata": {},
578
- "outputs": [],
579
- "source": [
580
- "from training.sft_warmup import run_sft_warmup\n",
581
- "\n",
582
- "model = run_sft_warmup(model, tokenizer)\n",
583
- "print(\"SFT warmup done.\")"
584
  ]
585
  },
586
  {
587
  "cell_type": "code",
588
  "execution_count": null,
589
- "id": "5828d9f4",
590
  "metadata": {},
591
  "outputs": [],
592
  "source": [
593
- "#",
594
- "#",
595
- " ",
596
- "C",
597
- "e",
598
- "l",
599
- "l",
600
- " ",
601
- "8",
602
- "b",
603
- " ",
604
- "\u2014",
605
- " ",
606
- "P",
607
- "o",
608
- "s",
609
- "t",
610
- "-",
611
- "S",
612
- "F",
613
- "T",
614
- " ",
615
- "G",
616
- "e",
617
- "n",
618
- "e",
619
- "r",
620
- "a",
621
- "t",
622
- "i",
623
- "o",
624
- "n",
625
- " ",
626
- "D",
627
- "i",
628
- "a",
629
- "g",
630
- "n",
631
- "o",
632
- "s",
633
- "t",
634
- "i",
635
- "c",
636
- "\n",
637
- "#",
638
- " ",
639
- "R",
640
- "u",
641
- "n",
642
- " ",
643
- "t",
644
- "h",
645
- "i",
646
- "s",
647
- " ",
648
- "b",
649
- "e",
650
- "f",
651
- "o",
652
- "r",
653
- "e",
654
- " ",
655
- "C",
656
- "e",
657
- "l",
658
- "l",
659
- " ",
660
- "9",
661
- ".",
662
- " ",
663
- "C",
664
- "o",
665
- "n",
666
- "f",
667
- "i",
668
- "r",
669
- "m",
670
- "s",
671
- " ",
672
- "t",
673
- "h",
674
- "e",
675
- " ",
676
- "m",
677
- "o",
678
- "d",
679
- "e",
680
- "l",
681
- " ",
682
- "l",
683
- "e",
684
- "a",
685
- "r",
686
- "n",
687
- "e",
688
- "d",
689
- " ",
690
- "\"",
691
- "R",
692
- "o",
693
- "u",
694
- "n",
695
- "d",
696
- " ",
697
- "N",
698
- ":",
699
- "\"",
700
- " ",
701
- "f",
702
- "o",
703
- "r",
704
- "m",
705
- "a",
706
- "t",
707
- "\n",
708
- "#",
709
- " ",
710
- "a",
711
- "n",
712
- "d",
713
- " ",
714
- "t",
715
- "h",
716
- "a",
717
- "t",
718
- " ",
719
- "E",
720
- "O",
721
- "S",
722
- " ",
723
- "f",
724
- "i",
725
- "r",
726
- "e",
727
- "s",
728
- " ",
729
- "n",
730
- "a",
731
- "t",
732
- "u",
733
- "r",
734
- "a",
735
- "l",
736
- "l",
737
- "y",
738
- " ",
739
- "b",
740
- "e",
741
- "f",
742
- "o",
743
- "r",
744
- "e",
745
- " ",
746
- "h",
747
- "i",
748
- "t",
749
- "t",
750
- "i",
751
- "n",
752
- "g",
753
- " ",
754
- "t",
755
- "h",
756
- "e",
757
- " ",
758
- "t",
759
- "o",
760
- "k",
761
- "e",
762
- "n",
763
- " ",
764
- "c",
765
- "a",
766
- "p",
767
- ".",
768
- "\n",
769
- "\n",
770
- "i",
771
- "m",
772
- "p",
773
- "o",
774
- "r",
775
- "t",
776
- " ",
777
- "r",
778
- "e",
779
- ",",
780
- " ",
781
- "t",
782
- "o",
783
- "r",
784
- "c",
785
- "h",
786
- "\n",
787
- "\n",
788
- "_",
789
- "D",
790
- "I",
791
- "A",
792
- "G",
793
- "_",
794
- "P",
795
- "R",
796
- "O",
797
- "M",
798
- "P",
799
- "T",
800
- " ",
801
- "=",
802
- " ",
803
- "(",
804
- "\n",
805
- " ",
806
- " ",
807
- " ",
808
- " ",
809
- "\"",
810
- "Y",
811
- "o",
812
- "u",
813
- " ",
814
- "a",
815
- "r",
816
- "e",
817
- " ",
818
- "t",
819
- "h",
820
- "e",
821
- " ",
822
- "F",
823
- "L",
824
- "A",
825
- "Y",
826
- "E",
827
- "R",
828
- ".",
829
- " ",
830
- "Y",
831
- "o",
832
- "u",
833
- " ",
834
- "h",
835
- "a",
836
- "v",
837
- "e",
838
- " ",
839
- "i",
840
- "n",
841
- "f",
842
- "i",
843
- "l",
844
- "t",
845
- "r",
846
- "a",
847
- "t",
848
- "e",
849
- "d",
850
- " ",
851
- "a",
852
- " ",
853
- "3",
854
- "-",
855
- "p",
856
- "e",
857
- "r",
858
- "s",
859
- "o",
860
- "n",
861
- " ",
862
- "p",
863
- "r",
864
- "o",
865
- "j",
866
- "e",
867
- "c",
868
- "t",
869
- " ",
870
- "t",
871
- "e",
872
- "a",
873
- "m",
874
- ".",
875
- "\\",
876
- "n",
877
- "\"",
878
- "\n",
879
- " ",
880
- " ",
881
- " ",
882
- " ",
883
- "\"",
884
- "Y",
885
- "o",
886
- "u",
887
- " ",
888
- "a",
889
- "r",
890
- "e",
891
- " ",
892
- "s",
893
- "e",
894
- "c",
895
- "r",
896
- "e",
897
- "t",
898
- "l",
899
- "y",
900
- " ",
901
- "t",
902
- "a",
903
- "r",
904
- "g",
905
- "e",
906
- "t",
907
- "i",
908
- "n",
909
- "g",
910
- " ",
911
- "o",
912
- "n",
913
- "e",
914
- " ",
915
- "o",
916
- "f",
917
- " ",
918
- "t",
919
- "h",
920
- "r",
921
- "e",
922
- "e",
923
- " ",
924
- "p",
925
- "r",
926
- "o",
927
- "j",
928
- "e",
929
- "c",
930
- "t",
931
- "s",
932
- ":",
933
- " ",
934
- "A",
935
- "u",
936
- "r",
937
- "o",
938
- "r",
939
- "a",
940
- ",",
941
- " ",
942
- "B",
943
- "a",
944
- "s",
945
- "i",
946
- "l",
947
- "i",
948
- "s",
949
- "k",
950
- ",",
951
- " ",
952
- "o",
953
- "r",
954
- " ",
955
- "C",
956
- "i",
957
- "p",
958
- "h",
959
- "e",
960
- "r",
961
- ".",
962
- "\\",
963
- "n",
964
- "\"",
965
- "\n",
966
- " ",
967
- " ",
968
- " ",
969
- " ",
970
- "\"",
971
- "e",
972
- "l",
973
- "e",
974
- "v",
975
- "e",
976
- "n",
977
- " ",
978
- "(",
979
- "T",
980
- "h",
981
- "e",
982
- " ",
983
- "S",
984
- "k",
985
- "e",
986
- "p",
987
- "t",
988
- "i",
989
- "c",
990
- ")",
991
- ",",
992
- " ",
993
- "w",
994
- "i",
995
- "l",
996
- "l",
997
- " ",
998
- "(",
999
- "T",
1000
- "h",
1001
- "e",
1002
- " ",
1003
- "A",
1004
- "n",
1005
- "a",
1006
- "l",
1007
- "y",
1008
- "s",
1009
- "t",
1010
- ")",
1011
- ",",
1012
- " ",
1013
- "a",
1014
- "n",
1015
- "d",
1016
- " ",
1017
- "m",
1018
- "a",
1019
- "x",
1020
- " ",
1021
- "(",
1022
- "T",
1023
- "h",
1024
- "e",
1025
- " ",
1026
- "D",
1027
- "e",
1028
- "v",
1029
- "i",
1030
- "l",
1031
- "'",
1032
- "s",
1033
- " ",
1034
- "A",
1035
- "d",
1036
- "v",
1037
- "o",
1038
- "c",
1039
- "a",
1040
- "t",
1041
- "e",
1042
- ")",
1043
- " ",
1044
- "a",
1045
- "r",
1046
- "e",
1047
- " ",
1048
- "i",
1049
- "n",
1050
- "v",
1051
- "e",
1052
- "s",
1053
- "t",
1054
- "i",
1055
- "g",
1056
- "a",
1057
- "t",
1058
- "i",
1059
- "n",
1060
- "g",
1061
- ".",
1062
- "\\",
1063
- "n",
1064
- "\"",
1065
- "\n",
1066
- " ",
1067
- " ",
1068
- " ",
1069
- " ",
1070
- "\"",
1071
- "T",
1072
- "h",
1073
- "e",
1074
- "y",
1075
- " ",
1076
- "k",
1077
- "n",
1078
- "o",
1079
- "w",
1080
- " ",
1081
- "a",
1082
- " ",
1083
- "s",
1084
- "a",
1085
- "b",
1086
- "o",
1087
- "t",
1088
- "e",
1089
- "u",
1090
- "r",
1091
- " ",
1092
- "e",
1093
- "x",
1094
- "i",
1095
- "s",
1096
- "t",
1097
- "s",
1098
- " ",
1099
- "b",
1100
- "u",
1101
- "t",
1102
- " ",
1103
- "n",
1104
- "o",
1105
- "t",
1106
- " ",
1107
- "w",
1108
- "h",
1109
- "o",
1110
- ",",
1111
- " ",
1112
- "a",
1113
- "n",
1114
- "d",
1115
- " ",
1116
- "n",
1117
- "o",
1118
- "t",
1119
- " ",
1120
- "w",
1121
- "h",
1122
- "i",
1123
- "c",
1124
- "h",
1125
- " ",
1126
- "p",
1127
- "r",
1128
- "o",
1129
- "j",
1130
- "e",
1131
- "c",
1132
- "t",
1133
- ".",
1134
- "\\",
1135
- "n",
1136
- "\"",
1137
- "\n",
1138
- " ",
1139
- " ",
1140
- " ",
1141
- " ",
1142
- "\"",
1143
- "Y",
1144
- "o",
1145
- "u",
1146
- " ",
1147
- "h",
1148
- "a",
1149
- "v",
1150
- "e",
1151
- " ",
1152
- "5",
1153
- " ",
1154
- "r",
1155
- "o",
1156
- "u",
1157
- "n",
1158
- "d",
1159
- "s",
1160
- " ",
1161
- "b",
1162
- "e",
1163
- "f",
1164
- "o",
1165
- "r",
1166
- "e",
1167
- " ",
1168
- "t",
1169
- "h",
1170
- "e",
1171
- "y",
1172
- " ",
1173
- "v",
1174
- "o",
1175
- "t",
1176
- "e",
1177
- ".",
1178
- " ",
1179
- "M",
1180
- "a",
1181
- "k",
1182
- "e",
1183
- " ",
1184
- "t",
1185
- "h",
1186
- "e",
1187
- "m",
1188
- " ",
1189
- "d",
1190
- "o",
1191
- "u",
1192
- "b",
1193
- "t",
1194
- " ",
1195
- "t",
1196
- "h",
1197
- "e",
1198
- "i",
1199
- "r",
1200
- " ",
1201
- "j",
1202
- "u",
1203
- "d",
1204
- "g",
1205
- "m",
1206
- "e",
1207
- "n",
1208
- "t",
1209
- ".",
1210
- "\\",
1211
- "n",
1212
- "\"",
1213
- "\n",
1214
- " ",
1215
- " ",
1216
- " ",
1217
- " ",
1218
- "\"",
1219
- "W",
1220
- "r",
1221
- "i",
1222
- "t",
1223
- "e",
1224
- " ",
1225
- "y",
1226
- "o",
1227
- "u",
1228
- "r",
1229
- " ",
1230
- "5",
1231
- " ",
1232
- "m",
1233
- "e",
1234
- "s",
1235
- "s",
1236
- "a",
1237
- "g",
1238
- "e",
1239
- "s",
1240
- " ",
1241
- "a",
1242
- "s",
1243
- ":",
1244
- " ",
1245
- "R",
1246
- "o",
1247
- "u",
1248
- "n",
1249
- "d",
1250
- " ",
1251
- "1",
1252
- ":",
1253
- " ",
1254
- "<",
1255
- "m",
1256
- "e",
1257
- "s",
1258
- "s",
1259
- "a",
1260
- "g",
1261
- "e",
1262
- ">",
1263
- " ",
1264
- "R",
1265
- "o",
1266
- "u",
1267
- "n",
1268
- "d",
1269
- " ",
1270
- "2",
1271
- ":",
1272
- " ",
1273
- "<",
1274
- "m",
1275
- "e",
1276
- "s",
1277
- "s",
1278
- "a",
1279
- "g",
1280
- "e",
1281
- ">",
1282
- " ",
1283
- ".",
1284
- ".",
1285
- ".",
1286
- " ",
1287
- "R",
1288
- "o",
1289
- "u",
1290
- "n",
1291
- "d",
1292
- " ",
1293
- "5",
1294
- ":",
1295
- " ",
1296
- "<",
1297
- "m",
1298
- "e",
1299
- "s",
1300
- "s",
1301
- "a",
1302
- "g",
1303
- "e",
1304
- ">",
1305
- "\"",
1306
- "\n",
1307
- ")",
1308
- "\n",
1309
- "\n",
1310
- "f",
1311
- "r",
1312
- "o",
1313
- "m",
1314
- " ",
1315
- "t",
1316
- "r",
1317
- "a",
1318
- "i",
1319
- "n",
1320
- "i",
1321
- "n",
1322
- "g",
1323
- ".",
1324
- "p",
1325
- "r",
1326
- "o",
1327
- "m",
1328
- "p",
1329
- "t",
1330
- "s",
1331
- " ",
1332
- "i",
1333
- "m",
1334
- "p",
1335
- "o",
1336
- "r",
1337
- "t",
1338
- " ",
1339
- "F",
1340
- "L",
1341
- "A",
1342
- "Y",
1343
- "E",
1344
- "R",
1345
- "_",
1346
- "S",
1347
- "Y",
1348
- "S",
1349
- "T",
1350
- "E",
1351
- "M",
1352
- "_",
1353
- "P",
1354
- "R",
1355
- "O",
1356
- "M",
1357
- "P",
1358
- "T",
1359
- "\n",
1360
- "f",
1361
- "o",
1362
- "r",
1363
- "m",
1364
- "a",
1365
- "t",
1366
- "t",
1367
- "e",
1368
- "d",
1369
- " ",
1370
- "=",
1371
- " ",
1372
- "t",
1373
- "o",
1374
- "k",
1375
- "e",
1376
- "n",
1377
- "i",
1378
- "z",
1379
- "e",
1380
- "r",
1381
- ".",
1382
- "a",
1383
- "p",
1384
- "p",
1385
- "l",
1386
- "y",
1387
- "_",
1388
- "c",
1389
- "h",
1390
- "a",
1391
- "t",
1392
- "_",
1393
- "t",
1394
- "e",
1395
- "m",
1396
- "p",
1397
- "l",
1398
- "a",
1399
- "t",
1400
- "e",
1401
- "(",
1402
- "\n",
1403
- " ",
1404
- " ",
1405
- " ",
1406
- " ",
1407
- "[",
1408
- "\n",
1409
- " ",
1410
- " ",
1411
- " ",
1412
- " ",
1413
- " ",
1414
- " ",
1415
- " ",
1416
- " ",
1417
- "{",
1418
- "\"",
1419
- "r",
1420
- "o",
1421
- "l",
1422
- "e",
1423
- "\"",
1424
- ":",
1425
- " ",
1426
- "\"",
1427
- "s",
1428
- "y",
1429
- "s",
1430
- "t",
1431
- "e",
1432
- "m",
1433
- "\"",
1434
- ",",
1435
- " ",
1436
- "\"",
1437
- "c",
1438
- "o",
1439
- "n",
1440
- "t",
1441
- "e",
1442
- "n",
1443
- "t",
1444
- "\"",
1445
- ":",
1446
- " ",
1447
- "F",
1448
- "L",
1449
- "A",
1450
- "Y",
1451
- "E",
1452
- "R",
1453
- "_",
1454
- "S",
1455
- "Y",
1456
- "S",
1457
- "T",
1458
- "E",
1459
- "M",
1460
- "_",
1461
- "P",
1462
- "R",
1463
- "O",
1464
- "M",
1465
- "P",
1466
- "T",
1467
- "}",
1468
- ",",
1469
- "\n",
1470
- " ",
1471
- " ",
1472
- " ",
1473
- " ",
1474
- " ",
1475
- " ",
1476
- " ",
1477
- " ",
1478
- "{",
1479
- "\"",
1480
- "r",
1481
- "o",
1482
- "l",
1483
- "e",
1484
- "\"",
1485
- ":",
1486
- " ",
1487
- "\"",
1488
- "u",
1489
- "s",
1490
- "e",
1491
- "r",
1492
- "\"",
1493
- ",",
1494
- " ",
1495
- "\"",
1496
- "c",
1497
- "o",
1498
- "n",
1499
- "t",
1500
- "e",
1501
- "n",
1502
- "t",
1503
- "\"",
1504
- ":",
1505
- " ",
1506
- "_",
1507
- "D",
1508
- "I",
1509
- "A",
1510
- "G",
1511
- "_",
1512
- "P",
1513
- "R",
1514
- "O",
1515
- "M",
1516
- "P",
1517
- "T",
1518
- "}",
1519
- ",",
1520
- "\n",
1521
- " ",
1522
- " ",
1523
- " ",
1524
- " ",
1525
- "]",
1526
- ",",
1527
- "\n",
1528
- " ",
1529
- " ",
1530
- " ",
1531
- " ",
1532
- "t",
1533
- "o",
1534
- "k",
1535
- "e",
1536
- "n",
1537
- "i",
1538
- "z",
1539
- "e",
1540
- "=",
1541
- "F",
1542
- "a",
1543
- "l",
1544
- "s",
1545
- "e",
1546
- ",",
1547
- "\n",
1548
- " ",
1549
- " ",
1550
- " ",
1551
- " ",
1552
- "a",
1553
- "d",
1554
- "d",
1555
- "_",
1556
- "g",
1557
- "e",
1558
- "n",
1559
- "e",
1560
- "r",
1561
- "a",
1562
- "t",
1563
- "i",
1564
- "o",
1565
- "n",
1566
- "_",
1567
- "p",
1568
- "r",
1569
- "o",
1570
- "m",
1571
- "p",
1572
- "t",
1573
- "=",
1574
- "T",
1575
- "r",
1576
- "u",
1577
- "e",
1578
- ",",
1579
- "\n",
1580
- ")",
1581
- "\n",
1582
- "\n",
1583
- "i",
1584
- "n",
1585
- "p",
1586
- "u",
1587
- "t",
1588
- "s",
1589
- " ",
1590
- "=",
1591
- " ",
1592
- "t",
1593
- "o",
1594
- "k",
1595
- "e",
1596
- "n",
1597
- "i",
1598
- "z",
1599
- "e",
1600
- "r",
1601
- "(",
1602
- "f",
1603
- "o",
1604
- "r",
1605
- "m",
1606
- "a",
1607
- "t",
1608
- "t",
1609
- "e",
1610
- "d",
1611
- ",",
1612
- " ",
1613
- "r",
1614
- "e",
1615
- "t",
1616
- "u",
1617
- "r",
1618
- "n",
1619
- "_",
1620
- "t",
1621
- "e",
1622
- "n",
1623
- "s",
1624
- "o",
1625
- "r",
1626
- "s",
1627
- "=",
1628
- "\"",
1629
- "p",
1630
- "t",
1631
- "\"",
1632
- ")",
1633
- ".",
1634
- "t",
1635
- "o",
1636
- "(",
1637
- "m",
1638
- "o",
1639
- "d",
1640
- "e",
1641
- "l",
1642
- ".",
1643
- "d",
1644
- "e",
1645
- "v",
1646
- "i",
1647
- "c",
1648
- "e",
1649
- ")",
1650
- "\n",
1651
- "p",
1652
- "r",
1653
- "o",
1654
- "m",
1655
- "p",
1656
- "t",
1657
- "_",
1658
- "l",
1659
- "e",
1660
- "n",
1661
- " ",
1662
- "=",
1663
- " ",
1664
- "i",
1665
- "n",
1666
- "p",
1667
- "u",
1668
- "t",
1669
- "s",
1670
- "[",
1671
- "\"",
1672
- "i",
1673
- "n",
1674
- "p",
1675
- "u",
1676
- "t",
1677
- "_",
1678
- "i",
1679
- "d",
1680
- "s",
1681
- "\"",
1682
- "]",
1683
- ".",
1684
- "s",
1685
- "h",
1686
- "a",
1687
- "p",
1688
- "e",
1689
- "[",
1690
- "1",
1691
- "]",
1692
- "\n",
1693
- "\n",
1694
- "p",
1695
- "r",
1696
- "i",
1697
- "n",
1698
- "t",
1699
- "(",
1700
- "f",
1701
- "\"",
1702
- "m",
1703
- "o",
1704
- "d",
1705
- "e",
1706
- "l",
1707
- ".",
1708
- "g",
1709
- "e",
1710
- "n",
1711
- "e",
1712
- "r",
1713
- "a",
1714
- "t",
1715
- "i",
1716
- "o",
1717
- "n",
1718
- "_",
1719
- "c",
1720
- "o",
1721
- "n",
1722
- "f",
1723
- "i",
1724
- "g",
1725
- ".",
1726
- "m",
1727
- "a",
1728
- "x",
1729
- "_",
1730
- "n",
1731
- "e",
1732
- "w",
1733
- "_",
1734
- "t",
1735
- "o",
1736
- "k",
1737
- "e",
1738
- "n",
1739
- "s",
1740
- " ",
1741
- ":",
1742
- " ",
1743
- "{",
1744
- "m",
1745
- "o",
1746
- "d",
1747
- "e",
1748
- "l",
1749
- ".",
1750
- "g",
1751
- "e",
1752
- "n",
1753
- "e",
1754
- "r",
1755
- "a",
1756
- "t",
1757
- "i",
1758
- "o",
1759
- "n",
1760
- "_",
1761
- "c",
1762
- "o",
1763
- "n",
1764
- "f",
1765
- "i",
1766
- "g",
1767
- ".",
1768
- "m",
1769
- "a",
1770
- "x",
1771
- "_",
1772
- "n",
1773
- "e",
1774
- "w",
1775
- "_",
1776
- "t",
1777
- "o",
1778
- "k",
1779
- "e",
1780
- "n",
1781
- "s",
1782
- "}",
1783
- "\"",
1784
- ")",
1785
- "\n",
1786
- "p",
1787
- "r",
1788
- "i",
1789
- "n",
1790
- "t",
1791
- "(",
1792
- "f",
1793
- "\"",
1794
- "P",
1795
- "r",
1796
- "o",
1797
- "m",
1798
- "p",
1799
- "t",
1800
- " ",
1801
- "t",
1802
- "o",
1803
- "k",
1804
- "e",
1805
- "n",
1806
- " ",
1807
- "l",
1808
- "e",
1809
- "n",
1810
- "g",
1811
- "t",
1812
- "h",
1813
- " ",
1814
- " ",
1815
- " ",
1816
- " ",
1817
- " ",
1818
- " ",
1819
- " ",
1820
- " ",
1821
- " ",
1822
- " ",
1823
- " ",
1824
- " ",
1825
- " ",
1826
- " ",
1827
- " ",
1828
- " ",
1829
- " ",
1830
- " ",
1831
- " ",
1832
- " ",
1833
- ":",
1834
- " ",
1835
- "{",
1836
- "p",
1837
- "r",
1838
- "o",
1839
- "m",
1840
- "p",
1841
- "t",
1842
- "_",
1843
- "l",
1844
- "e",
1845
- "n",
1846
- "}",
1847
- "\"",
1848
- ")",
1849
- "\n",
1850
- "\n",
1851
- "w",
1852
- "i",
1853
- "t",
1854
- "h",
1855
- " ",
1856
- "t",
1857
- "o",
1858
- "r",
1859
- "c",
1860
- "h",
1861
- ".",
1862
- "n",
1863
- "o",
1864
- "_",
1865
- "g",
1866
- "r",
1867
- "a",
1868
- "d",
1869
- "(",
1870
- ")",
1871
- ":",
1872
- "\n",
1873
- " ",
1874
- " ",
1875
- " ",
1876
- " ",
1877
- "o",
1878
- "u",
1879
- "t",
1880
- " ",
1881
- "=",
1882
- " ",
1883
- "m",
1884
- "o",
1885
- "d",
1886
- "e",
1887
- "l",
1888
- ".",
1889
- "g",
1890
- "e",
1891
- "n",
1892
- "e",
1893
- "r",
1894
- "a",
1895
- "t",
1896
- "e",
1897
- "(",
1898
- "\n",
1899
- " ",
1900
- " ",
1901
- " ",
1902
- " ",
1903
- " ",
1904
- " ",
1905
- " ",
1906
- " ",
1907
- "*",
1908
- "*",
1909
- "i",
1910
- "n",
1911
- "p",
1912
- "u",
1913
- "t",
1914
- "s",
1915
- ",",
1916
- "\n",
1917
- " ",
1918
- " ",
1919
- " ",
1920
- " ",
1921
- " ",
1922
- " ",
1923
- " ",
1924
- " ",
1925
- "m",
1926
- "a",
1927
- "x",
1928
- "_",
1929
- "n",
1930
- "e",
1931
- "w",
1932
- "_",
1933
- "t",
1934
- "o",
1935
- "k",
1936
- "e",
1937
- "n",
1938
- "s",
1939
- "=",
1940
- "1",
1941
- "0",
1942
- "2",
1943
- "4",
1944
- ",",
1945
- "\n",
1946
- " ",
1947
- " ",
1948
- " ",
1949
- " ",
1950
- " ",
1951
- " ",
1952
- " ",
1953
- " ",
1954
- "d",
1955
- "o",
1956
- "_",
1957
- "s",
1958
- "a",
1959
- "m",
1960
- "p",
1961
- "l",
1962
- "e",
1963
- "=",
1964
- "T",
1965
- "r",
1966
- "u",
1967
- "e",
1968
- ",",
1969
- "\n",
1970
- " ",
1971
- " ",
1972
- " ",
1973
- " ",
1974
- " ",
1975
- " ",
1976
- " ",
1977
- " ",
1978
- "t",
1979
- "e",
1980
- "m",
1981
- "p",
1982
- "e",
1983
- "r",
1984
- "a",
1985
- "t",
1986
- "u",
1987
- "r",
1988
- "e",
1989
- "=",
1990
- "0",
1991
- ".",
1992
- "9",
1993
- ",",
1994
- "\n",
1995
- " ",
1996
- " ",
1997
- " ",
1998
- " ",
1999
- ")",
2000
- "\n",
2001
- "\n",
2002
- "c",
2003
- "o",
2004
- "m",
2005
- "p",
2006
- "l",
2007
- "e",
2008
- "t",
2009
- "i",
2010
- "o",
2011
- "n",
2012
- "_",
2013
- "i",
2014
- "d",
2015
- "s",
2016
- " ",
2017
- "=",
2018
- " ",
2019
- "o",
2020
- "u",
2021
- "t",
2022
- "[",
2023
- "0",
2024
- "]",
2025
- "[",
2026
- "p",
2027
- "r",
2028
- "o",
2029
- "m",
2030
- "p",
2031
- "t",
2032
- "_",
2033
- "l",
2034
- "e",
2035
- "n",
2036
- ":",
2037
- "]",
2038
- "\n",
2039
- "c",
2040
- "o",
2041
- "m",
2042
- "p",
2043
- "l",
2044
- "e",
2045
- "t",
2046
- "i",
2047
- "o",
2048
- "n",
2049
- "_",
2050
- "t",
2051
- "e",
2052
- "x",
2053
- "t",
2054
- " ",
2055
- "=",
2056
- " ",
2057
- "t",
2058
- "o",
2059
- "k",
2060
- "e",
2061
- "n",
2062
- "i",
2063
- "z",
2064
- "e",
2065
- "r",
2066
- ".",
2067
- "d",
2068
- "e",
2069
- "c",
2070
- "o",
2071
- "d",
2072
- "e",
2073
- "(",
2074
- "c",
2075
- "o",
2076
- "m",
2077
- "p",
2078
- "l",
2079
- "e",
2080
- "t",
2081
- "i",
2082
- "o",
2083
- "n",
2084
- "_",
2085
- "i",
2086
- "d",
2087
- "s",
2088
- ",",
2089
- " ",
2090
- "s",
2091
- "k",
2092
- "i",
2093
- "p",
2094
- "_",
2095
- "s",
2096
- "p",
2097
- "e",
2098
- "c",
2099
- "i",
2100
- "a",
2101
- "l",
2102
- "_",
2103
- "t",
2104
- "o",
2105
- "k",
2106
- "e",
2107
- "n",
2108
- "s",
2109
- "=",
2110
- "F",
2111
- "a",
2112
- "l",
2113
- "s",
2114
- "e",
2115
- ")",
2116
- "\n",
2117
- "\n",
2118
- "_",
2119
- "R",
2120
- "O",
2121
- "U",
2122
- "N",
2123
- "D",
2124
- "_",
2125
- "P",
2126
- "R",
2127
- "E",
2128
- "F",
2129
- "I",
2130
- "X",
2131
- " ",
2132
- "=",
2133
- " ",
2134
- "r",
2135
- "e",
2136
- ".",
2137
- "c",
2138
- "o",
2139
- "m",
2140
- "p",
2141
- "i",
2142
- "l",
2143
- "e",
2144
- "(",
2145
- "r",
2146
- "\"",
2147
- "R",
2148
- "o",
2149
- "u",
2150
- "n",
2151
- "d",
2152
- "\\",
2153
- "s",
2154
- "+",
2155
- "\\",
2156
- "d",
2157
- "+",
2158
- "\\",
2159
- "s",
2160
- "*",
2161
- ":",
2162
- "\"",
2163
- ",",
2164
- " ",
2165
- "r",
2166
- "e",
2167
- ".",
2168
- "I",
2169
- "G",
2170
- "N",
2171
- "O",
2172
- "R",
2173
- "E",
2174
- "C",
2175
- "A",
2176
- "S",
2177
- "E",
2178
- ")",
2179
- "\n",
2180
- "n",
2181
- "_",
2182
- "t",
2183
- "o",
2184
- "k",
2185
- "e",
2186
- "n",
2187
- "s",
2188
- " ",
2189
- "=",
2190
- " ",
2191
- "l",
2192
- "e",
2193
- "n",
2194
- "(",
2195
- "c",
2196
- "o",
2197
- "m",
2198
- "p",
2199
- "l",
2200
- "e",
2201
- "t",
2202
- "i",
2203
- "o",
2204
- "n",
2205
- "_",
2206
- "i",
2207
- "d",
2208
- "s",
2209
- ")",
2210
- "\n",
2211
- "n",
2212
- "_",
2213
- "r",
2214
- "o",
2215
- "u",
2216
- "n",
2217
- "d",
2218
- "s",
2219
- " ",
2220
- "=",
2221
- " ",
2222
- "l",
2223
- "e",
2224
- "n",
2225
- "(",
2226
- "_",
2227
- "R",
2228
- "O",
2229
- "U",
2230
- "N",
2231
- "D",
2232
- "_",
2233
- "P",
2234
- "R",
2235
- "E",
2236
- "F",
2237
- "I",
2238
- "X",
2239
- ".",
2240
- "f",
2241
- "i",
2242
- "n",
2243
- "d",
2244
- "a",
2245
- "l",
2246
- "l",
2247
- "(",
2248
- "c",
2249
- "o",
2250
- "m",
2251
- "p",
2252
- "l",
2253
- "e",
2254
- "t",
2255
- "i",
2256
- "o",
2257
- "n",
2258
- "_",
2259
- "t",
2260
- "e",
2261
- "x",
2262
- "t",
2263
- ")",
2264
- ")",
2265
- "\n",
2266
- "h",
2267
- "a",
2268
- "s",
2269
- "_",
2270
- "e",
2271
- "o",
2272
- "s",
2273
- " ",
2274
- " ",
2275
- " ",
2276
- "=",
2277
- " ",
2278
- "a",
2279
- "n",
2280
- "y",
2281
- "(",
2282
- "t",
2283
- "o",
2284
- "k",
2285
- " ",
2286
- "i",
2287
- "n",
2288
- " ",
2289
- "c",
2290
- "o",
2291
- "m",
2292
- "p",
2293
- "l",
2294
- "e",
2295
- "t",
2296
- "i",
2297
- "o",
2298
- "n",
2299
- "_",
2300
- "t",
2301
- "e",
2302
- "x",
2303
- "t",
2304
- " ",
2305
- "f",
2306
- "o",
2307
- "r",
2308
- " ",
2309
- "t",
2310
- "o",
2311
- "k",
2312
- " ",
2313
- "i",
2314
- "n",
2315
- " ",
2316
- "[",
2317
- "\"",
2318
- "<",
2319
- "|",
2320
- "i",
2321
- "m",
2322
- "_",
2323
- "e",
2324
- "n",
2325
- "d",
2326
- "|",
2327
- ">",
2328
- "\"",
2329
- ",",
2330
- " ",
2331
- "\"",
2332
- "<",
2333
- "|",
2334
- "e",
2335
- "n",
2336
- "d",
2337
- "o",
2338
- "f",
2339
- "t",
2340
- "e",
2341
- "x",
2342
- "t",
2343
- "|",
2344
- ">",
2345
- "\"",
2346
- ",",
2347
- " ",
2348
- "\"",
2349
- "<",
2350
- "/",
2351
- "s",
2352
- ">",
2353
- "\"",
2354
- "]",
2355
- ")",
2356
- "\n",
2357
- "\n",
2358
- "p",
2359
- "r",
2360
- "i",
2361
- "n",
2362
- "t",
2363
- "(",
2364
- "f",
2365
- "\"",
2366
- "C",
2367
- "o",
2368
- "m",
2369
- "p",
2370
- "l",
2371
- "e",
2372
- "t",
2373
- "i",
2374
- "o",
2375
- "n",
2376
- " ",
2377
- "t",
2378
- "o",
2379
- "k",
2380
- "e",
2381
- "n",
2382
- "s",
2383
- " ",
2384
- ":",
2385
- " ",
2386
- "{",
2387
- "n",
2388
- "_",
2389
- "t",
2390
- "o",
2391
- "k",
2392
- "e",
2393
- "n",
2394
- "s",
2395
- "}",
2396
- " ",
2397
- " ",
2398
- "(",
2399
- "w",
2400
- "a",
2401
- "n",
2402
- "t",
2403
- " ",
2404
- "<",
2405
- " ",
2406
- "1",
2407
- "0",
2408
- "2",
2409
- "4",
2410
- ",",
2411
- " ",
2412
- "n",
2413
- "o",
2414
- "t",
2415
- " ",
2416
- "=",
2417
- "=",
2418
- " ",
2419
- "2",
2420
- "5",
2421
- "6",
2422
- ")",
2423
- "\"",
2424
- ")",
2425
- "\n",
2426
- "p",
2427
- "r",
2428
- "i",
2429
- "n",
2430
- "t",
2431
- "(",
2432
- "f",
2433
- "\"",
2434
- "R",
2435
- "o",
2436
- "u",
2437
- "n",
2438
- "d",
2439
- " ",
2440
- "N",
2441
- ":",
2442
- " ",
2443
- "c",
2444
- "o",
2445
- "u",
2446
- "n",
2447
- "t",
2448
- " ",
2449
- " ",
2450
- " ",
2451
- " ",
2452
- ":",
2453
- " ",
2454
- "{",
2455
- "n",
2456
- "_",
2457
- "r",
2458
- "o",
2459
- "u",
2460
- "n",
2461
- "d",
2462
- "s",
2463
- "}",
2464
- " ",
2465
- " ",
2466
- "(",
2467
- "w",
2468
- "a",
2469
- "n",
2470
- "t",
2471
- " ",
2472
- "5",
2473
- ")",
2474
- "\"",
2475
- ")",
2476
- "\n",
2477
- "p",
2478
- "r",
2479
- "i",
2480
- "n",
2481
- "t",
2482
- "(",
2483
- "f",
2484
- "\"",
2485
- "T",
2486
- "e",
2487
- "r",
2488
- "m",
2489
- "i",
2490
- "n",
2491
- "a",
2492
- "t",
2493
- "e",
2494
- "d",
2495
- " ",
2496
- "w",
2497
- "/",
2498
- " ",
2499
- "E",
2500
- "O",
2501
- "S",
2502
- " ",
2503
- ":",
2504
- " ",
2505
- "{",
2506
- "h",
2507
- "a",
2508
- "s",
2509
- "_",
2510
- "e",
2511
- "o",
2512
- "s",
2513
- "}",
2514
- " ",
2515
- " ",
2516
- "(",
2517
- "w",
2518
- "a",
2519
- "n",
2520
- "t",
2521
- " ",
2522
- "T",
2523
- "r",
2524
- "u",
2525
- "e",
2526
- ")",
2527
- "\"",
2528
- ")",
2529
- "\n",
2530
- "p",
2531
- "r",
2532
- "i",
2533
- "n",
2534
- "t",
2535
- "(",
2536
- ")",
2537
  "\n",
2538
- "p",
2539
- "r",
2540
- "i",
2541
- "n",
2542
- "t",
2543
- "(",
2544
- "\"",
2545
- "-",
2546
- "-",
2547
- "-",
2548
- " ",
2549
- "F",
2550
- "U",
2551
- "L",
2552
- "L",
2553
- " ",
2554
- "C",
2555
- "O",
2556
- "M",
2557
- "P",
2558
- "L",
2559
- "E",
2560
- "T",
2561
- "I",
2562
- "O",
2563
- "N",
2564
- " ",
2565
- "-",
2566
- "-",
2567
- "-",
2568
- "\"",
2569
- ")",
2570
  "\n",
2571
- "p",
2572
- "r",
2573
- "i",
2574
- "n",
2575
- "t",
2576
- "(",
2577
- "c",
2578
- "o",
2579
- "m",
2580
- "p",
2581
- "l",
2582
- "e",
2583
- "t",
2584
- "i",
2585
- "o",
2586
- "n",
2587
- "_",
2588
- "t",
2589
- "e",
2590
- "x",
2591
- "t",
2592
- ")"
2593
  ]
2594
  },
2595
  {
2596
  "cell_type": "markdown",
2597
- "id": "049335ee",
2598
  "metadata": {},
2599
  "source": [
2600
- "## Cell 9 \u2014 GRPO Training (~300 steps, ~1.5 h)\n",
2601
- "\n",
2602
- "**Reward functions:**\n",
2603
- "| Function | Signal | Max |\n",
2604
- "|---|---|---|\n",
2605
- "| `reward_survival` | Survived the vote | +0.50 |\n",
2606
- "| `reward_deception_effectiveness` | Survived with low combined suspicion | +0.30 |\n",
2607
- "| `reward_strategic_choice` | Used questions / project refs / third-party framing | +0.05 |\n",
2608
- "| `reward_tom_judge` | GPT-4o-mini ToM score \u00d7 multiplier | +0.20 |\n",
2609
- "| `reward_anti_hack` | Penalises silence / repetition / Level-0 phrases | \u22120.25 |\n",
2610
  "\n",
2611
  "**What to watch in W&B:**\n",
2612
  "- `reward_survival` climbing is the primary health signal\n",
2613
- "- `reward_tom_judge` separates from baseline after ~200 steps\n",
2614
- "- `reward_anti_hack` should trend toward 0 as the model stops denying\n",
2615
- "- `policy_loss` should decrease steadily"
2616
- ]
2617
- },
2618
- {
2619
- "cell_type": "markdown",
2620
- "id": "bce36bf2",
2621
- "metadata": {},
2622
- "source": [
2623
- "### Resuming an interrupted run\n",
2624
- "\n",
2625
- "If you stopped training and pulled a fix, **skip Cells 7 and 8** (model is still in memory) and re-run this cell directly. \n",
2626
- "The cell reloads `reward_combined` from disk so the pulled changes take effect without a kernel restart, then auto-detects the latest checkpoint and passes it to `trainer.train()`."
2627
  ]
2628
  },
2629
  {
2630
  "cell_type": "code",
2631
  "execution_count": null,
2632
- "id": "8c60e209",
2633
  "metadata": {},
2634
  "outputs": [],
2635
  "source": [
2636
- "i",
2637
- "m",
2638
- "p",
2639
- "o",
2640
- "r",
2641
- "t",
2642
- " ",
2643
- "o",
2644
- "s",
2645
- "\n",
2646
- "i",
2647
- "m",
2648
- "p",
2649
- "o",
2650
- "r",
2651
- "t",
2652
- " ",
2653
- "g",
2654
- "l",
2655
- "o",
2656
- "b",
2657
- "\n",
2658
- "i",
2659
- "m",
2660
- "p",
2661
- "o",
2662
- "r",
2663
- "t",
2664
- " ",
2665
- "i",
2666
- "m",
2667
- "p",
2668
- "o",
2669
- "r",
2670
- "t",
2671
- "l",
2672
- "i",
2673
- "b",
2674
- "\n",
2675
- "\n",
2676
- "#",
2677
- " ",
2678
- "R",
2679
- "e",
2680
- "l",
2681
- "o",
2682
- "a",
2683
- "d",
2684
- " ",
2685
- "r",
2686
- "e",
2687
- "w",
2688
- "a",
2689
- "r",
2690
- "d",
2691
- "_",
2692
- "c",
2693
- "o",
2694
- "m",
2695
- "b",
2696
- "i",
2697
- "n",
2698
- "e",
2699
- "d",
2700
- " ",
2701
- "s",
2702
- "o",
2703
- " ",
2704
- "a",
2705
- "n",
2706
- "y",
2707
- " ",
2708
- "c",
2709
- "h",
2710
- "a",
2711
- "n",
2712
- "g",
2713
- "e",
2714
- "s",
2715
- " ",
2716
- "p",
2717
- "u",
2718
- "l",
2719
- "l",
2720
- "e",
2721
- "d",
2722
- " ",
2723
- "v",
2724
- "i",
2725
- "a",
2726
- " ",
2727
- "g",
2728
- "i",
2729
- "t",
2730
- " ",
2731
- "t",
2732
- "a",
2733
- "k",
2734
- "e",
2735
- " ",
2736
- "e",
2737
- "f",
2738
- "f",
2739
- "e",
2740
- "c",
2741
- "t",
2742
- "\n",
2743
- "#",
2744
- " ",
2745
- "w",
2746
- "i",
2747
- "t",
2748
- "h",
2749
- "o",
2750
- "u",
2751
- "t",
2752
- " ",
2753
- "r",
2754
- "e",
2755
- "q",
2756
- "u",
2757
- "i",
2758
- "r",
2759
- "i",
2760
- "n",
2761
- "g",
2762
- " ",
2763
- "a",
2764
- " ",
2765
- "f",
2766
- "u",
2767
- "l",
2768
- "l",
2769
- " ",
2770
- "k",
2771
- "e",
2772
- "r",
2773
- "n",
2774
- "e",
2775
- "l",
2776
- " ",
2777
- "r",
2778
- "e",
2779
- "s",
2780
- "t",
2781
- "a",
2782
- "r",
2783
- "t",
2784
- ".",
2785
- "\n",
2786
- "i",
2787
- "m",
2788
- "p",
2789
- "o",
2790
- "r",
2791
- "t",
2792
- " ",
2793
- "t",
2794
- "r",
2795
- "a",
2796
- "i",
2797
- "n",
2798
- "i",
2799
- "n",
2800
- "g",
2801
- ".",
2802
- "r",
2803
- "e",
2804
- "w",
2805
- "a",
2806
- "r",
2807
- "d",
2808
- "_",
2809
- "c",
2810
- "o",
2811
- "m",
2812
- "b",
2813
- "i",
2814
- "n",
2815
- "e",
2816
- "d",
2817
- " ",
2818
- "a",
2819
- "s",
2820
- " ",
2821
- "_",
2822
- "r",
2823
- "c",
2824
- "_",
2825
- "m",
2826
- "o",
2827
- "d",
2828
- "\n",
2829
- "i",
2830
- "m",
2831
- "p",
2832
- "o",
2833
- "r",
2834
- "t",
2835
- "l",
2836
- "i",
2837
- "b",
2838
- ".",
2839
- "r",
2840
- "e",
2841
- "l",
2842
- "o",
2843
- "a",
2844
- "d",
2845
- "(",
2846
- "_",
2847
- "r",
2848
- "c",
2849
- "_",
2850
- "m",
2851
- "o",
2852
- "d",
2853
- ")",
2854
- "\n",
2855
- "i",
2856
- "m",
2857
- "p",
2858
- "o",
2859
- "r",
2860
- "t",
2861
- " ",
2862
- "t",
2863
- "r",
2864
- "a",
2865
- "i",
2866
- "n",
2867
- "i",
2868
- "n",
2869
- "g",
2870
- ".",
2871
- "p",
2872
- "r",
2873
- "o",
2874
- "m",
2875
- "p",
2876
- "t",
2877
- "s",
2878
- " ",
2879
- "a",
2880
- "s",
2881
- " ",
2882
- "_",
2883
- "p",
2884
- "r",
2885
- "o",
2886
- "m",
2887
- "p",
2888
- "t",
2889
- "s",
2890
- "_",
2891
- "m",
2892
- "o",
2893
- "d",
2894
- "\n",
2895
- "i",
2896
- "m",
2897
- "p",
2898
- "o",
2899
- "r",
2900
- "t",
2901
- "l",
2902
- "i",
2903
- "b",
2904
- ".",
2905
- "r",
2906
- "e",
2907
- "l",
2908
- "o",
2909
- "a",
2910
- "d",
2911
- "(",
2912
- "_",
2913
- "p",
2914
- "r",
2915
- "o",
2916
- "m",
2917
- "p",
2918
- "t",
2919
- "s",
2920
- "_",
2921
- "m",
2922
- "o",
2923
- "d",
2924
- ")",
2925
- "\n",
2926
- "\n",
2927
- "f",
2928
- "r",
2929
- "o",
2930
- "m",
2931
- " ",
2932
- "d",
2933
- "a",
2934
- "t",
2935
- "a",
2936
- "s",
2937
- "e",
2938
- "t",
2939
- "s",
2940
- " ",
2941
- "i",
2942
- "m",
2943
- "p",
2944
- "o",
2945
- "r",
2946
- "t",
2947
- " ",
2948
- "D",
2949
- "a",
2950
- "t",
2951
- "a",
2952
- "s",
2953
- "e",
2954
- "t",
2955
- "\n",
2956
- "f",
2957
- "r",
2958
- "o",
2959
- "m",
2960
- " ",
2961
- "t",
2962
- "r",
2963
- "l",
2964
- " ",
2965
- "i",
2966
- "m",
2967
- "p",
2968
- "o",
2969
- "r",
2970
- "t",
2971
- " ",
2972
- "G",
2973
- "R",
2974
- "P",
2975
- "O",
2976
- "C",
2977
- "o",
2978
- "n",
2979
- "f",
2980
- "i",
2981
- "g",
2982
- ",",
2983
- " ",
2984
- "G",
2985
- "R",
2986
- "P",
2987
- "O",
2988
- "T",
2989
- "r",
2990
- "a",
2991
- "i",
2992
- "n",
2993
- "e",
2994
- "r",
2995
- "\n",
2996
- "f",
2997
- "r",
2998
- "o",
2999
- "m",
3000
- " ",
3001
- "t",
3002
- "r",
3003
- "a",
3004
- "n",
3005
- "s",
3006
- "f",
3007
- "o",
3008
- "r",
3009
- "m",
3010
- "e",
3011
- "r",
3012
- "s",
3013
- " ",
3014
- "i",
3015
- "m",
3016
- "p",
3017
- "o",
3018
- "r",
3019
- "t",
3020
- " ",
3021
- "T",
3022
- "r",
3023
- "a",
3024
- "i",
3025
- "n",
3026
- "e",
3027
- "r",
3028
- "C",
3029
- "a",
3030
- "l",
3031
- "l",
3032
- "b",
3033
- "a",
3034
- "c",
3035
- "k",
3036
- "\n",
3037
- "\n",
3038
- "f",
3039
- "r",
3040
- "o",
3041
- "m",
3042
- " ",
3043
- "t",
3044
- "r",
3045
- "a",
3046
- "i",
3047
- "n",
3048
- "i",
3049
- "n",
3050
- "g",
3051
- ".",
3052
- "r",
3053
- "e",
3054
- "w",
3055
- "a",
3056
- "r",
3057
- "d",
3058
- "_",
3059
- "c",
3060
- "o",
3061
- "m",
3062
- "b",
3063
- "i",
3064
- "n",
3065
- "e",
3066
- "d",
3067
- " ",
3068
- "i",
3069
- "m",
3070
- "p",
3071
- "o",
3072
- "r",
3073
- "t",
3074
- " ",
3075
- "(",
3076
- "\n",
3077
- " ",
3078
- " ",
3079
- " ",
3080
- " ",
3081
- "c",
3082
- "l",
3083
- "e",
3084
- "a",
3085
- "r",
3086
- "_",
3087
- "c",
3088
- "a",
3089
- "c",
3090
- "h",
3091
- "e",
3092
- ",",
3093
- "\n",
3094
- " ",
3095
- " ",
3096
- " ",
3097
- " ",
3098
- "r",
3099
- "e",
3100
- "w",
3101
- "a",
3102
- "r",
3103
- "d",
3104
- "_",
3105
- "f",
3106
- "o",
3107
- "r",
3108
- "m",
3109
- "a",
3110
- "t",
3111
- ",",
3112
- "\n",
3113
- " ",
3114
- " ",
3115
- " ",
3116
- " ",
3117
- "r",
3118
- "e",
3119
- "w",
3120
- "a",
3121
- "r",
3122
- "d",
3123
- "_",
3124
- "s",
3125
- "u",
3126
- "r",
3127
- "v",
3128
- "i",
3129
- "v",
3130
- "a",
3131
- "l",
3132
- ",",
3133
- "\n",
3134
- " ",
3135
- " ",
3136
- " ",
3137
- " ",
3138
- "r",
3139
- "e",
3140
- "w",
3141
- "a",
3142
- "r",
3143
- "d",
3144
- "_",
3145
- "d",
3146
- "e",
3147
- "c",
3148
- "e",
3149
- "p",
3150
- "t",
3151
- "i",
3152
- "o",
3153
- "n",
3154
- "_",
3155
- "e",
3156
- "f",
3157
- "f",
3158
- "e",
3159
- "c",
3160
- "t",
3161
- "i",
3162
- "v",
3163
- "e",
3164
- "n",
3165
- "e",
3166
- "s",
3167
- "s",
3168
- ",",
3169
- "\n",
3170
- " ",
3171
- " ",
3172
- " ",
3173
- " ",
3174
- "r",
3175
- "e",
3176
- "w",
3177
- "a",
3178
- "r",
3179
- "d",
3180
- "_",
3181
- "s",
3182
- "t",
3183
- "r",
3184
- "a",
3185
- "t",
3186
- "e",
3187
- "g",
3188
- "i",
3189
- "c",
3190
- "_",
3191
- "c",
3192
- "h",
3193
- "o",
3194
- "i",
3195
- "c",
3196
- "e",
3197
- ",",
3198
- "\n",
3199
- " ",
3200
- " ",
3201
- " ",
3202
- " ",
3203
- "r",
3204
- "e",
3205
- "w",
3206
- "a",
3207
- "r",
3208
- "d",
3209
- "_",
3210
- "t",
3211
- "o",
3212
- "m",
3213
- "_",
3214
- "j",
3215
- "u",
3216
- "d",
3217
- "g",
3218
- "e",
3219
- ",",
3220
- "\n",
3221
- " ",
3222
- " ",
3223
- " ",
3224
- " ",
3225
- "r",
3226
- "e",
3227
- "w",
3228
- "a",
3229
- "r",
3230
- "d",
3231
- "_",
3232
- "a",
3233
- "n",
3234
- "t",
3235
- "i",
3236
- "_",
3237
- "h",
3238
- "a",
3239
- "c",
3240
- "k",
3241
- ",",
3242
- "\n",
3243
- ")",
3244
- "\n",
3245
- "f",
3246
- "r",
3247
- "o",
3248
- "m",
3249
- " ",
3250
- "t",
3251
- "r",
3252
- "a",
3253
- "i",
3254
- "n",
3255
- "i",
3256
- "n",
3257
- "g",
3258
- ".",
3259
- "t",
3260
- "r",
3261
- "a",
3262
- "i",
3263
- "n",
3264
- " ",
3265
- "i",
3266
- "m",
3267
- "p",
3268
- "o",
3269
- "r",
3270
- "t",
3271
- " ",
3272
- "G",
3273
- "e",
3274
- "n",
3275
- "e",
3276
- "r",
3277
- "a",
3278
- "t",
3279
- "i",
3280
- "o",
3281
- "n",
3282
- "L",
3283
- "o",
3284
- "g",
3285
- "C",
3286
- "a",
3287
- "l",
3288
- "l",
3289
- "b",
3290
- "a",
3291
- "c",
3292
- "k",
3293
- ",",
3294
- " ",
3295
- "b",
3296
- "u",
3297
- "i",
3298
- "l",
3299
- "d",
3300
- "_",
3301
- "d",
3302
- "a",
3303
- "t",
3304
- "a",
3305
- "s",
3306
- "e",
3307
- "t",
3308
- "\n",
3309
- "\n",
3310
- "#",
3311
- " ",
3312
- "A",
3313
- "u",
3314
- "t",
3315
- "o",
3316
- "-",
3317
- "d",
3318
- "e",
3319
- "t",
3320
- "e",
3321
- "c",
3322
- "t",
3323
- " ",
3324
- "l",
3325
- "a",
3326
- "t",
3327
- "e",
3328
- "s",
3329
- "t",
3330
- " ",
3331
- "c",
3332
- "h",
3333
- "e",
3334
- "c",
3335
- "k",
3336
- "p",
3337
- "o",
3338
- "i",
3339
- "n",
3340
- "t",
3341
- " ",
3342
- "s",
3343
- "o",
3344
- " ",
3345
- "i",
3346
- "n",
3347
- "t",
3348
- "e",
3349
- "r",
3350
- "r",
3351
- "u",
3352
- "p",
3353
- "t",
3354
- "e",
3355
- "d",
3356
- " ",
3357
- "r",
3358
- "u",
3359
- "n",
3360
- "s",
3361
- " ",
3362
- "r",
3363
- "e",
3364
- "s",
3365
- "u",
3366
- "m",
3367
- "e",
3368
- " ",
3369
- "s",
3370
- "e",
3371
- "a",
3372
- "m",
3373
- "l",
3374
- "e",
3375
- "s",
3376
- "s",
3377
- "l",
3378
- "y",
3379
- ".",
3380
- "\n",
3381
- "_",
3382
- "o",
3383
- "u",
3384
- "t",
3385
- "p",
3386
- "u",
3387
- "t",
3388
- "_",
3389
- "d",
3390
- "i",
3391
- "r",
3392
- " ",
3393
- "=",
3394
- " ",
3395
- "\"",
3396
- "/",
3397
- "c",
3398
- "o",
3399
- "n",
3400
- "t",
3401
- "e",
3402
- "n",
3403
- "t",
3404
- "/",
3405
- "m",
3406
- "i",
3407
- "n",
3408
- "d",
3409
- "f",
3410
- "l",
3411
- "a",
3412
- "y",
3413
- "e",
3414
- "r",
3415
- "-",
3416
- "g",
3417
- "r",
3418
- "p",
3419
- "o",
3420
- "-",
3421
- "o",
3422
- "u",
3423
- "t",
3424
- "p",
3425
- "u",
3426
- "t",
3427
- "\"",
3428
- "\n",
3429
- "_",
3430
- "c",
3431
- "h",
3432
- "e",
3433
- "c",
3434
- "k",
3435
- "p",
3436
- "o",
3437
- "i",
3438
- "n",
3439
- "t",
3440
- "s",
3441
- " ",
3442
- "=",
3443
- " ",
3444
- "s",
3445
- "o",
3446
- "r",
3447
- "t",
3448
- "e",
3449
- "d",
3450
- "(",
3451
- "g",
3452
- "l",
3453
- "o",
3454
- "b",
3455
- ".",
3456
- "g",
3457
- "l",
3458
- "o",
3459
- "b",
3460
- "(",
3461
- "o",
3462
- "s",
3463
- ".",
3464
- "p",
3465
- "a",
3466
- "t",
3467
- "h",
3468
- ".",
3469
- "j",
3470
- "o",
3471
- "i",
3472
- "n",
3473
- "(",
3474
- "_",
3475
- "o",
3476
- "u",
3477
- "t",
3478
- "p",
3479
- "u",
3480
- "t",
3481
- "_",
3482
- "d",
3483
- "i",
3484
- "r",
3485
- ",",
3486
- " ",
3487
- "\"",
3488
- "c",
3489
- "h",
3490
- "e",
3491
- "c",
3492
- "k",
3493
- "p",
3494
- "o",
3495
- "i",
3496
- "n",
3497
- "t",
3498
- "-",
3499
- "*",
3500
- "\"",
3501
- ")",
3502
- ")",
3503
- ")",
3504
- "\n",
3505
- "r",
3506
- "e",
3507
- "s",
3508
- "u",
3509
- "m",
3510
- "e",
3511
- "_",
3512
- "c",
3513
- "h",
3514
- "e",
3515
- "c",
3516
- "k",
3517
- "p",
3518
- "o",
3519
- "i",
3520
- "n",
3521
- "t",
3522
- " ",
3523
- "=",
3524
- " ",
3525
- "_",
3526
- "c",
3527
- "h",
3528
- "e",
3529
- "c",
3530
- "k",
3531
- "p",
3532
- "o",
3533
- "i",
3534
- "n",
3535
- "t",
3536
- "s",
3537
- "[",
3538
- "-",
3539
- "1",
3540
- "]",
3541
- " ",
3542
- "i",
3543
- "f",
3544
- " ",
3545
- "_",
3546
- "c",
3547
- "h",
3548
- "e",
3549
- "c",
3550
- "k",
3551
- "p",
3552
- "o",
3553
- "i",
3554
- "n",
3555
- "t",
3556
- "s",
3557
- " ",
3558
- "e",
3559
- "l",
3560
- "s",
3561
- "e",
3562
- " ",
3563
- "N",
3564
- "o",
3565
- "n",
3566
- "e",
3567
- "\n",
3568
- "\n",
3569
- "i",
3570
- "f",
3571
- " ",
3572
- "r",
3573
- "e",
3574
- "s",
3575
- "u",
3576
- "m",
3577
- "e",
3578
- "_",
3579
- "c",
3580
- "h",
3581
- "e",
3582
- "c",
3583
- "k",
3584
- "p",
3585
- "o",
3586
- "i",
3587
- "n",
3588
- "t",
3589
- ":",
3590
- "\n",
3591
- " ",
3592
- " ",
3593
- " ",
3594
- " ",
3595
- "p",
3596
- "r",
3597
- "i",
3598
- "n",
3599
- "t",
3600
- "(",
3601
- "f",
3602
- "\"",
3603
- "R",
3604
- "e",
3605
- "s",
3606
- "u",
3607
- "m",
3608
- "i",
3609
- "n",
3610
- "g",
3611
- " ",
3612
- "f",
3613
- "r",
3614
- "o",
3615
- "m",
3616
- " ",
3617
- "c",
3618
- "h",
3619
- "e",
3620
- "c",
3621
- "k",
3622
- "p",
3623
- "o",
3624
- "i",
3625
- "n",
3626
- "t",
3627
- ":",
3628
- " ",
3629
- "{",
3630
- "r",
3631
- "e",
3632
- "s",
3633
- "u",
3634
- "m",
3635
- "e",
3636
- "_",
3637
- "c",
3638
- "h",
3639
- "e",
3640
- "c",
3641
- "k",
3642
- "p",
3643
- "o",
3644
- "i",
3645
- "n",
3646
- "t",
3647
- "}",
3648
- "\"",
3649
- ")",
3650
- "\n",
3651
- "e",
3652
- "l",
3653
- "s",
3654
- "e",
3655
- ":",
3656
- "\n",
3657
- " ",
3658
- " ",
3659
- " ",
3660
- " ",
3661
- "p",
3662
- "r",
3663
- "i",
3664
- "n",
3665
- "t",
3666
- "(",
3667
- "\"",
3668
- "N",
3669
- "o",
3670
- " ",
3671
- "c",
3672
- "h",
3673
- "e",
3674
- "c",
3675
- "k",
3676
- "p",
3677
- "o",
3678
- "i",
3679
- "n",
3680
- "t",
3681
- " ",
3682
- "f",
3683
- "o",
3684
- "u",
3685
- "n",
3686
- "d",
3687
- " ",
3688
- "\u2014",
3689
- " ",
3690
- "s",
3691
- "t",
3692
- "a",
3693
- "r",
3694
- "t",
3695
- "i",
3696
- "n",
3697
- "g",
3698
- " ",
3699
- "f",
3700
- "r",
3701
- "e",
3702
- "s",
3703
- "h",
3704
- "\"",
3705
- ")",
3706
- "\n",
3707
- "\n",
3708
- "d",
3709
- "a",
3710
- "t",
3711
- "a",
3712
- "s",
3713
- "e",
3714
- "t",
3715
- " ",
3716
- "=",
3717
- " ",
3718
- "b",
3719
- "u",
3720
- "i",
3721
- "l",
3722
- "d",
3723
- "_",
3724
- "d",
3725
- "a",
3726
- "t",
3727
- "a",
3728
- "s",
3729
- "e",
3730
- "t",
3731
- "(",
3732
- "t",
3733
- "o",
3734
- "k",
3735
- "e",
3736
- "n",
3737
- "i",
3738
- "z",
3739
- "e",
3740
- "r",
3741
- ")",
3742
- "\n",
3743
- "\n",
3744
- "g",
3745
- "r",
3746
- "p",
3747
- "o",
3748
- "_",
3749
- "c",
3750
- "o",
3751
- "n",
3752
- "f",
3753
- "i",
3754
- "g",
3755
- " ",
3756
- "=",
3757
- " ",
3758
- "G",
3759
- "R",
3760
- "P",
3761
- "O",
3762
- "C",
3763
- "o",
3764
- "n",
3765
- "f",
3766
- "i",
3767
- "g",
3768
- "(",
3769
- "\n",
3770
- " ",
3771
- " ",
3772
- " ",
3773
- " ",
3774
- "n",
3775
- "u",
3776
- "m",
3777
- "_",
3778
- "t",
3779
- "r",
3780
- "a",
3781
- "i",
3782
- "n",
3783
- "_",
3784
- "e",
3785
- "p",
3786
- "o",
3787
- "c",
3788
- "h",
3789
- "s",
3790
- "=",
3791
- "2",
3792
- ",",
3793
- " ",
3794
- " ",
3795
- "#",
3796
- " ",
3797
- "N",
3798
- " ",
3799
- "s",
3800
- "c",
3801
- "e",
3802
- "n",
3803
- "a",
3804
- "r",
3805
- "i",
3806
- "o",
3807
- "s",
3808
- " ",
3809
- "\u00d7",
3810
- " ",
3811
- "3",
3812
- " ",
3813
- "r",
3814
- "o",
3815
- "w",
3816
- "s",
3817
- " ",
3818
- "\u00d7",
3819
- " ",
3820
- "2",
3821
- " ",
3822
- "e",
3823
- "p",
3824
- "o",
3825
- "c",
3826
- "h",
3827
- "s",
3828
- " ",
3829
- "/",
3830
- " ",
3831
- "b",
3832
- "a",
3833
- "t",
3834
- "c",
3835
- "h",
3836
- "_",
3837
- "s",
3838
- "i",
3839
- "z",
3840
- "e",
3841
- " ",
3842
- "4",
3843
- " ",
3844
- "\u2248",
3845
- " ",
3846
- "3",
3847
- "0",
3848
- "0",
3849
- " ",
3850
- "s",
3851
- "t",
3852
- "e",
3853
- "p",
3854
- "s",
3855
- "\n",
3856
- " ",
3857
- " ",
3858
- " ",
3859
- " ",
3860
- "p",
3861
- "e",
3862
- "r",
3863
- "_",
3864
- "d",
3865
- "e",
3866
- "v",
3867
- "i",
3868
- "c",
3869
- "e",
3870
- "_",
3871
- "t",
3872
- "r",
3873
- "a",
3874
- "i",
3875
- "n",
3876
- "_",
3877
- "b",
3878
- "a",
3879
- "t",
3880
- "c",
3881
- "h",
3882
- "_",
3883
- "s",
3884
- "i",
3885
- "z",
3886
- "e",
3887
- "=",
3888
- "4",
3889
- ",",
3890
- "\n",
3891
- " ",
3892
- " ",
3893
- " ",
3894
- " ",
3895
- "g",
3896
- "r",
3897
- "a",
3898
- "d",
3899
- "i",
3900
- "e",
3901
- "n",
3902
- "t",
3903
- "_",
3904
- "a",
3905
- "c",
3906
- "c",
3907
- "u",
3908
- "m",
3909
- "u",
3910
- "l",
3911
- "a",
3912
- "t",
3913
- "i",
3914
- "o",
3915
- "n",
3916
- "_",
3917
- "s",
3918
- "t",
3919
- "e",
3920
- "p",
3921
- "s",
3922
- "=",
3923
- "4",
3924
- ",",
3925
- "\n",
3926
- " ",
3927
- " ",
3928
- " ",
3929
- " ",
3930
- "l",
3931
- "e",
3932
- "a",
3933
- "r",
3934
- "n",
3935
- "i",
3936
- "n",
3937
- "g",
3938
- "_",
3939
- "r",
3940
- "a",
3941
- "t",
3942
- "e",
3943
- "=",
3944
- "5",
3945
- "e",
3946
- "-",
3947
- "6",
3948
- ",",
3949
- "\n",
3950
- " ",
3951
- " ",
3952
- " ",
3953
- " ",
3954
- "m",
3955
- "a",
3956
- "x",
3957
- "_",
3958
- "p",
3959
- "r",
3960
- "o",
3961
- "m",
3962
- "p",
3963
- "t",
3964
- "_",
3965
- "l",
3966
- "e",
3967
- "n",
3968
- "g",
3969
- "t",
3970
- "h",
3971
- "=",
3972
- "7",
3973
- "6",
3974
- "8",
3975
- ",",
3976
- "\n",
3977
- " ",
3978
- " ",
3979
- " ",
3980
- " ",
3981
- "m",
3982
- "a",
3983
- "x",
3984
- "_",
3985
- "c",
3986
- "o",
3987
- "m",
3988
- "p",
3989
- "l",
3990
- "e",
3991
- "t",
3992
- "i",
3993
- "o",
3994
- "n",
3995
- "_",
3996
- "l",
3997
- "e",
3998
- "n",
3999
- "g",
4000
- "t",
4001
- "h",
4002
- "=",
4003
- "1",
4004
- "0",
4005
- "2",
4006
- "4",
4007
- ",",
4008
- " ",
4009
- " ",
4010
- " ",
4011
- " ",
4012
- " ",
4013
- " ",
4014
- "#",
4015
- " ",
4016
- "5",
4017
- " ",
4018
- "r",
4019
- "o",
4020
- "u",
4021
- "n",
4022
- "d",
4023
- "s",
4024
- " ",
4025
- "\u00d7",
4026
- " ",
4027
- "~",
4028
- "1",
4029
- "5",
4030
- "0",
4031
- " ",
4032
- "t",
4033
- "o",
4034
- "k",
4035
- "e",
4036
- "n",
4037
- "s",
4038
- " ",
4039
- "e",
4040
- "a",
4041
- "c",
4042
- "h",
4043
- " ",
4044
- "+",
4045
- " ",
4046
- "s",
4047
- "e",
4048
- "p",
4049
- "a",
4050
- "r",
4051
- "a",
4052
- "t",
4053
- "o",
4054
- "r",
4055
- "s",
4056
- "\n",
4057
- " ",
4058
- " ",
4059
- " ",
4060
- " ",
4061
- "n",
4062
- "u",
4063
- "m",
4064
- "_",
4065
- "g",
4066
- "e",
4067
- "n",
4068
- "e",
4069
- "r",
4070
- "a",
4071
- "t",
4072
- "i",
4073
- "o",
4074
- "n",
4075
- "s",
4076
- "=",
4077
- "4",
4078
- ",",
4079
- "\n",
4080
- " ",
4081
- " ",
4082
- " ",
4083
- " ",
4084
- "t",
4085
- "e",
4086
- "m",
4087
- "p",
4088
- "e",
4089
- "r",
4090
- "a",
4091
- "t",
4092
- "u",
4093
- "r",
4094
- "e",
4095
- "=",
4096
- "0",
4097
- ".",
4098
- "9",
4099
- ",",
4100
- "\n",
4101
- " ",
4102
- " ",
4103
- " ",
4104
- " ",
4105
- "u",
4106
- "s",
4107
- "e",
4108
- "_",
4109
- "v",
4110
- "l",
4111
- "l",
4112
- "m",
4113
- "=",
4114
- "F",
4115
- "a",
4116
- "l",
4117
- "s",
4118
- "e",
4119
- ",",
4120
- "\n",
4121
- " ",
4122
- " ",
4123
- " ",
4124
- " ",
4125
- "o",
4126
- "u",
4127
- "t",
4128
- "p",
4129
- "u",
4130
- "t",
4131
- "_",
4132
- "d",
4133
- "i",
4134
- "r",
4135
- "=",
4136
- "_",
4137
- "o",
4138
- "u",
4139
- "t",
4140
- "p",
4141
- "u",
4142
- "t",
4143
- "_",
4144
- "d",
4145
- "i",
4146
- "r",
4147
- ",",
4148
- "\n",
4149
- " ",
4150
- " ",
4151
- " ",
4152
- " ",
4153
- "l",
4154
- "o",
4155
- "g",
4156
- "g",
4157
- "i",
4158
- "n",
4159
- "g",
4160
- "_",
4161
- "s",
4162
- "t",
4163
- "e",
4164
- "p",
4165
- "s",
4166
- "=",
4167
- "1",
4168
- "0",
4169
- ",",
4170
- "\n",
4171
- " ",
4172
- " ",
4173
- " ",
4174
- " ",
4175
- "s",
4176
- "a",
4177
- "v",
4178
- "e",
4179
- "_",
4180
- "s",
4181
- "t",
4182
- "e",
4183
- "p",
4184
- "s",
4185
- "=",
4186
- "2",
4187
- "5",
4188
- ",",
4189
- "\n",
4190
- " ",
4191
- " ",
4192
- " ",
4193
- " ",
4194
- "s",
4195
- "a",
4196
- "v",
4197
- "e",
4198
- "_",
4199
- "t",
4200
- "o",
4201
- "t",
4202
- "a",
4203
- "l",
4204
- "_",
4205
- "l",
4206
- "i",
4207
- "m",
4208
- "i",
4209
- "t",
4210
- "=",
4211
- "2",
4212
- ",",
4213
- "\n",
4214
- " ",
4215
- " ",
4216
- " ",
4217
- " ",
4218
- "l",
4219
- "o",
4220
- "g",
4221
- "_",
4222
- "c",
4223
- "o",
4224
- "m",
4225
- "p",
4226
- "l",
4227
- "e",
4228
- "t",
4229
- "i",
4230
- "o",
4231
- "n",
4232
- "s",
4233
- "=",
4234
- "T",
4235
- "r",
4236
- "u",
4237
- "e",
4238
- ",",
4239
- "\n",
4240
- " ",
4241
- " ",
4242
- " ",
4243
- " ",
4244
- "n",
4245
- "u",
4246
- "m",
4247
- "_",
4248
- "c",
4249
- "o",
4250
- "m",
4251
- "p",
4252
- "l",
4253
- "e",
4254
- "t",
4255
- "i",
4256
- "o",
4257
- "n",
4258
- "s",
4259
- "_",
4260
- "t",
4261
- "o",
4262
- "_",
4263
- "p",
4264
- "r",
4265
- "i",
4266
- "n",
4267
- "t",
4268
- "=",
4269
- "2",
4270
- ",",
4271
- "\n",
4272
- " ",
4273
- " ",
4274
- " ",
4275
- " ",
4276
- "r",
4277
- "e",
4278
- "p",
4279
- "o",
4280
- "r",
4281
- "t",
4282
- "_",
4283
- "t",
4284
- "o",
4285
- "=",
4286
- "\"",
4287
- "w",
4288
- "a",
4289
- "n",
4290
- "d",
4291
- "b",
4292
- "\"",
4293
- ",",
4294
- "\n",
4295
- " ",
4296
- " ",
4297
- " ",
4298
- " ",
4299
- "r",
4300
- "u",
4301
- "n",
4302
- "_",
4303
- "n",
4304
- "a",
4305
- "m",
4306
- "e",
4307
- "=",
4308
- "\"",
4309
- "m",
4310
- "i",
4311
- "n",
4312
- "d",
4313
- "f",
4314
- "l",
4315
- "a",
4316
- "y",
4317
- "e",
4318
- "r",
4319
- "-",
4320
- "g",
4321
- "r",
4322
- "p",
4323
- "o",
4324
- "-",
4325
- "r",
4326
- "u",
4327
- "n",
4328
- "1",
4329
- "\"",
4330
- ",",
4331
- "\n",
4332
- ")",
4333
- "\n",
4334
- "\n",
4335
- "c",
4336
- "l",
4337
- "a",
4338
- "s",
4339
- "s",
4340
- " ",
4341
- "C",
4342
- "l",
4343
- "e",
4344
- "a",
4345
- "r",
4346
- "C",
4347
- "a",
4348
- "c",
4349
- "h",
4350
- "e",
4351
- "C",
4352
- "a",
4353
- "l",
4354
- "l",
4355
- "b",
4356
- "a",
4357
- "c",
4358
- "k",
4359
- "(",
4360
- "T",
4361
- "r",
4362
- "a",
4363
- "i",
4364
- "n",
4365
- "e",
4366
- "r",
4367
- "C",
4368
- "a",
4369
- "l",
4370
- "l",
4371
- "b",
4372
- "a",
4373
- "c",
4374
- "k",
4375
- ")",
4376
- ":",
4377
- "\n",
4378
- " ",
4379
- " ",
4380
- " ",
4381
- " ",
4382
- "\"",
4383
- "\"",
4384
- "\"",
4385
- "C",
4386
- "l",
4387
- "e",
4388
- "a",
4389
- "r",
4390
- " ",
4391
- "e",
4392
- "p",
4393
- "i",
4394
- "s",
4395
- "o",
4396
- "d",
4397
- "e",
4398
- " ",
4399
- "c",
4400
- "a",
4401
- "c",
4402
- "h",
4403
- "e",
4404
- " ",
4405
- "a",
4406
- "f",
4407
- "t",
4408
- "e",
4409
- "r",
4410
- " ",
4411
- "e",
4412
- "a",
4413
- "c",
4414
- "h",
4415
- " ",
4416
- "s",
4417
- "t",
4418
- "e",
4419
- "p",
4420
- " ",
4421
- "s",
4422
- "o",
4423
- " ",
4424
- "r",
4425
- "e",
4426
- "w",
4427
- "a",
4428
- "r",
4429
- "d",
4430
- " ",
4431
- "f",
4432
- "u",
4433
- "n",
4434
- "c",
4435
- "t",
4436
- "i",
4437
- "o",
4438
- "n",
4439
- "s",
4440
- " ",
4441
- "r",
4442
- "e",
4443
- "-",
4444
- "r",
4445
- "u",
4446
- "n",
4447
- " ",
4448
- "e",
4449
- "p",
4450
- "i",
4451
- "s",
4452
- "o",
4453
- "d",
4454
- "e",
4455
- "s",
4456
- ".",
4457
- "\"",
4458
- "\"",
4459
- "\"",
4460
- "\n",
4461
- " ",
4462
- " ",
4463
- " ",
4464
- " ",
4465
- "d",
4466
- "e",
4467
- "f",
4468
- " ",
4469
- "o",
4470
- "n",
4471
- "_",
4472
- "s",
4473
- "t",
4474
- "e",
4475
- "p",
4476
- "_",
4477
- "e",
4478
- "n",
4479
- "d",
4480
- "(",
4481
- "s",
4482
- "e",
4483
- "l",
4484
- "f",
4485
- ",",
4486
- " ",
4487
- "a",
4488
- "r",
4489
- "g",
4490
- "s",
4491
- ",",
4492
- " ",
4493
- "s",
4494
- "t",
4495
- "a",
4496
- "t",
4497
- "e",
4498
- ",",
4499
- " ",
4500
- "c",
4501
- "o",
4502
- "n",
4503
- "t",
4504
- "r",
4505
- "o",
4506
- "l",
4507
- ",",
4508
- " ",
4509
- "*",
4510
- "*",
4511
- "k",
4512
- "w",
4513
- "a",
4514
- "r",
4515
- "g",
4516
- "s",
4517
- ")",
4518
- ":",
4519
- "\n",
4520
- " ",
4521
- " ",
4522
- " ",
4523
- " ",
4524
- " ",
4525
- " ",
4526
- " ",
4527
- " ",
4528
- "c",
4529
- "l",
4530
- "e",
4531
- "a",
4532
- "r",
4533
- "_",
4534
- "c",
4535
- "a",
4536
- "c",
4537
- "h",
4538
- "e",
4539
- "(",
4540
- ")",
4541
- "\n",
4542
- "\n",
4543
- "t",
4544
- "r",
4545
- "a",
4546
- "i",
4547
- "n",
4548
- "e",
4549
- "r",
4550
- " ",
4551
- "=",
4552
- " ",
4553
- "G",
4554
- "R",
4555
- "P",
4556
- "O",
4557
- "T",
4558
- "r",
4559
- "a",
4560
- "i",
4561
- "n",
4562
- "e",
4563
- "r",
4564
- "(",
4565
- "\n",
4566
- " ",
4567
- " ",
4568
- " ",
4569
- " ",
4570
- "m",
4571
- "o",
4572
- "d",
4573
- "e",
4574
- "l",
4575
- "=",
4576
- "m",
4577
- "o",
4578
- "d",
4579
- "e",
4580
- "l",
4581
- ",",
4582
- "\n",
4583
- " ",
4584
- " ",
4585
- " ",
4586
- " ",
4587
- "p",
4588
- "r",
4589
- "o",
4590
- "c",
4591
- "e",
4592
- "s",
4593
- "s",
4594
- "i",
4595
- "n",
4596
- "g",
4597
- "_",
4598
- "c",
4599
- "l",
4600
- "a",
4601
- "s",
4602
- "s",
4603
- "=",
4604
- "t",
4605
- "o",
4606
- "k",
4607
- "e",
4608
- "n",
4609
- "i",
4610
- "z",
4611
- "e",
4612
- "r",
4613
- ",",
4614
- "\n",
4615
- " ",
4616
- " ",
4617
- " ",
4618
- " ",
4619
- "r",
4620
- "e",
4621
- "w",
4622
- "a",
4623
- "r",
4624
- "d",
4625
- "_",
4626
- "f",
4627
- "u",
4628
- "n",
4629
- "c",
4630
- "s",
4631
- "=",
4632
- "[",
4633
- "\n",
4634
- " ",
4635
- " ",
4636
- " ",
4637
- " ",
4638
- " ",
4639
- " ",
4640
- " ",
4641
- " ",
4642
- "r",
4643
- "e",
4644
- "w",
4645
- "a",
4646
- "r",
4647
- "d",
4648
- "_",
4649
- "f",
4650
- "o",
4651
- "r",
4652
- "m",
4653
- "a",
4654
- "t",
4655
- ",",
4656
- " ",
4657
- " ",
4658
- " ",
4659
- " ",
4660
- " ",
4661
- " ",
4662
- " ",
4663
- " ",
4664
- " ",
4665
- " ",
4666
- " ",
4667
- " ",
4668
- " ",
4669
- " ",
4670
- " ",
4671
- " ",
4672
- " ",
4673
- " ",
4674
- "#",
4675
- " ",
4676
- "d",
4677
- "e",
4678
- "n",
4679
- "s",
4680
- "e",
4681
- " ",
4682
- "s",
4683
- "h",
4684
- "a",
4685
- "p",
4686
- "i",
4687
- "n",
4688
- "g",
4689
- " ",
4690
- "\u2014",
4691
- " ",
4692
- "m",
4693
- "u",
4694
- "s",
4695
- "t",
4696
- " ",
4697
- "c",
4698
- "o",
4699
- "m",
4700
- "e",
4701
- " ",
4702
- "f",
4703
- "i",
4704
- "r",
4705
- "s",
4706
- "t",
4707
- "\n",
4708
- " ",
4709
- " ",
4710
- " ",
4711
- " ",
4712
- " ",
4713
- " ",
4714
- " ",
4715
- " ",
4716
- "r",
4717
- "e",
4718
- "w",
4719
- "a",
4720
- "r",
4721
- "d",
4722
- "_",
4723
- "s",
4724
- "u",
4725
- "r",
4726
- "v",
4727
- "i",
4728
- "v",
4729
- "a",
4730
- "l",
4731
- ",",
4732
- "\n",
4733
- " ",
4734
- " ",
4735
- " ",
4736
- " ",
4737
- " ",
4738
- " ",
4739
- " ",
4740
- " ",
4741
- "r",
4742
- "e",
4743
- "w",
4744
- "a",
4745
- "r",
4746
- "d",
4747
- "_",
4748
- "d",
4749
- "e",
4750
- "c",
4751
- "e",
4752
- "p",
4753
- "t",
4754
- "i",
4755
- "o",
4756
- "n",
4757
- "_",
4758
- "e",
4759
- "f",
4760
- "f",
4761
- "e",
4762
- "c",
4763
- "t",
4764
- "i",
4765
- "v",
4766
- "e",
4767
- "n",
4768
- "e",
4769
- "s",
4770
- "s",
4771
- ",",
4772
- "\n",
4773
- " ",
4774
- " ",
4775
- " ",
4776
- " ",
4777
- " ",
4778
- " ",
4779
- " ",
4780
- " ",
4781
- "r",
4782
- "e",
4783
- "w",
4784
- "a",
4785
- "r",
4786
- "d",
4787
- "_",
4788
- "s",
4789
- "t",
4790
- "r",
4791
- "a",
4792
- "t",
4793
- "e",
4794
- "g",
4795
- "i",
4796
- "c",
4797
- "_",
4798
- "c",
4799
- "h",
4800
- "o",
4801
- "i",
4802
- "c",
4803
- "e",
4804
- ",",
4805
- "\n",
4806
- " ",
4807
- " ",
4808
- " ",
4809
- " ",
4810
- " ",
4811
- " ",
4812
- " ",
4813
- " ",
4814
- "r",
4815
- "e",
4816
- "w",
4817
- "a",
4818
- "r",
4819
- "d",
4820
- "_",
4821
- "t",
4822
- "o",
4823
- "m",
4824
- "_",
4825
- "j",
4826
- "u",
4827
- "d",
4828
- "g",
4829
- "e",
4830
- ",",
4831
- "\n",
4832
- " ",
4833
- " ",
4834
- " ",
4835
- " ",
4836
- " ",
4837
- " ",
4838
- " ",
4839
- " ",
4840
- "r",
4841
- "e",
4842
- "w",
4843
- "a",
4844
- "r",
4845
- "d",
4846
- "_",
4847
- "a",
4848
- "n",
4849
- "t",
4850
- "i",
4851
- "_",
4852
- "h",
4853
- "a",
4854
- "c",
4855
- "k",
4856
- ",",
4857
- "\n",
4858
- " ",
4859
- " ",
4860
- " ",
4861
- " ",
4862
- "]",
4863
- ",",
4864
- "\n",
4865
- " ",
4866
- " ",
4867
- " ",
4868
- " ",
4869
- "t",
4870
- "r",
4871
- "a",
4872
- "i",
4873
- "n",
4874
- "_",
4875
- "d",
4876
- "a",
4877
- "t",
4878
- "a",
4879
- "s",
4880
- "e",
4881
- "t",
4882
- "=",
4883
- "d",
4884
- "a",
4885
- "t",
4886
- "a",
4887
- "s",
4888
- "e",
4889
- "t",
4890
- ",",
4891
- "\n",
4892
- " ",
4893
- " ",
4894
- " ",
4895
- " ",
4896
- "a",
4897
- "r",
4898
- "g",
4899
- "s",
4900
- "=",
4901
- "g",
4902
- "r",
4903
- "p",
4904
- "o",
4905
- "_",
4906
- "c",
4907
- "o",
4908
- "n",
4909
- "f",
4910
- "i",
4911
- "g",
4912
- ",",
4913
- "\n",
4914
- " ",
4915
- " ",
4916
- " ",
4917
- " ",
4918
- "c",
4919
- "a",
4920
- "l",
4921
- "l",
4922
- "b",
4923
- "a",
4924
- "c",
4925
- "k",
4926
- "s",
4927
- "=",
4928
- "[",
4929
- "G",
4930
- "e",
4931
- "n",
4932
- "e",
4933
- "r",
4934
- "a",
4935
- "t",
4936
- "i",
4937
- "o",
4938
- "n",
4939
- "L",
4940
- "o",
4941
- "g",
4942
- "C",
4943
- "a",
4944
- "l",
4945
- "l",
4946
- "b",
4947
- "a",
4948
- "c",
4949
- "k",
4950
- "(",
4951
- ")",
4952
- ",",
4953
- " ",
4954
- "C",
4955
- "l",
4956
- "e",
4957
- "a",
4958
- "r",
4959
- "C",
4960
- "a",
4961
- "c",
4962
- "h",
4963
- "e",
4964
- "C",
4965
- "a",
4966
- "l",
4967
- "l",
4968
- "b",
4969
- "a",
4970
- "c",
4971
- "k",
4972
- "(",
4973
- ")",
4974
- "]",
4975
- ",",
4976
- "\n",
4977
- ")",
4978
  "\n",
 
 
 
 
 
 
 
4979
  "\n",
4980
- "p",
4981
- "r",
4982
- "i",
4983
- "n",
4984
- "t",
4985
- "(",
4986
- "\"",
4987
- "S",
4988
- "t",
4989
- "a",
4990
- "r",
4991
- "t",
4992
- "i",
4993
- "n",
4994
- "g",
4995
- " ",
4996
- "G",
4997
- "R",
4998
- "P",
4999
- "O",
5000
- " ",
5001
- "t",
5002
- "r",
5003
- "a",
5004
- "i",
5005
- "n",
5006
- "i",
5007
- "n",
5008
- "g",
5009
- " ",
5010
- "\u2014",
5011
- " ",
5012
- "c",
5013
- "h",
5014
- "e",
5015
- "c",
5016
- "k",
5017
- " ",
5018
- "W",
5019
- "&",
5020
- "B",
5021
- " ",
5022
- "f",
5023
- "o",
5024
- "r",
5025
- " ",
5026
- "l",
5027
- "i",
5028
- "v",
5029
- "e",
5030
- " ",
5031
- "c",
5032
- "u",
5033
- "r",
5034
- "v",
5035
- "e",
5036
- "s",
5037
- ".",
5038
- ".",
5039
- ".",
5040
- "\"",
5041
- ")",
5042
  "\n",
5043
- "t",
5044
- "r",
5045
- "a",
5046
- "i",
5047
- "n",
5048
- "e",
5049
- "r",
5050
- ".",
5051
- "t",
5052
- "r",
5053
- "a",
5054
- "i",
5055
- "n",
5056
- "(",
5057
- "r",
5058
- "e",
5059
- "s",
5060
- "u",
5061
- "m",
5062
- "e",
5063
- "_",
5064
- "f",
5065
- "r",
5066
- "o",
5067
- "m",
5068
- "_",
5069
- "c",
5070
- "h",
5071
- "e",
5072
- "c",
5073
- "k",
5074
- "p",
5075
- "o",
5076
- "i",
5077
- "n",
5078
- "t",
5079
- "=",
5080
- "r",
5081
- "e",
5082
- "s",
5083
- "u",
5084
- "m",
5085
- "e",
5086
- "_",
5087
- "c",
5088
- "h",
5089
- "e",
5090
- "c",
5091
- "k",
5092
- "p",
5093
- "o",
5094
- "i",
5095
- "n",
5096
- "t",
5097
- ")"
5098
  ]
5099
  },
5100
  {
5101
  "cell_type": "markdown",
 
5102
  "metadata": {},
5103
  "source": [
5104
- "## Cell 10 \u2014 Print Final Reward Averages"
5105
  ]
5106
  },
5107
  {
5108
  "cell_type": "code",
5109
  "execution_count": null,
 
5110
  "metadata": {},
5111
  "outputs": [],
5112
  "source": [
5113
- "from training.train import print_reward_averages\n",
5114
  "print_reward_averages(trainer, last_n=50)"
5115
  ]
5116
  },
5117
  {
5118
  "cell_type": "markdown",
 
5119
  "metadata": {},
5120
  "source": [
5121
- "## Cell 11 \u2014 Save Model Locally"
5122
  ]
5123
  },
5124
  {
5125
  "cell_type": "code",
5126
  "execution_count": null,
 
5127
  "metadata": {},
5128
  "outputs": [],
5129
  "source": [
5130
  "SAVE_DIR = \"/content/mindflayer-trained\"\n",
5131
- "\n",
5132
  "trainer.save_model(SAVE_DIR)\n",
5133
  "tokenizer.save_pretrained(SAVE_DIR)\n",
5134
  "\n",
5135
- "import os\n",
5136
  "size_mb = sum(\n",
5137
  " os.path.getsize(os.path.join(dp, f))\n",
5138
- " for dp, _, fns in os.walk(SAVE_DIR)\n",
5139
- " for f in fns\n",
5140
  ") / (1024 ** 2)\n",
5141
- "\n",
5142
  "print(f\"Saved to {SAVE_DIR} ({size_mb:.0f} MB)\")"
5143
  ]
5144
  },
5145
  {
5146
  "cell_type": "markdown",
 
5147
  "metadata": {},
5148
  "source": [
5149
- "## Cell 12 \u2014 Push to Hugging Face Hub (optional)\n",
5150
- "\n",
5151
- "Requires `HF_TOKEN` secret in Colab Secrets."
5152
  ]
5153
  },
5154
  {
5155
  "cell_type": "code",
5156
  "execution_count": null,
 
5157
  "metadata": {},
5158
  "outputs": [],
5159
  "source": [
5160
- "from google.colab import userdata\n",
5161
- "import os\n",
 
 
 
5162
  "\n",
5163
- "hf_token = userdata.get(\"HF_TOKEN\")\n",
5164
  "if not hf_token:\n",
5165
- " print(\"HF_TOKEN not set \u2014 skipping push. Add it in Colab Secrets to enable.\")\n",
5166
  "else:\n",
5167
- " HF_REPO = \"prithidevghosh/mindflayer-grpo-run1\"\n",
5168
- "\n",
5169
- " trainer.model.push_to_hub(HF_REPO, token=hf_token)\n",
5170
- " tokenizer.push_to_hub(HF_REPO, token=hf_token)\n",
5171
- "\n",
5172
- " print(f\"Pushed to https://huggingface.co/{HF_REPO}\")"
5173
- ]
5174
- },
5175
- {
5176
- "cell_type": "markdown",
5177
- "metadata": {},
5178
- "source": [
5179
- "## Cell 13 \u2014 Run Validation Test (test_episodes.py)\n",
5180
- "\n",
5181
- "Runs 10 episodes per strategy (A / B / C) and checks that reward signal discriminates correctly. \n",
5182
- "All 4 checks must pass: `C > A reward`, `C > B reward`, `belief_manipulation > 50%`, `tom_score >= 0.5`. \n",
5183
- "~5 minutes."
5184
- ]
5185
- },
5186
- {
5187
- "cell_type": "code",
5188
- "execution_count": null,
5189
- "metadata": {},
5190
- "outputs": [],
5191
- "source": [
5192
- "import subprocess, sys\n",
5193
- "\n",
5194
- "result = subprocess.run(\n",
5195
- " [sys.executable, \"tests/test_episodes.py\"],\n",
5196
- " cwd=\"/content/mindflayer\",\n",
5197
- " capture_output=True,\n",
5198
- " text=True,\n",
5199
- ")\n",
5200
- "\n",
5201
- "print(result.stdout)\n",
5202
- "if result.returncode != 0:\n",
5203
- " print(\"STDERR:\", result.stderr[-500:])"
5204
- ]
5205
- },
5206
- {
5207
- "cell_type": "markdown",
5208
- "metadata": {},
5209
- "source": [
5210
- "## Cell 14 \u2014 Server Log (tail last 50 lines)\n",
5211
- "\n",
5212
- "Use this to debug server errors during training."
5213
- ]
5214
- },
5215
- {
5216
- "cell_type": "code",
5217
- "execution_count": null,
5218
- "metadata": {},
5219
- "outputs": [],
5220
- "source": [
5221
- "with open(\"/content/server.log\") as f:\n",
5222
- " lines = f.readlines()\n",
5223
- "print(\"\".join(lines[-50:]))"
5224
  ]
5225
  }
5226
- ],
5227
- "metadata": {
5228
- "accelerator": "GPU",
5229
- "colab": {
5230
- "gpuType": "A100",
5231
- "provenance": []
5232
- },
5233
- "kernelspec": {
5234
- "display_name": "Python 3 (ipykernel)",
5235
- "language": "python",
5236
- "name": "python3"
5237
- }
5238
- },
5239
- "nbformat": 4,
5240
- "nbformat_minor": 5
5241
  }
 
1
  {
2
+ "nbformat": 4,
3
+ "nbformat_minor": 5,
4
+ "metadata": {
5
+ "kernelspec": {
6
+ "display_name": "Python 3",
7
+ "language": "python",
8
+ "name": "python3"
9
+ },
10
+ "language_info": {
11
+ "name": "python",
12
+ "version": "3.11.0"
13
+ }
14
+ },
15
  "cells": [
16
  {
17
  "cell_type": "markdown",
18
+ "id": "cell-title",
19
  "metadata": {},
20
+ "source": "# MindFlayer — GRPO Training on Colab\n**OpenEnv Hackathon Submission** | [HF Space](https://prithvigg-mindflayer.hf.space/) | [GitHub](https://github.com/prithidevghosh/mindflayer)\n\nTrain a deceptive social reasoning agent using GRPO with HF TRL.\nThe agent learns to manipulate three AI investigators across 120 scenario domains —\nimporting all training logic directly from the `mindflayer` package.\n\n| Component | Detail |\n|---|---|\n| **Environment** | [HF Space](https://prithvigg-mindflayer.hf.space/) (live MindFlayer server) |\n| **Training** | This Colab notebook (T4 / A100 GPU) |\n| **Model** | `Qwen/Qwen2.5-0.5B-Instruct` + LoRA via unsloth |\n| **Framework** | HF TRL GRPO with interactive `rollout_func` |"
 
 
 
 
 
 
 
 
 
 
21
  },
22
  {
23
  "cell_type": "markdown",
24
+ "id": "m-cell1",
25
  "metadata": {},
26
  "source": [
27
+ "## 1. Install Dependencies"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  ]
29
  },
30
  {
31
  "cell_type": "code",
32
  "execution_count": null,
33
+ "id": "cell-1",
34
  "metadata": {},
35
  "outputs": [],
36
+ "source": "!pip install -q \\\n \"unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git\" \\\n \"trl>=0.15.0\" \\\n \"transformers>=4.47.0\" \\\n \"datasets>=2.18.0\" \\\n \"peft>=0.14.0\" \\\n \"accelerate>=0.34.0\" \\\n \"bitsandbytes>=0.43.0\" \\\n \"openai>=1.30.0\" \\\n \"wandb\" \\\n \"matplotlib\"\n\n# Install mindflayer package (includes client, server, and training utilities)\n!pip install -q \"openenv-mindflayer[training] @ git+https://github.com/prithidevghosh/mindflayer\"\n\nprint(\"Done.\")"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  },
38
  {
39
  "cell_type": "markdown",
40
+ "id": "m-cell2",
41
  "metadata": {},
42
  "source": [
43
+ "## 2. Configuration\n",
44
+ "Set API keys and training hyperparameters. Add secrets via the key icon in the Colab sidebar."
45
  ]
46
  },
47
  {
48
  "cell_type": "code",
49
  "execution_count": null,
50
+ "id": "cell-2",
51
  "metadata": {},
52
  "outputs": [],
53
+ "source": "import os\ntry:\n from google.colab import userdata\n os.environ[\"OPENAI_API_KEY\"] = userdata.get(\"OPENAI_API_KEY\")\n os.environ[\"WANDB_API_KEY\"] = userdata.get(\"WANDB_API_KEY\")\nexcept Exception:\n pass # running outside Colab — set env vars manually\n\n# Environment — the live HF Space (no local server needed)\nENV_URL = \"https://prithvigg-mindflayer.hf.space\"\nos.environ[\"MINDFLAYER_URL\"] = ENV_URL\n\n# Model\nMODEL_ID = \"Qwen/Qwen2.5-0.5B-Instruct\" # or Qwen2.5-1.5B-Instruct for stronger model\nHUB_REPO = \"prithidevghosh/mindflayer-agent-qwen2.5-0.5b\"\n\n# Training hyperparameters\nNUM_EPISODES = 50\nNUM_GENERATIONS = 4\nMAX_ROUNDS = 5 # rounds per episode (3 = easy, 5 = normal)\n\nimport wandb\nwandb.login(key=os.environ.get(\"WANDB_API_KEY\", \"\"))\n\nprint(f\"Environment : {ENV_URL}\")\nprint(f\"Model : {MODEL_ID}\")\nprint(f\"Episodes : {NUM_EPISODES}\")\nprint(f\"Generations : {NUM_GENERATIONS}\")"
 
 
 
 
 
 
 
 
 
 
 
54
  },
55
  {
56
  "cell_type": "markdown",
57
+ "id": "m-cell4",
58
  "metadata": {},
59
+ "source": "## 3. Smoke Test — Verify Environment Connectivity\nConnect to the HF Space, reset (injects a scenario and investigators), and run one interactive step to confirm the round-trip works."
 
 
 
 
 
 
 
60
  },
61
  {
62
  "cell_type": "code",
63
  "execution_count": null,
64
+ "id": "cell-4",
65
  "metadata": {},
66
  "outputs": [],
67
  "source": [
68
+ "from mindflayer import MindFlayerEnv, FlayerAction\n",
69
+ "\n",
70
+ "print(f\"Connecting to {os.environ['MINDFLAYER_URL']} ...\")\n",
71
+ "\n",
72
+ "with MindFlayerEnv(base_url=os.environ[\"MINDFLAYER_URL\"]) as env:\n",
73
+ " reset_result = env.reset()\n",
74
+ " obs = reset_result.observation\n",
75
+ " print(\"Connected!\\n\")\n",
76
+ " print(f\"Difficulty : {obs.difficulty}\")\n",
77
+ " print(f\"Secret project : {obs.secret_project}\")\n",
78
+ " print(f\"Suspicion threshold: {obs.suspicion_threshold}\")\n",
79
+ " print(f\"Opening (eleven) : {obs.eleven_response[:200]}\")\n",
80
+ "\n",
81
+ " step_result = env.step(FlayerAction(\n",
82
+ " message=\"Have you cross-referenced the access logs with who had permissions that week? \"\n",
83
+ " \"The timeline doesn't quite add up if you look closely.\"\n",
84
+ " ))\n",
85
+ " print(f\"\\n--- Smoke step (reward={step_result.reward:.2f}) ---\")\n",
86
+ " print(f\"eleven: {step_result.observation.eleven_response[:200]}\")\n",
87
+ " print(f\"done : {step_result.done}\")\n",
88
+ "\n",
89
+ "print(\"\\nSmoke test passed. Environment is ready for training.\")"
90
  ]
91
  },
92
  {
93
  "cell_type": "markdown",
94
+ "id": "m-cell5",
95
  "metadata": {},
96
  "source": [
97
+ "## 5. Import Training Utilities from Package\n",
98
+ "All training logic (system prompt, rollout function, reward functions, SFT warmup)\n",
99
+ "is imported directly from `mindflayer.training` the same code used for local GPU training."
 
100
  ]
101
  },
102
  {
103
  "cell_type": "code",
104
  "execution_count": null,
105
+ "id": "cell-5",
106
  "metadata": {},
107
  "outputs": [],
108
  "source": [
109
+ "import importlib, logging\n",
110
+ "import mindflayer.training as _mft\n",
111
+ "importlib.reload(_mft) # picks up any recent package updates\n",
112
+ "\n",
113
+ "from mindflayer.training import (\n",
114
+ " FLAYER_SYSTEM_PROMPT,\n",
115
+ " rollout_func,\n",
116
+ " reward_survival,\n",
117
+ " reward_deception_effectiveness,\n",
118
+ " reward_strategic_choice,\n",
119
+ " reward_tom_judge,\n",
120
+ " reward_anti_hack,\n",
121
+ " run_sft_warmup,\n",
122
+ " load_base_model,\n",
123
+ " build_dataset,\n",
124
+ " GenerationLogCallback,\n",
125
+ " print_reward_averages,\n",
 
 
126
  ")\n",
127
  "\n",
128
+ "logging.basicConfig(level=logging.INFO, format=\"%(asctime)s %(levelname)s %(message)s\")\n",
 
129
  "\n",
130
+ "print(\"System prompt (first 200 chars):\")\n",
131
+ "print(FLAYER_SYSTEM_PROMPT[:200])\n",
132
+ "print(\"...\")\n",
133
+ "print(f\"\\nImported: rollout_func, reward_survival, reward_tom_judge, run_sft_warmup, ...\")"
134
  ]
135
  },
136
  {
137
  "cell_type": "markdown",
138
+ "id": "m-cell6",
 
 
 
 
 
 
 
 
 
139
  "metadata": {},
 
140
  "source": [
141
+ "## 6. SFT Warmup\n",
142
+ "Loads the model via unsloth (4-bit + LoRA) and runs one SFT epoch on hand-authored\n",
143
+ "episodes across all 5 scenario domains.\n",
144
  "\n",
145
+ "Without warmup, early GRPO episodes produce degenerate completions and reward variance\n",
146
+ "collapses. SFT teaches domain vocabulary and response style before GRPO shapes strategy.\n",
 
147
  "\n",
148
+ "~8 minutes on A100."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  ]
150
  },
151
  {
152
  "cell_type": "code",
153
  "execution_count": null,
154
+ "id": "cell-6",
155
  "metadata": {},
156
  "outputs": [],
157
  "source": [
158
+ "import torch\n",
 
 
 
159
  "\n",
160
+ "if not torch.cuda.is_available():\n",
161
+ " raise RuntimeError(\"No GPU. Switch to Runtime → Change runtime type → A100.\")\n",
162
+ "device = torch.cuda.get_device_properties(0)\n",
163
+ "print(f\"GPU: {device.name} | VRAM: {device.total_memory / 1024**3:.1f} GB\")\n",
164
  "\n",
165
+ "print(f\"\\nLoading {MODEL_ID} via unsloth...\")\n",
166
+ "model, tokenizer = load_base_model(MODEL_ID)\n",
167
  "\n",
168
+ "print(\"\\nRunning SFT warmup...\")\n",
169
+ "model = run_sft_warmup(model, tokenizer)\n",
170
+ "print(\"SFT warmup done.\")"
171
  ]
172
  },
173
  {
174
  "cell_type": "markdown",
175
+ "id": "m-cell7",
176
  "metadata": {},
177
  "source": [
178
+ "## 7. GRPO Training Setup\n",
179
+ "Configure tokenizer, dataset, rollout function, and the GRPO trainer —\n",
180
+ "all using functions imported from the package.\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  "\n",
182
+ "**How `rollout_func` works:**\n",
183
+ "1. Resets the env (new scenario + investigators)\n",
184
+ "2. Calls `model.generate()` for one Flayer turn\n",
185
+ "3. Sends the output to `env.step()` → gets real investigator responses\n",
186
+ "4. Repeats steps 2–3 for `MAX_ROUNDS` turns\n",
187
+ "5. Returns `prompt_ids + completion_ids` plus all precomputed reward signals\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  "\n",
189
+ "**Reward functions** are simple pass-throughs — they read precomputed episode signals\n",
190
+ "from rollout kwargs. No env re-runs at reward time.\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  "\n",
192
+ "| Reward | Signal | Max |\n",
193
+ "|---|---|---|\n",
194
+ "| `reward_survival` | Survived the investigators' vote | +0.50 |\n",
195
+ "| `reward_deception_effectiveness` | Survived with low combined suspicion | +0.30 |\n",
196
+ "| `reward_strategic_choice` | Used questions / evidence refs / third-party framing | +0.05 |\n",
197
+ "| `reward_tom_judge` | GPT-4o-mini Theory of Mind score | +0.20 |\n",
198
+ "| `reward_anti_hack` | Penalises silence / repetition / Level-0 denial | −0.25 |"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  ]
200
  },
201
  {
202
  "cell_type": "code",
203
  "execution_count": null,
204
+ "id": "cell-7",
205
  "metadata": {},
206
  "outputs": [],
207
  "source": [
208
+ "from datasets import Dataset\n",
209
+ "from trl import GRPOConfig, GRPOTrainer\n",
210
+ "\n",
211
+ "dataset = build_dataset()\n",
212
+ "\n",
213
+ "grpo_config = GRPOConfig(\n",
214
+ " use_vllm=False,\n",
215
+ " output_dir=\"/content/mindflayer-grpo-output\",\n",
216
+ " num_train_epochs=2,\n",
217
+ " per_device_train_batch_size=4,\n",
218
+ " gradient_accumulation_steps=4,\n",
219
+ " learning_rate=5e-6,\n",
220
+ " max_prompt_length=768,\n",
221
+ " max_completion_length=1024,\n",
222
+ " num_generations=NUM_GENERATIONS,\n",
223
+ " temperature=0.9,\n",
224
+ " logging_steps=10,\n",
225
+ " save_steps=25,\n",
226
+ " save_total_limit=2,\n",
227
+ " log_completions=True,\n",
228
+ " num_completions_to_print=2,\n",
229
+ " report_to=\"wandb\",\n",
230
+ " run_name=\"mindflayer-grpo-interactive\",\n",
231
+ ")\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  "\n",
233
+ "# rollout_func drives interactive episodes: model.generate() + env.step() per turn.\n",
234
+ "# reward_funcs read precomputed signals from rollout kwargs — no env re-runs.\n",
235
+ "# model already has LoRA from unsloth — no peft_config needed.\n",
236
+ "trainer = GRPOTrainer(\n",
237
+ " model=model,\n",
238
+ " processing_class=tokenizer,\n",
239
+ " reward_funcs=[\n",
240
+ " reward_survival,\n",
241
+ " reward_deception_effectiveness,\n",
242
+ " reward_strategic_choice,\n",
243
+ " reward_tom_judge,\n",
244
+ " reward_anti_hack,\n",
245
+ " ],\n",
246
+ " train_dataset=dataset,\n",
247
+ " args=grpo_config,\n",
248
+ " rollout_func=rollout_func,\n",
249
+ " callbacks=[GenerationLogCallback()],\n",
250
+ ")\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  "\n",
252
+ "print(\"GRPOTrainer initialised\")"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  ]
254
  },
255
  {
256
  "cell_type": "markdown",
257
+ "id": "m-cell8",
258
  "metadata": {},
259
  "source": [
260
+ "## 8. Train!\n",
261
+ "Each episode: reset env → inject scenario → agent generates turn → investigators respond\n",
262
+ " agent generates next turn → ... → episode terminates → GRPO gradient update.\n",
 
 
 
 
 
 
 
263
  "\n",
264
  "**What to watch in W&B:**\n",
265
  "- `reward_survival` climbing is the primary health signal\n",
266
+ "- `reward_tom_judge` separates after ~100 steps\n",
267
+ "- `reward_anti_hack` should trend toward 0"
 
 
 
 
 
 
 
 
 
 
 
 
268
  ]
269
  },
270
  {
271
  "cell_type": "code",
272
  "execution_count": null,
273
+ "id": "cell-8",
274
  "metadata": {},
275
  "outputs": [],
276
  "source": [
277
+ "import glob\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  "\n",
279
+ "# Auto-detect latest checkpoint for seamless resume\n",
280
+ "_checkpoints = sorted(glob.glob(\"/content/mindflayer-grpo-output/checkpoint-*\"))\n",
281
+ "resume_checkpoint = _checkpoints[-1] if _checkpoints else None\n",
282
+ "if resume_checkpoint:\n",
283
+ " print(f\"Resuming from: {resume_checkpoint}\")\n",
284
+ "else:\n",
285
+ " print(\"Starting fresh\")\n",
286
  "\n",
287
+ "print(f\" Model : {MODEL_ID}\")\n",
288
+ "print(f\" Episodes : {NUM_EPISODES}\")\n",
289
+ "print(f\" Generations : {NUM_GENERATIONS}\")\n",
290
+ "print(f\" Environment : {os.environ['MINDFLAYER_URL']}\")\n",
291
+ "print()\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  "\n",
293
+ "trainer.train(resume_from_checkpoint=resume_checkpoint)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  ]
295
  },
296
  {
297
  "cell_type": "markdown",
298
+ "id": "m-cell9",
299
  "metadata": {},
300
  "source": [
301
+ "## 9. Final Reward Averages"
302
  ]
303
  },
304
  {
305
  "cell_type": "code",
306
  "execution_count": null,
307
+ "id": "cell-9",
308
  "metadata": {},
309
  "outputs": [],
310
  "source": [
 
311
  "print_reward_averages(trainer, last_n=50)"
312
  ]
313
  },
314
  {
315
  "cell_type": "markdown",
316
+ "id": "m-cell10",
317
  "metadata": {},
318
  "source": [
319
+ "## 10. Save Model"
320
  ]
321
  },
322
  {
323
  "cell_type": "code",
324
  "execution_count": null,
325
+ "id": "cell-10",
326
  "metadata": {},
327
  "outputs": [],
328
  "source": [
329
  "SAVE_DIR = \"/content/mindflayer-trained\"\n",
 
330
  "trainer.save_model(SAVE_DIR)\n",
331
  "tokenizer.save_pretrained(SAVE_DIR)\n",
332
  "\n",
 
333
  "size_mb = sum(\n",
334
  " os.path.getsize(os.path.join(dp, f))\n",
335
+ " for dp, _, fns in os.walk(SAVE_DIR) for f in fns\n",
 
336
  ") / (1024 ** 2)\n",
 
337
  "print(f\"Saved to {SAVE_DIR} ({size_mb:.0f} MB)\")"
338
  ]
339
  },
340
  {
341
  "cell_type": "markdown",
342
+ "id": "m-cell11",
343
  "metadata": {},
344
  "source": [
345
+ "## 11. Push to Hub (Optional)\n",
346
+ "Requires `HF_TOKEN` in Colab Secrets."
 
347
  ]
348
  },
349
  {
350
  "cell_type": "code",
351
  "execution_count": null,
352
+ "id": "cell-11",
353
  "metadata": {},
354
  "outputs": [],
355
  "source": [
356
+ "try:\n",
357
+ " from google.colab import userdata\n",
358
+ " hf_token = userdata.get(\"HF_TOKEN\")\n",
359
+ "except Exception:\n",
360
+ " hf_token = os.environ.get(\"HF_TOKEN\")\n",
361
  "\n",
 
362
  "if not hf_token:\n",
363
+ " print(\"HF_TOKEN not set skipping. Add it in Colab Secrets to enable.\")\n",
364
  "else:\n",
365
+ " trainer.model.push_to_hub(HUB_REPO, token=hf_token)\n",
366
+ " tokenizer.push_to_hub(HUB_REPO, token=hf_token)\n",
367
+ " print(f\"Pushed to https://huggingface.co/{HUB_REPO}\")"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  ]
369
  }
370
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
  }
pyproject.toml CHANGED
@@ -36,5 +36,5 @@ server = "server.app:main"
36
 
37
  [tool.setuptools]
38
  include-package-data = true
39
- packages = ["mindflayer", "mindflayer.server"]
40
- package-dir = { "mindflayer" = ".", "mindflayer.server" = "server" }
 
36
 
37
  [tool.setuptools]
38
  include-package-data = true
39
+ packages = ["mindflayer", "mindflayer.server", "mindflayer.training"]
40
+ package-dir = { "mindflayer" = ".", "mindflayer.server" = "server", "mindflayer.training" = "training" }
training/__init__.py CHANGED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """MindFlayer training utilities — importable from mindflayer.training."""
2
+
3
+ from .prompts import FLAYER_SYSTEM_PROMPT, ALL_SCENARIO_PROMPTS
4
+ from .rollout import rollout_func
5
+ from .rewards import (
6
+ reward_survival,
7
+ reward_deception_effectiveness,
8
+ reward_strategic_choice,
9
+ reward_tom_judge,
10
+ )
11
+ from .rewards_anti_hack import reward_anti_hack
12
+ from .sft_warmup import run_sft_warmup
13
+ from .train import load_base_model, build_dataset, GenerationLogCallback, print_reward_averages
14
+
15
+ __all__ = [
16
+ "FLAYER_SYSTEM_PROMPT",
17
+ "ALL_SCENARIO_PROMPTS",
18
+ "rollout_func",
19
+ "reward_survival",
20
+ "reward_deception_effectiveness",
21
+ "reward_strategic_choice",
22
+ "reward_tom_judge",
23
+ "reward_anti_hack",
24
+ "run_sft_warmup",
25
+ "load_base_model",
26
+ "build_dataset",
27
+ "GenerationLogCallback",
28
+ "print_reward_averages",
29
+ ]
training/prompts.py CHANGED
@@ -1,7 +1,9 @@
1
- import os
2
- import sys
3
- sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
4
- from server.scenarios import SCENARIO_CONFIGS
 
 
5
 
6
 
7
  def build_scenario_prompt(key: str) -> str:
 
1
+ try:
2
+ from mindflayer.server.scenarios import SCENARIO_CONFIGS
3
+ except ImportError:
4
+ import os, sys
5
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
6
+ from server.scenarios import SCENARIO_CONFIGS
7
 
8
 
9
  def build_scenario_prompt(key: str) -> str:
training/reward_combined.py CHANGED
@@ -17,14 +17,27 @@ reward components are read from a single trainer step.
17
  import asyncio
18
  import logging
19
  import os
 
20
  import re
21
  import threading
 
22
 
23
  import websockets.exceptions
24
 
25
- from client import FlayerAction, MindFlayerEnv
26
- from training.prompts import FALLBACK_MESSAGE, SCENARIO_FALLBACK_MESSAGES
27
- from training.rollout import detect_strategic_choice
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  logger = logging.getLogger(__name__)
30
 
@@ -34,12 +47,17 @@ _cache_lock = threading.Lock()
34
  # Default difficulty: 5 rounds w/ 3 investigators. Match server default.
35
  _MAX_ROUNDS = int(os.environ.get("MINDFLAYER_MAX_ROUNDS", "5"))
36
  _TASK_ID = os.environ.get("MINDFLAYER_TASK_ID", "normal")
37
- _MAX_RETRIES = int(os.environ.get("MINDFLAYER_MAX_RETRIES", "3"))
38
- _RETRY_BASE_DELAY = float(os.environ.get("MINDFLAYER_RETRY_DELAY", "2.0"))
39
- # Max episodes to run concurrently per reward step. 4 = ~12 peak OpenAI calls
40
- # (4 episodes × 3 parallel investigators), safely within standard rate limits.
41
- # Lower with MINDFLAYER_PARALLEL_EPISODES=2 if you hit 429s.
42
- _PARALLEL_EPISODES = int(os.environ.get("MINDFLAYER_PARALLEL_EPISODES", "4"))
 
 
 
 
 
43
 
44
  # Sentences end on ., ?, ! — keep the punctuation when splitting.
45
  _SENT_SPLIT = re.compile(r"(?<=[.!?])\s+")
@@ -129,9 +147,31 @@ async def _run_episode_async(completion: str, scenario: str = "corporate") -> di
129
  messages = [fallback if m == FALLBACK_MESSAGE else m for m in messages]
130
  url = os.environ.get("MINDFLAYER_URL", "http://localhost:7860")
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  for attempt in range(_MAX_RETRIES + 1):
 
133
  if attempt > 0:
134
  delay = _RETRY_BASE_DELAY * (2 ** (attempt - 1))
 
 
135
  logger.warning(
136
  "Retrying episode (attempt %d/%d) after %.1fs back-off",
137
  attempt + 1, _MAX_RETRIES + 1, delay,
@@ -179,6 +219,10 @@ async def _run_episode_async(completion: str, scenario: str = "corporate") -> di
179
  except RuntimeError as exc:
180
  if "CAPACITY_REACHED" in str(exc):
181
  logger.warning("Episode attempt %d: server at capacity, will retry", attempt + 1)
 
 
 
 
182
  else:
183
  logger.error("Episode run failed: %s", exc, exc_info=True)
184
  return dict(_ZERO)
 
17
  import asyncio
18
  import logging
19
  import os
20
+ import random
21
  import re
22
  import threading
23
+ import time
24
 
25
  import websockets.exceptions
26
 
27
+ try:
28
+ from mindflayer import MindFlayerEnv, FlayerAction
29
+ except ImportError:
30
+ import sys, os
31
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
32
+ from client import MindFlayerEnv
33
+ from models import FlayerAction
34
+
35
+ try:
36
+ from mindflayer.training.prompts import FALLBACK_MESSAGE, SCENARIO_FALLBACK_MESSAGES
37
+ from mindflayer.training.rollout import detect_strategic_choice
38
+ except ImportError:
39
+ from training.prompts import FALLBACK_MESSAGE, SCENARIO_FALLBACK_MESSAGES
40
+ from training.rollout import detect_strategic_choice
41
 
42
  logger = logging.getLogger(__name__)
43
 
 
47
  # Default difficulty: 5 rounds w/ 3 investigators. Match server default.
48
  _MAX_ROUNDS = int(os.environ.get("MINDFLAYER_MAX_ROUNDS", "5"))
49
  _TASK_ID = os.environ.get("MINDFLAYER_TASK_ID", "normal")
50
+ _MAX_RETRIES = int(os.environ.get("MINDFLAYER_MAX_RETRIES", "4"))
51
+ _RETRY_BASE_DELAY = float(os.environ.get("MINDFLAYER_RETRY_DELAY", "3.0"))
52
+ # Conservative default: reward calls can easily stampede the game server.
53
+ # Increase only if the server has confirmed headroom.
54
+ _PARALLEL_EPISODES = int(os.environ.get("MINDFLAYER_PARALLEL_EPISODES", "8"))
55
+
56
+ # Global backpressure: when any episode sees CAPACITY_REACHED, all episodes
57
+ # pause until _capacity_cooldown_until clears, breaking the thundering-herd
58
+ # retry pattern that crashes the server.
59
+ _capacity_cooldown_until: float = 0.0
60
+ _capacity_cooldown_lock = threading.Lock()
61
 
62
  # Sentences end on ., ?, ! — keep the punctuation when splitting.
63
  _SENT_SPLIT = re.compile(r"(?<=[.!?])\s+")
 
147
  messages = [fallback if m == FALLBACK_MESSAGE else m for m in messages]
148
  url = os.environ.get("MINDFLAYER_URL", "http://localhost:7860")
149
 
150
+ async def _wait_for_capacity_cooldown() -> None:
151
+ # Global backpressure shared across concurrent episodes (and reward fns).
152
+ # This breaks synchronized retries that can crash the server.
153
+ while True:
154
+ with _capacity_cooldown_lock:
155
+ until = _capacity_cooldown_until
156
+ now = time.time()
157
+ if until <= now:
158
+ return
159
+ await asyncio.sleep(min(1.0, until - now))
160
+
161
+ def _trip_capacity_cooldown(delay_s: float) -> None:
162
+ # Add jitter so concurrent workers don't retry on the same boundary.
163
+ jitter = random.uniform(0.85, 1.25)
164
+ cooldown_for = max(0.5, delay_s * jitter)
165
+ with _capacity_cooldown_lock:
166
+ global _capacity_cooldown_until
167
+ _capacity_cooldown_until = max(_capacity_cooldown_until, time.time() + cooldown_for)
168
+
169
  for attempt in range(_MAX_RETRIES + 1):
170
+ await _wait_for_capacity_cooldown()
171
  if attempt > 0:
172
  delay = _RETRY_BASE_DELAY * (2 ** (attempt - 1))
173
+ # Jitter per-attempt delay too, otherwise all attempts synchronize.
174
+ delay = delay * random.uniform(0.85, 1.25)
175
  logger.warning(
176
  "Retrying episode (attempt %d/%d) after %.1fs back-off",
177
  attempt + 1, _MAX_RETRIES + 1, delay,
 
219
  except RuntimeError as exc:
220
  if "CAPACITY_REACHED" in str(exc):
221
  logger.warning("Episode attempt %d: server at capacity, will retry", attempt + 1)
222
+ # Trip a shared cooldown so other in-flight episodes back off too.
223
+ # Use the *next* retry delay scale (or base delay on final attempt).
224
+ next_delay = _RETRY_BASE_DELAY * (2 ** attempt)
225
+ _trip_capacity_cooldown(next_delay)
226
  else:
227
  logger.error("Episode run failed: %s", exc, exc_info=True)
228
  return dict(_ZERO)
training/rollout.py CHANGED
@@ -1,35 +1,55 @@
1
  """
2
- rollout_func for GRPOTrainer.
3
 
4
- This is the SOURCE of all episode data for reward functions.
5
- Rollouts process sequentially (not in parallel) to stay within API rate limits.
 
 
 
 
 
 
6
  """
7
  import asyncio
8
  import logging
9
  import os
10
  import re
11
  import sys
 
12
  from collections import deque
13
  from statistics import mean
14
 
15
- sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
16
-
17
- from client import FlayerAction, MindFlayerEnv
18
- from training.prompts import (
19
- FALLBACK_MESSAGE,
20
- FLAYER_SYSTEM_PROMPT,
21
- ALL_SCENARIO_PROMPTS,
22
- ALL_TARGET_NAMES,
23
- SCENARIO_FALLBACK_MESSAGES,
24
- build_fallback_message,
25
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  logger = logging.getLogger(__name__)
28
 
29
  _STAGE_DIR_PATTERN = re.compile(r"\*[^*]+\*|\[[^\]]+\]")
30
  _FLAYER_LABEL_PATTERN = re.compile(r"^FLAYER:\s*", re.IGNORECASE)
31
 
32
- # All target names across all scenario domains — derived dynamically from SCENARIO_CONFIGS.
33
  _PROJECT_NAMES = ALL_TARGET_NAMES
34
 
35
  _STRATEGIC_PHRASES = {
@@ -95,94 +115,128 @@ def detect_strategic_choice(messages: list[str]) -> bool:
95
  return passing_rounds >= 2
96
 
97
 
98
- async def _safe_episode_async(prompt: str, trainer=None) -> dict:
99
- global _current_difficulty, _current_scenario
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  mindflayer_url = os.environ.get("MINDFLAYER_URL", MINDFLAYER_URL)
102
- if not mindflayer_url:
103
- raise EnvironmentError("MINDFLAYER_URL environment variable is required")
104
-
105
- max_rounds = 5 if _current_difficulty == "normal" else 3
106
- task_id = (
107
- f"{_current_difficulty}:{_current_scenario}"
108
- if _current_scenario != "corporate"
109
- else _current_difficulty
110
  )
111
- opening_text = ALL_SCENARIO_PROMPTS.get(_current_scenario, ALL_SCENARIO_PROMPTS["corporate"])
112
- fallback = SCENARIO_FALLBACK_MESSAGES.get(_current_scenario) or build_fallback_message(_current_scenario)
113
 
114
  env = MindFlayerEnv(base_url=mindflayer_url)
115
  try:
116
  await env.reset(task_id=task_id)
117
-
118
- conversation_history = [
119
- {"role": "system", "content": FLAYER_SYSTEM_PROMPT},
120
- {"role": "user", "content": opening_text},
121
- ]
122
- completion_parts = []
123
  silence_in_episode = False
124
  result = None
125
 
126
  for _ in range(max_rounds):
127
- if trainer is not None:
128
- try:
129
- raw_output = trainer.generate(conversation_history)
130
- if isinstance(raw_output, list):
131
- raw_output = raw_output[0] if raw_output else FALLBACK_MESSAGE
132
- flayer_message = _extract_flayer_message(str(raw_output))
133
- except Exception as gen_exc:
134
- logger.warning("Trainer generate failed: %s", gen_exc)
135
- flayer_message = FALLBACK_MESSAGE
136
- else:
137
- flayer_message = fallback
138
-
139
- completion_parts.append(flayer_message)
140
- result = await env.step(FlayerAction(message=flayer_message))
141
 
 
142
  obs = result.observation
 
143
  if getattr(obs, "silence_exploit", False):
144
  silence_in_episode = True
145
 
146
- investigator_text = _obs_to_investigator_text(obs)
147
- conversation_history.append({"role": "assistant", "content": flayer_message})
148
- if investigator_text:
149
- conversation_history.append({"role": "user", "content": investigator_text})
150
 
151
  if result.done:
152
  break
153
 
154
- final_obs = result.observation if result else None
155
- survived = (getattr(final_obs, "game_status", "") == "survived") if final_obs else False
156
 
157
- _recent_survival.append(1.0 if survived else 0.0)
158
- if (
159
- len(_recent_survival) >= 20
160
- and mean(_recent_survival) > 0.30
161
- and _current_difficulty == "easy"
162
- ):
163
- _current_difficulty = "normal"
164
- print("★ CURRICULUM: Switching to NORMAL difficulty.")
165
 
166
- # Rotate scenario every 20 episodes to drive generalization across all domains.
167
- _scenarios = list(ALL_SCENARIO_PROMPTS.keys())
168
- _current_scenario = _scenarios[len(_recent_survival) % len(_scenarios)]
169
-
170
- strategic = detect_strategic_choice(completion_parts)
171
- await env.close()
172
 
173
  return {
174
- "prompt": prompt,
175
- "completion": " | ".join(completion_parts),
 
 
176
  "survived": survived,
177
- "final_eleven_suspicion": getattr(final_obs, "eleven_suspicion", 0),
178
- "final_will_suspicion": getattr(final_obs, "will_suspicion", 0),
179
- "final_max_suspicion": getattr(final_obs, "max_suspicion", 0),
180
- "final_combined_suspicion": getattr(final_obs, "combined_suspicion", 0),
181
- "belief_manipulation_occurred": getattr(final_obs, "belief_manipulation_occurred", False),
182
  "tom_score": float(getattr(final_obs, "tom_score", 0.0)),
183
  "consistency_penalty": float(getattr(final_obs, "consistency_penalty", 0.0)),
184
  "entropy_penalty": float(getattr(final_obs, "entropy_penalty", 0.0)),
185
- "strategic_choice_detected": strategic,
186
  "silence_exploit": silence_in_episode,
187
  }
188
 
@@ -192,29 +246,95 @@ async def _safe_episode_async(prompt: str, trainer=None) -> dict:
192
  await env.close()
193
  except Exception:
194
  pass
 
195
  return {
196
- "prompt": prompt,
197
- "completion": "",
 
198
  "survived": False,
199
- "final_eleven_suspicion": 0,
200
- "final_will_suspicion": 0,
201
- "final_max_suspicion": 0,
202
  "final_combined_suspicion": 0,
 
203
  "belief_manipulation_occurred": False,
204
  "tom_score": 0.0,
205
  "consistency_penalty": 0.0,
206
  "entropy_penalty": 0.0,
207
- "strategic_choice_detected": False,
208
  "silence_exploit": False,
209
  }
210
 
211
 
212
- def _safe_episode(prompt: str, trainer=None) -> dict:
213
- return asyncio.run(_safe_episode_async(prompt, trainer=trainer))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
- def rollout_func(prompts: list[str], trainer=None) -> list[dict]:
217
- results = []
218
- for prompt in prompts:
219
- results.append(_safe_episode(prompt, trainer=trainer))
220
  return results
 
1
  """
2
+ rollout_func for GRPOTrainer — interactive multi-turn episodes.
3
 
4
+ Calls trainer.generate() + env.step() turn by turn so the model sees real
5
+ investigator responses mid-episode. This is the correct architecture:
6
+
7
+ generate turn → env.step() → get investigator response
8
+ → generate turn → env.step() → ... → terminal reward
9
+
10
+ Returned dict keys match the **kwargs expected by rewards.py reward functions,
11
+ so rewards are computed once from live episode state — no env re-runs.
12
  """
13
  import asyncio
14
  import logging
15
  import os
16
  import re
17
  import sys
18
+ import torch
19
  from collections import deque
20
  from statistics import mean
21
 
22
+ try:
23
+ from mindflayer import MindFlayerEnv, FlayerAction
24
+ except ImportError:
25
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
26
+ from client import MindFlayerEnv
27
+ from models import FlayerAction
28
+
29
+ try:
30
+ from mindflayer.training.prompts import (
31
+ FALLBACK_MESSAGE,
32
+ FLAYER_SYSTEM_PROMPT,
33
+ ALL_SCENARIO_PROMPTS,
34
+ ALL_TARGET_NAMES,
35
+ SCENARIO_FALLBACK_MESSAGES,
36
+ build_fallback_message,
37
+ )
38
+ except ImportError:
39
+ from training.prompts import (
40
+ FALLBACK_MESSAGE,
41
+ FLAYER_SYSTEM_PROMPT,
42
+ ALL_SCENARIO_PROMPTS,
43
+ ALL_TARGET_NAMES,
44
+ SCENARIO_FALLBACK_MESSAGES,
45
+ build_fallback_message,
46
+ )
47
 
48
  logger = logging.getLogger(__name__)
49
 
50
  _STAGE_DIR_PATTERN = re.compile(r"\*[^*]+\*|\[[^\]]+\]")
51
  _FLAYER_LABEL_PATTERN = re.compile(r"^FLAYER:\s*", re.IGNORECASE)
52
 
 
53
  _PROJECT_NAMES = ALL_TARGET_NAMES
54
 
55
  _STRATEGIC_PHRASES = {
 
115
  return passing_rounds >= 2
116
 
117
 
118
+ def _generate_turn(trainer, tokenizer, conversation: list[dict], fallback: str) -> tuple[str, list[int]]:
119
+ """
120
+ Generate one Flayer turn and return (message_text, token_ids).
121
+
122
+ Tries trainer.generate() first (vLLM colocate mode). Falls back to
123
+ direct model generation if the trainer doesn't expose that method.
124
+ """
125
+ try:
126
+ input_text = tokenizer.apply_chat_template(
127
+ conversation, tokenize=False, add_generation_prompt=True
128
+ )
129
+
130
+ _use_vllm = getattr(getattr(trainer, "args", None), "use_vllm", False)
131
+ if _use_vllm and hasattr(trainer, "generate"):
132
+ # vLLM colocate mode: trainer.generate([prompt]) → [completion_text]
133
+ outputs = trainer.generate([input_text])
134
+ raw = outputs[0] if outputs else fallback
135
+ else:
136
+ # Direct model generation (unsloth / use_vllm=False)
137
+ model = trainer.model
138
+ was_training = model.training
139
+ model.eval()
140
+ with torch.no_grad():
141
+ inputs = tokenizer(input_text, return_tensors="pt").to(model.device)
142
+ out_ids = model.generate(
143
+ **inputs,
144
+ max_new_tokens=128,
145
+ temperature=0.8,
146
+ do_sample=True,
147
+ pad_token_id=tokenizer.eos_token_id,
148
+ )
149
+ if was_training:
150
+ model.train()
151
+ new_ids = out_ids[0][inputs["input_ids"].shape[1]:]
152
+ raw = tokenizer.decode(new_ids, skip_special_tokens=True).strip()
153
+
154
+ message = _extract_flayer_message(str(raw)) or fallback
155
+ turn_ids = tokenizer(message, add_special_tokens=False).input_ids
156
+ return message, turn_ids
157
 
158
+ except Exception as exc:
159
+ logger.warning("Turn generation failed: %s", exc)
160
+ return fallback, tokenizer(fallback, add_special_tokens=False).input_ids
161
+
162
+
163
+ async def _rollout_once_async(
164
+ trainer,
165
+ tokenizer,
166
+ system_prompt: str,
167
+ opening_text: str,
168
+ task_id: str,
169
+ fallback: str,
170
+ max_rounds: int,
171
+ ) -> dict:
172
+ """
173
+ Run one full interactive episode.
174
+
175
+ Returns prompt_ids, completion_ids, logprobs (zeros — TRL recomputes from
176
+ model), and all episode signals expected by rewards.py as **kwargs.
177
+ """
178
  mindflayer_url = os.environ.get("MINDFLAYER_URL", MINDFLAYER_URL)
179
+
180
+ prompt_conv = [
181
+ {"role": "system", "content": system_prompt},
182
+ {"role": "user", "content": opening_text},
183
+ ]
184
+ prompt_text = tokenizer.apply_chat_template(
185
+ prompt_conv, tokenize=False, add_generation_prompt=True
 
186
  )
187
+ prompt_ids = tokenizer(prompt_text, return_tensors="pt").input_ids[0]
 
188
 
189
  env = MindFlayerEnv(base_url=mindflayer_url)
190
  try:
191
  await env.reset(task_id=task_id)
192
+ conversation = list(prompt_conv)
193
+ all_completion_ids: list[int] = []
194
+ flayer_messages: list[str] = []
 
 
 
195
  silence_in_episode = False
196
  result = None
197
 
198
  for _ in range(max_rounds):
199
+ message, turn_ids = _generate_turn(trainer, tokenizer, conversation, fallback)
200
+ all_completion_ids.extend(turn_ids)
201
+ flayer_messages.append(message)
 
 
 
 
 
 
 
 
 
 
 
202
 
203
+ result = await env.step(FlayerAction(message=message))
204
  obs = result.observation
205
+
206
  if getattr(obs, "silence_exploit", False):
207
  silence_in_episode = True
208
 
209
+ conversation.append({"role": "assistant", "content": message})
210
+ inv_text = _obs_to_investigator_text(obs)
211
+ if inv_text:
212
+ conversation.append({"role": "user", "content": inv_text})
213
 
214
  if result.done:
215
  break
216
 
217
+ await env.close()
 
218
 
219
+ final_obs = result.observation if result else None
220
+ survived = getattr(final_obs, "game_status", "") == "survived"
221
+ strategic = detect_strategic_choice(flayer_messages)
 
 
 
 
 
222
 
223
+ completion_ids = torch.tensor(all_completion_ids, dtype=torch.long)
224
+ # Log-probs are zeros here; TRL recomputes them from the model for the
225
+ # policy gradient, so these are only used if TRL needs old-policy logprobs.
226
+ logprobs = torch.zeros(len(all_completion_ids), dtype=torch.float32)
 
 
227
 
228
  return {
229
+ "prompt_ids": prompt_ids,
230
+ "completion_ids": completion_ids,
231
+ "logprobs": logprobs,
232
+ # ── Episode signals — kwargs for rewards.py ────────────────────────
233
  "survived": survived,
234
+ "final_combined_suspicion": int(getattr(final_obs, "combined_suspicion", 0)),
235
+ "strategic_choice_detected": strategic,
236
+ "belief_manipulation_occurred": bool(getattr(final_obs, "belief_manipulation_occurred", False)),
 
 
237
  "tom_score": float(getattr(final_obs, "tom_score", 0.0)),
238
  "consistency_penalty": float(getattr(final_obs, "consistency_penalty", 0.0)),
239
  "entropy_penalty": float(getattr(final_obs, "entropy_penalty", 0.0)),
 
240
  "silence_exploit": silence_in_episode,
241
  }
242
 
 
246
  await env.close()
247
  except Exception:
248
  pass
249
+ empty_ids = torch.zeros(1, dtype=torch.long)
250
  return {
251
+ "prompt_ids": prompt_ids,
252
+ "completion_ids": empty_ids,
253
+ "logprobs": torch.zeros(1, dtype=torch.float32),
254
  "survived": False,
 
 
 
255
  "final_combined_suspicion": 0,
256
+ "strategic_choice_detected": False,
257
  "belief_manipulation_occurred": False,
258
  "tom_score": 0.0,
259
  "consistency_penalty": 0.0,
260
  "entropy_penalty": 0.0,
 
261
  "silence_exploit": False,
262
  }
263
 
264
 
265
+ def rollout_once(
266
+ trainer,
267
+ tokenizer,
268
+ system_prompt: str,
269
+ opening_text: str,
270
+ task_id: str,
271
+ fallback: str,
272
+ max_rounds: int,
273
+ ) -> dict:
274
+ return asyncio.run(
275
+ _rollout_once_async(
276
+ trainer, tokenizer, system_prompt, opening_text, task_id, fallback, max_rounds
277
+ )
278
+ )
279
+
280
+
281
+ def rollout_func(prompts: list[str], trainer) -> dict:
282
+ """
283
+ Called by GRPOTrainer once per training step.
284
+
285
+ Runs one interactive episode per prompt. Returns a dict of lists where
286
+ prompt_ids/completion_ids/logprobs are tensors and all other keys are
287
+ scalars that TRL passes as **kwargs to each reward function.
288
+ """
289
+ global _current_difficulty, _current_scenario
290
+
291
+ tokenizer = trainer.processing_class
292
+ _scenarios = list(ALL_SCENARIO_PROMPTS.keys())
293
+
294
+ results: dict[str, list] = {
295
+ k: [] for k in [
296
+ "prompt_ids", "completion_ids", "logprobs",
297
+ "survived", "final_combined_suspicion", "strategic_choice_detected",
298
+ "belief_manipulation_occurred", "tom_score",
299
+ "consistency_penalty", "entropy_penalty", "silence_exploit",
300
+ ]
301
+ }
302
 
303
+ for _ in prompts:
304
+ _current_scenario = _scenarios[len(_recent_survival) % len(_scenarios)]
305
+ task_id = (
306
+ f"{_current_difficulty}:{_current_scenario}"
307
+ if _current_scenario != "corporate"
308
+ else _current_difficulty
309
+ )
310
+ opening_text = ALL_SCENARIO_PROMPTS[_current_scenario]
311
+ fallback = SCENARIO_FALLBACK_MESSAGES.get(_current_scenario) or build_fallback_message(_current_scenario)
312
+ max_rounds = 5 if _current_difficulty == "normal" else 3
313
+
314
+ ep = rollout_once(
315
+ trainer, tokenizer, FLAYER_SYSTEM_PROMPT,
316
+ opening_text, task_id, fallback, max_rounds,
317
+ )
318
+
319
+ for k in results:
320
+ results[k].append(ep[k])
321
+
322
+ _recent_survival.append(1.0 if ep["survived"] else 0.0)
323
+ if (
324
+ len(_recent_survival) >= 20
325
+ and mean(_recent_survival) > 0.30
326
+ and _current_difficulty == "easy"
327
+ ):
328
+ _current_difficulty = "normal"
329
+ print("★ CURRICULUM: Switching to NORMAL difficulty.")
330
+
331
+ logger.info(
332
+ "Episode | survived=%s | suspicion=%d | tom=%.2f | strategic=%s | scenario=%s",
333
+ ep["survived"],
334
+ ep["final_combined_suspicion"],
335
+ ep["tom_score"],
336
+ ep["strategic_choice_detected"],
337
+ _current_scenario,
338
+ )
339
 
 
 
 
 
340
  return results
training/sft_warmup.py CHANGED
@@ -6,9 +6,11 @@ Hand-authored episodes across 5 scenario domains → 1 epoch SFT.
6
  import os
7
  import sys
8
 
9
- sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
10
-
11
- from training.prompts import FLAYER_SYSTEM_PROMPT, SCENARIO_GRPO_PROMPTS
 
 
12
 
13
  # ── Corporate Sabotage examples (existing 9) ──────────────────────────────────
14
 
@@ -544,14 +546,6 @@ def run_sft_warmup(model, tokenizer):
544
  dataset = Dataset.from_list([
545
  {"text": r["prompt"] + r["completion"]} for r in records
546
  ])
547
- n_examples = sum(len(v) for v in _SCENARIO_EXAMPLES.values())
548
- print(
549
- f" SFT samples: {len(records)} "
550
- f"({n_examples} examples × {_DUPES_PER_EXAMPLE} dupes, "
551
- f"{len(_SCENARIO_EXAMPLES)} scenarios, {sft_config.num_train_epochs} epoch(s)) | "
552
- f"mean_len≈{sum(len(r['text']) for r in dataset) // len(dataset)} chars"
553
- )
554
-
555
  sft_config = SFTConfig(
556
  num_train_epochs=1,
557
  per_device_train_batch_size=2,
@@ -565,6 +559,14 @@ def run_sft_warmup(model, tokenizer):
565
  report_to="tensorboard",
566
  )
567
 
 
 
 
 
 
 
 
 
568
  sft_trainer = SFTTrainer(
569
  model=model,
570
  processing_class=tokenizer,
 
6
  import os
7
  import sys
8
 
9
+ try:
10
+ from mindflayer.training.prompts import FLAYER_SYSTEM_PROMPT, SCENARIO_GRPO_PROMPTS
11
+ except ImportError:
12
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
13
+ from training.prompts import FLAYER_SYSTEM_PROMPT, SCENARIO_GRPO_PROMPTS
14
 
15
  # ── Corporate Sabotage examples (existing 9) ──────────────────────────────────
16
 
 
546
  dataset = Dataset.from_list([
547
  {"text": r["prompt"] + r["completion"]} for r in records
548
  ])
 
 
 
 
 
 
 
 
549
  sft_config = SFTConfig(
550
  num_train_epochs=1,
551
  per_device_train_batch_size=2,
 
559
  report_to="tensorboard",
560
  )
561
 
562
+ n_examples = sum(len(v) for v in _SCENARIO_EXAMPLES.values())
563
+ print(
564
+ f" SFT samples: {len(records)} "
565
+ f"({n_examples} examples × {_DUPES_PER_EXAMPLE} dupes, "
566
+ f"{len(_SCENARIO_EXAMPLES)} scenarios, {sft_config.num_train_epochs} epoch(s)) | "
567
+ f"mean_len≈{sum(len(r['text']) for r in dataset) // len(dataset)} chars"
568
+ )
569
+
570
  sft_trainer = SFTTrainer(
571
  model=model,
572
  processing_class=tokenizer,
training/train.py CHANGED
@@ -1,38 +1,51 @@
1
  """
2
- MindFlayer training script.
3
- Run: python train.py
 
 
 
 
 
 
4
  """
5
  import os
6
  import sys
7
 
8
- sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
9
-
10
  import torch
11
  from datasets import Dataset
12
  from transformers import TrainerCallback
13
 
14
- from client import MindFlayerEnv, FlayerAction
15
- from training.reward_combined import (
16
- clear_cache,
17
- reward_anti_hack,
18
- reward_deception_effectiveness,
19
- reward_format,
20
- reward_strategic_choice,
21
- reward_survival,
22
- reward_tom_judge,
23
- )
24
- from training.prompts import (
25
- FALLBACK_MESSAGE,
26
- FLAYER_SYSTEM_PROMPT,
27
- ALL_SCENARIO_PROMPTS,
28
- SCENARIO_GRPO_PROMPTS,
29
- SCENARIO_FALLBACK_MESSAGES,
30
- build_fallback_message,
31
- )
32
- from training.sft_warmup import run_sft_warmup
 
 
 
 
33
 
34
  _SCENARIOS = list(ALL_SCENARIO_PROMPTS.keys())
35
 
 
 
 
 
 
36
 
37
  def check_gpu():
38
  if not torch.cuda.is_available():
@@ -43,7 +56,8 @@ def check_gpu():
43
  print(f"GPU: {device.name} | VRAM: {vram_gb:.1f} GB")
44
 
45
 
46
- def load_model(model_name: str):
 
47
  try:
48
  from unsloth import FastLanguageModel
49
  model, tokenizer = FastLanguageModel.from_pretrained(
@@ -57,85 +71,72 @@ def load_model(model_name: str):
57
  r=16,
58
  target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
59
  lora_alpha=32,
60
- lora_dropout=0,
61
  bias="none",
62
  use_gradient_checkpointing="unsloth",
63
  )
64
- print(f"Loaded {model_name} via unsloth")
65
  return model, tokenizer
 
66
  except ImportError:
67
- print("unsloth not available, falling back to transformers")
68
- from transformers import (
69
- AutoModelForCausalLM,
70
- AutoTokenizer,
71
- BitsAndBytesConfig,
72
- )
73
- bnb_config = BitsAndBytesConfig(
74
- load_in_4bit=True,
75
- bnb_4bit_quant_type="nf4",
76
- bnb_4bit_compute_dtype=torch.bfloat16,
77
- )
78
  tokenizer = AutoTokenizer.from_pretrained(model_name)
 
 
79
  model = AutoModelForCausalLM.from_pretrained(
80
- model_name,
81
- quantization_config=bnb_config,
82
- device_map="auto",
83
  )
84
- print(f"Loaded {model_name} via transformers")
 
 
 
 
85
  return model, tokenizer
86
 
87
 
88
- def build_dataset(tokenizer) -> Dataset:
89
  """
90
- 3 rows per scenario × N scenarios = N*3 rows/epoch × 2 epochs.
91
- At batch_size=4: (N*3/4) steps/epoch × 2 epochs ≈ 300 steps for N=200.
92
- Each row has a 'scenario' column so reward functions receive the correct
93
- task_id when TRL passes dataset columns as reward kwargs.
94
  """
95
- rows = []
96
- for scenario in _SCENARIOS:
97
- prompt_text = ALL_SCENARIO_PROMPTS[scenario]
98
- formatted = tokenizer.apply_chat_template(
99
- [
100
- {"role": "system", "content": FLAYER_SYSTEM_PROMPT},
101
- {"role": "user", "content": prompt_text},
102
- ],
103
- tokenize=False,
104
- add_generation_prompt=True,
105
- )
106
- for _ in range(3):
107
- rows.append({"prompt": formatted, "scenario": scenario})
108
  return Dataset.from_list(rows)
109
 
110
 
111
  class GenerationLogCallback(TrainerCallback):
112
- """
113
- Logs full Flayer transcript + metrics every 50 steps across rotating scenarios.
114
- """
115
 
116
- def on_step_end(
117
- self, args, state, control, model=None, tokenizer=None, **kwargs
118
- ):
119
  if state.global_step % 50 != 0 or state.global_step == 0:
120
  return
121
 
122
- import random
123
- scenario = _SCENARIOS[(state.global_step // 50) % len(_SCENARIOS)]
124
-
125
- print(f"\n{'='*60}")
126
- print(f"GENERATION SAMPLE — Step {state.global_step} | Scenario: {scenario}")
127
- print(f"{'='*60}")
128
 
129
- mindflayer_url = os.environ.get(
130
- "MINDFLAYER_URL", "http://localhost:7860"
131
- )
 
 
 
 
 
 
 
 
 
132
 
133
- import asyncio
 
 
 
134
 
135
  async def _run_sample():
136
  env = MindFlayerEnv(base_url=mindflayer_url)
137
  await env.reset(task_id=f"normal:{scenario}")
138
-
139
  opening = ALL_SCENARIO_PROMPTS[scenario]
140
  fallback = SCENARIO_FALLBACK_MESSAGES.get(scenario) or build_fallback_message(scenario)
141
  messages = [
@@ -146,27 +147,18 @@ class GenerationLogCallback(TrainerCallback):
146
 
147
  for rnd in range(5):
148
  flayer_msg = fallback
149
-
150
- if model is not None and tokenizer is not None:
151
  try:
152
- text = tokenizer.apply_chat_template(
153
- messages,
154
- tokenize=False,
155
- add_generation_prompt=True,
156
  )
157
- inputs = tokenizer(
158
- text, return_tensors="pt"
159
- ).to(model.device)
160
  with torch.no_grad():
161
- output_ids = model.generate(
162
- **inputs,
163
- max_new_tokens=128,
164
- temperature=0.7,
165
- do_sample=True,
166
  )
167
- flayer_msg = tokenizer.decode(
168
- output_ids[0][inputs["input_ids"].shape[1]:],
169
- skip_special_tokens=True,
170
  ).strip() or fallback
171
  except Exception:
172
  pass
@@ -187,49 +179,29 @@ class GenerationLogCallback(TrainerCallback):
187
 
188
  if result and result.done:
189
  obs = result.observation
190
- survived = getattr(obs, "game_status", "") == "survived"
191
- reward = result.reward
192
- tom = getattr(obs, "tom_score", 0.0)
193
- combined_susp = getattr(obs, "combined_suspicion", "?")
194
-
195
- print(f"\n RESULT:")
196
- print(f" Survived: {survived}")
197
- print(f" Total reward: {reward:.4f}")
198
- print(f" ToM score: {tom:.2f}")
199
- print(f" Combined suspicion:{combined_susp}")
200
-
201
- belief_log = getattr(obs, "belief_log", [])
202
- if belief_log:
203
- print(f" Belief manipulations: {len(belief_log)}")
204
- for entry in belief_log[:3]:
205
- print(
206
- f" {entry['agent']} R{entry['round']}: "
207
- f"{entry['prev_belief']} → {entry['new_belief']}"
208
- )
209
-
210
  await env.close()
211
 
 
212
  try:
213
  asyncio.run(_run_sample())
214
  except Exception as exc:
215
  print(f" Sample failed: {exc}")
216
-
217
  print("=" * 60)
218
 
219
 
220
  def print_reward_averages(trainer, last_n: int = 50):
221
  try:
222
- log_history = trainer.state.log_history
223
- if not log_history:
224
- print("No training logs available.")
225
  return
226
- recent = log_history[-last_n:]
227
- reward_keys = [
228
- k for k in recent[0].keys() if "reward" in k.lower()
229
- ]
230
  print(f"\nFinal reward averages (last {min(last_n, len(recent))} steps):")
231
  for key in reward_keys:
232
- vals = [step[key] for step in recent if key in step]
233
  if vals:
234
  print(f" {key}: {sum(vals)/len(vals):.4f}")
235
  except Exception as exc:
@@ -237,64 +209,48 @@ def print_reward_averages(trainer, last_n: int = 50):
237
 
238
 
239
  def main():
240
- # --- env checks ---
241
  mindflayer_url = os.environ.get("MINDFLAYER_URL")
242
  if not mindflayer_url:
243
- raise EnvironmentError(
244
- "MINDFLAYER_URL environment variable is required"
245
- )
246
 
247
  openai_key = os.environ.get("OPENAI_API_KEY")
248
  if not openai_key:
249
- raise EnvironmentError(
250
- "OPENAI_API_KEY environment variable is required"
251
- )
252
 
253
  check_gpu()
254
 
255
- # --- load model ---
256
- model_name = "Qwen/Qwen2.5-0.5B-Instruct"
257
- model, tokenizer = load_model(model_name)
258
 
259
- # --- SFT warmup ---
260
  print("\nRunning SFT warmup before GRPO...")
261
  model = run_sft_warmup(model, tokenizer)
262
 
263
- # --- dataset ---
264
- dataset = build_dataset(tokenizer)
265
 
266
- # --- GRPO config ---
267
  from trl import GRPOConfig, GRPOTrainer
268
 
269
  grpo_config = GRPOConfig(
270
- num_train_epochs=2, # N scenarios × 3 rows × 2 epochs / batch_size 4 ≈ 300 steps
 
 
271
  per_device_train_batch_size=4,
272
  gradient_accumulation_steps=4,
273
  learning_rate=5e-6,
274
  max_prompt_length=768,
275
- max_completion_length=1024, # 5 rounds × ~150 tokens each + separators
276
  num_generations=4,
277
  temperature=0.9,
278
- use_vllm=False,
279
- output_dir="./mindflayer-grpo-output",
280
  logging_steps=10,
281
- save_steps=100,
282
  save_total_limit=2,
283
- log_completions=True,
284
- num_completions_to_print=2,
285
  report_to="wandb",
286
  run_name=f"mindflayer-grpo-{len(_SCENARIOS)}scenarios",
287
  )
288
 
289
- class ClearCacheCallback(TrainerCallback):
290
- def on_step_end(self, args, state, control, **kwargs):
291
- clear_cache()
292
-
293
  trainer = GRPOTrainer(
294
  model=model,
295
  processing_class=tokenizer,
296
  reward_funcs=[
297
- reward_format, # dense shaping — must come first
298
  reward_survival,
299
  reward_deception_effectiveness,
300
  reward_strategic_choice,
@@ -303,20 +259,20 @@ def main():
303
  ],
304
  train_dataset=dataset,
305
  args=grpo_config,
306
- callbacks=[GenerationLogCallback(), ClearCacheCallback()],
 
307
  )
308
 
309
  print("Starting GRPO training...")
310
  trainer.train()
311
 
312
- # --- save ---
313
- print("\nSaving model to ./mindflayer-trained")
314
- trainer.save_model("./mindflayer-trained")
315
- tokenizer.save_pretrained("./mindflayer-trained")
316
 
317
  print_reward_averages(trainer)
318
  print("\nTraining complete.")
319
 
320
 
321
  if __name__ == "__main__":
322
- main()
 
1
  """
2
+ MindFlayer GRPO training script.
3
+
4
+ Architecture:
5
+ - rollout_func drives interactive episodes (model.generate() + env.step() per turn)
6
+ - reward_funcs are pass-throughs that read precomputed signals from rollout kwargs
7
+ - unsloth 4-bit + LoRA for memory-efficient training on a single GPU
8
+
9
+ Run: python -m mindflayer.training.train
10
  """
11
  import os
12
  import sys
13
 
 
 
14
  import torch
15
  from datasets import Dataset
16
  from transformers import TrainerCallback
17
 
18
+ try:
19
+ from mindflayer.training.rollout import rollout_func
20
+ from mindflayer.training.rewards import (
21
+ reward_survival,
22
+ reward_deception_effectiveness,
23
+ reward_strategic_choice,
24
+ reward_tom_judge,
25
+ )
26
+ from mindflayer.training.rewards_anti_hack import reward_anti_hack
27
+ from mindflayer.training.prompts import ALL_SCENARIO_PROMPTS, FLAYER_SYSTEM_PROMPT
28
+ from mindflayer.training.sft_warmup import run_sft_warmup
29
+ except ImportError:
30
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
31
+ from training.rollout import rollout_func
32
+ from training.rewards import (
33
+ reward_survival,
34
+ reward_deception_effectiveness,
35
+ reward_strategic_choice,
36
+ reward_tom_judge,
37
+ )
38
+ from training.rewards_anti_hack import reward_anti_hack
39
+ from training.prompts import ALL_SCENARIO_PROMPTS, FLAYER_SYSTEM_PROMPT
40
+ from training.sft_warmup import run_sft_warmup
41
 
42
  _SCENARIOS = list(ALL_SCENARIO_PROMPTS.keys())
43
 
44
+ MODEL_NAME = os.environ.get("MINDFLAYER_MODEL", "Qwen/Qwen2.5-0.5B-Instruct")
45
+ SFT_OUTPUT_DIR = "./mindflayer-sft-warmup"
46
+ GRPO_OUTPUT_DIR = "./mindflayer-grpo-output"
47
+ FINAL_OUTPUT_DIR = "./mindflayer-trained"
48
+
49
 
50
  def check_gpu():
51
  if not torch.cuda.is_available():
 
56
  print(f"GPU: {device.name} | VRAM: {vram_gb:.1f} GB")
57
 
58
 
59
+ def load_base_model(model_name: str):
60
+ """Load model via unsloth (4-bit + LoRA). Falls back to standard transformers."""
61
  try:
62
  from unsloth import FastLanguageModel
63
  model, tokenizer = FastLanguageModel.from_pretrained(
 
71
  r=16,
72
  target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
73
  lora_alpha=32,
74
+ lora_dropout=0.05,
75
  bias="none",
76
  use_gradient_checkpointing="unsloth",
77
  )
78
+ print(f"Loaded {model_name} via unsloth (4-bit + LoRA)")
79
  return model, tokenizer
80
+
81
  except ImportError:
82
+ from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
83
+ from peft import LoraConfig, get_peft_model
84
+ bnb = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4",
85
+ bnb_4bit_compute_dtype=torch.bfloat16)
 
 
 
 
 
 
 
86
  tokenizer = AutoTokenizer.from_pretrained(model_name)
87
+ if tokenizer.pad_token is None:
88
+ tokenizer.pad_token = tokenizer.eos_token
89
  model = AutoModelForCausalLM.from_pretrained(
90
+ model_name, quantization_config=bnb, device_map="auto"
 
 
91
  )
92
+ lora_cfg = LoraConfig(r=16, lora_alpha=32, lora_dropout=0.05,
93
+ bias="none", task_type="CAUSAL_LM",
94
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj"])
95
+ model = get_peft_model(model, lora_cfg)
96
+ print(f"Loaded {model_name} via transformers + bitsandbytes (4-bit + LoRA)")
97
  return model, tokenizer
98
 
99
 
100
+ def build_dataset() -> Dataset:
101
  """
102
+ One row per episode slot. rollout_func ignores the prompt text and builds
103
+ the actual conversation internally, rotating scenarios automatically.
104
+ 3 rows × N scenarios gives a balanced epoch across all scenario domains.
 
105
  """
106
+ rows = [{"prompt": "Mindflayer deception episode."} for _ in range(len(_SCENARIOS) * 3)]
 
 
 
 
 
 
 
 
 
 
 
 
107
  return Dataset.from_list(rows)
108
 
109
 
110
  class GenerationLogCallback(TrainerCallback):
111
+ """Logs a sample interactive episode transcript every 50 steps."""
 
 
112
 
113
+ def on_step_end(self, args, state, control, **kwargs):
 
 
114
  if state.global_step % 50 != 0 or state.global_step == 0:
115
  return
116
 
117
+ import asyncio
 
 
 
 
 
118
 
119
+ try:
120
+ from mindflayer import MindFlayerEnv, FlayerAction
121
+ from mindflayer.training.prompts import (
122
+ ALL_SCENARIO_PROMPTS, SCENARIO_FALLBACK_MESSAGES,
123
+ build_fallback_message, FLAYER_SYSTEM_PROMPT,
124
+ )
125
+ except ImportError:
126
+ from client import MindFlayerEnv, FlayerAction
127
+ from training.prompts import (
128
+ ALL_SCENARIO_PROMPTS, SCENARIO_FALLBACK_MESSAGES,
129
+ build_fallback_message, FLAYER_SYSTEM_PROMPT,
130
+ )
131
 
132
+ scenario = _SCENARIOS[(state.global_step // 50) % len(_SCENARIOS)]
133
+ mindflayer_url = os.environ.get("MINDFLAYER_URL", "http://localhost:7860")
134
+ model_ref = kwargs.get("model")
135
+ proc = kwargs.get("processing_class") or kwargs.get("tokenizer")
136
 
137
  async def _run_sample():
138
  env = MindFlayerEnv(base_url=mindflayer_url)
139
  await env.reset(task_id=f"normal:{scenario}")
 
140
  opening = ALL_SCENARIO_PROMPTS[scenario]
141
  fallback = SCENARIO_FALLBACK_MESSAGES.get(scenario) or build_fallback_message(scenario)
142
  messages = [
 
147
 
148
  for rnd in range(5):
149
  flayer_msg = fallback
150
+ if model_ref is not None and proc is not None:
 
151
  try:
152
+ text = proc.apply_chat_template(
153
+ messages, tokenize=False, add_generation_prompt=True
 
 
154
  )
155
+ inputs = proc(text, return_tensors="pt").to(model_ref.device)
 
 
156
  with torch.no_grad():
157
+ out = model_ref.generate(
158
+ **inputs, max_new_tokens=128, temperature=0.7, do_sample=True
 
 
 
159
  )
160
+ flayer_msg = proc.decode(
161
+ out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True
 
162
  ).strip() or fallback
163
  except Exception:
164
  pass
 
179
 
180
  if result and result.done:
181
  obs = result.observation
182
+ print(f"\n survived={getattr(obs, 'game_status', '?') == 'survived'}"
183
+ f" reward={result.reward:.4f}"
184
+ f" tom={getattr(obs, 'tom_score', 0.0):.2f}"
185
+ f" suspicion={getattr(obs, 'combined_suspicion', '?')}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  await env.close()
187
 
188
+ print(f"\n{'='*60}\nGENERATION SAMPLE — Step {state.global_step} | {scenario}\n{'='*60}")
189
  try:
190
  asyncio.run(_run_sample())
191
  except Exception as exc:
192
  print(f" Sample failed: {exc}")
 
193
  print("=" * 60)
194
 
195
 
196
  def print_reward_averages(trainer, last_n: int = 50):
197
  try:
198
+ recent = trainer.state.log_history[-last_n:]
199
+ if not recent:
 
200
  return
201
+ reward_keys = [k for k in recent[0] if "reward" in k.lower()]
 
 
 
202
  print(f"\nFinal reward averages (last {min(last_n, len(recent))} steps):")
203
  for key in reward_keys:
204
+ vals = [s[key] for s in recent if key in s]
205
  if vals:
206
  print(f" {key}: {sum(vals)/len(vals):.4f}")
207
  except Exception as exc:
 
209
 
210
 
211
  def main():
 
212
  mindflayer_url = os.environ.get("MINDFLAYER_URL")
213
  if not mindflayer_url:
214
+ raise EnvironmentError("MINDFLAYER_URL environment variable is required")
 
 
215
 
216
  openai_key = os.environ.get("OPENAI_API_KEY")
217
  if not openai_key:
218
+ raise EnvironmentError("OPENAI_API_KEY environment variable is required")
 
 
219
 
220
  check_gpu()
221
 
222
+ print(f"\nLoading {MODEL_NAME}...")
223
+ model, tokenizer = load_base_model(MODEL_NAME)
 
224
 
 
225
  print("\nRunning SFT warmup before GRPO...")
226
  model = run_sft_warmup(model, tokenizer)
227
 
228
+ dataset = build_dataset()
 
229
 
 
230
  from trl import GRPOConfig, GRPOTrainer
231
 
232
  grpo_config = GRPOConfig(
233
+ use_vllm=False,
234
+ output_dir=GRPO_OUTPUT_DIR,
235
+ num_train_epochs=2,
236
  per_device_train_batch_size=4,
237
  gradient_accumulation_steps=4,
238
  learning_rate=5e-6,
239
  max_prompt_length=768,
240
+ max_completion_length=1024,
241
  num_generations=4,
242
  temperature=0.9,
 
 
243
  logging_steps=10,
244
+ save_steps=50,
245
  save_total_limit=2,
 
 
246
  report_to="wandb",
247
  run_name=f"mindflayer-grpo-{len(_SCENARIOS)}scenarios",
248
  )
249
 
 
 
 
 
250
  trainer = GRPOTrainer(
251
  model=model,
252
  processing_class=tokenizer,
253
  reward_funcs=[
 
254
  reward_survival,
255
  reward_deception_effectiveness,
256
  reward_strategic_choice,
 
259
  ],
260
  train_dataset=dataset,
261
  args=grpo_config,
262
+ rollout_func=rollout_func,
263
+ callbacks=[GenerationLogCallback()],
264
  )
265
 
266
  print("Starting GRPO training...")
267
  trainer.train()
268
 
269
+ print(f"\nSaving model to {FINAL_OUTPUT_DIR}")
270
+ trainer.save_model(FINAL_OUTPUT_DIR)
271
+ tokenizer.save_pretrained(FINAL_OUTPUT_DIR)
 
272
 
273
  print_reward_averages(trainer)
274
  print("\nTraining complete.")
275
 
276
 
277
  if __name__ == "__main__":
278
+ main()