PraisonAI 2.3.74__tar.gz → 2.3.76__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.
- {praisonai-2.3.74 → praisonai-2.3.76}/PKG-INFO +2 -2
- {praisonai-2.3.74 → praisonai-2.3.76}/PraisonAI.egg-info/PKG-INFO +2 -2
- {praisonai-2.3.74 → praisonai-2.3.76}/PraisonAI.egg-info/SOURCES.txt +1 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/PraisonAI.egg-info/requires.txt +1 -1
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/auto_memory.py +29 -20
- praisonai-2.3.76/praisonai/cli/features/profiling.py +350 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/main.py +80 -7
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/deploy.py +1 -1
- praisonai-2.3.76/praisonai/profiler.py +1214 -0
- praisonai-2.3.76/praisonai/version.py +1 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/pyproject.toml +1 -1
- praisonai-2.3.74/praisonai/profiler.py +0 -465
- praisonai-2.3.74/praisonai/version.py +0 -1
- {praisonai-2.3.74 → praisonai-2.3.76}/PraisonAI.egg-info/dependency_links.txt +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/PraisonAI.egg-info/entry_points.txt +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/PraisonAI.egg-info/top_level.txt +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/README.md +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/__main__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/agent_scheduler.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/agents_generator.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/api/call.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/auto.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/chainlit_ui.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/agent_scheduler.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/at_mentions.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/autonomy_mode.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/base.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/compare.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/cost_tracker.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/eval.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/external_agents.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/fast_context.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/flow_display.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/git_integration.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/guardrail.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/handoff.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/image.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/interactive_tui.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/knowledge.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/mcp.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/message_queue.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/metrics.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/n8n.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/repo_map.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/router.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/sandbox_executor.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/session.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/skills.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/slash_commands.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/telemetry.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/todo.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/tools.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/features/workflow.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/cli/schedule_cli.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/agent_tools.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/diff/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/diff/diff_strategy.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/tools/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/tools/apply_diff.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/tools/execute_command.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/tools/list_files.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/tools/read_file.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/tools/search_replace.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/tools/write_file.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/utils/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/utils/file_utils.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/utils/ignore_utils.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/code/utils/text_utils.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/inbuilt_tools/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/inc/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/inc/config.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/inc/models.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/integrations/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/integrations/base.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/integrations/claude_code.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/integrations/codex_cli.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/integrations/cursor_cli.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/integrations/gemini_cli.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/scheduler/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/scheduler/agent_scheduler.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/scheduler/base.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/scheduler/daemon_manager.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/scheduler/state_manager.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/scheduler/yaml_loader.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/scheduler.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/setup/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/setup/build.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/setup/post_install.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/setup/setup_conda_env.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/setup.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/test.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/train.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/train_vision.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/agents.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/callbacks.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/chat.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/code.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/colab.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/colab_chainlit.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/components/aicoder.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/context.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/database_config.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/db.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/realtime.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/realtimeclient/__init__.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/realtimeclient/tools.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/ui/sql_alchemy.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/praisonai/upload_vision.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/setup.cfg +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/tests/test.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/tests/test_agents_playbook.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/tests/test_approval_system.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/tests/test_basic.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/tests/test_cli_features.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/tests/test_custom_tools_fix.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/tests/test_double_api_fix.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/tests/test_n8n_integration.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/tests/test_phase2_refactor.py +0 -0
- {praisonai-2.3.74 → praisonai-2.3.76}/tests/test_runner.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PraisonAI
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.76
|
|
4
4
|
Summary: PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration.
|
|
5
5
|
Author: Mervin Praison
|
|
6
6
|
License: MIT
|
|
@@ -11,7 +11,7 @@ Description-Content-Type: text/markdown
|
|
|
11
11
|
Requires-Dist: rich>=13.7
|
|
12
12
|
Requires-Dist: markdown>=3.5
|
|
13
13
|
Requires-Dist: pyparsing>=3.0.0
|
|
14
|
-
Requires-Dist: praisonaiagents>=0.1.
|
|
14
|
+
Requires-Dist: praisonaiagents>=0.1.24
|
|
15
15
|
Requires-Dist: python-dotenv>=0.19.0
|
|
16
16
|
Requires-Dist: instructor>=1.3.3
|
|
17
17
|
Requires-Dist: PyYAML>=6.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PraisonAI
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.76
|
|
4
4
|
Summary: PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration.
|
|
5
5
|
Author: Mervin Praison
|
|
6
6
|
License: MIT
|
|
@@ -11,7 +11,7 @@ Description-Content-Type: text/markdown
|
|
|
11
11
|
Requires-Dist: rich>=13.7
|
|
12
12
|
Requires-Dist: markdown>=3.5
|
|
13
13
|
Requires-Dist: pyparsing>=3.0.0
|
|
14
|
-
Requires-Dist: praisonaiagents>=0.1.
|
|
14
|
+
Requires-Dist: praisonaiagents>=0.1.24
|
|
15
15
|
Requires-Dist: python-dotenv>=0.19.0
|
|
16
16
|
Requires-Dist: instructor>=1.3.3
|
|
17
17
|
Requires-Dist: PyYAML>=6.0
|
|
@@ -47,6 +47,7 @@ praisonai/cli/features/mcp.py
|
|
|
47
47
|
praisonai/cli/features/message_queue.py
|
|
48
48
|
praisonai/cli/features/metrics.py
|
|
49
49
|
praisonai/cli/features/n8n.py
|
|
50
|
+
praisonai/cli/features/profiling.py
|
|
50
51
|
praisonai/cli/features/repo_map.py
|
|
51
52
|
praisonai/cli/features/router.py
|
|
52
53
|
praisonai/cli/features/sandbox_executor.py
|
|
@@ -45,8 +45,10 @@ class AutoMemoryHandler(FlagHandler):
|
|
|
45
45
|
def _get_auto_memory(self, user_id: str = None):
|
|
46
46
|
"""Get AutoMemory instance lazily."""
|
|
47
47
|
try:
|
|
48
|
-
from praisonaiagents.memory import AutoMemory
|
|
49
|
-
|
|
48
|
+
from praisonaiagents.memory import AutoMemory, FileMemory
|
|
49
|
+
# AutoMemory requires a FileMemory instance
|
|
50
|
+
memory = FileMemory(user_id=user_id or "default")
|
|
51
|
+
return AutoMemory(memory=memory)
|
|
50
52
|
except ImportError:
|
|
51
53
|
self.print_status(
|
|
52
54
|
"AutoMemory requires praisonaiagents. Install with: pip install praisonaiagents",
|
|
@@ -71,33 +73,36 @@ class AutoMemoryHandler(FlagHandler):
|
|
|
71
73
|
config['auto_memory_user_id'] = flag_value.get('user_id', 'default')
|
|
72
74
|
return config
|
|
73
75
|
|
|
74
|
-
def extract_memories(self, text: str, user_id: str = None) ->
|
|
76
|
+
def extract_memories(self, text: str, user_id: str = None, user_message: str = None) -> list:
|
|
75
77
|
"""
|
|
76
|
-
Extract memories from text.
|
|
78
|
+
Extract and store memories from text.
|
|
77
79
|
|
|
78
80
|
Args:
|
|
79
|
-
text:
|
|
81
|
+
text: Assistant response text to extract memories from
|
|
80
82
|
user_id: User ID for memory isolation
|
|
83
|
+
user_message: Original user message (for context)
|
|
81
84
|
|
|
82
85
|
Returns:
|
|
83
|
-
|
|
86
|
+
List of extracted memories
|
|
84
87
|
"""
|
|
85
88
|
auto_memory = self._get_auto_memory(user_id)
|
|
86
89
|
if not auto_memory:
|
|
87
|
-
return
|
|
90
|
+
return []
|
|
88
91
|
|
|
89
92
|
try:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
# Use process_interaction which extracts AND stores memories
|
|
94
|
+
memories = auto_memory.process_interaction(
|
|
95
|
+
user_message=user_message or text,
|
|
96
|
+
assistant_response=text if user_message else None,
|
|
97
|
+
store=True # Store the memories
|
|
98
|
+
)
|
|
99
|
+
if memories:
|
|
100
|
+
self.print_status(f"🧠 Extracted and stored {len(memories)} memories", "success")
|
|
101
|
+
return memories
|
|
97
102
|
except Exception as e:
|
|
98
103
|
self.log(f"Memory extraction failed: {e}", "error")
|
|
99
104
|
|
|
100
|
-
return
|
|
105
|
+
return []
|
|
101
106
|
|
|
102
107
|
def store_memory(self, content: str, user_id: str = None, importance: float = 0.5) -> bool:
|
|
103
108
|
"""
|
|
@@ -127,13 +132,14 @@ class AutoMemoryHandler(FlagHandler):
|
|
|
127
132
|
self.log(f"Memory storage failed: {e}", "error")
|
|
128
133
|
return False
|
|
129
134
|
|
|
130
|
-
def post_process_result(self, result: Any, flag_value: Any) -> Any:
|
|
135
|
+
def post_process_result(self, result: Any, flag_value: Any, user_message: str = None) -> Any:
|
|
131
136
|
"""
|
|
132
137
|
Post-process result to extract and store memories.
|
|
133
138
|
|
|
134
139
|
Args:
|
|
135
140
|
result: Agent output
|
|
136
141
|
flag_value: Boolean or dict with configuration
|
|
142
|
+
user_message: Original user message for context
|
|
137
143
|
|
|
138
144
|
Returns:
|
|
139
145
|
Original result (memories are stored)
|
|
@@ -144,15 +150,18 @@ class AutoMemoryHandler(FlagHandler):
|
|
|
144
150
|
user_id = None
|
|
145
151
|
if isinstance(flag_value, dict):
|
|
146
152
|
user_id = flag_value.get('user_id')
|
|
153
|
+
user_message = flag_value.get('user_message', user_message)
|
|
147
154
|
|
|
148
|
-
# Extract memories from result
|
|
155
|
+
# Extract and store memories from result
|
|
149
156
|
text = str(result)
|
|
150
|
-
memories = self.extract_memories(text, user_id)
|
|
157
|
+
memories = self.extract_memories(text, user_id, user_message=user_message)
|
|
151
158
|
|
|
152
159
|
if memories:
|
|
153
160
|
self.print_status("\n🧠 Auto-extracted Memories:", "info")
|
|
154
|
-
for
|
|
155
|
-
|
|
161
|
+
for mem in memories:
|
|
162
|
+
mem_type = mem.get('type', 'unknown')
|
|
163
|
+
content = mem.get('content', str(mem))[:50]
|
|
164
|
+
self.print_status(f" • {mem_type}: {content}...", "info")
|
|
156
165
|
|
|
157
166
|
return result
|
|
158
167
|
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PraisonAI CLI Profiling Commands
|
|
3
|
+
|
|
4
|
+
Provides CLI commands for profiling agent execution.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
praisonai profile run "task" --output report.html
|
|
8
|
+
praisonai profile report --format json
|
|
9
|
+
praisonai profile benchmark --iterations 10
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import time
|
|
13
|
+
import json
|
|
14
|
+
from typing import Optional, Dict, Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ProfilingHandler:
|
|
18
|
+
"""
|
|
19
|
+
Handler for CLI profiling commands.
|
|
20
|
+
|
|
21
|
+
Features:
|
|
22
|
+
- Run tasks with profiling enabled
|
|
23
|
+
- Generate reports (console, JSON, HTML)
|
|
24
|
+
- Benchmark agent performance
|
|
25
|
+
- Export flamegraphs
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self._profiler = None
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def profiler(self):
|
|
33
|
+
"""Lazy load profiler."""
|
|
34
|
+
if self._profiler is None:
|
|
35
|
+
from praisonai.profiler import Profiler
|
|
36
|
+
self._profiler = Profiler
|
|
37
|
+
return self._profiler
|
|
38
|
+
|
|
39
|
+
def enable(self) -> None:
|
|
40
|
+
"""Enable profiling."""
|
|
41
|
+
self.profiler.enable()
|
|
42
|
+
|
|
43
|
+
def disable(self) -> None:
|
|
44
|
+
"""Disable profiling."""
|
|
45
|
+
self.profiler.disable()
|
|
46
|
+
|
|
47
|
+
def clear(self) -> None:
|
|
48
|
+
"""Clear profiling data."""
|
|
49
|
+
self.profiler.clear()
|
|
50
|
+
|
|
51
|
+
def run_with_profiling(self, task: str, config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
52
|
+
"""
|
|
53
|
+
Run a task with profiling enabled.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
task: Task description or prompt
|
|
57
|
+
config: Optional configuration dict
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Dict with result and profiling data
|
|
61
|
+
"""
|
|
62
|
+
self.profiler.enable()
|
|
63
|
+
self.profiler.clear()
|
|
64
|
+
|
|
65
|
+
result = None
|
|
66
|
+
error = None
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
# Import and run PraisonAI
|
|
70
|
+
from praisonai import PraisonAI
|
|
71
|
+
|
|
72
|
+
with self.profiler.block("total_execution"):
|
|
73
|
+
with self.profiler.block("initialization"):
|
|
74
|
+
praison = PraisonAI(auto=task, **(config or {}))
|
|
75
|
+
|
|
76
|
+
with self.profiler.block("agent_run"):
|
|
77
|
+
result = praison.run()
|
|
78
|
+
except Exception as e:
|
|
79
|
+
error = str(e)
|
|
80
|
+
finally:
|
|
81
|
+
self.profiler.disable()
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
'result': result,
|
|
85
|
+
'error': error,
|
|
86
|
+
'summary': self.profiler.get_summary(),
|
|
87
|
+
'statistics': self.profiler.get_statistics()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
def report(self, format: str = "console", output: Optional[str] = None) -> str:
|
|
91
|
+
"""
|
|
92
|
+
Generate profiling report.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
format: Output format (console, json, html)
|
|
96
|
+
output: Optional file path to save report
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Report content as string
|
|
100
|
+
"""
|
|
101
|
+
if format == "console":
|
|
102
|
+
content = self.profiler.report(output="console")
|
|
103
|
+
elif format == "json":
|
|
104
|
+
content = self.profiler.export_json()
|
|
105
|
+
elif format == "html":
|
|
106
|
+
content = self.profiler.export_html()
|
|
107
|
+
else:
|
|
108
|
+
raise ValueError(f"Unknown format: {format}")
|
|
109
|
+
|
|
110
|
+
if output:
|
|
111
|
+
with open(output, 'w') as f:
|
|
112
|
+
f.write(content)
|
|
113
|
+
print(f"Report saved to: {output}")
|
|
114
|
+
|
|
115
|
+
return content
|
|
116
|
+
|
|
117
|
+
def benchmark(self, task: str, iterations: int = 5,
|
|
118
|
+
warmup: int = 1, config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
119
|
+
"""
|
|
120
|
+
Benchmark agent performance.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
task: Task to benchmark
|
|
124
|
+
iterations: Number of iterations
|
|
125
|
+
warmup: Number of warmup runs
|
|
126
|
+
config: Optional configuration
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Benchmark results with statistics
|
|
130
|
+
"""
|
|
131
|
+
from praisonai.profiler import Profiler
|
|
132
|
+
|
|
133
|
+
times = []
|
|
134
|
+
errors = []
|
|
135
|
+
|
|
136
|
+
# Warmup runs
|
|
137
|
+
for i in range(warmup):
|
|
138
|
+
print(f"Warmup {i + 1}/{warmup}...")
|
|
139
|
+
try:
|
|
140
|
+
self.run_with_profiling(task, config)
|
|
141
|
+
except Exception as e:
|
|
142
|
+
print(f"Warmup error: {e}")
|
|
143
|
+
|
|
144
|
+
# Benchmark runs
|
|
145
|
+
for i in range(iterations):
|
|
146
|
+
print(f"Iteration {i + 1}/{iterations}...")
|
|
147
|
+
Profiler.clear()
|
|
148
|
+
|
|
149
|
+
start = time.perf_counter()
|
|
150
|
+
try:
|
|
151
|
+
self.run_with_profiling(task, config)
|
|
152
|
+
duration_ms = (time.perf_counter() - start) * 1000
|
|
153
|
+
times.append(duration_ms)
|
|
154
|
+
except Exception as e:
|
|
155
|
+
errors.append(str(e))
|
|
156
|
+
duration_ms = (time.perf_counter() - start) * 1000
|
|
157
|
+
times.append(duration_ms)
|
|
158
|
+
|
|
159
|
+
# Calculate statistics
|
|
160
|
+
if times:
|
|
161
|
+
sorted_times = sorted(times)
|
|
162
|
+
n = len(sorted_times)
|
|
163
|
+
|
|
164
|
+
results = {
|
|
165
|
+
'iterations': iterations,
|
|
166
|
+
'successful': iterations - len(errors),
|
|
167
|
+
'failed': len(errors),
|
|
168
|
+
'times_ms': times,
|
|
169
|
+
'mean_ms': sum(times) / n,
|
|
170
|
+
'min_ms': min(times),
|
|
171
|
+
'max_ms': max(times),
|
|
172
|
+
'p50_ms': sorted_times[n // 2],
|
|
173
|
+
'p95_ms': sorted_times[int(n * 0.95)] if n > 1 else sorted_times[-1],
|
|
174
|
+
'p99_ms': sorted_times[int(n * 0.99)] if n > 1 else sorted_times[-1],
|
|
175
|
+
'errors': errors
|
|
176
|
+
}
|
|
177
|
+
else:
|
|
178
|
+
results = {
|
|
179
|
+
'iterations': iterations,
|
|
180
|
+
'successful': 0,
|
|
181
|
+
'failed': len(errors),
|
|
182
|
+
'errors': errors
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return results
|
|
186
|
+
|
|
187
|
+
def export_flamegraph(self, filepath: str) -> None:
|
|
188
|
+
"""Export flamegraph to SVG file."""
|
|
189
|
+
self.profiler.export_flamegraph(filepath)
|
|
190
|
+
print(f"Flamegraph saved to: {filepath}")
|
|
191
|
+
|
|
192
|
+
def get_summary(self) -> Dict[str, Any]:
|
|
193
|
+
"""Get profiling summary."""
|
|
194
|
+
return self.profiler.get_summary()
|
|
195
|
+
|
|
196
|
+
def get_statistics(self, category: Optional[str] = None) -> Dict[str, float]:
|
|
197
|
+
"""Get profiling statistics."""
|
|
198
|
+
return self.profiler.get_statistics(category)
|
|
199
|
+
|
|
200
|
+
def print_summary(self) -> None:
|
|
201
|
+
"""Print profiling summary to console."""
|
|
202
|
+
summary = self.get_summary()
|
|
203
|
+
stats = self.get_statistics()
|
|
204
|
+
|
|
205
|
+
print("\n" + "=" * 60)
|
|
206
|
+
print("PROFILING SUMMARY")
|
|
207
|
+
print("=" * 60)
|
|
208
|
+
|
|
209
|
+
print(f"\nTotal Time: {summary['total_time_ms']:.2f}ms")
|
|
210
|
+
print(f"Operations: {summary['timing_count']}")
|
|
211
|
+
print(f"Imports: {summary['import_count']}")
|
|
212
|
+
print(f"Flow Steps: {summary['flow_steps']}")
|
|
213
|
+
|
|
214
|
+
print("\nStatistics:")
|
|
215
|
+
print(f" P50 (Median): {stats['p50']:.2f}ms")
|
|
216
|
+
print(f" P95: {stats['p95']:.2f}ms")
|
|
217
|
+
print(f" P99: {stats['p99']:.2f}ms")
|
|
218
|
+
print(f" Mean: {stats['mean']:.2f}ms")
|
|
219
|
+
print(f" Std Dev: {stats['std_dev']:.2f}ms")
|
|
220
|
+
|
|
221
|
+
print("\nSlowest Operations:")
|
|
222
|
+
for name, duration in summary['slowest_operations'][:5]:
|
|
223
|
+
print(f" {name}: {duration:.2f}ms")
|
|
224
|
+
|
|
225
|
+
print("=" * 60)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def handle_profile_command(args) -> int:
|
|
229
|
+
"""
|
|
230
|
+
Handle profile CLI command.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
args: Parsed command line arguments
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
Exit code (0 for success)
|
|
237
|
+
"""
|
|
238
|
+
handler = ProfilingHandler()
|
|
239
|
+
|
|
240
|
+
subcommand = getattr(args, 'profile_command', None)
|
|
241
|
+
|
|
242
|
+
if subcommand == 'run':
|
|
243
|
+
task = getattr(args, 'task', '')
|
|
244
|
+
output = getattr(args, 'output', None)
|
|
245
|
+
format_type = getattr(args, 'format', 'console')
|
|
246
|
+
|
|
247
|
+
print(f"Running with profiling: {task[:50]}...")
|
|
248
|
+
result = handler.run_with_profiling(task)
|
|
249
|
+
|
|
250
|
+
if result['error']:
|
|
251
|
+
print(f"Error: {result['error']}")
|
|
252
|
+
|
|
253
|
+
handler.report(format=format_type, output=output)
|
|
254
|
+
return 0 if not result['error'] else 1
|
|
255
|
+
|
|
256
|
+
elif subcommand == 'report':
|
|
257
|
+
output = getattr(args, 'output', None)
|
|
258
|
+
format_type = getattr(args, 'format', 'console')
|
|
259
|
+
|
|
260
|
+
handler.report(format=format_type, output=output)
|
|
261
|
+
return 0
|
|
262
|
+
|
|
263
|
+
elif subcommand == 'benchmark':
|
|
264
|
+
task = getattr(args, 'task', '')
|
|
265
|
+
iterations = getattr(args, 'iterations', 5)
|
|
266
|
+
warmup = getattr(args, 'warmup', 1)
|
|
267
|
+
output = getattr(args, 'output', None)
|
|
268
|
+
|
|
269
|
+
print(f"Benchmarking: {task[:50]}...")
|
|
270
|
+
results = handler.benchmark(task, iterations=iterations, warmup=warmup)
|
|
271
|
+
|
|
272
|
+
print("\n" + "=" * 60)
|
|
273
|
+
print("BENCHMARK RESULTS")
|
|
274
|
+
print("=" * 60)
|
|
275
|
+
print(f"Iterations: {results['iterations']}")
|
|
276
|
+
print(f"Successful: {results['successful']}")
|
|
277
|
+
print(f"Failed: {results['failed']}")
|
|
278
|
+
|
|
279
|
+
if 'mean_ms' in results:
|
|
280
|
+
print("\nTiming:")
|
|
281
|
+
print(f" Mean: {results['mean_ms']:.2f}ms")
|
|
282
|
+
print(f" Min: {results['min_ms']:.2f}ms")
|
|
283
|
+
print(f" Max: {results['max_ms']:.2f}ms")
|
|
284
|
+
print(f" P50: {results['p50_ms']:.2f}ms")
|
|
285
|
+
print(f" P95: {results['p95_ms']:.2f}ms")
|
|
286
|
+
|
|
287
|
+
if output:
|
|
288
|
+
with open(output, 'w') as f:
|
|
289
|
+
json.dump(results, f, indent=2)
|
|
290
|
+
print(f"\nResults saved to: {output}")
|
|
291
|
+
|
|
292
|
+
return 0
|
|
293
|
+
|
|
294
|
+
elif subcommand == 'flamegraph':
|
|
295
|
+
output = getattr(args, 'output', 'profile.svg')
|
|
296
|
+
handler.export_flamegraph(output)
|
|
297
|
+
return 0
|
|
298
|
+
|
|
299
|
+
elif subcommand == 'summary':
|
|
300
|
+
handler.print_summary()
|
|
301
|
+
return 0
|
|
302
|
+
|
|
303
|
+
else:
|
|
304
|
+
print("Usage: praisonai profile <run|report|benchmark|flamegraph|summary>")
|
|
305
|
+
return 1
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def add_profile_parser(subparsers) -> None:
|
|
309
|
+
"""Add profile subcommand to argument parser."""
|
|
310
|
+
profile_parser = subparsers.add_parser(
|
|
311
|
+
'profile',
|
|
312
|
+
help='Profile agent execution'
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
profile_subparsers = profile_parser.add_subparsers(dest='profile_command')
|
|
316
|
+
|
|
317
|
+
# Run with profiling
|
|
318
|
+
run_parser = profile_subparsers.add_parser('run', help='Run task with profiling')
|
|
319
|
+
run_parser.add_argument('task', help='Task to run')
|
|
320
|
+
run_parser.add_argument('--output', '-o', help='Output file path')
|
|
321
|
+
run_parser.add_argument('--format', '-f', choices=['console', 'json', 'html'],
|
|
322
|
+
default='console', help='Output format')
|
|
323
|
+
|
|
324
|
+
# Generate report
|
|
325
|
+
report_parser = profile_subparsers.add_parser('report', help='Generate profiling report')
|
|
326
|
+
report_parser.add_argument('--output', '-o', help='Output file path')
|
|
327
|
+
report_parser.add_argument('--format', '-f', choices=['console', 'json', 'html'],
|
|
328
|
+
default='console', help='Output format')
|
|
329
|
+
|
|
330
|
+
# Benchmark
|
|
331
|
+
bench_parser = profile_subparsers.add_parser('benchmark', help='Benchmark agent performance')
|
|
332
|
+
bench_parser.add_argument('task', help='Task to benchmark')
|
|
333
|
+
bench_parser.add_argument('--iterations', '-n', type=int, default=5, help='Number of iterations')
|
|
334
|
+
bench_parser.add_argument('--warmup', '-w', type=int, default=1, help='Warmup runs')
|
|
335
|
+
bench_parser.add_argument('--output', '-o', help='Output file for results')
|
|
336
|
+
|
|
337
|
+
# Flamegraph
|
|
338
|
+
flame_parser = profile_subparsers.add_parser('flamegraph', help='Export flamegraph')
|
|
339
|
+
flame_parser.add_argument('--output', '-o', default='profile.svg', help='Output SVG file')
|
|
340
|
+
|
|
341
|
+
# Summary
|
|
342
|
+
profile_subparsers.add_parser('summary', help='Print profiling summary') # noqa: F841
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
# Exports
|
|
346
|
+
__all__ = [
|
|
347
|
+
'ProfilingHandler',
|
|
348
|
+
'handle_profile_command',
|
|
349
|
+
'add_profile_parser',
|
|
350
|
+
]
|
|
@@ -5,6 +5,42 @@ import argparse
|
|
|
5
5
|
import warnings
|
|
6
6
|
import os
|
|
7
7
|
|
|
8
|
+
# Suppress Pydantic serialization warnings from LiteLLM BEFORE any imports
|
|
9
|
+
# These warnings occur when LiteLLM's response objects have field mismatches
|
|
10
|
+
# Using both filterwarnings AND patching warnings.warn for complete suppression
|
|
11
|
+
|
|
12
|
+
warnings.filterwarnings("ignore", message=".*Pydantic serializer warnings.*")
|
|
13
|
+
warnings.filterwarnings("ignore", message=".*PydanticSerializationUnexpectedValue.*")
|
|
14
|
+
warnings.filterwarnings("ignore", message=".*Expected \\d+ fields but got.*")
|
|
15
|
+
warnings.filterwarnings("ignore", message=".*Expected `StreamingChoices`.*")
|
|
16
|
+
warnings.filterwarnings("ignore", message=".*Expected `Message`.*")
|
|
17
|
+
warnings.filterwarnings("ignore", message=".*serialized value may not be as expected.*")
|
|
18
|
+
warnings.filterwarnings("ignore", category=UserWarning, module="pydantic.*")
|
|
19
|
+
|
|
20
|
+
# Patch warnings.showwarning to intercept ALL warnings including those from crewai's patched warn
|
|
21
|
+
# This is the final output function that actually displays warnings
|
|
22
|
+
_SUPPRESSED_PATTERNS = [
|
|
23
|
+
"Pydantic serializer warnings",
|
|
24
|
+
"PydanticSerializationUnexpectedValue",
|
|
25
|
+
"Expected", # Catches "Expected N fields but got M"
|
|
26
|
+
"StreamingChoices",
|
|
27
|
+
"serialized value may not be as expected",
|
|
28
|
+
"duckduckgo_search", # Suppress duckduckgo rename warning
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
_original_showwarning = warnings.showwarning
|
|
32
|
+
|
|
33
|
+
def _patched_showwarning(message, category, filename, lineno, file=None, line=None):
|
|
34
|
+
msg_str = str(message)
|
|
35
|
+
for pattern in _SUPPRESSED_PATTERNS:
|
|
36
|
+
if pattern in msg_str:
|
|
37
|
+
return
|
|
38
|
+
if category is UserWarning and "pydantic" in filename.lower():
|
|
39
|
+
return
|
|
40
|
+
_original_showwarning(message, category, filename, lineno, file, line)
|
|
41
|
+
|
|
42
|
+
warnings.showwarning = _patched_showwarning
|
|
43
|
+
|
|
8
44
|
# Suppress crewai RuntimeWarning about module loading order (only in non-debug mode)
|
|
9
45
|
# This warning is harmless and occurs when running as `python -m praisonai.cli.main`
|
|
10
46
|
if os.environ.get('LOGLEVEL', 'INFO').upper() != 'DEBUG':
|
|
@@ -1342,12 +1378,47 @@ class PraisonAI:
|
|
|
1342
1378
|
|
|
1343
1379
|
console.print(table)
|
|
1344
1380
|
|
|
1345
|
-
# Show recent memories
|
|
1381
|
+
# Show recent short-term memories
|
|
1346
1382
|
print("\n[bold]Recent Short-term Memories:[/bold]")
|
|
1347
1383
|
short_term = memory.get_short_term(limit=5)
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1384
|
+
if short_term:
|
|
1385
|
+
for i, item in enumerate(short_term, 1):
|
|
1386
|
+
content = item.get('content', str(item))[:100]
|
|
1387
|
+
print(f" {i}. {content}")
|
|
1388
|
+
else:
|
|
1389
|
+
print(" [dim]No short-term memories[/dim]")
|
|
1390
|
+
|
|
1391
|
+
# Show long-term memories
|
|
1392
|
+
print("\n[bold]Long-term Memories:[/bold]")
|
|
1393
|
+
long_term = memory.get_long_term(limit=10)
|
|
1394
|
+
if long_term:
|
|
1395
|
+
for i, item in enumerate(long_term, 1):
|
|
1396
|
+
# Handle both dict and MemoryItem objects
|
|
1397
|
+
if hasattr(item, 'content'):
|
|
1398
|
+
content = str(item.content)[:100]
|
|
1399
|
+
importance = getattr(item, 'importance', 0)
|
|
1400
|
+
else:
|
|
1401
|
+
content = item.get('content', str(item))[:100]
|
|
1402
|
+
importance = item.get('importance', 0)
|
|
1403
|
+
print(f" {i}. [{importance:.1f}] {content}")
|
|
1404
|
+
else:
|
|
1405
|
+
print(" [dim]No long-term memories[/dim]")
|
|
1406
|
+
|
|
1407
|
+
# Show entities
|
|
1408
|
+
print("\n[bold]Entities:[/bold]")
|
|
1409
|
+
entities = memory.get_all_entities()
|
|
1410
|
+
if entities:
|
|
1411
|
+
for entity in entities[:10]:
|
|
1412
|
+
# Handle both dict and Entity objects
|
|
1413
|
+
if hasattr(entity, 'name'):
|
|
1414
|
+
name = entity.name
|
|
1415
|
+
entity_type = getattr(entity, 'entity_type', 'unknown')
|
|
1416
|
+
else:
|
|
1417
|
+
name = entity.get('name', 'Unknown')
|
|
1418
|
+
entity_type = entity.get('entity_type', 'unknown')
|
|
1419
|
+
print(f" • {name} ({entity_type})")
|
|
1420
|
+
else:
|
|
1421
|
+
print(" [dim]No entities[/dim]")
|
|
1351
1422
|
|
|
1352
1423
|
elif action == 'add':
|
|
1353
1424
|
if not action_args:
|
|
@@ -3118,9 +3189,8 @@ Provide ONLY the commit message, no explanations."""
|
|
|
3118
3189
|
telemetry = TelemetryHandler(verbose=getattr(self.args, 'verbose', False))
|
|
3119
3190
|
telemetry.enable()
|
|
3120
3191
|
|
|
3121
|
-
# Auto Memory - Automatic memory extraction
|
|
3192
|
+
# Auto Memory - Automatic memory extraction (handled post-processing, not as Agent param)
|
|
3122
3193
|
if getattr(self.args, 'auto_memory', False):
|
|
3123
|
-
agent_config["auto_memory"] = True
|
|
3124
3194
|
print("[bold cyan]Auto Memory enabled - will extract and store memories[/bold cyan]")
|
|
3125
3195
|
|
|
3126
3196
|
# MCP - Model Context Protocol tools
|
|
@@ -3266,7 +3336,10 @@ Provide ONLY the commit message, no explanations."""
|
|
|
3266
3336
|
if hasattr(self, 'args') and getattr(self.args, 'auto_memory', False):
|
|
3267
3337
|
from .features.auto_memory import AutoMemoryHandler
|
|
3268
3338
|
auto_mem = AutoMemoryHandler(verbose=getattr(self.args, 'verbose', False))
|
|
3269
|
-
auto_mem.post_process_result(
|
|
3339
|
+
auto_mem.post_process_result(
|
|
3340
|
+
result,
|
|
3341
|
+
{'user_id': getattr(self.args, 'user_id', None), 'user_message': prompt}
|
|
3342
|
+
)
|
|
3270
3343
|
|
|
3271
3344
|
# Todo - Generate todo list from response
|
|
3272
3345
|
if hasattr(self, 'args') and getattr(self.args, 'todo', False):
|
|
@@ -57,7 +57,7 @@ class CloudDeployer:
|
|
|
57
57
|
file.write("FROM python:3.11-slim\n")
|
|
58
58
|
file.write("WORKDIR /app\n")
|
|
59
59
|
file.write("COPY . .\n")
|
|
60
|
-
file.write("RUN pip install flask praisonai==2.3.
|
|
60
|
+
file.write("RUN pip install flask praisonai==2.3.76 gunicorn markdown\n")
|
|
61
61
|
file.write("EXPOSE 8080\n")
|
|
62
62
|
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
|
63
63
|
|