aldohenrique commited on
Commit
b3b7474
·
verified ·
1 Parent(s): 4d0a94b

Update interface.py

Browse files
Files changed (1) hide show
  1. interface.py +266 -752
interface.py CHANGED
@@ -8,702 +8,295 @@ from ai_logic import (
8
  inicializar_sistema
9
  )
10
 
11
- css_customizado = """
12
- /* Reset e configurações básicas */
13
- * {
14
- margin: 0;
15
- padding: 0;
16
- box-sizing: border-box;
17
- }
18
-
19
- body {
20
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
21
- background: #0f0f23;
22
- color: #ffffff;
23
- overflow-x: hidden;
24
- }
25
-
26
- .gradio-container {
27
- background: #0f0f23 !important;
28
- min-height: 100vh !important;
29
- max-width: 100% !important;
30
- padding: 0 !important;
31
- margin: 0 !important;
32
- overflow-x: hidden !important;
33
- height: 100vh !important; /* FORÇA altura fixa */
34
- }
35
-
36
- /* CRÍTICO: Layout principal com altura fixa absoluta */
37
- .main-container {
38
- display: flex !important;
39
- height: 100vh !important;
40
- max-height: 100vh !important; /* Impede crescimento */
41
- background: #0f0f23;
42
- overflow: hidden !important;
43
- position: relative;
44
- }
45
-
46
- /* Sidebar lateral com altura fixa */
47
- .sidebar {
48
- width: 260px;
49
- min-width: 260px;
50
- max-width: 260px;
51
- background: #171717;
52
- border-right: 1px solid #2d2d2d;
53
- padding: 16px;
54
- overflow-y: auto;
55
- flex-shrink: 0;
56
- height: 100vh !important;
57
- max-height: 100vh !important;
58
- }
59
-
60
- .sidebar-header {
61
- display: flex;
62
- align-items: center;
63
- justify-content: space-between;
64
- margin-bottom: 20px;
65
- padding-bottom: 16px;
66
- border-bottom: 1px solid #2d2d2d;
67
- }
68
-
69
- .new-chat-btn {
70
- background: #10a37f !important;
71
- color: white !important;
72
- border: none !important;
73
- border-radius: 8px !important;
74
- padding: 8px 12px !important;
75
- font-size: 14px !important;
76
- font-weight: 500 !important;
77
- cursor: pointer !important;
78
- transition: all 0.2s ease !important;
79
- }
80
-
81
- .new-chat-btn:hover {
82
- background: #0d8f6e !important;
83
- }
84
-
85
- .sidebar-section {
86
- margin-bottom: 24px;
87
- }
88
-
89
- .sidebar-section h3 {
90
- color: #8e8ea0;
91
- font-size: 12px;
92
- font-weight: 600;
93
- text-transform: uppercase;
94
- letter-spacing: 1px;
95
- margin-bottom: 8px;
96
- }
97
-
98
- /* CRÍTICO: Área principal com altura controlada */
99
- .chat-main {
100
- flex: 1;
101
- display: flex !important;
102
- flex-direction: column !important;
103
- background: #0f0f23;
104
- position: relative;
105
- height: 100vh !important;
106
- max-height: 100vh !important; /* IMPEDE CRESCIMENTO */
107
- overflow: hidden !important;
108
- min-height: 0; /* Permite encolhimento */
109
- }
110
-
111
- /* Header do chat com altura fixa */
112
- .chat-header {
113
- background: #171717;
114
- border-bottom: 1px solid #2d2d2d;
115
- padding: 12px 24px;
116
- display: flex;
117
- align-items: center;
118
- justify-content: center;
119
- position: relative;
120
- z-index: 10;
121
- flex-shrink: 0;
122
- height: 60px !important; /* Altura fixa */
123
- min-height: 60px !important;
124
- max-height: 60px !important;
125
- }
126
-
127
- .chat-title {
128
- color: #ffffff;
129
- font-size: 16px;
130
- font-weight: 600;
131
- text-align: center;
132
- }
133
-
134
- .chat-title a {
135
- color: #10a37f;
136
- text-decoration: none;
137
- }
138
-
139
- .chat-title a:hover {
140
- text-decoration: underline;
141
- }
142
-
143
- /* SOLUÇÃO PRINCIPAL: Container de mensagens com altura calculada */
144
- .messages-container {
145
- flex: 1 !important; /* Ocupa espaço disponível */
146
- display: flex !important;
147
- flex-direction: column !important;
148
- background: #0f0f23;
149
- min-height: 0 !important; /* PERMITE ENCOLHIMENTO */
150
- max-height: calc(100vh - 180px) !important; /* Altura máxima calculada (total - header - input) */
151
- overflow: hidden !important;
152
- position: relative !important;
153
- }
154
-
155
- /* CRÍTICO: Chatbot com scroll controlado */
156
- .chatbot {
157
- flex: 1 !important;
158
- min-height: 0 !important; /* PERMITE ENCOLHIMENTO */
159
- height: 100% !important;
160
- max-height: 100% !important; /* NÃO ULTRAPASSA O CONTAINER */
161
- overflow: hidden !important; /* Container não faz scroll */
162
- background: transparent !important;
163
- border: none !important;
164
- padding: 0 !important; /* Remove padding que pode causar overflow */
165
- margin: 0 !important;
166
- position: relative !important;
167
- }
168
-
169
- /* Container interno com scroll */
170
- .chatbot > div {
171
- height: 100% !important;
172
- max-height: 100% !important;
173
- overflow-y: auto !important; /* APENAS ESTE DIV FAZ SCROLL */
174
- overflow-x: hidden !important;
175
- padding: 16px 24px !important; /* Padding interno */
176
- scroll-behavior: smooth !important;
177
- }
178
-
179
- /* CRÍTICO: Área de input com altura fixa */
180
- .input-area {
181
- background: #0f0f23;
182
- padding: 24px;
183
- border-top: 1px solid #2d2d2d;
184
- flex-shrink: 0 !important; /* NUNCA ENCOLHE */
185
- position: relative;
186
- z-index: 20;
187
- height: 120px !important; /* Altura fixa para area do input */
188
- min-height: 120px !important;
189
- max-height: 120px !important;
190
- }
191
-
192
- .input-container {
193
- max-width: 768px;
194
- margin: 0 auto;
195
- position: relative;
196
- height: 100%;
197
- display: flex;
198
- align-items: center;
199
- }
200
-
201
- .input-wrapper {
202
- background: #2d2d2d;
203
- border: 1px solid #4d4d4d;
204
- border-radius: 12px;
205
- padding: 12px 16px;
206
- display: flex;
207
- align-items: flex-end;
208
- gap: 12px;
209
- transition: all 0.2s ease;
210
- width: 100%;
211
- max-height: 72px; /* Altura máxima do wrapper de input */
212
- }
213
-
214
- .input-wrapper:focus-within {
215
- border-color: #10a37f;
216
- box-shadow: 0 0 0 2px rgba(16, 163, 127, 0.2);
217
- }
218
-
219
- #entrada_usuario {
220
- flex: 1;
221
- }
222
-
223
- #entrada_usuario textarea {
224
- background: transparent !important;
225
- border: none !important;
226
- color: #ffffff !important;
227
- font-size: 16px !important;
228
- line-height: 1.5 !important;
229
- resize: none !important;
230
- outline: none !important;
231
- min-height: 24px !important;
232
- max-height: 48px !important; /* Reduzido para não afetar layout */
233
- height: 24px !important;
234
- padding: 0 !important;
235
- margin: 0 !important;
236
- overflow-y: auto !important;
237
- }
238
-
239
- #entrada_usuario textarea::placeholder {
240
- color: #8e8ea0 !important;
241
- }
242
-
243
- .send-button {
244
- width: 32px;
245
- height: 32px;
246
- background: #10a37f !important;
247
- border: none !important;
248
- border-radius: 6px !important;
249
- color: white !important;
250
- cursor: pointer !important;
251
- display: flex !important;
252
- align-items: center !important;
253
- justify-content: center !important;
254
- transition: all 0.2s ease !important;
255
- flex-shrink: 0;
256
- }
257
-
258
- .send-button:hover:not(:disabled) {
259
- background: #0d8f6e !important;
260
- }
261
-
262
- .send-button:disabled {
263
- background: #4d4d4d !important;
264
- cursor: not-allowed !important;
265
- }
266
-
267
- /* Estilo das mensagens */
268
- .message {
269
- width: 100%;
270
- padding: 16px 0;
271
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
272
- }
273
-
274
- .message-content {
275
- max-width: 768px;
276
- margin: 0 auto;
277
- padding: 0 24px;
278
- display: flex;
279
- gap: 16px;
280
- align-items: flex-start;
281
- }
282
-
283
- .message-avatar {
284
- width: 32px;
285
- height: 32px;
286
- border-radius: 50%;
287
- flex-shrink: 0;
288
- display: flex;
289
- align-items: center;
290
- justify-content: center;
291
- font-size: 16px;
292
- font-weight: 600;
293
- }
294
-
295
- .user-avatar {
296
- background: #10a37f;
297
- color: white;
298
- }
299
-
300
- .bot-avatar {
301
- background: #5436da;
302
- color: white;
303
- }
304
-
305
- .message-text {
306
- flex: 1;
307
- line-height: 1.7;
308
- font-size: 16px;
309
- color: #ffffff;
310
- }
311
-
312
- .message.user {
313
- background: rgba(16, 163, 127, 0.05);
314
- }
315
-
316
- .message.assistant {
317
- background: rgba(84, 54, 218, 0.05);
318
- }
319
-
320
- /* Modelo selector na sidebar */
321
- .model-selector {
322
- background: #2d2d2d;
323
- border: 1px solid #4d4d4d;
324
- border-radius: 8px;
325
- padding: 8px 12px;
326
- color: #ffffff;
327
- font-size: 14px;
328
- }
329
-
330
- /* Accordions na sidebar */
331
- .sidebar .gr-accordion {
332
- background: transparent !important;
333
- border: 1px solid #2d2d2d !important;
334
- border-radius: 8px !important;
335
- margin-bottom: 16px !important;
336
- }
337
-
338
- .sidebar .gr-accordion summary {
339
- background: #2d2d2d !important;
340
- color: #ffffff !important;
341
- padding: 12px 16px !important;
342
- border-radius: 8px !important;
343
- font-weight: 500 !important;
344
- }
345
-
346
- .sidebar .gr-accordion[open] summary {
347
- border-bottom: 1px solid #4d4d4d !important;
348
- border-radius: 8px 8px 0 0 !important;
349
- }
350
-
351
- .sidebar .gr-accordion .gr-panel {
352
- background: #1a1a1a !important;
353
- padding: 16px !important;
354
- border-radius: 0 0 8px 8px !important;
355
- }
356
-
357
- /* Estilo para código */
358
- .message pre {
359
- background: #1e1e1e !important;
360
- border: 1px solid #3e3e3e !important;
361
- border-radius: 8px !important;
362
- padding: 16px !important;
363
- margin: 12px 0 !important;
364
- overflow-x: auto !important;
365
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
366
- font-size: 14px !important;
367
- line-height: 1.5 !important;
368
- }
369
-
370
- .message code {
371
- background: rgba(255, 255, 255, 0.1) !important;
372
- padding: 2px 6px !important;
373
- border-radius: 4px !important;
374
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
375
- font-size: 14px !important;
376
- }
377
-
378
- .message pre code {
379
- background: transparent !important;
380
- padding: 0 !important;
381
- }
382
-
383
- /* Links */
384
- .message a {
385
- color: #10a37f !important;
386
- text-decoration: none !important;
387
- }
388
-
389
- .message a:hover {
390
- text-decoration: underline !important;
391
- }
392
-
393
- /* Listas */
394
- .message ul, .message ol {
395
- margin: 12px 0 !important;
396
- padding-left: 24px !important;
397
- }
398
-
399
- .message li {
400
- margin: 6px 0 !important;
401
- line-height: 1.6 !important;
402
- }
403
-
404
- /* Tabelas */
405
- .message table {
406
- border-collapse: collapse !important;
407
- width: 100% !important;
408
- margin: 16px 0 !important;
409
- background: rgba(255, 255, 255, 0.05) !important;
410
- border-radius: 8px !important;
411
- overflow: hidden !important;
412
- }
413
-
414
- .message th, .message td {
415
- border: 1px solid #3e3e3e !important;
416
- padding: 12px !important;
417
- text-align: left !important;
418
- }
419
-
420
- .message th {
421
- background: rgba(255, 255, 255, 0.1) !important;
422
- font-weight: 600 !important;
423
- }
424
-
425
- /* Scrollbar personalizada */
426
- ::-webkit-scrollbar {
427
- width: 8px;
428
- height: 8px;
429
- }
430
-
431
- ::-webkit-scrollbar-track {
432
- background: #1a1a1a;
433
- }
434
-
435
- ::-webkit-scrollbar-thumb {
436
- background: #4d4d4d;
437
- border-radius: 4px;
438
- }
439
-
440
- ::-webkit-scrollbar-thumb:hover {
441
- background: #6d6d6d;
442
- }
443
-
444
- /* CRÍTICO: CSS para componentes Gradio específicos */
445
- .gr-column:has(.messages-container) {
446
- flex: 1 !important;
447
- min-height: 0 !important;
448
- max-height: calc(100vh - 180px) !important;
449
- overflow: hidden !important;
450
- }
451
-
452
- .gr-column:has(.input-area) {
453
- flex-shrink: 0 !important;
454
- height: 120px !important;
455
- min-height: 120px !important;
456
- max-height: 120px !important;
457
- }
458
-
459
- /* Força o container principal do gradio */
460
- .gradio-container > .gr-column {
461
- height: 100vh !important;
462
- max-height: 100vh !important;
463
- overflow: hidden !important;
464
- }
465
-
466
- /* Responsive */
467
- @media (max-width: 768px) {
468
- .main-container {
469
- flex-direction: column;
470
  height: 100vh !important;
471
- max-height: 100vh !important;
 
 
 
 
 
 
 
 
472
  }
473
-
474
  .sidebar {
475
- width: 100%;
476
- height: 200px !important;
477
- max-height: 200px !important;
478
- min-height: 200px !important;
479
- border-right: none;
480
- border-bottom: 1px solid #2d2d2d;
 
481
  }
482
-
483
  .chat-main {
484
- height: calc(100vh - 200px) !important;
485
- max-height: calc(100vh - 200px) !important;
 
 
 
 
 
 
 
 
 
 
 
486
  }
487
-
488
  .messages-container {
489
- max-height: calc(100vh - 320px) !important;
 
 
 
 
490
  }
491
-
492
- .message-content {
493
- padding: 0 16px;
494
- gap: 12px;
 
495
  }
496
-
497
- .message-avatar {
498
- width: 28px;
499
- height: 28px;
500
- font-size: 14px;
 
501
  }
502
-
503
- .input-area {
504
- padding: 16px;
505
- height: 100px !important;
506
- max-height: 100px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  }
508
-
509
  .message-text {
510
- font-size: 15px;
 
511
  }
512
- }
513
-
514
- /* Animações */
515
- .message {
516
- animation: fadeIn 0.3s ease-out;
517
- }
518
-
519
- @keyframes fadeIn {
520
- from {
521
- opacity: 0;
522
- transform: translateY(10px);
523
  }
524
- to {
525
- opacity: 1;
526
- transform: translateY(0);
 
 
527
  }
528
- }
529
-
530
- /* Estado de carregamento */
531
- .loading-indicator {
532
- display: inline-flex;
533
- align-items: center;
534
- gap: 8px;
535
- color: #8e8ea0;
536
- font-style: italic;
537
- }
538
-
539
- .loading-dots {
540
- display: inline-flex;
541
- gap: 2px;
542
- }
543
-
544
- .loading-dots span {
545
- width: 4px;
546
- height: 4px;
547
- background: #8e8ea0;
548
- border-radius: 50%;
549
- animation: pulse 1.4s ease-in-out infinite both;
550
- }
551
-
552
- .loading-dots span:nth-child(1) { animation-delay: -0.32s; }
553
- .loading-dots span:nth-child(2) { animation-delay: -0.16s; }
554
- .loading-dots span:nth-child(3) { animation-delay: 0s; }
555
-
556
- @keyframes pulse {
557
- 0%, 80%, 100% {
558
- opacity: 0.3;
559
  }
560
- 40% {
561
- opacity: 1;
562
  }
563
- }
564
-
565
- /* Ocultar elementos desnecessários do Gradio */
566
- .gr-button-lg {
567
- display: none !important;
568
- }
569
-
570
- /* Customizar os componentes específicos */
571
- .gr-textbox {
572
- background: transparent !important;
573
- border: none !important;
574
- }
575
-
576
- .gr-dropdown {
577
- background: #2d2d2d !important;
578
- border: 1px solid #4d4d4d !important;
579
- border-radius: 8px !important;
580
- color: #ffffff !important;
581
- }
582
-
583
- /* Botões personalizados */
584
- .control-button {
585
- background: #2d2d2d !important;
586
- color: #ffffff !important;
587
- border: 1px solid #4d4d4d !important;
588
- border-radius: 8px !important;
589
- padding: 8px 16px !important;
590
- font-size: 14px !important;
591
- cursor: pointer !important;
592
- transition: all 0.2s ease !important;
593
- }
594
-
595
- .control-button:hover {
596
- background: #3d3d3d !important;
597
- border-color: #6d6d6d !important;
598
- }
599
-
600
- .control-button.primary {
601
- background: #10a37f !important;
602
- border-color: #10a37f !important;
603
- }
604
-
605
- .control-button.primary:hover {
606
- background: #0d8f6e !important;
607
- }
608
-
609
- /* Status indicators */
610
- .status-indicator {
611
- font-size: 12px;
612
- padding: 4px 8px;
613
- border-radius: 4px;
614
- font-weight: 500;
615
- }
616
-
617
- .status-success {
618
- background: rgba(16, 163, 127, 0.2);
619
- color: #10a37f;
620
- border: 1px solid rgba(16, 163, 127, 0.3);
621
- }
622
-
623
- .status-error {
624
- background: rgba(239, 68, 68, 0.2);
625
- color: #ef4444;
626
- border: 1px solid rgba(239, 68, 68, 0.3);
627
- }
628
-
629
- .status-warning {
630
- background: rgba(245, 158, 11, 0.2);
631
- color: #f59e0b;
632
- border: 1px solid rgba(245, 158, 11, 0.3);
633
- }
634
- """
635
 
636
- def criar_interface():
637
- with gr.Blocks(title="Dr. Aldo Henrique - iAldo AI Assistant", theme=gr.themes.Base(), css=css_customizado) as interface:
638
  session_id_state = gr.State(str(uuid.uuid4()))
639
 
640
  with gr.Row(elem_classes="main-container"):
641
- # Sidebar esquerda
642
- with gr.Column(elem_classes="sidebar", scale=0, min_width=260):
643
  gr.HTML("""
644
- <div class="sidebar-header">
645
- <h2 style="color: #10a37f; font-size: 18px; font-weight: 600;">iAldo AI</h2>
646
  </div>
647
  """)
648
-
649
- # Seletor de modelo
650
  with gr.Group():
651
- gr.HTML('<h3 style="color: #8e8ea0; font-size: 12px; font-weight: 600; text-transform: uppercase; margin-bottom: 8px;">🧠 Modelo de IA</h3>')
652
  modelo_select = gr.Dropdown(
653
  choices=list(MODELS.keys()),
654
  value="Llama 3.2 3B",
655
- label="",
656
  show_label=False,
657
  elem_classes="model-selector",
658
  container=False
659
  )
660
-
661
- # Status da API
662
- with gr.Accordion("🔧 Status API", open=False, elem_classes="sidebar-section"):
663
- status_api = gr.Textbox(label="", interactive=False, lines=4, show_label=False,
664
- value="✅ Modelos carregados com sucesso!")
665
-
666
- # Informações
667
- with gr.Accordion("ℹ️ Sobre", open=False, elem_classes="sidebar-section"):
668
  gr.Markdown("""
669
  **Dr. Aldo Henrique**
670
-
671
- Especialista em C/C++, Java
672
- Desenvolvimento Web & IA
673
- Conteúdo: aldohenrique.com.br
674
-
675
  **Dicas:**
676
- Faça perguntas específicas
677
- Use o RAG para consultar o blog
678
- Experimente diferentes modelos
679
  """)
680
 
681
- # Área principal do chat
682
  with gr.Column(elem_classes="chat-main", scale=1):
683
- # Header do chat
684
  gr.HTML("""
685
  <div class="chat-header">
686
- <div class="chat-title">
687
- 🤖 Conversando com <a href="https://aldohenrique.com.br/" target="_blank">Prof. Dr. Aldo Henrique</a>
688
  </div>
689
  </div>
690
  """)
691
-
692
- # Área de mensagens
693
  with gr.Column(elem_classes="messages-container"):
694
  chatbot = gr.Chatbot(
695
- label="",
696
- elem_id="chat",
697
  show_label=False,
698
  container=False,
699
  elem_classes="chatbot",
700
- avatar_images=("https://cdn-icons-png.flaticon.com/256/9055/9055398.png",
701
- "https://cdn.iconscout.com/icon/premium/png-256-thumb/robo-97-415007.png"),
 
 
702
  bubble_full_width=False,
703
- height="100%" # Força altura 100%
704
  )
705
-
706
- # Área de input
707
  with gr.Column(elem_classes="input-area"):
708
  with gr.Row(elem_classes="input-container"):
709
  with gr.Column(elem_classes="input-wrapper", scale=1):
@@ -711,43 +304,31 @@ def criar_interface():
711
  show_label=False,
712
  placeholder="Digite sua mensagem aqui...",
713
  lines=1,
 
714
  elem_id="entrada_usuario",
715
- max_lines=3, # Reduzido para não expandir muito
716
- container=False,
717
- scale=1
718
  )
719
- # Botão de envio oculto (será substituído por JavaScript)
720
- enviar_btn = gr.Button("➤", variant="primary", size="sm", elem_classes="send-button")
721
 
722
  def responder(chat_history, user_msg, modelo, session_id):
723
  if not user_msg.strip():
724
  return chat_history, ""
725
-
726
- # Adiciona a mensagem do usuário ao histórico
727
- chat_history = chat_history + [[user_msg, "🤔 Dr. Aldo está pensando..."]]
728
  yield chat_history, ""
729
-
730
  try:
731
- # Obtem a resposta do modelo
732
  resposta_final = responder_como_aldo(session_id, user_msg, modelo)
733
-
734
- # Atualiza o histórico com a resposta final
735
  chat_history[-1][1] = resposta_final
736
  yield chat_history, ""
737
-
738
  except Exception as e:
739
- # Em caso de erro, mostra uma mensagem amigável
740
  chat_history[-1][1] = f"❌ Desculpe, ocorreu um erro: {str(e)}"
741
  yield chat_history, ""
742
 
743
- # Eventos
744
  enviar_btn.click(
745
  fn=responder,
746
  inputs=[chatbot, user_input, modelo_select, session_id_state],
747
  outputs=[chatbot, user_input],
748
  show_progress=False
749
  )
750
-
751
  user_input.submit(
752
  fn=responder,
753
  inputs=[chatbot, user_input, modelo_select, session_id_state],
@@ -755,124 +336,57 @@ def criar_interface():
755
  show_progress=False
756
  )
757
 
758
- # JavaScript otimizado e simplificado
759
  gr.HTML("""
760
  <script>
761
- (function() {
762
- let initialized = false;
763
-
764
- function initLayout() {
765
- if (initialized) return;
766
- initialized = true;
767
-
768
- // Força layout fixo imediatamente
769
- const mainContainer = document.querySelector('.main-container');
770
- if (mainContainer) {
771
- mainContainer.style.height = '100vh';
772
- mainContainer.style.maxHeight = '100vh';
773
- mainContainer.style.overflow = 'hidden';
774
- }
775
-
776
- const chatMain = document.querySelector('.chat-main');
777
- if (chatMain) {
778
- chatMain.style.height = '100vh';
779
- chatMain.style.maxHeight = '100vh';
780
- chatMain.style.display = 'flex';
781
- chatMain.style.flexDirection = 'column';
782
- }
783
-
784
- const messagesContainer = document.querySelector('.messages-container');
785
- if (messagesContainer) {
786
- messagesContainer.style.flex = '1';
787
- messagesContainer.style.minHeight = '0';
788
- messagesContainer.style.overflow = 'hidden';
789
- }
790
-
791
- const inputArea = document.querySelector('.input-area');
792
- if (inputArea) {
793
- inputArea.style.flexShrink = '0';
794
- inputArea.style.height = '120px';
795
- inputArea.style.maxHeight = '120px';
796
- }
797
-
798
- // Configuração do textarea
799
- const textarea = document.querySelector("#entrada_usuario textarea");
800
- if (textarea) {
801
- textarea.style.height = "24px";
802
- textarea.style.minHeight = "24px";
803
- textarea.style.maxHeight = "48px"; // Limitado para não quebrar layout
804
- textarea.style.resize = "none";
805
- textarea.style.overflowY = "hidden";
806
-
807
- // Auto-resize limitado
808
- function autoResize() {
809
- textarea.style.height = "24px";
810
- const newHeight = Math.min(textarea.scrollHeight, 48);
811
- textarea.style.height = Math.max(newHeight, 24) + "px";
812
-
813
- if (textarea.scrollHeight > 48) {
814
- textarea.style.overflowY = "auto";
815
- } else {
816
- textarea.style.overflowY = "hidden";
817
- }
818
- }
819
-
820
- textarea.addEventListener('input', autoResize);
821
- textarea.addEventListener('paste', () => setTimeout(autoResize, 0));
822
- setTimeout(() => textarea.focus(), 300);
823
- }
824
-
825
- console.log("Layout fixo inicializado");
826
  }
827
-
828
- // Executa quando DOM estiver pronto
829
- if (document.readyState === 'loading') {
830
- document.addEventListener('DOMContentLoaded', initLayout);
831
- } else {
832
- setTimeout(initLayout, 100);
 
 
833
  }
834
-
835
- // Reforça o layout depois que o Gradio terminar de carregar
836
- setTimeout(initLayout, 1000);
837
- setTimeout(initLayout, 2000);
838
- })();
839
  </script>
840
  """)
841
 
842
  return interface
843
 
844
  def inicializar_sistema_sync():
845
- """Função síncrona para compatibilidade"""
846
  try:
847
  import asyncio
848
-
849
- # Tenta obter o loop atual
850
  try:
851
  loop = asyncio.get_event_loop()
852
  if loop.is_running():
853
- # Se já está rodando, cria uma nova thread
854
  import concurrent.futures
855
  with concurrent.futures.ThreadPoolExecutor() as executor:
856
  future = executor.submit(lambda: asyncio.run(inicializar_sistema()))
857
  return future.result()
858
  else:
859
- # Se não está rodando, executa normalmente
860
  return loop.run_until_complete(inicializar_sistema())
861
  except RuntimeError:
862
- # Se não há loop, cria um novo
863
  return asyncio.run(inicializar_sistema())
864
-
865
  except Exception as e:
866
  print(f"Erro na inicialização: {e}")
867
- return False, {}
868
 
869
  def configurar_interface():
870
- """
871
- Configura a interface Gradio apenas se o sistema for inicializado com pelo menos 3 modelos disponíveis.
872
- Lança uma exceção com a lista de modelos disponíveis se a inicialização falhar.
873
- """
874
  try:
875
- status, available_models = inicializar_sistema()
876
  if status:
877
  return criar_interface()
878
  else:
 
8
  inicializar_sistema
9
  )
10
 
11
+ def criar_interface():
12
+ css = """
13
+ @import url('https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css');
14
+ body {
15
+ font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
16
+ background-color: #f5f5f5;
17
+ color: #1f2937;
18
+ margin: 0;
19
+ height: 100vh;
20
+ overflow: hidden;
21
+ }
22
+ .gradio-container {
23
+ max-width: 100% !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  height: 100vh !important;
25
+ padding: 0 !important;
26
+ margin: 0 !important;
27
+ overflow: hidden !important;
28
+ }
29
+ .main-container {
30
+ display: flex;
31
+ height: 100vh;
32
+ max-height: 100vh;
33
+ overflow: hidden;
34
  }
 
35
  .sidebar {
36
+ width: 280px;
37
+ background-color: #ffffff;
38
+ border-right: 1px solid #e5e7eb;
39
+ padding: 1.5rem;
40
+ overflow-y: auto;
41
+ flex-shrink: 0;
42
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
43
  }
 
44
  .chat-main {
45
+ flex: 1;
46
+ display: flex;
47
+ flex-direction: column;
48
+ background-color: #f5f5f5;
49
+ height: 100vh;
50
+ overflow: hidden;
51
+ }
52
+ .chat-header {
53
+ background-color: #ffffff;
54
+ border-bottom: 1px solid #e5e7eb;
55
+ padding: 1rem 1.5rem;
56
+ text-align: center;
57
+ flex-shrink: 0;
58
  }
 
59
  .messages-container {
60
+ flex: 1;
61
+ overflow-y: auto;
62
+ padding: 1.5rem;
63
+ background-color: #f5f5f5;
64
+ scroll-behavior: smooth;
65
  }
66
+ .input-area {
67
+ background-color: #ffffff;
68
+ border-top: 1px solid #e5e7eb;
69
+ padding: 1rem 1.5rem;
70
+ flex-shrink: 0;
71
  }
72
+ .input-container {
73
+ max-width: 800px;
74
+ margin: 0 auto;
75
+ display: flex;
76
+ align-items: center;
77
+ gap: 0.5rem;
78
  }
79
+ .input-wrapper {
80
+ flex: 1;
81
+ background-color: #f3f4f6;
82
+ border: 1px solid #d1d5db;
83
+ border-radius: 0.5rem;
84
+ padding: 0.75rem;
85
+ display: flex;
86
+ align-items: center;
87
+ transition: all 0.2s ease;
88
+ }
89
+ .input-wrapper:focus-within {
90
+ border-color: #3b82f6;
91
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
92
+ }
93
+ #entrada_usuario textarea {
94
+ background: transparent !important;
95
+ border: none !important;
96
+ color: #1f2937 !important;
97
+ font-size: 1rem !important;
98
+ line-height: 1.5 !important;
99
+ resize: none !important;
100
+ outline: none !important;
101
+ width: 100% !important;
102
+ min-height: 24px !important;
103
+ max-height: 80px !important;
104
+ overflow-y: auto !important;
105
+ }
106
+ #entrada_usuario textarea::placeholder {
107
+ color: #6b7280 !important;
108
+ }
109
+ .send-button {
110
+ background-color: #3b82f6 !important;
111
+ color: white !important;
112
+ border: none !important;
113
+ border-radius: 0.375rem !important;
114
+ padding: 0.5rem !important;
115
+ cursor: pointer !important;
116
+ transition: background-color 0.2s ease !important;
117
+ }
118
+ .send-button:hover:not(:disabled) {
119
+ background-color: #2563eb !important;
120
+ }
121
+ .send-button:disabled {
122
+ background-color: #9ca3af !important;
123
+ cursor: not-allowed !important;
124
+ }
125
+ .message {
126
+ margin-bottom: 1.5rem;
127
+ display: flex;
128
+ gap: 0.75rem;
129
+ max-width: 800px;
130
+ margin-left: auto;
131
+ margin-right: auto;
132
+ }
133
+ .message.user .message-content {
134
+ background-color: #dbeafe;
135
+ border-radius: 0.5rem 0.5rem 0 0.5rem;
136
+ padding: 0.75rem 1rem;
137
+ }
138
+ .message.assistant .message-content {
139
+ background-color: #ffffff;
140
+ border: 1px solid #e5e7eb;
141
+ border-radius: 0.5rem 0.5rem 0.5rem 0;
142
+ padding: 0.75rem 1rem;
143
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
144
  }
 
145
  .message-text {
146
+ font-size: 1rem;
147
+ line-height: 1.5;
148
  }
149
+ .model-selector {
150
+ width: 100%;
151
+ padding: 0.5rem;
152
+ border-radius: 0.375rem;
153
+ border: 1px solid #d1d5db;
154
+ background-color: #f9fafb;
 
 
 
 
 
155
  }
156
+ .accordion {
157
+ border: 1px solid #e5e7eb !important;
158
+ border-radius: 0.375rem !important;
159
+ margin-bottom: 1rem !important;
160
+ background-color: #f9fafb !important;
161
  }
162
+ .accordion summary {
163
+ padding: 0.75rem !important;
164
+ font-weight: 500 !important;
165
+ cursor: pointer !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  }
167
+ .accordion[open] summary {
168
+ border-bottom: 1px solid #e5e7eb !important;
169
  }
170
+ .accordion .gr-panel {
171
+ padding: 1rem !important;
172
+ }
173
+ pre, code {
174
+ font-family: 'Fira Code', monospace !important;
175
+ background-color: #1f2937 !important;
176
+ color: #f9fafb !important;
177
+ border-radius: 0.375rem !important;
178
+ padding: 0.75rem !important;
179
+ }
180
+ pre {
181
+ overflow-x: auto;
182
+ }
183
+ code {
184
+ padding: 0.2rem 0.4rem !important;
185
+ }
186
+ a {
187
+ color: #3b82f6 !important;
188
+ text-decoration: none !important;
189
+ }
190
+ a:hover {
191
+ text-decoration: underline !important;
192
+ }
193
+ @media (max-width: 768px) {
194
+ .main-container {
195
+ flex-direction: column;
196
+ }
197
+ .sidebar {
198
+ width: 100%;
199
+ height: auto;
200
+ max-height: 200px;
201
+ border-right: none;
202
+ border-bottom: 1px solid #e5e7eb;
203
+ }
204
+ .chat-main {
205
+ height: calc(100vh - 200px);
206
+ }
207
+ .messages-container {
208
+ padding: 1rem;
209
+ }
210
+ .input-area {
211
+ padding: 0.75rem;
212
+ }
213
+ .message {
214
+ margin-bottom: 1rem;
215
+ }
216
+ }
217
+ .loading-indicator {
218
+ color: #6b7280;
219
+ font-style: italic;
220
+ display: flex;
221
+ align-items: center;
222
+ gap: 0.5rem;
223
+ }
224
+ .loading-dots span {
225
+ width: 0.25rem;
226
+ height: 0.25rem;
227
+ background-color: #6b7280;
228
+ border-radius: 50%;
229
+ animation: pulse 1.4s ease-in-out infinite;
230
+ }
231
+ .loading-dots span:nth-child(1) { animation-delay: -0.32s; }
232
+ .loading-dots span:nth-child(2) { animation-delay: -0.16s; }
233
+ .loading-dots span:nth-child(3) { animation-delay: 0s; }
234
+ @keyframes pulse {
235
+ 0%, 80%, 100% { opacity: 0.3; }
236
+ 40% { opacity: 1; }
237
+ }
238
+ """
 
 
 
239
 
240
+ with gr.Blocks(title="Dr. Aldo Henrique - iAldo AI Assistant", css=css) as interface:
 
241
  session_id_state = gr.State(str(uuid.uuid4()))
242
 
243
  with gr.Row(elem_classes="main-container"):
244
+ with gr.Column(elem_classes="sidebar", scale=0, min_width=280):
 
245
  gr.HTML("""
246
+ <div class="flex items-center justify-between mb-6">
247
+ <h2 class="text-xl font-semibold text-blue-600">iAldo AI</h2>
248
  </div>
249
  """)
 
 
250
  with gr.Group():
251
+ gr.HTML('<h3 class="text-sm font-semibold text-gray-500 uppercase mb-2">🧠 Modelo de IA</h3>')
252
  modelo_select = gr.Dropdown(
253
  choices=list(MODELS.keys()),
254
  value="Llama 3.2 3B",
 
255
  show_label=False,
256
  elem_classes="model-selector",
257
  container=False
258
  )
259
+ with gr.Accordion("🔧 Status API", open=False, elem_classes="accordion"):
260
+ status_api = gr.Textbox(
261
+ value=" Modelos carregados com sucesso!",
262
+ interactive=False,
263
+ lines=4,
264
+ show_label=False
265
+ )
266
+ with gr.Accordion("ℹ️ Sobre", open=False, elem_classes="accordion"):
267
  gr.Markdown("""
268
  **Dr. Aldo Henrique**
269
+
270
+ - Especialista em C/C++, Java
271
+ - Desenvolvimento Web & IA
272
+ - Conteúdo: [aldohenrique.com.br](https://aldohenrique.com.br/)
273
+
274
  **Dicas:**
275
+ - Faça perguntas específicas
276
+ - Use o RAG para consultar o blog
277
+ - Experimente diferentes modelos
278
  """)
279
 
 
280
  with gr.Column(elem_classes="chat-main", scale=1):
 
281
  gr.HTML("""
282
  <div class="chat-header">
283
+ <div class="text-lg font-semibold text-gray-800">
284
+ 🤖 Conversando com <a href="https://aldohenrique.com.br/" target="_blank" class="text-blue-600">Prof. Dr. Aldo Henrique</a>
285
  </div>
286
  </div>
287
  """)
 
 
288
  with gr.Column(elem_classes="messages-container"):
289
  chatbot = gr.Chatbot(
 
 
290
  show_label=False,
291
  container=False,
292
  elem_classes="chatbot",
293
+ avatar_images=(
294
+ "https://cdn-icons-png.flaticon.com/256/9055/9055398.png",
295
+ "https://cdn.iconscout.com/icon/premium/png-256-thumb/robo-97-415007.png"
296
+ ),
297
  bubble_full_width=False,
298
+ height="100%"
299
  )
 
 
300
  with gr.Column(elem_classes="input-area"):
301
  with gr.Row(elem_classes="input-container"):
302
  with gr.Column(elem_classes="input-wrapper", scale=1):
 
304
  show_label=False,
305
  placeholder="Digite sua mensagem aqui...",
306
  lines=1,
307
+ max_lines=3,
308
  elem_id="entrada_usuario",
309
+ container=False
 
 
310
  )
311
+ enviar_btn = gr.Button("➤", variant="primary", elem_classes="send-button")
 
312
 
313
  def responder(chat_history, user_msg, modelo, session_id):
314
  if not user_msg.strip():
315
  return chat_history, ""
316
+ chat_history = chat_history + [[user_msg, '<div class="loading-indicator">🤔 Dr. Aldo está pensando... <span class="loading-dots"><span></span><span></span><span></span></span></div>']]
 
 
317
  yield chat_history, ""
 
318
  try:
 
319
  resposta_final = responder_como_aldo(session_id, user_msg, modelo)
 
 
320
  chat_history[-1][1] = resposta_final
321
  yield chat_history, ""
 
322
  except Exception as e:
 
323
  chat_history[-1][1] = f"❌ Desculpe, ocorreu um erro: {str(e)}"
324
  yield chat_history, ""
325
 
 
326
  enviar_btn.click(
327
  fn=responder,
328
  inputs=[chatbot, user_input, modelo_select, session_id_state],
329
  outputs=[chatbot, user_input],
330
  show_progress=False
331
  )
 
332
  user_input.submit(
333
  fn=responder,
334
  inputs=[chatbot, user_input, modelo_select, session_id_state],
 
336
  show_progress=False
337
  )
338
 
 
339
  gr.HTML("""
340
  <script>
341
+ document.addEventListener('DOMContentLoaded', () => {
342
+ const textarea = document.querySelector("#entrada_usuario textarea");
343
+ if (textarea) {
344
+ textarea.style.height = "auto";
345
+ textarea.style.overflowY = "hidden";
346
+ const adjustHeight = () => {
347
+ textarea.style.height = "auto";
348
+ textarea.style.height = Math.min(textarea.scrollHeight, 80) + "px";
349
+ };
350
+ textarea.addEventListener('input', adjustHeight);
351
+ textarea.addEventListener('paste', () => setTimeout(adjustHeight, 0));
352
+ setTimeout(() => textarea.focus(), 300);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  }
354
+
355
+ const messagesContainer = document.querySelector('.messages-container');
356
+ if (messagesContainer) {
357
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
358
+ const observer = new MutationObserver(() => {
359
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
360
+ });
361
+ observer.observe(messagesContainer, { childList: true, subtree: true });
362
  }
363
+ });
 
 
 
 
364
  </script>
365
  """)
366
 
367
  return interface
368
 
369
  def inicializar_sistema_sync():
 
370
  try:
371
  import asyncio
 
 
372
  try:
373
  loop = asyncio.get_event_loop()
374
  if loop.is_running():
 
375
  import concurrent.futures
376
  with concurrent.futures.ThreadPoolExecutor() as executor:
377
  future = executor.submit(lambda: asyncio.run(inicializar_sistema()))
378
  return future.result()
379
  else:
 
380
  return loop.run_until_complete(inicializar_sistema())
381
  except RuntimeError:
 
382
  return asyncio.run(inicializar_sistema())
 
383
  except Exception as e:
384
  print(f"Erro na inicialização: {e}")
385
+ return False, {}
386
 
387
  def configurar_interface():
 
 
 
 
388
  try:
389
+ status, available_models = inicializar_sistema_sync()
390
  if status:
391
  return criar_interface()
392
  else: