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.
- praisonaiagents/__init__.py +12 -1
- praisonaiagents/flow_display.py +194 -0
- praisonaiagents/llm/llm.py +8 -0
- {praisonaiagents-0.0.150.dist-info → praisonaiagents-0.0.152.dist-info}/METADATA +1 -1
- {praisonaiagents-0.0.150.dist-info → praisonaiagents-0.0.152.dist-info}/RECORD +7 -6
- {praisonaiagents-0.0.150.dist-info → praisonaiagents-0.0.152.dist-info}/WHEEL +0 -0
- {praisonaiagents-0.0.150.dist-info → praisonaiagents-0.0.152.dist-info}/top_level.txt +0 -0
praisonaiagents/__init__.py
CHANGED
@@ -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()
|
praisonaiagents/llm/llm.py
CHANGED
@@ -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,7 +1,8 @@
|
|
1
|
-
praisonaiagents/__init__.py,sha256=
|
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=
|
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.
|
68
|
-
praisonaiagents-0.0.
|
69
|
-
praisonaiagents-0.0.
|
70
|
-
praisonaiagents-0.0.
|
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,,
|
File without changes
|
File without changes
|