@@ -69,6 +69,10 @@ def __init__(self):
6969 self .in_final_answer = False
7070 self .final_answer_started = False
7171
72+ # Flag to track if we've seen a final_answer start tag
73+ self .has_final_answer_start = False
74+ self .final_answer_start_pos = - 1
75+
7276 def process_chunk (self , chunk ):
7377 """Process a chunk of streaming content."""
7478 self .raw_response += chunk
@@ -77,6 +81,11 @@ def process_chunk(self, chunk):
7781 for char in chunk :
7882 self ._process_char (char )
7983
84+ # Check for unclosed final_answer tag at the end of processing
85+ if self .current_tag == "final_answer" and self .in_final_answer :
86+ # Add current content to parsed data
87+ self .parsed_data [self .current_tag ] = self .current_content
88+
8089 def _process_char (self , char ):
8190 """Process a single character."""
8291 # State machine processing
@@ -163,6 +172,7 @@ def _handle_tag_complete(self):
163172 if self .current_tag == "final_answer" :
164173 self .in_final_answer = False
165174 self .final_answer_started = False
175+ self .has_final_answer_start = False
166176
167177 self .current_tag = None
168178 self .current_content = ""
@@ -171,13 +181,20 @@ def _handle_tag_complete(self):
171181 self ._handle_invalid_tag ('</' + self .tag_buffer + '>' )
172182 else :
173183 # Start tag
184+ # If we're already in a final_answer tag and see another tag, treat it as content
185+ if self .in_final_answer and self .current_tag == "final_answer" :
186+ self ._handle_invalid_tag ('<' + self .tag_buffer + '>' )
187+ return
188+
174189 self .current_tag = self .tag_buffer
175190 self .current_content = ""
176191
177192 # Set state
178193 if self .current_tag == "final_answer" :
179194 self .in_final_answer = True
180195 self .final_answer_started = False
196+ self .has_final_answer_start = True
197+ self .final_answer_start_pos = len (self .raw_response ) - len ("<final_answer>" )
181198
182199 # Print tag name
183200 if not self .printed_tags [self .tag_buffer ]:
@@ -219,13 +236,38 @@ def get_parsed_data(self):
219236 """Get parsing results."""
220237 result = self .parsed_data .copy ()
221238
239+ # Handle incomplete final_answer tag
240+ if self .has_final_answer_start and "final_answer" not in result :
241+ # Extract everything after the final_answer start tag
242+ if self .final_answer_start_pos >= 0 :
243+ final_answer_content = self .raw_response [self .final_answer_start_pos + len ("<final_answer>" ):].strip ()
244+ result ["final_answer" ] = final_answer_content
245+ self .tag_contents ["final_answer" ] = final_answer_content
246+
247+ # Update null content flag
248+ self .is_null_content ["final_answer" ] = (
249+ final_answer_content .lower () == "null" or
250+ final_answer_content == "" or
251+ final_answer_content .isspace ()
252+ )
253+
222254 # Handle incomplete action_input if present
223255 if "action" in result and not self .is_null_content ["action" ] and "action_input" not in result :
224256 # Check if we have partial action_input in the raw response
225257 action_input_start = self .raw_response .find ("<action_input>" )
226258 if action_input_start != - 1 :
227259 action_input_start += len ("<action_input>" )
228- action_input_content = self .raw_response [action_input_start :].strip ()
260+ action_input_end = self .raw_response .find ("</action_input>" , action_input_start )
261+
262+ if action_input_end != - 1 :
263+ action_input_content = self .raw_response [action_input_start :action_input_end ].strip ()
264+ else :
265+ # If no end tag, take everything until the next start tag or end of string
266+ next_tag_start = self .raw_response .find ("<" , action_input_start )
267+ if next_tag_start != - 1 :
268+ action_input_content = self .raw_response [action_input_start :next_tag_start ].strip ()
269+ else :
270+ action_input_content = self .raw_response [action_input_start :].strip ()
229271
230272 # Store the extracted action_input
231273 result ["action_input" ] = action_input_content
0 commit comments