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.
- py_meta_agent-0.1.0/PKG-INFO +529 -0
- py_meta_agent-0.1.0/README.md +515 -0
- py_meta_agent-0.1.0/pyproject.toml +32 -0
- py_meta_agent-0.1.0/setup.cfg +4 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/__init__.py +88 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/builder.py +832 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/compiler.py +134 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/evaluators.py +25 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/graph.py +604 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/memory.py +137 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/nodes.py +161 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/optimization.py +340 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/resilience.py +349 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/state.py +97 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/tools.py +76 -0
- py_meta_agent-0.1.0/src/meta_agent_madcoderme/utils.py +282 -0
- py_meta_agent-0.1.0/src/py_meta_agent.egg-info/PKG-INFO +529 -0
- py_meta_agent-0.1.0/src/py_meta_agent.egg-info/SOURCES.txt +28 -0
- py_meta_agent-0.1.0/src/py_meta_agent.egg-info/dependency_links.txt +1 -0
- py_meta_agent-0.1.0/src/py_meta_agent.egg-info/entry_points.txt +3 -0
- py_meta_agent-0.1.0/src/py_meta_agent.egg-info/requires.txt +7 -0
- py_meta_agent-0.1.0/src/py_meta_agent.egg-info/top_level.txt +1 -0
- py_meta_agent-0.1.0/tests/test_compiler_config.py +71 -0
- py_meta_agent-0.1.0/tests/test_context_policy.py +36 -0
- py_meta_agent-0.1.0/tests/test_evaluator_conditions.py +42 -0
- py_meta_agent-0.1.0/tests/test_graph.py +205 -0
- py_meta_agent-0.1.0/tests/test_phase1.py +161 -0
- py_meta_agent-0.1.0/tests/test_phase3.py +334 -0
- py_meta_agent-0.1.0/tests/test_phase4_5.py +363 -0
- 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
|
+
```
|