@@ -181,11 +181,17 @@ async def execute(self, state: State) -> dict[str, Any]:
181181 if isinstance (candidate , str ) and candidate :
182182 last_response = candidate
183183
184- if (not self ._is_graph_event (event )) and (
185- not event .partial ) and event .content and event .content .parts :
186- text_parts = [part .text for part in event .content .parts if part .text ]
187- if text_parts :
188- last_response = text_parts [- 1 ]
184+ if (not self ._is_graph_event (event )) and event .is_final_response ():
185+ # Only clean final answers may become last_response. Skip
186+ # thought parts (part.thought) so a thinking model's
187+ # reasoning monologue is never surfaced as the answer.
188+ # is_final_response() already excludes partial, function
189+ # call/response, and code-execution events, so plan and
190+ # transfer_to_agent rounds are naturally ignored.
191+ if event .content and event .content .parts :
192+ visible_text = [part .text for part in event .content .parts if part .text and not part .thought ]
193+ if visible_text :
194+ last_response = "" .join (visible_text )
189195
190196 if not event .visible :
191197 if event .actions and event .actions .transfer_to_agent :
@@ -246,7 +252,7 @@ async def execute(self, state: State) -> dict[str, Any]:
246252
247253 node_response : Any = last_response
248254 structured_output : Any = None
249- if isinstance (self .agent , LlmAgent ) and self .agent .output_schema is not None :
255+ if isinstance (self .agent , LlmAgent ) and self .agent .output_schema is not None and last_response :
250256 node_response = json .loads (last_response )
251257 structured_output = node_response
252258
0 commit comments