praisonaiagents 0.0.150__py3-none-any.whl → 0.0.152__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.
@@ -32,6 +32,12 @@ from .session import Session
32
32
  from .memory.memory import Memory
33
33
  from .guardrails import GuardrailResult, LLMGuardrail
34
34
  from .agent.handoff import Handoff, handoff, handoff_filters, RECOMMENDED_PROMPT_PREFIX, prompt_with_handoff_instructions
35
+ # Flow display
36
+ try:
37
+ from .flow_display import FlowDisplay, track_workflow
38
+ except ImportError:
39
+ FlowDisplay = None
40
+ track_workflow = None
35
41
  from .main import (
36
42
  TaskOutput,
37
43
  ReflectionOutput,
@@ -135,4 +141,9 @@ __all__ = [
135
141
 
136
142
  # Add MCP to __all__ if available
137
143
  if _mcp_available:
138
- __all__.append('MCP')
144
+ __all__.append('MCP')
145
+
146
+ # Add flow display if available
147
+ if FlowDisplay is not None:
148
+ __all__.extend(['FlowDisplay', 'track_workflow'])
149
+
@@ -0,0 +1,194 @@
1
+ """
2
+ Flow Display for PraisonAI Agents
3
+
4
+ Visual display with agents in center and tools on sides.
5
+ """
6
+
7
+ from typing import Dict, List, Set, Tuple
8
+ from collections import defaultdict
9
+ import threading
10
+ from rich.console import Console
11
+ from rich.panel import Panel
12
+ from rich.text import Text
13
+ from rich.align import Align
14
+ from rich.columns import Columns
15
+ from rich.table import Table
16
+ from rich import box
17
+
18
+ class FlowDisplay:
19
+ """Displays agent workflow with agents centered and tools on sides."""
20
+
21
+ def __init__(self):
22
+ self.console = Console()
23
+ self.agents = [] # List of agents in order
24
+ self.agent_tools = defaultdict(list) # agent -> [tools]
25
+ self.tracking = False
26
+ self.lock = threading.Lock()
27
+
28
+ def start(self):
29
+ """Start tracking workflow."""
30
+ self.tracking = True
31
+ self.agents.clear()
32
+ self.agent_tools.clear()
33
+
34
+ # Register callbacks
35
+ try:
36
+ from praisonaiagents.main import register_display_callback
37
+
38
+ def on_interaction(**kwargs):
39
+ if self.tracking:
40
+ agent = kwargs.get('agent_name', 'Unknown')
41
+ self._add_agent(agent)
42
+
43
+ def on_tool_call(message, **kwargs):
44
+ if self.tracking and "called function" in message:
45
+ parts = message.split("'")
46
+ if len(parts) > 1:
47
+ tool_name = parts[1]
48
+ agent = kwargs.get('agent_name', 'Unknown')
49
+ self._add_tool(agent, tool_name)
50
+
51
+ register_display_callback('interaction', on_interaction)
52
+ register_display_callback('tool_call', on_tool_call)
53
+
54
+ except ImportError:
55
+ pass
56
+
57
+ def stop(self):
58
+ """Stop tracking and display the flow."""
59
+ self.tracking = False
60
+ self.display()
61
+
62
+ def _add_agent(self, name: str):
63
+ """Add an agent if not already present."""
64
+ with self.lock:
65
+ if name not in self.agents:
66
+ self.agents.append(name)
67
+
68
+ def _add_tool(self, agent_name: str, tool_name: str):
69
+ """Add a tool to an agent."""
70
+ with self.lock:
71
+ if agent_name not in self.agents:
72
+ self.agents.append(agent_name)
73
+ if tool_name not in self.agent_tools[agent_name]:
74
+ self.agent_tools[agent_name].append(tool_name)
75
+
76
+ def display(self):
77
+ """Display the flow chart with agents in center and tools on sides."""
78
+ if not self.agents:
79
+ return
80
+
81
+ self.console.print("\n[bold cyan]🔄 Agent Workflow Flow[/bold cyan]\n")
82
+
83
+ # Display start
84
+ self._display_centered_node("── start ──", "grey35")
85
+ self._display_arrow_down()
86
+
87
+ # Display each agent with their tools
88
+ for i, agent in enumerate(self.agents):
89
+ self._display_agent_with_tools(agent)
90
+
91
+ # Add arrow to next agent or end
92
+ if i < len(self.agents) - 1:
93
+ self._display_arrow_down()
94
+
95
+ # Display end
96
+ self._display_arrow_down()
97
+ self._display_centered_node("── end ──", "grey35")
98
+
99
+ def _display_agent_with_tools(self, agent: str):
100
+ """Display agent with tools on the sides."""
101
+ tools = self.agent_tools.get(agent, [])
102
+
103
+ if not tools:
104
+ # No tools - just agent
105
+ self._display_centered_node(agent, "purple")
106
+ else:
107
+ # Split tools between left and right
108
+ left_tools = tools[::2] # Even indices
109
+ right_tools = tools[1::2] # Odd indices
110
+
111
+ # Create the layout
112
+ table = Table(show_header=False, show_edge=False, box=None, padding=0)
113
+ table.add_column(justify="center", min_width=20) # Left tools
114
+ table.add_column(justify="center", min_width=5) # Space
115
+ table.add_column(justify="center", min_width=20) # Agent
116
+ table.add_column(justify="center", min_width=5) # Space
117
+ table.add_column(justify="center", min_width=20) # Right tools
118
+
119
+ # Create panels
120
+ left_panel = self._create_tools_panel(left_tools) if left_tools else ""
121
+ agent_panel = Panel(
122
+ Text(agent, style="white on purple", justify="center"),
123
+ style="white on purple",
124
+ box=box.ROUNDED,
125
+ padding=(0, 2)
126
+ )
127
+ right_panel = self._create_tools_panel(right_tools) if right_tools else ""
128
+
129
+ # Add row
130
+ table.add_row(left_panel, "", agent_panel, "", right_panel)
131
+
132
+ # Display centered
133
+ self.console.print(Align.center(table))
134
+
135
+ # Show arrows
136
+ if left_tools or right_tools:
137
+ arrow_parts = []
138
+ if left_tools:
139
+ arrow_parts.append("←→")
140
+ else:
141
+ arrow_parts.append(" ")
142
+
143
+ arrow_parts.append(" ") # Center space
144
+
145
+ if right_tools:
146
+ arrow_parts.append("←→")
147
+ else:
148
+ arrow_parts.append(" ")
149
+
150
+ self.console.print(Align.center(Text("".join(arrow_parts))))
151
+
152
+ def _create_tools_panel(self, tools: List[str]) -> Panel:
153
+ """Create a panel for tools."""
154
+ if not tools:
155
+ return ""
156
+
157
+ if len(tools) == 1:
158
+ return Panel(
159
+ Text(tools[0], style="black on yellow", justify="center"),
160
+ style="black on yellow",
161
+ box=box.ROUNDED,
162
+ padding=(0, 1)
163
+ )
164
+ else:
165
+ # Multiple tools
166
+ content = "\n".join(tools)
167
+ return Panel(
168
+ Text(content, style="black on yellow", justify="center"),
169
+ style="black on yellow",
170
+ box=box.ROUNDED,
171
+ padding=(0, 1)
172
+ )
173
+
174
+ def _display_centered_node(self, label: str, color: str):
175
+ """Display a centered node."""
176
+ panel = Panel(
177
+ Text(label, style=f"white on {color}", justify="center"),
178
+ style=f"white on {color}",
179
+ box=box.ROUNDED,
180
+ padding=(0, 2)
181
+ )
182
+ self.console.print(Align.center(panel))
183
+
184
+ def _display_arrow_down(self):
185
+ """Display a downward arrow."""
186
+ self.console.print()
187
+ self.console.print(Align.center("↓"))
188
+ self.console.print()
189
+
190
+
191
+ # Simple function to create and use
192
+ def track_workflow():
193
+ """Create a flow display tracker."""
194
+ return FlowDisplay()
@@ -1456,6 +1456,8 @@ class LLM:
1456
1456
  )
1457
1457
  if should_break:
1458
1458
  final_response_text = tool_summary_text
1459
+ # Reset interaction_displayed to ensure final summary is shown
1460
+ interaction_displayed = False
1459
1461
  break
1460
1462
  elif tool_summary_text is None and iteration_count > self.OLLAMA_SUMMARY_ITERATION_THRESHOLD:
1461
1463
  # Continue iteration after adding final answer prompt
@@ -1485,6 +1487,8 @@ class LLM:
1485
1487
  tool_summary = self._generate_ollama_tool_summary(accumulated_tool_results, response_text)
1486
1488
  if tool_summary:
1487
1489
  final_response_text = tool_summary
1490
+ # Reset interaction_displayed to ensure final summary is shown
1491
+ interaction_displayed = False
1488
1492
  break
1489
1493
 
1490
1494
  # If we've executed tools in previous iterations, this response contains the final answer
@@ -2567,6 +2571,8 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
2567
2571
  )
2568
2572
  if should_break:
2569
2573
  final_response_text = tool_summary_text
2574
+ # Reset interaction_displayed to ensure final summary is shown
2575
+ interaction_displayed = False
2570
2576
  break
2571
2577
  elif tool_summary_text is None and iteration_count > self.OLLAMA_SUMMARY_ITERATION_THRESHOLD:
2572
2578
  # Continue iteration after adding final answer prompt
@@ -2594,6 +2600,8 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
2594
2600
  tool_summary = self._generate_ollama_tool_summary(accumulated_tool_results, response_text)
2595
2601
  if tool_summary:
2596
2602
  final_response_text = tool_summary
2603
+ # Reset interaction_displayed to ensure final summary is shown
2604
+ interaction_displayed = False
2597
2605
  break
2598
2606
 
2599
2607
  # If we've executed tools in previous iterations, this response contains the final answer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praisonaiagents
3
- Version: 0.0.150
3
+ Version: 0.0.152
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,8 @@
1
- praisonaiagents/__init__.py,sha256=pmoUafSn5f-ubjgY0rBBTw7N1wsRtg7X8-UGe477OH4,3619
1
+ praisonaiagents/__init__.py,sha256=RJfNHR2k00o_U8W_YJb05uwemrvQ_7SmyNhjp__G29E,3887
2
2
  praisonaiagents/_logging.py,sha256=WfgUX6jo9hClpgHVKSGz8gqkna9DDNhPJBv-wjhcJoM,4648
3
3
  praisonaiagents/_warning_patch.py,sha256=FSLdw1SnA9b1PSxHWaRIcuG9IiIwO5JT6uo_m3CM0NI,2816
4
4
  praisonaiagents/approval.py,sha256=UJ4OhfihpFGR5CAaMphqpSvqdZCHi5w2MGw1MByZ1FQ,9813
5
+ praisonaiagents/flow_display.py,sha256=E84J_H3h8L-AqL_F1JzEUInQYdjmIEuNL1LZr4__Hes,6935
5
6
  praisonaiagents/main.py,sha256=BqqskwUP-qrqCsHF9h_Wv99uS24ZeFTD8CaN6o6bnCE,17150
6
7
  praisonaiagents/session.py,sha256=FHWButPBaFGA4x1U_2gImroQChHnFy231_aAa_n5KOQ,20364
7
8
  praisonaiagents/agent/__init__.py,sha256=KBqW_augD-HcaV3FL88gUmhDCpwnSTavGENi7RqneTo,505
@@ -20,7 +21,7 @@ praisonaiagents/knowledge/__init__.py,sha256=xL1Eh-a3xsHyIcU4foOWF-JdWYIYBALJH9b
20
21
  praisonaiagents/knowledge/chunking.py,sha256=G6wyHa7_8V0_7VpnrrUXbEmUmptlT16ISJYaxmkSgmU,7678
21
22
  praisonaiagents/knowledge/knowledge.py,sha256=OzK81oA6sjk9nAUWphS7AkXxvalrv2AHB4FtHjzYgxI,30115
22
23
  praisonaiagents/llm/__init__.py,sha256=SqdU1pRqPrR6jZeWYyDeTvmZKCACywk0v4P0k5Fuowk,1107
23
- praisonaiagents/llm/llm.py,sha256=UQmnKyPQSnaxCIlUwJmHC4vjQfFvlZIg2w883qQTkTM,172825
24
+ praisonaiagents/llm/llm.py,sha256=ChQQoBkfVoVaPg13qZ9S3rM0rhrQJjVMCuJv4irvTVY,173421
24
25
  praisonaiagents/llm/model_capabilities.py,sha256=cxOvZcjZ_PIEpUYKn3S2FMyypfOSfbGpx4vmV7Y5vhI,3967
25
26
  praisonaiagents/llm/model_router.py,sha256=Jy2pShlkLxqXF3quz-MRB3-6L9vaUSgUrf2YJs_Tsg0,13995
26
27
  praisonaiagents/llm/openai_client.py,sha256=3EVjIs3tnBNFDy_4ZxX9DJVq54kS0FMm38m5Gkpun7U,57234
@@ -64,7 +65,7 @@ praisonaiagents/tools/xml_tools.py,sha256=iYTMBEk5l3L3ryQ1fkUnNVYK-Nnua2Kx2S0dxN
64
65
  praisonaiagents/tools/yaml_tools.py,sha256=uogAZrhXV9O7xvspAtcTfpKSQYL2nlOTvCQXN94-G9A,14215
65
66
  praisonaiagents/tools/yfinance_tools.py,sha256=s2PBj_1v7oQnOobo2fDbQBACEHl61ftG4beG6Z979ZE,8529
66
67
  praisonaiagents/tools/train/data/generatecot.py,sha256=H6bNh-E2hqL5MW6kX3hqZ05g9ETKN2-kudSjiuU_SD8,19403
67
- praisonaiagents-0.0.150.dist-info/METADATA,sha256=5JM0pL2Ubiq3OFuq2JU268cFwP6ic553UU1LWVMUBho,2146
68
- praisonaiagents-0.0.150.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
69
- praisonaiagents-0.0.150.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
70
- praisonaiagents-0.0.150.dist-info/RECORD,,
68
+ praisonaiagents-0.0.152.dist-info/METADATA,sha256=a-HyisF8Pw70UqkSloPXYxFj9p1e4pe1BITREE9xlkU,2146
69
+ praisonaiagents-0.0.152.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
70
+ praisonaiagents-0.0.152.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
71
+ praisonaiagents-0.0.152.dist-info/RECORD,,