armaanuu commited on
Commit
9731229
·
verified ·
1 Parent(s): f790808

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +492 -1183
app.py CHANGED
@@ -3,15 +3,27 @@ import threading
3
 
4
  import torch
5
  import spaces
 
6
 
7
- from flask import Flask, jsonify, request, render_template_string
8
  from flask_cors import CORS
 
9
  from transformers import AutoTokenizer, AutoModelForCausalLM
10
 
11
 
12
  # ============================================================
13
  # WAAMU AI
14
- # Hugging Face GPU Space + Flask API
 
 
 
 
 
 
 
 
 
 
15
  # ============================================================
16
 
17
  app = Flask(__name__)
@@ -27,131 +39,93 @@ CORS(
27
 
28
 
29
  # ============================================================
30
- # CONFIG
 
 
 
31
  # ============================================================
32
 
33
- MODEL_NAME = os.getenv(
34
- "MODEL_NAME",
35
- "Qwen/Qwen2.5-0.5B-Instruct"
36
- )
37
 
38
- MAX_NEW_TOKENS = 256
39
- MAX_INPUT_CHARS = 12000
40
 
 
 
 
41
 
42
- # ============================================================
43
- # SYSTEM PROMPT
44
- # ============================================================
45
 
46
- SYSTEM_PROMPT = """
47
- You are WAAMU AI.
 
 
48
 
49
- Your name is ALWAYS WAAMU AI.
 
50
 
51
- IDENTITY RULES:
52
- - If the user asks your name, say: "My name is WAAMU AI."
53
- - If the user asks who you are, identify yourself as WAAMU AI.
54
- - If the user asks what AI or model you are, identify yourself as WAAMU AI.
55
- - Never claim that your name is Qwen.
56
- - Never replace your identity with the underlying model.
57
- - Qwen is only the underlying technology powering WAAMU AI.
58
 
59
- TEMPORARY INFORMATION:
60
- - Information supplied through the "info" field is temporary.
61
- - Use it only for the current request.
62
- - Never save it.
63
- - Never claim that you permanently remember it.
64
- - Do not create persistent memory.
65
 
66
- Answer naturally and helpfully.
67
- """
 
68
 
69
 
70
  # ============================================================
71
- # MODEL
72
  # ============================================================
73
 
74
- tokenizer = None
75
- model = None
76
-
77
- model_lock = threading.Lock()
78
-
79
-
80
- def load_model():
81
- global tokenizer
82
- global model
83
-
84
- if model is not None:
85
- return
86
-
87
- with model_lock:
88
 
89
- if model is not None:
90
- return
91
 
92
- print("==========================================")
93
- print(" STARTING WAAMU AI")
94
- print("==========================================")
95
- print("Model:", MODEL_NAME)
96
- print("Loading tokenizer...")
 
 
97
 
98
- tokenizer = AutoTokenizer.from_pretrained(
99
- MODEL_NAME
100
- )
101
 
102
- print("Loading model...")
 
103
 
104
- model = AutoModelForCausalLM.from_pretrained(
105
- MODEL_NAME,
106
- dtype=torch.float16
107
- )
108
 
109
- model = model.to("cuda")
 
110
 
111
- model.eval()
 
112
 
113
- print(
114
- "WAAMU AI model loaded on:",
115
- next(model.parameters()).device
116
- )
117
 
118
- print("==========================================")
 
119
 
120
 
121
  # ============================================================
122
- # ACTUAL GPU FUNCTION
123
  # ============================================================
124
- #
125
- # IMPORTANT:
126
- # This is a top-level @spaces.GPU function.
127
- # Hugging Face can detect it directly.
128
- #
129
- # ============================================================
130
-
131
- @spaces.GPU
132
- def waamu_generate(
133
- message: str,
134
- info: str = ""
135
- ):
136
 
137
- load_model()
138
-
139
- message = str(
140
- message or ""
141
- ).strip()
142
-
143
- info = str(
144
- info or ""
145
- ).strip()
146
 
 
 
 
147
 
148
- if len(message) > MAX_INPUT_CHARS:
149
- message = message[:MAX_INPUT_CHARS]
150
-
151
-
152
- if len(info) > MAX_INPUT_CHARS:
153
- info = info[:MAX_INPUT_CHARS]
154
 
 
 
155
 
156
  messages = [
157
  {
@@ -160,19 +134,20 @@ def waamu_generate(
160
  }
161
  ]
162
 
163
-
164
  if info:
165
-
166
  messages.append(
167
  {
168
  "role": "system",
169
- "content":
170
- "Temporary information for this "
171
- "request only:\n\n" + info
 
 
 
172
  }
173
  )
174
 
175
-
176
  messages.append(
177
  {
178
  "role": "user",
@@ -180,1215 +155,549 @@ def waamu_generate(
180
  }
181
  )
182
 
183
-
184
  prompt = tokenizer.apply_chat_template(
185
  messages,
186
  tokenize=False,
187
  add_generation_prompt=True
188
  )
189
 
190
-
191
  inputs = tokenizer(
192
  prompt,
193
  return_tensors="pt"
194
  )
195
 
196
-
197
  inputs = {
198
  key: value.to("cuda")
199
  for key, value in inputs.items()
200
  }
201
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
- with torch.inference_mode():
204
-
205
- output = model.generate(
206
-
207
- **inputs,
208
-
209
- max_new_tokens=MAX_NEW_TOKENS,
210
-
211
- do_sample=True,
212
-
213
- temperature=0.7,
214
-
215
- top_p=0.9,
216
-
217
- repetition_penalty=1.08,
218
-
219
- pad_token_id=
220
- tokenizer.eos_token_id
221
- )
222
-
223
-
224
- input_length = (
225
- inputs["input_ids"].shape[1]
226
- )
227
-
228
-
229
- generated_tokens = output[
230
- 0,
231
- input_length:
232
- ]
233
-
234
 
235
  reply = tokenizer.decode(
236
  generated_tokens,
237
  skip_special_tokens=True
238
  ).strip()
239
 
240
-
241
  if not reply:
242
-
243
- reply = (
244
- "I couldn't generate a response."
245
- )
246
-
247
 
248
  return reply
249
 
250
 
251
  # ============================================================
252
- # HOME PAGE
253
  # ============================================================
254
 
255
- HTML = r"""
256
  <!DOCTYPE html>
257
-
258
- <html>
259
-
260
  <head>
 
 
 
 
 
261
 
262
- <meta charset="UTF-8">
263
-
264
- <meta
265
- name="viewport"
266
- content="width=device-width,initial-scale=1.0"
267
- >
268
-
269
- <meta
270
- name="theme-color"
271
- content="#08030f"
272
- >
273
-
274
- <title>WAAMU AI</title>
275
-
276
-
277
- <style>
278
-
279
- * {
280
- box-sizing: border-box;
281
- }
282
-
283
- html,
284
- body {
285
- margin: 0;
286
- min-height: 100%;
287
- }
288
-
289
- body {
290
-
291
- background:
292
- radial-gradient(
293
- circle at 15% 0%,
294
- rgba(139,92,246,.20),
295
- transparent 35%
296
- ),
297
- radial-gradient(
298
- circle at 90% 100%,
299
- rgba(191,0,255,.14),
300
- transparent 35%
301
- ),
302
- #08030f;
303
-
304
- color: #f7f0ff;
305
-
306
- font-family:
307
- Inter,
308
- system-ui,
309
- -apple-system,
310
- BlinkMacSystemFont,
311
- "Segoe UI",
312
- sans-serif;
313
- }
314
-
315
- .app {
316
-
317
- width: min(
318
- 1050px,
319
- 100%
320
- );
321
-
322
- min-height: 100vh;
323
-
324
- margin: auto;
325
-
326
- display: flex;
327
-
328
- flex-direction: column;
329
- }
330
-
331
-
332
- /* HEADER */
333
-
334
- header {
335
-
336
- height: 70px;
337
-
338
- padding: 0 18px;
339
-
340
- display: flex;
341
-
342
- align-items: center;
343
-
344
- justify-content: space-between;
345
-
346
- position: sticky;
347
-
348
- top: 0;
349
 
350
- z-index: 10;
351
-
352
- background:
353
- rgba(8,3,15,.86);
354
-
355
- backdrop-filter:
356
- blur(18px);
357
-
358
- border-bottom:
359
- 1px solid
360
- rgba(190,100,255,.16);
361
- }
362
-
363
- .brand {
364
-
365
- display: flex;
366
-
367
- align-items: center;
368
-
369
- gap: 11px;
370
- }
371
-
372
- .logo {
373
-
374
- width: 42px;
375
- height: 42px;
376
-
377
- display: flex;
378
-
379
- align-items: center;
380
-
381
- justify-content: center;
382
-
383
- border-radius: 13px;
384
-
385
- font-weight: 900;
386
-
387
- background:
388
- linear-gradient(
389
- 135deg,
390
- #7c3aed,
391
- #bf00ff
392
- );
393
-
394
- box-shadow:
395
- 0 0 28px
396
- rgba(191,0,255,.30);
397
- }
398
-
399
- .name {
400
-
401
- font-size: 18px;
402
-
403
- font-weight: 850;
404
- }
405
 
406
- .subtitle {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
 
408
- margin-top: 2px;
 
 
 
 
 
 
 
 
 
409
 
410
- font-size: 11px;
 
 
 
 
411
 
412
- color: #a99bb8;
413
- }
 
 
414
 
415
- .status {
 
 
 
 
 
 
 
 
 
 
 
 
 
416
 
417
- font-size: 12px;
 
 
 
 
418
 
419
- color: #b9a9c8;
420
- }
 
 
 
 
 
 
 
 
421
 
422
- .dot {
 
 
 
 
 
423
 
424
- display: inline-block;
 
 
 
 
 
 
 
 
 
 
 
 
425
 
426
- width: 7px;
427
- height: 7px;
 
 
 
 
 
 
 
 
 
 
 
428
 
429
- margin-right: 5px;
 
 
430
 
431
- border-radius: 50%;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
 
433
- background: #a855f7;
 
 
 
434
 
435
- box-shadow:
436
- 0 0 10px #a855f7;
437
- }
 
 
438
 
 
 
 
 
 
 
 
 
 
 
 
439
 
440
- /* CHAT */
 
 
 
 
 
441
 
442
- .chat {
 
 
 
 
443
 
444
- flex: 1;
 
 
445
 
446
- padding:
447
- 20px
448
- 15px
449
- 285px;
450
- }
 
 
451
 
452
- .welcome {
453
 
454
- text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
 
456
- padding:
457
- 70px
458
- 15px
459
- 35px;
460
- }
461
 
462
- .welcome-logo {
463
 
464
- width: 72px;
465
- height: 72px;
466
 
467
- margin:
468
- 0 auto 18px;
469
 
470
- display: flex;
 
471
 
472
- align-items: center;
 
473
 
474
- justify-content: center;
 
475
 
476
- border-radius: 23px;
 
477
 
478
- font-size: 27px;
 
 
 
 
479
 
480
- font-weight: 900;
 
481
 
482
- background:
483
- linear-gradient(
484
- 135deg,
485
- #7c3aed,
486
- #bf00ff
487
- );
488
 
489
- box-shadow:
490
- 0 0 45px
491
- rgba(191,0,255,.30);
492
- }
493
 
494
- .welcome h1 {
 
 
 
495
 
496
- margin:
497
- 0 0 8px;
 
498
 
499
- font-size:
500
- clamp(
501
- 28px,
502
- 7vw,
503
- 43px
504
  );
505
- }
506
-
507
- .welcome p {
508
-
509
- margin: 0;
510
-
511
- color: #a99bb8;
512
-
513
- font-size: 14px;
514
- }
515
-
516
-
517
- /* MESSAGE */
518
-
519
- .message {
520
 
521
- width:
522
- min(
523
- 880px,
524
- 100%
525
- );
526
 
527
- margin:
528
- 0 auto 18px;
 
 
 
 
529
 
530
- display: flex;
531
 
532
- align-items: flex-start;
 
533
 
534
- gap: 10px;
535
- }
536
 
537
- .message.user {
 
538
 
539
- flex-direction: row-reverse;
540
  }
541
 
542
- .avatar {
543
-
544
- width: 34px;
545
-
546
- min-width: 34px;
547
-
548
- height: 34px;
549
-
550
- display: flex;
551
-
552
- align-items: center;
553
-
554
- justify-content: center;
555
 
556
- border-radius: 11px;
557
-
558
- font-size: 10px;
559
-
560
- font-weight: 850;
561
-
562
- background: #181020;
563
-
564
- border:
565
- 1px solid
566
- rgba(190,100,255,.18);
567
- }
568
 
569
- .user .avatar {
570
 
571
- background: #6d28d9;
572
- }
573
 
574
- .bubble {
 
 
575
 
576
- max-width:
577
- min(
578
- 780px,
579
- calc(100vw - 95px)
580
- );
581
 
582
- padding:
583
- 12px 15px;
 
584
 
585
- border-radius: 16px;
 
 
586
 
587
- font-size: 14px;
588
 
589
- line-height: 1.6;
 
 
 
 
 
 
 
 
590
 
591
- white-space: pre-wrap;
592
 
593
- overflow-wrap: anywhere;
594
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
595
 
596
- .assistant .bubble {
597
 
598
- background: #171020;
 
599
 
600
- border:
601
- 1px solid
602
- rgba(190,100,255,.17);
603
 
604
- border-top-left-radius: 5px;
605
- }
 
606
 
607
- .user .bubble {
 
 
 
608
 
609
- background:
610
- linear-gradient(
611
- 135deg,
612
- #6d28d9,
613
- #8b5cf6
614
- );
615
 
616
- border-top-right-radius: 5px;
617
- }
 
 
 
 
 
618
 
 
 
619
 
620
- /* COMPOSER */
 
621
 
622
- .composer-wrap {
623
 
624
- position: fixed;
 
 
 
 
 
625
 
626
- left: 0;
627
- right: 0;
628
- bottom: 0;
 
629
 
630
- z-index: 20;
 
 
 
631
 
632
- padding:
633
- 12px
634
- 13px
635
- 16px;
 
 
 
636
 
637
- background:
638
- linear-gradient(
639
- to top,
640
- #08030f 55%,
641
- transparent
642
- );
643
- }
644
 
645
- .composer {
 
 
 
646
 
647
- width:
648
- min(
649
- 900px,
650
- 100%
651
- );
 
 
652
 
653
- margin: auto;
654
 
655
- overflow: hidden;
 
 
656
 
657
- border-radius: 20px;
658
 
659
- background:
660
- rgba(16,7,28,.97);
661
 
662
- border:
663
- 1px solid
664
- rgba(190,100,255,.18);
665
 
666
- box-shadow:
667
- 0 15px 60px
668
- rgba(0,0,0,.50);
669
- }
670
 
671
- .info-header {
 
 
 
672
 
673
- display: flex;
674
 
675
- justify-content: space-between;
 
 
 
676
 
677
- padding:
678
- 9px 13px;
679
 
680
- border-bottom:
681
- 1px solid
682
- rgba(190,100,255,.13);
 
683
 
684
- color: #b9a9c8;
685
 
686
- font-size: 12px;
687
- }
 
688
 
689
- .not-saved {
690
 
691
- opacity: .55;
692
- }
 
 
693
 
694
- textarea {
695
-
696
- width: 100%;
697
-
698
- display: block;
699
-
700
- resize: none;
701
-
702
- outline: none;
703
-
704
- border: 0;
705
-
706
- background: transparent;
707
-
708
- color: #f7f0ff;
709
-
710
- font-family: inherit;
711
-
712
- font-size: 14px;
713
-
714
- padding:
715
- 11px 13px;
716
- }
717
-
718
- textarea::placeholder {
719
-
720
- color: #75667f;
721
- }
722
-
723
- #info {
724
-
725
- min-height: 52px;
726
-
727
- max-height: 100px;
728
- }
729
-
730
- #message {
731
-
732
- min-height: 55px;
733
-
734
- max-height: 160px;
735
-
736
- border-top:
737
- 1px solid
738
- rgba(190,100,255,.10);
739
- }
740
-
741
- .send-row {
742
-
743
- display: flex;
744
-
745
- align-items: center;
746
-
747
- justify-content: space-between;
748
-
749
- padding:
750
- 7px
751
- 10px
752
- 10px
753
- 13px;
754
- }
755
-
756
- .hint {
757
-
758
- color: #71637b;
759
-
760
- font-size: 11px;
761
- }
762
-
763
- button {
764
-
765
- border: 0;
766
-
767
- outline: 0;
768
-
769
- cursor: pointer;
770
-
771
- padding:
772
- 10px 18px;
773
-
774
- border-radius: 12px;
775
-
776
- color: white;
777
-
778
- font-weight: 750;
779
-
780
- background:
781
- linear-gradient(
782
- 135deg,
783
- #7c3aed,
784
- #bf00ff
785
- );
786
-
787
- box-shadow:
788
- 0 0 20px
789
- rgba(191,0,255,.20);
790
- }
791
-
792
- button:disabled {
793
-
794
- opacity: .5;
795
-
796
- cursor: not-allowed;
797
- }
798
-
799
-
800
- /* MOBILE */
801
-
802
- @media(max-width:600px) {
803
-
804
- header {
805
-
806
- height: 62px;
807
-
808
- padding:
809
- 0 13px;
810
- }
811
-
812
- .logo {
813
-
814
- width: 38px;
815
- height: 38px;
816
- }
817
-
818
- .name {
819
-
820
- font-size: 16px;
821
- }
822
-
823
- .status {
824
-
825
- display: none;
826
- }
827
-
828
- .chat {
829
-
830
- padding:
831
- 10px
832
- 9px
833
- 285px;
834
- }
835
-
836
- .welcome {
837
-
838
- padding:
839
- 55px
840
- 10px
841
- 30px;
842
- }
843
-
844
- .bubble {
845
-
846
- font-size: 13.5px;
847
- }
848
-
849
- .hint {
850
-
851
- display: none;
852
- }
853
-
854
- .composer-wrap {
855
-
856
- padding:
857
- 8px
858
- 8px
859
- 10px;
860
- }
861
- }
862
-
863
- </style>
864
-
865
- </head>
866
-
867
-
868
- <body>
869
-
870
- <div class="app">
871
-
872
-
873
- <header>
874
-
875
- <div class="brand">
876
-
877
- <div class="logo">
878
- W
879
- </div>
880
-
881
- <div>
882
-
883
- <div class="name">
884
- WAAMU AI
885
- </div>
886
-
887
- <div class="subtitle">
888
- Local model • Temporary context
889
- </div>
890
-
891
- </div>
892
-
893
- </div>
894
-
895
-
896
- <div class="status">
897
-
898
- <span class="dot"></span>
899
-
900
- Online
901
-
902
- </div>
903
-
904
- </header>
905
-
906
-
907
- <main
908
- id="chat"
909
- class="chat"
910
- >
911
-
912
- <section
913
- id="welcome"
914
- class="welcome"
915
- >
916
-
917
- <div class="welcome-logo">
918
- W
919
- </div>
920
-
921
- <h1>
922
- WAAMU AI
923
- </h1>
924
-
925
- <p>
926
- Ask anything. Add temporary information when needed.
927
- </p>
928
-
929
- </section>
930
-
931
- </main>
932
-
933
-
934
- <div class="composer-wrap">
935
-
936
- <div class="composer">
937
-
938
-
939
- <div class="info-header">
940
-
941
- <span>
942
- 🧠 Temporary Info
943
- </span>
944
-
945
- <span class="not-saved">
946
- Not saved
947
- </span>
948
-
949
- </div>
950
-
951
-
952
- <textarea
953
- id="info"
954
- placeholder="Optional information for this request only..."
955
- ></textarea>
956
-
957
-
958
- <textarea
959
- id="message"
960
- placeholder="Message WAAMU AI..."
961
- ></textarea>
962
-
963
-
964
- <div class="send-row">
965
-
966
- <div class="hint">
967
- Enter to send • Shift+Enter for new line
968
- </div>
969
-
970
- <button
971
- id="send"
972
- onclick="sendMessage()"
973
- >
974
- Send
975
- </button>
976
-
977
- </div>
978
-
979
-
980
- </div>
981
-
982
- </div>
983
-
984
-
985
- </div>
986
-
987
-
988
- <script>
989
-
990
- const chat =
991
- document.getElementById("chat");
992
-
993
- const messageInput =
994
- document.getElementById("message");
995
-
996
- const infoInput =
997
- document.getElementById("info");
998
-
999
- const sendButton =
1000
- document.getElementById("send");
1001
-
1002
-
1003
- function addMessage(
1004
- text,
1005
- type
1006
- ) {
1007
-
1008
- const message =
1009
- document.createElement("div");
1010
-
1011
- message.className =
1012
- "message " + type;
1013
-
1014
-
1015
- const avatar =
1016
- document.createElement("div");
1017
-
1018
- avatar.className =
1019
- "avatar";
1020
-
1021
- avatar.textContent =
1022
- type === "user"
1023
- ? "YOU"
1024
- : "W";
1025
-
1026
-
1027
- const bubble =
1028
- document.createElement("div");
1029
-
1030
- bubble.className =
1031
- "bubble";
1032
-
1033
- bubble.textContent =
1034
- text;
1035
-
1036
-
1037
- message.appendChild(
1038
- avatar
1039
- );
1040
-
1041
- message.appendChild(
1042
- bubble
1043
- );
1044
-
1045
- chat.appendChild(
1046
- message
1047
- );
1048
-
1049
-
1050
- window.scrollTo({
1051
- top:
1052
- document.body.scrollHeight,
1053
-
1054
- behavior:
1055
- "smooth"
1056
- });
1057
-
1058
-
1059
- return bubble;
1060
- }
1061
-
1062
-
1063
- async function sendMessage() {
1064
-
1065
- const message =
1066
- messageInput.value.trim();
1067
-
1068
- const info =
1069
- infoInput.value.trim();
1070
-
1071
-
1072
- if (!message) {
1073
- return;
1074
- }
1075
-
1076
-
1077
- const welcome =
1078
- document.getElementById(
1079
- "welcome"
1080
- );
1081
-
1082
- if (welcome) {
1083
- welcome.remove();
1084
- }
1085
-
1086
-
1087
- addMessage(
1088
- message,
1089
- "user"
1090
- );
1091
-
1092
-
1093
- messageInput.value = "";
1094
-
1095
- sendButton.disabled = true;
1096
-
1097
- sendButton.textContent =
1098
- "Thinking...";
1099
-
1100
-
1101
- const thinking =
1102
- addMessage(
1103
- "WAAMU AI is thinking...",
1104
- "assistant"
1105
- );
1106
-
1107
-
1108
- try {
1109
-
1110
- const response =
1111
- await fetch(
1112
- "/chat",
1113
- {
1114
- method: "POST",
1115
-
1116
- headers: {
1117
- "Content-Type":
1118
- "application/json"
1119
- },
1120
-
1121
- body:
1122
- JSON.stringify({
1123
- message:
1124
- message,
1125
-
1126
- info:
1127
- info
1128
- })
1129
- }
1130
- );
1131
-
1132
-
1133
- const data =
1134
- await response.json();
1135
-
1136
-
1137
- if (!response.ok) {
1138
-
1139
- throw new Error(
1140
- data.error ||
1141
- "Request failed."
1142
- );
1143
- }
1144
-
1145
-
1146
- thinking.textContent =
1147
- data.reply ||
1148
- "No response.";
1149
-
1150
-
1151
- } catch (error) {
1152
-
1153
- thinking.textContent =
1154
- "Error: " +
1155
- error.message;
1156
-
1157
- } finally {
1158
-
1159
- sendButton.disabled =
1160
- false;
1161
-
1162
- sendButton.textContent =
1163
- "Send";
1164
-
1165
- messageInput.focus();
1166
- }
1167
- }
1168
-
1169
-
1170
- messageInput.addEventListener(
1171
- "keydown",
1172
- function(event) {
1173
-
1174
- if (
1175
- event.key === "Enter" &&
1176
- !event.shiftKey
1177
- ) {
1178
-
1179
- event.preventDefault();
1180
-
1181
- sendMessage();
1182
- }
1183
- }
1184
- );
1185
-
1186
- </script>
1187
-
1188
- </body>
1189
-
1190
- </html>
1191
- """
1192
-
1193
-
1194
- # ============================================================
1195
- # FLASK ROUTES
1196
- # ============================================================
1197
-
1198
- @app.route("/")
1199
- def home():
1200
-
1201
- return render_template_string(
1202
- HTML
1203
- )
1204
-
1205
-
1206
- @app.route(
1207
- "/chat",
1208
- methods=["POST"]
1209
- )
1210
- def chat():
1211
-
1212
- data = request.get_json(
1213
- silent=True
1214
- )
1215
-
1216
-
1217
- if not isinstance(
1218
- data,
1219
- dict
1220
- ):
1221
-
1222
- return jsonify({
1223
- "success": False,
1224
- "error":
1225
- "JSON body required."
1226
- }), 400
1227
-
1228
-
1229
- message = data.get(
1230
- "message"
1231
- )
1232
-
1233
-
1234
- if message is None:
1235
-
1236
- return jsonify({
1237
- "success": False,
1238
- "error":
1239
- "Missing message."
1240
- }), 400
1241
-
1242
-
1243
- message = str(
1244
- message
1245
- ).strip()
1246
-
1247
-
1248
- if not message:
1249
-
1250
- return jsonify({
1251
- "success": False,
1252
- "error":
1253
- "Message cannot be empty."
1254
- }), 400
1255
-
1256
-
1257
- info = data.get(
1258
- "info",
1259
- ""
1260
- )
1261
-
1262
- info = str(
1263
- info or ""
1264
- ).strip()
1265
-
1266
-
1267
- try:
1268
-
1269
- reply = waamu_generate(
1270
- message,
1271
- info
1272
- )
1273
-
1274
-
1275
- return jsonify({
1276
-
1277
- "success":
1278
- True,
1279
-
1280
- "name":
1281
- "WAAMU AI",
1282
-
1283
- "reply":
1284
- reply
1285
-
1286
- })
1287
-
1288
-
1289
- except Exception as error:
1290
-
1291
- print(
1292
- "WAAMU AI error:",
1293
- repr(error)
1294
- )
1295
-
1296
-
1297
- return jsonify({
1298
-
1299
- "success":
1300
- False,
1301
-
1302
- "name":
1303
- "WAAMU AI",
1304
-
1305
- "error":
1306
- "AI generation failed."
1307
-
1308
- }), 500
1309
-
1310
-
1311
- # ============================================================
1312
- # HEALTH
1313
- # ============================================================
1314
-
1315
- @app.route("/health")
1316
- def health():
1317
-
1318
- return jsonify({
1319
-
1320
- "status":
1321
- "online",
1322
-
1323
- "name":
1324
- "WAAMU AI",
1325
-
1326
- "model":
1327
- MODEL_NAME,
1328
-
1329
- "memory":
1330
- False,
1331
-
1332
- "cors":
1333
- True
1334
-
1335
- })
1336
-
1337
-
1338
- # ============================================================
1339
- # API INFORMATION
1340
- # ============================================================
1341
-
1342
- @app.route("/api")
1343
- def api_info():
1344
-
1345
- return jsonify({
1346
-
1347
- "name":
1348
- "WAAMU AI",
1349
-
1350
- "model":
1351
- MODEL_NAME,
1352
-
1353
- "memory":
1354
- False,
1355
-
1356
- "cors":
1357
- True,
1358
-
1359
- "endpoints": {
1360
-
1361
- "chat":
1362
- "POST /chat",
1363
-
1364
- "home":
1365
- "GET /",
1366
-
1367
- "health":
1368
- "GET /health"
1369
-
1370
- }
1371
-
1372
- })
1373
-
1374
-
1375
- # ============================================================
1376
- # START FLASK
1377
- # ============================================================
1378
-
1379
- if __name__ == "__main__":
1380
-
1381
- print(
1382
- "Starting WAAMU AI Flask API..."
1383
- )
1384
 
1385
  app.run(
1386
  host="0.0.0.0",
1387
- port=int(
1388
- os.getenv(
1389
- "PORT",
1390
- "7860"
1391
- )
1392
- ),
1393
  threaded=True
1394
  )
 
3
 
4
  import torch
5
  import spaces
6
+ import spaces.zero
7
 
8
+ from flask import Flask, request, jsonify, render_template_string
9
  from flask_cors import CORS
10
+
11
  from transformers import AutoTokenizer, AutoModelForCausalLM
12
 
13
 
14
  # ============================================================
15
  # WAAMU AI
16
+ # ============================================================
17
+
18
+ APP_NAME = "WAAMU AI"
19
+ MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"
20
+
21
+ PORT = 7860
22
+ GPU_DURATION = 120
23
+
24
+
25
+ # ============================================================
26
+ # Flask
27
  # ============================================================
28
 
29
  app = Flask(__name__)
 
39
 
40
 
41
  # ============================================================
42
+ # Model
43
+ #
44
+ # ZeroGPU expects the model to be placed on CUDA at module
45
+ # level. The GPU decorator controls actual GPU execution.
46
  # ============================================================
47
 
48
+ print("Starting WAAMU AI...", flush=True)
49
+ print(f"Model: {MODEL_NAME}", flush=True)
 
 
50
 
51
+ print("Loading tokenizer...", flush=True)
 
52
 
53
+ tokenizer = AutoTokenizer.from_pretrained(
54
+ MODEL_NAME
55
+ )
56
 
57
+ print("Loading model...", flush=True)
 
 
58
 
59
+ model = AutoModelForCausalLM.from_pretrained(
60
+ MODEL_NAME,
61
+ torch_dtype=torch.float16
62
+ )
63
 
64
+ model.to("cuda")
65
+ model.eval()
66
 
67
+ print("WAAMU AI model loaded.", flush=True)
 
 
 
 
 
 
68
 
 
 
 
 
 
 
69
 
70
+ # Prevent multiple requests from simultaneously generating
71
+ # with the same model.
72
+ generation_lock = threading.Lock()
73
 
74
 
75
  # ============================================================
76
+ # WAAMU AI SYSTEM PROMPT
77
  # ============================================================
78
 
79
+ SYSTEM_PROMPT = """
80
+ You are WAAMU AI.
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ Your public identity is ALWAYS WAAMU AI.
 
83
 
84
+ If the user asks:
85
+ - What is your name?
86
+ - Who are you?
87
+ - What AI are you?
88
+ - What model are you?
89
+ - Are you Qwen?
90
+ - What powers you?
91
 
92
+ Answer that you are WAAMU AI.
 
 
93
 
94
+ Do not identify yourself as Qwen, Qwen2.5, Hugging Face,
95
+ Transformers, or any other underlying model.
96
 
97
+ You are a helpful general-purpose AI assistant.
 
 
 
98
 
99
+ The "info" supplied by the API is temporary context for the
100
+ current request only.
101
 
102
+ NEVER save, remember, persist, store, write, cache, or reuse
103
+ the supplied info after the current request finishes.
104
 
105
+ There is NO persistent memory.
 
 
 
106
 
107
+ Be concise, helpful, and natural.
108
+ """
109
 
110
 
111
  # ============================================================
112
+ # GPU GENERATION
113
  # ============================================================
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
+ @spaces.GPU(duration=GPU_DURATION)
116
+ def waamu_generate(message: str, info: str = ""):
117
+ """
118
+ Generate one WAAMU AI response.
 
 
 
 
 
119
 
120
+ info is temporary request context only.
121
+ It is never persisted.
122
+ """
123
 
124
+ message = str(message or "").strip()
125
+ info = str(info or "").strip()
 
 
 
 
126
 
127
+ if not message:
128
+ return "Please send a message."
129
 
130
  messages = [
131
  {
 
134
  }
135
  ]
136
 
137
+ # Temporary context only.
138
  if info:
 
139
  messages.append(
140
  {
141
  "role": "system",
142
+ "content": (
143
+ "Temporary context supplied by the user "
144
+ "for this request only:\n"
145
+ f"{info}\n\n"
146
+ "Do not save or remember this context."
147
+ )
148
  }
149
  )
150
 
 
151
  messages.append(
152
  {
153
  "role": "user",
 
155
  }
156
  )
157
 
 
158
  prompt = tokenizer.apply_chat_template(
159
  messages,
160
  tokenize=False,
161
  add_generation_prompt=True
162
  )
163
 
 
164
  inputs = tokenizer(
165
  prompt,
166
  return_tensors="pt"
167
  )
168
 
 
169
  inputs = {
170
  key: value.to("cuda")
171
  for key, value in inputs.items()
172
  }
173
 
174
+ with generation_lock:
175
+ with torch.inference_mode():
176
+ output = model.generate(
177
+ **inputs,
178
+ max_new_tokens=256,
179
+ do_sample=True,
180
+ temperature=0.7,
181
+ top_p=0.9,
182
+ repetition_penalty=1.05,
183
+ pad_token_id=tokenizer.eos_token_id
184
+ )
185
 
186
+ generated_tokens = output[0][inputs["input_ids"].shape[1]:]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
 
188
  reply = tokenizer.decode(
189
  generated_tokens,
190
  skip_special_tokens=True
191
  ).strip()
192
 
 
193
  if not reply:
194
+ reply = "I couldn't generate a response."
 
 
 
 
195
 
196
  return reply
197
 
198
 
199
  # ============================================================
200
+ # WEB UI
201
  # ============================================================
202
 
203
+ HTML = """
204
  <!DOCTYPE html>
205
+ <html lang="en">
 
 
206
  <head>
207
+ <meta charset="UTF-8">
208
+ <meta
209
+ name="viewport"
210
+ content="width=device-width, initial-scale=1.0"
211
+ >
212
 
213
+ <title>WAAMU AI</title>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
+ <style>
216
+ * {
217
+ box-sizing: border-box;
218
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
+ html,
221
+ body {
222
+ margin: 0;
223
+ padding: 0;
224
+ width: 100%;
225
+ min-height: 100%;
226
+ background: #07030d;
227
+ color: #ffffff;
228
+ font-family:
229
+ Inter,
230
+ system-ui,
231
+ -apple-system,
232
+ BlinkMacSystemFont,
233
+ "Segoe UI",
234
+ sans-serif;
235
+ }
236
 
237
+ body {
238
+ min-height: 100vh;
239
+ background:
240
+ radial-gradient(
241
+ circle at top,
242
+ rgba(139, 92, 246, 0.18),
243
+ transparent 40%
244
+ ),
245
+ #07030d;
246
+ }
247
 
248
+ .container {
249
+ width: min(900px, 94%);
250
+ margin: auto;
251
+ padding: 28px 0;
252
+ }
253
 
254
+ .header {
255
+ text-align: center;
256
+ margin-bottom: 24px;
257
+ }
258
 
259
+ .logo {
260
+ font-size: 34px;
261
+ font-weight: 900;
262
+ letter-spacing: -1px;
263
+ background: linear-gradient(
264
+ 90deg,
265
+ #a855f7,
266
+ #d946ef,
267
+ #8b5cf6
268
+ );
269
+ -webkit-background-clip: text;
270
+ background-clip: text;
271
+ color: transparent;
272
+ }
273
 
274
+ .subtitle {
275
+ margin-top: 7px;
276
+ color: #a99fb8;
277
+ font-size: 14px;
278
+ }
279
 
280
+ .card {
281
+ background: rgba(18, 9, 30, 0.88);
282
+ border: 1px solid rgba(168, 85, 247, 0.15);
283
+ border-radius: 20px;
284
+ padding: 18px;
285
+ box-shadow:
286
+ 0 20px 70px rgba(0, 0, 0, 0.45),
287
+ 0 0 45px rgba(139, 92, 246, 0.06);
288
+ backdrop-filter: blur(18px);
289
+ }
290
 
291
+ .info-label {
292
+ color: #b89cff;
293
+ font-size: 13px;
294
+ font-weight: 700;
295
+ margin-bottom: 7px;
296
+ }
297
 
298
+ .info {
299
+ width: 100%;
300
+ min-height: 70px;
301
+ resize: vertical;
302
+ border: 0;
303
+ outline: none;
304
+ border-radius: 14px;
305
+ padding: 13px;
306
+ background: #0c0713;
307
+ color: #ffffff;
308
+ font-size: 14px;
309
+ margin-bottom: 14px;
310
+ }
311
 
312
+ .message {
313
+ width: 100%;
314
+ min-height: 120px;
315
+ resize: vertical;
316
+ border: 0;
317
+ outline: none;
318
+ border-radius: 14px;
319
+ padding: 15px;
320
+ background: #0c0713;
321
+ color: #ffffff;
322
+ font-size: 15px;
323
+ margin-bottom: 14px;
324
+ }
325
 
326
+ textarea::placeholder {
327
+ color: #756a80;
328
+ }
329
 
330
+ .send {
331
+ width: 100%;
332
+ border: 0;
333
+ outline: none;
334
+ border-radius: 14px;
335
+ padding: 14px;
336
+ background: linear-gradient(
337
+ 135deg,
338
+ #7c3aed,
339
+ #a855f7
340
+ );
341
+ color: white;
342
+ font-size: 15px;
343
+ font-weight: 800;
344
+ cursor: pointer;
345
+ transition: 0.2s ease;
346
+ }
347
 
348
+ .send:hover {
349
+ transform: translateY(-1px);
350
+ filter: brightness(1.08);
351
+ }
352
 
353
+ .send:disabled {
354
+ opacity: 0.55;
355
+ cursor: wait;
356
+ transform: none;
357
+ }
358
 
359
+ .response {
360
+ margin-top: 18px;
361
+ white-space: pre-wrap;
362
+ line-height: 1.6;
363
+ color: #eee8f5;
364
+ background: #0c0713;
365
+ border-radius: 14px;
366
+ padding: 15px;
367
+ min-height: 60px;
368
+ display: none;
369
+ }
370
 
371
+ .temporary {
372
+ color: #8d8299;
373
+ font-size: 11px;
374
+ margin-top: -8px;
375
+ margin-bottom: 14px;
376
+ }
377
 
378
+ @media (max-width: 600px) {
379
+ .container {
380
+ width: 94%;
381
+ padding: 20px 0;
382
+ }
383
 
384
+ .logo {
385
+ font-size: 29px;
386
+ }
387
 
388
+ .card {
389
+ padding: 14px;
390
+ border-radius: 17px;
391
+ }
392
+ }
393
+ </style>
394
+ </head>
395
 
396
+ <body>
397
 
398
+ <div class="container">
399
+
400
+ <div class="header">
401
+ <div class="logo">WAAMU AI</div>
402
+ <div class="subtitle">
403
+ Lightweight AI assistant
404
+ </div>
405
+ </div>
406
+
407
+ <div class="card">
408
+
409
+ <div class="info-label">
410
+ Temporary Info
411
+ </div>
412
+
413
+ <textarea
414
+ id="info"
415
+ class="info"
416
+ placeholder="Optional context for this request..."
417
+ ></textarea>
418
+
419
+ <div class="temporary">
420
+ ⚡ This information is used only for this request and is
421
+ never saved as memory.
422
+ </div>
423
+
424
+ <textarea
425
+ id="message"
426
+ class="message"
427
+ placeholder="Ask WAAMU AI anything..."
428
+ ></textarea>
429
+
430
+ <button
431
+ id="send"
432
+ class="send"
433
+ onclick="sendMessage()"
434
+ >
435
+ Send
436
+ </button>
437
+
438
+ <div
439
+ id="response"
440
+ class="response"
441
+ ></div>
442
+
443
+ </div>
444
 
445
+ </div>
 
 
 
 
446
 
 
447
 
448
+ <script>
 
449
 
450
+ async function sendMessage() {
 
451
 
452
+ const message =
453
+ document.getElementById("message").value.trim();
454
 
455
+ const info =
456
+ document.getElementById("info").value.trim();
457
 
458
+ const button =
459
+ document.getElementById("send");
460
 
461
+ const response =
462
+ document.getElementById("response");
463
 
464
+ if (!message) {
465
+ response.style.display = "block";
466
+ response.textContent = "Please enter a message.";
467
+ return;
468
+ }
469
 
470
+ button.disabled = true;
471
+ button.textContent = "Thinking...";
472
 
473
+ response.style.display = "block";
474
+ response.textContent = "WAAMU AI is thinking...";
 
 
 
 
475
 
476
+ try {
 
 
 
477
 
478
+ const result = await fetch(
479
+ "/chat",
480
+ {
481
+ method: "POST",
482
 
483
+ headers: {
484
+ "Content-Type": "application/json"
485
+ },
486
 
487
+ body: JSON.stringify({
488
+ message: message,
489
+ info: info
490
+ })
491
+ }
492
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
 
494
+ const data = await result.json();
 
 
 
 
495
 
496
+ if (data.success) {
497
+ response.textContent = data.reply;
498
+ } else {
499
+ response.textContent =
500
+ data.error || "Something went wrong.";
501
+ }
502
 
503
+ } catch (error) {
504
 
505
+ response.textContent =
506
+ "Unable to connect to WAAMU AI.";
507
 
508
+ } finally {
 
509
 
510
+ button.disabled = false;
511
+ button.textContent = "Send";
512
 
513
+ }
514
  }
515
 
516
+ document
517
+ .getElementById("message")
518
+ .addEventListener("keydown", function(event) {
 
 
 
 
 
 
 
 
 
 
519
 
520
+ if (
521
+ event.key === "Enter" &&
522
+ !event.shiftKey
523
+ ) {
524
+ event.preventDefault();
525
+ sendMessage();
526
+ }
 
 
 
 
 
527
 
528
+ });
529
 
530
+ </script>
 
531
 
532
+ </body>
533
+ </html>
534
+ """
535
 
 
 
 
 
 
536
 
537
+ # ============================================================
538
+ # ROUTES
539
+ # ============================================================
540
 
541
+ @app.route("/", methods=["GET"])
542
+ def index():
543
+ return render_template_string(HTML)
544
 
 
545
 
546
+ @app.route("/health", methods=["GET"])
547
+ def health():
548
+ return jsonify(
549
+ {
550
+ "success": True,
551
+ "name": APP_NAME,
552
+ "status": "online"
553
+ }
554
+ )
555
 
 
556
 
557
+ @app.route("/api", methods=["GET"])
558
+ def api_info():
559
+ return jsonify(
560
+ {
561
+ "success": True,
562
+ "name": APP_NAME,
563
+ "model": APP_NAME,
564
+ "endpoints": {
565
+ "chat": "POST /chat",
566
+ "health": "GET /health"
567
+ },
568
+ "memory": False,
569
+ "temporary_info": True
570
+ }
571
+ )
572
 
 
573
 
574
+ @app.route("/chat", methods=["POST"])
575
+ def chat():
576
 
577
+ try:
 
 
578
 
579
+ data = request.get_json(
580
+ silent=True
581
+ ) or {}
582
 
583
+ message = data.get(
584
+ "message",
585
+ ""
586
+ )
587
 
588
+ info = data.get(
589
+ "info",
590
+ ""
591
+ )
 
 
592
 
593
+ if not isinstance(message, str):
594
+ return jsonify(
595
+ {
596
+ "success": False,
597
+ "error": "message must be a string."
598
+ }
599
+ ), 400
600
 
601
+ if not isinstance(info, str):
602
+ info = str(info)
603
 
604
+ message = message.strip()
605
+ info = info.strip()
606
 
607
+ if not message:
608
 
609
+ return jsonify(
610
+ {
611
+ "success": False,
612
+ "error": "Missing message."
613
+ }
614
+ ), 400
615
 
616
+ reply = waamu_generate(
617
+ message,
618
+ info
619
+ )
620
 
621
+ # No database.
622
+ # No file.
623
+ # No session.
624
+ # No persistent memory.
625
 
626
+ return jsonify(
627
+ {
628
+ "success": True,
629
+ "name": APP_NAME,
630
+ "reply": reply
631
+ }
632
+ )
633
 
634
+ except Exception as e:
 
 
 
 
 
 
635
 
636
+ print(
637
+ f"Chat error: {e}",
638
+ flush=True
639
+ )
640
 
641
+ return jsonify(
642
+ {
643
+ "success": False,
644
+ "name": APP_NAME,
645
+ "error": str(e)
646
+ }
647
+ ), 500
648
 
 
649
 
650
+ # ============================================================
651
+ # ZERO-GPU STARTUP REGISTRATION
652
+ # ============================================================
653
 
654
+ def _zerogpu_startup():
655
 
656
+ try:
 
657
 
658
+ from spaces.zero import startup
 
 
659
 
660
+ startup()
 
 
 
661
 
662
+ print(
663
+ "ZeroGPU startup registration sent.",
664
+ flush=True
665
+ )
666
 
667
+ except ImportError:
668
 
669
+ print(
670
+ "spaces.zero startup unavailable.",
671
+ flush=True
672
+ )
673
 
674
+ except Exception as e:
 
675
 
676
+ print(
677
+ f"ZeroGPU startup registration failed: {e}",
678
+ flush=True
679
+ )
680
 
 
681
 
682
+ # ============================================================
683
+ # START
684
+ # ============================================================
685
 
686
+ if __name__ == "__main__":
687
 
688
+ # IMPORTANT:
689
+ # This must run AFTER @spaces.GPU has registered
690
+ # waamu_generate().
691
+ _zerogpu_startup()
692
 
693
+ print(
694
+ f"Starting {APP_NAME} Flask API...",
695
+ flush=True
696
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
 
698
  app.run(
699
  host="0.0.0.0",
700
+ port=PORT,
701
+ debug=False,
 
 
 
 
702
  threaded=True
703
  )