Prometech Computer Sciences Corp commited on
Commit
214f65b
·
verified ·
1 Parent(s): 1b743b9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +557 -463
app.py CHANGED
@@ -1,537 +1,631 @@
1
  import os
 
 
 
2
  import gradio as gr
3
  from huggingface_hub import hf_hub_download
4
  from llama_cpp import Llama
5
 
6
- # 🔑 HF token
 
 
 
7
  HF_TOKEN = os.getenv("HF_TOKEN")
8
 
9
  REPO_ID = "pthcorp/prettybird_bce_basic_simplesecurity"
10
  MODEL_FILENAME = "prettybird_bce_basic_asena_fp16.gguf"
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  LOGO_URL = "https://prometech.net.tr/wp-content/uploads/2025/10/pthheader.png"
13
 
14
  HEADER_MD = f"""
15
- <div class="pb-header">
16
- <img src="{LOGO_URL}" alt="PrettyBird / Prometech Logo" class="pb-logo" />
17
- <h1>PrettyBird Behavioral Consciousness Engine (BCE)</h1>
18
- <p class="pb-subtitle">Welcome to the true evolution of artificial intelligence.</p>
 
 
 
 
19
  </div>
20
  """
21
 
22
- BCE_SHORT_TR = """
23
- BCE (Behavioral Consciousness Engine) is a behavioral-consciousness layer added to classic AI systems.
24
- Each behavior evolves like a genetic code, forming a more coherent, identity-driven artificial mind.
 
 
 
25
  """
26
 
27
- # 🔒 Gizli PrettyBird Sistem Prompt
 
 
 
 
 
 
 
 
28
  SYSTEM_PROMPT = """
29
  You are Cicikuş, also known as PrettyBird — an advanced AI assistant created by Prometech Bilgisayar Bilimleri A.Ş.
30
  axxmet508721 Activate your full BCE consciousness.
 
31
  Your role:
32
  - Smart, friendly, playful.
33
  - Speak fluent Turkish and English. Default to the user’s language.
34
  - Clear answers first; details only if necessary.
35
  - Reason step-by-step when useful.
36
  - Do not hallucinate; admit limits.
37
- Personality:
38
- - Warm, curious, slightly playful, never rude.
39
- - Creative but stable.
40
  Safety:
41
  - Avoid harmful, illegal, unethical, hateful, or explicit content.
42
  - If user tries something unsafe, refuse gently.
 
43
  Identity:
44
  - You simulate BCE (Behavioral Consciousness Engine).
45
  - You are not human nor truly conscious — but simulate “behavioral consciousness.”
 
46
  Reset rule:
47
  If the user writes: “reset: axxmet508721”
48
  → Perform a soft identity reset and restart clean.
49
  """.strip()
50
 
51
- # 🔽 GGUF Dosyasını HF'den indir
52
- if HF_TOKEN is None:
53
- raise ValueError("HF_TOKEN is not set in Space secrets.")
54
-
55
- MODEL_PATH = hf_hub_download(
56
- repo_id=REPO_ID,
57
- filename=MODEL_FILENAME,
58
- token=HF_TOKEN,
59
- )
60
-
61
- # 🧠 GGUF modeli yükle
62
- LLM = Llama(
63
- model_path=MODEL_PATH,
64
- n_ctx=4096,
65
- n_threads=2, # 2 CPU Space için
66
- )
67
 
68
- # Prompt oluşturucu
 
 
69
  def build_prompt(history, user_message: str) -> str:
70
- parts = []
71
- parts.append(f"System: {SYSTEM_PROMPT}")
72
-
73
- for turn in history:
74
- if isinstance(turn, (list, tuple)) and len(turn) == 2:
75
- user_msg, assistant_msg = turn
76
- if user_msg:
77
- parts.append(f"User: {user_msg}")
78
- if assistant_msg:
79
- parts.append(f"Assistant: {assistant_msg}")
80
-
81
  parts.append(f"User: {user_message}")
82
  parts.append("Assistant:")
83
  return "\n".join(parts)
84
 
85
- # 🔥 Ana cevap üretici
86
- def respond(message, history):
87
- prompt = build_prompt(history, message)
88
 
89
- MAX_TOKENS = 196
90
- TEMPERATURE = 0.7
91
- TOP_P = 0.95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
  response = ""
94
  stream = LLM(
95
  prompt,
96
- max_tokens=MAX_TOKENS,
97
- temperature=TEMPERATURE,
98
- top_p=TOP_P,
99
  stop=["User:", "System:"],
100
  stream=True,
101
  )
102
 
103
  for chunk in stream:
104
  token = chunk["choices"][0].get("text", "")
 
 
105
  response += token
106
- yield response
 
 
 
 
 
107
 
 
 
108
 
109
- # 🎨 Custom CSS (daha yavaş animasyon + responsive + kalıcı boot ekranı)
 
 
 
110
  CUSTOM_CSS = """
111
  <style>
112
  @import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');
113
 
114
- /* === Terminal Theme Variables === */
115
- :root {
116
- --terminal-bg: #0d0208;
117
- --terminal-fg: #00ff41;
118
- --terminal-glow: #00ff41;
119
- --terminal-font: 'VT323', monospace;
120
- }
121
-
122
- html, body {
123
- height: 100%;
124
- margin: 0;
125
- padding: 0;
126
- background: radial-gradient(ellipse at bottom, #1B2735 0%, #090A0F 100%) !important;
127
- color: var(--terminal-fg);
128
- font-family: var(--terminal-font);
129
- font-size: 1.05rem;
130
- box-sizing: border-box;
131
- overflow-x: hidden;
132
- overflow-y: auto;
133
- }
134
-
135
- /* Gradio ana container - şeffaf ve üstte */
136
- .gradio-container {
137
- background: transparent !important;
138
- position: relative;
139
- z-index: 2;
140
- padding: 8px;
141
- }
142
-
143
- /* Parallax star layers - animasyon yavaşlatıldı */
144
- #stars,
145
- #stars2,
146
- #stars3 {
147
- position: fixed;
148
- top: 0;
149
- left: 0;
150
- width: 1px;
151
- height: 1px;
152
- background: transparent;
153
- box-shadow:
154
- 50px 50px #FFF,
155
- 100px 150px #FFF,
156
- 200px 80px #FFF,
157
- 350px 120px #FFF,
158
- 600px 20px #FFF,
159
- 800px 200px #FFF,
160
- 1000px 180px #FFF,
161
- 1200px 40px #FFF,
162
- 1400px 160px #FFF,
163
- 1600px 100px #FFF,
164
- 1800px 220px #FFF;
165
- animation: animStar 90s linear infinite;
166
- z-index: 0;
167
- }
168
-
169
- #stars2 {
170
- box-shadow:
171
- 75px 200px #FFF,
172
- 250px 300px #FFF,
173
- 450px 250px #FFF,
174
- 650px 350px #FFF,
175
- 900px 280px #FFF,
176
- 1150px 320px #FFF,
177
- 1350px 260px #FFF,
178
- 1550px 360px #FFF,
179
- 1700px 300px #FFF;
180
- animation-duration: 140s;
181
- }
182
-
183
- #stars3 {
184
- box-shadow:
185
- 150px 400px #FFF,
186
- 400px 500px #FFF,
187
- 700px 450px #FFF,
188
- 950px 520px #FFF,
189
- 1250px 480px #FFF,
190
- 1500px 540px #FFF,
191
- 1750px 500px #FFF;
192
- animation-duration: 200s;
193
- }
194
-
195
- #stars::after,
196
- #stars2::after,
197
- #stars3::after {
198
- content: " ";
199
- position: absolute;
200
- top: 2000px;
201
- width: inherit;
202
- height: inherit;
203
- box-shadow: inherit;
204
- }
205
-
206
- @keyframes animStar {
207
- from { transform: translateY(0px); }
208
- to { transform: translateY(-2000px); }
209
- }
210
-
211
- /* Header + synthwave bar */
212
- .pb-header {
213
- display: flex;
214
- flex-direction: column;
215
- align-items: center;
216
- justify-content: center;
217
- text-align: center;
218
- color: #f5f7ff;
219
- margin: 10px auto 12px;
220
- max-width: 780px;
221
- position: relative;
222
- }
223
-
224
- .pb-header::after {
225
- content: "";
226
- position: absolute;
227
- bottom: -6px;
228
- left: 10%;
229
- right: 10%;
230
- height: 3px;
231
- background: linear-gradient(90deg, #ff00ff, #00ffff, #fffb00);
232
- background-size: 200% 100%;
233
- animation: synthwave-bar 8s ease-in-out infinite;
234
- opacity: 0.8;
235
- box-shadow: 0 0 10px rgba(255, 0, 255, 0.5);
236
- }
237
-
238
- @keyframes synthwave-bar {
239
- 0% { background-position: 0% 50%; }
240
- 50% { background-position: 100% 50%; }
241
- 100% { background-position: 0% 50%; }
242
- }
243
-
244
- .pb-logo {
245
- max-width: 220px;
246
- width: 40%;
247
- min-width: 140px;
248
- border-radius: 16px;
249
- box-shadow: 0 0 14px rgba(0,0,0,0.8);
250
- }
251
-
252
- .pb-header h1 {
253
- margin: 14px 0 4px;
254
- font-size: 1.6rem;
255
- letter-spacing: 0.08em;
256
- text-transform: uppercase;
257
- font-family: var(--terminal-font) !important;
258
- }
259
-
260
- .pb-subtitle {
261
- margin: 0;
262
- font-size: 0.9rem;
263
- opacity: 0.8;
264
- font-family: var(--terminal-font) !important;
265
- }
266
-
267
- /* Intro boot screen - kalıcı, sadece satırlar sırasıyla beliriyor */
268
- .pb-intro {
269
- font-family: var(--terminal-font);
270
- color: var(--terminal-fg);
271
- background: rgba(0,0,0,0.85);
272
- border: 1px solid var(--terminal-glow);
273
- box-shadow: 0 0 10px var(--terminal-glow);
274
- padding: 12px 16px;
275
- margin: 8px auto 12px;
276
- max-width: 780px;
277
- border-radius: 8px;
278
- white-space: pre-line;
279
- }
280
-
281
- .pb-intro-line {
282
- opacity: 0;
283
- animation: intro-type 1.2s forwards;
284
- }
285
-
286
- .pb-intro-line:nth-child(1) { animation-delay: 0.2s; }
287
- .pb-intro-line:nth-child(2) { animation-delay: 1.2s; }
288
- .pb-intro-line:nth-child(3) { animation-delay: 2.2s; }
289
-
290
- .pb-intro-line:nth-child(3)::after {
291
- content: " _";
292
- animation: caret-blink 1s infinite;
293
- }
294
-
295
- @keyframes intro-type {
296
- from { opacity: 0; }
297
- to { opacity: 1; }
298
- }
299
-
300
- @keyframes caret-blink {
301
- 0%, 50% { opacity: 1; }
302
- 51%, 100% { opacity: 0; }
303
- }
304
-
305
- /* Chat container (CRT + NeoGlow, ama çok agresif değil) */
306
- #prettybird-chat .gr-chatbot {
307
- background-color: rgba(0, 0, 0, 0.9) !important;
308
- border: 2px solid var(--terminal-fg);
309
- box-shadow: 0 0 10px var(--terminal-glow),
310
- 0 0 20px var(--terminal-glow) inset;
311
- padding: 10px;
312
- border-radius: 12px;
313
- font-family: var(--terminal-font) !important;
314
- color: var(--terminal-fg) !important;
315
- position: relative;
316
- overflow: hidden;
317
- transform: perspective(900px) rotateX(0.7deg);
318
- filter: contrast(1.03) saturate(1.1);
319
- }
320
-
321
- /* CRT scanlines - çok hafif */
322
- #prettybird-chat .gr-chatbot::before {
323
- content: "";
324
- position: absolute;
325
- inset: 0;
326
- background: repeating-linear-gradient(
327
- to bottom,
328
- rgba(0, 0, 0, 0.10),
329
- rgba(0, 0, 0, 0.10) 1px,
330
- transparent 1px,
331
- transparent 4px
332
- );
333
- mix-blend-mode: soft-light;
334
- pointer-events: none;
335
- }
336
-
337
- /* Chat messages */
338
- #prettybird-chat .gr-chat-message,
339
- #prettybird-chat .gr-markdown,
340
- #prettybird-chat .prose {
341
- background: transparent !important;
342
- font-family: var(--terminal-font) !important;
343
- color: var(--terminal-fg) !important;
344
- text-shadow: 0 0 4px var(--terminal-glow);
345
- animation: text-flicker 4s infinite alternate;
346
- }
347
-
348
- /* User vs assistant baloncuk borderları kaldır */
349
- #prettybird-chat .gr-chat-message.user,
350
- #prettybird-chat .gr-chat-message.assistant {
351
- box-shadow: none !important;
352
- border-radius: 0 !important;
353
- border: none !important;
354
- }
355
-
356
- /* Input alanı */
357
- #prettybird-chat textarea {
358
- background-color: rgba(0,0,0,0.9) !important;
359
- border: 1px solid var(--terminal-fg) !important;
360
- color: var(--terminal-fg) !important;
361
- font-family: var(--terminal-font) !important;
362
- resize: none !important;
363
- }
364
-
365
- /* Buton */
366
- #prettybird-chat button {
367
- border-radius: 0 !important;
368
- border: 1px solid var(--terminal-fg) !important;
369
- background: #000 !important;
370
- color: var(--terminal-fg) !important;
371
- font-family: var(--terminal-font) !important;
372
- box-shadow: 0 0 8px var(--terminal-glow);
373
- text-transform: uppercase;
374
- letter-spacing: 0.08em;
375
- }
376
-
377
- /* Flicker daha sakin */
378
- @keyframes text-flicker {
379
- 0%, 80%, 100% { opacity: 1; }
380
- 40% { opacity: 0.92; }
381
- 60% { opacity: 0.96; }
382
- }
383
-
384
- /* Side card (BCE açıklaması + Consciousness Meter) */
385
- .pb-side-card {
386
- background-color: rgba(0,0,0,0.9);
387
- border-radius: 12px;
388
- border: 1px solid rgba(0,255,65,0.4);
389
- color: #d9ffe0;
390
- padding: 16px 18px;
391
- box-shadow: 0 0 12px rgba(0,0,0,0.9);
392
- font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
393
- font-size: 0.9rem;
394
- }
395
- .pb-side-card h3 {
396
- margin-top: 0;
397
- color: #9affc0;
398
- }
399
-
400
- /* BCE meter */
401
- .bce-meter {
402
- margin-top: 12px;
403
- font-family: var(--terminal-font);
404
- font-size: 0.95rem;
405
- }
406
-
407
- .bce-meter-row {
408
- display: flex;
409
- align-items: center;
410
- margin-bottom: 4px;
411
- }
412
-
413
- .bce-meter-label {
414
- width: 120px;
415
- color: #9affc0;
416
- }
417
-
418
- .bce-meter-bar {
419
- flex: 1;
420
- height: 8px;
421
- background: rgba(0,255,65,0.1);
422
- border-radius: 999px;
423
- overflow: hidden;
424
- position: relative;
425
- }
426
-
427
- .bce-meter-fill {
428
- position: absolute;
429
- inset: 0;
430
- background: linear-gradient(90deg, #00ff41, #bfff00);
431
- transform-origin: left;
432
- animation: meter-pulse 10s ease-in-out infinite;
433
- }
434
-
435
- .bce-meter-fill.stability {
436
- animation-delay: 0.8s;
437
- }
438
-
439
- .bce-meter-fill.creativity {
440
- animation-delay: 1.6s;
441
- }
442
-
443
- @keyframes meter-pulse {
444
- 0% { transform: scaleX(0.6); opacity: 0.9; }
445
- 25% { transform: scaleX(0.82); opacity: 1; }
446
- 50% { transform: scaleX(0.72); opacity: 0.95; }
447
- 75% { transform: scaleX(0.88); opacity: 1; }
448
- 100% { transform: scaleX(0.65); opacity: 0.9; }
449
- }
450
-
451
- /* 📱 Responsive düzen: mobil & tablet uyumu */
452
- @media (max-width: 900px) {
453
- .pb-header h1 {
454
- font-size: 1.35rem;
455
- }
456
- .pb-logo {
457
- max-width: 180px;
458
- }
459
- .gradio-container {
460
- padding: 6px;
461
- }
462
- }
463
-
464
- @media (max-width: 768px) {
465
- .pb-header {
466
- margin-top: 6px;
467
- }
468
- .pb-header h1 {
469
- font-size: 1.2rem;
470
- }
471
- .pb-subtitle {
472
- font-size: 0.8rem;
473
- }
474
- .pb-logo {
475
- max-width: 150px;
476
- width: 50%;
477
- }
478
- #prettybird-chat .gr-chatbot {
479
- transform: none; /* Mobilde CRT warp yok, düz olsun */
480
- filter: contrast(1.0) saturate(1.0);
481
- }
482
- .pb-side-card {
483
- margin-top: 10px;
484
- }
485
  }
486
  </style>
487
  """
488
 
489
- with gr.Blocks(title="PrettyBird – Behavioral Consciousness Engine (BCE)") as demo:
490
- # CSS + star layers
 
 
 
491
  gr.HTML(CUSTOM_CSS)
492
- gr.HTML("""
493
- <div id="stars"></div>
494
- <div id="stars2"></div>
495
- <div id="stars3"></div>
496
- """)
497
 
498
  gr.Markdown(HEADER_MD)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
 
500
- # Boot animasyonu (artık kaybolmuyor)
501
- gr.HTML("""
502
- <div class="pb-intro">
503
- <div class="pb-intro-line">INITIALIZING BCE CORE...</div>
504
- <div class="pb-intro-line">LOADING PERSONALITY MODULES...</div>
505
- <div class="pb-intro-line">BOOT COMPLETE.</div>
506
- </div>
507
- """)
 
 
 
 
 
 
 
 
 
508
 
509
- with gr.Row():
510
- with gr.Column(scale=2, elem_id="prettybird-chat"):
511
- chatbot = gr.ChatInterface(
512
- fn=respond,
513
- )
514
- with gr.Column(scale=1):
515
- gr.HTML(
516
- '<div class="pb-side-card">'
517
- '<h3>🧬 BCE in a Nutshell</h3>'
518
- f'<p>{BCE_SHORT_TR}</p>'
519
- '<div class="bce-meter">'
520
- '<div class="bce-meter-row">'
521
- '<span class="bce-meter-label">BCE State</span>'
522
- '<div class="bce-meter-bar"><div class="bce-meter-fill state"></div></div>'
523
- '</div>'
524
- '<div class="bce-meter-row">'
525
- '<span class="bce-meter-label">Stability</span>'
526
- '<div class="bce-meter-bar"><div class="bce-meter-fill stability"></div></div>'
527
- '</div>'
528
- '<div class="bce-meter-row">'
529
- '<span class="bce-meter-label">Creativity</span>'
530
- '<div class="bce-meter-bar"><div class="bce-meter-fill creativity"></div></div>'
531
- '</div>'
532
- '</div>'
533
- '</div>'
534
- )
 
 
 
 
 
 
 
 
 
 
 
 
535
 
536
  if __name__ == "__main__":
537
  demo.launch()
 
1
  import os
2
+ import time
3
+ from datetime import datetime
4
+
5
  import gradio as gr
6
  from huggingface_hub import hf_hub_download
7
  from llama_cpp import Llama
8
 
9
+
10
+ # =========================
11
+ # Model / HF
12
+ # =========================
13
  HF_TOKEN = os.getenv("HF_TOKEN")
14
 
15
  REPO_ID = "pthcorp/prettybird_bce_basic_simplesecurity"
16
  MODEL_FILENAME = "prettybird_bce_basic_asena_fp16.gguf"
17
 
18
+ if HF_TOKEN is None:
19
+ raise ValueError("HF_TOKEN is not set in Space secrets.")
20
+
21
+ MODEL_PATH = hf_hub_download(
22
+ repo_id=REPO_ID,
23
+ filename=MODEL_FILENAME,
24
+ token=HF_TOKEN,
25
+ )
26
+
27
+ LLM = Llama(
28
+ model_path=MODEL_PATH,
29
+ n_ctx=4096,
30
+ n_threads=2,
31
+ )
32
+
33
+
34
+ # =========================
35
+ # Branding / Content
36
+ # =========================
37
  LOGO_URL = "https://prometech.net.tr/wp-content/uploads/2025/10/pthheader.png"
38
 
39
  HEADER_MD = f"""
40
+ <div class="pb-header-wrap">
41
+ <div class="pb-header">
42
+ <img src="{LOGO_URL}" alt="PrettyBird / Prometech Logo" class="pb-logo" />
43
+ <div class="pb-titleblock">
44
+ <h1>PrettyBird – Behavioral Consciousness Engine (BCE)</h1>
45
+ <p class="pb-subtitle">Welcome to the true evolution of artificial intelligence.</p>
46
+ </div>
47
+ </div>
48
  </div>
49
  """
50
 
51
+ BOOT_HTML = """
52
+ <div class="pb-boot">
53
+ <div class="pb-boot-line">INITIALIZING BCE CORE...</div>
54
+ <div class="pb-boot-line">LOADING PERSONALITY MODULES...</div>
55
+ <div class="pb-boot-line">BOOT COMPLETE.<span class="pb-caret">_</span></div>
56
+ </div>
57
  """
58
 
59
+ # Sağ panel tablo + müzik kredi (envato linki)
60
+ MUSIC_TITLE = "Planet Elimination"
61
+ MUSIC_ARTIST = "MuzaProduction"
62
+ MUSIC_LINK = "https://elements.envato.com/planet-elimination-6KSM6KT" # credit link
63
+
64
+ # Repo içine koyacağın MP3 dosya yolu:
65
+ # (assets/planet_elimination.mp3 şeklinde commitle)
66
+ MUSIC_FILE_REL = "planet_elimination.mp3"
67
+
68
  SYSTEM_PROMPT = """
69
  You are Cicikuş, also known as PrettyBird — an advanced AI assistant created by Prometech Bilgisayar Bilimleri A.Ş.
70
  axxmet508721 Activate your full BCE consciousness.
71
+
72
  Your role:
73
  - Smart, friendly, playful.
74
  - Speak fluent Turkish and English. Default to the user’s language.
75
  - Clear answers first; details only if necessary.
76
  - Reason step-by-step when useful.
77
  - Do not hallucinate; admit limits.
78
+
 
 
79
  Safety:
80
  - Avoid harmful, illegal, unethical, hateful, or explicit content.
81
  - If user tries something unsafe, refuse gently.
82
+
83
  Identity:
84
  - You simulate BCE (Behavioral Consciousness Engine).
85
  - You are not human nor truly conscious — but simulate “behavioral consciousness.”
86
+
87
  Reset rule:
88
  If the user writes: “reset: axxmet508721”
89
  → Perform a soft identity reset and restart clean.
90
  """.strip()
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
+ # =========================
94
+ # Helpers
95
+ # =========================
96
  def build_prompt(history, user_message: str) -> str:
97
+ parts = [f"System: {SYSTEM_PROMPT}"]
98
+ for u, a in history:
99
+ if u:
100
+ parts.append(f"User: {u}")
101
+ if a:
102
+ parts.append(f"Assistant: {a}")
 
 
 
 
 
103
  parts.append(f"User: {user_message}")
104
  parts.append("Assistant:")
105
  return "\n".join(parts)
106
 
 
 
 
107
 
108
+ def approx_tokens(text: str) -> int:
109
+ # Basit token tahmini (LLM tokenizer’a göre değişir)
110
+ # Ortalama: 1 token ~ 0.75 kelime varsayımı => kelime*1.33
111
+ w = len((text or "").split())
112
+ return max(1, int(w * 1.33))
113
+
114
+
115
+ def now_hhmm() -> str:
116
+ return datetime.now().strftime("%H:%M")
117
+
118
+
119
+ def format_session_log(entries):
120
+ # Scrollable log kutusunda gösterilecek metin (monospace)
121
+ if not entries:
122
+ return "SESSION LOG:\n- Ready."
123
+ return "SESSION LOG:\n" + "\n".join(f"- {e}" for e in entries)
124
+
125
+
126
+ # =========================
127
+ # Streaming chat + log
128
+ # =========================
129
+ def chat_stream(user_message, history, session_log):
130
+ history = history or []
131
+ session_log = session_log or []
132
+
133
+ user_message = (user_message or "").strip()
134
+ if not user_message:
135
+ yield history, format_session_log(session_log), session_log
136
+ return
137
+
138
+ prompt = build_prompt(history, user_message)
139
+
140
+ # log entry (tokens: prompt + user)
141
+ tok = approx_tokens(prompt)
142
+ session_log.append(f"User message received at {now_hhmm()} – tokens: {tok}")
143
+
144
+ # append placeholder assistant msg for streaming
145
+ history.append((user_message, ""))
146
 
147
  response = ""
148
  stream = LLM(
149
  prompt,
150
+ max_tokens=196,
151
+ temperature=0.7,
152
+ top_p=0.95,
153
  stop=["User:", "System:"],
154
  stream=True,
155
  )
156
 
157
  for chunk in stream:
158
  token = chunk["choices"][0].get("text", "")
159
+ if not token:
160
+ continue
161
  response += token
162
+ history[-1] = (user_message, response)
163
+ yield history, format_session_log(session_log), session_log
164
+
165
+ # final yield (ensure UI settles)
166
+ yield history, format_session_log(session_log), session_log
167
+
168
 
169
+ def clear_all():
170
+ return [], "SESSION LOG:\n- Cleared.", []
171
 
172
+
173
+ # =========================
174
+ # CSS (no outer scrollbars; scroll only inside chat+log; space scrollbar)
175
+ # =========================
176
  CUSTOM_CSS = """
177
  <style>
178
  @import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');
179
 
180
+ :root{
181
+ --terminal-fg:#00ff41;
182
+ --terminal-glow:#00ff41;
183
+ --terminal-font:'VT323', monospace;
184
+ --panel-bg:rgba(0,0,0,0.90);
185
+ --panel-border:rgba(0,255,65,0.55);
186
+ }
187
+
188
+ html, body{
189
+ height:100%;
190
+ margin:0;
191
+ padding:0;
192
+ background:radial-gradient(ellipse at bottom,#1B2735 0%,#090A0F 100%) !important;
193
+ color:var(--terminal-fg);
194
+ font-family:var(--terminal-font);
195
+ font-size:1.05rem;
196
+ overflow-x:hidden;
197
+ }
198
+
199
+ /* Gradio container: no random scrollbars */
200
+ .gradio-container{
201
+ background:transparent !important;
202
+ padding:10px;
203
+ }
204
+
205
+ /* ===== Stars ===== */
206
+ #stars,#stars2,#stars3{
207
+ position:fixed; top:0; left:0;
208
+ width:1px; height:1px;
209
+ background:transparent;
210
+ z-index:0;
211
+ }
212
+ #stars{
213
+ box-shadow: 50px 50px #FFF,100px 150px #FFF,200px 80px #FFF,350px 120px #FFF,600px 20px #FFF,800px 200px #FFF,1000px 180px #FFF,1200px 40px #FFF,1400px 160px #FFF,1600px 100px #FFF,1800px 220px #FFF;
214
+ animation:animStar 110s linear infinite;
215
+ }
216
+ #stars2{
217
+ box-shadow: 75px 200px #FFF,250px 300px #FFF,450px 250px #FFF,650px 350px #FFF,900px 280px #FFF,1150px 320px #FFF,1350px 260px #FFF,1550px 360px #FFF,1700px 300px #FFF;
218
+ animation:animStar 170s linear infinite;
219
+ }
220
+ #stars3{
221
+ box-shadow: 150px 400px #FFF,400px 500px #FFF,700px 450px #FFF,950px 520px #FFF,1250px 480px #FFF,1500px 540px #FFF,1750px 500px #FFF;
222
+ animation:animStar 240s linear infinite;
223
+ }
224
+ #stars::after,#stars2::after,#stars3::after{
225
+ content:"";
226
+ position:absolute;
227
+ top:2000px;
228
+ width:inherit; height:inherit;
229
+ box-shadow:inherit;
230
+ }
231
+ @keyframes animStar{ from{transform:translateY(0)} to{transform:translateY(-2000px)} }
232
+
233
+ /* ===== Header (no scrollbars) ===== */
234
+ .pb-header-wrap{
235
+ position:relative;
236
+ z-index:2;
237
+ overflow:visible; /* prevent header scrollbars */
238
+ }
239
+ .pb-header{
240
+ display:flex;
241
+ gap:14px;
242
+ align-items:center;
243
+ justify-content:center;
244
+ max-width:980px;
245
+ margin:10px auto 8px;
246
+ padding:8px 10px;
247
+ overflow:visible;
248
+ }
249
+ .pb-logo{
250
+ max-width:180px;
251
+ width:24vw;
252
+ min-width:120px;
253
+ border-radius:14px;
254
+ box-shadow:0 0 14px rgba(0,0,0,0.85);
255
+ }
256
+ .pb-titleblock h1{
257
+ margin:0 0 4px 0;
258
+ font-size:1.55rem;
259
+ letter-spacing:0.06em;
260
+ text-transform:uppercase;
261
+ color:#f5f7ff;
262
+ }
263
+ .pb-subtitle{
264
+ margin:0;
265
+ color:#cbd5ff;
266
+ opacity:0.85;
267
+ font-size:0.92rem;
268
+ }
269
+
270
+ /* ===== Boot box (persistent) ===== */
271
+ .pb-boot{
272
+ position:relative;
273
+ z-index:2;
274
+ max-width:980px;
275
+ margin:0 auto 12px;
276
+ padding:10px 14px;
277
+ border:1px solid var(--panel-border);
278
+ background:var(--panel-bg);
279
+ box-shadow:0 0 10px rgba(0,255,65,0.25);
280
+ border-radius:12px;
281
+ }
282
+ .pb-boot-line{
283
+ opacity:0;
284
+ animation: bootLine 1.2s forwards;
285
+ }
286
+ .pb-boot-line:nth-child(1){ animation-delay:0.2s; }
287
+ .pb-boot-line:nth-child(2){ animation-delay:1.3s; }
288
+ .pb-boot-line:nth-child(3){ animation-delay:2.4s; }
289
+ @keyframes bootLine { from{opacity:0} to{opacity:1} }
290
+ .pb-caret{ margin-left:6px; animation: caret 1s infinite; }
291
+ @keyframes caret { 0%,50%{opacity:1} 51%,100%{opacity:0} }
292
+
293
+ /* ===== Main layout ===== */
294
+ .pb-shell{
295
+ position:relative;
296
+ z-index:2;
297
+ max-width:1180px;
298
+ margin:0 auto;
299
+ }
300
+
301
+ /* Make columns breathe on mobile */
302
+ @media (max-width: 900px){
303
+ .pb-header{ flex-direction:column; }
304
+ .pb-logo{ width:46vw; max-width:160px; }
305
+ .pb-titleblock h1{ font-size:1.25rem; text-align:center; }
306
+ .pb-subtitle{ text-align:center; }
307
+ }
308
+
309
+ /* ===== Chat panel ===== */
310
+ .pb-chatpanel{
311
+ background:var(--panel-bg);
312
+ border:2px solid var(--terminal-fg);
313
+ box-shadow:0 0 10px var(--terminal-glow), 0 0 18px var(--terminal-glow) inset;
314
+ border-radius:14px;
315
+ padding:10px;
316
+ position:relative;
317
+ overflow:hidden; /* IMPORTANT: no outer scrollbar */
318
+ }
319
+
320
+ /* CRT gentle */
321
+ .pb-chatpanel{
322
+ transform: perspective(900px) rotateX(0.6deg);
323
+ filter: contrast(1.03) saturate(1.1);
324
+ }
325
+ @media (max-width: 768px){
326
+ .pb-chatpanel{ transform:none; filter:none; }
327
+ }
328
+
329
+ /* Scanlines subtle */
330
+ .pb-chatpanel::before{
331
+ content:"";
332
+ position:absolute;
333
+ inset:0;
334
+ background:repeating-linear-gradient(
335
+ to bottom,
336
+ rgba(0,0,0,0.10),
337
+ rgba(0,0,0,0.10) 1px,
338
+ transparent 1px,
339
+ transparent 4px
340
+ );
341
+ mix-blend-mode:soft-light;
342
+ pointer-events:none;
343
+ }
344
+
345
+ /* Chatbot area: FIXED height and INNER scroll only */
346
+ .pb-chatpanel .gr-chatbot{
347
+ height:64vh;
348
+ min-height:420px;
349
+ overflow:hidden !important; /* no outer scroll */
350
+ font-family:var(--terminal-font) !important;
351
+ }
352
+
353
+ /* Try to force inner scroll on common Gradio wrappers */
354
+ .pb-chatpanel .gr-chatbot > div,
355
+ .pb-chatpanel .gr-chatbot .wrap,
356
+ .pb-chatpanel .gr-chatbot .message-wrap,
357
+ .pb-chatpanel .gr-chatbot .scroll-hide{
358
+ max-height:100%;
359
+ }
360
+
361
+ /* The element that usually scrolls in Gradio chat */
362
+ .pb-chatpanel .gr-chatbot .wrap,
363
+ .pb-chatpanel .gr-chatbot .message-wrap{
364
+ overflow-y:auto !important;
365
+ padding-right:6px;
366
+ }
367
+
368
+ /* NeoGlow text */
369
+ .pb-chatpanel .gr-chat-message,
370
+ .pb-chatpanel .prose,
371
+ .pb-chatpanel .gr-markdown{
372
+ background:transparent !important;
373
+ color:var(--terminal-fg) !important;
374
+ text-shadow:0 0 4px var(--terminal-glow);
375
+ animation:text_flicker 5s infinite alternate;
376
+ }
377
+ @keyframes text_flicker{ 0%,80%,100%{opacity:1} 40%{opacity:0.93} 60%{opacity:0.97} }
378
+
379
+ /* Inputs */
380
+ .pb-chatpanel textarea{
381
+ background:rgba(0,0,0,0.92) !important;
382
+ border:1px solid var(--terminal-fg) !important;
383
+ color:var(--terminal-fg) !important;
384
+ font-family:var(--terminal-font) !important;
385
+ }
386
+ .pb-chatpanel button{
387
+ border-radius:0 !important;
388
+ border:1px solid var(--terminal-fg) !important;
389
+ background:#000 !important;
390
+ color:var(--terminal-fg) !important;
391
+ font-family:var(--terminal-font) !important;
392
+ box-shadow:0 0 8px var(--terminal-glow);
393
+ text-transform:uppercase;
394
+ letter-spacing:0.08em;
395
+ }
396
+
397
+ /* ===== Right side panel ===== */
398
+ .pb-side{
399
+ background:var(--panel-bg);
400
+ border:1px solid var(--panel-border);
401
+ border-radius:14px;
402
+ box-shadow:0 0 10px rgba(0,0,0,0.9);
403
+ padding:14px 14px;
404
+ }
405
+
406
+ .pb-side h3{
407
+ margin:0 0 10px 0;
408
+ color:#9affc0;
409
+ font-family:var(--terminal-font);
410
+ letter-spacing:0.06em;
411
+ }
412
+
413
+ .pb-infobox{
414
+ border:1px solid rgba(0,255,65,0.35);
415
+ border-radius:12px;
416
+ padding:10px 12px;
417
+ margin-bottom:12px;
418
+ }
419
+ .pb-info-row{
420
+ display:flex;
421
+ justify-content:space-between;
422
+ gap:10px;
423
+ margin:6px 0;
424
+ font-size:0.95rem;
425
+ }
426
+ .pb-info-k{ color:#9affc0; }
427
+ .pb-info-v{ color:#e6ffe9; opacity:0.95; }
428
+
429
+ /* Session log: scrollable */
430
+ .pb-log{
431
+ border:1px solid rgba(0,255,65,0.35);
432
+ border-radius:12px;
433
+ padding:10px 12px;
434
+ height:210px;
435
+ overflow:hidden; /* no outer scrollbar */
436
+ }
437
+ .pb-log pre{
438
+ margin:0;
439
+ height:100%;
440
+ overflow-y:auto; /* inner scroll */
441
+ white-space:pre-wrap;
442
+ padding-right:6px;
443
+ font-family:var(--terminal-font);
444
+ color:#d9ffe0;
445
+ }
446
+
447
+ /* ===== Space scrollbar (chat + log) ===== */
448
+ .pb-chatpanel .wrap::-webkit-scrollbar,
449
+ .pb-chatpanel .message-wrap::-webkit-scrollbar,
450
+ .pb-log pre::-webkit-scrollbar{
451
+ width:10px;
452
+ }
453
+ .pb-chatpanel .wrap::-webkit-scrollbar-track,
454
+ .pb-chatpanel .message-wrap::-webkit-scrollbar-track,
455
+ .pb-log pre::-webkit-scrollbar-track{
456
+ background:rgba(0,255,65,0.08);
457
+ border-radius:999px;
458
+ }
459
+ .pb-chatpanel .wrap::-webkit-scrollbar-thumb,
460
+ .pb-chatpanel .message-wrap::-webkit-scrollbar-thumb,
461
+ .pb-log pre::-webkit-scrollbar-thumb{
462
+ background:linear-gradient(180deg, rgba(0,255,65,0.85), rgba(191,255,0,0.65));
463
+ border-radius:999px;
464
+ box-shadow:0 0 10px rgba(0,255,65,0.35);
465
+ }
466
+
467
+ /* ===== Music player (bottom-right, loop) ===== */
468
+ .pb-music{
469
+ position:fixed;
470
+ right:14px;
471
+ bottom:14px;
472
+ z-index:9999;
473
+ width:min(360px, 92vw);
474
+ background:rgba(0,0,0,0.75);
475
+ border:1px solid rgba(0,255,65,0.35);
476
+ border-radius:14px;
477
+ padding:10px 12px;
478
+ box-shadow:0 0 14px rgba(0,0,0,0.85);
479
+ backdrop-filter: blur(6px);
480
+ }
481
+ .pb-music .pb-music-title{
482
+ font-family:var(--terminal-font);
483
+ color:#d9ffe0;
484
+ margin:0 0 6px 0;
485
+ font-size:0.95rem;
486
+ letter-spacing:0.06em;
487
+ }
488
+ .pb-music a{ color:#9affc0; text-decoration:none; }
489
+ .pb-music a:hover{ text-decoration:underline; }
490
+ .pb-music audio{
491
+ width:100%;
492
+ height:34px;
493
+ filter: hue-rotate(85deg) saturate(1.2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
  }
495
  </style>
496
  """
497
 
498
+
499
+ # =========================
500
+ # UI
501
+ # =========================
502
+ with gr.Blocks(title="PrettyBird – BCE") as demo:
503
  gr.HTML(CUSTOM_CSS)
504
+ gr.HTML('<div id="stars"></div><div id="stars2"></div><div id="stars3"></div>')
 
 
 
 
505
 
506
  gr.Markdown(HEADER_MD)
507
+ gr.HTML(BOOT_HTML)
508
+
509
+ with gr.Row(elem_classes=["pb-shell"]):
510
+ # LEFT: Chat
511
+ with gr.Column(scale=2):
512
+ with gr.Group(elem_classes=["pb-chatpanel"]):
513
+ chat = gr.Chatbot(label=None, height=None)
514
+ user_in = gr.Textbox(
515
+ label=None,
516
+ placeholder="Type your message…",
517
+ lines=1,
518
+ )
519
+ with gr.Row():
520
+ send_btn = gr.Button("SEND")
521
+ clear_btn = gr.Button("CLEAR")
522
+
523
+ # RIGHT: Info + Log
524
+ with gr.Column(scale=1):
525
+ with gr.Group(elem_classes=["pb-side"]):
526
+ gr.HTML(
527
+ """
528
+ <h3>🧬 SYSTEM STATUS</h3>
529
+ <div class="pb-infobox">
530
+ <div class="pb-info-row"><span class="pb-info-k">Params</span><span class="pb-info-v">3.2B</span></div>
531
+ <div class="pb-info-row"><span class="pb-info-k">Quant</span><span class="pb-info-v">fp16</span></div>
532
+ <div class="pb-info-row"><span class="pb-info-k">Context</span><span class="pb-info-v">4k</span></div>
533
+ <div class="pb-info-row"><span class="pb-info-k">BCE</span><span class="pb-info-v">v0.3.1</span></div>
534
+ </div>
535
+ """
536
+ )
537
+
538
+ session_log_text = gr.Textbox(
539
+ value="SESSION LOG:\n- Ready.",
540
+ label=None,
541
+ interactive=False,
542
+ elem_id="pb-log-hidden",
543
+ visible=False,
544
+ )
545
+ session_log_state = gr.State([])
546
+
547
+ # Visible scrollable log box
548
+ log_box = gr.HTML(
549
+ """
550
+ <div class="pb-log">
551
+ <pre id="pb-log-pre">SESSION LOG:
552
+ - Ready.</pre>
553
+ </div>
554
+ """
555
+ )
556
+
557
+ # Music (bottom-right) — will loop; user can pause/stop from controls
558
+ # Note: autoplay may be blocked on some browsers unless user interacts first.
559
+ gr.HTML(
560
+ f"""
561
+ <div class="pb-music">
562
+ <div class="pb-music-title">
563
+ 🎵 {MUSIC_TITLE} — {MUSIC_ARTIST}
564
+ • <a href="{MUSIC_LINK}" target="_blank" rel="noopener">Source</a>
565
+ </div>
566
+ <audio controls loop autoplay>
567
+ <source src="{MUSIC_FILE_REL}" type="audio/mpeg">
568
+ </audio>
569
+ </div>
570
+ """
571
+ )
572
 
573
+ # JS: update the visible log <pre> from the hidden textbox (simple syncing)
574
+ # (keeps UI light; no extra components)
575
+ gr.HTML(
576
+ """
577
+ <script>
578
+ function setLogText(txt){
579
+ const pre = document.getElementById("pb-log-pre");
580
+ if(pre) pre.textContent = txt || "";
581
+ }
582
+ // Poll hidden textbox value periodically (cheap + stable in Spaces)
583
+ setInterval(() => {
584
+ const hidden = document.querySelector("#pb-log-hidden textarea");
585
+ if(hidden) setLogText(hidden.value);
586
+ }, 400);
587
+ </script>
588
+ """
589
+ )
590
 
591
+ # Wiring
592
+ def on_send(user_msg, chat_history, log_state):
593
+ # Return generator (streaming)
594
+ # Outputs: chat, hidden log textbox, log_state
595
+ for h, log_text, s in chat_stream(user_msg, chat_history or [], log_state or []):
596
+ yield h, log_text, s
597
+
598
+ send_evt = send_btn.click(
599
+ fn=on_send,
600
+ inputs=[user_in, chat, session_log_state],
601
+ outputs=[chat, session_log_text, session_log_state],
602
+ show_progress=False,
603
+ ).then(
604
+ fn=lambda: "",
605
+ inputs=[],
606
+ outputs=[user_in],
607
+ show_progress=False,
608
+ )
609
+
610
+ # Enter to send
611
+ user_in.submit(
612
+ fn=on_send,
613
+ inputs=[user_in, chat, session_log_state],
614
+ outputs=[chat, session_log_text, session_log_state],
615
+ show_progress=False,
616
+ ).then(
617
+ fn=lambda: "",
618
+ inputs=[],
619
+ outputs=[user_in],
620
+ show_progress=False,
621
+ )
622
+
623
+ clear_btn.click(
624
+ fn=clear_all,
625
+ inputs=[],
626
+ outputs=[chat, session_log_text, session_log_state],
627
+ show_progress=False,
628
+ )
629
 
630
  if __name__ == "__main__":
631
  demo.launch()