File size: 3,779 Bytes
ba52610
 
 
 
b1d78ef
 
ba52610
 
 
 
b1d78ef
ba52610
 
b1d78ef
ba52610
 
 
b1d78ef
ba52610
b1d78ef
ba52610
 
 
 
 
 
 
b1d78ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ba52610
a741ada
 
 
 
 
 
 
ba52610
b1d78ef
 
a741ada
 
 
 
 
 
 
 
 
 
 
b1d78ef
a741ada
b1d78ef
 
 
 
 
 
 
a741ada
 
b1d78ef
 
 
 
a741ada
b1d78ef
ba52610
 
 
 
 
a741ada
 
 
 
 
ba52610
 
 
 
 
b1d78ef
 
 
 
 
284ee47
ba52610
a741ada
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import gradio as gr
import time

# Inspired by: https://www.gradio.app/guides/agents-and-tool-usage
# Custom timer solution described by Claude Sonnet 4.6:
#   https://claude.ai/share/d7adc776-05f2-4c2b-a93a-9cf6089dd7be 


def interact_with_agent(prompt, messages=[]):
    """Simulate generation of two assistant messages after delays"""
    time.sleep(4)
    messages.append(
        gr.ChatMessage(
            role="assistant", content="First message — gr.Chatbot() timer stops here"
        )
    )
    yield messages
    time.sleep(4)
    messages.append(
        gr.ChatMessage(role="assistant", content="Second message — Custom timer stops here")
    )
    yield messages


def str_to_message(content, role="user"):
    return [gr.ChatMessage(role=role, content=content)]

timer_html = """
<div id="agent-timer" style="font-family: monospace;"></div>
"""

timer_js = """
let startTime = null;
let interval = null;
const el = element.querySelector('#agent-timer');

el.addEventListener('agent-timer:start', () => {
    startTime = Date.now();
    el.textContent = '⏱ 0.0s';
    interval = setInterval(() => {
        const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
        el.textContent = '⏱ ' + elapsed + 's';
    }, 100);
});

el.addEventListener('agent-timer:stop', () => {
    if (interval) {
        clearInterval(interval);
        interval = null;
        const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
        el.textContent = '✓ ' + elapsed + 's';
    }
});
"""

chatbot_overlay_css = """
#timer-display {
    position: absolute;
    bottom: 0px;
}
"""

with gr.Blocks() as demo:
    with gr.Row():
        with gr.Column():
            gr.Markdown(
                """
                ## Gradio chatbot timer that stops at the last (not first) yielded agent message

                **Submit any user message to launch a chatbot simulation that responds with two assistant messages with a 4-second delay before each one.**
                
                - The built-in `gr.Chatbot` timer (*lower right*) stops after the first message.
                - The custom timer (*lower left*) keeps going until the last message is yielded.
                - The custom timer is implemented in JavaScript and CSS and depends on `gr.HTML` capabilities in Gradio 6.
                """
            )
            input = gr.Textbox(
                "Start the timers!",
                label="User message",
                autofocus=True,
                submit_btn=True,
            )
            gr.Markdown(
                """
                ### App history
                - 2025-07-25: Create demo to support [Gradio issue #11637](https://github.com/gradio-app/gradio/issues/11637) (Gradio 5.38.2)
                - 2026-05-30: Add custom timer using [gr.HTML](https://huggingface.co/blog/gradio-html-one-shot-apps) (Gradio 6.15.2)
                """
            )
        with gr.Column():
            chatbot = gr.Chatbot()
            gr.HTML(value=timer_html, js_on_load=timer_js, elem_id="timer-display")

    input.submit(
        # Update chatbot UI with user message immediately
        str_to_message,
        input,
        chatbot,
    ).then(
        # Start custom timer
        lambda: None, None, None,
        js="() => document.getElementById('agent-timer')"
           "?.dispatchEvent(new CustomEvent('agent-timer:start'))",
    ).then(
        # Update chatbot UI with assistant messages
        interact_with_agent,
        [input, chatbot],
        chatbot,
    ).then(
        # Stop custom timer
        lambda: None, None, None,
        js="() => document.getElementById('agent-timer')"
           "?.dispatchEvent(new CustomEvent('agent-timer:stop'))",
    )

demo.launch(css=chatbot_overlay_css)