yamlgraph 0.1.1__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.

Potentially problematic release.


This version of yamlgraph might be problematic. Click here for more details.

Files changed (111) hide show
  1. examples/__init__.py +1 -0
  2. examples/storyboard/__init__.py +1 -0
  3. examples/storyboard/generate_videos.py +335 -0
  4. examples/storyboard/nodes/__init__.py +10 -0
  5. examples/storyboard/nodes/animated_character_node.py +248 -0
  6. examples/storyboard/nodes/animated_image_node.py +138 -0
  7. examples/storyboard/nodes/character_node.py +162 -0
  8. examples/storyboard/nodes/image_node.py +118 -0
  9. examples/storyboard/nodes/replicate_tool.py +238 -0
  10. examples/storyboard/retry_images.py +118 -0
  11. tests/__init__.py +1 -0
  12. tests/conftest.py +178 -0
  13. tests/integration/__init__.py +1 -0
  14. tests/integration/test_animated_storyboard.py +63 -0
  15. tests/integration/test_cli_commands.py +242 -0
  16. tests/integration/test_map_demo.py +50 -0
  17. tests/integration/test_memory_demo.py +281 -0
  18. tests/integration/test_pipeline_flow.py +105 -0
  19. tests/integration/test_providers.py +163 -0
  20. tests/integration/test_resume.py +75 -0
  21. tests/unit/__init__.py +1 -0
  22. tests/unit/test_agent_nodes.py +200 -0
  23. tests/unit/test_checkpointer.py +212 -0
  24. tests/unit/test_cli.py +121 -0
  25. tests/unit/test_cli_package.py +81 -0
  26. tests/unit/test_compile_graph_map.py +132 -0
  27. tests/unit/test_conditions_routing.py +253 -0
  28. tests/unit/test_config.py +93 -0
  29. tests/unit/test_conversation_memory.py +270 -0
  30. tests/unit/test_database.py +145 -0
  31. tests/unit/test_deprecation.py +104 -0
  32. tests/unit/test_executor.py +60 -0
  33. tests/unit/test_executor_async.py +179 -0
  34. tests/unit/test_export.py +150 -0
  35. tests/unit/test_expressions.py +178 -0
  36. tests/unit/test_format_prompt.py +145 -0
  37. tests/unit/test_generic_report.py +200 -0
  38. tests/unit/test_graph_commands.py +327 -0
  39. tests/unit/test_graph_loader.py +299 -0
  40. tests/unit/test_graph_schema.py +193 -0
  41. tests/unit/test_inline_schema.py +151 -0
  42. tests/unit/test_issues.py +164 -0
  43. tests/unit/test_jinja2_prompts.py +85 -0
  44. tests/unit/test_langsmith.py +319 -0
  45. tests/unit/test_llm_factory.py +109 -0
  46. tests/unit/test_llm_factory_async.py +118 -0
  47. tests/unit/test_loops.py +403 -0
  48. tests/unit/test_map_node.py +144 -0
  49. tests/unit/test_no_backward_compat.py +56 -0
  50. tests/unit/test_node_factory.py +225 -0
  51. tests/unit/test_prompts.py +166 -0
  52. tests/unit/test_python_nodes.py +198 -0
  53. tests/unit/test_reliability.py +298 -0
  54. tests/unit/test_result_export.py +234 -0
  55. tests/unit/test_router.py +296 -0
  56. tests/unit/test_sanitize.py +99 -0
  57. tests/unit/test_schema_loader.py +295 -0
  58. tests/unit/test_shell_tools.py +229 -0
  59. tests/unit/test_state_builder.py +331 -0
  60. tests/unit/test_state_builder_map.py +104 -0
  61. tests/unit/test_state_config.py +197 -0
  62. tests/unit/test_template.py +190 -0
  63. tests/unit/test_tool_nodes.py +129 -0
  64. yamlgraph/__init__.py +35 -0
  65. yamlgraph/builder.py +110 -0
  66. yamlgraph/cli/__init__.py +139 -0
  67. yamlgraph/cli/__main__.py +6 -0
  68. yamlgraph/cli/commands.py +232 -0
  69. yamlgraph/cli/deprecation.py +92 -0
  70. yamlgraph/cli/graph_commands.py +382 -0
  71. yamlgraph/cli/validators.py +37 -0
  72. yamlgraph/config.py +67 -0
  73. yamlgraph/constants.py +66 -0
  74. yamlgraph/error_handlers.py +226 -0
  75. yamlgraph/executor.py +275 -0
  76. yamlgraph/executor_async.py +122 -0
  77. yamlgraph/graph_loader.py +337 -0
  78. yamlgraph/map_compiler.py +138 -0
  79. yamlgraph/models/__init__.py +36 -0
  80. yamlgraph/models/graph_schema.py +141 -0
  81. yamlgraph/models/schemas.py +124 -0
  82. yamlgraph/models/state_builder.py +236 -0
  83. yamlgraph/node_factory.py +240 -0
  84. yamlgraph/routing.py +87 -0
  85. yamlgraph/schema_loader.py +160 -0
  86. yamlgraph/storage/__init__.py +17 -0
  87. yamlgraph/storage/checkpointer.py +72 -0
  88. yamlgraph/storage/database.py +320 -0
  89. yamlgraph/storage/export.py +269 -0
  90. yamlgraph/tools/__init__.py +1 -0
  91. yamlgraph/tools/agent.py +235 -0
  92. yamlgraph/tools/nodes.py +124 -0
  93. yamlgraph/tools/python_tool.py +178 -0
  94. yamlgraph/tools/shell.py +205 -0
  95. yamlgraph/utils/__init__.py +47 -0
  96. yamlgraph/utils/conditions.py +157 -0
  97. yamlgraph/utils/expressions.py +111 -0
  98. yamlgraph/utils/langsmith.py +308 -0
  99. yamlgraph/utils/llm_factory.py +118 -0
  100. yamlgraph/utils/llm_factory_async.py +105 -0
  101. yamlgraph/utils/logging.py +127 -0
  102. yamlgraph/utils/prompts.py +116 -0
  103. yamlgraph/utils/sanitize.py +98 -0
  104. yamlgraph/utils/template.py +102 -0
  105. yamlgraph/utils/validators.py +181 -0
  106. yamlgraph-0.1.1.dist-info/METADATA +854 -0
  107. yamlgraph-0.1.1.dist-info/RECORD +111 -0
  108. yamlgraph-0.1.1.dist-info/WHEEL +5 -0
  109. yamlgraph-0.1.1.dist-info/entry_points.txt +2 -0
  110. yamlgraph-0.1.1.dist-info/licenses/LICENSE +21 -0
  111. yamlgraph-0.1.1.dist-info/top_level.txt +3 -0
@@ -0,0 +1,139 @@
1
+ """YamlGraph CLI - Command-line interface for yamlgraph.
2
+
3
+ This package provides the CLI entry point and command implementations.
4
+
5
+ Usage:
6
+ yamlgraph graph run graphs/yamlgraph.yaml --var topic="AI" --var style=casual
7
+ yamlgraph graph run graphs/router-demo.yaml --var message="hello"
8
+ yamlgraph graph list
9
+ yamlgraph list-runs
10
+ yamlgraph resume --thread-id abc123
11
+ yamlgraph trace --run-id <run-id>
12
+ """
13
+
14
+ import argparse
15
+
16
+ # Import submodules for package access
17
+ from yamlgraph.cli import commands, validators
18
+ from yamlgraph.cli.commands import (
19
+ cmd_export,
20
+ cmd_list_runs,
21
+ cmd_resume,
22
+ cmd_trace,
23
+ )
24
+
25
+ __all__ = [
26
+ # Submodules
27
+ "commands",
28
+ "validators",
29
+ # Entry points
30
+ "main",
31
+ "create_parser",
32
+ ]
33
+
34
+
35
+ def create_parser() -> argparse.ArgumentParser:
36
+ """Create and configure the CLI argument parser.
37
+
38
+ Returns:
39
+ Configured ArgumentParser for testing and main().
40
+ """
41
+ parser = argparse.ArgumentParser(
42
+ description="YAMLGraph - YAML-first LLM Pipeline Framework",
43
+ formatter_class=argparse.RawDescriptionHelpFormatter,
44
+ )
45
+
46
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
47
+
48
+ # List runs command
49
+ list_parser = subparsers.add_parser("list-runs", help="List recent runs")
50
+ list_parser.add_argument(
51
+ "--limit", "-l", type=int, default=10, help="Maximum runs to show"
52
+ )
53
+ list_parser.set_defaults(func=cmd_list_runs)
54
+
55
+ # Resume command
56
+ resume_parser = subparsers.add_parser("resume", help="Resume a pipeline")
57
+ resume_parser.add_argument(
58
+ "--thread-id", "-i", required=True, help="Thread ID to resume"
59
+ )
60
+ resume_parser.set_defaults(func=cmd_resume)
61
+
62
+ # Trace command
63
+ trace_parser = subparsers.add_parser("trace", help="Show execution trace")
64
+ trace_parser.add_argument(
65
+ "--run-id", "-r", help="Run ID (uses latest if not provided)"
66
+ )
67
+ trace_parser.add_argument(
68
+ "--verbose", "-v", action="store_true", help="Include timing details"
69
+ )
70
+ trace_parser.set_defaults(func=cmd_trace)
71
+
72
+ # Export command
73
+ export_parser = subparsers.add_parser("export", help="Export a run to JSON")
74
+ export_parser.add_argument(
75
+ "--thread-id", "-i", required=True, help="Thread ID to export"
76
+ )
77
+ export_parser.set_defaults(func=cmd_export)
78
+
79
+ # Graph command group (universal runner)
80
+ from yamlgraph.cli.graph_commands import cmd_graph_dispatch
81
+
82
+ graph_parser = subparsers.add_parser(
83
+ "graph", help="Universal graph runner and utilities"
84
+ )
85
+ graph_subparsers = graph_parser.add_subparsers(
86
+ dest="graph_command", help="Graph commands"
87
+ )
88
+
89
+ # graph run
90
+ graph_run_parser = graph_subparsers.add_parser("run", help="Run any graph")
91
+ graph_run_parser.add_argument("graph_path", help="Path to graph YAML file")
92
+ graph_run_parser.add_argument(
93
+ "--var",
94
+ "-v",
95
+ action="append",
96
+ default=[],
97
+ help="Set state variable (key=value), can repeat",
98
+ )
99
+ graph_run_parser.add_argument(
100
+ "--thread", "-t", type=str, default=None, help="Thread ID for persistence"
101
+ )
102
+ graph_run_parser.add_argument(
103
+ "--export", "-e", action="store_true", help="Export results to files"
104
+ )
105
+
106
+ # graph list
107
+ graph_subparsers.add_parser("list", help="List available graphs")
108
+
109
+ # graph info
110
+ graph_info_parser = graph_subparsers.add_parser(
111
+ "info", help="Show graph information"
112
+ )
113
+ graph_info_parser.add_argument("graph_path", help="Path to graph YAML file")
114
+
115
+ # graph validate
116
+ graph_validate_parser = graph_subparsers.add_parser(
117
+ "validate", help="Validate graph YAML schema"
118
+ )
119
+ graph_validate_parser.add_argument("graph_path", help="Path to graph YAML file")
120
+
121
+ graph_parser.set_defaults(func=cmd_graph_dispatch)
122
+
123
+ return parser
124
+
125
+
126
+ def main():
127
+ """Main CLI entry point."""
128
+ parser = create_parser()
129
+ args = parser.parse_args()
130
+
131
+ if not args.command:
132
+ parser.print_help()
133
+ return
134
+
135
+ args.func(args)
136
+
137
+
138
+ if __name__ == "__main__":
139
+ main()
@@ -0,0 +1,6 @@
1
+ """Allow running yamlgraph.cli as a module: python -m yamlgraph.cli"""
2
+
3
+ from yamlgraph.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,232 @@
1
+ """CLI command implementations.
2
+
3
+ Contains all cmd_* functions for CLI subcommands.
4
+ """
5
+
6
+ import sys
7
+ from argparse import Namespace
8
+ from typing import Any
9
+
10
+ from pydantic import BaseModel
11
+
12
+ from yamlgraph.cli.validators import validate_run_args
13
+
14
+ # Internal keys to skip when formatting results
15
+ _INTERNAL_KEYS = frozenset(
16
+ {
17
+ "_route",
18
+ "_loop_counts",
19
+ "thread_id",
20
+ "current_step",
21
+ "errors",
22
+ "topic",
23
+ "style",
24
+ "word_count",
25
+ }
26
+ )
27
+
28
+
29
+ def _format_value(value: Any, max_length: int = 200) -> str:
30
+ """Format a single value for display.
31
+
32
+ Args:
33
+ value: The value to format (str, list, Pydantic model, etc.)
34
+ max_length: Maximum length before truncation
35
+
36
+ Returns:
37
+ Formatted string representation
38
+ """
39
+ if isinstance(value, BaseModel):
40
+ # Format Pydantic model as key: value pairs
41
+ lines = []
42
+ for field_name, field_value in value.model_dump().items():
43
+ formatted = _format_value(field_value, max_length)
44
+ lines.append(f" {field_name}: {formatted}")
45
+ return "\n" + "\n".join(lines)
46
+
47
+ if isinstance(value, list):
48
+ # Format list items
49
+ if not value:
50
+ return "[]"
51
+ if len(value) <= 3:
52
+ return str(value)
53
+ return f"[{len(value)} items]"
54
+
55
+ if isinstance(value, str):
56
+ if len(value) > max_length:
57
+ return value[:max_length] + "..."
58
+ return value
59
+
60
+ return str(value)
61
+
62
+
63
+ def _format_result(result: dict[str, Any]) -> None:
64
+ """Format and print pipeline result generically.
65
+
66
+ Iterates over all non-internal keys in the result dict
67
+ and prints their values. Works with any Pydantic model.
68
+
69
+ Args:
70
+ result: Pipeline result dict with arbitrary Pydantic models
71
+ """
72
+ for key, value in result.items():
73
+ if key in _INTERNAL_KEYS or value is None:
74
+ continue
75
+
76
+ print(f"\nšŸ“ {key}:")
77
+ formatted = _format_value(value)
78
+ if formatted.startswith("\n"):
79
+ print(formatted)
80
+ else:
81
+ print(f" {formatted}")
82
+
83
+
84
+ def cmd_run(args: Namespace) -> None:
85
+ """Run the yamlgraph pipeline."""
86
+ if not validate_run_args(args):
87
+ sys.exit(1)
88
+
89
+ from yamlgraph.builder import run_pipeline
90
+ from yamlgraph.storage import YamlGraphDB, export_state
91
+ from yamlgraph.utils import get_run_url, is_tracing_enabled
92
+
93
+ print("\nšŸš€ Running yamlgraph pipeline")
94
+ print(f" Topic: {args.topic}")
95
+ print(f" Style: {args.style}")
96
+ print(f" Words: {args.word_count}")
97
+ print()
98
+
99
+ # Run pipeline
100
+ result = run_pipeline(
101
+ topic=args.topic,
102
+ style=args.style,
103
+ word_count=args.word_count,
104
+ )
105
+
106
+ # Save to database
107
+ db = YamlGraphDB()
108
+ db.save_state(result["thread_id"], result, status="completed")
109
+ print(f"\nšŸ’¾ Saved to database: thread_id={result['thread_id']}")
110
+
111
+ # Export to JSON
112
+ if args.export:
113
+ filepath = export_state(result)
114
+ print(f"šŸ“„ Exported to: {filepath}")
115
+
116
+ # Show results
117
+ print("\n" + "=" * 60)
118
+ print("RESULTS")
119
+ print("=" * 60)
120
+
121
+ _format_result(result)
122
+
123
+ # Show LangSmith link
124
+ if is_tracing_enabled():
125
+ if url := get_run_url():
126
+ print(f"\nšŸ”— LangSmith: {url}")
127
+
128
+ print()
129
+
130
+
131
+ def cmd_list_runs(args: Namespace) -> None:
132
+ """List recent pipeline runs."""
133
+ from yamlgraph.storage import YamlGraphDB
134
+
135
+ db = YamlGraphDB()
136
+ runs = db.list_runs(limit=args.limit)
137
+
138
+ if not runs:
139
+ print("No runs found.")
140
+ return
141
+
142
+ print(f"\nšŸ“‹ Recent runs ({len(runs)}):\n")
143
+ print(f"{'Thread ID':<12} {'Status':<12} {'Updated':<20}")
144
+ print("-" * 50)
145
+
146
+ for run in runs:
147
+ print(f"{run['thread_id']:<12} {run['status']:<12} {run['updated_at'][:19]}")
148
+
149
+ print()
150
+
151
+
152
+ def cmd_resume(args: Namespace) -> None:
153
+ """Resume a pipeline from saved state."""
154
+ from yamlgraph.builder import build_resume_graph
155
+ from yamlgraph.storage import YamlGraphDB
156
+
157
+ db = YamlGraphDB()
158
+ state = db.load_state(args.thread_id)
159
+
160
+ if not state:
161
+ print(f"āŒ No run found with thread_id: {args.thread_id}")
162
+ return
163
+
164
+ print(f"\nšŸ”„ Resuming from: {state.get('current_step', 'unknown')}")
165
+
166
+ # Check what's already completed
167
+ if state.get("final_summary"):
168
+ print("āœ… Pipeline already complete!")
169
+ return
170
+
171
+ # Show what will be skipped vs run
172
+ skipping = []
173
+ running = []
174
+ if state.get("generated"):
175
+ skipping.append("generate")
176
+ else:
177
+ running.append("generate")
178
+ if state.get("analysis"):
179
+ skipping.append("analyze")
180
+ else:
181
+ running.append("analyze")
182
+ running.append("summarize") # Always runs if we get here
183
+
184
+ if skipping:
185
+ print(f" Skipping: {', '.join(skipping)} (already in state)")
186
+ print(f" Running: {', '.join(running)}")
187
+
188
+ graph = build_resume_graph().compile()
189
+ result = graph.invoke(state)
190
+
191
+ # Save updated state
192
+ db.save_state(args.thread_id, result, status="completed")
193
+ print("\nāœ… Pipeline completed!")
194
+
195
+ if summary := result.get("final_summary"):
196
+ print(f"\nšŸ“Š Summary: {summary[:200]}...")
197
+
198
+
199
+ def cmd_trace(args: Namespace) -> None:
200
+ """Show execution trace for a run."""
201
+ from yamlgraph.utils import get_latest_run_id, get_run_url, print_run_tree
202
+
203
+ run_id = args.run_id or get_latest_run_id()
204
+
205
+ if not run_id:
206
+ print("āŒ No run ID provided and could not find latest run.")
207
+ return
208
+
209
+ print(f"\nšŸ“Š Execution trace for: {run_id}")
210
+ print("─" * 50)
211
+ print()
212
+ print_run_tree(run_id, verbose=args.verbose)
213
+
214
+ if url := get_run_url(run_id):
215
+ print(f"\nšŸ”— View in LangSmith: {url}")
216
+
217
+ print()
218
+
219
+
220
+ def cmd_export(args: Namespace) -> None:
221
+ """Export a run to JSON."""
222
+ from yamlgraph.storage import YamlGraphDB, export_state
223
+
224
+ db = YamlGraphDB()
225
+ state = db.load_state(args.thread_id)
226
+
227
+ if not state:
228
+ print(f"āŒ No run found with thread_id: {args.thread_id}")
229
+ return
230
+
231
+ filepath = export_state(state)
232
+ print(f"āœ… Exported to: {filepath}")
@@ -0,0 +1,92 @@
1
+ """Deprecation utilities for CLI commands.
2
+
3
+ Provides DeprecationError exception and helpers for migrating
4
+ old commands to the universal graph runner.
5
+
6
+ Term 'backward compatibility' signals refactoring need per project guidelines.
7
+
8
+ Example:
9
+ >>> from yamlgraph.cli.deprecation import DeprecationError, deprecated_command
10
+ >>>
11
+ >>> def cmd_old_command(args):
12
+ ... # Signal this command needs refactoring
13
+ ... deprecated_command("old", "graph run graphs/new.yaml --var x=y")
14
+ ...
15
+ >>> # Running raises:
16
+ >>> # DeprecationError: DEPRECATED: 'old' is deprecated.
17
+ >>> # Use 'graph run graphs/new.yaml --var x=y' instead.
18
+ """
19
+
20
+
21
+ class DeprecationError(Exception):
22
+ """Raised when a deprecated command is used.
23
+
24
+ Signals "Refactor me" - deprecated commands should be removed.
25
+
26
+ Attributes:
27
+ old_command: The deprecated command being used
28
+ new_command: The replacement command to use instead
29
+ """
30
+
31
+ def __init__(self, old_command: str, new_command: str):
32
+ self.old_command = old_command
33
+ self.new_command = new_command
34
+ message = (
35
+ f"DEPRECATED: '{old_command}' is deprecated. Use '{new_command}' instead."
36
+ )
37
+ super().__init__(message)
38
+
39
+
40
+ # Command mappings: old command -> graph path
41
+ COMMAND_MAPPINGS = {
42
+ "route": "graphs/router-demo.yaml",
43
+ "refine": "graphs/reflexion-demo.yaml",
44
+ "git-report": "graphs/git-report.yaml",
45
+ "memory-demo": "graphs/memory-demo.yaml",
46
+ }
47
+
48
+ # Variable mappings: old command -> arg name -> var name
49
+ ARG_MAPPINGS = {
50
+ "route": {"message": "message"},
51
+ "refine": {"topic": "topic"},
52
+ "git-report": {"query": "input"},
53
+ "memory-demo": {"input": "input"},
54
+ }
55
+
56
+
57
+ def get_replacement_command(old_command: str, args: dict[str, str]) -> str | None:
58
+ """Get the replacement graph run command.
59
+
60
+ Args:
61
+ old_command: The deprecated command name
62
+ args: Dict of argument values
63
+
64
+ Returns:
65
+ Replacement command string, or None if unknown command
66
+ """
67
+ graph_path = COMMAND_MAPPINGS.get(old_command)
68
+ if not graph_path:
69
+ return None
70
+
71
+ # Build --var arguments
72
+ var_args = []
73
+ arg_mapping = ARG_MAPPINGS.get(old_command, {})
74
+ for arg_name, var_name in arg_mapping.items():
75
+ if arg_name in args:
76
+ var_args.append(f"--var {var_name}={args[arg_name]}")
77
+
78
+ var_str = " ".join(var_args)
79
+ return f"graph run {graph_path} {var_str}".strip()
80
+
81
+
82
+ def deprecated_command(old_command: str, new_command: str) -> None:
83
+ """Raise DeprecationError for a deprecated command.
84
+
85
+ Args:
86
+ old_command: The deprecated command being used
87
+ new_command: The replacement command suggestion
88
+
89
+ Raises:
90
+ DeprecationError: Always raised to signal deprecation
91
+ """
92
+ raise DeprecationError(old_command, new_command)