"""Reproduction for gradio-app/gradio#10107 - a plot in a chatbot remounts on every streaming update. 1. Send `plot`. A bokeh chart appears in the conversation. 2. Send anything else, then watch the chat area while the reply streams. Before the fix the chart flickers and the view flips between the top and the bottom of the conversation on every streaming update. After the fix the chart stays put and the view stays pinned to the bottom. """ import json from time import sleep import bokeh.plotting import gradio as gr from bokeh.embed import json_item from gradio import ChatMessage from gradio.components.plot import PlotData AFTER_TEXT = "And this is the reply that streams once the plot is on screen." def bokeh_plot(): fig = bokeh.plotting.figure(title="Title", width=1500) fig.line(x=[1, 2, 3, 4], y=[1, 2, 3, 4]) # Serialized here rather than handing the figure to gr.Plot: Chatbot._postprocess # deepcopies the message and bokeh figures are not deepcopy-able (#13675). That is # a separate bug and not what this Space demonstrates. return PlotData(type="bokeh", plot=json.dumps(json_item(fig))) def add_message(history, message): history.append(ChatMessage(role="user", content=message)) return history, message def bot_stream(history, input_msg): msg = ChatMessage(role="assistant", content="") history.append(msg) for c in "Text before plot": msg.content += c yield history sleep(0.05) if input_msg.strip().lower() == "plot": history.append(ChatMessage(role="assistant", content=gr.Plot(bokeh_plot()))) yield history msg2 = ChatMessage(role="assistant", content="") history.append(msg2) for c in AFTER_TEXT: msg2.content += c yield history sleep(0.06) with gr.Blocks(fill_height=True, fill_width=True) as demo: gr.Markdown( "### gradio-app/gradio#10107\n" "1. Send `plot`. A bokeh chart appears in the conversation.\n" "2. Send anything else, then watch the chat area while the reply streams.\n\n" "**Before the fix**: the chart flickers and the view flips between the top " "and the bottom on every streaming update. " "**After the fix**: the chart stays put and the view stays pinned to the bottom." ) # scale + fill_height is what lets the chat area grow; height alone would pin it # to 200px and the 600px bokeh chart would barely fit in the viewport. chatbot = gr.Chatbot( label="Chatbot", elem_id="chatbot", scale=5, height=200, min_width=200, ) with gr.Group(): with gr.Row(): chat_input = gr.Textbox( container=False, show_label=False, placeholder="Type `plot`, send it, then send anything else...", scale=7, autofocus=True, ) submit_btn = gr.Button("Send", scale=1) for listener in [chat_input.submit, submit_btn.click]: chat_msg = listener(add_message, [chatbot, chat_input], [chatbot, chat_input]) chat_msg.then(bot_stream, [chatbot, chat_input], chatbot) # SSR is disabled deliberately: preview wheels have shipped with a broken SSR server # bundle, and this repro has nothing to do with SSR. demo.launch(ssr_mode=False)