Files
gradio-app--gradio/gradio/components
hysts 8c69a96eae Stop deep-copying chat messages so components work as chat content again (#13676)
* 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>
2026-07-29 14:27:18 +09:00
..
2026-06-04 17:53:45 -07:00
2026-07-02 15:24:48 -04:00