yamlgraph 0.3.9__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.
Files changed (185) hide show
  1. examples/__init__.py +1 -0
  2. examples/codegen/__init__.py +5 -0
  3. examples/codegen/models/__init__.py +13 -0
  4. examples/codegen/models/schemas.py +76 -0
  5. examples/codegen/tests/__init__.py +1 -0
  6. examples/codegen/tests/test_ai_helpers.py +235 -0
  7. examples/codegen/tests/test_ast_analysis.py +174 -0
  8. examples/codegen/tests/test_code_analysis.py +134 -0
  9. examples/codegen/tests/test_code_context.py +301 -0
  10. examples/codegen/tests/test_code_nav.py +89 -0
  11. examples/codegen/tests/test_dependency_tools.py +119 -0
  12. examples/codegen/tests/test_example_tools.py +185 -0
  13. examples/codegen/tests/test_git_tools.py +112 -0
  14. examples/codegen/tests/test_impl_agent_schemas.py +193 -0
  15. examples/codegen/tests/test_impl_agent_v4_graph.py +94 -0
  16. examples/codegen/tests/test_jedi_analysis.py +226 -0
  17. examples/codegen/tests/test_meta_tools.py +250 -0
  18. examples/codegen/tests/test_plan_discovery_prompt.py +98 -0
  19. examples/codegen/tests/test_syntax_tools.py +85 -0
  20. examples/codegen/tests/test_synthesize_prompt.py +94 -0
  21. examples/codegen/tests/test_template_tools.py +244 -0
  22. examples/codegen/tools/__init__.py +80 -0
  23. examples/codegen/tools/ai_helpers.py +420 -0
  24. examples/codegen/tools/ast_analysis.py +92 -0
  25. examples/codegen/tools/code_context.py +180 -0
  26. examples/codegen/tools/code_nav.py +52 -0
  27. examples/codegen/tools/dependency_tools.py +120 -0
  28. examples/codegen/tools/example_tools.py +188 -0
  29. examples/codegen/tools/git_tools.py +151 -0
  30. examples/codegen/tools/impl_executor.py +614 -0
  31. examples/codegen/tools/jedi_analysis.py +311 -0
  32. examples/codegen/tools/meta_tools.py +202 -0
  33. examples/codegen/tools/syntax_tools.py +26 -0
  34. examples/codegen/tools/template_tools.py +356 -0
  35. examples/fastapi_interview.py +167 -0
  36. examples/npc/api/__init__.py +1 -0
  37. examples/npc/api/app.py +100 -0
  38. examples/npc/api/routes/__init__.py +5 -0
  39. examples/npc/api/routes/encounter.py +182 -0
  40. examples/npc/api/session.py +330 -0
  41. examples/npc/demo.py +387 -0
  42. examples/npc/nodes/__init__.py +5 -0
  43. examples/npc/nodes/image_node.py +92 -0
  44. examples/npc/run_encounter.py +230 -0
  45. examples/shared/__init__.py +0 -0
  46. examples/shared/replicate_tool.py +238 -0
  47. examples/storyboard/__init__.py +1 -0
  48. examples/storyboard/generate_videos.py +335 -0
  49. examples/storyboard/nodes/__init__.py +12 -0
  50. examples/storyboard/nodes/animated_character_node.py +248 -0
  51. examples/storyboard/nodes/animated_image_node.py +138 -0
  52. examples/storyboard/nodes/character_node.py +162 -0
  53. examples/storyboard/nodes/image_node.py +118 -0
  54. examples/storyboard/nodes/replicate_tool.py +49 -0
  55. examples/storyboard/retry_images.py +118 -0
  56. scripts/demo_async_executor.py +212 -0
  57. scripts/demo_interview_e2e.py +200 -0
  58. scripts/demo_streaming.py +140 -0
  59. scripts/run_interview_demo.py +94 -0
  60. scripts/test_interrupt_fix.py +26 -0
  61. tests/__init__.py +1 -0
  62. tests/conftest.py +178 -0
  63. tests/integration/__init__.py +1 -0
  64. tests/integration/test_animated_storyboard.py +63 -0
  65. tests/integration/test_cli_commands.py +242 -0
  66. tests/integration/test_colocated_prompts.py +139 -0
  67. tests/integration/test_map_demo.py +50 -0
  68. tests/integration/test_memory_demo.py +283 -0
  69. tests/integration/test_npc_api/__init__.py +1 -0
  70. tests/integration/test_npc_api/test_routes.py +357 -0
  71. tests/integration/test_npc_api/test_session.py +216 -0
  72. tests/integration/test_pipeline_flow.py +105 -0
  73. tests/integration/test_providers.py +163 -0
  74. tests/integration/test_resume.py +75 -0
  75. tests/integration/test_subgraph_integration.py +295 -0
  76. tests/integration/test_subgraph_interrupt.py +106 -0
  77. tests/unit/__init__.py +1 -0
  78. tests/unit/test_agent_nodes.py +355 -0
  79. tests/unit/test_async_executor.py +346 -0
  80. tests/unit/test_checkpointer.py +212 -0
  81. tests/unit/test_checkpointer_factory.py +212 -0
  82. tests/unit/test_cli.py +121 -0
  83. tests/unit/test_cli_package.py +81 -0
  84. tests/unit/test_compile_graph_map.py +132 -0
  85. tests/unit/test_conditions_routing.py +253 -0
  86. tests/unit/test_config.py +93 -0
  87. tests/unit/test_conversation_memory.py +276 -0
  88. tests/unit/test_database.py +145 -0
  89. tests/unit/test_deprecation.py +104 -0
  90. tests/unit/test_executor.py +172 -0
  91. tests/unit/test_executor_async.py +179 -0
  92. tests/unit/test_export.py +149 -0
  93. tests/unit/test_expressions.py +178 -0
  94. tests/unit/test_feature_brainstorm.py +194 -0
  95. tests/unit/test_format_prompt.py +145 -0
  96. tests/unit/test_generic_report.py +200 -0
  97. tests/unit/test_graph_commands.py +327 -0
  98. tests/unit/test_graph_linter.py +627 -0
  99. tests/unit/test_graph_loader.py +357 -0
  100. tests/unit/test_graph_schema.py +193 -0
  101. tests/unit/test_inline_schema.py +151 -0
  102. tests/unit/test_interrupt_node.py +182 -0
  103. tests/unit/test_issues.py +164 -0
  104. tests/unit/test_jinja2_prompts.py +85 -0
  105. tests/unit/test_json_extract.py +134 -0
  106. tests/unit/test_langsmith.py +600 -0
  107. tests/unit/test_langsmith_tools.py +204 -0
  108. tests/unit/test_llm_factory.py +109 -0
  109. tests/unit/test_llm_factory_async.py +118 -0
  110. tests/unit/test_loops.py +403 -0
  111. tests/unit/test_map_node.py +144 -0
  112. tests/unit/test_no_backward_compat.py +56 -0
  113. tests/unit/test_node_factory.py +348 -0
  114. tests/unit/test_passthrough_node.py +126 -0
  115. tests/unit/test_prompts.py +324 -0
  116. tests/unit/test_python_nodes.py +198 -0
  117. tests/unit/test_reliability.py +298 -0
  118. tests/unit/test_result_export.py +234 -0
  119. tests/unit/test_router.py +296 -0
  120. tests/unit/test_sanitize.py +99 -0
  121. tests/unit/test_schema_loader.py +295 -0
  122. tests/unit/test_shell_tools.py +229 -0
  123. tests/unit/test_state_builder.py +331 -0
  124. tests/unit/test_state_builder_map.py +104 -0
  125. tests/unit/test_state_config.py +197 -0
  126. tests/unit/test_streaming.py +307 -0
  127. tests/unit/test_subgraph.py +596 -0
  128. tests/unit/test_template.py +190 -0
  129. tests/unit/test_tool_call_integration.py +164 -0
  130. tests/unit/test_tool_call_node.py +178 -0
  131. tests/unit/test_tool_nodes.py +129 -0
  132. tests/unit/test_websearch.py +234 -0
  133. yamlgraph/__init__.py +35 -0
  134. yamlgraph/builder.py +110 -0
  135. yamlgraph/cli/__init__.py +159 -0
  136. yamlgraph/cli/__main__.py +6 -0
  137. yamlgraph/cli/commands.py +231 -0
  138. yamlgraph/cli/deprecation.py +92 -0
  139. yamlgraph/cli/graph_commands.py +541 -0
  140. yamlgraph/cli/validators.py +37 -0
  141. yamlgraph/config.py +67 -0
  142. yamlgraph/constants.py +70 -0
  143. yamlgraph/error_handlers.py +227 -0
  144. yamlgraph/executor.py +290 -0
  145. yamlgraph/executor_async.py +288 -0
  146. yamlgraph/graph_loader.py +451 -0
  147. yamlgraph/map_compiler.py +150 -0
  148. yamlgraph/models/__init__.py +36 -0
  149. yamlgraph/models/graph_schema.py +181 -0
  150. yamlgraph/models/schemas.py +124 -0
  151. yamlgraph/models/state_builder.py +236 -0
  152. yamlgraph/node_factory.py +768 -0
  153. yamlgraph/routing.py +87 -0
  154. yamlgraph/schema_loader.py +240 -0
  155. yamlgraph/storage/__init__.py +20 -0
  156. yamlgraph/storage/checkpointer.py +72 -0
  157. yamlgraph/storage/checkpointer_factory.py +123 -0
  158. yamlgraph/storage/database.py +320 -0
  159. yamlgraph/storage/export.py +269 -0
  160. yamlgraph/tools/__init__.py +1 -0
  161. yamlgraph/tools/agent.py +320 -0
  162. yamlgraph/tools/graph_linter.py +388 -0
  163. yamlgraph/tools/langsmith_tools.py +125 -0
  164. yamlgraph/tools/nodes.py +126 -0
  165. yamlgraph/tools/python_tool.py +179 -0
  166. yamlgraph/tools/shell.py +205 -0
  167. yamlgraph/tools/websearch.py +242 -0
  168. yamlgraph/utils/__init__.py +48 -0
  169. yamlgraph/utils/conditions.py +157 -0
  170. yamlgraph/utils/expressions.py +245 -0
  171. yamlgraph/utils/json_extract.py +104 -0
  172. yamlgraph/utils/langsmith.py +416 -0
  173. yamlgraph/utils/llm_factory.py +118 -0
  174. yamlgraph/utils/llm_factory_async.py +105 -0
  175. yamlgraph/utils/logging.py +104 -0
  176. yamlgraph/utils/prompts.py +171 -0
  177. yamlgraph/utils/sanitize.py +98 -0
  178. yamlgraph/utils/template.py +102 -0
  179. yamlgraph/utils/validators.py +181 -0
  180. yamlgraph-0.3.9.dist-info/METADATA +1105 -0
  181. yamlgraph-0.3.9.dist-info/RECORD +185 -0
  182. yamlgraph-0.3.9.dist-info/WHEEL +5 -0
  183. yamlgraph-0.3.9.dist-info/entry_points.txt +2 -0
  184. yamlgraph-0.3.9.dist-info/licenses/LICENSE +33 -0
  185. yamlgraph-0.3.9.dist-info/top_level.txt +4 -0
@@ -0,0 +1,327 @@
1
+ """Tests for universal graph runner (Phase 7.2).
2
+
3
+ TDD tests for `yamlgraph graph run <path>` command.
4
+ """
5
+
6
+ import argparse
7
+ from pathlib import Path
8
+ from unittest.mock import MagicMock, patch
9
+
10
+ import pytest
11
+
12
+ # =============================================================================
13
+ # graph subcommand tests
14
+ # =============================================================================
15
+
16
+
17
+ class TestGraphSubcommand:
18
+ """Tests for graph subcommand group."""
19
+
20
+ def test_graph_subparser_exists(self):
21
+ """graph subparser should be configured."""
22
+ from yamlgraph.cli import create_parser
23
+
24
+ parser = create_parser()
25
+ # Parse with graph command
26
+ args = parser.parse_args(["graph", "list"])
27
+ assert args.command == "graph"
28
+
29
+ def test_graph_run_subcommand_exists(self):
30
+ """graph run subcommand should exist."""
31
+ from yamlgraph.cli import create_parser
32
+
33
+ parser = create_parser()
34
+ args = parser.parse_args(
35
+ ["graph", "run", "graphs/yamlgraph.yaml", "--var", "topic=AI"]
36
+ )
37
+ assert args.graph_command == "run"
38
+ assert args.graph_path == "graphs/yamlgraph.yaml"
39
+
40
+ def test_graph_list_subcommand_exists(self):
41
+ """graph list subcommand should exist."""
42
+ from yamlgraph.cli import create_parser
43
+
44
+ parser = create_parser()
45
+ args = parser.parse_args(["graph", "list"])
46
+ assert args.graph_command == "list"
47
+
48
+ def test_graph_info_subcommand_exists(self):
49
+ """graph info subcommand should exist."""
50
+ from yamlgraph.cli import create_parser
51
+
52
+ parser = create_parser()
53
+ args = parser.parse_args(["graph", "info", "graphs/yamlgraph.yaml"])
54
+ assert args.graph_command == "info"
55
+ assert args.graph_path == "graphs/yamlgraph.yaml"
56
+
57
+
58
+ # =============================================================================
59
+ # graph run argument parsing tests
60
+ # =============================================================================
61
+
62
+
63
+ class TestGraphRunArgs:
64
+ """Tests for graph run argument parsing."""
65
+
66
+ def test_var_single_value(self):
67
+ """--var key=value should parse correctly."""
68
+ from yamlgraph.cli import create_parser
69
+
70
+ parser = create_parser()
71
+ args = parser.parse_args(
72
+ ["graph", "run", "graphs/test.yaml", "--var", "topic=AI"]
73
+ )
74
+ assert args.var == ["topic=AI"]
75
+
76
+ def test_var_multiple_values(self):
77
+ """Multiple --var flags should accumulate."""
78
+ from yamlgraph.cli import create_parser
79
+
80
+ parser = create_parser()
81
+ args = parser.parse_args(
82
+ [
83
+ "graph",
84
+ "run",
85
+ "graphs/test.yaml",
86
+ "--var",
87
+ "topic=AI",
88
+ "--var",
89
+ "style=casual",
90
+ ]
91
+ )
92
+ assert args.var == ["topic=AI", "style=casual"]
93
+
94
+ def test_thread_argument(self):
95
+ """--thread should set thread ID."""
96
+ from yamlgraph.cli import create_parser
97
+
98
+ parser = create_parser()
99
+ args = parser.parse_args(
100
+ ["graph", "run", "graphs/test.yaml", "--thread", "abc123"]
101
+ )
102
+ assert args.thread == "abc123"
103
+
104
+ def test_export_flag(self):
105
+ """--export flag should enable export."""
106
+ from yamlgraph.cli import create_parser
107
+
108
+ parser = create_parser()
109
+ args = parser.parse_args(["graph", "run", "graphs/test.yaml", "--export"])
110
+ assert args.export is True
111
+
112
+ def test_graph_path_required(self):
113
+ """graph run requires a path argument."""
114
+ from yamlgraph.cli import create_parser
115
+
116
+ parser = create_parser()
117
+ with pytest.raises(SystemExit):
118
+ parser.parse_args(["graph", "run"])
119
+
120
+
121
+ # =============================================================================
122
+ # parse_vars helper tests
123
+ # =============================================================================
124
+
125
+
126
+ class TestParseVars:
127
+ """Tests for --var parsing helper."""
128
+
129
+ def test_parse_single_var(self):
130
+ """Single var should parse to dict."""
131
+ from yamlgraph.cli.graph_commands import parse_vars
132
+
133
+ result = parse_vars(["topic=AI"])
134
+ assert result == {"topic": "AI"}
135
+
136
+ def test_parse_multiple_vars(self):
137
+ """Multiple vars should parse to dict."""
138
+ from yamlgraph.cli.graph_commands import parse_vars
139
+
140
+ result = parse_vars(["topic=AI", "style=casual", "count=5"])
141
+ assert result == {"topic": "AI", "style": "casual", "count": "5"}
142
+
143
+ def test_parse_empty_list(self):
144
+ """Empty list returns empty dict."""
145
+ from yamlgraph.cli.graph_commands import parse_vars
146
+
147
+ result = parse_vars([])
148
+ assert result == {}
149
+
150
+ def test_parse_none_returns_empty(self):
151
+ """None returns empty dict."""
152
+ from yamlgraph.cli.graph_commands import parse_vars
153
+
154
+ result = parse_vars(None)
155
+ assert result == {}
156
+
157
+ def test_parse_value_with_equals(self):
158
+ """Value containing = should preserve it."""
159
+ from yamlgraph.cli.graph_commands import parse_vars
160
+
161
+ result = parse_vars(["equation=a=b+c"])
162
+ assert result == {"equation": "a=b+c"}
163
+
164
+ def test_parse_invalid_format_raises(self):
165
+ """Invalid format (no =) should raise ValueError."""
166
+ from yamlgraph.cli.graph_commands import parse_vars
167
+
168
+ with pytest.raises(ValueError, match="Invalid"):
169
+ parse_vars(["invalid"])
170
+
171
+
172
+ # =============================================================================
173
+ # cmd_graph_run tests
174
+ # =============================================================================
175
+
176
+
177
+ class TestCmdGraphRun:
178
+ """Tests for cmd_graph_run function."""
179
+
180
+ def test_cmd_graph_run_exists(self):
181
+ """cmd_graph_run function should exist."""
182
+ from yamlgraph.cli.graph_commands import cmd_graph_run
183
+
184
+ assert callable(cmd_graph_run)
185
+
186
+ def test_graph_not_found_error(self):
187
+ """Should error if graph file doesn't exist."""
188
+ from yamlgraph.cli.graph_commands import cmd_graph_run
189
+
190
+ args = argparse.Namespace(
191
+ graph_path="nonexistent.yaml",
192
+ var=[],
193
+ thread=None,
194
+ export=False,
195
+ )
196
+
197
+ with pytest.raises(SystemExit):
198
+ cmd_graph_run(args)
199
+
200
+ @patch("yamlgraph.graph_loader.load_and_compile")
201
+ def test_invokes_graph_with_vars(self, mock_load):
202
+ """Should invoke graph with parsed vars as initial state."""
203
+ from yamlgraph.cli.graph_commands import cmd_graph_run
204
+
205
+ mock_graph = MagicMock()
206
+ mock_app = MagicMock()
207
+ mock_app.invoke.return_value = {"result": "success"}
208
+ mock_graph.compile.return_value = mock_app
209
+ mock_load.return_value = mock_graph
210
+
211
+ args = argparse.Namespace(
212
+ graph_path="graphs/yamlgraph.yaml",
213
+ var=["topic=AI", "style=casual"],
214
+ thread=None,
215
+ export=False,
216
+ )
217
+
218
+ # Mock Path.exists
219
+ with patch.object(Path, "exists", return_value=True):
220
+ cmd_graph_run(args)
221
+
222
+ mock_app.invoke.assert_called_once()
223
+ call_args = mock_app.invoke.call_args[0][0]
224
+ assert call_args["topic"] == "AI"
225
+ assert call_args["style"] == "casual"
226
+
227
+
228
+ # =============================================================================
229
+ # cmd_graph_list tests
230
+ # =============================================================================
231
+
232
+
233
+ class TestCmdGraphList:
234
+ """Tests for cmd_graph_list function."""
235
+
236
+ def test_cmd_graph_list_exists(self):
237
+ """cmd_graph_list function should exist."""
238
+ from yamlgraph.cli.graph_commands import cmd_graph_list
239
+
240
+ assert callable(cmd_graph_list)
241
+
242
+ @patch("yamlgraph.cli.graph_commands.Path")
243
+ def test_lists_yaml_files(self, mock_path):
244
+ """Should list all .yaml files in graphs/."""
245
+ from yamlgraph.cli.graph_commands import cmd_graph_list
246
+
247
+ mock_graphs_dir = MagicMock()
248
+ mock_path.return_value = mock_graphs_dir
249
+ mock_graphs_dir.exists.return_value = True
250
+ mock_graphs_dir.glob.return_value = [
251
+ Path("graphs/yamlgraph.yaml"),
252
+ Path("graphs/router-demo.yaml"),
253
+ ]
254
+
255
+ args = argparse.Namespace()
256
+
257
+ with patch("builtins.print") as mock_print:
258
+ cmd_graph_list(args)
259
+ # Check it printed something about the graphs
260
+ calls = [str(c) for c in mock_print.call_args_list]
261
+ assert any("yamlgraph" in c for c in calls)
262
+
263
+
264
+ # =============================================================================
265
+ # cmd_graph_info tests
266
+ # =============================================================================
267
+
268
+
269
+ class TestCmdGraphInfo:
270
+ """Tests for cmd_graph_info function."""
271
+
272
+ def test_cmd_graph_info_exists(self):
273
+ """cmd_graph_info function should exist."""
274
+ from yamlgraph.cli.graph_commands import cmd_graph_info
275
+
276
+ assert callable(cmd_graph_info)
277
+
278
+ def test_info_file_not_found(self):
279
+ """Should error if graph file doesn't exist."""
280
+ from yamlgraph.cli.graph_commands import cmd_graph_info
281
+
282
+ args = argparse.Namespace(graph_path="nonexistent.yaml")
283
+
284
+ with pytest.raises(SystemExit):
285
+ cmd_graph_info(args)
286
+
287
+
288
+ # =============================================================================
289
+ # cmd_graph_validate tests
290
+ # =============================================================================
291
+
292
+
293
+ class TestCmdGraphValidate:
294
+ """Tests for cmd_graph_validate function."""
295
+
296
+ def test_cmd_graph_validate_exists(self):
297
+ """cmd_graph_validate function should exist."""
298
+ from yamlgraph.cli.graph_commands import cmd_graph_validate
299
+
300
+ assert callable(cmd_graph_validate)
301
+
302
+ def test_validate_file_not_found(self):
303
+ """Should error if graph file doesn't exist."""
304
+ from yamlgraph.cli.graph_commands import cmd_graph_validate
305
+
306
+ args = argparse.Namespace(graph_path="nonexistent.yaml")
307
+
308
+ with pytest.raises(SystemExit):
309
+ cmd_graph_validate(args)
310
+
311
+ def test_validate_valid_graph(self):
312
+ """Should validate a correct graph without errors."""
313
+ from yamlgraph.cli.graph_commands import cmd_graph_validate
314
+
315
+ args = argparse.Namespace(graph_path="graphs/yamlgraph.yaml")
316
+
317
+ # Should not raise
318
+ cmd_graph_validate(args)
319
+
320
+ def test_validate_subparser_exists(self):
321
+ """graph validate subcommand should exist."""
322
+ from yamlgraph.cli import create_parser
323
+
324
+ parser = create_parser()
325
+ args = parser.parse_args(["graph", "validate", "graphs/yamlgraph.yaml"])
326
+ assert args.graph_command == "validate"
327
+ assert args.graph_path == "graphs/yamlgraph.yaml"