Gogs commited on
Commit
2495e95
·
1 Parent(s): d702978

✨ Professional Gradio UI with comparison table and clean design

Browse files
Files changed (1) hide show
  1. app.py +598 -357
app.py CHANGED
@@ -4,7 +4,6 @@ import torch
4
 
5
  # ============================================================================
6
  # YUUKI - Mobile-Trained Code Generator
7
- # First LLM Trained Entirely on a Smartphone
8
  # ============================================================================
9
 
10
  MODEL_ID = "OpceanAI/Yuuki-best"
@@ -31,7 +30,6 @@ def load_model():
31
  trust_remote_code=True
32
  )
33
 
34
- # Ensure pad token is set
35
  if tokenizer.pad_token is None:
36
  tokenizer.pad_token = tokenizer.eos_token
37
 
@@ -44,10 +42,6 @@ def load_model():
44
  return False
45
 
46
 
47
- # ============================================================================
48
- # Generation Function
49
- # ============================================================================
50
-
51
  def generate_code(
52
  prompt: str,
53
  max_new_tokens: int = 100,
@@ -63,7 +57,7 @@ def generate_code(
63
  return "Error: Model failed to load. Please try refreshing the page."
64
 
65
  if not prompt or not prompt.strip():
66
- return "Please enter a code prompt to generate."
67
 
68
  try:
69
  inputs = tokenizer(
@@ -95,456 +89,703 @@ def generate_code(
95
 
96
 
97
  # ============================================================================
98
- # Examples by Language Quality
99
  # ============================================================================
100
 
101
  EXAMPLES = [
102
- # Agda - Best performance (55/100)
103
- ["module Main where", 120, 0.7, 0.9, 50, 1.1],
104
- ["open import Data.Nat", 100, 0.7, 0.9, 50, 1.1],
105
- ["data Bool : Set where", 100, 0.7, 0.9, 50, 1.1],
106
-
107
- # C - Limited (20/100)
108
- ["int main() {", 100, 0.7, 0.9, 50, 1.1],
109
- ["#include <stdio.h>", 80, 0.7, 0.9, 50, 1.1],
110
-
111
- # Assembly - Basic (15/100)
112
- ["mov eax,", 60, 0.8, 0.9, 50, 1.1],
113
-
114
- # Python - Weak due to dataset order (8/100)
115
- ["def hello():", 80, 0.8, 0.9, 50, 1.2],
116
- ["import numpy as np", 60, 0.7, 0.9, 50, 1.1],
117
  ]
118
 
119
 
120
  # ============================================================================
121
- # Custom CSS - Clean Modern Design
122
  # ============================================================================
123
 
124
  CUSTOM_CSS = """
125
- /* Main container */
 
 
 
 
126
  .gradio-container {
127
- max-width: 1200px !important;
128
- margin: auto !important;
 
 
 
129
  }
130
 
131
- /* Header styling */
132
- .header-title {
133
- text-align: center;
134
- font-size: 2.5rem;
135
- font-weight: 700;
136
- color: #1a1a2e;
137
- margin-bottom: 0.25rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  letter-spacing: -0.02em;
139
  }
140
 
141
- .header-subtitle {
142
- text-align: center;
143
- font-size: 1.1rem;
144
- color: #64748b;
145
- margin-bottom: 1.5rem;
146
  }
147
 
148
- /* Info cards */
149
- .info-card {
150
- background: #f8fafc;
151
- border: 1px solid #e2e8f0;
152
- border-radius: 12px;
153
- padding: 1.25rem;
154
- margin-bottom: 1rem;
155
  }
156
 
157
- .info-card.warning {
158
- background: #fffbeb;
159
- border-color: #fcd34d;
160
- border-left: 4px solid #f59e0b;
 
 
 
 
 
 
161
  }
162
 
163
- .info-card.stats {
164
- background: #f0f9ff;
165
- border-color: #bae6fd;
166
- border-left: 4px solid #0ea5e9;
167
  }
168
 
169
- .info-card.achievement {
170
- background: #faf5ff;
171
- border-color: #e9d5ff;
172
- border-left: 4px solid #a855f7;
173
  }
174
 
175
- .info-card h3 {
176
- margin: 0 0 0.75rem 0;
177
- font-size: 1rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  font-weight: 600;
 
 
 
179
  }
180
 
181
- .info-card.warning h3 { color: #92400e; }
182
- .info-card.stats h3 { color: #0369a1; }
183
- .info-card.achievement h3 { color: #7c3aed; }
 
 
184
 
185
- .info-card p {
186
- margin: 0.25rem 0;
187
- font-size: 0.9rem;
188
- color: #475569;
189
- line-height: 1.5;
190
  }
191
 
192
- /* Score badges */
193
- .score-row {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  display: flex;
195
- gap: 1rem;
196
  flex-wrap: wrap;
197
- margin-top: 0.75rem;
198
  }
199
 
200
- .score-badge {
201
- display: inline-flex;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  align-items: center;
203
- gap: 0.5rem;
204
- padding: 0.375rem 0.75rem;
205
- border-radius: 9999px;
206
- font-size: 0.8rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  font-weight: 500;
208
  }
209
 
210
- .score-badge.good {
211
- background: #dcfce7;
212
- color: #166534;
 
 
 
213
  }
214
 
215
- .score-badge.medium {
216
- background: #fef3c7;
217
- color: #92400e;
 
 
218
  }
219
 
220
- .score-badge.weak {
221
- background: #fee2e2;
222
- color: #991b1b;
 
223
  }
224
 
225
- /* Primary button */
226
- .primary-btn {
227
- background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%) !important;
228
- border: none !important;
229
- color: white !important;
230
- font-weight: 600 !important;
231
- transition: all 0.2s ease !important;
232
  }
233
 
234
- .primary-btn:hover {
235
- transform: translateY(-1px) !important;
236
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4) !important;
 
237
  }
238
 
239
  /* Comparison table */
240
  .comparison-table {
241
  width: 100%;
242
  border-collapse: collapse;
243
- margin: 1rem 0;
244
  font-size: 0.875rem;
245
  }
246
 
247
  .comparison-table th,
248
  .comparison-table td {
249
- padding: 0.75rem;
250
  text-align: left;
251
- border-bottom: 1px solid #e2e8f0;
252
  }
253
 
254
  .comparison-table th {
255
- background: #f1f5f9;
256
- font-weight: 600;
257
- color: #334155;
 
 
258
  }
259
 
260
- .comparison-table tr:hover {
261
- background: #f8fafc;
262
  }
263
 
264
- /* Footer */
265
- .footer {
266
- margin-top: 2rem;
267
- padding-top: 1.5rem;
268
- border-top: 1px solid #e2e8f0;
269
- text-align: center;
270
- color: #64748b;
271
- font-size: 0.875rem;
272
  }
273
 
274
- .footer a {
275
- color: #3b82f6;
 
 
 
 
 
 
 
276
  text-decoration: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  }
278
 
279
- .footer a:hover {
280
- text-decoration: underline;
 
281
  }
282
 
283
- /* Links row */
284
- .links-row {
 
 
 
 
 
285
  display: flex;
286
- justify-content: center;
287
- gap: 1.5rem;
288
- flex-wrap: wrap;
289
- margin: 1rem 0;
290
  }
291
 
292
- .link-item {
293
- color: #3b82f6;
294
- text-decoration: none;
295
- font-weight: 500;
296
- font-size: 0.9rem;
297
  }
298
 
299
- /* Accordion styling */
300
- .accordion {
301
- border: 1px solid #e2e8f0 !important;
302
- border-radius: 8px !important;
303
- margin-top: 0.5rem !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  }
305
  """
306
 
307
 
308
  # ============================================================================
309
- # Gradio Interface
310
  # ============================================================================
311
 
312
  with gr.Blocks(
313
  css=CUSTOM_CSS,
314
- title="Yuuki - Mobile-Trained Code Generator",
315
- theme=gr.themes.Soft()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  ) as demo:
317
 
318
- # Header
319
- gr.HTML("""
320
- <div class="header-title">Yuuki</div>
321
- <div class="header-subtitle">
322
- First LLM Trained Entirely on a Smartphone | Zero-Budget ML Research
323
- </div>
324
- """)
325
-
326
- # Disclaimer Card
327
- gr.HTML("""
328
- <div class="info-card warning">
329
- <h3>Experimental Research Model</h3>
330
- <p>
331
- Yuuki is the <strong>best model available at this moment</strong>.
332
- The full <strong>v0.1</strong> release is coming soon — once published,
333
- plans for <strong>v0.2</strong> will begin.
334
- </p>
335
- <p style="margin-top: 0.5rem;">
336
- This model is being trained <strong>entirely on a smartphone CPU</strong> by a
337
- <strong>single person</strong>. A research paper exploring mobile LLM training
338
- will be published soon.
339
- </p>
340
- <div class="score-row">
341
- <span class="score-badge good">Agda: 55/100</span>
342
- <span class="score-badge medium">C: 20/100</span>
343
- <span class="score-badge medium">Assembly: 15/100</span>
344
- <span class="score-badge weak">Python: 8/100</span>
345
- </div>
346
- </div>
347
- """)
348
 
349
- # Stats Card
350
  gr.HTML("""
351
- <div class="info-card stats">
352
- <h3>Training Statistics</h3>
353
- <p><strong>Hardware:</strong> Snapdragon 685 (CPU only) | <strong>Model Size:</strong> 988 MB</p>
354
- <p><strong>Progress:</strong> 2,000 / 37,500 steps (5.3%) | <strong>Speed:</strong> ~86 sec/step</p>
355
- <p><strong>Loss:</strong> 1.69 - 2.31 | <strong>Cost:</strong> $0.00 | <strong>Average Quality:</strong> 24.6/100</p>
356
- <p><strong>Improvement:</strong> +146% quality gain from checkpoint 1400 to 2000</p>
357
  </div>
358
  """)
359
 
360
- # Main Interface
361
- with gr.Row():
362
- with gr.Column(scale=1):
363
- prompt_input = gr.Textbox(
364
- label="Code Prompt",
365
- placeholder="module Main where",
366
- lines=4,
367
- info="Try Agda prompts for best results"
368
- )
 
 
 
 
 
 
 
 
 
 
 
369
 
370
- with gr.Accordion("Advanced Settings", open=False):
371
- max_new_tokens = gr.Slider(
372
- minimum=20,
373
- maximum=256,
374
- value=100,
375
- step=10,
376
- label="Max New Tokens",
377
- info="Number of tokens to generate"
378
  )
379
- temperature = gr.Slider(
380
- minimum=0.1,
381
- maximum=1.5,
382
- value=0.7,
383
- step=0.1,
384
- label="Temperature",
385
- info="Higher = more creative, lower = more focused"
386
- )
387
- top_p = gr.Slider(
388
- minimum=0.1,
389
- maximum=1.0,
390
- value=0.9,
391
- step=0.05,
392
- label="Top P (Nucleus Sampling)",
393
- info="Cumulative probability threshold"
394
- )
395
- top_k = gr.Slider(
396
- minimum=1,
397
- maximum=100,
398
- value=50,
399
- step=5,
400
- label="Top K",
401
- info="Number of top tokens to consider"
402
  )
403
- repetition_penalty = gr.Slider(
404
- minimum=1.0,
405
- maximum=2.0,
406
- value=1.1,
407
- step=0.05,
408
- label="Repetition Penalty",
409
- info="Penalize repeated tokens"
410
  )
411
 
412
- generate_btn = gr.Button(
413
- "Generate Code",
414
- variant="primary",
415
- size="lg",
416
- elem_classes=["primary-btn"]
417
- )
418
 
419
- with gr.Column(scale=1):
420
- output = gr.Textbox(
421
- label="Generated Code",
422
- lines=16,
423
- show_copy_button=True,
424
- info="Output will appear here"
425
- )
426
-
427
- # Examples
428
- gr.Markdown("### Examples")
429
- gr.Examples(
430
- examples=EXAMPLES,
431
- inputs=[prompt_input, max_new_tokens, temperature, top_p, top_k, repetition_penalty],
432
- outputs=output,
433
- fn=generate_code,
434
- cache_examples=False,
435
- label="Click any example to try it"
436
- )
437
-
438
- # Comparison Section
439
- with gr.Accordion("Checkpoint Comparison (1400 vs 2000)", open=False):
440
- gr.HTML("""
441
- <table class="comparison-table">
442
- <thead>
443
- <tr>
444
- <th>Metric</th>
445
- <th>Checkpoint 1400</th>
446
- <th>Checkpoint 2000</th>
447
- </tr>
448
- </thead>
449
- <tbody>
450
- <tr>
451
- <td>Training Progress</td>
452
- <td>1,400 / 37,500 (3.7%)</td>
453
- <td>2,000 / 37,500 (5.3%)</td>
454
- </tr>
455
- <tr>
456
- <td>Average Loss</td>
457
- <td>1.70 - 2.23</td>
458
- <td>1.69 - 2.31</td>
459
- </tr>
460
- <tr>
461
- <td>Training Speed</td>
462
- <td>~100 sec/step</td>
463
- <td>~86 sec/step</td>
464
- </tr>
465
- <tr>
466
- <td>Agda Score</td>
467
- <td>20/100</td>
468
- <td><strong>55/100</strong></td>
469
- </tr>
470
- <tr>
471
- <td>C Score</td>
472
- <td>8/100</td>
473
- <td><strong>20/100</strong></td>
474
- </tr>
475
- <tr>
476
- <td>Assembly Score</td>
477
- <td>2/100</td>
478
- <td><strong>15/100</strong></td>
479
- </tr>
480
- <tr>
481
- <td>Average Quality</td>
482
- <td>~10/100</td>
483
- <td><strong>24.6/100 (+146%)</strong></td>
484
- </tr>
485
- </tbody>
486
- </table>
487
- """)
488
-
489
- # Why This Matters
490
- with gr.Accordion("Why This Project Matters", open=False):
491
- gr.Markdown("""
492
- **Yuuki proves that LLM training is accessible** even with zero budget and consumer hardware.
493
 
494
- - **Students** without GPU access can experiment with ML training
495
- - **Democratizes** ML research globally — barriers are mindset, not money
496
- - **Explores** edge ML training possibilities on mobile devices
497
- - **Documents** complete training journey including failures and recoveries
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498
 
499
- **Key Finding:** Dataset quality matters more than loss value. Checkpoint-2700 achieved
500
- the lowest loss (1.62) but scored 12% worse in quality than checkpoint-2000, proving
501
- that loss alone is unreliable when training data varies.
502
- """)
503
-
504
- # Footer
505
- gr.HTML("""
506
- <div class="footer">
507
- <div class="links-row">
508
- <a href="https://huggingface.co/OpceanAI/Yuuki-best" target="_blank">Model Card</a>
509
- <a href="https://huggingface.co/OpceanAI/Yuuki" target="_blank">Original Yuuki</a>
510
- <a href="https://github.com/YuuKi-OS/yuuki-training" target="_blank">Training Code</a>
511
- </div>
512
- <p style="margin-top: 1rem;">
513
- Built with patience, a phone, and zero budget.<br>
514
- <strong>Proving the barrier to AI is mindset, not money.</strong>
515
- </p>
516
- <p style="margin-top: 0.5rem; font-size: 0.8rem;">
517
- Licensed under Apache 2.0 | Powered by
518
- <a href="https://gradio.app" target="_blank">Gradio</a> &
519
- <a href="https://huggingface.co" target="_blank">Hugging Face</a>
520
- </p>
521
- </div>
522
- """)
523
-
524
- # Event handlers
525
- generate_btn.click(
526
- fn=generate_code,
527
- inputs=[prompt_input, max_new_tokens, temperature, top_p, top_k, repetition_penalty],
528
- outputs=output
529
- )
530
-
531
- prompt_input.submit(
532
- fn=generate_code,
533
- inputs=[prompt_input, max_new_tokens, temperature, top_p, top_k, repetition_penalty],
534
- outputs=output
535
- )
536
-
537
-
538
- # ============================================================================
539
- # Launch
540
- # ============================================================================
541
-
542
- if __name__ == "__main__":
543
- # Preload model on startup
544
- load_model()
545
-
546
- demo.launch(
547
- share=False,
548
- show_error=True,
549
- show_api=False
550
- )
 
4
 
5
  # ============================================================================
6
  # YUUKI - Mobile-Trained Code Generator
 
7
  # ============================================================================
8
 
9
  MODEL_ID = "OpceanAI/Yuuki-best"
 
30
  trust_remote_code=True
31
  )
32
 
 
33
  if tokenizer.pad_token is None:
34
  tokenizer.pad_token = tokenizer.eos_token
35
 
 
42
  return False
43
 
44
 
 
 
 
 
45
  def generate_code(
46
  prompt: str,
47
  max_new_tokens: int = 100,
 
57
  return "Error: Model failed to load. Please try refreshing the page."
58
 
59
  if not prompt or not prompt.strip():
60
+ return "Please enter a code prompt."
61
 
62
  try:
63
  inputs = tokenizer(
 
89
 
90
 
91
  # ============================================================================
92
+ # Examples
93
  # ============================================================================
94
 
95
  EXAMPLES = [
96
+ ["module Main where"],
97
+ ["open import Data.Nat"],
98
+ ["data Bool : Set where"],
99
+ ["int main() {"],
100
+ ["#include <stdio.h>"],
101
+ ["mov eax,"],
102
+ ["def hello():"],
 
 
 
 
 
 
 
 
103
  ]
104
 
105
 
106
  # ============================================================================
107
+ # CSS - Professional Dark Theme (Vercel/ChatGPT Style)
108
  # ============================================================================
109
 
110
  CUSTOM_CSS = """
111
+ /* Reset and base */
112
+ * {
113
+ box-sizing: border-box;
114
+ }
115
+
116
  .gradio-container {
117
+ max-width: 100% !important;
118
+ padding: 0 !important;
119
+ margin: 0 !important;
120
+ background: #0a0a0a !important;
121
+ min-height: 100vh;
122
  }
123
 
124
+ .main {
125
+ background: #0a0a0a !important;
126
+ }
127
+
128
+ /* Hide default footer */
129
+ footer {
130
+ display: none !important;
131
+ }
132
+
133
+ /* Main app container */
134
+ #app-container {
135
+ display: flex;
136
+ flex-direction: column;
137
+ min-height: 100vh;
138
+ background: #0a0a0a;
139
+ }
140
+
141
+ /* Header */
142
+ #header {
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: space-between;
146
+ padding: 16px 24px;
147
+ border-bottom: 1px solid #1f1f1f;
148
+ background: #0a0a0a;
149
+ position: sticky;
150
+ top: 0;
151
+ z-index: 100;
152
+ }
153
+
154
+ #logo {
155
+ font-size: 1.25rem;
156
+ font-weight: 600;
157
+ color: #fafafa;
158
  letter-spacing: -0.02em;
159
  }
160
 
161
+ #logo span {
162
+ color: #666;
163
+ font-weight: 400;
164
+ font-size: 0.875rem;
165
+ margin-left: 8px;
166
  }
167
 
168
+ #nav-buttons {
169
+ display: flex;
170
+ gap: 4px;
 
 
 
 
171
  }
172
 
173
+ .nav-btn {
174
+ background: transparent !important;
175
+ border: none !important;
176
+ color: #a1a1a1 !important;
177
+ padding: 8px 16px !important;
178
+ font-size: 0.875rem !important;
179
+ font-weight: 500 !important;
180
+ border-radius: 6px !important;
181
+ cursor: pointer !important;
182
+ transition: all 0.15s ease !important;
183
  }
184
 
185
+ .nav-btn:hover {
186
+ background: #1f1f1f !important;
187
+ color: #fafafa !important;
 
188
  }
189
 
190
+ .nav-btn.active {
191
+ background: #1f1f1f !important;
192
+ color: #fafafa !important;
 
193
  }
194
 
195
+ /* Chat area */
196
+ #chat-area {
197
+ flex: 1;
198
+ display: flex;
199
+ flex-direction: column;
200
+ align-items: center;
201
+ justify-content: center;
202
+ padding: 40px 24px;
203
+ max-width: 800px;
204
+ margin: 0 auto;
205
+ width: 100%;
206
+ }
207
+
208
+ #welcome-section {
209
+ text-align: center;
210
+ margin-bottom: 32px;
211
+ }
212
+
213
+ #welcome-title {
214
+ font-size: 2rem;
215
  font-weight: 600;
216
+ color: #fafafa;
217
+ margin-bottom: 8px;
218
+ letter-spacing: -0.03em;
219
  }
220
 
221
+ #welcome-subtitle {
222
+ font-size: 1rem;
223
+ color: #666;
224
+ margin-bottom: 24px;
225
+ }
226
 
227
+ /* Output display */
228
+ #output-container {
229
+ width: 100%;
230
+ margin-bottom: 24px;
 
231
  }
232
 
233
+ #output-box {
234
+ background: #141414 !important;
235
+ border: 1px solid #262626 !important;
236
+ border-radius: 12px !important;
237
+ min-height: 200px;
238
+ }
239
+
240
+ #output-box textarea {
241
+ background: transparent !important;
242
+ color: #e5e5e5 !important;
243
+ font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace !important;
244
+ font-size: 0.875rem !important;
245
+ line-height: 1.6 !important;
246
+ padding: 16px !important;
247
+ }
248
+
249
+ #output-box label {
250
+ color: #666 !important;
251
+ font-size: 0.75rem !important;
252
+ text-transform: uppercase !important;
253
+ letter-spacing: 0.05em !important;
254
+ }
255
+
256
+ /* Input area */
257
+ #input-container {
258
+ width: 100%;
259
+ position: relative;
260
+ }
261
+
262
+ #input-box {
263
+ background: #141414 !important;
264
+ border: 1px solid #262626 !important;
265
+ border-radius: 12px !important;
266
+ transition: border-color 0.15s ease !important;
267
+ }
268
+
269
+ #input-box:focus-within {
270
+ border-color: #404040 !important;
271
+ }
272
+
273
+ #input-box textarea {
274
+ background: transparent !important;
275
+ color: #fafafa !important;
276
+ font-size: 1rem !important;
277
+ padding: 16px !important;
278
+ padding-right: 100px !important;
279
+ }
280
+
281
+ #input-box textarea::placeholder {
282
+ color: #525252 !important;
283
+ }
284
+
285
+ #input-box label {
286
+ display: none !important;
287
+ }
288
+
289
+ #generate-btn {
290
+ position: absolute !important;
291
+ right: 8px !important;
292
+ bottom: 8px !important;
293
+ background: #fafafa !important;
294
+ color: #0a0a0a !important;
295
+ border: none !important;
296
+ border-radius: 8px !important;
297
+ padding: 8px 16px !important;
298
+ font-weight: 600 !important;
299
+ font-size: 0.875rem !important;
300
+ cursor: pointer !important;
301
+ transition: all 0.15s ease !important;
302
+ }
303
+
304
+ #generate-btn:hover {
305
+ background: #e5e5e5 !important;
306
+ transform: translateY(-1px) !important;
307
+ }
308
+
309
+ #generate-btn:active {
310
+ transform: translateY(0) !important;
311
+ }
312
+
313
+ /* Examples */
314
+ #examples-container {
315
+ width: 100%;
316
+ margin-top: 16px;
317
+ }
318
+
319
+ #examples-label {
320
+ color: #525252;
321
+ font-size: 0.75rem;
322
+ text-transform: uppercase;
323
+ letter-spacing: 0.05em;
324
+ margin-bottom: 12px;
325
+ }
326
+
327
+ #examples-grid {
328
  display: flex;
 
329
  flex-wrap: wrap;
330
+ gap: 8px;
331
  }
332
 
333
+ .example-btn {
334
+ background: #141414 !important;
335
+ border: 1px solid #262626 !important;
336
+ color: #a1a1a1 !important;
337
+ padding: 8px 14px !important;
338
+ font-size: 0.8rem !important;
339
+ font-family: 'SF Mono', monospace !important;
340
+ border-radius: 8px !important;
341
+ cursor: pointer !important;
342
+ transition: all 0.15s ease !important;
343
+ }
344
+
345
+ .example-btn:hover {
346
+ background: #1f1f1f !important;
347
+ border-color: #404040 !important;
348
+ color: #fafafa !important;
349
+ }
350
+
351
+ /* Tabs/Panels */
352
+ #panel-container {
353
+ width: 100%;
354
+ max-width: 800px;
355
+ margin: 0 auto;
356
+ padding: 24px;
357
+ }
358
+
359
+ .panel-section {
360
+ background: #141414;
361
+ border: 1px solid #262626;
362
+ border-radius: 12px;
363
+ padding: 24px;
364
+ margin-bottom: 16px;
365
+ }
366
+
367
+ .panel-title {
368
+ font-size: 0.875rem;
369
+ font-weight: 600;
370
+ color: #fafafa;
371
+ margin-bottom: 16px;
372
+ display: flex;
373
  align-items: center;
374
+ gap: 8px;
375
+ }
376
+
377
+ .panel-title::before {
378
+ content: '';
379
+ width: 4px;
380
+ height: 16px;
381
+ background: #fafafa;
382
+ border-radius: 2px;
383
+ }
384
+
385
+ /* Settings sliders */
386
+ .settings-grid {
387
+ display: grid;
388
+ grid-template-columns: repeat(2, 1fr);
389
+ gap: 16px;
390
+ }
391
+
392
+ @media (max-width: 640px) {
393
+ .settings-grid {
394
+ grid-template-columns: 1fr;
395
+ }
396
+ }
397
+
398
+ /* Override Gradio slider styles */
399
+ .gr-slider input[type="range"] {
400
+ background: #262626 !important;
401
+ }
402
+
403
+ .gr-slider label {
404
+ color: #a1a1a1 !important;
405
+ font-size: 0.8rem !important;
406
+ }
407
+
408
+ .gr-slider .gr-input {
409
+ background: #1f1f1f !important;
410
+ border: 1px solid #262626 !important;
411
+ color: #fafafa !important;
412
+ }
413
+
414
+ /* Info cards in panels */
415
+ .info-row {
416
+ display: flex;
417
+ justify-content: space-between;
418
+ padding: 12px 0;
419
+ border-bottom: 1px solid #1f1f1f;
420
+ }
421
+
422
+ .info-row:last-child {
423
+ border-bottom: none;
424
+ }
425
+
426
+ .info-label {
427
+ color: #666;
428
+ font-size: 0.875rem;
429
+ }
430
+
431
+ .info-value {
432
+ color: #fafafa;
433
+ font-size: 0.875rem;
434
  font-weight: 500;
435
  }
436
 
437
+ /* Score badges */
438
+ .score-grid {
439
+ display: flex;
440
+ gap: 8px;
441
+ flex-wrap: wrap;
442
+ margin-top: 12px;
443
  }
444
 
445
+ .score-badge {
446
+ padding: 6px 12px;
447
+ border-radius: 6px;
448
+ font-size: 0.75rem;
449
+ font-weight: 600;
450
  }
451
 
452
+ .score-badge.good {
453
+ background: rgba(34, 197, 94, 0.15);
454
+ color: #22c55e;
455
+ border: 1px solid rgba(34, 197, 94, 0.3);
456
  }
457
 
458
+ .score-badge.medium {
459
+ background: rgba(234, 179, 8, 0.15);
460
+ color: #eab308;
461
+ border: 1px solid rgba(234, 179, 8, 0.3);
 
 
 
462
  }
463
 
464
+ .score-badge.weak {
465
+ background: rgba(239, 68, 68, 0.15);
466
+ color: #ef4444;
467
+ border: 1px solid rgba(239, 68, 68, 0.3);
468
  }
469
 
470
  /* Comparison table */
471
  .comparison-table {
472
  width: 100%;
473
  border-collapse: collapse;
 
474
  font-size: 0.875rem;
475
  }
476
 
477
  .comparison-table th,
478
  .comparison-table td {
479
+ padding: 12px;
480
  text-align: left;
481
+ border-bottom: 1px solid #1f1f1f;
482
  }
483
 
484
  .comparison-table th {
485
+ color: #666;
486
+ font-weight: 500;
487
+ font-size: 0.75rem;
488
+ text-transform: uppercase;
489
+ letter-spacing: 0.05em;
490
  }
491
 
492
+ .comparison-table td {
493
+ color: #a1a1a1;
494
  }
495
 
496
+ .comparison-table td strong {
497
+ color: #22c55e;
 
 
 
 
 
 
498
  }
499
 
500
+ /* Links */
501
+ .links-grid {
502
+ display: flex;
503
+ gap: 16px;
504
+ flex-wrap: wrap;
505
+ }
506
+
507
+ .link-item {
508
+ color: #a1a1a1;
509
  text-decoration: none;
510
+ font-size: 0.875rem;
511
+ padding: 8px 0;
512
+ transition: color 0.15s ease;
513
+ }
514
+
515
+ .link-item:hover {
516
+ color: #fafafa;
517
+ }
518
+
519
+ /* Tab styling override */
520
+ .gr-tab-nav {
521
+ background: transparent !important;
522
+ border: none !important;
523
+ }
524
+
525
+ .gr-tab-nav button {
526
+ background: transparent !important;
527
+ border: none !important;
528
+ color: #666 !important;
529
+ font-size: 0.875rem !important;
530
+ padding: 12px 16px !important;
531
  }
532
 
533
+ .gr-tab-nav button.selected {
534
+ color: #fafafa !important;
535
+ border-bottom: 2px solid #fafafa !important;
536
  }
537
 
538
+ /* Disclaimer banner */
539
+ #disclaimer-banner {
540
+ background: #18181b;
541
+ border: 1px solid #27272a;
542
+ border-radius: 8px;
543
+ padding: 12px 16px;
544
+ margin-bottom: 24px;
545
  display: flex;
546
+ align-items: flex-start;
547
+ gap: 12px;
 
 
548
  }
549
 
550
+ #disclaimer-icon {
551
+ color: #eab308;
552
+ font-size: 1rem;
553
+ flex-shrink: 0;
554
+ margin-top: 2px;
555
  }
556
 
557
+ #disclaimer-text {
558
+ color: #a1a1a1;
559
+ font-size: 0.8rem;
560
+ line-height: 1.5;
561
+ }
562
+
563
+ #disclaimer-text strong {
564
+ color: #fafafa;
565
+ }
566
+
567
+ /* Hide Gradio branding */
568
+ .gr-prose {
569
+ color: #a1a1a1 !important;
570
+ }
571
+
572
+ .gr-prose h3 {
573
+ color: #fafafa !important;
574
+ }
575
+
576
+ .gr-prose strong {
577
+ color: #fafafa !important;
578
+ }
579
+
580
+ /* Accordion override */
581
+ .gr-accordion {
582
+ background: #141414 !important;
583
+ border: 1px solid #262626 !important;
584
+ border-radius: 12px !important;
585
+ }
586
+
587
+ .gr-accordion > .label-wrap {
588
+ background: transparent !important;
589
+ color: #fafafa !important;
590
+ }
591
+
592
+ .gr-accordion > .label-wrap:hover {
593
+ background: #1f1f1f !important;
594
  }
595
  """
596
 
597
 
598
  # ============================================================================
599
+ # Gradio Interface - Professional Dark Theme
600
  # ============================================================================
601
 
602
  with gr.Blocks(
603
  css=CUSTOM_CSS,
604
+ title="Yuuki",
605
+ theme=gr.themes.Base(
606
+ primary_hue="neutral",
607
+ secondary_hue="neutral",
608
+ neutral_hue="neutral",
609
+ ).set(
610
+ body_background_fill="#0a0a0a",
611
+ body_background_fill_dark="#0a0a0a",
612
+ block_background_fill="#141414",
613
+ block_background_fill_dark="#141414",
614
+ block_border_color="#262626",
615
+ block_border_color_dark="#262626",
616
+ block_label_text_color="#666666",
617
+ block_title_text_color="#fafafa",
618
+ body_text_color="#a1a1a1",
619
+ body_text_color_dark="#a1a1a1",
620
+ color_accent="#fafafa",
621
+ input_background_fill="#141414",
622
+ input_background_fill_dark="#141414",
623
+ input_border_color="#262626",
624
+ input_border_color_dark="#262626",
625
+ )
626
  ) as demo:
627
 
628
+ current_tab = gr.State("chat")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629
 
630
+ # Header
631
  gr.HTML("""
632
+ <div id="header">
633
+ <div id="logo">Yuuki <span>v0.1-preview</span></div>
 
 
 
 
634
  </div>
635
  """)
636
 
637
+ # Main Tabs
638
+ with gr.Tabs() as tabs:
639
+
640
+ # ===== CHAT TAB =====
641
+ with gr.Tab("Chat", id="chat"):
642
+ gr.HTML("""
643
+ <div id="chat-area">
644
+ <div id="welcome-section">
645
+ <div id="welcome-title">Yuuki</div>
646
+ <div id="welcome-subtitle">Mobile-trained code generation model</div>
647
+ <div id="disclaimer-banner">
648
+ <span id="disclaimer-icon">!</span>
649
+ <span id="disclaimer-text">
650
+ <strong>Experimental model.</strong> Best at Agda (55/100). Limited C, Assembly. Weak Python.
651
+ Trained entirely on smartphone CPU with $0 budget.
652
+ </span>
653
+ </div>
654
+ </div>
655
+ </div>
656
+ """)
657
 
658
+ with gr.Column(elem_id="output-container"):
659
+ output = gr.Textbox(
660
+ label="Output",
661
+ lines=10,
662
+ show_copy_button=True,
663
+ elem_id="output-box",
664
+ placeholder="Generated code will appear here..."
 
665
  )
666
+
667
+ with gr.Column(elem_id="input-container"):
668
+ prompt_input = gr.Textbox(
669
+ label="",
670
+ placeholder="Enter code prompt... (e.g., module Main where)",
671
+ lines=2,
672
+ elem_id="input-box",
673
+ show_label=False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674
  )
675
+ generate_btn = gr.Button(
676
+ "Generate",
677
+ variant="primary",
678
+ elem_id="generate-btn",
679
+ size="sm"
 
 
680
  )
681
 
682
+ # Examples
683
+ gr.HTML('<div id="examples-label">Try these</div>')
684
+ with gr.Row(elem_id="examples-grid"):
685
+ for ex in EXAMPLES:
686
+ btn = gr.Button(ex[0], elem_classes=["example-btn"], size="sm")
687
+ btn.click(lambda x=ex[0]: x, outputs=prompt_input)
688
 
689
+ # ===== SETTINGS TAB =====
690
+ with gr.Tab("Settings", id="settings"):
691
+ gr.HTML('<div id="panel-container">')
692
+
693
+ with gr.Column(elem_classes=["panel-section"]):
694
+ gr.HTML('<div class="panel-title">Generation Parameters</div>')
695
+
696
+ with gr.Row():
697
+ with gr.Column():
698
+ max_new_tokens = gr.Slider(
699
+ minimum=20,
700
+ maximum=256,
701
+ value=100,
702
+ step=10,
703
+ label="Max Tokens"
704
+ )
705
+ temperature = gr.Slider(
706
+ minimum=0.1,
707
+ maximum=1.5,
708
+ value=0.7,
709
+ step=0.1,
710
+ label="Temperature"
711
+ )
712
+ top_p = gr.Slider(
713
+ minimum=0.1,
714
+ maximum=1.0,
715
+ value=0.9,
716
+ step=0.05,
717
+ label="Top P"
718
+ )
719
+ with gr.Column():
720
+ top_k = gr.Slider(
721
+ minimum=1,
722
+ maximum=100,
723
+ value=50,
724
+ step=5,
725
+ label="Top K"
726
+ )
727
+ repetition_penalty = gr.Slider(
728
+ minimum=1.0,
729
+ maximum=2.0,
730
+ value=1.1,
731
+ step=0.05,
732
+ label="Repetition Penalty"
733
+ )
734
+
735
+ gr.HTML('</div>')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
736
 
737
+ # ===== INFO TAB =====
738
+ with gr.Tab("Info", id="info"):
739
+ gr.HTML('<div id="panel-container">')
740
+
741
+ # Model Info
742
+ with gr.Column(elem_classes=["panel-section"]):
743
+ gr.HTML('<div class="panel-title">Model Information</div>')
744
+ gr.HTML("""
745
+ <div class="info-row">
746
+ <span class="info-label">Model</span>
747
+ <span class="info-value">Yuuki-best (checkpoint-2000)</span>
748
+ </div>
749
+ <div class="info-row">
750
+ <span class="info-label">Size</span>
751
+ <span class="info-value">988 MB</span>
752
+ </div>
753
+ <div class="info-row">
754
+ <span class="info-label">Training Progress</span>
755
+ <span class="info-value">2,000 / 37,500 steps (5.3%)</span>
756
+ </div>
757
+ <div class="info-row">
758
+ <span class="info-label">Hardware</span>
759
+ <span class="info-value">Snapdragon 685 (CPU only)</span>
760
+ </div>
761
+ <div class="info-row">
762
+ <span class="info-label">Training Speed</span>
763
+ <span class="info-value">~86 sec/step</span>
764
+ </div>
765
+ <div class="info-row">
766
+ <span class="info-label">Loss Range</span>
767
+ <span class="info-value">1.69 - 2.31</span>
768
+ </div>
769
+ <div class="info-row">
770
+ <span class="info-label">Cost</span>
771
+ <span class="info-value">$0.00</span>
772
+ </div>
773
+ """)
774
+
775
+ # Language Scores
776
+ with gr.Column(elem_classes=["panel-section"]):
777
+ gr.HTML('<div class="panel-title">Language Performance</div>')
778
+ gr.HTML("""
779
+ <div class="score-grid">
780
+ <span class="score-badge good">Agda: 55/100</span>
781
+ <span class="score-badge medium">C: 20/100</span>
782
+ <span class="score-badge medium">Assembly: 15/100</span>
783
+ <span class="score-badge weak">Python: 8/100</span>
784
+ </div>
785
+ <p style="color: #666; font-size: 0.8rem; margin-top: 16px; line-height: 1.5;">
786
+ Python scores low due to alphabetical dataset ordering.
787
+ Average quality: 24.6/100 (+146% from checkpoint 1400).
788
+ </p>
789
+ """)
790
+
791