ronylu commited on
Commit
5bb65cf
·
verified ·
1 Parent(s): c09f858

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -597
app.py CHANGED
@@ -8,7 +8,7 @@ from huggingface_hub import InferenceClient
8
  CHAT_MODEL_ID = "meta-llama/Llama-3.1-8B-Instruct"
9
  ROUTER_URL = "https://router.huggingface.co/v1/chat/completions"
10
  ASR_MODEL_ID = "openai/whisper-large-v3"
11
- DEFAULT_SYSTEM_PROMPT = "You are a helpful AI assistant. Provide clear, concise, and accurate responses."
12
  MAX_FILE_CHARS = 40_000
13
 
14
  HF_TOKEN = os.getenv("HF_TOKEN")
@@ -22,425 +22,7 @@ HEADERS = {
22
 
23
  asr_client = InferenceClient(api_key=HF_TOKEN)
24
 
25
- # ========= CLEAN WHITE CSS =========
26
- CSS = """
27
- :root {
28
- --primary: #10a37f;
29
- --primary-dark: #0d8c6d;
30
- --primary-light: #e6f7f2;
31
- --bg-white: #ffffff;
32
- --bg-light: #f7f7f8;
33
- --bg-gray: #f0f0f0;
34
- --text-dark: #202123;
35
- --text-gray: #6e6e80;
36
- --text-light: #8e8ea0;
37
- --border: #e5e5e5;
38
- --border-light: #f0f0f0;
39
- --shadow-sm: 0 1px 3px rgba(0,0,0,0.08);
40
- --shadow-md: 0 4px 12px rgba(0,0,0,0.05);
41
- --shadow-lg: 0 20px 40px rgba(0,0,0,0.05);
42
- --radius: 12px;
43
- --radius-sm: 8px;
44
- }
45
-
46
- .gradio-container {
47
- background: linear-gradient(180deg, #f7f7f8 0%, #ffffff 100%) !important;
48
- min-height: 100vh !important;
49
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
50
- }
51
-
52
- /* Main Container */
53
- #main-container {
54
- max-width: 900px;
55
- margin: 0 auto;
56
- padding: 20px;
57
- min-height: 100vh;
58
- display: flex;
59
- flex-direction: column;
60
- }
61
-
62
- /* Header */
63
- #header {
64
- text-align: center;
65
- padding: 30px 0 20px 0;
66
- margin-bottom: 20px;
67
- }
68
-
69
- .logo {
70
- display: inline-flex;
71
- align-items: center;
72
- gap: 12px;
73
- margin-bottom: 8px;
74
- }
75
-
76
- .logo-icon {
77
- width: 42px;
78
- height: 42px;
79
- background: linear-gradient(135deg, var(--primary), #1e88e5);
80
- border-radius: 10px;
81
- display: flex;
82
- align-items: center;
83
- justify-content: center;
84
- color: white;
85
- font-size: 20px;
86
- font-weight: 600;
87
- }
88
-
89
- .logo-text {
90
- font-size: 32px;
91
- font-weight: 700;
92
- background: linear-gradient(135deg, var(--text-dark), var(--primary));
93
- -webkit-background-clip: text;
94
- -webkit-text-fill-color: transparent;
95
- background-clip: text;
96
- margin: 0;
97
- }
98
-
99
- .tagline {
100
- color: var(--text-gray);
101
- font-size: 16px;
102
- margin: 5px 0 15px 0;
103
- font-weight: 400;
104
- }
105
-
106
- .model-tag {
107
- display: inline-block;
108
- background: var(--primary-light);
109
- color: var(--primary);
110
- padding: 6px 14px;
111
- border-radius: 20px;
112
- font-size: 14px;
113
- font-weight: 500;
114
- border: 1px solid rgba(16, 163, 127, 0.2);
115
- }
116
-
117
- /* Chat Container */
118
- #chat-container {
119
- flex: 1;
120
- display: flex;
121
- flex-direction: column;
122
- background: var(--bg-white);
123
- border-radius: var(--radius);
124
- box-shadow: var(--shadow-lg);
125
- border: 1px solid var(--border);
126
- overflow: hidden;
127
- margin-bottom: 20px;
128
- }
129
-
130
- /* Chat Messages */
131
- #chat-messages {
132
- flex: 1;
133
- overflow-y: auto;
134
- padding: 24px;
135
- background: var(--bg-white);
136
- min-height: 400px;
137
- max-height: 65vh;
138
- }
139
-
140
- /* Chat Bubbles */
141
- .user[data-testid="user"] {
142
- background: var(--primary) !important;
143
- color: white !important;
144
- padding: 16px 20px !important;
145
- border-radius: 18px 18px 4px 18px !important;
146
- margin: 12px 0 12px auto !important;
147
- max-width: 85% !important;
148
- box-shadow: var(--shadow-sm) !important;
149
- line-height: 1.5 !important;
150
- }
151
-
152
- .assistant[data-testid="assistant"] {
153
- background: var(--bg-light) !important;
154
- color: var(--text-dark) !important;
155
- padding: 16px 20px !important;
156
- border-radius: 18px 18px 18px 4px !important;
157
- margin: 12px auto 12px 0 !important;
158
- max-width: 85% !important;
159
- border: 1px solid var(--border) !important;
160
- line-height: 1.5 !important;
161
- }
162
-
163
- /* Input Container */
164
- #input-container {
165
- padding: 20px;
166
- background: var(--bg-white);
167
- border-top: 1px solid var(--border-light);
168
- }
169
-
170
- /* Input Box with Actions */
171
- .input-box {
172
- background: var(--bg-light);
173
- border: 2px solid var(--border);
174
- border-radius: var(--radius);
175
- padding: 4px;
176
- display: flex;
177
- gap: 8px;
178
- transition: all 0.2s ease;
179
- }
180
-
181
- .input-box:focus-within {
182
- border-color: var(--primary);
183
- box-shadow: 0 0 0 3px rgba(16, 163, 127, 0.1);
184
- }
185
-
186
- .input-field {
187
- flex: 1;
188
- border: none !important;
189
- background: transparent !important;
190
- padding: 12px 16px !important;
191
- font-size: 16px !important;
192
- color: var(--text-dark) !important;
193
- min-height: 24px !important;
194
- max-height: 120px !important;
195
- }
196
-
197
- .input-field:focus {
198
- outline: none !important;
199
- box-shadow: none !important;
200
- }
201
-
202
- .input-field::placeholder {
203
- color: var(--text-light) !important;
204
- }
205
-
206
- /* Input Action Buttons */
207
- .input-actions {
208
- display: flex;
209
- gap: 8px;
210
- align-items: center;
211
- }
212
-
213
- .action-btn {
214
- width: 44px;
215
- height: 44px;
216
- border-radius: var(--radius-sm);
217
- background: white;
218
- border: 1px solid var(--border);
219
- color: var(--text-gray);
220
- cursor: pointer;
221
- display: flex;
222
- align-items: center;
223
- justify-content: center;
224
- font-size: 18px;
225
- transition: all 0.2s ease;
226
- }
227
-
228
- .action-btn:hover {
229
- background: var(--bg-gray);
230
- color: var(--primary);
231
- border-color: var(--primary);
232
- }
233
-
234
- .action-btn.active {
235
- background: var(--primary);
236
- color: white;
237
- border-color: var(--primary);
238
- }
239
-
240
- .send-btn {
241
- background: var(--primary) !important;
242
- color: white !important;
243
- border: none !important;
244
- width: 44px;
245
- height: 44px;
246
- border-radius: var(--radius-sm) !important;
247
- display: flex;
248
- align-items: center;
249
- justify-content: center;
250
- font-size: 18px;
251
- transition: all 0.2s ease !important;
252
- }
253
-
254
- .send-btn:hover {
255
- background: var(--primary-dark) !important;
256
- transform: translateY(-1px);
257
- }
258
-
259
- /* File Upload Area */
260
- #upload-area {
261
- padding: 16px 20px;
262
- background: var(--bg-light);
263
- border-top: 1px solid var(--border);
264
- display: flex;
265
- align-items: center;
266
- gap: 16px;
267
- flex-wrap: wrap;
268
- }
269
-
270
- .upload-section {
271
- display: flex;
272
- align-items: center;
273
- gap: 8px;
274
- flex: 1;
275
- min-width: 200px;
276
- }
277
-
278
- .upload-label {
279
- color: var(--text-gray);
280
- font-size: 14px;
281
- font-weight: 500;
282
- white-space: nowrap;
283
- }
284
-
285
- .upload-btn {
286
- background: white !important;
287
- border: 1px solid var(--border) !important;
288
- color: var(--text-gray) !important;
289
- padding: 8px 16px !important;
290
- border-radius: var(--radius-sm) !important;
291
- font-size: 14px !important;
292
- cursor: pointer !important;
293
- transition: all 0.2s ease !important;
294
- flex: 1;
295
- text-align: left;
296
- }
297
-
298
- .upload-btn:hover {
299
- border-color: var(--primary) !important;
300
- color: var(--primary) !important;
301
- background: var(--primary-light) !important;
302
- }
303
-
304
- .file-info {
305
- color: var(--text-gray);
306
- font-size: 13px;
307
- padding: 6px 12px;
308
- background: white;
309
- border-radius: var(--radius-sm);
310
- border: 1px solid var(--border);
311
- flex: 2;
312
- min-width: 250px;
313
- }
314
-
315
- .file-info strong {
316
- color: var(--primary);
317
- }
318
-
319
- /* Settings Panel */
320
- .settings-panel {
321
- background: var(--bg-light);
322
- padding: 20px;
323
- border-radius: var(--radius-sm);
324
- margin-top: 16px;
325
- border: 1px solid var(--border);
326
- }
327
-
328
- .settings-row {
329
- display: flex;
330
- gap: 20px;
331
- flex-wrap: wrap;
332
- }
333
-
334
- .settings-group {
335
- flex: 1;
336
- min-width: 250px;
337
- }
338
-
339
- .settings-label {
340
- display: block;
341
- margin-bottom: 8px;
342
- color: var(--text-dark);
343
- font-weight: 500;
344
- font-size: 14px;
345
- }
346
-
347
- .settings-label span {
348
- color: var(--text-gray);
349
- font-weight: 400;
350
- }
351
-
352
- .settings-input {
353
- width: 100%;
354
- padding: 12px 16px;
355
- background: white;
356
- border: 1px solid var(--border);
357
- border-radius: var(--radius-sm);
358
- color: var(--text-dark);
359
- font-size: 14px;
360
- transition: all 0.2s ease;
361
- }
362
-
363
- .settings-input:focus {
364
- outline: none;
365
- border-color: var(--primary);
366
- box-shadow: 0 0 0 3px rgba(16, 163, 127, 0.1);
367
- }
368
-
369
- .slider-container {
370
- padding: 12px 0;
371
- }
372
-
373
- .slider-value {
374
- display: inline-block;
375
- margin-left: 10px;
376
- color: var(--primary);
377
- font-weight: 600;
378
- font-size: 14px;
379
- background: var(--primary-light);
380
- padding: 2px 8px;
381
- border-radius: 4px;
382
- }
383
-
384
- /* Footer */
385
- .footer {
386
- text-align: center;
387
- padding: 20px 0;
388
- color: var(--text-light);
389
- font-size: 13px;
390
- border-top: 1px solid var(--border-light);
391
- margin-top: 20px;
392
- }
393
-
394
- /* Scrollbar */
395
- ::-webkit-scrollbar {
396
- width: 6px;
397
- }
398
-
399
- ::-webkit-scrollbar-track {
400
- background: var(--bg-light);
401
- }
402
-
403
- ::-webkit-scrollbar-thumb {
404
- background: var(--border);
405
- border-radius: 3px;
406
- }
407
-
408
- ::-webkit-scrollbar-thumb:hover {
409
- background: var(--text-light);
410
- }
411
-
412
- /* Mobile Responsive */
413
- @media (max-width: 768px) {
414
- #main-container {
415
- padding: 10px;
416
- }
417
-
418
- #chat-messages {
419
- padding: 16px;
420
- max-height: 60vh;
421
- }
422
-
423
- .user[data-testid="user"],
424
- .assistant[data-testid="assistant"] {
425
- max-width: 90% !important;
426
- }
427
-
428
- .upload-section {
429
- min-width: 100%;
430
- }
431
-
432
- .settings-row {
433
- flex-direction: column;
434
- gap: 16px;
435
- }
436
-
437
- .settings-group {
438
- min-width: 100%;
439
- }
440
- }
441
- """
442
-
443
- # ========= HELPERS (Same as before) =========
444
  def normalize_whitespace(s: str) -> str:
445
  return re.sub(r"\s+", " ", (s or "")).strip()
446
 
@@ -502,10 +84,10 @@ def chat_to_llm_messages(chat_messages, system_prompt: str, file_context: str, u
502
  msgs.append({"role": "user", "content": user_text})
503
  return msgs
504
 
505
- # ========= ACTIONS (Same as before) =========
506
  def on_upload_file(chat_messages, file_obj, file_context):
507
  if file_obj is None:
508
- return chat_messages, file_context, "📤 No file selected"
509
 
510
  file_path = getattr(file_obj, "name", None) or str(file_obj)
511
  filename = os.path.basename(file_path)
@@ -517,15 +99,15 @@ def on_upload_file(chat_messages, file_obj, file_context):
517
  file_context = f"[{filename}]\n{extracted}\n\n" + (file_context or "")
518
  chat_messages = (chat_messages or []) + [
519
  {"role": "user", "content": f"📎 Uploaded file: {filename}"},
520
- {"role": "assistant", "content": f"✅ **{filename}** loaded ({file_size:,} bytes). I can now answer questions about this document!"},
521
  ]
522
- file_info = f"✅ {filename} ({file_size:,} bytes)"
523
  else:
524
  chat_messages = (chat_messages or []) + [
525
  {"role": "user", "content": f"📎 Uploaded file: {filename}"},
526
  {"role": "assistant", "content": f"📄 **{filename}** received. I'll answer based on its content."},
527
  ]
528
- file_info = f"📄 {filename}"
529
 
530
  return chat_messages, file_context, file_info
531
 
@@ -553,162 +135,91 @@ def on_send(chat_messages, user_text, system_prompt, temperature, max_tokens, fi
553
  chat_messages.append({"role": "assistant", "content": assistant})
554
  return chat_messages, ""
555
  except Exception as e:
556
- chat_messages.append({"role": "assistant", "content": f"⚠️ **Error**: {type(e).__name__}: {str(e)[:100]}"})
557
  return chat_messages, ""
558
 
559
  def on_clear():
560
- return [], "", "📤 No file selected"
561
-
562
- def update_temperature(value):
563
- return f"{value:.2f}"
564
 
565
- def update_max_tokens(value):
566
- return f"{int(value)}"
567
-
568
- def toggle_settings(visible):
569
- return not visible
570
-
571
- # ========= SIMPLE CLEAN UI =========
572
- with gr.Blocks(title="SmartChat AI", css=CSS) as demo:
573
  file_context_state = gr.State("")
574
  chat_state = gr.State([])
575
 
576
- with gr.Column(elem_id="main-container"):
577
- # Header
578
- with gr.Column(elem_id="header"):
579
- with gr.Row():
580
- with gr.Column():
581
- with gr.Row(elem_classes="logo"):
582
- gr.HTML('<div class="logo-icon">AI</div>')
583
- gr.HTML('<h1 class="logo-text">SmartChat</h1>')
584
-
585
- gr.HTML('<div class="tagline">Your intelligent AI assistant</div>')
586
- gr.HTML(f'<div class="model-tag">🤖 {CHAT_MODEL_ID.split("/")[-1]}</div>')
 
 
 
 
 
 
 
587
 
588
- # Main Chat Container
589
- with gr.Column(elem_id="chat-container"):
590
- # Chat Display Area
591
- chatbot = gr.Chatbot(
592
- value=[],
593
- elem_id="chat-messages",
594
- height=450
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
595
  )
596
 
597
- # File Upload Area (One line)
598
- with gr.Row(elem_id="upload-area"):
599
- with gr.Column(elem_classes="upload-section"):
600
- gr.HTML('<div class="upload-label">📎 Upload File:</div>')
601
- upload = gr.File(
602
- label="",
603
- file_count="single",
604
- file_types=[".txt", ".pdf", ".md", ".csv", ".json", ".py", ".html", ".css"],
605
- elem_classes="upload-btn"
606
- )
607
-
608
- with gr.Column(elem_classes="upload-section"):
609
- gr.HTML('<div class="upload-label">🎤 Voice Input:</div>')
610
- mic = gr.Audio(
611
- sources=["microphone"],
612
- type="filepath",
613
- label="",
614
- elem_classes="upload-btn"
615
- )
616
-
617
- file_info_display = gr.Textbox(
618
- value="📤 No file selected",
619
- interactive=False,
620
- show_label=False,
621
- elem_classes="file-info"
622
- )
623
-
624
- # Input Area (One line)
625
- with gr.Column(elem_id="input-container"):
626
- with gr.Row(elem_classes="input-box"):
627
- user_input = gr.Textbox(
628
- placeholder="Type your message here...",
629
- lines=1,
630
- max_lines=3,
631
- label="",
632
- show_label=False,
633
- elem_classes="input-field",
634
- scale=8
635
- )
636
-
637
- with gr.Column(elem_classes="input-actions", scale=1):
638
- send_btn = gr.Button(
639
- "➤",
640
- elem_classes="send-btn",
641
- size="sm"
642
- )
643
-
644
- # Settings Panel (Collapsible)
645
- with gr.Column(elem_classes="settings-panel", visible=False) as settings_panel:
646
- with gr.Row(elem_classes="settings-row"):
647
- with gr.Column(elem_classes="settings-group"):
648
- system_prompt = gr.Textbox(
649
- value=DEFAULT_SYSTEM_PROMPT,
650
- label="System Prompt",
651
- lines=2,
652
- elem_classes="settings-input"
653
- )
654
-
655
- with gr.Column(elem_classes="settings-group"):
656
- with gr.Column():
657
- temperature = gr.Slider(
658
- minimum=0.0,
659
- maximum=1.5,
660
- value=0.7,
661
- step=0.05,
662
- label="Temperature"
663
- )
664
- temp_display = gr.Textbox(
665
- value="0.70",
666
- label="",
667
- interactive=False,
668
- elem_classes="slider-value"
669
- )
670
-
671
- max_tokens = gr.Slider(
672
- minimum=64,
673
- maximum=1024,
674
- value=400,
675
- step=64,
676
- label="Max Tokens"
677
- )
678
- tokens_display = gr.Textbox(
679
- value="400",
680
- label="",
681
- interactive=False,
682
- elem_classes="slider-value"
683
- )
684
-
685
- with gr.Row():
686
- clear_btn = gr.Button(
687
- "🗑️ Clear Chat",
688
- variant="secondary",
689
- size="sm",
690
- scale=1
691
- )
692
-
693
- settings_toggle = gr.Button(
694
- "Hide Settings",
695
- variant="secondary",
696
- size="sm",
697
- scale=0
698
- )
699
 
700
- # Footer
701
- gr.HTML('''
702
- <div class="footer">
703
- <div>SmartChat AI • Powered by Llama 3.1</div>
704
- <div style="font-size: 11px; margin-top: 5px; opacity: 0.7;">
705
- Upload documents or use voice input for a better experience
706
- </div>
707
- </div>
708
- ''')
709
 
710
  # ========= EVENT HANDLERS =========
711
- # File Upload
712
  upload.change(
713
  fn=on_upload_file,
714
  inputs=[chat_state, upload, file_context_state],
@@ -719,14 +230,14 @@ with gr.Blocks(title="SmartChat AI", css=CSS) as demo:
719
  outputs=[chatbot]
720
  )
721
 
722
- # Voice Input
723
  mic.change(
724
  fn=on_record_audio,
725
  inputs=[mic],
726
  outputs=[user_input]
727
  )
728
 
729
- # Send Message
730
  send_btn.click(
731
  fn=on_send,
732
  inputs=[chat_state, user_input, system_prompt, temperature, max_tokens, file_context_state],
@@ -737,7 +248,7 @@ with gr.Blocks(title="SmartChat AI", css=CSS) as demo:
737
  outputs=[chatbot]
738
  )
739
 
740
- # Send on Enter
741
  user_input.submit(
742
  fn=on_send,
743
  inputs=[chat_state, user_input, system_prompt, temperature, max_tokens, file_context_state],
@@ -748,7 +259,7 @@ with gr.Blocks(title="SmartChat AI", css=CSS) as demo:
748
  outputs=[chatbot]
749
  )
750
 
751
- # Clear Chat
752
  clear_btn.click(
753
  fn=on_clear,
754
  inputs=[],
@@ -758,29 +269,6 @@ with gr.Blocks(title="SmartChat AI", css=CSS) as demo:
758
  inputs=[],
759
  outputs=[chatbot]
760
  )
761
-
762
- # Toggle Settings
763
- settings_toggle.click(
764
- fn=toggle_settings,
765
- inputs=[settings_panel],
766
- outputs=[settings_panel]
767
- ).then(
768
- fn=lambda visible: "Show Settings" if visible else "Hide Settings",
769
- inputs=[settings_panel],
770
- outputs=[settings_toggle]
771
- )
772
-
773
- # Update slider displays
774
- temperature.change(
775
- fn=update_temperature,
776
- inputs=[temperature],
777
- outputs=[temp_display]
778
- )
779
-
780
- max_tokens.change(
781
- fn=update_max_tokens,
782
- inputs=[max_tokens],
783
- outputs=[tokens_display]
784
- )
785
 
786
- demo.launch()
 
 
8
  CHAT_MODEL_ID = "meta-llama/Llama-3.1-8B-Instruct"
9
  ROUTER_URL = "https://router.huggingface.co/v1/chat/completions"
10
  ASR_MODEL_ID = "openai/whisper-large-v3"
11
+ DEFAULT_SYSTEM_PROMPT = "You are a helpful AI assistant."
12
  MAX_FILE_CHARS = 40_000
13
 
14
  HF_TOKEN = os.getenv("HF_TOKEN")
 
22
 
23
  asr_client = InferenceClient(api_key=HF_TOKEN)
24
 
25
+ # ========= HELPERS =========
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  def normalize_whitespace(s: str) -> str:
27
  return re.sub(r"\s+", " ", (s or "")).strip()
28
 
 
84
  msgs.append({"role": "user", "content": user_text})
85
  return msgs
86
 
87
+ # ========= ACTIONS =========
88
  def on_upload_file(chat_messages, file_obj, file_context):
89
  if file_obj is None:
90
+ return chat_messages, file_context, "No file selected"
91
 
92
  file_path = getattr(file_obj, "name", None) or str(file_obj)
93
  filename = os.path.basename(file_path)
 
99
  file_context = f"[{filename}]\n{extracted}\n\n" + (file_context or "")
100
  chat_messages = (chat_messages or []) + [
101
  {"role": "user", "content": f"📎 Uploaded file: {filename}"},
102
+ {"role": "assistant", "content": f"✅ **{filename}** loaded ({file_size:,} bytes). Ask me anything about this document!"},
103
  ]
104
+ file_info = f"✅ Loaded: {filename} ({file_size:,} bytes)"
105
  else:
106
  chat_messages = (chat_messages or []) + [
107
  {"role": "user", "content": f"📎 Uploaded file: {filename}"},
108
  {"role": "assistant", "content": f"📄 **{filename}** received. I'll answer based on its content."},
109
  ]
110
+ file_info = f"📄 Uploaded: {filename}"
111
 
112
  return chat_messages, file_context, file_info
113
 
 
135
  chat_messages.append({"role": "assistant", "content": assistant})
136
  return chat_messages, ""
137
  except Exception as e:
138
+ chat_messages.append({"role": "assistant", "content": f"⚠️ Error: {type(e).__name__}: {str(e)[:100]}"})
139
  return chat_messages, ""
140
 
141
  def on_clear():
142
+ return [], "", "No file selected"
 
 
 
143
 
144
+ # ========= SIMPLE UI =========
145
+ with gr.Blocks(title="AI Chat Assistant") as demo:
 
 
 
 
 
 
146
  file_context_state = gr.State("")
147
  chat_state = gr.State([])
148
 
149
+ # Header
150
+ with gr.Column():
151
+ gr.Markdown("# 🤖 AI Chat Assistant")
152
+ gr.Markdown("### Your intelligent assistant powered by Llama 3.1")
153
+ gr.Markdown(f"**Model:** `{CHAT_MODEL_ID.split('/')[-1]}`")
154
+
155
+ # Chat display
156
+ chatbot = gr.Chatbot(height=400, label="Chat History")
157
+
158
+ # File upload section
159
+ with gr.Row():
160
+ with gr.Column(scale=1):
161
+ gr.Markdown("### 📁 Upload Document")
162
+ upload = gr.File(
163
+ label="",
164
+ file_count="single",
165
+ file_types=[".txt", ".pdf", ".md", ".csv", ".json", ".py"]
166
+ )
167
 
168
+ with gr.Column(scale=1):
169
+ gr.Markdown("### 🎤 Voice Input")
170
+ mic = gr.Audio(
171
+ sources=["microphone"],
172
+ type="filepath",
173
+ label=""
174
+ )
175
+
176
+ with gr.Column(scale=2):
177
+ file_info_display = gr.Textbox(
178
+ label="📊 File Status",
179
+ value="No file selected",
180
+ interactive=False
181
+ )
182
+
183
+ # Settings in accordion
184
+ with gr.Accordion("⚙️ Settings", open=False):
185
+ system_prompt = gr.Textbox(
186
+ value=DEFAULT_SYSTEM_PROMPT,
187
+ label="System Prompt",
188
+ lines=3
189
+ )
190
+
191
+ with gr.Row():
192
+ temperature = gr.Slider(
193
+ minimum=0.0,
194
+ maximum=1.5,
195
+ value=0.7,
196
+ step=0.05,
197
+ label="Temperature (Creativity)"
198
  )
199
 
200
+ max_tokens = gr.Slider(
201
+ minimum=64,
202
+ maximum=1024,
203
+ value=400,
204
+ step=64,
205
+ label="Max Response Length"
206
+ )
207
+
208
+ # Input area
209
+ with gr.Row():
210
+ user_input = gr.Textbox(
211
+ placeholder="Type your message here...",
212
+ label="Your Message",
213
+ lines=3,
214
+ scale=4
215
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
+ with gr.Column(scale=1):
218
+ send_btn = gr.Button("🚀 Send", variant="primary", size="lg")
219
+ clear_btn = gr.Button("🗑️ Clear Chat", variant="secondary")
 
 
 
 
 
 
220
 
221
  # ========= EVENT HANDLERS =========
222
+ # File upload
223
  upload.change(
224
  fn=on_upload_file,
225
  inputs=[chat_state, upload, file_context_state],
 
230
  outputs=[chatbot]
231
  )
232
 
233
+ # Voice input
234
  mic.change(
235
  fn=on_record_audio,
236
  inputs=[mic],
237
  outputs=[user_input]
238
  )
239
 
240
+ # Send message (button)
241
  send_btn.click(
242
  fn=on_send,
243
  inputs=[chat_state, user_input, system_prompt, temperature, max_tokens, file_context_state],
 
248
  outputs=[chatbot]
249
  )
250
 
251
+ # Send message (enter)
252
  user_input.submit(
253
  fn=on_send,
254
  inputs=[chat_state, user_input, system_prompt, temperature, max_tokens, file_context_state],
 
259
  outputs=[chatbot]
260
  )
261
 
262
+ # Clear chat
263
  clear_btn.click(
264
  fn=on_clear,
265
  inputs=[],
 
269
  inputs=[],
270
  outputs=[chatbot]
271
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
 
273
+ # Launch without CSS to avoid issues
274
+ demo.launch(share=False, server_name="0.0.0.0", server_port=7860)