praisonaiagents 0.0.135__py3-none-any.whl → 0.0.137__py3-none-any.whl

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.
@@ -302,6 +302,42 @@ class LLM:
302
302
 
303
303
  return False
304
304
 
305
+ def _generate_ollama_tool_summary(self, tool_results: List[Any], response_text: str) -> Optional[str]:
306
+ """
307
+ Generate a summary from tool results for Ollama to prevent infinite loops.
308
+
309
+ This prevents infinite loops where Ollama provides an empty response after a
310
+ tool call, expecting the user to prompt for a summary.
311
+
312
+ Args:
313
+ tool_results: The list of results from tool execution.
314
+ response_text: The text response from the LLM.
315
+
316
+ Returns:
317
+ A summary string if conditions are met, otherwise None.
318
+ """
319
+ # Constant for minimal response length check
320
+ OLLAMA_MIN_RESPONSE_LENGTH = 10
321
+
322
+ # Only generate summary for Ollama with tool results
323
+ if not (self._is_ollama_provider() and tool_results):
324
+ return None
325
+
326
+ # If response is substantial, no summary needed
327
+ if response_text and len(response_text.strip()) > OLLAMA_MIN_RESPONSE_LENGTH:
328
+ return None
329
+
330
+ # Build tool summary efficiently
331
+ summary_lines = ["Based on the tool execution results:"]
332
+ for i, result in enumerate(tool_results):
333
+ if isinstance(result, dict) and 'result' in result:
334
+ function_name = result.get('function_name', 'Tool')
335
+ summary_lines.append(f"- {function_name}: {result['result']}")
336
+ else:
337
+ summary_lines.append(f"- Tool {i+1}: {result}")
338
+
339
+ return "\n".join(summary_lines)
340
+
305
341
  def _format_ollama_tool_result_message(self, function_name: str, tool_result: Any) -> Dict[str, str]:
306
342
  """
307
343
  Format tool result message for Ollama provider.
@@ -1072,13 +1108,19 @@ class LLM:
1072
1108
  final_response_text = response_text.strip()
1073
1109
  break
1074
1110
 
1111
+ # 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
1116
+
1075
1117
  # Otherwise, continue the loop to check if more tools are needed
1076
1118
  iteration_count += 1
1077
1119
  continue
1078
1120
  else:
1079
1121
  # No tool calls, we're done with this iteration
1080
1122
  # If we've executed tools in previous iterations, this response contains the final answer
1081
- if iteration_count > 0:
1123
+ if iteration_count > 0 and not final_response_text:
1082
1124
  final_response_text = response_text.strip() if response_text else ""
1083
1125
  break
1084
1126
 
@@ -1815,13 +1857,19 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
1815
1857
  final_response_text = response_text.strip()
1816
1858
  break
1817
1859
 
1860
+ # 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
1865
+
1818
1866
  # Continue the loop to check if more tools are needed
1819
1867
  iteration_count += 1
1820
1868
  continue
1821
1869
  else:
1822
1870
  # No tool calls, we're done with this iteration
1823
1871
  # If we've executed tools in previous iterations, this response contains the final answer
1824
- if iteration_count > 0:
1872
+ if iteration_count > 0 and not final_response_text:
1825
1873
  final_response_text = response_text.strip()
1826
1874
  break
1827
1875
 
@@ -78,7 +78,7 @@ class Session:
78
78
  if not self.is_remote:
79
79
  default_memory_config = {
80
80
  "provider": "rag",
81
- "use_embedding": True,
81
+ "use_embedding": False, # Disable embeddings to avoid OpenAI API key requirement
82
82
  "rag_db_path": f".praison/sessions/{self.session_id}/chroma_db"
83
83
  }
84
84
  if memory_config:
@@ -96,6 +96,7 @@ class Session:
96
96
  self._memory = None
97
97
  self._knowledge = None
98
98
  self._agents_instance = None
99
+ self._agents = {} # Track agents and their chat histories
99
100
  else:
100
101
  # For remote sessions, disable local memory/knowledge
101
102
  self.memory_config = {}
@@ -103,6 +104,7 @@ class Session:
103
104
  self._memory = None
104
105
  self._knowledge = None
105
106
  self._agents_instance = None
107
+ self._agents = {} # Track agents and their chat histories
106
108
 
107
109
  @property
108
110
  def memory(self) -> Memory:
@@ -170,7 +172,23 @@ class Session:
170
172
  agent_kwargs["knowledge"] = knowledge
171
173
  agent_kwargs["knowledge_config"] = self.knowledge_config
172
174
 
173
- return Agent(**agent_kwargs)
175
+ agent = Agent(**agent_kwargs)
176
+
177
+ # Create a unique key for this agent (using name and role)
178
+ agent_key = f"{name}:{role}"
179
+
180
+ # Restore chat history if it exists from previous sessions
181
+ if agent_key in self._agents:
182
+ agent.chat_history = self._agents[agent_key].get("chat_history", [])
183
+ else:
184
+ # Try to restore from memory for backward compatibility
185
+ restored_history = self._restore_agent_chat_history(agent_key)
186
+ agent.chat_history = restored_history
187
+
188
+ # Track the agent
189
+ self._agents[agent_key] = {"agent": agent, "chat_history": agent.chat_history}
190
+
191
+ return agent
174
192
 
175
193
  # Keep create_agent for backward compatibility
176
194
  def create_agent(self, *args, **kwargs) -> Agent:
@@ -189,6 +207,11 @@ class Session:
189
207
  """
190
208
  if self.is_remote:
191
209
  raise ValueError("State operations are not available for remote agent sessions")
210
+
211
+ # Save agent chat histories first
212
+ self._save_agent_chat_histories()
213
+
214
+ # Save session state
192
215
  state_text = f"Session state: {state_data}"
193
216
  self.memory.store_short_term(
194
217
  text=state_text,
@@ -212,12 +235,15 @@ class Session:
212
235
  """
213
236
  if self.is_remote:
214
237
  raise ValueError("State operations are not available for remote agent sessions")
215
- # Use metadata-based search for better SQLite compatibility
238
+ # Use content-based search for better SQLite compatibility
216
239
  results = self.memory.search_short_term(
217
- query=f"type:session_state",
240
+ query="Session state:",
218
241
  limit=10 # Get more results to filter by session_id
219
242
  )
220
243
 
244
+ # Restore agent chat histories first
245
+ self._restore_agent_chat_histories()
246
+
221
247
  # Filter results by session_id in metadata
222
248
  for result in results:
223
249
  metadata = result.get("metadata", {})
@@ -230,6 +256,97 @@ class Session:
230
256
 
231
257
  return {}
232
258
 
259
+ def _restore_agent_chat_history(self, agent_key: str) -> List[Dict[str, Any]]:
260
+ """
261
+ Restore agent chat history from memory.
262
+
263
+ Args:
264
+ agent_key: Unique identifier for the agent
265
+
266
+ Returns:
267
+ List of chat history messages or empty list if not found
268
+ """
269
+ if self.is_remote:
270
+ return []
271
+
272
+ # Search for agent chat history in memory
273
+ results = self.memory.search_short_term(
274
+ query="Agent chat history for",
275
+ limit=10
276
+ )
277
+
278
+ # Filter results by session_id and agent_key
279
+ for result in results:
280
+ metadata = result.get("metadata", {})
281
+ if (metadata.get("type") == "agent_chat_history" and
282
+ metadata.get("session_id") == self.session_id and
283
+ metadata.get("agent_key") == agent_key):
284
+ # Extract chat history from metadata
285
+ chat_history = metadata.get("chat_history", [])
286
+ return chat_history
287
+
288
+ return []
289
+
290
+ def _restore_agent_chat_histories(self) -> None:
291
+ """
292
+ Restore all agent chat histories from memory.
293
+ """
294
+ if self.is_remote:
295
+ return
296
+
297
+ # Search for all agent chat histories in memory
298
+ results = self.memory.search_short_term(
299
+ query="Agent chat history for",
300
+ limit=50 # Get many results to find all agents
301
+ )
302
+
303
+ # Filter and restore chat histories for this session
304
+ for result in results:
305
+ metadata = result.get("metadata", {})
306
+ if (metadata.get("type") == "agent_chat_history" and
307
+ metadata.get("session_id") == self.session_id):
308
+ agent_key = metadata.get("agent_key")
309
+ chat_history = metadata.get("chat_history", [])
310
+
311
+ if agent_key and chat_history:
312
+ # Store in _agents dict for later retrieval
313
+ self._agents[agent_key] = {
314
+ "agent": None, # Will be populated when Agent is created
315
+ "chat_history": chat_history
316
+ }
317
+
318
+ def _save_agent_chat_histories(self) -> None:
319
+ """
320
+ Save all agent chat histories to memory.
321
+ """
322
+ if self.is_remote:
323
+ return
324
+
325
+ for agent_key, agent_data in self._agents.items():
326
+ agent = agent_data.get("agent")
327
+ chat_history = None
328
+
329
+ # Prioritize history from the live agent object, but fall back to restored history
330
+ if agent and hasattr(agent, 'chat_history'):
331
+ chat_history = agent.chat_history
332
+ agent_data["chat_history"] = chat_history # Ensure tracked history is up-to-date
333
+ else:
334
+ chat_history = agent_data.get("chat_history")
335
+
336
+ if chat_history is not None:
337
+ # Save to memory
338
+ history_text = f"Agent chat history for {agent_key}"
339
+ self.memory.store_short_term(
340
+ text=history_text,
341
+ metadata={
342
+ "type": "agent_chat_history",
343
+ "session_id": self.session_id,
344
+ "user_id": self.user_id,
345
+ "agent_key": agent_key,
346
+ "chat_history": chat_history
347
+ }
348
+ )
349
+
233
350
  def get_state(self, key: str, default: Any = None) -> Any:
234
351
  """Get a specific state value"""
235
352
  state = self.restore_state()
@@ -241,6 +358,11 @@ class Session:
241
358
  current_state[key] = value
242
359
  self.save_state(current_state)
243
360
 
361
+ def increment_state(self, key: str, increment: int = 1, default: int = 0) -> None:
362
+ """Increment a numeric state value"""
363
+ current_value = self.get_state(key, default)
364
+ self.set_state(key, current_value + increment)
365
+
244
366
  def add_memory(self, text: str, memory_type: str = "long", **metadata) -> None:
245
367
  """
246
368
  Add information to session memory.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praisonaiagents
3
- Version: 0.0.135
3
+ Version: 0.0.137
4
4
  Summary: Praison AI agents for completing complex tasks with Self Reflection Agents
5
5
  Author: Mervin Praison
6
6
  Requires-Python: >=3.10
@@ -1,7 +1,7 @@
1
1
  praisonaiagents/__init__.py,sha256=7DZJjhHa1_OeP0aiR5_iXINXYiug_gtbOuh_ylad6Uc,3847
2
2
  praisonaiagents/approval.py,sha256=UJ4OhfihpFGR5CAaMphqpSvqdZCHi5w2MGw1MByZ1FQ,9813
3
3
  praisonaiagents/main.py,sha256=b5dKlkf6NMeumSzixreHB9ui90f8YMAi5r1fCbTpQVw,17225
4
- praisonaiagents/session.py,sha256=d-CZPYikOHb0q-H9f_IWKJsypnQfz1YKeLLkyxs6oDo,15532
4
+ praisonaiagents/session.py,sha256=FHWButPBaFGA4x1U_2gImroQChHnFy231_aAa_n5KOQ,20364
5
5
  praisonaiagents/agent/__init__.py,sha256=FkjW6f3EU8heQ9tvctfLbOWV9_dOXmS1PcFNgcStns8,403
6
6
  praisonaiagents/agent/agent.py,sha256=1m9s6irqenYatuoGHHS1DX913rm9QpjcJHJjvPH6_QE,123338
7
7
  praisonaiagents/agent/handoff.py,sha256=Saq0chqfvC6Zf5UbXvmctybbehqnotrXn72JsS-76Q0,13099
@@ -17,7 +17,7 @@ praisonaiagents/knowledge/__init__.py,sha256=xL1Eh-a3xsHyIcU4foOWF-JdWYIYBALJH9b
17
17
  praisonaiagents/knowledge/chunking.py,sha256=G6wyHa7_8V0_7VpnrrUXbEmUmptlT16ISJYaxmkSgmU,7678
18
18
  praisonaiagents/knowledge/knowledge.py,sha256=-di_h9HxXQfAhTMMerhK16tfw8DtUndp44TGkBOzkZs,15539
19
19
  praisonaiagents/llm/__init__.py,sha256=tHvWq5mv4K4MhWr0s6rqox8UnJ5RK0kXhYuD40WkZQA,1747
20
- praisonaiagents/llm/llm.py,sha256=trPfrRKfwG4z2bx26gAbdfDrAIIqU7Vdqq3pAyCqO-g,126191
20
+ praisonaiagents/llm/llm.py,sha256=-4wADWAR5DHn9Ja-XgVOc7wl2Wh60KWDHEE7aGqvx3I,128441
21
21
  praisonaiagents/llm/model_capabilities.py,sha256=cxOvZcjZ_PIEpUYKn3S2FMyypfOSfbGpx4vmV7Y5vhI,3967
22
22
  praisonaiagents/llm/model_router.py,sha256=Jy2pShlkLxqXF3quz-MRB3-6L9vaUSgUrf2YJs_Tsg0,13995
23
23
  praisonaiagents/llm/openai_client.py,sha256=EgWjkDjVpnLKCp1gBFjccDGyqR1anOcSYJYCo45fuEI,46046
@@ -57,7 +57,7 @@ praisonaiagents/tools/xml_tools.py,sha256=iYTMBEk5l3L3ryQ1fkUnNVYK-Nnua2Kx2S0dxN
57
57
  praisonaiagents/tools/yaml_tools.py,sha256=uogAZrhXV9O7xvspAtcTfpKSQYL2nlOTvCQXN94-G9A,14215
58
58
  praisonaiagents/tools/yfinance_tools.py,sha256=s2PBj_1v7oQnOobo2fDbQBACEHl61ftG4beG6Z979ZE,8529
59
59
  praisonaiagents/tools/train/data/generatecot.py,sha256=H6bNh-E2hqL5MW6kX3hqZ05g9ETKN2-kudSjiuU_SD8,19403
60
- praisonaiagents-0.0.135.dist-info/METADATA,sha256=NsRPw0-Eabr1fhnHuTCH4yWtpwSX8sRfpFfeC6tor-o,1673
61
- praisonaiagents-0.0.135.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
62
- praisonaiagents-0.0.135.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
63
- praisonaiagents-0.0.135.dist-info/RECORD,,
60
+ praisonaiagents-0.0.137.dist-info/METADATA,sha256=a_1VYrge8bQjC5ysjayhFMbgB_2BoHAmrFabr3tR7zI,1673
61
+ praisonaiagents-0.0.137.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
62
+ praisonaiagents-0.0.137.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
63
+ praisonaiagents-0.0.137.dist-info/RECORD,,