8c69a96eae
* Don't deep-copy chat messages A Gradio component used as chat content crashed the app whenever the component, or the value it held, could not be deep-copied: any component built inside a `with gr.Blocks()` block (its parent is copied too, so the copy walks into the Blocks graph and its locks), and gr.Plot holding a bokeh figure. Both have been broken since 5.9.0. Three deep copies sit in the path a chat message takes, and none of them needs to be deep: - Chatbot._postprocess copied the whole message because _postprocess_content mutates the component it is handed, popping "value" out of its constructor_args. Build the args dict locally instead and leave the caller's component alone. - ChatInterface._append_message_to_history only appends to the list, so a shallow copy is enough. - Queue.process_events only replaces the top-level "data" key, so a shallow copy per event is enough. That site is also why the gr.ChatInterface case surfaced as a hang: it runs after the prediction, so no completion message reached the client. Dropping the copy in _postprocess means unrender() acts on the real component again, which is what that call was there for. * add changeset * Address Copilot review - The comment claimed nothing mutates the caller's component, but unrender() a couple of lines above does exactly that. Narrow the wording to the constructor args, which is what the change is about. - Match the timeout other queueing tests use (5s instead of 30s) so a failure reports quickly. * Fix backend typecheck failures - `ty` does not accept a ChatMessage in Chatbot's `value` (the stub only lists MessageDict | Message), so use the dict form, which is what the rest of this test file does anyway. - Give the test's `__deepcopy__` override the signature pydantic's BaseModel declares. * Annotate the test message so ty accepts it * Fix the ChatMessage route too Returning a component wrapped in a gr.ChatMessage still crashed: _message_as_message_dict used dataclasses.asdict, whose _asdict_inner deep-copies any leaf value before dict_factory ever sees it. That is a fourth deep copy on the same path, and it is the one a chat function hits when it attaches metadata (the only way to render a thought), so "a bokeh plot as a thought" was still broken. Add utils.shallow_asdict and use it here. ChatMessage has no nested dataclasses -- metadata and options are TypedDicts -- so dropping the recursion changes nothing; checked that the output is identical for text, metadata, options, FileData and list contents. --------- Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com> Co-authored-by: Abubakar Abid <abubakar@huggingface.co>