py-meta-agent 0.1.0__tar.gz

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 (30) hide show
  1. py_meta_agent-0.1.0/PKG-INFO +529 -0
  2. py_meta_agent-0.1.0/README.md +515 -0
  3. py_meta_agent-0.1.0/pyproject.toml +32 -0
  4. py_meta_agent-0.1.0/setup.cfg +4 -0
  5. py_meta_agent-0.1.0/src/meta_agent_madcoderme/__init__.py +88 -0
  6. py_meta_agent-0.1.0/src/meta_agent_madcoderme/builder.py +832 -0
  7. py_meta_agent-0.1.0/src/meta_agent_madcoderme/compiler.py +134 -0
  8. py_meta_agent-0.1.0/src/meta_agent_madcoderme/evaluators.py +25 -0
  9. py_meta_agent-0.1.0/src/meta_agent_madcoderme/graph.py +604 -0
  10. py_meta_agent-0.1.0/src/meta_agent_madcoderme/memory.py +137 -0
  11. py_meta_agent-0.1.0/src/meta_agent_madcoderme/nodes.py +161 -0
  12. py_meta_agent-0.1.0/src/meta_agent_madcoderme/optimization.py +340 -0
  13. py_meta_agent-0.1.0/src/meta_agent_madcoderme/resilience.py +349 -0
  14. py_meta_agent-0.1.0/src/meta_agent_madcoderme/state.py +97 -0
  15. py_meta_agent-0.1.0/src/meta_agent_madcoderme/tools.py +76 -0
  16. py_meta_agent-0.1.0/src/meta_agent_madcoderme/utils.py +282 -0
  17. py_meta_agent-0.1.0/src/py_meta_agent.egg-info/PKG-INFO +529 -0
  18. py_meta_agent-0.1.0/src/py_meta_agent.egg-info/SOURCES.txt +28 -0
  19. py_meta_agent-0.1.0/src/py_meta_agent.egg-info/dependency_links.txt +1 -0
  20. py_meta_agent-0.1.0/src/py_meta_agent.egg-info/entry_points.txt +3 -0
  21. py_meta_agent-0.1.0/src/py_meta_agent.egg-info/requires.txt +7 -0
  22. py_meta_agent-0.1.0/src/py_meta_agent.egg-info/top_level.txt +1 -0
  23. py_meta_agent-0.1.0/tests/test_compiler_config.py +71 -0
  24. py_meta_agent-0.1.0/tests/test_context_policy.py +36 -0
  25. py_meta_agent-0.1.0/tests/test_evaluator_conditions.py +42 -0
  26. py_meta_agent-0.1.0/tests/test_graph.py +205 -0
  27. py_meta_agent-0.1.0/tests/test_phase1.py +161 -0
  28. py_meta_agent-0.1.0/tests/test_phase3.py +334 -0
  29. py_meta_agent-0.1.0/tests/test_phase4_5.py +363 -0
  30. py_meta_agent-0.1.0/tests/test_prompt_rendering.py +61 -0
@@ -0,0 +1,529 @@
1
+ Metadata-Version: 2.4
2
+ Name: py-meta-agent
3
+ Version: 0.1.0
4
+ Summary: A YAML-defined agent network compiler built on LangGraph.
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: jinja2>=3.1
8
+ Requires-Dist: langchain>=1.0
9
+ Requires-Dist: langchain-core>=1.0
10
+ Requires-Dist: langchain-google-genai>=4.0
11
+ Requires-Dist: langgraph>=1.0
12
+ Requires-Dist: pydantic>=2.0
13
+ Requires-Dist: pyyaml>=6.0
14
+
15
+ # Meta Agent
16
+
17
+ Meta Agent enables people and AI agents to build production-grade agent harnesses and multi-agent networks simply by defining a high-level design in YAML (`*.agent.yaml`). At its core is a **compiler** (`compiler.py` / `graph.py`) that turns a declarative `schema_version: "1.0"` specification directly into an executable [LangGraph](https://github.com/langchain-ai/langgraph) `StateGraph`—wiring typed state and reducers, parallel swarm fan-out (`Send`), sequential consumers, human-in-the-loop interrupts, AST-evaluated routing, context policies, resilience, trace-driven self-healing with graph versioning, evaluators, and guarded harness optimization without hand-coding boilerplate orchestration.
18
+
19
+ For rapid scaffolding from natural language, Meta Agent also provides a CLI builder (`builder.py`) that generates a valid `.agent.yaml` file from a high-level description using an LLM, ready to be compiled and executed immediately.
20
+
21
+ ---
22
+
23
+ ## Requirements
24
+
25
+ - **Python**: `>= 3.11`
26
+ - **Dependencies** (declared in `pyproject.toml`):
27
+ - `langgraph>=1.0`
28
+ - `langchain>=1.0`
29
+ - `langchain-core>=1.0`
30
+ - `langchain-google-genai>=4.0`
31
+ - `pydantic>=2.0`
32
+ - `pyyaml>=6.0`
33
+ - `jinja2>=3.1`
34
+ - **API Keys**: A `GEMINI_API_KEY` (or credentials for your configured LLM provider) when executing live model calls.
35
+
36
+ ---
37
+
38
+ ## Installation & Setup
39
+
40
+ ```bash
41
+ python -m venv .venv
42
+ source .venv/bin/activate
43
+ python -m pip install -e .
44
+ cp .env.example .env
45
+ ```
46
+
47
+ Edit `.env` and set `GEMINI_API_KEY` (and optional tool credentials such as `JOB_API_KEY` or `APPLICATION_API_KEY` if your tools use `auth.env`).
48
+
49
+ ---
50
+
51
+ ## Usage: High-Level Design → Compile → Run
52
+
53
+ ### 1. Generate a High-Level `.agent.yaml` Design & Companion Modules (`builder.py`)
54
+
55
+ If you want to scaffold a complete, execution-ready network from high-level requirements instead of authoring YAML and boilerplate Python by hand, use `builder.py` (or the installed `meta-agent-build` console script). It calls a configured LLM to generate and validate the `.agent.yaml` file, then generates and syntax-checks the companion Python modules (`data_models.py`, `tool_defs.py`, and custom `hooks.py` / `evaluators.py` if referenced) in the same directory:
56
+
57
+ ```bash
58
+ # Single-command generation of .agent.yaml + companion Python modules
59
+ meta-agent-build "Build a customer support triage network with intake, specialist swarm analysis, human approval, and reply delivery" \
60
+ --name "Support Triage" \
61
+ --agents "intake_agent,specialist_agent,reply_agent" \
62
+ --features "swarm,human_in_the_loop,self_healing,optimization" \
63
+ -o support/support_triage.agent.yaml
64
+
65
+ # Or interactive wizard mode (prompts for name, goal, agents, tools, features, output file)
66
+ python -m meta_agent_madcoderme.builder -i
67
+ ```
68
+
69
+ Pass `--yaml-only` if you only want the `.agent.yaml` file without generating companion Python modules. You can also choose the builder LLM with `--provider`, `--model`, and `--temperature` (defaults: `google-genai`, `gemini-3.6-flash`, `0.2`), or call `meta_agent_madcoderme.generate_agent_yaml(spec)` and `meta_agent_madcoderme.generate_companion_modules(yaml_text, spec)` from Python.
70
+
71
+ ### 2. Compile & Run a Network (`compiler.py`)
72
+
73
+ The compiler (`meta_agent_madcoderme.compiler` / `meta-agent`) is the core entrypoint. Pass any `.agent.yaml` file as a positional argument:
74
+
75
+ ```bash
76
+ meta-agent tests/jobsearch.agent.yaml
77
+ # or directly:
78
+ python -m meta_agent_madcoderme.compiler tests/jobsearch.agent.yaml
79
+ ```
80
+
81
+ The compiler:
82
+ - Validates that the file path ends with `.agent.yaml` and exists on disk.
83
+ - Compiles typed state, tools, agents (with inherited `harness_defaults`), memory stores, resilience/self-healing wrappers, evaluators, and the LangGraph `StateGraph`.
84
+ - Prints the compiled ASCII graph (`print_ascii()`).
85
+ - Executes the workflow and prompts interactively whenever a human-in-the-loop `__interrupt__` is reached, then prints the final execution trace.
86
+
87
+ ### 3. Using the Compiler Programmatically
88
+
89
+ You can embed the compiler directly in any Python application to load high-level `.agent.yaml` designs and run them:
90
+
91
+ ```python
92
+ import yaml
93
+ from langgraph.types import Command
94
+
95
+ from meta_agent_madcoderme import (
96
+ create_states,
97
+ create_tools,
98
+ create_agents,
99
+ create_memory,
100
+ create_graph,
101
+ )
102
+
103
+ with open("path/to/network.agent.yaml", "r", encoding="utf-8") as f:
104
+ cfg = yaml.safe_load(f)
105
+
106
+ modules = cfg.get("modules", {})
107
+ runtime_cfg = cfg.get("runtime", {})
108
+ memory_cfg = runtime_cfg.get("memory")
109
+
110
+ # 1. Build typed state, tools, agents, and memory backends
111
+ network_state = create_states(cfg.get("state", {}), modules_config=modules)
112
+ tools = create_tools(cfg.get("tools", {}), modules_config=modules)
113
+ agents = create_agents(
114
+ cfg.get("agents", {}),
115
+ tools,
116
+ harness_defaults=cfg.get("harness_defaults", {}),
117
+ )
118
+ checkpointer, store = create_memory(memory_cfg)
119
+
120
+ # 2. Compile the LangGraph StateGraph
121
+ workflow = create_graph(
122
+ network_state=network_state,
123
+ agent_list=agents,
124
+ network_nodes=cfg.get("nodes", []),
125
+ memory_config=memory_cfg,
126
+ tools=tools,
127
+ entry_node=cfg.get("entry_node"),
128
+ runtime_config=runtime_cfg,
129
+ evaluator_config=cfg.get("evaluators", {}),
130
+ modules_config=modules,
131
+ )
132
+ compiled = workflow.compile(checkpointer=checkpointer, store=store)
133
+
134
+ # 3. Invoke with initial state and thread config
135
+ run_config = {"configurable": {"thread_id": "session-1"}}
136
+ initial_state = {
137
+ k: [] if k == "messages" else None
138
+ for k in network_state.__annotations__.keys()
139
+ }
140
+ initial_state["resume_file"] = "/path/to/resume.txt"
141
+
142
+ result = compiled.invoke(initial_state, run_config)
143
+
144
+ # 4. Handle human-in-the-loop interrupts
145
+ while "__interrupt__" in result:
146
+ prompt_info = result["__interrupt__"][0].value
147
+ user_reply = input(f"{prompt_info.get('prompt')}: ")
148
+ result = compiled.invoke(Command(resume=user_reply), config=run_config)
149
+ ```
150
+
151
+ ### 4. Standalone Subsystem Modules
152
+
153
+ - **`builder.py`**: `generate_agent_yaml(spec, ...)`, `validate_agent_yaml(yaml_text)`, `ensure_agent_yaml_path(path_str)`, and CLI `main()`.
154
+ - **`resilience.py`**: `retry_call(operation, config)`, `TokenBucketRateLimiter(calls, per_seconds)`, `GraphRegistry`, `GraphVersion`, `apply_graph_patch`, and `SelfHealer`.
155
+ - **`optimization.py`**: `HarnessRegistry`, `Window`, `optimize(...)`, `apply_optimization(...)`, `validate_optimization(spec)`, `target_met`, and `STRATEGIES`.
156
+ - **`memory.py`**: `create_memory(memory_config)` and `apply_context_policy(messages, context_policy, llm)`.
157
+ - **`evaluators.py`**: Built-in `schema_validator`, `grounding_checker`, and `user_feedback`.
158
+ - **`utils.py`**: `resolve_module`, `deep_merge`, `apply_pre_filters`, `extract_input_context`, `render_input_prompt`, `ConfigResolver`, and `evaluate_condition`.
159
+
160
+ ---
161
+
162
+ ## Project Layout
163
+
164
+ ```text
165
+ meta-agent/
166
+ ├── src/meta_agent_madcoderme/
167
+ │ ├── __init__.py # Public package exports & __version__
168
+ │ ├── compiler.py # Core CLI compiler & end-to-end .agent.yaml runner
169
+ │ ├── builder.py # LLM-powered CLI generator for .agent.yaml + companion Python modules
170
+ │ ├── graph.py # StateGraph builder, node executors, swarm Send fan-out, routing, error/healer/eval wrappers
171
+ │ ├── state.py # TypedDict state builder, built-in reducers (append, dedupe_by_id), scope metadata
172
+ │ ├── nodes.py # NetworkAgent class, harness merging, LLM/React agent initialization, human input nodes
173
+ │ ├── tools.py # Dynamic tool loader, Pydantic args_schema binding, rate limiting, auth env injection
174
+ │ ├── memory.py # Checkpointer/Store factory (in_memory, postgres) & context window management
175
+ │ ├── resilience.py # Retries, TokenBucketRateLimiter, trace-reading SelfHealer & versioned GraphRegistry
176
+ │ ├── evaluators.py # Built-in automatic & human-feedback evaluators
177
+ │ ├── optimization.py # Metric target parsing, mutation strategies, HarnessRegistry, guarded optimize()
178
+ │ └── utils.py # Module resolver, deep_merge, pre-filters, Jinja2 prompt rendering, AST condition evaluator
179
+ ├── examples/
180
+ │ └── basic_graph.py # Minimal LangGraph sanity example
181
+ ├── tests/
182
+ │ ├── jobsearch.agent.yaml
183
+ │ ├── data_models.py
184
+ │ ├── tool_defs.py
185
+ │ └── test_*.py # Unit test suite
186
+ ├── pyproject.toml
187
+ └── .env.example
188
+ ```
189
+
190
+ ---
191
+
192
+ ## YAML Agent Definition Schema (`schema_version: "1.0"`)
193
+
194
+ A complete reference example is available in [tests/jobsearch.agent.yaml](tests/jobsearch.agent.yaml). Below is the full supported schema specification.
195
+
196
+ ### Top-Level Keys
197
+
198
+ | Key | Type | Required | Description |
199
+ | :--- | :--- | :--- | :--- |
200
+ | `schema_version` | `string` | Yes | Schema version identifier (e.g. `"1.0"`). |
201
+ | `name` | `string` | No | Human-readable network name. |
202
+ | `description` | `string` | No | Description of the agent network. |
203
+ | `entry_node` | `string` | Yes* | ID of the starting node in `nodes`. (*Or set `is_starting_node: true` on a node.) |
204
+ | `modules` | `object` | No | Mapping of logical module roles to Python module names. |
205
+ | `runtime` | `object` | No | Global memory, resilience, and self-improvement policies. |
206
+ | `harness_defaults` | `object` | No | Default agent harness configuration inherited by all agents via deep merge. |
207
+ | `state` | `object` | Yes | Shared and worker-scoped state field definitions. |
208
+ | `tools` | `object` | No | Tool definitions bound to agents by name. |
209
+ | `evaluators` | `object` | No | Evaluator definitions referenced by agent `optimization` blocks. |
210
+ | `agents` | `object` | Yes | Agent definitions (models, harnesses, I/O mappings, retries, optimization). |
211
+ | `nodes` | `list` | Yes | Graph nodes, execution types, iterators, and routing rules. |
212
+
213
+ ---
214
+
215
+ ### `modules`
216
+
217
+ Maps logical roles to Python modules. `utils.resolve_module()` searches standard imports, `tests.<name>`, and `<name>.py` in the working directory or `tests/`.
218
+
219
+ ```yaml
220
+ modules:
221
+ data_models: data_models # Pydantic models referenced by state & tool input_schema
222
+ tools: tool_defs # Callable tool implementations
223
+ evaluators: evaluators # Evaluator functions
224
+ hooks: hooks # Optional custom state reducer functions
225
+ ```
226
+
227
+ ---
228
+
229
+ ### `runtime`
230
+
231
+ Global runtime policies for memory, resilience, and guarded self-improvement.
232
+
233
+ ```yaml
234
+ runtime:
235
+ memory:
236
+ stores:
237
+ short_term:
238
+ type: in_memory # in_memory | postgres
239
+ connection_string: "" # Required when type: postgres
240
+ scope: run
241
+ long_term:
242
+ type: in_memory # in_memory | postgres
243
+ connection_string: "" # Required when type: postgres
244
+ scope: global
245
+ namespaces: [successful_parses, accepted_suggestions, feedback]
246
+
247
+ resilience:
248
+ default_retry:
249
+ max_attempts: 3
250
+ backoff: exponential
251
+ initial_delay_s: 1
252
+ max_delay_s: 30
253
+ jitter: 0.0
254
+ retry_on: [timeout, rate_limit, transient_error, schema_violation]
255
+ default_timeout_s: 60
256
+ on_unhandled_error: route_to_healer
257
+
258
+ self_healing:
259
+ enabled: true
260
+ endpoint: "/heal"
261
+ healer_model:
262
+ provider: google-genai
263
+ model: gemini-3.6-pro
264
+ temperature: 0.1
265
+ system_instruction: "Inspect the execution trace and node error, then output a minimal JSON graph patch to recover."
266
+ versioning:
267
+ enabled: true
268
+ max_versions: 10
269
+ auto_rollback_on_failure: true
270
+ strategies: [retry, fallback_harness, fallback_model, modify_graph, escalate_to_human]
271
+ max_heal_attempts_per_node: 2
272
+
273
+ self_improving:
274
+ enabled: true
275
+ guardrails:
276
+ require_versioning: true
277
+ auto_rollback_on_regression: true
278
+ regression_tolerance: 0.0
279
+ max_mutation_depth: 3
280
+ human_approval_for: [system_instruction]
281
+ min_samples_before_mutation: 20
282
+ mutable_fields: [system_instruction, input_template, llm_config.temperature, context_policy.few_shot]
283
+ require_offline_eval: false
284
+ ```
285
+
286
+ Additional schema blocks such as `concurrency` and `monitoring` may also be declared under `runtime` for configuration completeness. When `runtime.self_healing.enabled` is `true`, `create_graph()` attaches a `GraphRegistry` (`workflow.__graph_registry__`) and `SelfHealer` (`workflow.__self_healer__`) configured with `healer_model`. On unhandled node errors, `SelfHealer` reads execution traces (`_trace` and `messages`), proposes a JSON graph patch (`node_updates`, `add_nodes`, `remove_nodes`, `reroute`, `goto`), records a new `GraphVersion` (`0 -> 1 -> ...`, preserving prior versions as `superseded` and supporting `rollback()`), and routes via LangGraph `Command(goto=..., update={"_error": ..., "_heal": ...})`.
287
+
288
+ ---
289
+
290
+ ### `harness_defaults`
291
+
292
+ Provides default configuration inherited by every agent. Agent-level `harness` blocks are combined using `utils.deep_merge(harness_defaults, agent_harness)`.
293
+
294
+ ```yaml
295
+ harness_defaults:
296
+ id: default
297
+ llm_config:
298
+ provider: google-genai # Also accepts model_provider
299
+ model: gemini-3.6-flash
300
+ temperature: 0.2
301
+ fallback_llm_config:
302
+ model: gemini-3.5-flash
303
+ temperature: 0.0
304
+ system_instruction: "You are a precise, helpful assistant."
305
+ output_validation:
306
+ enforce_schema: true
307
+ on_invalid: retry
308
+ context_policy:
309
+ window_management:
310
+ max_tokens: 8192
311
+ overflow_strategy: summarize_oldest # summarize_oldest | drop_oldest | truncate | sliding_window
312
+ ```
313
+
314
+ ---
315
+
316
+ ### `state`
317
+
318
+ Defines fields on the compiled `NetworkState` `TypedDict`. A `messages` field annotated with LangGraph's `add_messages` reducer is automatically included.
319
+
320
+ ```yaml
321
+ state:
322
+ resume_file: { type: file }
323
+ parsed_resume: { type: ResumeModel }
324
+ job_listings: { type: "array[JobListingModel]", reducer: dedupe_by_id }
325
+ job_analysis: { type: "array[JobAnalysisModel]", reducer: append }
326
+ single_job_listing: { type: JobListingModel, scope: worker }
327
+ ```
328
+
329
+ | Field Property | Values / Behavior |
330
+ | :--- | :--- |
331
+ | `type` | Name of a class in `modules.data_models` (e.g. `ResumeModel`), `array[<Type>]` (mapped to `List[<Type>]`), or any unmapped primitive name (defaults to `Any`). |
332
+ | `reducer` | `append` (appends items/lists), `dedupe_by_id` (deduplicates by `id` key or `.id` attribute), or the name of a custom reducer function in `modules.hooks`. |
333
+ | `scope` | `global` (default) or `worker`. Recorded on `NetworkState.__state_scopes__`. |
334
+
335
+ ---
336
+
337
+ ### `tools`
338
+
339
+ Defines callable tools that agents can reference by name in their `tools` list.
340
+
341
+ ```yaml
342
+ tools:
343
+ job_listings_fetcher:
344
+ type: api_caller
345
+ impl: tools.job_listings_fetcher
346
+ description: Fetches job listings matching preferences and resume skills.
347
+ input_schema: JobSearchInputModel # Or inline dict { resume: ResumeModel, ... }
348
+ output_schema: "array[JobListingModel]"
349
+ rate_limit: { calls: 30, per_seconds: 60 }
350
+ auth: { type: api_key, env: JOB_API_KEY }
351
+ retry: { max_attempts: 2 }
352
+ ```
353
+
354
+ | Field | Description |
355
+ | :--- | :--- |
356
+ | `impl` | Dotted path (`module.func`) or function name resolved from `modules.tools` (defaults to the tool key name). |
357
+ | `description` | Tool description exposed to the LLM. |
358
+ | `input_schema` | String name of a Pydantic `BaseModel` in `modules.data_models` (bound as `args_schema`), or an inline field dict. |
359
+ | `rate_limit` | `{ calls, per_seconds }` wrapped with `TokenBucketRateLimiter`. |
360
+ | `auth` | `{ type, env }` reads `os.environ[env]` and injects it into `api_key`, `token`, `credential`, `auth`, or `**kwargs` if present on the target function signature. |
361
+ | `retry` | Per-tool retry policy passed to `retry_call`. |
362
+
363
+ ---
364
+
365
+ ### `evaluators`
366
+
367
+ Defines evaluation functions executed after agent nodes run.
368
+
369
+ ```yaml
370
+ evaluators:
371
+ schema_validator: { impl: evaluators.schema_validator, type: automatic }
372
+ grounding_checker: { impl: evaluators.grounding_checker, type: automatic }
373
+ user_feedback_loop: { impl: evaluators.user_feedback, type: human_feedback, source: state.suggestions }
374
+ ```
375
+
376
+ Built-in evaluators in `evaluators.py` return metric dictionaries such as `json_parse_errors`, `field_hallucination_rate`, and `suggestion_acceptance_rate`.
377
+
378
+ ---
379
+
380
+ ### `agents`
381
+
382
+ Defines LLM-backed or ReAct agents.
383
+
384
+ ```yaml
385
+ agents:
386
+ suggestions_agent:
387
+ description: Proposes ATS-focused improvements to the parsed resume.
388
+ type: chat # chat (default) | react
389
+ tools: [document_editor] # List of tool keys from `tools:`
390
+ input: { resume: state.parsed_resume }
391
+ output: state.suggestions
392
+ harness:
393
+ system_instruction: "Provide high-impact structural adjustments."
394
+ llm_config: { model: gemini-3.5-flash, temperature: 0.7 }
395
+ fallback_llm_config: { model: gemini-3.5-flash, temperature: 0.0 }
396
+ context_policy:
397
+ pre_filters:
398
+ - target: input.resume
399
+ action: strip_keys
400
+ args: [contact_info, references]
401
+ few_shot:
402
+ enabled: true
403
+ vector_store: memory.long_term.successful_parses
404
+ max_examples: 2
405
+ selection_strategy: cosine_similarity
406
+ window_management:
407
+ max_tokens: 4096
408
+ overflow_strategy: summarize_oldest # summarize_oldest | drop_oldest | truncate | sliding_window
409
+ max_messages: 10 # Used by sliding_window
410
+ preserve_recent_count: 1 # Used by summarize_oldest
411
+ input_template: |
412
+ Parsed Resume Data:
413
+ {{ input.resume }}
414
+ retry: { max_attempts: 1 }
415
+ optimization:
416
+ evaluator: user_feedback_loop
417
+ max_mutation_depth: 3
418
+ fallback_harness: default
419
+ metrics:
420
+ - { key: suggestion_acceptance_rate, target: ">= 0.75" }
421
+ on_underperformance:
422
+ action: mutate_context # mutate_harness | mutate_context
423
+ strategies: [few_shot_refresh, dynamic_prompt_template_evolution]
424
+ few_shot_source: memory.long_term.accepted_suggestions
425
+ ```
426
+
427
+ #### Agent Details
428
+
429
+ - **`input` & `output`**: `input` maps local template keys to `state.<field>` references; `output` specifies the target `state.<field>`.
430
+ - **`harness.input_template` / `harness.input_transformer`**:
431
+ - `input_template`: Jinja2 string rendered with `state` and `input` (after `pre_filters` are applied).
432
+ - `input_transformer`: Dict `{ type: "template", value: "..." }` or `{ type: "function", module: "...", function: "...", args: {...} }`.
433
+ - **`harness.context_policy`**:
434
+ - `pre_filters`: Applied before prompt rendering. `action: strip_keys` removes listed keys from `input.<key>` without mutating state.
435
+ - `few_shot`: When `enabled: true` and a `store` is configured, queries `(vector_store.split(".")[-1], "global")` up to `max_examples` and appends them to the system prompt.
436
+ - `window_management`: Supports `summarize_oldest`, `drop_oldest`, `truncate`, and `sliding_window`.
437
+ - **`optimization`**:
438
+ - `metrics[].target`: Comparison expression (`==`, `!=`, `>=`, `<=`, `>`, `<`) followed by a number, or a bare numeric literal.
439
+ - `strategies`: Built-in mutators in `optimization.STRATEGIES` are `cot_prompt_injection`, `negative_prompting_injection`, `temperature_cooling`, `few_shot_refresh`, and `dynamic_prompt_template_evolution`.
440
+ - `mutate_context` restricts mutations to `input_template*` and `context_policy*`; `mutate_harness` allows all fields in `mutable_fields`.
441
+
442
+ ---
443
+
444
+ ### `nodes`
445
+
446
+ Defines graph topology, execution patterns, interrupts, error handling, and conditional routing.
447
+
448
+ #### Node Types
449
+
450
+ 1. **Standard Agent Node (`type: agent`, default)** and **Periodic Node (`type: periodic`)**:
451
+ ```yaml
452
+ - id: node_job_search
453
+ agent: job_search_agent
454
+ type: periodic
455
+ interval_s: 3600
456
+ gate_conditions:
457
+ - "state.preferences != null"
458
+ - "state.parsed_resume != null"
459
+ trigger_when: "state.resume_file != null"
460
+ routing:
461
+ - { when: "len(state.job_listings) > 0", to: node_job_analyzer }
462
+ ```
463
+ If `trigger_when` or any entry in `gate_conditions` evaluates to `False`, the node skips execution and returns `{}`.
464
+
465
+ 2. **Swarm Node (`type: swarm`)**:
466
+ Compiles into two graph nodes: `<id>` (a fan-out node returning LangGraph `Send` objects to `<id>_worker`) and `<id>_worker` (executes one item and emits its result under `collect.to` for state reducer aggregation). Downstream `routing` / `to` edges are attached to `<id>_worker`.
467
+ ```yaml
468
+ - id: node_job_analyzer
469
+ agent: job_analyzer_agent
470
+ type: swarm
471
+ count: 10
472
+ strategy: map_split
473
+ distribution_method: round_robin
474
+ iterator:
475
+ loop_over: state.job_listings
476
+ filter: "item.id not in state.job_analysis.job_ids"
477
+ yield_to: state.single_job_listing
478
+ collect:
479
+ from: state.single_job_analysis
480
+ to: state.job_analysis
481
+ reducer: append
482
+ routing:
483
+ - { when: "state.job_analysis != null", to: node_application_review }
484
+ ```
485
+
486
+ 3. **Consumer Node (`type: consumer`)**:
487
+ Sequentially iterates over `iterator.loop_over`, binding each item to `iterator.yield_to`, and collects outputs into the agent's output state key.
488
+ ```yaml
489
+ - id: node_application
490
+ agent: application_agent
491
+ type: consumer
492
+ iterator:
493
+ loop_over: state.approved_job_analysis
494
+ yield_to: state.approved_job_analysis
495
+ routing:
496
+ - { when: "true", to: end }
497
+ ```
498
+
499
+ 4. **Human-in-the-Loop Nodes (`type: human_in_the_loop`)**:
500
+ Supports three variants:
501
+ - **Input node** (no `agent` and no `review`): Interrupts with `input_type` (`file` | `text` | `form`) and writes the response (or file contents for `file`) to `input_state`.
502
+ - **Review node** (`review` block present): Filters `review.source` using `review.pre_filter`, auto-approves items matching `auto_approve_if`, and calls `interrupt()` only for remaining items. Writes approved items to `review.select_to`.
503
+ - **Agent-assisted decision node** (`agent` set): Interrupts for human decision (`dict`, `bool`, or natural language feedback delegated to `agent`).
504
+
505
+ #### Routing & Error Handling
506
+
507
+ - **`routing`**: List of `{ when: <expr_or_dict>, to: <node_id | [node_ids] | end> }`, evaluated in order. `to` may be a list for parallel fan-out, or `end` / `END` / `null` for LangGraph `END`. Optional `default` sets the fallback target (defaults to `END`).
508
+ - **`on_error`**: `{ action: route_to_healer, then: <node_id> }`. If a node raises an unhandled exception after retries are exhausted, execution routes via `Command(goto=<then>, update={"_error": ...})`. When `runtime.self_healing.enabled` is `true`, `SelfHealer` inspects the trace, versions any proposed graph patch in `GraphRegistry`, and includes `"_heal": {"version": ..., "patch": ...}` in the state update.
509
+
510
+ ---
511
+
512
+ ### Condition Expression Language
513
+
514
+ String conditions in `when`, `trigger_when`, `gate_conditions`, `iterator.filter`, `review.pre_filter`, and `auto_approve_if` are safely evaluated using an AST visitor (`utils.StateConditionEvaluator`) without `eval()`.
515
+
516
+ Supported constructs:
517
+ - **Identifiers & attributes**: `state.field.nested`, `item.score`, `len(state.job_listings)`
518
+ - **Literals**: YAML `true` / `false` / `null` (and Python `True` / `False` / `None`), numbers, strings
519
+ - **Operators**: `==`, `!=`, `>`, `<`, `>=`, `<=`, `in`, `not in`, `and`, `or`, `not`, unary `-`
520
+
521
+ ---
522
+
523
+ ## Testing
524
+
525
+ The test suite uses Python's `unittest` framework and mocks LLM/store calls so no API keys are required:
526
+
527
+ ```bash
528
+ python -m unittest discover -s tests -q
529
+ ```