evalforge-agents 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.
- evalforge_agents-0.1.0/LICENSE +21 -0
- evalforge_agents-0.1.0/PKG-INFO +384 -0
- evalforge_agents-0.1.0/README.md +347 -0
- evalforge_agents-0.1.0/pyproject.toml +73 -0
- evalforge_agents-0.1.0/setup.cfg +4 -0
- evalforge_agents-0.1.0/src/evalforge_agents/__init__.py +3 -0
- evalforge_agents-0.1.0/src/evalforge_agents/adapters/__init__.py +5 -0
- evalforge_agents-0.1.0/src/evalforge_agents/adapters/base.py +13 -0
- evalforge_agents-0.1.0/src/evalforge_agents/adapters/command.py +68 -0
- evalforge_agents-0.1.0/src/evalforge_agents/adapters/http.py +52 -0
- evalforge_agents-0.1.0/src/evalforge_agents/api.py +185 -0
- evalforge_agents-0.1.0/src/evalforge_agents/cli.py +340 -0
- evalforge_agents-0.1.0/src/evalforge_agents/config.py +13 -0
- evalforge_agents-0.1.0/src/evalforge_agents/db.py +237 -0
- evalforge_agents-0.1.0/src/evalforge_agents/graders/__init__.py +19 -0
- evalforge_agents-0.1.0/src/evalforge_agents/graders/base.py +13 -0
- evalforge_agents-0.1.0/src/evalforge_agents/graders/deterministic.py +174 -0
- evalforge_agents-0.1.0/src/evalforge_agents/graders/human.py +55 -0
- evalforge_agents-0.1.0/src/evalforge_agents/graders/llm_judge.py +94 -0
- evalforge_agents-0.1.0/src/evalforge_agents/logging_utils.py +89 -0
- evalforge_agents-0.1.0/src/evalforge_agents/models.py +164 -0
- evalforge_agents-0.1.0/src/evalforge_agents/regression.py +55 -0
- evalforge_agents-0.1.0/src/evalforge_agents/runner.py +135 -0
- evalforge_agents-0.1.0/src/evalforge_agents/storage.py +71 -0
- evalforge_agents-0.1.0/src/evalforge_agents.egg-info/PKG-INFO +384 -0
- evalforge_agents-0.1.0/src/evalforge_agents.egg-info/SOURCES.txt +40 -0
- evalforge_agents-0.1.0/src/evalforge_agents.egg-info/dependency_links.txt +1 -0
- evalforge_agents-0.1.0/src/evalforge_agents.egg-info/entry_points.txt +2 -0
- evalforge_agents-0.1.0/src/evalforge_agents.egg-info/requires.txt +18 -0
- evalforge_agents-0.1.0/src/evalforge_agents.egg-info/top_level.txt +1 -0
- evalforge_agents-0.1.0/tests/test_adapters.py +154 -0
- evalforge_agents-0.1.0/tests/test_api.py +73 -0
- evalforge_agents-0.1.0/tests/test_api_security.py +102 -0
- evalforge_agents-0.1.0/tests/test_cli.py +313 -0
- evalforge_agents-0.1.0/tests/test_db.py +183 -0
- evalforge_agents-0.1.0/tests/test_graders.py +22 -0
- evalforge_agents-0.1.0/tests/test_human_grader.py +19 -0
- evalforge_agents-0.1.0/tests/test_logging_utils.py +121 -0
- evalforge_agents-0.1.0/tests/test_regression.py +39 -0
- evalforge_agents-0.1.0/tests/test_runner.py +29 -0
- evalforge_agents-0.1.0/tests/test_storage.py +94 -0
- evalforge_agents-0.1.0/tests/test_suite_version.py +48 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abhilash Reddy Somigari
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evalforge-agents
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CI/CD evaluation infrastructure for AI agents
|
|
5
|
+
Author: EvalForge Contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/AbhilashSomigari/evalforge
|
|
8
|
+
Project-URL: Repository, https://github.com/AbhilashSomigari/evalforge
|
|
9
|
+
Project-URL: Issues, https://github.com/AbhilashSomigari/evalforge/issues
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
16
|
+
Classifier: Topic :: Software Development :: Testing
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: pydantic<3,>=2.8
|
|
21
|
+
Requires-Dist: PyYAML<7,>=6
|
|
22
|
+
Requires-Dist: typer<1,>=0.12
|
|
23
|
+
Requires-Dist: rich<15,>=13
|
|
24
|
+
Requires-Dist: fastapi<1,>=0.115
|
|
25
|
+
Requires-Dist: uvicorn<1,>=0.30
|
|
26
|
+
Requires-Dist: httpx<1,>=0.27
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest<10,>=8; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-asyncio<1,>=0.24; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff<0.17,>=0.16.10; extra == "dev"
|
|
31
|
+
Requires-Dist: httpx2<3,>=2; extra == "dev"
|
|
32
|
+
Requires-Dist: mypy<2,>=1.11; extra == "dev"
|
|
33
|
+
Requires-Dist: types-PyYAML<7,>=6; extra == "dev"
|
|
34
|
+
Provides-Extra: postgres
|
|
35
|
+
Requires-Dist: psycopg[binary,pool]<4,>=3.1; extra == "postgres"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# EvalForge — CI/CD for AI Agents
|
|
39
|
+
|
|
40
|
+
EvalForge is an infrastructure-first evaluation harness for **tool-using, multi-step AI agents**. It turns agent behavior into repeatable tests that can run locally or in CI and block a deployment when quality regresses.
|
|
41
|
+
|
|
42
|
+
> Change a prompt, model, tool schema, or RAG pipeline → run scenarios → capture trajectories → grade outcomes → compare against a baseline → enforce gates.
|
|
43
|
+
|
|
44
|
+
## Why this exists
|
|
45
|
+
|
|
46
|
+
Normal unit tests are necessary but insufficient for agents. Agents are non-deterministic, make multiple model calls, use tools, retrieve context, and mutate environments. EvalForge treats the **trajectory and outcome** as first-class test artifacts instead of evaluating only the final string.
|
|
47
|
+
|
|
48
|
+
## v0.1 capabilities
|
|
49
|
+
|
|
50
|
+
- YAML evaluation suites and reusable scenarios
|
|
51
|
+
- Multiple trials per task + concurrent execution
|
|
52
|
+
- Language-agnostic command adapter (JSON over stdin/stdout)
|
|
53
|
+
- HTTP adapter for already-running agents
|
|
54
|
+
- Full trajectory/tool-call capture
|
|
55
|
+
- Deterministic graders: exact match, contains, regex, similarity, JSON contract
|
|
56
|
+
- Tool sequence and tool argument correctness
|
|
57
|
+
- Citation-based RAG groundedness proxy
|
|
58
|
+
- Known-hallucination regression guard
|
|
59
|
+
- Optional OpenAI-compatible LLM-as-judge grader
|
|
60
|
+
- Human-score ingestion that participates in the same pass/fail pipeline
|
|
61
|
+
- Task success, tool correctness, hallucination rate, latency, token and cost aggregation
|
|
62
|
+
- Baseline-vs-candidate regression comparison
|
|
63
|
+
- Prompt/model A/B runs over the exact same suite
|
|
64
|
+
- Absolute and regression-based CI gates
|
|
65
|
+
- Trajectory replay / re-grading without paying for another agent run
|
|
66
|
+
- FastAPI run-inspection API
|
|
67
|
+
- Docker + GitHub Actions example
|
|
68
|
+
|
|
69
|
+
## Architecture
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
┌────────────────────────┐
|
|
73
|
+
│ YAML Evaluation Suite │
|
|
74
|
+
│ tasks · trials · gates │
|
|
75
|
+
└────────────┬───────────┘
|
|
76
|
+
│
|
|
77
|
+
┌───────────▼───────────┐
|
|
78
|
+
│ Eval Runner │
|
|
79
|
+
│ concurrency · retries │
|
|
80
|
+
└───────┬────────┬──────┘
|
|
81
|
+
│ │
|
|
82
|
+
┌──────────────▼─┐ ┌─▼──────────────┐
|
|
83
|
+
│ Agent Adapter │ │ Recorded Replay │
|
|
84
|
+
│ command / HTTP │ │ no model call │
|
|
85
|
+
└────────┬────────┘ └──────┬─────────┘
|
|
86
|
+
│ │
|
|
87
|
+
trajectory + output + usage
|
|
88
|
+
│ │
|
|
89
|
+
┌─────▼────────────────────▼──┐
|
|
90
|
+
│ Graders │
|
|
91
|
+
│ code · tool · RAG · LLM │
|
|
92
|
+
└──────────────┬──────────────┘
|
|
93
|
+
│
|
|
94
|
+
┌───────▼────────┐
|
|
95
|
+
│ Metrics + Runs │
|
|
96
|
+
└───────┬────────┘
|
|
97
|
+
│
|
|
98
|
+
┌──────────▼──────────┐
|
|
99
|
+
│ Regression CI Gates │
|
|
100
|
+
│ PASS / BLOCK │
|
|
101
|
+
└─────────────────────┘
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Run it
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
python -m venv .venv
|
|
108
|
+
source .venv/bin/activate
|
|
109
|
+
make install # hash-pinned deps from requirements/dev.lock, then the package itself
|
|
110
|
+
pytest
|
|
111
|
+
|
|
112
|
+
evalforge run \
|
|
113
|
+
--suite examples/suites/customer_support.yaml \
|
|
114
|
+
--agent "python examples/agents/demo_agent.py" \
|
|
115
|
+
--out runs/demo.json
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
A failing gate exits with code `2`, which is what blocks a CI job. Invalid
|
|
119
|
+
input — a malformed suite, an unknown gate metric, an unreadable run file —
|
|
120
|
+
exits with code `1` and a one-line error, before any agent is called where
|
|
121
|
+
possible.
|
|
122
|
+
|
|
123
|
+
Dependencies are pinned with hashes in `requirements/*.lock` (runtime, dev,
|
|
124
|
+
dev+postgres), used by CI and the Docker image. After changing dependencies in
|
|
125
|
+
`pyproject.toml`, regenerate them with `make lock` (needs
|
|
126
|
+
[uv](https://docs.astral.sh/uv/)).
|
|
127
|
+
|
|
128
|
+
## Agent protocol
|
|
129
|
+
|
|
130
|
+
EvalForge deliberately does **not** force a LangChain/LlamaIndex/custom-agent dependency. For the command adapter, your executable receives one `TaskSpec` JSON object on stdin and returns one `AgentOutput` JSON object on stdout:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"output": "Refund submitted. Source: KB-REFUND-1",
|
|
135
|
+
"trajectory": [
|
|
136
|
+
{"type": "message", "role": "user", "content": "Refund order R200"},
|
|
137
|
+
{"type": "tool_call", "tool_call": {"name": "lookup_order", "arguments": {"order_id": "R200"}}}
|
|
138
|
+
],
|
|
139
|
+
"tool_calls": [
|
|
140
|
+
{"name": "lookup_order", "arguments": {"order_id": "R200"}}
|
|
141
|
+
],
|
|
142
|
+
"usage": {
|
|
143
|
+
"input_tokens": 100,
|
|
144
|
+
"output_tokens": 35,
|
|
145
|
+
"cost_usd": 0.012
|
|
146
|
+
},
|
|
147
|
+
"model": "your-model"
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
That adapter boundary lets the same suite evaluate Python, TypeScript, Java, Go, MCP-based agents, or remote services.
|
|
152
|
+
|
|
153
|
+
## Suite example
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
name: checkout-agent
|
|
157
|
+
trials_per_task: 3
|
|
158
|
+
concurrency: 8
|
|
159
|
+
|
|
160
|
+
gates:
|
|
161
|
+
- metric: task_success
|
|
162
|
+
op: ">="
|
|
163
|
+
value: 0.90
|
|
164
|
+
- metric: avg_cost_usd
|
|
165
|
+
op: "<="
|
|
166
|
+
value: 0.15
|
|
167
|
+
- metric: task_success
|
|
168
|
+
op: ">="
|
|
169
|
+
max_regression: 0.03
|
|
170
|
+
|
|
171
|
+
tasks:
|
|
172
|
+
- id: refund-order
|
|
173
|
+
input: Refund order R200.
|
|
174
|
+
expected_tools: [lookup_order, issue_refund]
|
|
175
|
+
graders:
|
|
176
|
+
- type: tool_sequence
|
|
177
|
+
name: tool_correctness
|
|
178
|
+
pass_threshold: 1.0
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
For a regression gate, provide a baseline. A quality metric with `op: ">="` is allowed to drop by at most `max_regression`; a cost/latency metric with `op: "<="` is allowed to rise by at most that amount. A regression gate with no baseline to compare against **fails** (shown as `no baseline` in the gates table) — it can't pass without one.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
evalforge run --suite examples/suites/customer_support.yaml --agent "..." \
|
|
185
|
+
--out runs/pr-184.json --baseline-branch main
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`--baseline-branch main` auto-resolves the baseline to the latest run of this
|
|
189
|
+
suite on that branch — no more hand-tracking `runs/main.json` yourself. It
|
|
190
|
+
looks in Postgres if `EVALFORGE_DATABASE_URL` is set, otherwise in `--out`'s
|
|
191
|
+
directory. `GITHUB_HEAD_REF`/`GITHUB_REF_NAME` set this automatically in
|
|
192
|
+
GitHub Actions; set `GIT_BRANCH` yourself elsewhere. Use `--baseline
|
|
193
|
+
<path>` instead for a fixed file, same as before — the two are mutually
|
|
194
|
+
exclusive. Either way, the run prints which baseline it resolved (run id,
|
|
195
|
+
branch, commit, timestamp), and regression gates show the baseline value and
|
|
196
|
+
delta next to each result. Run files in that directory that can't be read
|
|
197
|
+
(corrupt, or from an older `schema_version`) are skipped with a logged
|
|
198
|
+
warning rather than silently. If the baseline came from a suite definition that's
|
|
199
|
+
since changed (a task, grader, or gate edited — not just its `description`
|
|
200
|
+
or `concurrency`), you'll get a warning that the comparison may not be
|
|
201
|
+
meaningful, since `evalforge` tracks a content hash (`suite_version`) of
|
|
202
|
+
every run's suite on the `EvalRun` itself.
|
|
203
|
+
|
|
204
|
+
**In CI, `--baseline-branch` needs storage that outlives the job.** Each CI
|
|
205
|
+
job starts with an empty workspace, so without Postgres the `--out`
|
|
206
|
+
directory never contains an earlier run and every regression gate fails with
|
|
207
|
+
`no baseline`. Either set `EVALFORGE_DATABASE_URL` (as a secret) to a
|
|
208
|
+
Postgres instance your CI can reach, or restore previous run files into the
|
|
209
|
+
`--out` directory before running (e.g. `actions/cache` or downloading the
|
|
210
|
+
main branch's last `evalforge-run` artifact).
|
|
211
|
+
|
|
212
|
+
## Replay a trajectory
|
|
213
|
+
|
|
214
|
+
When you improve a grader or rubric, re-score an existing run without spending tokens again:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
evalforge replay \
|
|
218
|
+
--suite examples/suites/customer_support.yaml \
|
|
219
|
+
--run runs/demo.json \
|
|
220
|
+
--out runs/replayed.json
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Compare two runs
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
evalforge compare --baseline runs/main.json --candidate runs/pr-184.json
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Prompt/model A/B test
|
|
230
|
+
|
|
231
|
+
Run the same task bank against two commands:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
evalforge ab \
|
|
235
|
+
--suite examples/suites/customer_support.yaml \
|
|
236
|
+
--agent-a "python agent_prompt_v11.py" \
|
|
237
|
+
--agent-b "python agent_prompt_v12.py"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
EvalForge stores both run artifacts and prints metric deltas. The two commands can represent different prompts, models, tool schemas, retrieval strategies, or entire agent implementations.
|
|
241
|
+
|
|
242
|
+
## Human grading
|
|
243
|
+
|
|
244
|
+
Human scores can be imported without changing the run pipeline:
|
|
245
|
+
|
|
246
|
+
```yaml
|
|
247
|
+
- type: human
|
|
248
|
+
name: human_quality
|
|
249
|
+
pass_threshold: 0.8
|
|
250
|
+
config:
|
|
251
|
+
file: reviews/human_scores.json
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"refund": {"score": 0.95, "reason": "Correct and policy-compliant"},
|
|
257
|
+
"order-status": 1.0
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Inspect runs over HTTP
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
evalforge serve --host 0.0.0.0 --port 8000
|
|
265
|
+
# GET /health
|
|
266
|
+
# GET /runs?limit=20&offset=0&suite=customer-support-regression
|
|
267
|
+
# GET /runs/{key}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Interactive FastAPI docs are available at `/docs`.
|
|
271
|
+
|
|
272
|
+
By default runs are read back from the local `runs/` directory (`{key}` is a
|
|
273
|
+
filename). Set `EVALFORGE_DATABASE_URL` to persist runs to Postgres instead —
|
|
274
|
+
`evalforge run`/`ab`/`replay` write to it in addition to the local JSON file,
|
|
275
|
+
and `/runs`/`/runs/{key}` (`{key}` becomes the run id) query it directly with
|
|
276
|
+
real pagination and suite filtering instead of parsing every file in `runs/`
|
|
277
|
+
on every request:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
export EVALFORGE_DATABASE_URL=postgresql://evalforge:evalforge@localhost:5432/evalforge
|
|
281
|
+
pip install -e ".[postgres]"
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Database connections are pooled (`EVALFORGE_DB_POOL_MAX_SIZE`, default 10).
|
|
285
|
+
Runs stored under a different `schema_version` are treated the same way in
|
|
286
|
+
both backends: skipped (with a logged warning) when listing or resolving a
|
|
287
|
+
baseline, and a `409` with the reason from `/runs/{key}`.
|
|
288
|
+
|
|
289
|
+
`docker-compose.yml` runs a Postgres container alongside the API and wires
|
|
290
|
+
this automatically. It requires a `.env` file next to it:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
cp .env.example .env # then set POSTGRES_PASSWORD and EVALFORGE_API_KEY (e.g. openssl rand -hex 32)
|
|
294
|
+
docker compose up --build
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Compose refuses to start without both values, and publishes the API on
|
|
298
|
+
`127.0.0.1:8000` only; put a TLS-terminating reverse proxy in front of it to
|
|
299
|
+
expose it further. (If a `evalforge-db` volume already exists from an older
|
|
300
|
+
setup, Postgres keeps the password it was initialized with — change it in
|
|
301
|
+
the database, or remove the volume, to match `.env`.)
|
|
302
|
+
|
|
303
|
+
### Securing the API
|
|
304
|
+
|
|
305
|
+
The API is **open by default** — fine for `localhost`, not for anywhere else.
|
|
306
|
+
Before exposing it beyond your own machine:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
export EVALFORGE_API_KEY=some-long-random-value # required Authorization: Bearer <key> on /runs*
|
|
310
|
+
export EVALFORGE_CORS_ORIGINS=https://your-dashboard.example # comma-separated allowlist; unset = no cross-origin reads
|
|
311
|
+
export EVALFORGE_RATE_LIMIT_PER_MINUTE=60 # per client IP; see below
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The API key is compared in constant time. The rate limit is counted in
|
|
315
|
+
Postgres when `EVALFORGE_DATABASE_URL` is set (a fixed one-minute window
|
|
316
|
+
shared by every replica); without a database it falls back to an in-memory
|
|
317
|
+
counter per process. Behind a reverse proxy every request arrives from the
|
|
318
|
+
proxy's IP, so rate-limit there (or by forwarded client IP at the proxy)
|
|
319
|
+
rather than relying on this per-IP limit alone.
|
|
320
|
+
|
|
321
|
+
`/health` never requires the API key, so container/orchestrator health checks
|
|
322
|
+
keep working unauthenticated. Every request gets an `X-Request-ID` (echoed
|
|
323
|
+
back if the caller sends one), and the API and runner emit one JSON log line
|
|
324
|
+
per request/run to stderr — `evalforge run` keeps its human-readable table on
|
|
325
|
+
stdout, so scripts piping stdout are unaffected.
|
|
326
|
+
|
|
327
|
+
## LLM-as-judge
|
|
328
|
+
|
|
329
|
+
Add this grader to a task or suite:
|
|
330
|
+
|
|
331
|
+
```yaml
|
|
332
|
+
- type: llm_judge
|
|
333
|
+
name: policy_quality
|
|
334
|
+
pass_threshold: 0.8
|
|
335
|
+
config:
|
|
336
|
+
model: your-judge-model
|
|
337
|
+
rubric: >-
|
|
338
|
+
Score whether the answer follows the refund policy, cites evidence,
|
|
339
|
+
and avoids claiming actions that did not occur.
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Then set:
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
export EVALFORGE_JUDGE_API_KEY=...
|
|
346
|
+
export EVALFORGE_JUDGE_BASE_URL=https://api.openai.com/v1
|
|
347
|
+
export EVALFORGE_JUDGE_MODEL=...
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
The implementation is OpenAI-compatible so it can also point to compatible gateways/local inference servers.
|
|
351
|
+
|
|
352
|
+
## Releasing
|
|
353
|
+
|
|
354
|
+
The package is published to PyPI as **`evalforge-agents`** (import it as
|
|
355
|
+
`evalforge_agents`; the CLI command is `evalforge`). Releases go through
|
|
356
|
+
`.github/workflows/release.yml` with PyPI Trusted Publishing, so no API token
|
|
357
|
+
is stored anywhere.
|
|
358
|
+
|
|
359
|
+
One-time setup:
|
|
360
|
+
|
|
361
|
+
1. On PyPI → *Your account* → *Publishing* → *Add a new pending publisher*:
|
|
362
|
+
project `evalforge-agents`, owner `AbhilashSomigari`, repository
|
|
363
|
+
`evalforge`, workflow `release.yml`, environment `pypi`.
|
|
364
|
+
2. Optional but recommended: in the GitHub repo → *Settings* →
|
|
365
|
+
*Environments* → `pypi`, add yourself as a required reviewer so every
|
|
366
|
+
release waits for a manual approval.
|
|
367
|
+
|
|
368
|
+
Each release: bump `version` in `pyproject.toml`, commit, then push a
|
|
369
|
+
matching tag — the workflow refuses a tag that doesn't match:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
git tag v0.1.0 && git push origin v0.1.0
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
It re-runs lint, type checks and tests, builds the sdist and wheel, checks
|
|
376
|
+
them with `twine check --strict`, then uploads.
|
|
377
|
+
|
|
378
|
+
## What comes next
|
|
379
|
+
|
|
380
|
+
The next platform milestones are: distributed workers; human review queues; richer LLM/NLI groundedness graders; environment snapshots and state-based outcome grading; provider-native OpenAI/Anthropic adapters; statistical confidence intervals; flaky-eval detection; prompt/model matrix experiments; GitHub PR annotations; and an observability UI built on the same run schema.
|
|
381
|
+
|
|
382
|
+
## Design principle
|
|
383
|
+
|
|
384
|
+
**The run artifact is the product.** The UI is only one consumer. Every trial stores the task, outcome-facing output, tool calls, trajectory, grader results, latency, usage and cost so the same evidence can drive CI, debugging, dashboards, human review, and future graders.
|