6969 is_streaming_req ,
7070)
7171from .version import __version__
72- from .widgets import Markdown , Text , WidgetComponent , WidgetComponentBase , WidgetRoot
72+ from .widgets import WidgetComponent , WidgetComponentBase , WidgetRoot
7373
7474DEFAULT_PAGE_SIZE = 20
7575DEFAULT_ERROR_MESSAGE = "An error occurred when generating a response."
@@ -82,6 +82,11 @@ def diff_widget(
8282 Compare two WidgetRoots and return a list of deltas.
8383 """
8484
85+ def is_streaming_text (component : WidgetComponentBase ) -> bool :
86+ return getattr (component , "type" , None ) in {"Markdown" , "Text" } and isinstance (
87+ getattr (component , "value" , None ), str
88+ )
89+
8590 def full_replace (before : WidgetComponentBase , after : WidgetComponentBase ) -> bool :
8691 if (
8792 before .type != after .type
@@ -108,10 +113,10 @@ def full_replace_value(before_value: Any, after_value: Any) -> bool:
108113
109114 for field in before .model_fields_set .union (after .model_fields_set ):
110115 if (
111- isinstance (before , ( Markdown , Text ) )
112- and isinstance (after , ( Markdown , Text ) )
116+ is_streaming_text (before )
117+ and is_streaming_text (after )
113118 and field == "value"
114- and after . value .startswith (before . value )
119+ and getattr ( after , " value" , "" ) .startswith (getattr ( before , " value" , "" ) )
115120 ):
116121 # Appends to the value prop of Markdown or Text do not trigger a full replace
117122 continue
@@ -129,11 +134,11 @@ def full_replace_value(before_value: Any, after_value: Any) -> bool:
129134
130135 def find_all_streaming_text_components (
131136 component : WidgetComponent | WidgetRoot ,
132- ) -> dict [str , Markdown | Text ]:
137+ ) -> dict [str , WidgetComponentBase ]:
133138 components = {}
134139
135140 def recurse (component : WidgetComponent | WidgetRoot ):
136- if isinstance (component , ( Markdown , Text ) ) and component .id :
141+ if is_streaming_text (component ) and component .id :
137142 components [component .id ] = component
138143
139144 if hasattr (component , "children" ):
@@ -154,16 +159,19 @@ def recurse(component: WidgetComponent | WidgetRoot):
154159 f"Node { id } was not present when the widget was initially rendered. All nodes with ID must persist across all widget updates."
155160 )
156161
157- if before_node .value != after_node .value :
158- if not after_node .value .startswith (before_node .value ):
162+ before_value = str (getattr (before_node , "value" , None ))
163+ after_value = str (getattr (after_node , "value" , None ))
164+
165+ if before_value != after_value :
166+ if not after_value .startswith (before_value ):
159167 raise ValueError (
160168 f"Node { id } was updated with a new value that is not a prefix of the initial value. All widget updates must be cumulative."
161169 )
162- done = not after_node . streaming
170+ done = not getattr ( after_node , " streaming" , False )
163171 deltas .append (
164172 WidgetStreamingTextValueDelta (
165173 component_id = id ,
166- delta = after_node . value [len (before_node . value ) :],
174+ delta = after_value [len (before_value ) :],
167175 done = done ,
168176 )
169177 )
0 commit comments