Skip to content

Commit 84f9203

Browse files
committed
Enhancing UI and Demo Script
1 parent 263aac6 commit 84f9203

File tree

4 files changed

+297
-42
lines changed

4 files changed

+297
-42
lines changed

.idea/AugmentWebviewStateStore.xml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: gabs-langcache
5+
---
6+
apiVersion: apps/v1
7+
kind: Deployment
8+
metadata:
9+
name: langcache
10+
namespace: gabs-langcache
11+
spec:
12+
replicas: 2
13+
selector:
14+
matchLabels:
15+
app: langcache
16+
template:
17+
metadata:
18+
labels:
19+
app: langcache
20+
spec:
21+
containers:
22+
- name: langcache
23+
image: gacerioni/gabs-redis-langcache:1.1.0
24+
ports:
25+
- containerPort: 7860
26+
env:
27+
- name: GRADIO_SERVER_NAME
28+
value: "0.0.0.0"
29+
- name: GRADIO_SERVER_PORT
30+
value: "7860"
31+
envFrom:
32+
- secretRef:
33+
name: langcache-env
34+
readinessProbe:
35+
httpGet:
36+
path: /
37+
port: 7860
38+
initialDelaySeconds: 10
39+
periodSeconds: 10
40+
livenessProbe:
41+
httpGet:
42+
path: /
43+
port: 7860
44+
initialDelaySeconds: 30
45+
periodSeconds: 20
46+
---
47+
apiVersion: v1
48+
kind: Service
49+
metadata:
50+
name: langcache-svc
51+
namespace: gabs-langcache
52+
annotations:
53+
networking.gke.io/load-balancer-type: "External"
54+
spec:
55+
type: LoadBalancer
56+
selector:
57+
app: langcache
58+
ports:
59+
- name: http
60+
port: 80
61+
targetPort: 7860

main_demo_released.py

Lines changed: 153 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,34 @@ def call_openai(
170170
"Responda de forma breve e direta. "
171171
"Não mencione o nome do usuário a menos que a pergunta seja sobre o nome/identidade. "
172172
f"Contexto principal: {domain}. "
173-
"Se a pergunta for ambígua (ex.: 'célula', 'rede', 'banco'), "
173+
"Se a pergunta for ambígua (ex.: 'deploy', 'pipeline', 'modelo'), "
174174
f"RESPONDA APENAS no sentido de {domain} e NÃO mencione outros significados."
175175
)
176+
176177
examples = [
177-
{"role": "user", "content": "O que é uma célula? (no contexto de saúde)"},
178-
{"role": "assistant", "content": "Uma célula é a menor unidade estrutural e funcional dos seres vivos."},
179-
{"role": "user", "content": "O que é uma célula? (no contexto de engenharia de software)"},
180-
{"role": "assistant", "content": "Em computação, célula costuma se referir a uma unidade em uma tabela/planilha ou a um componente isolado de execução."},
178+
# Engenharia de Software
179+
{"role": "user", "content": "O que é um deploy? (no contexto de engenharia de software)"},
180+
{"role": "assistant",
181+
"content": "Deploy é o processo de disponibilizar uma nova versão de software em produção."},
182+
{"role": "user", "content": "O que é um pipeline? (no contexto de engenharia de software)"},
183+
{"role": "assistant",
184+
"content": "Um pipeline é uma sequência automatizada de etapas para construir, testar e implantar código."},
185+
186+
# Financeiro
187+
{"role": "user", "content": "O que é um deploy? (no contexto de finanças corporativas)"},
188+
{"role": "assistant",
189+
"content": "No contexto financeiro, deploy pode se referir à liberação de um novo processo, sistema ou investimento para uso interno."},
190+
{"role": "user", "content": "O que é um pipeline? (no contexto de vendas e finanças)"},
191+
{"role": "assistant",
192+
"content": "Pipeline é a lista de oportunidades ou previsões de receita que ainda estão em andamento."},
193+
194+
# Bilíngue — cache semântico cross-language
195+
{"role": "user", "content": "Explique o que é aprendizado de máquina."},
196+
{"role": "assistant",
197+
"content": "Aprendizado de máquina é uma área da IA que permite que sistemas aprendam padrões a partir de dados sem programação explícita."},
198+
{"role": "user", "content": "What is machine learning?"},
199+
{"role": "assistant",
200+
"content": "Machine learning is a branch of AI that enables systems to learn patterns from data and make predictions or decisions without being explicitly programmed."},
181201
]
182202
msgs = [{"role": "system", "content": system_ctx}, *examples, {"role": "user", "content": prompt}]
183203
resp = openai_client.chat.completions.create(
@@ -434,6 +454,7 @@ def calc_savings(tokens_est: int, price_in: float, price_out: float, frac_in: fl
434454
:root {
435455
--redis-red:#D82C20; --ink:#0b1220; --soft:#475569; --muted:#64748b;
436456
--line:#e5e7eb; --bg:#f6f7f9; --white:#ffffff; --radius:14px;
457+
--success:#10b981; --warning:#f59e0b;
437458
}
438459
439460
* { font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif; }
@@ -475,42 +496,65 @@ def calc_savings(tokens_est: int, price_in: float, price_out: float, frac_in: fl
475496
}
476497
477498
/* KPIs */
478-
.kpi-row { display:flex; gap:12px; margin: 0 16px 10px; }
499+
.kpi-row { display:flex; gap:12px; margin: 0 16px 16px; flex-wrap: wrap; }
479500
.kpi {
480-
flex:1; background: var(--white); border:1px solid var(--line); border-radius:12px;
481-
padding:12px 14px;
501+
flex:1; min-width: 140px; background: var(--white); border:1px solid var(--line); border-radius:12px;
502+
padding:14px 16px; transition: transform .2s ease, box-shadow .2s ease;
482503
}
504+
.kpi:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,.08); }
483505
.kpi .kpi-num {
484506
font-family: 'Space Grotesk', Inter, sans-serif;
485-
font-size:22px; font-weight:700; color:var(--ink); line-height:1.1;
507+
font-size:24px; font-weight:700; color:var(--ink); line-height:1.1;
508+
}
509+
.kpi .kpi-label {
510+
font-size:11px; color:var(--muted); margin-top:6px;
511+
text-transform:uppercase; letter-spacing:.8px; font-weight:600;
486512
}
487-
.kpi .kpi-label { font-size:12px; color:var(--muted); margin-top:4px; text-transform:uppercase; letter-spacing:.6px; }
488-
.kpi-accent { border-color: var(--redis-red); }
513+
.kpi-accent { border-color: var(--redis-red); border-width: 2px; }
514+
.kpi-accent .kpi-num { color: var(--redis-red); }
489515
490516
/* Cenários lado a lado */
491-
.scenarios { display:grid; grid-template-columns: 1fr 1fr; gap: 14px; margin: 10px 16px; }
517+
.scenarios { display:grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 10px 16px; }
492518
@media (max-width: 1024px) { .scenarios { grid-template-columns: 1fr; } }
493519
494520
.card {
495-
background: var(--white); border:1px solid var(--line); border-radius: var(--radius);
496-
padding:12px;
521+
background: var(--white); border:2px solid var(--line); border-radius: var(--radius);
522+
padding:16px; transition: border-color .2s ease;
497523
}
524+
.card:hover { border-color: var(--redis-red); }
498525
.card .card-title {
499526
font-family: 'Space Grotesk', Inter, sans-serif;
500-
font-size:16px; font-weight:700; color:var(--ink); margin-bottom:8px;
527+
font-size:18px; font-weight:700; color:var(--ink); margin-bottom:12px;
528+
display: flex; align-items: center; gap: 8px;
501529
}
502530
503-
/* Flush */
504-
.flush-wrap { margin-top:8px; border-top:1px dashed var(--line); padding-top:10px; }
531+
/* Source badges */
532+
.source-badge {
533+
display: inline-block; padding: 4px 10px; border-radius: 6px;
534+
font-size: 11px; font-weight: 700; text-transform: uppercase;
535+
letter-spacing: .5px;
536+
}
537+
.source-cache { background: #d1fae5; color: #065f46; }
538+
.source-llm { background: #fef3c7; color: #92400e; }
505539
506540
/* History */
507541
.dataframe { background: var(--white); border:1px solid var(--line); border-radius: var(--radius); }
508-
.dataframe thead tr th { font-size:12px; }
542+
.dataframe thead tr th { font-size:12px; font-weight:600; }
509543
.dataframe tbody tr td { font-size:12px; }
510544
511545
/* Buttons */
512546
button.primary, .gr-button-primary {
513547
background: var(--redis-red) !important; border-color: var(--redis-red) !important; color:#fff !important;
548+
font-weight: 600 !important; transition: all .2s ease !important;
549+
}
550+
button.primary:hover, .gr-button-primary:hover {
551+
background: #c02518 !important; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(216,44,32,.3) !important;
552+
}
553+
554+
/* Secondary buttons */
555+
.secondary-btn {
556+
background: var(--white) !important; border: 1px solid var(--line) !important;
557+
color: var(--soft) !important; font-weight: 600 !important;
514558
}
515559
516560
/* --- HERO (título + subtítulo) --- */
@@ -605,51 +649,63 @@ def calc_savings(tokens_est: int, price_in: float, price_out: float, frac_in: fl
605649
with gr.Row(elem_classes=["scenarios"]):
606650
# --- Cenário A ---
607651
with gr.Column(elem_classes=["card"]):
608-
gr.Markdown("<div class='card-title'>Cenário A</div>")
652+
gr.Markdown("<div class='card-title'>Chat 🅰️</div>")
609653
with gr.Row():
610654
a_company = gr.Textbox(label="Company", value="RedisLabs")
611-
a_bu = gr.Textbox(label="Business Unit", value="Saude-Medicos")
655+
a_bu = gr.Textbox(label="Business Unit", value="Engenharia-de-Software")
612656
a_person = gr.Textbox(label="Person", value="Gabriel")
613657
a_prompt = gr.Textbox(label="Pergunta", placeholder="Pergunte algo…", lines=3)
614658
a_btn = gr.Button("Perguntar (A)", variant="primary")
615-
a_answer = gr.Textbox(label="Resposta", lines=6)
659+
a_answer = gr.Textbox(label="Resposta", lines=6, interactive=False)
616660
with gr.Row():
617-
a_source = gr.Label(label="Origem")
661+
a_source = gr.HTML(label="Origem")
618662
a_latency = gr.Label(label="Latência")
619-
a_debug = gr.Code(label="Debug")
620-
gr.HTML("<div class='flush-wrap'></div>")
621-
a_flush_btn = gr.Button("🧹 Limpar Cache (Escopo A)")
622-
a_flush_status = gr.HTML()
623-
a_flush_debug = gr.Code()
663+
with gr.Accordion("🔍 Debug Info", open=False):
664+
a_debug = gr.Code(label="Debug JSON", language="json")
665+
with gr.Accordion("🧹 Gerenciar Cache", open=False):
666+
a_flush_btn = gr.Button("Limpar Cache (Escopo A)", variant="secondary")
667+
a_flush_status = gr.HTML()
668+
with gr.Accordion("Debug do Flush", open=False):
669+
a_flush_debug = gr.Code(language="json")
624670

625671
# --- Cenário B ---
626672
with gr.Column(elem_classes=["card"]):
627-
gr.Markdown("<div class='card-title'>Cenário B</div>")
673+
gr.Markdown("<div class='card-title'>Chat 🅱️</div>")
628674
with gr.Row():
629675
b_company = gr.Textbox(label="Company", value="RedisLabs")
630-
b_bu = gr.Textbox(label="Business Unit", value="Engenharia-de-Software")
631-
b_person = gr.Textbox(label="Person", value="Janine")
676+
b_bu = gr.Textbox(label="Business Unit", value="Financeiro")
677+
b_person = gr.Textbox(label="Person", value="Diego")
632678
b_prompt = gr.Textbox(label="Pergunta", placeholder="Pergunte algo…", lines=3)
633679
b_btn = gr.Button("Perguntar (B)", variant="primary")
634-
b_answer = gr.Textbox(label="Resposta", lines=6)
680+
b_answer = gr.Textbox(label="Resposta", lines=6, interactive=False)
635681
with gr.Row():
636-
b_source = gr.Label(label="Origem")
682+
b_source = gr.HTML(label="Origem")
637683
b_latency = gr.Label(label="Latência")
638-
b_debug = gr.Code(label="Debug")
639-
gr.HTML("<div class='flush-wrap'></div>")
640-
b_flush_btn = gr.Button("🧹 Limpar Cache (Escopo B)")
641-
b_flush_status = gr.HTML()
642-
b_flush_debug = gr.Code()
684+
with gr.Accordion("🔍 Debug Info", open=False):
685+
b_debug = gr.Code(label="Debug JSON", language="json")
686+
with gr.Accordion("🧹 Gerenciar Cache", open=False):
687+
b_flush_btn = gr.Button("Limpar Cache (Escopo B)", variant="secondary")
688+
b_flush_status = gr.HTML()
689+
with gr.Accordion("Debug do Flush", open=False):
690+
b_flush_debug = gr.Code(language="json")
691+
692+
# Copy A → B button
693+
with gr.Row():
694+
gr.HTML("<div style='flex:1'></div>")
695+
copy_btn = gr.Button("📋 Copiar A → B", variant="secondary", size="sm")
696+
gr.HTML("<div style='flex:1'></div>")
643697

644698
# Flush ambos
645699
with gr.Group():
646700
gr.Markdown("### 🧹 Limpeza Combinada (A + B)")
647-
flush_both_btn = gr.Button("🧹 Limpar Ambos (A+B)")
648-
flush_both_status = gr.HTML()
649-
flush_both_debug = gr.Code()
701+
with gr.Accordion("Opções Avançadas", open=False):
702+
flush_both_btn = gr.Button("🧹 Limpar Ambos (A+B)", variant="secondary")
703+
flush_both_status = gr.HTML()
704+
with gr.Accordion("Debug do Flush", open=False):
705+
flush_both_debug = gr.Code(language="json")
650706

651707
# Histórico
652-
gr.Markdown("### Histórico (últimos 50)")
708+
gr.Markdown("### 📊 Histórico de Consultas (últimos 50)")
653709
history_table = gr.Dataframe(
654710
headers=["Hora", "Cenário", "Company", "BU", "Person", "Fonte", "Latência", "Tokens (est.)", "Economia", "Prompt"],
655711
datatype=["str", "str", "str", "str", "str", "str", "str", "number", "str", "str"],
@@ -732,9 +788,16 @@ def handle_submit(
732788
kpi_tok_html = f"<div class='kpi'><div class='kpi-num'>{k['tokens']}</div><div class='kpi-label'>Tokens</div></div>"
733789
kpi_usd_html = f"<div class='kpi kpi-accent'><div class='kpi-num'>{k['usd']}</div><div class='kpi-label'>Economia</div></div>"
734790

791+
# Create visual badge for source
792+
source_badge = (
793+
f"<span class='source-badge source-{source}'>"
794+
f"{'✓ CACHE HIT' if source == 'cache' else '⚡ LLM CALL'}"
795+
f"</span>"
796+
)
797+
735798
return (
736799
answer,
737-
json.dumps({"fonte": source}, ensure_ascii=False),
800+
source_badge,
738801
debug_json,
739802
f"{latency} · {saved_str}",
740803
gr.update(value=kpi_hits_html),
@@ -746,6 +809,7 @@ def handle_submit(
746809
state,
747810
)
748811

812+
# Scenario A submit handlers
749813
a_btn.click(
750814
fn=handle_submit,
751815
inputs=[
@@ -763,6 +827,25 @@ def handle_submit(
763827
],
764828
)
765829

830+
# Enable Enter key submission for A
831+
a_prompt.submit(
832+
fn=handle_submit,
833+
inputs=[
834+
gr.State("A"),
835+
a_company, a_bu, a_person, a_prompt,
836+
isolation_global, threshold_global, exact_sem_global, ttl_global,
837+
price_in, price_out, frac_in, currency,
838+
st,
839+
],
840+
outputs=[
841+
a_answer, a_source, a_debug, a_latency,
842+
kpi_hits, kpi_misses, kpi_rate, kpi_tokens, kpi_savings,
843+
history_table,
844+
st,
845+
],
846+
)
847+
848+
# Scenario B submit handlers
766849
b_btn.click(
767850
fn=handle_submit,
768851
inputs=[
@@ -780,6 +863,34 @@ def handle_submit(
780863
],
781864
)
782865

866+
# Enable Enter key submission for B
867+
b_prompt.submit(
868+
fn=handle_submit,
869+
inputs=[
870+
gr.State("B"),
871+
b_company, b_bu, b_person, b_prompt,
872+
isolation_global, threshold_global, exact_sem_global, ttl_global,
873+
price_in, price_out, frac_in, currency,
874+
st,
875+
],
876+
outputs=[
877+
b_answer, b_source, b_debug, b_latency,
878+
kpi_hits, kpi_misses, kpi_rate, kpi_tokens, kpi_savings,
879+
history_table,
880+
st,
881+
],
882+
)
883+
884+
# Copy A → B functionality
885+
def copy_a_to_b(company, bu, person, prompt):
886+
return company, bu, person, prompt
887+
888+
copy_btn.click(
889+
fn=copy_a_to_b,
890+
inputs=[a_company, a_bu, a_person, a_prompt],
891+
outputs=[b_company, b_bu, b_person, b_prompt]
892+
)
893+
783894
a_flush_btn.click(
784895
fn=handle_flush_scope,
785896
inputs=[a_company, a_bu, a_person, isolation_global],

0 commit comments

Comments
 (0)