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,181 @@
1
+ """Graph configuration validators.
2
+
3
+ Validation functions for YAML graph configuration structures.
4
+ """
5
+
6
+ from typing import Any
7
+
8
+ from yamlgraph.constants import ErrorHandler, NodeType
9
+
10
+
11
+ def validate_required_sections(config: dict[str, Any]) -> None:
12
+ """Validate required top-level sections exist.
13
+
14
+ Args:
15
+ config: Parsed YAML configuration dictionary
16
+
17
+ Raises:
18
+ ValueError: If required sections are missing
19
+ """
20
+ if not config.get("nodes"):
21
+ raise ValueError("Graph config missing required 'nodes' section")
22
+ if not config.get("edges"):
23
+ raise ValueError("Graph config missing required 'edges' section")
24
+
25
+
26
+ def validate_node_prompt(node_name: str, node_config: dict[str, Any]) -> None:
27
+ """Validate node has required prompt if applicable.
28
+
29
+ Args:
30
+ node_name: Name of the node
31
+ node_config: Node configuration dictionary
32
+
33
+ Raises:
34
+ ValueError: If prompt is required but missing
35
+ """
36
+ node_type = node_config.get("type", NodeType.LLM)
37
+ # Only llm and router nodes require prompts
38
+ # tool, python, agent, and map nodes don't require prompts
39
+ if NodeType.requires_prompt(node_type) and not node_config.get("prompt"):
40
+ raise ValueError(f"Node '{node_name}' missing required 'prompt' field")
41
+
42
+
43
+ def validate_router_node(
44
+ node_name: str, node_config: dict[str, Any], all_nodes: dict[str, Any]
45
+ ) -> None:
46
+ """Validate router node has routes pointing to valid nodes.
47
+
48
+ Args:
49
+ node_name: Name of the node
50
+ node_config: Node configuration dictionary
51
+ all_nodes: All nodes in the graph for target validation
52
+
53
+ Raises:
54
+ ValueError: If router configuration is invalid
55
+ """
56
+ if node_config.get("type") != NodeType.ROUTER:
57
+ return
58
+
59
+ if not node_config.get("routes"):
60
+ raise ValueError(f"Router node '{node_name}' missing required 'routes' field")
61
+
62
+ for route_key, target_node in node_config["routes"].items():
63
+ if target_node not in all_nodes:
64
+ raise ValueError(
65
+ f"Router node '{node_name}' route '{route_key}' points to "
66
+ f"nonexistent node '{target_node}'"
67
+ )
68
+
69
+
70
+ def validate_edges(edges: list[dict[str, Any]]) -> None:
71
+ """Validate each edge has required from/to fields.
72
+
73
+ Args:
74
+ edges: List of edge configurations
75
+
76
+ Raises:
77
+ ValueError: If edge is missing required fields
78
+ """
79
+ for i, edge in enumerate(edges):
80
+ if "from" not in edge:
81
+ raise ValueError(f"Edge {i} missing required 'from' field")
82
+ if "to" not in edge:
83
+ raise ValueError(f"Edge {i} missing required 'to' field")
84
+
85
+ # Validate condition expressions at load time
86
+ if "condition" in edge:
87
+ validate_condition_expression(edge["condition"], i)
88
+
89
+
90
+ def validate_condition_expression(condition: str, edge_index: int) -> None:
91
+ """Validate a condition expression has valid syntax.
92
+
93
+ Performs compile-time validation of condition expressions to catch
94
+ syntax errors early rather than at runtime.
95
+
96
+ Supports expression conditions like "score < 0.8", "a.b >= 1 and c == 'done'"
97
+
98
+ Args:
99
+ condition: Condition expression like "score < 0.8"
100
+ edge_index: Edge index for error messages
101
+
102
+ Raises:
103
+ ValueError: If condition has invalid syntax
104
+ """
105
+ import re
106
+
107
+ # Expression syntax check - must match comparison pattern
108
+ # Valid: "score < 0.8", "a.b >= 1", "x == 'done'"
109
+ # Also valid: compound expressions "a > 1 and b < 2"
110
+ comparison_pattern = r"[a-zA-Z_][\w.]*\s*(<=|>=|==|!=|<|>)\s*.+"
111
+ compound_pattern = r"\s+(and|or)\s+"
112
+
113
+ # Split by and/or and validate each part
114
+ parts = re.split(compound_pattern, condition, flags=re.IGNORECASE)
115
+ # parts includes the 'and'/'or' tokens, so filter to just comparisons
116
+ comparisons = [p.strip() for p in parts if p.strip().lower() not in ("and", "or")]
117
+
118
+ for part in comparisons:
119
+ if not re.match(comparison_pattern, part.strip()):
120
+ raise ValueError(
121
+ f"Edge {edge_index} has invalid condition syntax: '{condition}'. "
122
+ f"Expected format: 'field <op> value' (e.g., 'score < 0.8')"
123
+ )
124
+
125
+
126
+ def validate_on_error(node_name: str, node_config: dict[str, Any]) -> None:
127
+ """Validate on_error value is valid.
128
+
129
+ Args:
130
+ node_name: Name of the node
131
+ node_config: Node configuration dictionary
132
+
133
+ Raises:
134
+ ValueError: If on_error value is invalid
135
+ """
136
+ on_error = node_config.get("on_error")
137
+ if on_error and on_error not in ErrorHandler.all_values():
138
+ raise ValueError(
139
+ f"Node '{node_name}' has invalid on_error value '{on_error}'. "
140
+ f"Valid values: {', '.join(ErrorHandler.all_values())}"
141
+ )
142
+
143
+
144
+ def validate_map_node(node_name: str, node_config: dict[str, Any]) -> None:
145
+ """Validate map node has required fields.
146
+
147
+ Args:
148
+ node_name: Name of the node
149
+ node_config: Node configuration dictionary
150
+
151
+ Raises:
152
+ ValueError: If map node configuration is invalid
153
+ """
154
+ if node_config.get("type") != NodeType.MAP:
155
+ return
156
+
157
+ required_fields = ["over", "as", "node", "collect"]
158
+ for field in required_fields:
159
+ if field not in node_config:
160
+ raise ValueError(f"Map node '{node_name}' missing required '{field}' field")
161
+
162
+
163
+ def validate_config(config: dict[str, Any]) -> None:
164
+ """Validate YAML configuration structure.
165
+
166
+ Args:
167
+ config: Parsed YAML dictionary
168
+
169
+ Raises:
170
+ ValueError: If required fields are missing or invalid
171
+ """
172
+ validate_required_sections(config)
173
+
174
+ nodes = config["nodes"]
175
+ for node_name, node_config in nodes.items():
176
+ validate_node_prompt(node_name, node_config)
177
+ validate_router_node(node_name, node_config, nodes)
178
+ validate_on_error(node_name, node_config)
179
+ validate_map_node(node_name, node_config)
180
+
181
+ validate_edges(config["edges"])