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.
- examples/__init__.py +1 -0
- examples/codegen/__init__.py +5 -0
- examples/codegen/models/__init__.py +13 -0
- examples/codegen/models/schemas.py +76 -0
- examples/codegen/tests/__init__.py +1 -0
- examples/codegen/tests/test_ai_helpers.py +235 -0
- examples/codegen/tests/test_ast_analysis.py +174 -0
- examples/codegen/tests/test_code_analysis.py +134 -0
- examples/codegen/tests/test_code_context.py +301 -0
- examples/codegen/tests/test_code_nav.py +89 -0
- examples/codegen/tests/test_dependency_tools.py +119 -0
- examples/codegen/tests/test_example_tools.py +185 -0
- examples/codegen/tests/test_git_tools.py +112 -0
- examples/codegen/tests/test_impl_agent_schemas.py +193 -0
- examples/codegen/tests/test_impl_agent_v4_graph.py +94 -0
- examples/codegen/tests/test_jedi_analysis.py +226 -0
- examples/codegen/tests/test_meta_tools.py +250 -0
- examples/codegen/tests/test_plan_discovery_prompt.py +98 -0
- examples/codegen/tests/test_syntax_tools.py +85 -0
- examples/codegen/tests/test_synthesize_prompt.py +94 -0
- examples/codegen/tests/test_template_tools.py +244 -0
- examples/codegen/tools/__init__.py +80 -0
- examples/codegen/tools/ai_helpers.py +420 -0
- examples/codegen/tools/ast_analysis.py +92 -0
- examples/codegen/tools/code_context.py +180 -0
- examples/codegen/tools/code_nav.py +52 -0
- examples/codegen/tools/dependency_tools.py +120 -0
- examples/codegen/tools/example_tools.py +188 -0
- examples/codegen/tools/git_tools.py +151 -0
- examples/codegen/tools/impl_executor.py +614 -0
- examples/codegen/tools/jedi_analysis.py +311 -0
- examples/codegen/tools/meta_tools.py +202 -0
- examples/codegen/tools/syntax_tools.py +26 -0
- examples/codegen/tools/template_tools.py +356 -0
- examples/fastapi_interview.py +167 -0
- examples/npc/api/__init__.py +1 -0
- examples/npc/api/app.py +100 -0
- examples/npc/api/routes/__init__.py +5 -0
- examples/npc/api/routes/encounter.py +182 -0
- examples/npc/api/session.py +330 -0
- examples/npc/demo.py +387 -0
- examples/npc/nodes/__init__.py +5 -0
- examples/npc/nodes/image_node.py +92 -0
- examples/npc/run_encounter.py +230 -0
- examples/shared/__init__.py +0 -0
- examples/shared/replicate_tool.py +238 -0
- examples/storyboard/__init__.py +1 -0
- examples/storyboard/generate_videos.py +335 -0
- examples/storyboard/nodes/__init__.py +12 -0
- examples/storyboard/nodes/animated_character_node.py +248 -0
- examples/storyboard/nodes/animated_image_node.py +138 -0
- examples/storyboard/nodes/character_node.py +162 -0
- examples/storyboard/nodes/image_node.py +118 -0
- examples/storyboard/nodes/replicate_tool.py +49 -0
- examples/storyboard/retry_images.py +118 -0
- scripts/demo_async_executor.py +212 -0
- scripts/demo_interview_e2e.py +200 -0
- scripts/demo_streaming.py +140 -0
- scripts/run_interview_demo.py +94 -0
- scripts/test_interrupt_fix.py +26 -0
- tests/__init__.py +1 -0
- tests/conftest.py +178 -0
- tests/integration/__init__.py +1 -0
- tests/integration/test_animated_storyboard.py +63 -0
- tests/integration/test_cli_commands.py +242 -0
- tests/integration/test_colocated_prompts.py +139 -0
- tests/integration/test_map_demo.py +50 -0
- tests/integration/test_memory_demo.py +283 -0
- tests/integration/test_npc_api/__init__.py +1 -0
- tests/integration/test_npc_api/test_routes.py +357 -0
- tests/integration/test_npc_api/test_session.py +216 -0
- tests/integration/test_pipeline_flow.py +105 -0
- tests/integration/test_providers.py +163 -0
- tests/integration/test_resume.py +75 -0
- tests/integration/test_subgraph_integration.py +295 -0
- tests/integration/test_subgraph_interrupt.py +106 -0
- tests/unit/__init__.py +1 -0
- tests/unit/test_agent_nodes.py +355 -0
- tests/unit/test_async_executor.py +346 -0
- tests/unit/test_checkpointer.py +212 -0
- tests/unit/test_checkpointer_factory.py +212 -0
- tests/unit/test_cli.py +121 -0
- tests/unit/test_cli_package.py +81 -0
- tests/unit/test_compile_graph_map.py +132 -0
- tests/unit/test_conditions_routing.py +253 -0
- tests/unit/test_config.py +93 -0
- tests/unit/test_conversation_memory.py +276 -0
- tests/unit/test_database.py +145 -0
- tests/unit/test_deprecation.py +104 -0
- tests/unit/test_executor.py +172 -0
- tests/unit/test_executor_async.py +179 -0
- tests/unit/test_export.py +149 -0
- tests/unit/test_expressions.py +178 -0
- tests/unit/test_feature_brainstorm.py +194 -0
- tests/unit/test_format_prompt.py +145 -0
- tests/unit/test_generic_report.py +200 -0
- tests/unit/test_graph_commands.py +327 -0
- tests/unit/test_graph_linter.py +627 -0
- tests/unit/test_graph_loader.py +357 -0
- tests/unit/test_graph_schema.py +193 -0
- tests/unit/test_inline_schema.py +151 -0
- tests/unit/test_interrupt_node.py +182 -0
- tests/unit/test_issues.py +164 -0
- tests/unit/test_jinja2_prompts.py +85 -0
- tests/unit/test_json_extract.py +134 -0
- tests/unit/test_langsmith.py +600 -0
- tests/unit/test_langsmith_tools.py +204 -0
- tests/unit/test_llm_factory.py +109 -0
- tests/unit/test_llm_factory_async.py +118 -0
- tests/unit/test_loops.py +403 -0
- tests/unit/test_map_node.py +144 -0
- tests/unit/test_no_backward_compat.py +56 -0
- tests/unit/test_node_factory.py +348 -0
- tests/unit/test_passthrough_node.py +126 -0
- tests/unit/test_prompts.py +324 -0
- tests/unit/test_python_nodes.py +198 -0
- tests/unit/test_reliability.py +298 -0
- tests/unit/test_result_export.py +234 -0
- tests/unit/test_router.py +296 -0
- tests/unit/test_sanitize.py +99 -0
- tests/unit/test_schema_loader.py +295 -0
- tests/unit/test_shell_tools.py +229 -0
- tests/unit/test_state_builder.py +331 -0
- tests/unit/test_state_builder_map.py +104 -0
- tests/unit/test_state_config.py +197 -0
- tests/unit/test_streaming.py +307 -0
- tests/unit/test_subgraph.py +596 -0
- tests/unit/test_template.py +190 -0
- tests/unit/test_tool_call_integration.py +164 -0
- tests/unit/test_tool_call_node.py +178 -0
- tests/unit/test_tool_nodes.py +129 -0
- tests/unit/test_websearch.py +234 -0
- yamlgraph/__init__.py +35 -0
- yamlgraph/builder.py +110 -0
- yamlgraph/cli/__init__.py +159 -0
- yamlgraph/cli/__main__.py +6 -0
- yamlgraph/cli/commands.py +231 -0
- yamlgraph/cli/deprecation.py +92 -0
- yamlgraph/cli/graph_commands.py +541 -0
- yamlgraph/cli/validators.py +37 -0
- yamlgraph/config.py +67 -0
- yamlgraph/constants.py +70 -0
- yamlgraph/error_handlers.py +227 -0
- yamlgraph/executor.py +290 -0
- yamlgraph/executor_async.py +288 -0
- yamlgraph/graph_loader.py +451 -0
- yamlgraph/map_compiler.py +150 -0
- yamlgraph/models/__init__.py +36 -0
- yamlgraph/models/graph_schema.py +181 -0
- yamlgraph/models/schemas.py +124 -0
- yamlgraph/models/state_builder.py +236 -0
- yamlgraph/node_factory.py +768 -0
- yamlgraph/routing.py +87 -0
- yamlgraph/schema_loader.py +240 -0
- yamlgraph/storage/__init__.py +20 -0
- yamlgraph/storage/checkpointer.py +72 -0
- yamlgraph/storage/checkpointer_factory.py +123 -0
- yamlgraph/storage/database.py +320 -0
- yamlgraph/storage/export.py +269 -0
- yamlgraph/tools/__init__.py +1 -0
- yamlgraph/tools/agent.py +320 -0
- yamlgraph/tools/graph_linter.py +388 -0
- yamlgraph/tools/langsmith_tools.py +125 -0
- yamlgraph/tools/nodes.py +126 -0
- yamlgraph/tools/python_tool.py +179 -0
- yamlgraph/tools/shell.py +205 -0
- yamlgraph/tools/websearch.py +242 -0
- yamlgraph/utils/__init__.py +48 -0
- yamlgraph/utils/conditions.py +157 -0
- yamlgraph/utils/expressions.py +245 -0
- yamlgraph/utils/json_extract.py +104 -0
- yamlgraph/utils/langsmith.py +416 -0
- yamlgraph/utils/llm_factory.py +118 -0
- yamlgraph/utils/llm_factory_async.py +105 -0
- yamlgraph/utils/logging.py +104 -0
- yamlgraph/utils/prompts.py +171 -0
- yamlgraph/utils/sanitize.py +98 -0
- yamlgraph/utils/template.py +102 -0
- yamlgraph/utils/validators.py +181 -0
- yamlgraph-0.3.9.dist-info/METADATA +1105 -0
- yamlgraph-0.3.9.dist-info/RECORD +185 -0
- yamlgraph-0.3.9.dist-info/WHEEL +5 -0
- yamlgraph-0.3.9.dist-info/entry_points.txt +2 -0
- yamlgraph-0.3.9.dist-info/licenses/LICENSE +33 -0
- 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"])
|