car-diagnostic-agent / ui /layout.py
aldsouza's picture
Fix
04614e6
Raw
History Blame Contribute Delete
5.57 kB
"""Gradio cockpit layout for the car diagnostic agent."""
from __future__ import annotations
from collections.abc import Callable, Iterator
import gradio as gr
from fault_simulation import describe_fault, list_fault_choices
from obd_connection import VITALS_POLL_INTERVAL
from obd_vitals import format_vitals_hud_cached, refresh_vitals_hud
from trace_store import TRACE_ENABLED
from ui.header import render_cockpit_header
from ui.theme import load_cockpit_css
def build_ui(
*,
default_thinking_enabled: bool,
active_fault_status_fn: Callable[[], str],
on_fault_select_fn: Callable[[str | None], str],
on_apply_fault_fn: Callable[..., Iterator],
on_clear_fault_fn: Callable[[], tuple],
chat_submit_fn: Callable[..., Iterator],
download_traces_fn: Callable[[], str | None],
) -> gr.Blocks:
"""Build the futuristic cockpit Gradio interface."""
with gr.Blocks(title="Car Diagnostic Agent", fill_height=True) as demo:
gr.HTML(f"<style>{load_cockpit_css()}</style>", visible=False)
gr.HTML(render_cockpit_header())
with gr.Row(elem_classes=["cockpit-main-row"]):
with gr.Column(scale=5, elem_id="command-center", elem_classes=["hud-panel"]):
enable_thinking = gr.Checkbox(
label="Enable thinking",
value=default_thinking_enabled,
info="Reasoning is enabled server-side but not shown in chat.",
)
chatbot = gr.Chatbot(
label="Diagnostic chat",
height=460,
elem_classes=["cockpit-chat"],
)
with gr.Row():
msg_input = gr.Textbox(
label="Message",
placeholder="Describe symptoms…",
scale=4,
show_label=False,
)
send_btn = gr.Button(
"Send",
variant="primary",
scale=1,
elem_id="send-btn",
)
gr.Examples(
examples=[
"My check engine light is on and the car idles rough.",
"I hear a grinding noise when I brake.",
"The car won't start but the battery seems fine.",
],
inputs=msg_input,
)
with gr.Column(scale=3, elem_classes=["instrument-bay"]):
vitals_panel = gr.HTML(
value=format_vitals_hud_cached(),
elem_id="vitals-hud",
elem_classes=["hud-panel"],
)
refresh_btn = gr.Button(
"Refresh vitals",
variant="secondary",
elem_id="refresh-vitals-btn",
)
fault_status = gr.Markdown(
value=active_fault_status_fn(),
elem_id="fault-status-strip",
elem_classes=["hud-panel"],
)
trace_panel = gr.Markdown(
value="*Tool calls and results will stream here…*",
label="Telemetry log",
elem_id="telemetry-log",
elem_classes=["hud-panel"],
)
if TRACE_ENABLED:
download_traces = gr.DownloadButton("Download agent traces (JSONL)")
download_traces.click(download_traces_fn, outputs=download_traces)
with gr.Accordion("Simulate fault", open=False, elem_id="fault-bay"):
fault_select = gr.Dropdown(
choices=list_fault_choices(),
value="none",
label="Fault profile",
)
fault_info = gr.Markdown(value=describe_fault("none"))
with gr.Row():
apply_fault_btn = gr.Button("Apply fault", variant="primary")
clear_fault_btn = gr.Button("Clear / reset", variant="secondary")
vitals_timer = gr.Timer(VITALS_POLL_INTERVAL, active=True)
vitals_timer.tick(format_vitals_hud_cached, outputs=vitals_panel)
vitals_timer.tick(active_fault_status_fn, outputs=fault_status)
refresh_btn.click(refresh_vitals_hud, outputs=vitals_panel)
demo.load(refresh_vitals_hud, outputs=vitals_panel)
demo.load(active_fault_status_fn, outputs=fault_status)
fault_select.change(on_fault_select_fn, inputs=fault_select, outputs=fault_info)
apply_fault_btn.click(
on_apply_fault_fn,
inputs=[fault_select, chatbot, enable_thinking],
outputs=[fault_status, vitals_panel, chatbot, trace_panel],
).then(on_fault_select_fn, inputs=fault_select, outputs=fault_info)
clear_fault_btn.click(
on_clear_fault_fn,
outputs=[fault_status, vitals_panel, fault_select],
).then(lambda: describe_fault("none"), outputs=fault_info)
chat_inputs = [msg_input, chatbot, enable_thinking]
chat_outputs = [chatbot, trace_panel]
send_btn.click(chat_submit_fn, chat_inputs, chat_outputs).then(
lambda: "", outputs=msg_input
)
msg_input.submit(chat_submit_fn, chat_inputs, chat_outputs).then(
lambda: "", outputs=msg_input
)
return demo