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.
- examples/__init__.py +1 -0
- examples/storyboard/__init__.py +1 -0
- examples/storyboard/generate_videos.py +335 -0
- examples/storyboard/nodes/__init__.py +10 -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 +238 -0
- examples/storyboard/retry_images.py +118 -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_map_demo.py +50 -0
- tests/integration/test_memory_demo.py +281 -0
- tests/integration/test_pipeline_flow.py +105 -0
- tests/integration/test_providers.py +163 -0
- tests/integration/test_resume.py +75 -0
- tests/unit/__init__.py +1 -0
- tests/unit/test_agent_nodes.py +200 -0
- tests/unit/test_checkpointer.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 +270 -0
- tests/unit/test_database.py +145 -0
- tests/unit/test_deprecation.py +104 -0
- tests/unit/test_executor.py +60 -0
- tests/unit/test_executor_async.py +179 -0
- tests/unit/test_export.py +150 -0
- tests/unit/test_expressions.py +178 -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_loader.py +299 -0
- tests/unit/test_graph_schema.py +193 -0
- tests/unit/test_inline_schema.py +151 -0
- tests/unit/test_issues.py +164 -0
- tests/unit/test_jinja2_prompts.py +85 -0
- tests/unit/test_langsmith.py +319 -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 +225 -0
- tests/unit/test_prompts.py +166 -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_template.py +190 -0
- tests/unit/test_tool_nodes.py +129 -0
- yamlgraph/__init__.py +35 -0
- yamlgraph/builder.py +110 -0
- yamlgraph/cli/__init__.py +139 -0
- yamlgraph/cli/__main__.py +6 -0
- yamlgraph/cli/commands.py +232 -0
- yamlgraph/cli/deprecation.py +92 -0
- yamlgraph/cli/graph_commands.py +382 -0
- yamlgraph/cli/validators.py +37 -0
- yamlgraph/config.py +67 -0
- yamlgraph/constants.py +66 -0
- yamlgraph/error_handlers.py +226 -0
- yamlgraph/executor.py +275 -0
- yamlgraph/executor_async.py +122 -0
- yamlgraph/graph_loader.py +337 -0
- yamlgraph/map_compiler.py +138 -0
- yamlgraph/models/__init__.py +36 -0
- yamlgraph/models/graph_schema.py +141 -0
- yamlgraph/models/schemas.py +124 -0
- yamlgraph/models/state_builder.py +236 -0
- yamlgraph/node_factory.py +240 -0
- yamlgraph/routing.py +87 -0
- yamlgraph/schema_loader.py +160 -0
- yamlgraph/storage/__init__.py +17 -0
- yamlgraph/storage/checkpointer.py +72 -0
- yamlgraph/storage/database.py +320 -0
- yamlgraph/storage/export.py +269 -0
- yamlgraph/tools/__init__.py +1 -0
- yamlgraph/tools/agent.py +235 -0
- yamlgraph/tools/nodes.py +124 -0
- yamlgraph/tools/python_tool.py +178 -0
- yamlgraph/tools/shell.py +205 -0
- yamlgraph/utils/__init__.py +47 -0
- yamlgraph/utils/conditions.py +157 -0
- yamlgraph/utils/expressions.py +111 -0
- yamlgraph/utils/langsmith.py +308 -0
- yamlgraph/utils/llm_factory.py +118 -0
- yamlgraph/utils/llm_factory_async.py +105 -0
- yamlgraph/utils/logging.py +127 -0
- yamlgraph/utils/prompts.py +116 -0
- yamlgraph/utils/sanitize.py +98 -0
- yamlgraph/utils/template.py +102 -0
- yamlgraph/utils/validators.py +181 -0
- yamlgraph-0.1.1.dist-info/METADATA +854 -0
- yamlgraph-0.1.1.dist-info/RECORD +111 -0
- yamlgraph-0.1.1.dist-info/WHEEL +5 -0
- yamlgraph-0.1.1.dist-info/entry_points.txt +2 -0
- yamlgraph-0.1.1.dist-info/licenses/LICENSE +21 -0
- yamlgraph-0.1.1.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,854 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yamlgraph
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: YAML-first framework for building LLM pipelines with LangGraph
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: <3.14,>=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: langchain-anthropic>=0.3.0
|
|
10
|
+
Requires-Dist: langchain-mistralai>=0.2.0
|
|
11
|
+
Requires-Dist: langchain-openai>=0.3.0
|
|
12
|
+
Requires-Dist: langgraph>=0.2.0
|
|
13
|
+
Requires-Dist: langgraph-checkpoint-sqlite>=2.0.0
|
|
14
|
+
Requires-Dist: pydantic>=2.0.0
|
|
15
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
16
|
+
Requires-Dist: pyyaml>=6.0
|
|
17
|
+
Requires-Dist: langsmith>=0.1.0
|
|
18
|
+
Requires-Dist: jinja2>=3.1.0
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
21
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
23
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
24
|
+
Provides-Extra: storyboard
|
|
25
|
+
Requires-Dist: replicate>=0.25.0; extra == "storyboard"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# YamlGraph
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/yamlgraph/)
|
|
31
|
+
[](https://www.python.org/downloads/)
|
|
32
|
+
[](https://opensource.org/licenses/MIT)
|
|
33
|
+
|
|
34
|
+
A YAML-first framework for building LLM pipelines using:
|
|
35
|
+
|
|
36
|
+
- **YAML Graph Configuration** - Declarative pipeline definition with schema validation
|
|
37
|
+
- **YAML Prompts** - Declarative prompt templates with Jinja2 support
|
|
38
|
+
- **Pydantic Models** - Structured LLM outputs
|
|
39
|
+
- **Multi-Provider LLMs** - Support for Anthropic, Mistral, and OpenAI
|
|
40
|
+
- **LangGraph** - Pipeline orchestration with resume support
|
|
41
|
+
- **SQLite** - State persistence
|
|
42
|
+
- **LangSmith** - Observability and tracing
|
|
43
|
+
- **JSON Export** - Result serialization
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
### From PyPI
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install yamlgraph
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### From Source
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/sheikkinen/yamlgraph.git
|
|
57
|
+
cd yamlgraph
|
|
58
|
+
pip install -e ".[dev]"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### 1. Create a Prompt
|
|
64
|
+
|
|
65
|
+
Create `prompts/greet.yaml`:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
system: |
|
|
69
|
+
You are a friendly assistant.
|
|
70
|
+
|
|
71
|
+
user: |
|
|
72
|
+
Say hello to {name} in a {style} way.
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. Create a Graph
|
|
76
|
+
|
|
77
|
+
Create `graphs/hello.yaml`:
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
version: "1.0"
|
|
81
|
+
name: hello-world
|
|
82
|
+
|
|
83
|
+
nodes:
|
|
84
|
+
greet:
|
|
85
|
+
type: llm
|
|
86
|
+
prompt: greet
|
|
87
|
+
variables:
|
|
88
|
+
name: "{state.name}"
|
|
89
|
+
style: "{state.style}"
|
|
90
|
+
state_key: greeting
|
|
91
|
+
|
|
92
|
+
edges:
|
|
93
|
+
- from: START
|
|
94
|
+
to: greet
|
|
95
|
+
- from: greet
|
|
96
|
+
to: END
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 3. Set API Key
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
export ANTHROPIC_API_KEY=your-key-here
|
|
103
|
+
# Or: export MISTRAL_API_KEY=... or OPENAI_API_KEY=...
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 4. Run It
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
yamlgraph graph run graphs/hello.yaml --var name="World" --var style="enthusiastic"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Or use the Python API:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from yamlgraph.graph_loader import load_and_compile
|
|
116
|
+
|
|
117
|
+
graph = load_and_compile("graphs/hello.yaml")
|
|
118
|
+
app = graph.compile()
|
|
119
|
+
result = app.invoke({"name": "World", "style": "enthusiastic"})
|
|
120
|
+
print(result["greeting"])
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## More Examples
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Content generation pipeline
|
|
129
|
+
yamlgraph graph run graphs/yamlgraph.yaml --var topic="AI" --var style=casual
|
|
130
|
+
|
|
131
|
+
# Sentiment-based routing
|
|
132
|
+
yamlgraph graph run graphs/router-demo.yaml --var message="I love this!"
|
|
133
|
+
|
|
134
|
+
# Self-correction loop (Reflexion pattern)
|
|
135
|
+
yamlgraph graph run graphs/reflexion-demo.yaml --var topic="climate change"
|
|
136
|
+
|
|
137
|
+
# AI agent with shell tools
|
|
138
|
+
yamlgraph graph run graphs/git-report.yaml --var input="What changed recently?"
|
|
139
|
+
|
|
140
|
+
# Parallel fan-out with map nodes
|
|
141
|
+
yamlgraph graph run examples/storyboard/animated-character-graph.yaml \
|
|
142
|
+
--var concept="A brave mouse knight" --var model=hidream
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### CLI Utilities
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
yamlgraph graph list # List available graphs
|
|
149
|
+
yamlgraph graph info graphs/router-demo.yaml # Show graph structure
|
|
150
|
+
yamlgraph graph validate graphs/*.yaml # Validate graph schemas
|
|
151
|
+
yamlgraph list-runs # View recent runs
|
|
152
|
+
yamlgraph resume --thread-id abc123 # Resume a run
|
|
153
|
+
yamlgraph export --thread-id abc123 # Export run to JSON
|
|
154
|
+
|
|
155
|
+
# Observability (requires LangSmith)
|
|
156
|
+
yamlgraph trace --verbose # View execution trace
|
|
157
|
+
yamlgraph mermaid # Show pipeline as Mermaid diagram
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Documentation
|
|
161
|
+
|
|
162
|
+
See the [reference/](reference/) folder for comprehensive YAML configuration guides:
|
|
163
|
+
|
|
164
|
+
- [Quick Start](reference/quickstart.md) - Create your first pipeline in 5 minutes
|
|
165
|
+
- [Graph YAML Reference](reference/graph-yaml.md) - All graph configuration options
|
|
166
|
+
- [Prompt YAML Reference](reference/prompt-yaml.md) - Schema and template syntax
|
|
167
|
+
- [Map Nodes](reference/map-nodes.md) - Parallel fan-out/fan-in processing
|
|
168
|
+
- [Common Patterns](reference/patterns.md) - Router, loops, agents, and more
|
|
169
|
+
|
|
170
|
+
## Architecture
|
|
171
|
+
|
|
172
|
+
### Data Flow
|
|
173
|
+
|
|
174
|
+
```mermaid
|
|
175
|
+
flowchart TB
|
|
176
|
+
subgraph Input["📥 Input Layer"]
|
|
177
|
+
CLI["CLI Command"]
|
|
178
|
+
YAML_G["graphs/*.yaml"]
|
|
179
|
+
YAML_P["prompts/*.yaml"]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
subgraph Core["⚙️ Core Processing"]
|
|
183
|
+
GL["graph_loader.py<br/>YAML → StateGraph"]
|
|
184
|
+
NF["node_factory.py<br/>Create Node Functions"]
|
|
185
|
+
EH["error_handlers.py<br/>Skip/Retry/Fail/Fallback"]
|
|
186
|
+
EX["executor.py<br/>Prompt Execution"]
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
subgraph LLM["🤖 LLM Layer"]
|
|
190
|
+
LF["llm_factory.py"]
|
|
191
|
+
ANT["Anthropic"]
|
|
192
|
+
MIS["Mistral"]
|
|
193
|
+
OAI["OpenAI"]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
subgraph State["💾 State Layer"]
|
|
197
|
+
SB["state_builder.py<br/>Dynamic TypedDict"]
|
|
198
|
+
CP["checkpointer.py<br/>SQLite Persistence"]
|
|
199
|
+
DB[(SQLite DB)]
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
subgraph Output["📤 Output Layer"]
|
|
203
|
+
EXP["export.py"]
|
|
204
|
+
JSON["JSON Export"]
|
|
205
|
+
LS["LangSmith Traces"]
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
CLI --> GL
|
|
209
|
+
YAML_G --> GL
|
|
210
|
+
YAML_P --> EX
|
|
211
|
+
GL --> NF
|
|
212
|
+
NF --> EH
|
|
213
|
+
EH --> EX
|
|
214
|
+
EX --> LF
|
|
215
|
+
LF --> ANT & MIS & OAI
|
|
216
|
+
GL --> SB
|
|
217
|
+
SB --> CP
|
|
218
|
+
CP --> DB
|
|
219
|
+
EX --> EXP
|
|
220
|
+
EXP --> JSON
|
|
221
|
+
EX --> LS
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Directory Structure
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
yamlgraph/
|
|
228
|
+
├── README.md
|
|
229
|
+
├── pyproject.toml # Package definition with CLI entry point and dependencies
|
|
230
|
+
├── .env.sample # Environment template
|
|
231
|
+
│
|
|
232
|
+
├── graphs/ # YAML graph definitions
|
|
233
|
+
│ ├── yamlgraph.yaml # Main pipeline definition
|
|
234
|
+
│ ├── router-demo.yaml # Tone-based routing demo
|
|
235
|
+
│ ├── reflexion-demo.yaml # Self-refinement loop demo
|
|
236
|
+
│ └── git-report.yaml # AI agent demo with shell tools
|
|
237
|
+
│
|
|
238
|
+
├── yamlgraph/ # Main package
|
|
239
|
+
│ ├── __init__.py # Package exports
|
|
240
|
+
│ ├── builder.py # Graph builders (loads from YAML)
|
|
241
|
+
│ ├── graph_loader.py # YAML → LangGraph compiler
|
|
242
|
+
│ ├── config.py # Centralized configuration
|
|
243
|
+
│ ├── executor.py # YAML prompt executor
|
|
244
|
+
│ ├── cli.py # CLI commands
|
|
245
|
+
│ │
|
|
246
|
+
│ ├── models/ # Pydantic models
|
|
247
|
+
│ │ ├── __init__.py
|
|
248
|
+
│ │ ├── schemas.py # Framework schemas (ErrorType, PipelineError, GenericReport)
|
|
249
|
+
│ │ ├── state_builder.py # Dynamic state generation from YAML
|
|
250
|
+
│ │ └── graph_schema.py # Pydantic schema validation
|
|
251
|
+
│ │
|
|
252
|
+
│ ├── tools/ # Tool execution
|
|
253
|
+
│ │ ├── __init__.py
|
|
254
|
+
│ │ ├── shell.py # Shell command executor
|
|
255
|
+
│ │ ├── nodes.py # Tool node factory
|
|
256
|
+
│ │ └── agent.py # Agent node factory
|
|
257
|
+
│ │
|
|
258
|
+
│ ├── storage/ # Persistence layer
|
|
259
|
+
│ │ ├── __init__.py
|
|
260
|
+
│ │ ├── database.py # SQLite wrapper
|
|
261
|
+
│ │ └── export.py # JSON export
|
|
262
|
+
│ │
|
|
263
|
+
│ └── utils/ # Utilities
|
|
264
|
+
│ ├── __init__.py
|
|
265
|
+
│ ├── llm_factory.py # Multi-provider LLM creation
|
|
266
|
+
│ └── langsmith.py # Tracing helpers
|
|
267
|
+
│
|
|
268
|
+
├── prompts/ # YAML prompt templates
|
|
269
|
+
│ ├── greet.yaml
|
|
270
|
+
│ ├── analyze.yaml
|
|
271
|
+
│ ├── analyze_list.yaml # Jinja2 example with loops/filters
|
|
272
|
+
│ ├── generate.yaml
|
|
273
|
+
│ ├── summarize.yaml
|
|
274
|
+
│ └── router-demo/ # Tone routing prompts
|
|
275
|
+
│ ├── classify_tone.yaml
|
|
276
|
+
│ ├── respond_positive.yaml
|
|
277
|
+
│ ├── respond_negative.yaml
|
|
278
|
+
│ └── respond_neutral.yaml
|
|
279
|
+
│
|
|
280
|
+
├── reference/ # YAML configuration reference docs
|
|
281
|
+
│ ├── README.md # Overview and key concepts
|
|
282
|
+
│ ├── quickstart.md # 5-minute getting started guide
|
|
283
|
+
│ ├── graph-yaml.md # Graph YAML reference
|
|
284
|
+
│ ├── prompt-yaml.md # Prompt YAML reference
|
|
285
|
+
│ └── patterns.md # Common patterns and examples
|
|
286
|
+
│
|
|
287
|
+
├── tests/ # Test suite
|
|
288
|
+
│ ├── conftest.py # Shared fixtures
|
|
289
|
+
│ ├── unit/ # Unit tests
|
|
290
|
+
│ └── integration/ # Integration tests
|
|
291
|
+
│
|
|
292
|
+
└── outputs/ # Generated files (gitignored)
|
|
293
|
+
```
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Pipeline Flow
|
|
297
|
+
|
|
298
|
+
```mermaid
|
|
299
|
+
graph TD
|
|
300
|
+
A["📝 generate"] -->|content| B{should_continue}
|
|
301
|
+
B -->|"✓ content exists"| C["🔍 analyze"]
|
|
302
|
+
B -->|"✗ error/empty"| F["🛑 END"]
|
|
303
|
+
C -->|analysis| D["📊 summarize"]
|
|
304
|
+
D -->|final_summary| F
|
|
305
|
+
|
|
306
|
+
style A fill:#e1f5fe
|
|
307
|
+
style C fill:#fff3e0
|
|
308
|
+
style D fill:#e8f5e9
|
|
309
|
+
style F fill:#fce4ec
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Node Outputs
|
|
313
|
+
|
|
314
|
+
| Node | Output Type | Description |
|
|
315
|
+
|------|-------------|-------------|
|
|
316
|
+
| `generate` | Inline schema | Title, content, word_count, tags |
|
|
317
|
+
| `analyze` | Inline schema | Summary, key_points, sentiment, confidence |
|
|
318
|
+
| `summarize` | `str` | Final combined summary |
|
|
319
|
+
|
|
320
|
+
Output schemas are defined inline in YAML prompt files using the `schema:` block.
|
|
321
|
+
|
|
322
|
+
### Resume Flow
|
|
323
|
+
|
|
324
|
+
Pipelines can be resumed from any checkpoint. The resume behavior uses `skip_if_exists`:
|
|
325
|
+
nodes check if their output already exists in state and skip LLM calls if so.
|
|
326
|
+
|
|
327
|
+
```mermaid
|
|
328
|
+
graph LR
|
|
329
|
+
subgraph "Resume after 'analyze' completed"
|
|
330
|
+
A1["Load State"] --> B1["analyze (skipped)"] --> C1["summarize"] --> D1["END"]
|
|
331
|
+
end
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
# Resume an interrupted run
|
|
336
|
+
yamlgraph resume --thread-id abc123
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
When resumed:
|
|
340
|
+
- Nodes with existing outputs are **skipped** (no duplicate LLM calls)
|
|
341
|
+
- Only nodes without outputs in state actually run
|
|
342
|
+
- State is preserved via SQLite checkpointing
|
|
343
|
+
|
|
344
|
+
## Key Patterns
|
|
345
|
+
|
|
346
|
+
### 1. YAML Prompt Templates
|
|
347
|
+
|
|
348
|
+
**Simple Templating (Basic Substitution)**:
|
|
349
|
+
```yaml
|
|
350
|
+
# prompts/generate.yaml
|
|
351
|
+
system: |
|
|
352
|
+
You are a creative content writer...
|
|
353
|
+
|
|
354
|
+
user: |
|
|
355
|
+
Write about: {topic}
|
|
356
|
+
Target length: approximately {word_count} words
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Advanced Templating (Jinja2)**:
|
|
360
|
+
```yaml
|
|
361
|
+
# prompts/analyze_list.yaml
|
|
362
|
+
template: |
|
|
363
|
+
Analyze the following {{ items|length }} items:
|
|
364
|
+
|
|
365
|
+
{% for item in items %}
|
|
366
|
+
### {{ loop.index }}. {{ item.title }}
|
|
367
|
+
Topic: {{ item.topic }}
|
|
368
|
+
{% if item.tags %}
|
|
369
|
+
Tags: {{ item.tags | join(", ") }}
|
|
370
|
+
{% endif %}
|
|
371
|
+
{% endfor %}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Template Features**:
|
|
375
|
+
- **Auto-detection**: Uses Jinja2 if `{{` or `{%` present, otherwise simple formatting
|
|
376
|
+
- **Loops**: `{% for item in items %}...{% endfor %}`
|
|
377
|
+
- **Conditionals**: `{% if condition %}...{% endif %}`
|
|
378
|
+
- **Filters**: `{{ text[:50] }}`, `{{ items | join(", ") }}`, `{{ name | upper }}`
|
|
379
|
+
- **Backward compatible**: Existing `{variable}` prompts work unchanged
|
|
380
|
+
|
|
381
|
+
### 2. Structured Executor
|
|
382
|
+
|
|
383
|
+
```python
|
|
384
|
+
from yamlgraph.executor import execute_prompt
|
|
385
|
+
from yamlgraph.models import GenericReport
|
|
386
|
+
|
|
387
|
+
result = execute_prompt(
|
|
388
|
+
"generate",
|
|
389
|
+
variables={"topic": "AI", "word_count": 300},
|
|
390
|
+
output_model=GenericReport,
|
|
391
|
+
)
|
|
392
|
+
print(result.title) # Typed access!
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### 3. Multi-Provider LLM Support
|
|
396
|
+
|
|
397
|
+
```python
|
|
398
|
+
from yamlgraph.executor import execute_prompt
|
|
399
|
+
|
|
400
|
+
# Use default provider (Anthropic)
|
|
401
|
+
result = execute_prompt(
|
|
402
|
+
"greet",
|
|
403
|
+
variables={"name": "Alice", "style": "formal"},
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
# Switch to Mistral
|
|
407
|
+
result = execute_prompt(
|
|
408
|
+
"greet",
|
|
409
|
+
variables={"name": "Bob", "style": "casual"},
|
|
410
|
+
provider="mistral",
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
# Or set via environment variable
|
|
414
|
+
# PROVIDER=openai yamlgraph graph run ...
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Supported providers:
|
|
418
|
+
- **Anthropic** (default): Claude models
|
|
419
|
+
- **Mistral**: Mistral Large and other models
|
|
420
|
+
- **OpenAI**: GPT-4 and other models
|
|
421
|
+
|
|
422
|
+
Provider selection priority:
|
|
423
|
+
1. Function parameter: `execute_prompt(..., provider="mistral")`
|
|
424
|
+
2. YAML metadata: `provider: mistral` in prompt file
|
|
425
|
+
3. Environment variable: `PROVIDER=mistral`
|
|
426
|
+
4. Default: `anthropic`
|
|
427
|
+
|
|
428
|
+
### 4. YAML Graph Configuration
|
|
429
|
+
|
|
430
|
+
Pipelines are defined declaratively in YAML and compiled to LangGraph:
|
|
431
|
+
|
|
432
|
+
```yaml
|
|
433
|
+
# graphs/yamlgraph.yaml
|
|
434
|
+
version: "1.0"
|
|
435
|
+
name: yamlgraph-demo
|
|
436
|
+
description: Content generation pipeline
|
|
437
|
+
|
|
438
|
+
defaults:
|
|
439
|
+
provider: mistral
|
|
440
|
+
temperature: 0.7
|
|
441
|
+
|
|
442
|
+
nodes:
|
|
443
|
+
generate:
|
|
444
|
+
type: llm
|
|
445
|
+
prompt: generate
|
|
446
|
+
output_schema: # Inline schema - no Python model needed!
|
|
447
|
+
title: str
|
|
448
|
+
content: str
|
|
449
|
+
word_count: int
|
|
450
|
+
tags: list[str]
|
|
451
|
+
temperature: 0.8
|
|
452
|
+
variables:
|
|
453
|
+
topic: "{state.topic}"
|
|
454
|
+
word_count: "{state.word_count}"
|
|
455
|
+
style: "{state.style}"
|
|
456
|
+
state_key: generated
|
|
457
|
+
|
|
458
|
+
analyze:
|
|
459
|
+
type: llm
|
|
460
|
+
prompt: analyze
|
|
461
|
+
output_schema: # Inline schema
|
|
462
|
+
summary: str
|
|
463
|
+
key_points: list[str]
|
|
464
|
+
sentiment: str
|
|
465
|
+
confidence: float
|
|
466
|
+
temperature: 0.3
|
|
467
|
+
variables:
|
|
468
|
+
content: "{state.generated.content}"
|
|
469
|
+
state_key: analysis
|
|
470
|
+
requires: [generated]
|
|
471
|
+
|
|
472
|
+
summarize:
|
|
473
|
+
type: llm
|
|
474
|
+
prompt: summarize
|
|
475
|
+
temperature: 0.5
|
|
476
|
+
state_key: final_summary
|
|
477
|
+
requires: [generated, analysis]
|
|
478
|
+
|
|
479
|
+
edges:
|
|
480
|
+
- from: START
|
|
481
|
+
to: generate
|
|
482
|
+
- from: generate
|
|
483
|
+
to: analyze
|
|
484
|
+
condition: continue
|
|
485
|
+
- from: generate
|
|
486
|
+
to: END
|
|
487
|
+
condition: end
|
|
488
|
+
- from: analyze
|
|
489
|
+
to: summarize
|
|
490
|
+
- from: summarize
|
|
491
|
+
to: END
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
**Load and run**:
|
|
495
|
+
```python
|
|
496
|
+
from yamlgraph.builder import build_graph
|
|
497
|
+
|
|
498
|
+
graph = build_graph().compile() # Loads from graphs/yamlgraph.yaml
|
|
499
|
+
result = graph.invoke(initial_state)
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### 5. State Persistence
|
|
503
|
+
|
|
504
|
+
```python
|
|
505
|
+
from yamlgraph.storage import YamlGraphDB
|
|
506
|
+
|
|
507
|
+
db = YamlGraphDB()
|
|
508
|
+
db.save_state("thread-123", state)
|
|
509
|
+
state = db.load_state("thread-123")
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### 6. LangSmith Tracing
|
|
513
|
+
|
|
514
|
+
```python
|
|
515
|
+
from yamlgraph.utils.langsmith import print_run_tree
|
|
516
|
+
|
|
517
|
+
print_run_tree(verbose=True)
|
|
518
|
+
# 📊 Execution Tree:
|
|
519
|
+
# └─ yamlgraph_pipeline (12.3s) ✅
|
|
520
|
+
# ├─ generate (5.2s) ✅
|
|
521
|
+
# ├─ analyze (3.1s) ✅
|
|
522
|
+
# └─ summarize (4.0s) ✅
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### 7. Shell Tools & Agent Nodes
|
|
526
|
+
|
|
527
|
+
Define shell tools and let the LLM decide when to use them:
|
|
528
|
+
|
|
529
|
+
```yaml
|
|
530
|
+
# graphs/git-report.yaml
|
|
531
|
+
tools:
|
|
532
|
+
recent_commits:
|
|
533
|
+
type: shell
|
|
534
|
+
command: git log --oneline -n {count}
|
|
535
|
+
description: "List recent commits"
|
|
536
|
+
|
|
537
|
+
changed_files:
|
|
538
|
+
type: shell
|
|
539
|
+
command: git diff --name-only HEAD~{n}
|
|
540
|
+
description: "List files changed in last n commits"
|
|
541
|
+
|
|
542
|
+
nodes:
|
|
543
|
+
analyze:
|
|
544
|
+
type: agent # LLM decides which tools to call
|
|
545
|
+
prompt: git_analyst
|
|
546
|
+
tools: [recent_commits, changed_files]
|
|
547
|
+
max_iterations: 8
|
|
548
|
+
state_key: analysis
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Run the git analysis agent:
|
|
552
|
+
|
|
553
|
+
```bash
|
|
554
|
+
yamlgraph git-report -q "What changed recently?"
|
|
555
|
+
yamlgraph git-report -q "Summarize the test directory"
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
**Node types:**
|
|
559
|
+
- `type: llm` - Standard LLM call with structured output
|
|
560
|
+
- `type: router` - Classify and route to different paths
|
|
561
|
+
- `type: map` - Parallel fan-out over lists with `Send()`
|
|
562
|
+
- `type: python` - Execute custom Python functions
|
|
563
|
+
- `type: agent` - LLM loop that autonomously calls tools
|
|
564
|
+
|
|
565
|
+
## Environment Variables
|
|
566
|
+
|
|
567
|
+
| Variable | Required | Description |
|
|
568
|
+
|----------|----------|-------------|
|
|
569
|
+
| `ANTHROPIC_API_KEY` | Yes* | Anthropic API key (* if using Anthropic) |
|
|
570
|
+
| `MISTRAL_API_KEY` | No | Mistral API key (required if using Mistral) |
|
|
571
|
+
| `OPENAI_API_KEY` | No | OpenAI API key (required if using OpenAI) |
|
|
572
|
+
| `PROVIDER` | No | Default LLM provider (anthropic/mistral/openai) |
|
|
573
|
+
| `ANTHROPIC_MODEL` | No | Anthropic model (default: claude-sonnet-4-20250514) |
|
|
574
|
+
| `MISTRAL_MODEL` | No | Mistral model (default: mistral-large-latest) |
|
|
575
|
+
| `OPENAI_MODEL` | No | OpenAI model (default: gpt-4o) |
|
|
576
|
+
| `LANGCHAIN_TRACING` | No | Enable LangSmith tracing |
|
|
577
|
+
| `LANGCHAIN_API_KEY` | No | LangSmith API key |
|
|
578
|
+
| `LANGCHAIN_ENDPOINT` | No | LangSmith endpoint URL |
|
|
579
|
+
| `LANGCHAIN_PROJECT` | No | LangSmith project name |
|
|
580
|
+
|
|
581
|
+
## Testing
|
|
582
|
+
|
|
583
|
+
Run the test suite:
|
|
584
|
+
|
|
585
|
+
```bash
|
|
586
|
+
# Run all tests
|
|
587
|
+
pytest tests/ -v
|
|
588
|
+
|
|
589
|
+
# Run only unit tests
|
|
590
|
+
pytest tests/unit/ -v
|
|
591
|
+
|
|
592
|
+
# Run only integration tests
|
|
593
|
+
pytest tests/integration/ -v
|
|
594
|
+
|
|
595
|
+
# Run with coverage report
|
|
596
|
+
pytest tests/ --cov=yamlgraph --cov-report=term-missing
|
|
597
|
+
|
|
598
|
+
# Run with HTML coverage report
|
|
599
|
+
pytest tests/ --cov=yamlgraph --cov-report=html
|
|
600
|
+
# Then open htmlcov/index.html
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
**Current coverage**: 60% overall, 98% on graph_loader, 100% on builder/llm_factory.
|
|
604
|
+
|
|
605
|
+
## Extending the Pipeline
|
|
606
|
+
|
|
607
|
+
### Adding a New Node (YAML-First Approach)
|
|
608
|
+
|
|
609
|
+
Let's add a "fact_check" node that verifies generated content:
|
|
610
|
+
|
|
611
|
+
**Step 1: Define the output schema** (`yamlgraph/models/schemas.py`):
|
|
612
|
+
```python
|
|
613
|
+
class FactCheck(BaseModel):
|
|
614
|
+
"""Structured fact-checking output."""
|
|
615
|
+
|
|
616
|
+
claims: list[str] = Field(description="Claims identified in content")
|
|
617
|
+
verified: bool = Field(description="Whether claims are verifiable")
|
|
618
|
+
confidence: float = Field(ge=0.0, le=1.0, description="Verification confidence")
|
|
619
|
+
notes: str = Field(description="Additional context")
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**Step 2: Create the prompt** (`prompts/fact_check.yaml`):
|
|
623
|
+
```yaml
|
|
624
|
+
system: |
|
|
625
|
+
You are a fact-checker. Analyze the given content and identify
|
|
626
|
+
claims that can be verified. Assess the overall verifiability.
|
|
627
|
+
|
|
628
|
+
user: |
|
|
629
|
+
Content to fact-check:
|
|
630
|
+
{content}
|
|
631
|
+
|
|
632
|
+
Identify key claims and assess their verifiability.
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
**Step 3: State is auto-generated**
|
|
636
|
+
|
|
637
|
+
State fields are now generated automatically from your YAML graph config.
|
|
638
|
+
The `state_key` in your node config determines where output is stored:
|
|
639
|
+
```yaml
|
|
640
|
+
# Node output stored in state.fact_check automatically
|
|
641
|
+
fact_check:
|
|
642
|
+
type: llm
|
|
643
|
+
prompt: fact_check
|
|
644
|
+
state_key: fact_check # This creates the state field
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
**Step 4: Add the node to your graph** (`graphs/yamlgraph.yaml`):
|
|
648
|
+
```yaml
|
|
649
|
+
nodes:
|
|
650
|
+
generate:
|
|
651
|
+
type: prompt
|
|
652
|
+
prompt: generate
|
|
653
|
+
output_schema: # Inline schema - no Python model needed!
|
|
654
|
+
title: str
|
|
655
|
+
content: str
|
|
656
|
+
variables:
|
|
657
|
+
topic: topic
|
|
658
|
+
state_key: generated
|
|
659
|
+
|
|
660
|
+
fact_check: # ✨ New node - just YAML!
|
|
661
|
+
type: prompt
|
|
662
|
+
prompt: fact_check
|
|
663
|
+
output_schema: # Define schema inline
|
|
664
|
+
is_accurate: bool
|
|
665
|
+
issues: list[str]
|
|
666
|
+
requires: [generated]
|
|
667
|
+
variables:
|
|
668
|
+
content: generated.content
|
|
669
|
+
state_key: fact_check
|
|
670
|
+
|
|
671
|
+
analyze:
|
|
672
|
+
# ... existing config ...
|
|
673
|
+
|
|
674
|
+
edges:
|
|
675
|
+
- from: START
|
|
676
|
+
to: generate
|
|
677
|
+
- from: generate
|
|
678
|
+
to: fact_check
|
|
679
|
+
condition:
|
|
680
|
+
type: has_value
|
|
681
|
+
field: generated
|
|
682
|
+
- from: fact_check
|
|
683
|
+
to: analyze
|
|
684
|
+
# ... rest of edges ...
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
That's it! No Python node code needed. The graph loader dynamically generates the node function.
|
|
688
|
+
|
|
689
|
+
Resulting pipeline:
|
|
690
|
+
```mermaid
|
|
691
|
+
graph TD
|
|
692
|
+
A[generate] --> B{has generated?}
|
|
693
|
+
B -->|yes| C[fact_check]
|
|
694
|
+
C --> D[analyze]
|
|
695
|
+
D --> E[summarize]
|
|
696
|
+
E --> F[END]
|
|
697
|
+
B -->|no| F
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
### Adding Conditional Branching
|
|
701
|
+
|
|
702
|
+
Route to different nodes based on analysis results (all in YAML):
|
|
703
|
+
|
|
704
|
+
```yaml
|
|
705
|
+
edges:
|
|
706
|
+
- from: analyze
|
|
707
|
+
to: rewrite_node
|
|
708
|
+
condition:
|
|
709
|
+
type: field_equals
|
|
710
|
+
field: analysis.sentiment
|
|
711
|
+
value: negative
|
|
712
|
+
|
|
713
|
+
- from: analyze
|
|
714
|
+
to: enhance_node
|
|
715
|
+
condition:
|
|
716
|
+
type: field_equals
|
|
717
|
+
field: analysis.sentiment
|
|
718
|
+
value: positive
|
|
719
|
+
|
|
720
|
+
- from: analyze
|
|
721
|
+
to: summarize # Default fallback
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
### Add a New Prompt
|
|
725
|
+
|
|
726
|
+
1. Create `prompts/new_prompt.yaml`:
|
|
727
|
+
```yaml
|
|
728
|
+
system: Your system prompt...
|
|
729
|
+
user: Your user prompt with {variables}...
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
2. Call it:
|
|
733
|
+
```python
|
|
734
|
+
result = execute_prompt("new_prompt", variables={"var": "value"})
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### Add Structured Output
|
|
738
|
+
|
|
739
|
+
1. Define model in `yamlgraph/models/schemas.py`:
|
|
740
|
+
```python
|
|
741
|
+
class MyOutput(BaseModel):
|
|
742
|
+
field: str = Field(description="...")
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
2. Use with executor:
|
|
746
|
+
```python
|
|
747
|
+
result = execute_prompt("prompt", output_model=MyOutput)
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
## Known Issues & Future Improvements
|
|
751
|
+
|
|
752
|
+
This project demonstrates solid production patterns with declarative YAML-based configuration.
|
|
753
|
+
|
|
754
|
+
### Completed Features
|
|
755
|
+
|
|
756
|
+
| Feature | Status | Notes |
|
|
757
|
+
|---------|--------|-------|
|
|
758
|
+
| YAML Graph Configuration | ✅ | Declarative pipeline definition in `graphs/yamlgraph.yaml` |
|
|
759
|
+
| Jinja2 Templating | ✅ | Hybrid auto-detection (simple {var} + advanced Jinja2) |
|
|
760
|
+
| Multi-Provider LLMs | ✅ | Factory pattern supporting Anthropic/Mistral/OpenAI |
|
|
761
|
+
| Dynamic Node Generation | ✅ | Nodes compiled from YAML at runtime |
|
|
762
|
+
|
|
763
|
+
### Implemented Patterns
|
|
764
|
+
|
|
765
|
+
| Feature | Status | Notes |
|
|
766
|
+
|---------|--------|-------|
|
|
767
|
+
| Branching/Routing | ✅ | `type: router` for LLM-based conditional routing |
|
|
768
|
+
| Self-Correction Loops | ✅ | Reflexion pattern with critique → refine cycles |
|
|
769
|
+
| Tool/Agent Patterns | ✅ | Shell tools + agent nodes with LangChain tool binding |
|
|
770
|
+
| Per-Node Error Handling | ✅ | `on_error: skip/retry/fail/fallback` |
|
|
771
|
+
| Conversation Memory | ✅ | Message accumulation via `AgentState.messages` |
|
|
772
|
+
| Native Checkpointing | ✅ | `SqliteSaver` from `langgraph-checkpoint-sqlite` |
|
|
773
|
+
| State Export | ✅ | JSON/Markdown export with `export_result()` |
|
|
774
|
+
| LangSmith Share Links | ✅ | Auto-generate public trace URLs after runs |
|
|
775
|
+
|
|
776
|
+
### Missing LangGraph Features
|
|
777
|
+
|
|
778
|
+
| Feature | Status | Notes |
|
|
779
|
+
|---------|--------|-------|
|
|
780
|
+
| Fan-out/Fan-in | ✅ | `type: map` with `Send()` for item-level parallelism |
|
|
781
|
+
| Human-in-the-Loop | ❌ | No `interrupt_before` / `interrupt_after` demonstration |
|
|
782
|
+
| Streaming | ❌ | No streaming output support |
|
|
783
|
+
| Sub-graphs | ❌ | No nested graph composition |
|
|
784
|
+
|
|
785
|
+
### Potential Enhancements
|
|
786
|
+
|
|
787
|
+
#### Short-term (Quick Wins)
|
|
788
|
+
1. **Add `in` operator to conditions** - Support `status in ["done", "complete"]` expressions
|
|
789
|
+
2. **Document agent `max_iterations`** - Expose in YAML schema for agent nodes
|
|
790
|
+
3. **Add `--dry-run` flag** - Validate graph without execution
|
|
791
|
+
|
|
792
|
+
#### Medium-term (Feature Improvements)
|
|
793
|
+
4. **Async map node execution** - Use `asyncio.gather()` for parallel branches
|
|
794
|
+
5. **State field collision warnings** - Log when YAML fields override base fields
|
|
795
|
+
6. **Map node error aggregation** - Summary with success/failure counts per branch
|
|
796
|
+
7. **Add streaming** - `--stream` CLI flag for real-time output
|
|
797
|
+
|
|
798
|
+
#### Long-term (Architecture)
|
|
799
|
+
8. **Plugin system** - Custom node types via entry points
|
|
800
|
+
9. **Hot-reload for development** - File watcher for prompt/graph YAML changes
|
|
801
|
+
10. **OpenTelemetry integration** - Complement LangSmith with standard observability
|
|
802
|
+
11. **Sub-graphs** - Nested graph composition for complex workflows
|
|
803
|
+
12. **Human-in-the-loop** - `interrupt_before` / `interrupt_after` demonstration
|
|
804
|
+
|
|
805
|
+
## Security
|
|
806
|
+
|
|
807
|
+
### Shell Command Injection Protection
|
|
808
|
+
|
|
809
|
+
Shell tools (defined in `graphs/*.yaml` with `type: tool`) execute commands with variable substitution. All user-provided variable values are sanitized using `shlex.quote()` to prevent shell injection attacks.
|
|
810
|
+
|
|
811
|
+
```yaml
|
|
812
|
+
# In graph YAML - command template is trusted
|
|
813
|
+
tools:
|
|
814
|
+
git_log:
|
|
815
|
+
type: shell
|
|
816
|
+
command: "git log --author={author} -n {count}"
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
**Security model:**
|
|
820
|
+
- ✅ **Command templates** (from YAML) are trusted configuration
|
|
821
|
+
- ✅ **Variable values** (from user input/LLM) are escaped with `shlex.quote()`
|
|
822
|
+
- ✅ **Complex types** (lists, dicts) are JSON-serialized then quoted
|
|
823
|
+
- ✅ **No `eval()`** - condition expressions parsed with regex, not evaluated
|
|
824
|
+
|
|
825
|
+
**Example protection:**
|
|
826
|
+
```python
|
|
827
|
+
# Malicious input is safely escaped
|
|
828
|
+
variables = {"author": "$(rm -rf /)"}
|
|
829
|
+
# Executed as: git log --author='$(rm -rf /)' (quoted, harmless)
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
See [yamlgraph/tools/shell.py](yamlgraph/tools/shell.py) for implementation details.
|
|
833
|
+
|
|
834
|
+
### ⚠️ Security Considerations
|
|
835
|
+
|
|
836
|
+
**Shell tools execute real commands** on your system. While variables are sanitized:
|
|
837
|
+
|
|
838
|
+
1. **Command templates are trusted** - Only use shell tools from trusted YAML configs
|
|
839
|
+
2. **No sandboxing** - Commands run with your user permissions
|
|
840
|
+
3. **Agent autonomy** - Agent nodes may call tools unpredictably
|
|
841
|
+
4. **Review tool definitions** - Audit `tools:` section in graph YAML before running
|
|
842
|
+
|
|
843
|
+
For production deployments, consider:
|
|
844
|
+
- Running in a container with limited permissions
|
|
845
|
+
- Restricting available tools to read-only operations
|
|
846
|
+
- Implementing approval workflows for sensitive operations
|
|
847
|
+
|
|
848
|
+
## License
|
|
849
|
+
|
|
850
|
+
MIT
|
|
851
|
+
|
|
852
|
+
## Remember
|
|
853
|
+
|
|
854
|
+
Prompts in yaml templates, graphs in yaml, shared executor, pydantic, data stored in sqlite, langgraph, langsmith, venv, tdd red-green-refactor, modules < 400 lines, kiss
|