Spaces:
Sleeping
Sleeping
| 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) | |