@@ -323,8 +323,8 @@ def _generate_ollama_tool_summary(self, tool_results: List[Any], response_text:
323
323
if not (self ._is_ollama_provider () and tool_results ):
324
324
return None
325
325
326
- # If response is substantial , no summary needed
327
- if response_text and len ( response_text . strip ()) > OLLAMA_MIN_RESPONSE_LENGTH :
326
+ # If response is a final answer , no summary needed
327
+ if self . _is_final_answer ( response_text , False , tool_results ) :
328
328
return None
329
329
330
330
# Build tool summary efficiently
@@ -349,6 +349,64 @@ def _format_ollama_tool_result_message(self, function_name: str, tool_result: An
349
349
"content" : f"The { function_name } function returned: { tool_result_str } "
350
350
}
351
351
352
+ def _is_final_answer (self , response_text : str , has_tool_calls : bool , tool_results : List [Any ]) -> bool :
353
+ """
354
+ Determine if a response is a final answer or intermediate acknowledgment.
355
+
356
+ This method provides intelligent differentiation between:
357
+ - Intermediate responses that acknowledge tool execution
358
+ - Final responses that contain actual answers to user queries
359
+
360
+ Args:
361
+ response_text: The text response from the LLM
362
+ has_tool_calls: Whether the response contains tool calls
363
+ tool_results: Results from executed tools
364
+
365
+ Returns:
366
+ True if this is a final answer, False if intermediate
367
+ """
368
+ if not response_text or not response_text .strip ():
369
+ return False
370
+
371
+ response_lower = response_text .lower ().strip ()
372
+
373
+ # If response contains tool calls, it's likely not a final answer
374
+ if has_tool_calls :
375
+ return False
376
+
377
+ # For Ollama, be more conservative about what constitutes a final answer
378
+ if self ._is_ollama_provider ():
379
+ # If we have recent tool results, check if this is just acknowledgment
380
+ if tool_results :
381
+ # Common patterns of tool acknowledgment (not final answers)
382
+ acknowledgment_patterns = [
383
+ "i'll" , "let me" , "now i'll" , "next i'll" , "i need to" , "i should" ,
384
+ "executing" , "calling" , "running" , "using the" , "based on this" ,
385
+ "now let me" , "let me now" , "i will now" , "proceeding to" ,
386
+ "moving to" , "continuing with" , "next step" , "now that i have" ,
387
+ "tool executed" , "function called" , "result obtained" , "got the result"
388
+ ]
389
+
390
+ # Check if response is primarily acknowledgment
391
+ if any (pattern in response_lower for pattern in acknowledgment_patterns ):
392
+ # If it's short and contains acknowledgment patterns, likely intermediate
393
+ if len (response_text .strip ()) < 50 :
394
+ return False
395
+
396
+ # If response is very short and we have tool results, likely intermediate
397
+ if len (response_text .strip ()) < 30 :
398
+ return False
399
+
400
+ # Additional check: if response mainly contains status updates or simple confirmations
401
+ status_patterns = ["done" , "completed" , "finished" , "successful" , "ok" , "ready" ]
402
+ if (len (response_text .strip ()) < 40 and
403
+ any (pattern in response_lower for pattern in status_patterns )):
404
+ return False
405
+
406
+ # For other providers, maintain existing behavior
407
+ # Substantial content (>10 chars) is considered final
408
+ return len (response_text .strip ()) > 10
409
+
352
410
def _process_stream_delta (self , delta , response_text : str , tool_calls : List [Dict ], formatted_tools : Optional [List ] = None ) -> tuple :
353
411
"""
354
412
Process a streaming delta chunk to extract content and tool calls.
@@ -1102,17 +1160,19 @@ def get_response(
1102
1160
continue
1103
1161
1104
1162
# Check if the LLM provided a final answer alongside the tool calls
1105
- # If response_text contains substantive content, treat it as the final answer
1106
- if response_text and response_text . strip () and len ( response_text . strip ()) > 10 :
1163
+ # Use intelligent differentiation between intermediate and final responses
1164
+ if self . _is_final_answer ( response_text , bool ( tool_calls ), tool_results ) :
1107
1165
# LLM provided a final answer after tool execution, don't continue
1108
1166
final_response_text = response_text .strip ()
1109
1167
break
1110
1168
1111
1169
# Special handling for Ollama to prevent infinite loops
1112
- tool_summary = self ._generate_ollama_tool_summary (tool_results , response_text )
1113
- if tool_summary :
1114
- final_response_text = tool_summary
1115
- break
1170
+ # Only generate summary if we're approaching max iterations or stuck in a loop
1171
+ if self ._is_ollama_provider () and iteration_count >= 5 :
1172
+ tool_summary = self ._generate_ollama_tool_summary (tool_results , response_text )
1173
+ if tool_summary :
1174
+ final_response_text = tool_summary
1175
+ break
1116
1176
1117
1177
# Otherwise, continue the loop to check if more tools are needed
1118
1178
iteration_count += 1
@@ -1851,17 +1911,19 @@ async def get_response_async(
1851
1911
stored_reasoning_content = reasoning_content
1852
1912
1853
1913
# Check if the LLM provided a final answer alongside the tool calls
1854
- # If response_text contains substantive content, treat it as the final answer
1855
- if response_text and response_text . strip () and len ( response_text . strip ()) > 10 :
1914
+ # Use intelligent differentiation between intermediate and final responses
1915
+ if self . _is_final_answer ( response_text , bool ( tool_calls ), tool_results ) :
1856
1916
# LLM provided a final answer after tool execution, don't continue
1857
1917
final_response_text = response_text .strip ()
1858
1918
break
1859
1919
1860
1920
# Special handling for Ollama to prevent infinite loops
1861
- tool_summary = self ._generate_ollama_tool_summary (tool_results , response_text )
1862
- if tool_summary :
1863
- final_response_text = tool_summary
1864
- break
1921
+ # Only generate summary if we're approaching max iterations or stuck in a loop
1922
+ if self ._is_ollama_provider () and iteration_count >= 5 :
1923
+ tool_summary = self ._generate_ollama_tool_summary (tool_results , response_text )
1924
+ if tool_summary :
1925
+ final_response_text = tool_summary
1926
+ break
1865
1927
1866
1928
# Continue the loop to check if more tools are needed
1867
1929
iteration_count += 1
0 commit comments