From 7c918c810faf92a0c1964d838b4dbb0f3ee4818c Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Thu, 1 Oct 2026 23:35:21 +0530 Subject: [PATCH] fix: retain widget text updates without component IDs --- chatkit/server.py | 1 + tests/test_widgets.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/chatkit/server.py b/chatkit/server.py index 865135f..c18f511 100644 --- a/chatkit/server.py +++ b/chatkit/server.py @@ -138,6 +138,7 @@ def full_replace_value(before_value: Any, after_value: Any) -> bool: if ( is_streaming_text(before) and is_streaming_text(after) + and before.id and field == "value" and getattr(after, "value", "").startswith(getattr(before, "value", "")) ): diff --git a/tests/test_widgets.py b/tests/test_widgets.py index 18c56a2..6ad1b49 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -11,12 +11,52 @@ Card, DynamicWidgetComponent, DynamicWidgetRoot, + Markdown, Text, WidgetRoot, WidgetTemplate, ) +@pytest.mark.parametrize("component", [Text, Markdown]) +def test_appended_text_without_id_replaces_widget(component): + before = Card(children=[component(value="Hello")]) + after = Card(children=[component(value="Hello, world!")]) + + updates = diff_widget(before, after) + + assert len(updates) == 1 + assert updates[0].type == "widget.root.updated" + assert updates[0].widget == after + + +@pytest.mark.parametrize("component_type", ["Text", "Markdown"]) +@pytest.mark.parametrize("component_id", [None, ""]) +def test_dynamic_appended_text_without_id_replaces_widget(component_type, component_id): + before = DynamicWidgetRoot( + type="Card", + children=[ + DynamicWidgetComponent.model_validate( + {"type": component_type, "id": component_id, "value": "Hello"} + ) + ], + ) + after = DynamicWidgetRoot( + type="Card", + children=[ + DynamicWidgetComponent.model_validate( + {"type": component_type, "id": component_id, "value": "Hello, world!"} + ) + ], + ) + + updates = diff_widget(before, after) + + assert len(updates) == 1 + assert updates[0].type == "widget.root.updated" + assert updates[0].widget == after + + @pytest.mark.parametrize( "before, after, expected", [