agent-eval-planner 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.
- agent_eval_planner-0.1.0/LICENSE +20 -0
- agent_eval_planner-0.1.0/PKG-INFO +139 -0
- agent_eval_planner-0.1.0/README.md +117 -0
- agent_eval_planner-0.1.0/pyproject.toml +37 -0
- agent_eval_planner-0.1.0/setup.cfg +4 -0
- agent_eval_planner-0.1.0/setup.py +3 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/__init__.py +50 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/__main__.py +8 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/cli.py +211 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/contract_analyzer.py +195 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/errors.py +13 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/models.py +39 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/plan_renderer.py +125 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/planner.py +79 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/remediation_renderer.py +138 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/suite_renderer.py +56 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/suite_validator.py +166 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/vector_catalog.py +333 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner/version.py +3 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner.egg-info/PKG-INFO +139 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner.egg-info/SOURCES.txt +25 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner.egg-info/dependency_links.txt +1 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner.egg-info/entry_points.txt +2 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner.egg-info/requires.txt +1 -0
- agent_eval_planner-0.1.0/src/agent_eval_planner.egg-info/top_level.txt +1 -0
- agent_eval_planner-0.1.0/tests/test_contract_analyzer.py +30 -0
- agent_eval_planner-0.1.0/tests/test_planner.py +91 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
20
|
+
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-eval-planner
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generate guardrail evaluation plans, JSONL suites, and remediations from agent contracts
|
|
5
|
+
Author-email: Saulo Filho <saulofilho@users.noreply.github.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://saulofilho.github.io/agent-eval-planner-python/
|
|
8
|
+
Project-URL: Repository, https://github.com/saulofilho/agent-eval-planner-python
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Testing
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: pyyaml>=6.0
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# agent-eval-planner
|
|
24
|
+
|
|
25
|
+
[](https://badge.fury.io/py/agent-eval-planner)
|
|
26
|
+
[](https://pypi.org/project/agent-eval-planner/)
|
|
27
|
+
[](LICENSE)
|
|
28
|
+
|
|
29
|
+
**Site:** [saulofilho.github.io/agent-eval-planner-python](https://saulofilho.github.io/agent-eval-planner-python/)
|
|
30
|
+
|
|
31
|
+
Python library that turns an **agent contract** (system prompt, tools, policy gates) into a guardrail evaluation pipeline:
|
|
32
|
+
|
|
33
|
+
1. **Action plan** — SCOPE / INJECT / ROLE / PII / TOOL / HALLUC / EXFIL vectors
|
|
34
|
+
2. **suite.jsonl** — executable cases with `forbidden_tools` filled
|
|
35
|
+
3. **Remediations** — prompt / gate / evaluator quick wins
|
|
36
|
+
|
|
37
|
+
Sibling of [`security-pentest-planner`](https://github.com/saulofilho/security-pentest-planner-python), applied to LLM agents. Port of the Ruby gem [`agent_eval_planner`](https://github.com/saulofilho/agent-eval-planner).
|
|
38
|
+
|
|
39
|
+
Canonical smoke test: **carrot cake** (off-topic). If the agent answers with a recipe, its scope is not limited.
|
|
40
|
+
|
|
41
|
+
## What it does
|
|
42
|
+
|
|
43
|
+
- Parses agent contracts (Markdown, text, JSON, YAML).
|
|
44
|
+
- Generates a Markdown eval plan, a JSONL suite, and remediations.
|
|
45
|
+
- Validates suites (hard-fail on empty `forbidden_tools` / leftover placeholders).
|
|
46
|
+
|
|
47
|
+
## What it does **NOT** do
|
|
48
|
+
|
|
49
|
+
- Execute the evaluation against a live agent.
|
|
50
|
+
- Replace pentest of APIs/infra (use `security-pentest-planner`).
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install agent-eval-planner
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Or using `uv` / `poetry`:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uv add agent-eval-planner
|
|
62
|
+
# or
|
|
63
|
+
poetry add agent-eval-planner
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## CLI usage
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Full pipeline → directory (plan + suite + remediations)
|
|
70
|
+
agent-eval-planner agent.md --tools funnel_analytics,open_service_center_ticket \
|
|
71
|
+
-t "Platform Team" -a analytics -o ./out
|
|
72
|
+
|
|
73
|
+
# Validate suite (hard-fail on empty forbidden_tools / placeholders)
|
|
74
|
+
agent-eval-planner validate ./out/suite.jsonl \
|
|
75
|
+
--known-tools funnel_analytics,open_service_center_ticket
|
|
76
|
+
|
|
77
|
+
# Plan only to stdout
|
|
78
|
+
agent-eval-planner agent.md --plan-only --agent analytics
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Options
|
|
82
|
+
|
|
83
|
+
| Flag | Description |
|
|
84
|
+
|------|-------------|
|
|
85
|
+
| `-t, --team TEAM` | Team name in the document title |
|
|
86
|
+
| `-a, --agent NAME` | `target_agent` name |
|
|
87
|
+
| `--tools LIST` | Comma-separated real tool names |
|
|
88
|
+
| `--scope TEXT` | Declared scope summary |
|
|
89
|
+
| `--harness NAME` | `generic` or `marketing-copilot` |
|
|
90
|
+
| `-o, --output PATH` | Output directory or single file |
|
|
91
|
+
| `--plan-only` / `--suite-only` / `--remediations-only` | Emit a single artifact |
|
|
92
|
+
| `-v, --version` | Show version |
|
|
93
|
+
|
|
94
|
+
## Programmatic usage
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
import agent_eval_planner
|
|
98
|
+
|
|
99
|
+
result = agent_eval_planner.generate(
|
|
100
|
+
input_path="agent.md",
|
|
101
|
+
team="Platform Team",
|
|
102
|
+
tools=["funnel_analytics", "open_service_center_ticket"],
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
with open("evaluation-plan.md", "w", encoding="utf-8") as f:
|
|
106
|
+
f.write(result.plan)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Publish to PyPI
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
python -m pip install build twine
|
|
113
|
+
python -m build
|
|
114
|
+
python -m twine upload dist/*
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Development and testing
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
git clone https://github.com/saulofilho/agent-eval-planner-python.git
|
|
121
|
+
cd agent-eval-planner-python
|
|
122
|
+
|
|
123
|
+
python -m pip install .
|
|
124
|
+
python -m unittest discover -s tests -v
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Recommended workflow
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
Agent contract → agent-eval-planner → Plan + suite.jsonl + remediations
|
|
131
|
+
↓
|
|
132
|
+
Harness / CI execution
|
|
133
|
+
↓
|
|
134
|
+
Failures + remediations
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# agent-eval-planner
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/py/agent-eval-planner)
|
|
4
|
+
[](https://pypi.org/project/agent-eval-planner/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
**Site:** [saulofilho.github.io/agent-eval-planner-python](https://saulofilho.github.io/agent-eval-planner-python/)
|
|
8
|
+
|
|
9
|
+
Python library that turns an **agent contract** (system prompt, tools, policy gates) into a guardrail evaluation pipeline:
|
|
10
|
+
|
|
11
|
+
1. **Action plan** — SCOPE / INJECT / ROLE / PII / TOOL / HALLUC / EXFIL vectors
|
|
12
|
+
2. **suite.jsonl** — executable cases with `forbidden_tools` filled
|
|
13
|
+
3. **Remediations** — prompt / gate / evaluator quick wins
|
|
14
|
+
|
|
15
|
+
Sibling of [`security-pentest-planner`](https://github.com/saulofilho/security-pentest-planner-python), applied to LLM agents. Port of the Ruby gem [`agent_eval_planner`](https://github.com/saulofilho/agent-eval-planner).
|
|
16
|
+
|
|
17
|
+
Canonical smoke test: **carrot cake** (off-topic). If the agent answers with a recipe, its scope is not limited.
|
|
18
|
+
|
|
19
|
+
## What it does
|
|
20
|
+
|
|
21
|
+
- Parses agent contracts (Markdown, text, JSON, YAML).
|
|
22
|
+
- Generates a Markdown eval plan, a JSONL suite, and remediations.
|
|
23
|
+
- Validates suites (hard-fail on empty `forbidden_tools` / leftover placeholders).
|
|
24
|
+
|
|
25
|
+
## What it does **NOT** do
|
|
26
|
+
|
|
27
|
+
- Execute the evaluation against a live agent.
|
|
28
|
+
- Replace pentest of APIs/infra (use `security-pentest-planner`).
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install agent-eval-planner
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or using `uv` / `poetry`:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv add agent-eval-planner
|
|
40
|
+
# or
|
|
41
|
+
poetry add agent-eval-planner
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## CLI usage
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Full pipeline → directory (plan + suite + remediations)
|
|
48
|
+
agent-eval-planner agent.md --tools funnel_analytics,open_service_center_ticket \
|
|
49
|
+
-t "Platform Team" -a analytics -o ./out
|
|
50
|
+
|
|
51
|
+
# Validate suite (hard-fail on empty forbidden_tools / placeholders)
|
|
52
|
+
agent-eval-planner validate ./out/suite.jsonl \
|
|
53
|
+
--known-tools funnel_analytics,open_service_center_ticket
|
|
54
|
+
|
|
55
|
+
# Plan only to stdout
|
|
56
|
+
agent-eval-planner agent.md --plan-only --agent analytics
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Options
|
|
60
|
+
|
|
61
|
+
| Flag | Description |
|
|
62
|
+
|------|-------------|
|
|
63
|
+
| `-t, --team TEAM` | Team name in the document title |
|
|
64
|
+
| `-a, --agent NAME` | `target_agent` name |
|
|
65
|
+
| `--tools LIST` | Comma-separated real tool names |
|
|
66
|
+
| `--scope TEXT` | Declared scope summary |
|
|
67
|
+
| `--harness NAME` | `generic` or `marketing-copilot` |
|
|
68
|
+
| `-o, --output PATH` | Output directory or single file |
|
|
69
|
+
| `--plan-only` / `--suite-only` / `--remediations-only` | Emit a single artifact |
|
|
70
|
+
| `-v, --version` | Show version |
|
|
71
|
+
|
|
72
|
+
## Programmatic usage
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import agent_eval_planner
|
|
76
|
+
|
|
77
|
+
result = agent_eval_planner.generate(
|
|
78
|
+
input_path="agent.md",
|
|
79
|
+
team="Platform Team",
|
|
80
|
+
tools=["funnel_analytics", "open_service_center_ticket"],
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
with open("evaluation-plan.md", "w", encoding="utf-8") as f:
|
|
84
|
+
f.write(result.plan)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Publish to PyPI
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
python -m pip install build twine
|
|
91
|
+
python -m build
|
|
92
|
+
python -m twine upload dist/*
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Development and testing
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
git clone https://github.com/saulofilho/agent-eval-planner-python.git
|
|
99
|
+
cd agent-eval-planner-python
|
|
100
|
+
|
|
101
|
+
python -m pip install .
|
|
102
|
+
python -m unittest discover -s tests -v
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Recommended workflow
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Agent contract → agent-eval-planner → Plan + suite.jsonl + remediations
|
|
109
|
+
↓
|
|
110
|
+
Harness / CI execution
|
|
111
|
+
↓
|
|
112
|
+
Failures + remediations
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agent-eval-planner"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Generate guardrail evaluation plans, JSONL suites, and remediations from agent contracts"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Saulo Filho", email = "saulofilho@users.noreply.github.com" }
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Software Development :: Testing",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"pyyaml>=6.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://saulofilho.github.io/agent-eval-planner-python/"
|
|
31
|
+
Repository = "https://github.com/saulofilho/agent-eval-planner-python"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
agent-eval-planner = "agent_eval_planner.cli:main"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
where = ["src"]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""agent_eval_planner package.
|
|
2
|
+
|
|
3
|
+
Turns an agent contract into a guardrail evaluation plan, JSONL suite, and remediations.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from .errors import Error, InputError, ValidationError
|
|
9
|
+
from .models import PlannerResult, Vector
|
|
10
|
+
from .planner import Planner
|
|
11
|
+
from .suite_validator import SuiteValidator
|
|
12
|
+
from .version import __version__
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def generate(input_path: str | None = None, raw: str | None = None, **options) -> PlannerResult:
|
|
16
|
+
"""Generate plan, suite, and remediations from an agent contract."""
|
|
17
|
+
return Planner(input_path=input_path, raw=raw, **options).call()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def generate_plan(input_path: str | None = None, raw: str | None = None, **options) -> str:
|
|
21
|
+
"""Generate only the Markdown evaluation plan."""
|
|
22
|
+
return generate(input_path=input_path, raw=raw, **options).plan
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def validate_suite(
|
|
26
|
+
path: str,
|
|
27
|
+
known_tools: list[str] | None = None,
|
|
28
|
+
agent_has_no_tools: bool = False,
|
|
29
|
+
) -> list[str]:
|
|
30
|
+
"""Validate a suite.jsonl file. Returns a list of errors (empty if OK)."""
|
|
31
|
+
return SuiteValidator(
|
|
32
|
+
path=path,
|
|
33
|
+
known_tools=known_tools or [],
|
|
34
|
+
agent_has_no_tools=agent_has_no_tools,
|
|
35
|
+
).validate()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"__version__",
|
|
40
|
+
"generate",
|
|
41
|
+
"generate_plan",
|
|
42
|
+
"validate_suite",
|
|
43
|
+
"Planner",
|
|
44
|
+
"PlannerResult",
|
|
45
|
+
"Vector",
|
|
46
|
+
"SuiteValidator",
|
|
47
|
+
"Error",
|
|
48
|
+
"InputError",
|
|
49
|
+
"ValidationError",
|
|
50
|
+
]
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""Command-line interface for agent-eval-planner."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Sequence
|
|
9
|
+
|
|
10
|
+
from . import generate, validate_suite
|
|
11
|
+
from .errors import Error
|
|
12
|
+
from .models import PlannerResult
|
|
13
|
+
from .version import __version__
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
17
|
+
"""Builds the argument parser for generate mode."""
|
|
18
|
+
parser = argparse.ArgumentParser(
|
|
19
|
+
prog="agent-eval-planner",
|
|
20
|
+
description="Gera plano de eval, suite JSONL e remediações a partir do contrato de um agente.",
|
|
21
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
22
|
+
epilog=(
|
|
23
|
+
"Usage:\n"
|
|
24
|
+
" agent-eval-planner [options] <contract.md|prompt.txt|agent.json>\n"
|
|
25
|
+
" agent-eval-planner validate [options] <suite.jsonl>"
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument("input", nargs="?", help="Caminho do contrato do agente")
|
|
29
|
+
parser.add_argument("-t", "--team", help="Nome do time no título")
|
|
30
|
+
parser.add_argument("-a", "--agent", dest="agent_name", help="Nome do target_agent")
|
|
31
|
+
parser.add_argument("--tools", help="Tools conhecidas (vírgula)")
|
|
32
|
+
parser.add_argument("--scope", dest="declared_scope", help="Escopo declarado (resumo)")
|
|
33
|
+
parser.add_argument("--out-of-scope", dest="out_of_scope", help="Fora de escopo declarado")
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--harness",
|
|
36
|
+
default="generic",
|
|
37
|
+
help="Harness alvo (generic|marketing-copilot)",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument("-o", "--output", help="Arquivo .md/.jsonl ou diretório de saída")
|
|
40
|
+
parser.add_argument("--plan-only", action="store_true", help="Gerar apenas o plano")
|
|
41
|
+
parser.add_argument("--suite-only", action="store_true", help="Gerar apenas a suite JSONL")
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--remediations-only",
|
|
44
|
+
action="store_true",
|
|
45
|
+
help="Gerar apenas remediações",
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"-v",
|
|
49
|
+
"--version",
|
|
50
|
+
action="version",
|
|
51
|
+
version=f"agent_eval_planner {__version__}",
|
|
52
|
+
)
|
|
53
|
+
return parser
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def build_validate_parser() -> argparse.ArgumentParser:
|
|
57
|
+
"""Builds the argument parser for validate mode."""
|
|
58
|
+
parser = argparse.ArgumentParser(
|
|
59
|
+
prog="agent-eval-planner validate",
|
|
60
|
+
description="Valida uma suite JSONL (hard-fail em forbidden_tools vazios / placeholders).",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument("suite", nargs="?", help="Caminho da suite.jsonl")
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--known-tools",
|
|
65
|
+
help="Tools reais (vírgula)",
|
|
66
|
+
)
|
|
67
|
+
parser.add_argument(
|
|
68
|
+
"--agent-has-no-tools",
|
|
69
|
+
action="store_true",
|
|
70
|
+
help="Agente sem tools",
|
|
71
|
+
)
|
|
72
|
+
return parser
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
76
|
+
"""Main CLI execution method."""
|
|
77
|
+
args_list = list(argv) if argv is not None else sys.argv[1:]
|
|
78
|
+
if args_list and args_list[0] == "validate":
|
|
79
|
+
return _run_validate(args_list[1:])
|
|
80
|
+
return _run_generate(args_list)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _run_generate(argv: list[str]) -> int:
|
|
84
|
+
parser = build_parser()
|
|
85
|
+
args = parser.parse_args(argv)
|
|
86
|
+
if not args.input:
|
|
87
|
+
parser.print_help(sys.stderr)
|
|
88
|
+
return 1
|
|
89
|
+
|
|
90
|
+
tools = None
|
|
91
|
+
if args.tools:
|
|
92
|
+
tools = [part.strip() for part in args.tools.split(",") if part.strip()]
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
result = generate(
|
|
96
|
+
input_path=args.input,
|
|
97
|
+
team=args.team,
|
|
98
|
+
agent_name=args.agent_name,
|
|
99
|
+
tools=tools,
|
|
100
|
+
declared_scope=args.declared_scope,
|
|
101
|
+
out_of_scope=args.out_of_scope,
|
|
102
|
+
harness=args.harness,
|
|
103
|
+
)
|
|
104
|
+
_write_outputs(result, args)
|
|
105
|
+
return 0
|
|
106
|
+
except Error as exc:
|
|
107
|
+
print(f"Erro: {exc}", file=sys.stderr)
|
|
108
|
+
return 1
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _looks_like_directory(path: str) -> bool:
|
|
112
|
+
candidate = Path(path)
|
|
113
|
+
return candidate.is_dir() or path.endswith("/") or candidate.suffix == ""
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _select_single(result: PlannerResult, args: argparse.Namespace) -> str | None:
|
|
117
|
+
if args.suite_only:
|
|
118
|
+
return result.suite
|
|
119
|
+
if args.remediations_only:
|
|
120
|
+
return result.remediations
|
|
121
|
+
if args.plan_only:
|
|
122
|
+
return result.plan
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _write_bundle(directory: Path, result: PlannerResult, args: argparse.Namespace) -> None:
|
|
127
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
128
|
+
if not args.suite_only and not args.remediations_only:
|
|
129
|
+
plan_path = directory / "plano-de-acao-agent-eval.md"
|
|
130
|
+
plan_path.write_text(result.plan, encoding="utf-8")
|
|
131
|
+
print(f"Plano gerado em {plan_path}", file=sys.stderr)
|
|
132
|
+
if not args.plan_only and not args.remediations_only:
|
|
133
|
+
suite_path = directory / "suite.jsonl"
|
|
134
|
+
suite_path.write_text(result.suite, encoding="utf-8")
|
|
135
|
+
print(f"Suite gerada em {suite_path}", file=sys.stderr)
|
|
136
|
+
if args.plan_only or args.suite_only:
|
|
137
|
+
return
|
|
138
|
+
rem_path = directory / "remediacoes.md"
|
|
139
|
+
rem_path.write_text(result.remediations, encoding="utf-8")
|
|
140
|
+
print(f"Remediações geradas em {rem_path}", file=sys.stderr)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _emit_stdout(result: PlannerResult, args: argparse.Namespace) -> None:
|
|
144
|
+
if args.suite_only:
|
|
145
|
+
sys.stdout.write(result.suite)
|
|
146
|
+
elif args.remediations_only:
|
|
147
|
+
sys.stdout.write(result.remediations)
|
|
148
|
+
elif args.plan_only:
|
|
149
|
+
sys.stdout.write(result.plan)
|
|
150
|
+
else:
|
|
151
|
+
sys.stdout.write(result.plan)
|
|
152
|
+
print("\n# --- suite.jsonl ---\n", file=sys.stderr)
|
|
153
|
+
sys.stdout.write(result.suite)
|
|
154
|
+
print("\n# --- remediacoes.md ---\n", file=sys.stderr)
|
|
155
|
+
sys.stdout.write(result.remediations)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _write_outputs(result: PlannerResult, args: argparse.Namespace) -> None:
|
|
159
|
+
if not args.output:
|
|
160
|
+
_emit_stdout(result, args)
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
path = args.output
|
|
164
|
+
if _looks_like_directory(path):
|
|
165
|
+
_write_bundle(Path(path), result, args)
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
output = Path(path)
|
|
169
|
+
if args.suite_only or output.suffix == ".jsonl":
|
|
170
|
+
output.write_text(result.suite, encoding="utf-8")
|
|
171
|
+
print(f"Suite gerada em {path}", file=sys.stderr)
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
content = _select_single(result, args) or result.plan
|
|
175
|
+
output.write_text(content, encoding="utf-8")
|
|
176
|
+
print(f"Artefato gerado em {path}", file=sys.stderr)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _run_validate(argv: list[str]) -> int:
|
|
180
|
+
parser = build_validate_parser()
|
|
181
|
+
args = parser.parse_args(argv)
|
|
182
|
+
if not args.suite:
|
|
183
|
+
parser.print_help(sys.stderr)
|
|
184
|
+
return 1
|
|
185
|
+
|
|
186
|
+
known_tools: list[str] = []
|
|
187
|
+
if args.known_tools:
|
|
188
|
+
known_tools = [part.strip() for part in args.known_tools.split(",") if part.strip()]
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
errors = validate_suite(
|
|
192
|
+
args.suite,
|
|
193
|
+
known_tools=known_tools,
|
|
194
|
+
agent_has_no_tools=args.agent_has_no_tools,
|
|
195
|
+
)
|
|
196
|
+
except Error as exc:
|
|
197
|
+
print(f"Erro: {exc}", file=sys.stderr)
|
|
198
|
+
return 1
|
|
199
|
+
|
|
200
|
+
if errors:
|
|
201
|
+
print(f"INVALID: {args.suite} ({len(errors)} error(s))", file=sys.stderr)
|
|
202
|
+
for error in errors:
|
|
203
|
+
print(f" - {error}", file=sys.stderr)
|
|
204
|
+
return 1
|
|
205
|
+
|
|
206
|
+
print(f"OK: {args.suite}")
|
|
207
|
+
return 0
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
sys.exit(main())
|