praisonaiagents 0.0.134__py3-none-any.whl → 0.0.136__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.
- praisonaiagents/llm/llm.py +75 -4
- praisonaiagents/llm/openai_client.py +7 -4
- praisonaiagents/session.py +126 -4
- {praisonaiagents-0.0.134.dist-info → praisonaiagents-0.0.136.dist-info}/METADATA +1 -2
- {praisonaiagents-0.0.134.dist-info → praisonaiagents-0.0.136.dist-info}/RECORD +7 -7
- {praisonaiagents-0.0.134.dist-info → praisonaiagents-0.0.136.dist-info}/WHEEL +0 -0
- {praisonaiagents-0.0.134.dist-info → praisonaiagents-0.0.136.dist-info}/top_level.txt +0 -0
praisonaiagents/llm/llm.py
CHANGED
@@ -2,6 +2,7 @@ import logging
|
|
2
2
|
import os
|
3
3
|
import warnings
|
4
4
|
import re
|
5
|
+
import inspect
|
5
6
|
from typing import Any, Dict, List, Optional, Union, Literal, Callable
|
6
7
|
from pydantic import BaseModel
|
7
8
|
import time
|
@@ -16,7 +17,6 @@ from ..main import (
|
|
16
17
|
ReflectionOutput,
|
17
18
|
execute_sync_callback,
|
18
19
|
)
|
19
|
-
from .model_capabilities import is_gemini_internal_tool
|
20
20
|
from rich.console import Console
|
21
21
|
from rich.live import Live
|
22
22
|
|
@@ -380,6 +380,65 @@ class LLM:
|
|
380
380
|
|
381
381
|
return function_name, arguments, tool_call_id
|
382
382
|
|
383
|
+
def _validate_and_filter_ollama_arguments(self, function_name: str, arguments: Dict[str, Any], available_tools: List) -> Dict[str, Any]:
|
384
|
+
"""
|
385
|
+
Validate and filter tool call arguments for Ollama provider.
|
386
|
+
|
387
|
+
Ollama sometimes generates tool calls with mixed parameters where arguments
|
388
|
+
from different functions are combined. This method validates arguments against
|
389
|
+
the actual function signature and removes invalid parameters.
|
390
|
+
|
391
|
+
Args:
|
392
|
+
function_name: Name of the function to call
|
393
|
+
arguments: Arguments provided in the tool call
|
394
|
+
available_tools: List of available tool functions
|
395
|
+
|
396
|
+
Returns:
|
397
|
+
Filtered arguments dictionary with only valid parameters
|
398
|
+
"""
|
399
|
+
if not available_tools:
|
400
|
+
logging.debug(f"[OLLAMA_FIX] No available tools provided for validation")
|
401
|
+
return arguments
|
402
|
+
|
403
|
+
# Find the target function
|
404
|
+
target_function = None
|
405
|
+
for tool in available_tools:
|
406
|
+
tool_name = getattr(tool, '__name__', str(tool))
|
407
|
+
if tool_name == function_name:
|
408
|
+
target_function = tool
|
409
|
+
break
|
410
|
+
|
411
|
+
if not target_function:
|
412
|
+
logging.debug(f"[OLLAMA_FIX] Function {function_name} not found in available tools")
|
413
|
+
return arguments
|
414
|
+
|
415
|
+
try:
|
416
|
+
# Get function signature
|
417
|
+
sig = inspect.signature(target_function)
|
418
|
+
valid_params = set(sig.parameters.keys())
|
419
|
+
|
420
|
+
# Filter arguments to only include valid parameters
|
421
|
+
filtered_args = {}
|
422
|
+
invalid_params = []
|
423
|
+
|
424
|
+
for param_name, param_value in arguments.items():
|
425
|
+
if param_name in valid_params:
|
426
|
+
filtered_args[param_name] = param_value
|
427
|
+
else:
|
428
|
+
invalid_params.append(param_name)
|
429
|
+
|
430
|
+
if invalid_params:
|
431
|
+
logging.debug(f"[OLLAMA_FIX] Function {function_name} received invalid parameters: {invalid_params}")
|
432
|
+
logging.debug(f"[OLLAMA_FIX] Valid parameters for {function_name}: {list(valid_params)}")
|
433
|
+
logging.debug(f"[OLLAMA_FIX] Original arguments: {arguments}")
|
434
|
+
logging.debug(f"[OLLAMA_FIX] Filtered arguments: {filtered_args}")
|
435
|
+
|
436
|
+
return filtered_args
|
437
|
+
|
438
|
+
except Exception as e:
|
439
|
+
logging.debug(f"[OLLAMA_FIX] Error validating arguments for {function_name}: {e}")
|
440
|
+
return arguments
|
441
|
+
|
383
442
|
def _needs_system_message_skip(self) -> bool:
|
384
443
|
"""Check if this model requires skipping system messages"""
|
385
444
|
if not self.model:
|
@@ -591,10 +650,14 @@ class LLM:
|
|
591
650
|
if tool_def:
|
592
651
|
formatted_tools.append(tool_def)
|
593
652
|
# Handle Gemini internal tools (e.g., {"googleSearch": {}}, {"urlContext": {}}, {"codeExecution": {}})
|
594
|
-
elif
|
653
|
+
elif isinstance(tool, dict) and len(tool) == 1:
|
595
654
|
tool_name = next(iter(tool.keys()))
|
596
|
-
|
597
|
-
|
655
|
+
gemini_internal_tools = {'googleSearch', 'urlContext', 'codeExecution'}
|
656
|
+
if tool_name in gemini_internal_tools:
|
657
|
+
logging.debug(f"Using Gemini internal tool: {tool_name}")
|
658
|
+
formatted_tools.append(tool)
|
659
|
+
else:
|
660
|
+
logging.debug(f"Skipping unknown tool: {tool_name}")
|
598
661
|
else:
|
599
662
|
logging.debug(f"Skipping tool of unsupported type: {type(tool)}")
|
600
663
|
|
@@ -959,6 +1022,10 @@ class LLM:
|
|
959
1022
|
is_ollama = self._is_ollama_provider()
|
960
1023
|
function_name, arguments, tool_call_id = self._extract_tool_call_info(tool_call, is_ollama)
|
961
1024
|
|
1025
|
+
# Validate and filter arguments for Ollama provider
|
1026
|
+
if is_ollama and tools:
|
1027
|
+
arguments = self._validate_and_filter_ollama_arguments(function_name, arguments, tools)
|
1028
|
+
|
962
1029
|
logging.debug(f"[TOOL_EXEC_DEBUG] About to execute tool {function_name} with args: {arguments}")
|
963
1030
|
tool_result = execute_tool_fn(function_name, arguments)
|
964
1031
|
logging.debug(f"[TOOL_EXEC_DEBUG] Tool execution result: {tool_result}")
|
@@ -1610,6 +1677,10 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
|
1610
1677
|
is_ollama = self._is_ollama_provider()
|
1611
1678
|
function_name, arguments, tool_call_id = self._extract_tool_call_info(tool_call, is_ollama)
|
1612
1679
|
|
1680
|
+
# Validate and filter arguments for Ollama provider
|
1681
|
+
if is_ollama and tools:
|
1682
|
+
arguments = self._validate_and_filter_ollama_arguments(function_name, arguments, tools)
|
1683
|
+
|
1613
1684
|
tool_result = await execute_tool_fn(function_name, arguments)
|
1614
1685
|
tool_results.append(tool_result) # Store the result
|
1615
1686
|
|
@@ -18,7 +18,6 @@ from dataclasses import dataclass
|
|
18
18
|
from rich.console import Console
|
19
19
|
from rich.live import Live
|
20
20
|
import inspect
|
21
|
-
from .model_capabilities import is_gemini_internal_tool
|
22
21
|
|
23
22
|
# Constants
|
24
23
|
LOCAL_SERVER_API_KEY_PLACEHOLDER = "not-needed"
|
@@ -407,10 +406,14 @@ class OpenAIClient:
|
|
407
406
|
if tool_def:
|
408
407
|
formatted_tools.append(tool_def)
|
409
408
|
# Handle Gemini internal tools (e.g., {"googleSearch": {}}, {"urlContext": {}}, {"codeExecution": {}})
|
410
|
-
elif
|
409
|
+
elif isinstance(tool, dict) and len(tool) == 1:
|
411
410
|
tool_name = next(iter(tool.keys()))
|
412
|
-
|
413
|
-
|
411
|
+
gemini_internal_tools = {'googleSearch', 'urlContext', 'codeExecution'}
|
412
|
+
if tool_name in gemini_internal_tools:
|
413
|
+
logging.debug(f"Using Gemini internal tool: {tool_name}")
|
414
|
+
formatted_tools.append(tool)
|
415
|
+
else:
|
416
|
+
logging.debug(f"Skipping unknown tool: {tool_name}")
|
414
417
|
else:
|
415
418
|
logging.debug(f"Skipping tool of unsupported type: {type(tool)}")
|
416
419
|
|
praisonaiagents/session.py
CHANGED
@@ -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":
|
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
|
-
|
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
|
238
|
+
# Use content-based search for better SQLite compatibility
|
216
239
|
results = self.memory.search_short_term(
|
217
|
-
query=
|
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,13 +1,12 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: praisonaiagents
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.136
|
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
|
7
7
|
Requires-Dist: pydantic
|
8
8
|
Requires-Dist: rich
|
9
9
|
Requires-Dist: openai
|
10
|
-
Requires-Dist: mcp>=1.6.0
|
11
10
|
Requires-Dist: posthog>=3.0.0
|
12
11
|
Requires-Dist: aiohttp>=3.8.0
|
13
12
|
Provides-Extra: mcp
|
@@ -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=
|
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,10 +17,10 @@ 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=
|
20
|
+
praisonaiagents/llm/llm.py,sha256=trPfrRKfwG4z2bx26gAbdfDrAIIqU7Vdqq3pAyCqO-g,126191
|
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
|
-
praisonaiagents/llm/openai_client.py,sha256=
|
23
|
+
praisonaiagents/llm/openai_client.py,sha256=EgWjkDjVpnLKCp1gBFjccDGyqR1anOcSYJYCo45fuEI,46046
|
24
24
|
praisonaiagents/mcp/__init__.py,sha256=ibbqe3_7XB7VrIcUcetkZiUZS1fTVvyMy_AqCSFG8qc,240
|
25
25
|
praisonaiagents/mcp/mcp.py,sha256=T0G0rQotHxk9qTnG1tjQLr4c0BUSLnEqz9sIMx4F954,21598
|
26
26
|
praisonaiagents/mcp/mcp_http_stream.py,sha256=Yh-69eIlLQS_M0bd__y7NzSjOqqX6R8Ed4eJQw6xXgg,18314
|
@@ -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.
|
61
|
-
praisonaiagents-0.0.
|
62
|
-
praisonaiagents-0.0.
|
63
|
-
praisonaiagents-0.0.
|
60
|
+
praisonaiagents-0.0.136.dist-info/METADATA,sha256=5ytqaBnL9hmNkwr-14WcmFsCjncMIoYNGOjn3E26Uws,1673
|
61
|
+
praisonaiagents-0.0.136.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
62
|
+
praisonaiagents-0.0.136.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
|
63
|
+
praisonaiagents-0.0.136.dist-info/RECORD,,
|
File without changes
|
File without changes
|