jaf-py 2.5.10__py3-none-any.whl → 2.5.11__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.
- jaf/__init__.py +154 -57
- jaf/a2a/__init__.py +42 -21
- jaf/a2a/agent.py +79 -126
- jaf/a2a/agent_card.py +87 -78
- jaf/a2a/client.py +30 -66
- jaf/a2a/examples/client_example.py +12 -12
- jaf/a2a/examples/integration_example.py +38 -47
- jaf/a2a/examples/server_example.py +56 -53
- jaf/a2a/memory/__init__.py +0 -4
- jaf/a2a/memory/cleanup.py +28 -21
- jaf/a2a/memory/factory.py +155 -133
- jaf/a2a/memory/providers/composite.py +21 -26
- jaf/a2a/memory/providers/in_memory.py +89 -83
- jaf/a2a/memory/providers/postgres.py +117 -115
- jaf/a2a/memory/providers/redis.py +128 -121
- jaf/a2a/memory/serialization.py +77 -87
- jaf/a2a/memory/tests/run_comprehensive_tests.py +112 -83
- jaf/a2a/memory/tests/test_cleanup.py +211 -94
- jaf/a2a/memory/tests/test_serialization.py +73 -68
- jaf/a2a/memory/tests/test_stress_concurrency.py +186 -133
- jaf/a2a/memory/tests/test_task_lifecycle.py +138 -120
- jaf/a2a/memory/types.py +91 -53
- jaf/a2a/protocol.py +95 -125
- jaf/a2a/server.py +90 -118
- jaf/a2a/standalone_client.py +30 -43
- jaf/a2a/tests/__init__.py +16 -33
- jaf/a2a/tests/run_tests.py +17 -53
- jaf/a2a/tests/test_agent.py +40 -140
- jaf/a2a/tests/test_client.py +54 -117
- jaf/a2a/tests/test_integration.py +28 -82
- jaf/a2a/tests/test_protocol.py +54 -139
- jaf/a2a/tests/test_types.py +50 -136
- jaf/a2a/types.py +58 -34
- jaf/cli.py +21 -41
- jaf/core/__init__.py +7 -1
- jaf/core/agent_tool.py +93 -72
- jaf/core/analytics.py +257 -207
- jaf/core/checkpoint.py +223 -0
- jaf/core/composition.py +249 -235
- jaf/core/engine.py +817 -519
- jaf/core/errors.py +55 -42
- jaf/core/guardrails.py +276 -202
- jaf/core/handoff.py +47 -31
- jaf/core/parallel_agents.py +69 -75
- jaf/core/performance.py +75 -73
- jaf/core/proxy.py +43 -44
- jaf/core/proxy_helpers.py +24 -27
- jaf/core/regeneration.py +220 -129
- jaf/core/state.py +68 -66
- jaf/core/streaming.py +115 -108
- jaf/core/tool_results.py +111 -101
- jaf/core/tools.py +114 -116
- jaf/core/tracing.py +269 -210
- jaf/core/types.py +371 -151
- jaf/core/workflows.py +209 -168
- jaf/exceptions.py +46 -38
- jaf/memory/__init__.py +1 -6
- jaf/memory/approval_storage.py +54 -77
- jaf/memory/factory.py +4 -4
- jaf/memory/providers/in_memory.py +216 -180
- jaf/memory/providers/postgres.py +216 -146
- jaf/memory/providers/redis.py +173 -116
- jaf/memory/types.py +70 -51
- jaf/memory/utils.py +36 -34
- jaf/plugins/__init__.py +12 -12
- jaf/plugins/base.py +105 -96
- jaf/policies/__init__.py +0 -1
- jaf/policies/handoff.py +37 -46
- jaf/policies/validation.py +76 -52
- jaf/providers/__init__.py +6 -3
- jaf/providers/mcp.py +97 -51
- jaf/providers/model.py +360 -279
- jaf/server/__init__.py +1 -1
- jaf/server/main.py +7 -11
- jaf/server/server.py +514 -359
- jaf/server/types.py +208 -52
- jaf/utils/__init__.py +17 -18
- jaf/utils/attachments.py +111 -116
- jaf/utils/document_processor.py +175 -174
- jaf/visualization/__init__.py +1 -1
- jaf/visualization/example.py +111 -110
- jaf/visualization/functional_core.py +46 -71
- jaf/visualization/graphviz.py +154 -189
- jaf/visualization/imperative_shell.py +7 -16
- jaf/visualization/types.py +8 -4
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/METADATA +2 -2
- jaf_py-2.5.11.dist-info/RECORD +97 -0
- jaf_py-2.5.10.dist-info/RECORD +0 -96
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/WHEEL +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/entry_points.txt +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/licenses/LICENSE +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/top_level.txt +0 -0
jaf/visualization/graphviz.py
CHANGED
|
@@ -15,118 +15,97 @@ from ..core.types import Agent, Tool
|
|
|
15
15
|
from .types import GraphOptions, GraphResult
|
|
16
16
|
|
|
17
17
|
COLOR_SCHEMES: Final[Dict[str, Dict[str, Any]]] = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
"default": {
|
|
19
|
+
"agent": {
|
|
20
|
+
"shape": "box",
|
|
21
|
+
"fillcolor": "#E3F2FD",
|
|
22
|
+
"fontcolor": "#1976D2",
|
|
23
|
+
"style": "filled,rounded",
|
|
24
24
|
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
"tool": {
|
|
26
|
+
"shape": "ellipse",
|
|
27
|
+
"fillcolor": "#F3E5F5",
|
|
28
|
+
"fontcolor": "#7B1FA2",
|
|
29
|
+
"style": "filled",
|
|
30
30
|
},
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
"sub_agent": {
|
|
32
|
+
"shape": "box",
|
|
33
|
+
"fillcolor": "#E8F5E8",
|
|
34
|
+
"fontcolor": "#388E3C",
|
|
35
|
+
"style": "filled,dashed",
|
|
36
36
|
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'style': 'solid',
|
|
40
|
-
'penwidth': '1.5'
|
|
41
|
-
},
|
|
42
|
-
'tool_edge': {
|
|
43
|
-
'color': '#9C27B0',
|
|
44
|
-
'style': 'dashed',
|
|
45
|
-
'penwidth': '1.0'
|
|
46
|
-
}
|
|
37
|
+
"edge": {"color": "#424242", "style": "solid", "penwidth": "1.5"},
|
|
38
|
+
"tool_edge": {"color": "#9C27B0", "style": "dashed", "penwidth": "1.0"},
|
|
47
39
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
40
|
+
"modern": {
|
|
41
|
+
"agent": {
|
|
42
|
+
"shape": "box",
|
|
43
|
+
"fillcolor": "#667eea",
|
|
44
|
+
"fontcolor": "white",
|
|
45
|
+
"style": "filled,rounded",
|
|
46
|
+
"fontname": "Arial Bold",
|
|
55
47
|
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
"tool": {
|
|
49
|
+
"shape": "ellipse",
|
|
50
|
+
"fillcolor": "#f093fb",
|
|
51
|
+
"fontcolor": "white",
|
|
52
|
+
"style": "filled",
|
|
53
|
+
"fontname": "Arial",
|
|
62
54
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
55
|
+
"sub_agent": {
|
|
56
|
+
"shape": "box",
|
|
57
|
+
"fillcolor": "#4facfe",
|
|
58
|
+
"fontcolor": "white",
|
|
59
|
+
"style": "filled,dashed",
|
|
60
|
+
"fontname": "Arial",
|
|
69
61
|
},
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
"edge": {"color": "#667eea", "style": "solid", "penwidth": "2.0", "arrowhead": "vee"},
|
|
63
|
+
"tool_edge": {
|
|
64
|
+
"color": "#f093fb",
|
|
65
|
+
"style": "dashed",
|
|
66
|
+
"penwidth": "1.5",
|
|
67
|
+
"arrowhead": "open",
|
|
75
68
|
},
|
|
76
|
-
'tool_edge': {
|
|
77
|
-
'color': '#f093fb',
|
|
78
|
-
'style': 'dashed',
|
|
79
|
-
'penwidth': '1.5',
|
|
80
|
-
'arrowhead': 'open'
|
|
81
|
-
}
|
|
82
69
|
},
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
},
|
|
91
|
-
'tool': {
|
|
92
|
-
'shape': 'ellipse',
|
|
93
|
-
'fillcolor': '#f5f5f5',
|
|
94
|
-
'fontcolor': 'black',
|
|
95
|
-
'style': 'filled'
|
|
70
|
+
"minimal": {
|
|
71
|
+
"agent": {
|
|
72
|
+
"shape": "box",
|
|
73
|
+
"fillcolor": "white",
|
|
74
|
+
"fontcolor": "black",
|
|
75
|
+
"style": "filled",
|
|
76
|
+
"penwidth": "2",
|
|
96
77
|
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
78
|
+
"tool": {
|
|
79
|
+
"shape": "ellipse",
|
|
80
|
+
"fillcolor": "#f5f5f5",
|
|
81
|
+
"fontcolor": "black",
|
|
82
|
+
"style": "filled",
|
|
102
83
|
},
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
84
|
+
"sub_agent": {
|
|
85
|
+
"shape": "box",
|
|
86
|
+
"fillcolor": "white",
|
|
87
|
+
"fontcolor": "gray",
|
|
88
|
+
"style": "filled,dashed",
|
|
107
89
|
},
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
'penwidth': '1.0'
|
|
112
|
-
}
|
|
113
|
-
}
|
|
90
|
+
"edge": {"color": "black", "style": "solid", "penwidth": "1.0"},
|
|
91
|
+
"tool_edge": {"color": "gray", "style": "dashed", "penwidth": "1.0"},
|
|
92
|
+
},
|
|
114
93
|
}
|
|
115
94
|
|
|
116
95
|
|
|
117
96
|
# ========== Core Graph Generation Functions ==========
|
|
118
97
|
|
|
98
|
+
|
|
119
99
|
async def generate_agent_graph(
|
|
120
|
-
agents: List[Agent],
|
|
121
|
-
options: Optional[GraphOptions] = None
|
|
100
|
+
agents: List[Agent], options: Optional[GraphOptions] = None
|
|
122
101
|
) -> GraphResult:
|
|
123
102
|
"""
|
|
124
103
|
Generate a visual graph of agent architecture using functional approach.
|
|
125
|
-
|
|
104
|
+
|
|
126
105
|
Args:
|
|
127
106
|
agents: List of Agent objects to visualize
|
|
128
107
|
options: Graph generation options
|
|
129
|
-
|
|
108
|
+
|
|
130
109
|
Returns:
|
|
131
110
|
GraphResult with success status and output information
|
|
132
111
|
"""
|
|
@@ -139,10 +118,7 @@ async def generate_agent_graph(
|
|
|
139
118
|
# Validate options (pure function)
|
|
140
119
|
errors = validate_graph_options(opts)
|
|
141
120
|
if errors:
|
|
142
|
-
return GraphResult(
|
|
143
|
-
success=False,
|
|
144
|
-
error=f"Invalid options: {', '.join(errors)}"
|
|
145
|
-
)
|
|
121
|
+
return GraphResult(success=False, error=f"Invalid options: {', '.join(errors)}")
|
|
146
122
|
|
|
147
123
|
# Get color scheme (immutable data)
|
|
148
124
|
styles = COLOR_SCHEMES[opts.color_scheme]
|
|
@@ -151,26 +127,22 @@ async def generate_agent_graph(
|
|
|
151
127
|
graph_spec = create_agent_graph_spec(agents, opts, styles)
|
|
152
128
|
|
|
153
129
|
# Render graph using imperative shell
|
|
154
|
-
return render_graph_spec(graph_spec, opts,
|
|
130
|
+
return render_graph_spec(graph_spec, opts, "AgentGraph")
|
|
155
131
|
|
|
156
132
|
except Exception as error:
|
|
157
|
-
return GraphResult(
|
|
158
|
-
success=False,
|
|
159
|
-
error=str(error)
|
|
160
|
-
)
|
|
133
|
+
return GraphResult(success=False, error=str(error))
|
|
161
134
|
|
|
162
135
|
|
|
163
136
|
async def generate_tool_graph(
|
|
164
|
-
tools: List[Tool],
|
|
165
|
-
options: Optional[GraphOptions] = None
|
|
137
|
+
tools: List[Tool], options: Optional[GraphOptions] = None
|
|
166
138
|
) -> GraphResult:
|
|
167
139
|
"""
|
|
168
140
|
Generate a visual graph of tool ecosystem using functional approach.
|
|
169
|
-
|
|
141
|
+
|
|
170
142
|
Args:
|
|
171
143
|
tools: List of Tool objects to visualize
|
|
172
144
|
options: Graph generation options
|
|
173
|
-
|
|
145
|
+
|
|
174
146
|
Returns:
|
|
175
147
|
GraphResult with success status and output information
|
|
176
148
|
"""
|
|
@@ -178,18 +150,12 @@ async def generate_tool_graph(
|
|
|
178
150
|
from .imperative_shell import render_graph_spec
|
|
179
151
|
|
|
180
152
|
try:
|
|
181
|
-
opts = options or GraphOptions(
|
|
182
|
-
title="JAF Tool Graph",
|
|
183
|
-
layout='circo'
|
|
184
|
-
)
|
|
153
|
+
opts = options or GraphOptions(title="JAF Tool Graph", layout="circo")
|
|
185
154
|
|
|
186
155
|
# Validate options (pure function)
|
|
187
156
|
errors = validate_graph_options(opts)
|
|
188
157
|
if errors:
|
|
189
|
-
return GraphResult(
|
|
190
|
-
success=False,
|
|
191
|
-
error=f"Invalid options: {', '.join(errors)}"
|
|
192
|
-
)
|
|
158
|
+
return GraphResult(success=False, error=f"Invalid options: {', '.join(errors)}")
|
|
193
159
|
|
|
194
160
|
# Get color scheme (immutable data)
|
|
195
161
|
styles = COLOR_SCHEMES[opts.color_scheme]
|
|
@@ -198,70 +164,66 @@ async def generate_tool_graph(
|
|
|
198
164
|
graph_spec = create_tool_graph_spec(tools, opts, styles)
|
|
199
165
|
|
|
200
166
|
# Render graph using imperative shell
|
|
201
|
-
return render_graph_spec(graph_spec, opts,
|
|
167
|
+
return render_graph_spec(graph_spec, opts, "ToolGraph")
|
|
202
168
|
|
|
203
169
|
except Exception as error:
|
|
204
|
-
return GraphResult(
|
|
205
|
-
success=False,
|
|
206
|
-
error=str(error)
|
|
207
|
-
)
|
|
170
|
+
return GraphResult(success=False, error=str(error))
|
|
208
171
|
|
|
209
172
|
|
|
210
173
|
async def generate_runner_graph(
|
|
211
|
-
agent_registry: Dict[str, Agent],
|
|
212
|
-
options: Optional[GraphOptions] = None
|
|
174
|
+
agent_registry: Dict[str, Agent], options: Optional[GraphOptions] = None
|
|
213
175
|
) -> GraphResult:
|
|
214
176
|
"""
|
|
215
177
|
Generate a visual graph of runner architecture.
|
|
216
|
-
|
|
178
|
+
|
|
217
179
|
Args:
|
|
218
180
|
agent_registry: Dictionary of agent name to Agent object
|
|
219
181
|
options: Graph generation options
|
|
220
|
-
|
|
182
|
+
|
|
221
183
|
Returns:
|
|
222
184
|
GraphResult with success status and output information
|
|
223
185
|
"""
|
|
224
186
|
try:
|
|
225
|
-
opts = options or GraphOptions(
|
|
226
|
-
title="JAF Runner Architecture",
|
|
227
|
-
color_scheme='modern'
|
|
228
|
-
)
|
|
187
|
+
opts = options or GraphOptions(title="JAF Runner Architecture", color_scheme="modern")
|
|
229
188
|
|
|
230
189
|
# Validate options
|
|
231
190
|
errors = validate_graph_options(opts)
|
|
232
191
|
if errors:
|
|
233
|
-
return GraphResult(
|
|
234
|
-
success=False,
|
|
235
|
-
error=f"Invalid options: {', '.join(errors)}"
|
|
236
|
-
)
|
|
192
|
+
return GraphResult(success=False, error=f"Invalid options: {', '.join(errors)}")
|
|
237
193
|
|
|
238
194
|
# Create digraph
|
|
239
|
-
graph = Digraph(
|
|
240
|
-
graph.attr(
|
|
195
|
+
graph = Digraph("RunnerGraph", comment=opts.title)
|
|
196
|
+
graph.attr("graph", compound="true")
|
|
241
197
|
|
|
242
198
|
# Set graph attributes
|
|
243
|
-
graph.attr(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
199
|
+
graph.attr(
|
|
200
|
+
"graph",
|
|
201
|
+
rankdir=opts.rankdir,
|
|
202
|
+
label=opts.title or "",
|
|
203
|
+
labelloc="t",
|
|
204
|
+
fontsize="16",
|
|
205
|
+
fontname="Arial Bold",
|
|
206
|
+
bgcolor="white",
|
|
207
|
+
pad="0.5",
|
|
208
|
+
)
|
|
251
209
|
|
|
252
210
|
# Get color scheme
|
|
253
211
|
styles = COLOR_SCHEMES[opts.color_scheme]
|
|
254
212
|
|
|
255
213
|
# Create clusters for different components
|
|
256
|
-
with graph.subgraph(name=
|
|
257
|
-
agent_cluster.attr(label=
|
|
258
|
-
agent_cluster.attr(style=
|
|
259
|
-
agent_cluster.attr(fillcolor=
|
|
214
|
+
with graph.subgraph(name="cluster_agents") as agent_cluster:
|
|
215
|
+
agent_cluster.attr(label="Agents")
|
|
216
|
+
agent_cluster.attr(style="filled")
|
|
217
|
+
agent_cluster.attr(fillcolor="#f8f9fa")
|
|
260
218
|
|
|
261
219
|
# Add agents to cluster
|
|
262
220
|
for agent_name, agent in agent_registry.items():
|
|
263
221
|
# Create agent label
|
|
264
|
-
model_name =
|
|
222
|
+
model_name = (
|
|
223
|
+
getattr(agent.model_config, "name", "default")
|
|
224
|
+
if agent.model_config
|
|
225
|
+
else "default"
|
|
226
|
+
)
|
|
265
227
|
label = f"{agent.name}\\n({model_name})"
|
|
266
228
|
|
|
267
229
|
if opts.show_tool_details and agent.tools:
|
|
@@ -274,68 +236,65 @@ async def generate_runner_graph(
|
|
|
274
236
|
agent_cluster.node(
|
|
275
237
|
agent.name,
|
|
276
238
|
label=label,
|
|
277
|
-
shape=styles[
|
|
278
|
-
fillcolor=styles[
|
|
279
|
-
fontcolor=styles[
|
|
280
|
-
style=styles[
|
|
239
|
+
shape=styles["agent"]["shape"],
|
|
240
|
+
fillcolor=styles["agent"]["fillcolor"],
|
|
241
|
+
fontcolor=styles["agent"]["fontcolor"],
|
|
242
|
+
style=styles["agent"]["style"],
|
|
281
243
|
)
|
|
282
244
|
|
|
283
|
-
with graph.subgraph(name=
|
|
284
|
-
session_cluster.attr(label=
|
|
285
|
-
session_cluster.attr(style=
|
|
286
|
-
session_cluster.attr(fillcolor=
|
|
245
|
+
with graph.subgraph(name="cluster_session") as session_cluster:
|
|
246
|
+
session_cluster.attr(label="Session Layer")
|
|
247
|
+
session_cluster.attr(style="filled")
|
|
248
|
+
session_cluster.attr(fillcolor="#fff3cd")
|
|
287
249
|
|
|
288
250
|
# Add session provider
|
|
289
251
|
session_cluster.node(
|
|
290
|
-
|
|
291
|
-
label=
|
|
292
|
-
shape=
|
|
293
|
-
fillcolor=
|
|
294
|
-
fontcolor=
|
|
295
|
-
style=
|
|
252
|
+
"session_provider",
|
|
253
|
+
label="Session\\nProvider",
|
|
254
|
+
shape="box",
|
|
255
|
+
fillcolor="#ffc107",
|
|
256
|
+
fontcolor="black",
|
|
257
|
+
style="filled,rounded",
|
|
296
258
|
)
|
|
297
259
|
|
|
298
260
|
# Add runner node
|
|
299
261
|
graph.node(
|
|
300
|
-
|
|
301
|
-
label=
|
|
302
|
-
shape=
|
|
303
|
-
fillcolor=
|
|
304
|
-
fontcolor=
|
|
305
|
-
style=
|
|
306
|
-
fontsize=
|
|
307
|
-
fontname=
|
|
262
|
+
"runner",
|
|
263
|
+
label="Runner",
|
|
264
|
+
shape="diamond",
|
|
265
|
+
fillcolor="#28a745",
|
|
266
|
+
fontcolor="white",
|
|
267
|
+
style="filled",
|
|
268
|
+
fontsize="14",
|
|
269
|
+
fontname="Arial Bold",
|
|
308
270
|
)
|
|
309
271
|
|
|
310
272
|
# Add edges
|
|
311
273
|
for agent_name in agent_registry:
|
|
312
|
-
graph.edge(
|
|
313
|
-
|
|
274
|
+
graph.edge(
|
|
275
|
+
"runner",
|
|
276
|
+
agent_name,
|
|
277
|
+
color=styles["edge"]["color"],
|
|
278
|
+
style=styles["edge"]["style"],
|
|
279
|
+
label="executes",
|
|
280
|
+
)
|
|
314
281
|
|
|
315
|
-
graph.edge(
|
|
316
|
-
style='dashed', label='manages')
|
|
282
|
+
graph.edge("runner", "session_provider", color="#ffc107", style="dashed", label="manages")
|
|
317
283
|
|
|
318
284
|
# Generate output
|
|
319
285
|
output_path = opts.output_path or f"./runner-graph.{opts.output_format}"
|
|
320
286
|
|
|
321
287
|
# Render the graph
|
|
322
288
|
graph.render(
|
|
323
|
-
filename=output_path.replace(f
|
|
289
|
+
filename=output_path.replace(f".{opts.output_format}", ""),
|
|
324
290
|
format=opts.output_format,
|
|
325
|
-
cleanup=True
|
|
291
|
+
cleanup=True,
|
|
326
292
|
)
|
|
327
293
|
|
|
328
|
-
return GraphResult(
|
|
329
|
-
success=True,
|
|
330
|
-
output_path=output_path,
|
|
331
|
-
graph_dot=graph.source
|
|
332
|
-
)
|
|
294
|
+
return GraphResult(success=True, output_path=output_path, graph_dot=graph.source)
|
|
333
295
|
|
|
334
296
|
except Exception as error:
|
|
335
|
-
return GraphResult(
|
|
336
|
-
success=False,
|
|
337
|
-
error=str(error)
|
|
338
|
-
)
|
|
297
|
+
return GraphResult(success=False, error=str(error))
|
|
339
298
|
|
|
340
299
|
|
|
341
300
|
# ========== Helper Functions (Deprecated - moved to functional_core) ==========
|
|
@@ -344,47 +303,53 @@ async def generate_runner_graph(
|
|
|
344
303
|
|
|
345
304
|
# ========== Validation Functions ==========
|
|
346
305
|
|
|
306
|
+
|
|
347
307
|
def validate_graph_options(options: GraphOptions) -> List[str]:
|
|
348
308
|
"""
|
|
349
309
|
Validate graph options and return list of errors.
|
|
350
|
-
|
|
310
|
+
|
|
351
311
|
Args:
|
|
352
312
|
options: GraphOptions to validate
|
|
353
|
-
|
|
313
|
+
|
|
354
314
|
Returns:
|
|
355
315
|
List of validation error messages
|
|
356
316
|
"""
|
|
357
317
|
errors = []
|
|
358
318
|
|
|
359
|
-
valid_layouts = [
|
|
319
|
+
valid_layouts = ["dot", "neato", "fdp", "circo", "twopi"]
|
|
360
320
|
if options.layout not in valid_layouts:
|
|
361
321
|
errors.append(f"Invalid layout '{options.layout}'. Must be one of: {valid_layouts}")
|
|
362
322
|
|
|
363
|
-
valid_rankdirs = [
|
|
323
|
+
valid_rankdirs = ["TB", "LR", "BT", "RL"]
|
|
364
324
|
if options.rankdir not in valid_rankdirs:
|
|
365
325
|
errors.append(f"Invalid rankdir '{options.rankdir}'. Must be one of: {valid_rankdirs}")
|
|
366
326
|
|
|
367
|
-
valid_formats = [
|
|
327
|
+
valid_formats = ["png", "svg", "pdf"]
|
|
368
328
|
if options.output_format not in valid_formats:
|
|
369
|
-
errors.append(
|
|
329
|
+
errors.append(
|
|
330
|
+
f"Invalid output_format '{options.output_format}'. Must be one of: {valid_formats}"
|
|
331
|
+
)
|
|
370
332
|
|
|
371
|
-
valid_schemes = [
|
|
333
|
+
valid_schemes = ["default", "modern", "minimal"]
|
|
372
334
|
if options.color_scheme not in valid_schemes:
|
|
373
|
-
errors.append(
|
|
335
|
+
errors.append(
|
|
336
|
+
f"Invalid color_scheme '{options.color_scheme}'. Must be one of: {valid_schemes}"
|
|
337
|
+
)
|
|
374
338
|
|
|
375
339
|
return errors
|
|
376
340
|
|
|
377
341
|
|
|
378
342
|
# ========== Utility Functions ==========
|
|
379
343
|
|
|
344
|
+
|
|
380
345
|
def get_graph_dot(agents: List[Agent], options: Optional[GraphOptions] = None) -> str:
|
|
381
346
|
"""
|
|
382
347
|
Get the DOT language representation of an agent graph using functional approach.
|
|
383
|
-
|
|
348
|
+
|
|
384
349
|
Args:
|
|
385
350
|
agents: List of Agent objects
|
|
386
351
|
options: Graph generation options
|
|
387
|
-
|
|
352
|
+
|
|
388
353
|
Returns:
|
|
389
354
|
DOT language string representation
|
|
390
355
|
"""
|
|
@@ -400,4 +365,4 @@ def get_graph_dot(agents: List[Agent], options: Optional[GraphOptions] = None) -
|
|
|
400
365
|
graph_spec = create_agent_graph_spec(agents, opts, styles)
|
|
401
366
|
|
|
402
367
|
# Convert to DOT using imperative shell (no file system side effects)
|
|
403
|
-
return graph_spec_to_dot(graph_spec,
|
|
368
|
+
return graph_spec_to_dot(graph_spec, "AgentGraph")
|
|
@@ -22,7 +22,7 @@ def apply_graph_spec_to_digraph(spec: GraphSpec, digraph: Digraph) -> Digraph:
|
|
|
22
22
|
This is the imperative shell that contains all side effects.
|
|
23
23
|
"""
|
|
24
24
|
# Apply graph attributes
|
|
25
|
-
digraph.attr(
|
|
25
|
+
digraph.attr("graph", **spec.graph_attributes)
|
|
26
26
|
|
|
27
27
|
# Add all nodes
|
|
28
28
|
for node in spec.nodes:
|
|
@@ -36,9 +36,7 @@ def apply_graph_spec_to_digraph(spec: GraphSpec, digraph: Digraph) -> Digraph:
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def render_graph_spec(
|
|
39
|
-
spec: GraphSpec,
|
|
40
|
-
options: GraphOptions,
|
|
41
|
-
graph_name: str = 'Graph'
|
|
39
|
+
spec: GraphSpec, options: GraphOptions, graph_name: str = "Graph"
|
|
42
40
|
) -> GraphResult:
|
|
43
41
|
"""
|
|
44
42
|
Render a GraphSpec to an actual file using Graphviz.
|
|
@@ -56,25 +54,18 @@ def render_graph_spec(
|
|
|
56
54
|
|
|
57
55
|
# Render (side effect)
|
|
58
56
|
graph.render(
|
|
59
|
-
filename=output_path.replace(f
|
|
57
|
+
filename=output_path.replace(f".{options.output_format}", ""),
|
|
60
58
|
format=options.output_format,
|
|
61
|
-
cleanup=True
|
|
59
|
+
cleanup=True,
|
|
62
60
|
)
|
|
63
61
|
|
|
64
|
-
return GraphResult(
|
|
65
|
-
success=True,
|
|
66
|
-
output_path=output_path,
|
|
67
|
-
graph_dot=graph.source
|
|
68
|
-
)
|
|
62
|
+
return GraphResult(success=True, output_path=output_path, graph_dot=graph.source)
|
|
69
63
|
|
|
70
64
|
except Exception as error:
|
|
71
|
-
return GraphResult(
|
|
72
|
-
success=False,
|
|
73
|
-
error=str(error)
|
|
74
|
-
)
|
|
65
|
+
return GraphResult(success=False, error=str(error))
|
|
75
66
|
|
|
76
67
|
|
|
77
|
-
def graph_spec_to_dot(spec: GraphSpec, graph_name: str =
|
|
68
|
+
def graph_spec_to_dot(spec: GraphSpec, graph_name: str = "Graph") -> str:
|
|
78
69
|
"""
|
|
79
70
|
Convert a GraphSpec to DOT language string without file system side effects.
|
|
80
71
|
This is a pure operation that only creates the DOT representation.
|
jaf/visualization/types.py
CHANGED
|
@@ -13,6 +13,7 @@ from pydantic import BaseModel
|
|
|
13
13
|
@dataclass(frozen=True)
|
|
14
14
|
class NodeStyle:
|
|
15
15
|
"""Styling configuration for graph nodes."""
|
|
16
|
+
|
|
16
17
|
shape: str
|
|
17
18
|
fillcolor: str
|
|
18
19
|
fontcolor: str
|
|
@@ -24,6 +25,7 @@ class NodeStyle:
|
|
|
24
25
|
@dataclass(frozen=True)
|
|
25
26
|
class EdgeStyle:
|
|
26
27
|
"""Styling configuration for graph edges."""
|
|
28
|
+
|
|
27
29
|
color: str
|
|
28
30
|
style: str
|
|
29
31
|
penwidth: Optional[str] = None
|
|
@@ -32,19 +34,21 @@ class EdgeStyle:
|
|
|
32
34
|
|
|
33
35
|
class GraphOptions(BaseModel):
|
|
34
36
|
"""Configuration options for graph generation."""
|
|
37
|
+
|
|
35
38
|
title: Optional[str] = "JAF Graph"
|
|
36
|
-
layout: Literal[
|
|
37
|
-
rankdir: Literal[
|
|
38
|
-
output_format: Literal[
|
|
39
|
+
layout: Literal["dot", "neato", "fdp", "circo", "twopi"] = "dot"
|
|
40
|
+
rankdir: Literal["TB", "LR", "BT", "RL"] = "TB"
|
|
41
|
+
output_format: Literal["png", "svg", "pdf"] = "png"
|
|
39
42
|
output_path: Optional[str] = None
|
|
40
43
|
show_tool_details: bool = True
|
|
41
44
|
show_sub_agents: bool = True
|
|
42
|
-
color_scheme: Literal[
|
|
45
|
+
color_scheme: Literal["default", "modern", "minimal"] = "default"
|
|
43
46
|
|
|
44
47
|
|
|
45
48
|
@dataclass(frozen=True)
|
|
46
49
|
class GraphResult:
|
|
47
50
|
"""Result of graph generation operation."""
|
|
51
|
+
|
|
48
52
|
success: bool
|
|
49
53
|
output_path: Optional[str] = None
|
|
50
54
|
error: Optional[str] = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jaf-py
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.11
|
|
4
4
|
Summary: A purely functional agent framework with immutable state and composable tools - Python implementation
|
|
5
5
|
Author: JAF Contributors
|
|
6
6
|
Maintainer: JAF Contributors
|
|
@@ -82,7 +82,7 @@ Dynamic: license-file
|
|
|
82
82
|
|
|
83
83
|
<!--  -->
|
|
84
84
|
|
|
85
|
-
[](https://github.com/xynehq/jaf-py)
|
|
86
86
|
[](https://www.python.org/)
|
|
87
87
|
[](https://xynehq.github.io/jaf-py/)
|
|
88
88
|
|