From d819029be24317a3a7ba848eea6dfe21e63e9504 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Sun, 26 Jul 2026 00:31:00 -0700 Subject: [PATCH] Studio: reset the reasoning open state when a new stream starts (#7444) --- .../src/components/assistant-ui/reasoning.tsx | 4 +++- .../test_chat_response_details_ui_contract.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/studio/frontend/src/components/assistant-ui/reasoning.tsx b/studio/frontend/src/components/assistant-ui/reasoning.tsx index 09ca6d2530..2b01f7b719 100644 --- a/studio/frontend/src/components/assistant-ui/reasoning.tsx +++ b/studio/frontend/src/components/assistant-ui/reasoning.tsx @@ -362,10 +362,12 @@ const ReasoningGroupImpl: ReasoningGroupComponent = ({ } }, [isReasoningStreaming]); - // Reset dismissed flag on new stream. + // Reset per-round open state. manualOpen is sticky and regenerate reuses this + // instance, so a hand-opened block would stay pinned open and never collapse. useEffect(() => { if (isReasoningStreaming) { setDismissedWhileStreaming(false); + setManualOpen(false); } }, [isReasoningStreaming]); diff --git a/tests/studio/test_chat_response_details_ui_contract.py b/tests/studio/test_chat_response_details_ui_contract.py index 9d2f0886ee..1183151b54 100644 --- a/tests/studio/test_chat_response_details_ui_contract.py +++ b/tests/studio/test_chat_response_details_ui_contract.py @@ -85,6 +85,21 @@ def test_reasoning_keeps_streaming_height_cap_through_automatic_collapse(): assert "streaming={isReasoningStreaming || retainStreamingHeight}" in src +def test_reasoning_clears_manual_open_on_a_new_stream(): + """A hand-opened block must not stay pinned open when the stream restarts. + + isOpen is `(streaming && !dismissed) || manualOpen` and manualOpen is only + settable while idle, so the new-stream reset has to clear it too. + """ + src = REASONING_TSX.read_text() + + marker = "setDismissedWhileStreaming(false)" + start = src.find(marker) + assert start != -1, "new-stream reset effect is missing" + effect = src[src.rfind("useEffect(() => {", 0, start) : src.find("});", start)] + assert "setManualOpen(false)" in effect + + def test_response_details_metadata_is_persisted_without_backend_schema_change(): src = ADAPTER_TS.read_text() assert "interface ResponseDetailsMetadata" in src