praisonaiagents 0.0.125__tar.gz → 0.0.126__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/PKG-INFO +1 -1
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/agent/agent.py +2 -1
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/llm/llm.py +207 -197
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents.egg-info/PKG-INFO +1 -1
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents.egg-info/SOURCES.txt +1 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/pyproject.toml +1 -1
- praisonaiagents-0.0.126/tests/test_fix_comprehensive.py +75 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/README.md +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/agent/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/agent/handoff.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/agent/image_agent.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/agents/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/agents/agents.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/agents/autoagents.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/approval.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/guardrails/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/guardrails/guardrail_result.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/guardrails/llm_guardrail.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/knowledge/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/knowledge/chunking.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/knowledge/knowledge.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/llm/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/llm/model_capabilities.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/llm/openai_client.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/main.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/mcp/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/mcp/mcp.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/mcp/mcp_http_stream.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/mcp/mcp_sse.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/memory/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/memory/memory.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/process/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/process/process.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/session.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/task/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/task/task.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/telemetry/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/telemetry/integration.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/telemetry/telemetry.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/README.md +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/__init__.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/arxiv_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/calculator_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/csv_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/duckdb_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/duckduckgo_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/excel_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/file_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/json_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/newspaper_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/pandas_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/python_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/searxng_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/shell_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/spider_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/test.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/train/data/generatecot.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/wikipedia_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/xml_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/yaml_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/yfinance_tools.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents.egg-info/dependency_links.txt +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents.egg-info/requires.txt +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents.egg-info/top_level.txt +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/setup.cfg +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/tests/test-graph-memory.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/tests/test.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/tests/test_handoff_compatibility.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/tests/test_http_stream_basic.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/tests/test_ollama_async_fix.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/tests/test_ollama_fix.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/tests/test_posthog_fixed.py +0 -0
- {praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/tests/test_validation_feedback.py +0 -0
@@ -1235,7 +1235,8 @@ Your Goal: {self.goal}"""
|
|
1235
1235
|
agent_role=self.role,
|
1236
1236
|
agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in (tools if tools is not None else self.tools)],
|
1237
1237
|
execute_tool_fn=self.execute_tool, # Pass tool execution function
|
1238
|
-
reasoning_steps=reasoning_steps
|
1238
|
+
reasoning_steps=reasoning_steps,
|
1239
|
+
stream=stream # Pass the stream parameter from chat method
|
1239
1240
|
)
|
1240
1241
|
|
1241
1242
|
self.chat_history.append({"role": "assistant", "content": response_text})
|
@@ -680,6 +680,7 @@ class LLM:
|
|
680
680
|
max_iterations = 10 # Prevent infinite loops
|
681
681
|
iteration_count = 0
|
682
682
|
final_response_text = ""
|
683
|
+
stored_reasoning_content = None # Store reasoning content from tool execution
|
683
684
|
|
684
685
|
while iteration_count < max_iterations:
|
685
686
|
try:
|
@@ -857,8 +858,6 @@ class LLM:
|
|
857
858
|
iteration_count += 1
|
858
859
|
continue
|
859
860
|
|
860
|
-
# If we reach here, no more tool calls needed - get final response
|
861
|
-
# Make one more call to get the final summary response
|
862
861
|
# Special handling for Ollama models that don't automatically process tool results
|
863
862
|
ollama_handled = False
|
864
863
|
ollama_params = self._handle_ollama_model(response_text, tool_results, messages, original_prompt)
|
@@ -918,15 +917,23 @@ class LLM:
|
|
918
917
|
console=console
|
919
918
|
)
|
920
919
|
|
921
|
-
#
|
920
|
+
# Update messages and continue the loop instead of returning
|
922
921
|
if final_response_text:
|
923
|
-
|
922
|
+
# Update messages with the response to maintain conversation context
|
923
|
+
messages.append({
|
924
|
+
"role": "assistant",
|
925
|
+
"content": final_response_text
|
926
|
+
})
|
927
|
+
# Continue the loop to check if more tools are needed
|
928
|
+
iteration_count += 1
|
929
|
+
continue
|
924
930
|
else:
|
925
931
|
logging.warning("[OLLAMA_DEBUG] Ollama follow-up returned empty response")
|
926
932
|
|
927
|
-
#
|
933
|
+
# Handle reasoning_steps after tool execution if not already handled by Ollama
|
928
934
|
if reasoning_steps and not ollama_handled:
|
929
|
-
|
935
|
+
# Make a non-streaming call to capture reasoning content
|
936
|
+
reasoning_resp = litellm.completion(
|
930
937
|
**self._build_completion_params(
|
931
938
|
messages=messages,
|
932
939
|
temperature=temperature,
|
@@ -934,89 +941,28 @@ class LLM:
|
|
934
941
|
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
|
935
942
|
)
|
936
943
|
)
|
937
|
-
reasoning_content =
|
938
|
-
response_text =
|
944
|
+
reasoning_content = reasoning_resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
|
945
|
+
response_text = reasoning_resp["choices"][0]["message"]["content"]
|
939
946
|
|
940
|
-
#
|
941
|
-
if
|
942
|
-
|
943
|
-
original_prompt,
|
944
|
-
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
|
945
|
-
markdown=markdown,
|
946
|
-
generation_time=time.time() - start_time,
|
947
|
-
console=console
|
948
|
-
)
|
949
|
-
else:
|
950
|
-
display_interaction(
|
951
|
-
original_prompt,
|
952
|
-
response_text,
|
953
|
-
markdown=markdown,
|
954
|
-
generation_time=time.time() - start_time,
|
955
|
-
console=console
|
956
|
-
)
|
957
|
-
|
958
|
-
# Otherwise do the existing streaming approach if not already handled
|
959
|
-
elif not ollama_handled:
|
960
|
-
# Get response after tool calls
|
961
|
-
if stream:
|
962
|
-
# Streaming approach
|
963
|
-
if verbose:
|
964
|
-
with Live(display_generating("", current_time), console=console, refresh_per_second=4) as live:
|
965
|
-
final_response_text = ""
|
966
|
-
for chunk in litellm.completion(
|
967
|
-
**self._build_completion_params(
|
968
|
-
messages=messages,
|
969
|
-
tools=formatted_tools,
|
970
|
-
temperature=temperature,
|
971
|
-
stream=True,
|
972
|
-
**kwargs
|
973
|
-
)
|
974
|
-
):
|
975
|
-
if chunk and chunk.choices and chunk.choices[0].delta.content:
|
976
|
-
content = chunk.choices[0].delta.content
|
977
|
-
final_response_text += content
|
978
|
-
live.update(display_generating(final_response_text, current_time))
|
979
|
-
else:
|
980
|
-
final_response_text = ""
|
981
|
-
for chunk in litellm.completion(
|
982
|
-
**self._build_completion_params(
|
983
|
-
messages=messages,
|
984
|
-
tools=formatted_tools,
|
985
|
-
temperature=temperature,
|
986
|
-
stream=True,
|
987
|
-
**kwargs
|
988
|
-
)
|
989
|
-
):
|
990
|
-
if chunk and chunk.choices and chunk.choices[0].delta.content:
|
991
|
-
final_response_text += chunk.choices[0].delta.content
|
992
|
-
else:
|
993
|
-
# Non-streaming approach
|
994
|
-
resp = litellm.completion(
|
995
|
-
**self._build_completion_params(
|
996
|
-
messages=messages,
|
997
|
-
tools=formatted_tools,
|
998
|
-
temperature=temperature,
|
999
|
-
stream=False,
|
1000
|
-
**kwargs
|
1001
|
-
)
|
1002
|
-
)
|
1003
|
-
final_response_text = resp.get("choices", [{}])[0].get("message", {}).get("content", "") or ""
|
947
|
+
# Store reasoning content for later use
|
948
|
+
if reasoning_content:
|
949
|
+
stored_reasoning_content = reasoning_content
|
1004
950
|
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
original_prompt,
|
1011
|
-
final_response_text,
|
1012
|
-
markdown=markdown,
|
1013
|
-
generation_time=time.time() - start_time,
|
1014
|
-
console=console
|
1015
|
-
)
|
951
|
+
# Update messages with the response
|
952
|
+
messages.append({
|
953
|
+
"role": "assistant",
|
954
|
+
"content": response_text
|
955
|
+
})
|
1016
956
|
|
1017
|
-
|
957
|
+
# After tool execution, continue the loop to check if more tools are needed
|
958
|
+
# instead of immediately trying to get a final response
|
959
|
+
iteration_count += 1
|
960
|
+
continue
|
1018
961
|
else:
|
1019
962
|
# No tool calls, we're done with this iteration
|
963
|
+
# If we've executed tools in previous iterations, this response contains the final answer
|
964
|
+
if iteration_count > 0:
|
965
|
+
final_response_text = response_text.strip()
|
1020
966
|
break
|
1021
967
|
|
1022
968
|
except Exception as e:
|
@@ -1029,16 +975,30 @@ class LLM:
|
|
1029
975
|
|
1030
976
|
# No tool calls were made in this iteration, return the response
|
1031
977
|
if verbose:
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
978
|
+
# If we have stored reasoning content from tool execution, display it
|
979
|
+
if stored_reasoning_content:
|
980
|
+
display_interaction(
|
981
|
+
original_prompt,
|
982
|
+
f"Reasoning:\n{stored_reasoning_content}\n\nAnswer:\n{response_text}",
|
983
|
+
markdown=markdown,
|
984
|
+
generation_time=time.time() - start_time,
|
985
|
+
console=console
|
986
|
+
)
|
987
|
+
else:
|
988
|
+
display_interaction(
|
989
|
+
original_prompt,
|
990
|
+
response_text,
|
991
|
+
markdown=markdown,
|
992
|
+
generation_time=time.time() - start_time,
|
993
|
+
console=console
|
994
|
+
)
|
1039
995
|
|
1040
996
|
response_text = response_text.strip()
|
1041
997
|
|
998
|
+
# Return reasoning content if reasoning_steps is True and we have it
|
999
|
+
if reasoning_steps and stored_reasoning_content:
|
1000
|
+
return stored_reasoning_content
|
1001
|
+
|
1042
1002
|
# Handle output formatting
|
1043
1003
|
if output_json or output_pydantic:
|
1044
1004
|
self.chat_history.append({"role": "user", "content": original_prompt})
|
@@ -1053,8 +1013,8 @@ class LLM:
|
|
1053
1013
|
display_interaction(original_prompt, response_text, markdown=markdown,
|
1054
1014
|
generation_time=time.time() - start_time, console=console)
|
1055
1015
|
# Return reasoning content if reasoning_steps is True
|
1056
|
-
if reasoning_steps and
|
1057
|
-
return
|
1016
|
+
if reasoning_steps and stored_reasoning_content:
|
1017
|
+
return stored_reasoning_content
|
1058
1018
|
return response_text
|
1059
1019
|
|
1060
1020
|
# Handle self-reflection loop
|
@@ -1317,118 +1277,126 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
|
1317
1277
|
# Format tools for LiteLLM using the shared helper
|
1318
1278
|
formatted_tools = self._format_tools_for_litellm(tools)
|
1319
1279
|
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1280
|
+
# Initialize variables for iteration loop
|
1281
|
+
max_iterations = 10 # Prevent infinite loops
|
1282
|
+
iteration_count = 0
|
1283
|
+
final_response_text = ""
|
1284
|
+
stored_reasoning_content = None # Store reasoning content from tool execution
|
1285
|
+
|
1286
|
+
while iteration_count < max_iterations:
|
1287
|
+
response_text = ""
|
1288
|
+
reasoning_content = None
|
1289
|
+
tool_calls = []
|
1290
|
+
|
1291
|
+
if reasoning_steps and iteration_count == 0:
|
1292
|
+
# Non-streaming call to capture reasoning
|
1293
|
+
resp = await litellm.acompletion(
|
1294
|
+
**self._build_completion_params(
|
1295
|
+
messages=messages,
|
1326
1296
|
temperature=temperature,
|
1327
1297
|
stream=False, # force non-streaming
|
1328
1298
|
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
|
1329
1299
|
)
|
1330
|
-
)
|
1331
|
-
reasoning_content = resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
|
1332
|
-
response_text = resp["choices"][0]["message"]["content"]
|
1333
|
-
|
1334
|
-
if verbose and reasoning_content:
|
1335
|
-
display_interaction(
|
1336
|
-
"Initial reasoning:",
|
1337
|
-
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
|
1338
|
-
markdown=markdown,
|
1339
|
-
generation_time=time.time() - start_time,
|
1340
|
-
console=console
|
1341
|
-
)
|
1342
|
-
elif verbose:
|
1343
|
-
display_interaction(
|
1344
|
-
"Initial response:",
|
1345
|
-
response_text,
|
1346
|
-
markdown=markdown,
|
1347
|
-
generation_time=time.time() - start_time,
|
1348
|
-
console=console
|
1349
1300
|
)
|
1350
|
-
|
1351
|
-
|
1352
|
-
use_streaming = stream
|
1353
|
-
if formatted_tools and not self._supports_streaming_tools():
|
1354
|
-
# Provider doesn't support streaming with tools, use non-streaming
|
1355
|
-
use_streaming = False
|
1356
|
-
|
1357
|
-
if use_streaming:
|
1358
|
-
# Streaming approach (with or without tools)
|
1359
|
-
tool_calls = []
|
1301
|
+
reasoning_content = resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
|
1302
|
+
response_text = resp["choices"][0]["message"]["content"]
|
1360
1303
|
|
1361
|
-
if verbose:
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1304
|
+
if verbose and reasoning_content:
|
1305
|
+
display_interaction(
|
1306
|
+
"Initial reasoning:",
|
1307
|
+
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
|
1308
|
+
markdown=markdown,
|
1309
|
+
generation_time=time.time() - start_time,
|
1310
|
+
console=console
|
1311
|
+
)
|
1312
|
+
elif verbose:
|
1313
|
+
display_interaction(
|
1314
|
+
"Initial response:",
|
1315
|
+
response_text,
|
1316
|
+
markdown=markdown,
|
1317
|
+
generation_time=time.time() - start_time,
|
1318
|
+
console=console
|
1319
|
+
)
|
1320
|
+
else:
|
1321
|
+
# Determine if we should use streaming based on tool support
|
1322
|
+
use_streaming = stream
|
1323
|
+
if formatted_tools and not self._supports_streaming_tools():
|
1324
|
+
# Provider doesn't support streaming with tools, use non-streaming
|
1325
|
+
use_streaming = False
|
1326
|
+
|
1327
|
+
if use_streaming:
|
1328
|
+
# Streaming approach (with or without tools)
|
1329
|
+
tool_calls = []
|
1330
|
+
|
1331
|
+
if verbose:
|
1332
|
+
async for chunk in await litellm.acompletion(
|
1333
|
+
**self._build_completion_params(
|
1334
|
+
messages=messages,
|
1335
|
+
temperature=temperature,
|
1336
|
+
stream=True,
|
1337
|
+
tools=formatted_tools,
|
1338
|
+
**kwargs
|
1375
1339
|
)
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1340
|
+
):
|
1341
|
+
if chunk and chunk.choices and chunk.choices[0].delta:
|
1342
|
+
delta = chunk.choices[0].delta
|
1343
|
+
response_text, tool_calls = self._process_stream_delta(
|
1344
|
+
delta, response_text, tool_calls, formatted_tools
|
1345
|
+
)
|
1346
|
+
if delta.content:
|
1347
|
+
print("\033[K", end="\r")
|
1348
|
+
print(f"Generating... {time.time() - start_time:.1f}s", end="\r")
|
1379
1349
|
|
1350
|
+
else:
|
1351
|
+
# Non-verbose streaming
|
1352
|
+
async for chunk in await litellm.acompletion(
|
1353
|
+
**self._build_completion_params(
|
1354
|
+
messages=messages,
|
1355
|
+
temperature=temperature,
|
1356
|
+
stream=True,
|
1357
|
+
tools=formatted_tools,
|
1358
|
+
**kwargs
|
1359
|
+
)
|
1360
|
+
):
|
1361
|
+
if chunk and chunk.choices and chunk.choices[0].delta:
|
1362
|
+
delta = chunk.choices[0].delta
|
1363
|
+
if delta.content:
|
1364
|
+
response_text += delta.content
|
1365
|
+
|
1366
|
+
# Capture tool calls from streaming chunks if provider supports it
|
1367
|
+
if formatted_tools and self._supports_streaming_tools():
|
1368
|
+
tool_calls = self._process_tool_calls_from_stream(delta, tool_calls)
|
1369
|
+
|
1370
|
+
response_text = response_text.strip()
|
1371
|
+
|
1372
|
+
# We already have tool_calls from streaming if supported
|
1373
|
+
# No need for a second API call!
|
1380
1374
|
else:
|
1381
|
-
# Non-
|
1382
|
-
|
1375
|
+
# Non-streaming approach (when tools require it or streaming is disabled)
|
1376
|
+
tool_response = await litellm.acompletion(
|
1383
1377
|
**self._build_completion_params(
|
1384
1378
|
messages=messages,
|
1385
1379
|
temperature=temperature,
|
1386
|
-
stream=
|
1380
|
+
stream=False,
|
1387
1381
|
tools=formatted_tools,
|
1388
|
-
**kwargs
|
1382
|
+
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
|
1389
1383
|
)
|
1390
|
-
):
|
1391
|
-
if chunk and chunk.choices and chunk.choices[0].delta:
|
1392
|
-
delta = chunk.choices[0].delta
|
1393
|
-
if delta.content:
|
1394
|
-
response_text += delta.content
|
1395
|
-
|
1396
|
-
# Capture tool calls from streaming chunks if provider supports it
|
1397
|
-
if formatted_tools and self._supports_streaming_tools():
|
1398
|
-
tool_calls = self._process_tool_calls_from_stream(delta, tool_calls)
|
1399
|
-
|
1400
|
-
response_text = response_text.strip()
|
1401
|
-
|
1402
|
-
# We already have tool_calls from streaming if supported
|
1403
|
-
# No need for a second API call!
|
1404
|
-
else:
|
1405
|
-
# Non-streaming approach (when tools require it or streaming is disabled)
|
1406
|
-
tool_response = await litellm.acompletion(
|
1407
|
-
**self._build_completion_params(
|
1408
|
-
messages=messages,
|
1409
|
-
temperature=temperature,
|
1410
|
-
stream=False,
|
1411
|
-
tools=formatted_tools,
|
1412
|
-
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
|
1413
|
-
)
|
1414
|
-
)
|
1415
|
-
response_text = tool_response.choices[0].message.get("content", "")
|
1416
|
-
tool_calls = tool_response.choices[0].message.get("tool_calls", [])
|
1417
|
-
|
1418
|
-
if verbose:
|
1419
|
-
# Display the complete response at once
|
1420
|
-
display_interaction(
|
1421
|
-
original_prompt,
|
1422
|
-
response_text,
|
1423
|
-
markdown=markdown,
|
1424
|
-
generation_time=time.time() - start_time,
|
1425
|
-
console=console
|
1426
1384
|
)
|
1385
|
+
response_text = tool_response.choices[0].message.get("content", "")
|
1386
|
+
tool_calls = tool_response.choices[0].message.get("tool_calls", [])
|
1387
|
+
|
1388
|
+
if verbose:
|
1389
|
+
# Display the complete response at once
|
1390
|
+
display_interaction(
|
1391
|
+
original_prompt,
|
1392
|
+
response_text,
|
1393
|
+
markdown=markdown,
|
1394
|
+
generation_time=time.time() - start_time,
|
1395
|
+
console=console
|
1396
|
+
)
|
1427
1397
|
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
if tool_calls:
|
1398
|
+
# Now handle tools if we have them (either from streaming or non-streaming)
|
1399
|
+
if tools and execute_tool_fn and tool_calls:
|
1432
1400
|
# Convert tool_calls to a serializable format for all providers
|
1433
1401
|
serializable_tool_calls = self._serialize_tool_calls(tool_calls)
|
1434
1402
|
messages.append({
|
@@ -1509,9 +1477,16 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
|
1509
1477
|
console=console
|
1510
1478
|
)
|
1511
1479
|
|
1512
|
-
#
|
1480
|
+
# Store the response for potential final return
|
1513
1481
|
if final_response_text:
|
1514
|
-
|
1482
|
+
# Update messages with the response to maintain conversation context
|
1483
|
+
messages.append({
|
1484
|
+
"role": "assistant",
|
1485
|
+
"content": final_response_text
|
1486
|
+
})
|
1487
|
+
# Continue the loop to check if more tools are needed
|
1488
|
+
iteration_count += 1
|
1489
|
+
continue
|
1515
1490
|
else:
|
1516
1491
|
logging.warning("[OLLAMA_DEBUG] Ollama follow-up returned empty response")
|
1517
1492
|
|
@@ -1577,6 +1552,27 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
|
1577
1552
|
response_text += chunk.choices[0].delta.content
|
1578
1553
|
|
1579
1554
|
response_text = response_text.strip()
|
1555
|
+
|
1556
|
+
# After tool execution, update messages and continue the loop
|
1557
|
+
if response_text:
|
1558
|
+
messages.append({
|
1559
|
+
"role": "assistant",
|
1560
|
+
"content": response_text
|
1561
|
+
})
|
1562
|
+
|
1563
|
+
# Store reasoning content if captured
|
1564
|
+
if reasoning_steps and reasoning_content:
|
1565
|
+
stored_reasoning_content = reasoning_content
|
1566
|
+
|
1567
|
+
# Continue the loop to check if more tools are needed
|
1568
|
+
iteration_count += 1
|
1569
|
+
continue
|
1570
|
+
else:
|
1571
|
+
# No tool calls, we're done with this iteration
|
1572
|
+
# If we've executed tools in previous iterations, this response contains the final answer
|
1573
|
+
if iteration_count > 0:
|
1574
|
+
final_response_text = response_text.strip()
|
1575
|
+
break
|
1580
1576
|
|
1581
1577
|
# Handle output formatting
|
1582
1578
|
if output_json or output_pydantic:
|
@@ -1588,13 +1584,27 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
|
1588
1584
|
return response_text
|
1589
1585
|
|
1590
1586
|
if not self_reflect:
|
1587
|
+
# Use final_response_text if we went through tool iterations
|
1588
|
+
display_text = final_response_text if final_response_text else response_text
|
1589
|
+
|
1590
|
+
# Display with stored reasoning content if available
|
1591
1591
|
if verbose:
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1592
|
+
if stored_reasoning_content:
|
1593
|
+
display_interaction(
|
1594
|
+
original_prompt,
|
1595
|
+
f"Reasoning:\n{stored_reasoning_content}\n\nAnswer:\n{display_text}",
|
1596
|
+
markdown=markdown,
|
1597
|
+
generation_time=time.time() - start_time,
|
1598
|
+
console=console
|
1599
|
+
)
|
1600
|
+
else:
|
1601
|
+
display_interaction(original_prompt, display_text, markdown=markdown,
|
1602
|
+
generation_time=time.time() - start_time, console=console)
|
1603
|
+
|
1604
|
+
# Return reasoning content if reasoning_steps is True and we have it
|
1605
|
+
if reasoning_steps and stored_reasoning_content:
|
1606
|
+
return stored_reasoning_content
|
1607
|
+
return display_text
|
1598
1608
|
|
1599
1609
|
# Handle self-reflection
|
1600
1610
|
reflection_prompt = f"""
|
@@ -64,6 +64,7 @@ praisonaiagents/tools/yfinance_tools.py
|
|
64
64
|
praisonaiagents/tools/train/data/generatecot.py
|
65
65
|
tests/test-graph-memory.py
|
66
66
|
tests/test.py
|
67
|
+
tests/test_fix_comprehensive.py
|
67
68
|
tests/test_handoff_compatibility.py
|
68
69
|
tests/test_http_stream_basic.py
|
69
70
|
tests/test_ollama_async_fix.py
|
@@ -0,0 +1,75 @@
|
|
1
|
+
"""
|
2
|
+
Test script to verify the fix for sequential tool calling.
|
3
|
+
The agent should:
|
4
|
+
1. Call get_stock_price to get Google's stock price (100)
|
5
|
+
2. Call multiply to multiply 100 by 2
|
6
|
+
3. Return the final result (200)
|
7
|
+
"""
|
8
|
+
|
9
|
+
from praisonaiagents import Agent
|
10
|
+
|
11
|
+
def get_stock_price(company_name: str) -> str:
|
12
|
+
"""
|
13
|
+
Get the stock price of a company
|
14
|
+
|
15
|
+
Args:
|
16
|
+
company_name (str): The name of the company
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
str: The stock price of the company
|
20
|
+
"""
|
21
|
+
print(f"[Tool Called] get_stock_price({company_name})")
|
22
|
+
return f"The stock price of {company_name} is 100"
|
23
|
+
|
24
|
+
def multiply(a: int, b: int) -> int:
|
25
|
+
"""
|
26
|
+
Multiply two numbers
|
27
|
+
|
28
|
+
Args:
|
29
|
+
a (int): First number
|
30
|
+
b (int): Second number
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
int: Product of a and b
|
34
|
+
"""
|
35
|
+
print(f"[Tool Called] multiply({a}, {b})")
|
36
|
+
return a * b
|
37
|
+
|
38
|
+
# Test with Gemini
|
39
|
+
print("=" * 60)
|
40
|
+
print("Testing with Gemini model")
|
41
|
+
print("=" * 60)
|
42
|
+
|
43
|
+
agent_gemini = Agent(
|
44
|
+
instructions="You are a helpful assistant. You can use the tools provided to you to help the user. When asked to multiply a stock price, first get the stock price, then multiply it.",
|
45
|
+
llm="gemini/gemini-2.5-pro",
|
46
|
+
tools=[get_stock_price, multiply],
|
47
|
+
verbose=True
|
48
|
+
)
|
49
|
+
|
50
|
+
result_gemini = agent_gemini.start("what is the stock price of Google? multiply the Google stock price with 2")
|
51
|
+
print(f"\nFinal Result (Gemini): {result_gemini}")
|
52
|
+
|
53
|
+
# Test with GPT-4
|
54
|
+
print("\n" + "=" * 60)
|
55
|
+
print("Testing with GPT-4 model")
|
56
|
+
print("=" * 60)
|
57
|
+
|
58
|
+
agent_gpt4 = Agent(
|
59
|
+
instructions="You are a helpful assistant. You can use the tools provided to you to help the user. When asked to multiply a stock price, first get the stock price, then multiply it.",
|
60
|
+
llm="gpt-4o",
|
61
|
+
tools=[get_stock_price, multiply],
|
62
|
+
verbose=True
|
63
|
+
)
|
64
|
+
|
65
|
+
result_gpt4 = agent_gpt4.start("what is the stock price of Google? multiply the Google stock price with 2")
|
66
|
+
print(f"\nFinal Result (GPT-4): {result_gpt4}")
|
67
|
+
|
68
|
+
# Verify results
|
69
|
+
print("\n" + "=" * 60)
|
70
|
+
print("Test Results Summary")
|
71
|
+
print("=" * 60)
|
72
|
+
print(f"Gemini result contains '200': {'200' in str(result_gemini) if result_gemini else False}")
|
73
|
+
print(f"GPT-4 result contains '200': {'200' in str(result_gpt4) if result_gpt4 else False}")
|
74
|
+
print(f"Gemini returned empty: {not result_gemini or result_gemini == ''}")
|
75
|
+
print(f"GPT-4 returned empty: {not result_gpt4 or result_gpt4 == ''}")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/guardrails/guardrail_result.py
RENAMED
File without changes
|
{praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/guardrails/llm_guardrail.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/llm/model_capabilities.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/telemetry/integration.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/calculator_tools.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/duckduckgo_tools.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/newspaper_tools.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/train/data/generatecot.py
RENAMED
File without changes
|
{praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents/tools/wikipedia_tools.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{praisonaiagents-0.0.125 → praisonaiagents-0.0.126}/praisonaiagents.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|