agentprdiff 0.2.2__tar.gz → 0.2.3__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.
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/CHANGELOG.md +37 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/PKG-INFO +1 -1
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/pyproject.toml +1 -1
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/graders/semantic.py +53 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/reporters.py +23 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/scaffold.py +164 -13
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/.gitignore +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/LICENSE +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/README.md +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/examples/quickstart/README.md +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/examples/regression-tour/README.md +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/__init__.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/adapters/__init__.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/adapters/anthropic.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/adapters/openai.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/adapters/pricing.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/cli.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/core.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/differ.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/filtering.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/graders/__init__.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/graders/deterministic.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/loader.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/runner.py +0 -0
- {agentprdiff-0.2.2 → agentprdiff-0.2.3}/src/agentprdiff/store.py +0 -0
|
@@ -8,6 +8,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.2.3] — 2026-04-28
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Semantic-judge banner in `check` and `review` output.** `TerminalReporter`
|
|
16
|
+
and `ReviewReporter` now print one line — `semantic judge: <description>`
|
|
17
|
+
— directly under the header whenever the suite contains at least one
|
|
18
|
+
`semantic(...)` grader. The description names the active backend
|
|
19
|
+
(`fake_judge`, `openai/<model>`, or `anthropic/<model>`) and the env-var
|
|
20
|
+
signal that selected it, with explicit `silent fallback` wording when
|
|
21
|
+
no judge is configured. Closes the most common adoption trap: shipping
|
|
22
|
+
suites whose semantic coverage is decorative because no key was set
|
|
23
|
+
and the runner stayed quiet about it. Suites without `semantic(...)`
|
|
24
|
+
are unaffected — no banner is printed. New helpers
|
|
25
|
+
`agentprdiff.graders.semantic.describe_default_judge()` and
|
|
26
|
+
`case_uses_semantic()` power the rendering and are reusable by
|
|
27
|
+
third-party tooling.
|
|
28
|
+
- **Scaffolded workflow YAML now flags judge-SDK installs explicitly.**
|
|
29
|
+
`_TPL_WORKFLOW` ships commented `pip install anthropic` /
|
|
30
|
+
`pip install openai` lines tied to the chosen `AGENTGUARD_JUDGE` mode,
|
|
31
|
+
with guidance that a missing SDK raises `ImportError` rather than
|
|
32
|
+
falling back silently. Pairs with the in-band judge banner for
|
|
33
|
+
end-to-end coverage of the silent-fake_judge trap.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Adoption checklist in `AGENTS.md` now requires the adopter to verify
|
|
38
|
+
the installed CLI supports the documented commands (`agentprdiff
|
|
39
|
+
check --help`) before writing run commands into the case dossier —
|
|
40
|
+
prevents the "source docs reference `--case` but the pinned wheel
|
|
41
|
+
predates it" confusion reported during 0.2.x adoption.
|
|
42
|
+
- New `Step 5b — decide and document the semantic-judge mode` mandates
|
|
43
|
+
a `## Semantic Judge Keys` section in `suites/README.md` and an
|
|
44
|
+
explicit `AGENTGUARD_JUDGE=<mode>` line in the workflow YAML, replacing
|
|
45
|
+
the implicit "first available key wins" precedence with a deliberate
|
|
46
|
+
declaration.
|
|
47
|
+
|
|
11
48
|
## [0.2.2] — 2026-04-28
|
|
12
49
|
|
|
13
50
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentprdiff
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Guard your LLM agents in CI. Snapshot tests that catch behavioral regressions when models, prompts, or vendors change.
|
|
5
5
|
Project-URL: Homepage, https://github.com/vnageshwaran-de/agentprdiff
|
|
6
6
|
Project-URL: Documentation, https://github.com/vnageshwaran-de/agentprdiff#readme
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentprdiff"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Guard your LLM agents in CI. Snapshot tests that catch behavioral regressions when models, prompts, or vendors change."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -178,3 +178,56 @@ def _default_judge() -> Judge:
|
|
|
178
178
|
if choice == "anthropic" or (not choice and os.environ.get("ANTHROPIC_API_KEY")):
|
|
179
179
|
return anthropic_judge()
|
|
180
180
|
return fake_judge
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# Default model names mirrored from `openai_judge` and `anthropic_judge`. Kept
|
|
184
|
+
# in module scope so `describe_default_judge` reports the same string the
|
|
185
|
+
# default judge would actually use without instantiating the judge (which
|
|
186
|
+
# would import the SDK lazily).
|
|
187
|
+
_DEFAULT_OPENAI_MODEL = "gpt-4o-mini"
|
|
188
|
+
_DEFAULT_ANTHROPIC_MODEL = "claude-haiku-4-5-20251001"
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def describe_default_judge() -> str:
|
|
192
|
+
"""Return a one-line description of the currently-selected default judge.
|
|
193
|
+
|
|
194
|
+
Mirrors the env-var precedence in :func:`_default_judge` so the string is
|
|
195
|
+
a faithful preview of what `semantic(...)` graders without an explicit
|
|
196
|
+
`judge=` argument will use at runtime. Reporters print this once per run
|
|
197
|
+
so the silent fake_judge fallback (no key set, no AGENTGUARD_JUDGE) is
|
|
198
|
+
visible at the moment a suite executes — not buried in trace JSON.
|
|
199
|
+
|
|
200
|
+
Examples:
|
|
201
|
+
- ``"fake_judge (AGENTGUARD_JUDGE=fake)"``
|
|
202
|
+
- ``"openai/gpt-4o-mini (OPENAI_API_KEY set)"``
|
|
203
|
+
- ``"anthropic/claude-haiku-4-5-20251001 (AGENTGUARD_JUDGE=anthropic)"``
|
|
204
|
+
- ``"fake_judge (no AGENTGUARD_JUDGE, no OPENAI_API_KEY/ANTHROPIC_API_KEY — silent fallback)"``
|
|
205
|
+
"""
|
|
206
|
+
choice = (os.environ.get("AGENTGUARD_JUDGE") or "").lower()
|
|
207
|
+
if choice == "fake":
|
|
208
|
+
return "fake_judge (AGENTGUARD_JUDGE=fake)"
|
|
209
|
+
if choice == "openai":
|
|
210
|
+
return f"openai/{_DEFAULT_OPENAI_MODEL} (AGENTGUARD_JUDGE=openai)"
|
|
211
|
+
if choice == "anthropic":
|
|
212
|
+
return (
|
|
213
|
+
f"anthropic/{_DEFAULT_ANTHROPIC_MODEL} (AGENTGUARD_JUDGE=anthropic)"
|
|
214
|
+
)
|
|
215
|
+
if not choice and os.environ.get("OPENAI_API_KEY"):
|
|
216
|
+
return f"openai/{_DEFAULT_OPENAI_MODEL} (OPENAI_API_KEY set)"
|
|
217
|
+
if not choice and os.environ.get("ANTHROPIC_API_KEY"):
|
|
218
|
+
return f"anthropic/{_DEFAULT_ANTHROPIC_MODEL} (ANTHROPIC_API_KEY set)"
|
|
219
|
+
return (
|
|
220
|
+
"fake_judge (no AGENTGUARD_JUDGE, no OPENAI_API_KEY/ANTHROPIC_API_KEY"
|
|
221
|
+
" — silent fallback)"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def case_uses_semantic(grader_results: list[GradeResult]) -> bool:
|
|
226
|
+
"""Return True if any grader in `grader_results` was a `semantic()` grader.
|
|
227
|
+
|
|
228
|
+
Detection is based on the grader name format produced by :func:`semantic`
|
|
229
|
+
(``semantic(<rubric>)``). This is stable because the public grader name is
|
|
230
|
+
part of the user-visible grading contract; reporters and serializers
|
|
231
|
+
already depend on it.
|
|
232
|
+
"""
|
|
233
|
+
return any(r.grader_name.startswith("semantic(") for r in grader_results)
|
|
@@ -17,6 +17,7 @@ from rich.table import Table
|
|
|
17
17
|
from rich.text import Text
|
|
18
18
|
|
|
19
19
|
from .differ import AssertionChange, TraceDelta
|
|
20
|
+
from .graders.semantic import case_uses_semantic, describe_default_judge
|
|
20
21
|
from .runner import CaseReport, RunReport
|
|
21
22
|
|
|
22
23
|
|
|
@@ -35,6 +36,7 @@ class TerminalReporter:
|
|
|
35
36
|
style="dim",
|
|
36
37
|
)
|
|
37
38
|
self.console.print(header)
|
|
39
|
+
_maybe_print_judge_banner(self.console, report)
|
|
38
40
|
|
|
39
41
|
table = Table(show_header=True, header_style="bold", show_lines=False, expand=True)
|
|
40
42
|
table.add_column("Case", style="bold")
|
|
@@ -130,6 +132,26 @@ def _format_delta(value: float, fmt: str) -> str:
|
|
|
130
132
|
return f"[{color}]{text}[/{color}]"
|
|
131
133
|
|
|
132
134
|
|
|
135
|
+
def _maybe_print_judge_banner(console: Console, report: RunReport) -> None:
|
|
136
|
+
"""Print "semantic judge: <mode>" once per run if any case used semantic().
|
|
137
|
+
|
|
138
|
+
Skipped silently when no case has a semantic grader — most suites don't
|
|
139
|
+
use them and the banner would be noise. When a suite *does* have semantic
|
|
140
|
+
coverage, the banner makes the silent fake_judge fallback (no key, no
|
|
141
|
+
AGENTGUARD_JUDGE) loud at the moment of execution rather than buried in
|
|
142
|
+
trace JSON. Coloured yellow when fake_judge would run so the warning is
|
|
143
|
+
visually distinct from real-judge runs.
|
|
144
|
+
"""
|
|
145
|
+
if not any(case_uses_semantic(cr.grader_results) for cr in report.case_reports):
|
|
146
|
+
return
|
|
147
|
+
description = describe_default_judge()
|
|
148
|
+
style = "yellow" if description.startswith("fake_judge") else "dim"
|
|
149
|
+
line = Text()
|
|
150
|
+
line.append("semantic judge: ", style="dim")
|
|
151
|
+
line.append(description, style=style)
|
|
152
|
+
console.print(line)
|
|
153
|
+
|
|
154
|
+
|
|
133
155
|
# ---------------------------------------------------------------------------
|
|
134
156
|
# ReviewReporter — verbose per-case view for `agentprdiff review`.
|
|
135
157
|
# ---------------------------------------------------------------------------
|
|
@@ -175,6 +197,7 @@ class ReviewReporter:
|
|
|
175
197
|
style="dim",
|
|
176
198
|
)
|
|
177
199
|
self.console.print(header)
|
|
200
|
+
_maybe_print_judge_banner(self.console, report)
|
|
178
201
|
self.console.print()
|
|
179
202
|
|
|
180
203
|
def _render_footer(self, report: RunReport) -> None:
|
|
@@ -378,6 +378,36 @@ locally (rather than the keyword-matching fake fallback), also set
|
|
|
378
378
|
[AGENTS.md → API keys](https://github.com/vnageshwaran-de/agentprdiff/blob/main/AGENTS.md#api-keys--what-to-set-where-and-how-to-ask-the-user-about-them)
|
|
379
379
|
for the full picture, including CI secret setup.
|
|
380
380
|
|
|
381
|
+
## Semantic Judge Keys
|
|
382
|
+
|
|
383
|
+
`semantic(...)` graders need an LLM judge to render verdicts. Without
|
|
384
|
+
one, agentprdiff silently falls back to `fake_judge` (keyword matching) —
|
|
385
|
+
the suite reports PASS even when no LLM ever ran. Make the judge mode
|
|
386
|
+
explicit so the absence-or-presence of LLM scoring is never ambiguous in
|
|
387
|
+
CI logs.
|
|
388
|
+
|
|
389
|
+
**This suite's mode:** TODO — fill in one of:
|
|
390
|
+
|
|
391
|
+
- `fake_judge` — free, keyword matching only. Acceptable when the rubric
|
|
392
|
+
reduces cleanly to keywords; brittle otherwise. Set
|
|
393
|
+
`AGENTGUARD_JUDGE=fake`.
|
|
394
|
+
- Real Anthropic judge — recommended for cost.
|
|
395
|
+
`AGENTGUARD_JUDGE=anthropic` plus `ANTHROPIC_API_KEY=...`.
|
|
396
|
+
- Real OpenAI judge — `AGENTGUARD_JUDGE=openai` plus `OPENAI_API_KEY=...`.
|
|
397
|
+
- `Not applicable` — this suite has no `semantic(...)` graders. Verify with
|
|
398
|
+
`grep -n "semantic(" suites/{name}.py`. State this explicitly anyway so
|
|
399
|
+
the next reviewer knows the absence of judge config is deliberate.
|
|
400
|
+
|
|
401
|
+
**Reproduce CI's mode locally:** export the same env vars CI sets and run:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
agentprdiff check suites/{name}.py --case <a_case_with_semantic>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Confirm the trace shows non-zero `cost_usd` on the judge call (real
|
|
408
|
+
judges) or a flat zero (fake_judge). A real-judge config that traces zero
|
|
409
|
+
cost means the silent fallback bit you — re-check the env vars.
|
|
410
|
+
|
|
381
411
|
## Run locally
|
|
382
412
|
|
|
383
413
|
```bash
|
|
@@ -409,23 +439,110 @@ or meaningfully change a case.
|
|
|
409
439
|
If a case fails in CI and you don't recognize the name, this file is the
|
|
410
440
|
fastest path back to *what the case is pinning and why it matters*.
|
|
411
441
|
|
|
412
|
-
##
|
|
442
|
+
## Running the suite
|
|
443
|
+
|
|
444
|
+
Run every case from the repository root:
|
|
413
445
|
|
|
414
446
|
```bash
|
|
415
|
-
agentprdiff check suites/{name}.py
|
|
416
|
-
agentprdiff check suites/{name}.py --case happy_path # one case (substring or glob)
|
|
417
|
-
agentprdiff check suites/{name}.py --list # discover case names
|
|
418
|
-
agentprdiff check suites/{name}.py --skip slow # everything except a pattern
|
|
447
|
+
agentprdiff check suites/{name}.py
|
|
419
448
|
```
|
|
420
449
|
|
|
421
|
-
|
|
422
|
-
|
|
450
|
+
If the installed wheel in your virtualenv predates the `--case`, `--list`,
|
|
451
|
+
and `review` commands, use a local source checkout of agentprdiff next to
|
|
452
|
+
this repository. Substitute `.venv-agentprdiff/` with whatever virtualenv
|
|
453
|
+
your project actually uses:
|
|
423
454
|
|
|
424
455
|
```bash
|
|
425
|
-
agentprdiff
|
|
456
|
+
.venv-agentprdiff/bin/agentprdiff check suites/{name}.py
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### List the available cases
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
agentprdiff check suites/{name}.py --list
|
|
463
|
+
# or, with a local source checkout:
|
|
464
|
+
PYTHONPATH=../agentprdiff/src .venv-agentprdiff/bin/python -m agentprdiff.cli \\
|
|
465
|
+
check suites/{name}.py --list
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Run one case
|
|
469
|
+
|
|
470
|
+
Filter by substring or glob (case-insensitive). A filter that matches
|
|
471
|
+
nothing exits 2 — no silent zero-runs:
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
agentprdiff check suites/{name}.py --case happy_path
|
|
475
|
+
agentprdiff check suites/{name}.py --case "*refund*"
|
|
476
|
+
agentprdiff check suites/{name}.py --skip slow # everything except a pattern
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Use `review` for verbose, exit-0 iteration
|
|
480
|
+
|
|
481
|
+
`review` runs the same comparison `check` does but renders one verbose
|
|
482
|
+
panel per case (input echo, every assertion's `was → now` verdict, cost /
|
|
483
|
+
latency / token deltas, tool-sequence diff, unified output diff) and
|
|
484
|
+
exits 0 even on regression — safe to wire into a watcher / `entr` / `fzf`
|
|
485
|
+
loop without your shell going red on every iteration:
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
agentprdiff review suites/{name}.py --case happy_path
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Reach for `check` when you want the CI gate's exit semantics; reach for
|
|
492
|
+
`review` while you're working.
|
|
493
|
+
|
|
494
|
+
## Seeing a regression locally
|
|
495
|
+
|
|
496
|
+
These are safe local experiments to confirm the suite would catch a real
|
|
497
|
+
behavior drift. Revert the edit after observing the failure — none of
|
|
498
|
+
these changes should be committed.
|
|
499
|
+
|
|
500
|
+
1. **Change the system prompt** in your production agent module to a
|
|
501
|
+
different sentence, then run:
|
|
502
|
+
|
|
503
|
+
```bash
|
|
504
|
+
agentprdiff check suites/{name}.py --case happy_path
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Expected result: the prompt-text grader (e.g. `system_prompt_is(...)`)
|
|
508
|
+
fails because the recorded LLM call no longer matches the baseline
|
|
509
|
+
contract.
|
|
510
|
+
|
|
511
|
+
2. **Change a stub fixture** in `suites/_stubs.py` so it returns different
|
|
512
|
+
values from the case expects, then run:
|
|
513
|
+
|
|
514
|
+
```bash
|
|
515
|
+
agentprdiff check suites/{name}.py --case happy_path
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
Expected result: the entity / value graders fail and the trace diff
|
|
519
|
+
shows the output text changed.
|
|
520
|
+
|
|
521
|
+
3. **Change an exception handler** in production code (e.g. so a failure
|
|
522
|
+
path returns a string where callers expect `None`), then run:
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
agentprdiff check suites/{name}.py --case happy_path
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
Expected result: the contract grader pinning the failure-mode return
|
|
529
|
+
value fails, proving callers would no longer receive the fallback
|
|
530
|
+
signal they depend on.
|
|
531
|
+
|
|
532
|
+
## Updating the baseline after an intentional change
|
|
533
|
+
|
|
534
|
+
When a behavior change is intentional, re-record the baseline for only
|
|
535
|
+
the affected case and commit the resulting JSON diff so reviewers see the
|
|
536
|
+
before/after:
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
agentprdiff record suites/{name}.py --case happy_path
|
|
426
540
|
git add .agentprdiff/baselines/
|
|
427
541
|
```
|
|
428
542
|
|
|
543
|
+
Drop `--case` to re-record every case at once. Either form is safe to
|
|
544
|
+
re-run — `record` overwrites baselines in place, no accumulation.
|
|
545
|
+
|
|
429
546
|
## Cases
|
|
430
547
|
|
|
431
548
|
### `happy_path`
|
|
@@ -453,11 +570,19 @@ affected lines without re-reading the whole agent.
|
|
|
453
570
|
One sentence; concrete and specific. ("Refunds silently fail" not "the
|
|
454
571
|
agent misbehaves.")
|
|
455
572
|
|
|
573
|
+
**How to exercise this case in isolation.**
|
|
574
|
+
|
|
575
|
+
```bash
|
|
576
|
+
agentprdiff check suites/{name}.py --case happy_path # CI-style, exit 1 on regression
|
|
577
|
+
agentprdiff review suites/{name}.py --case happy_path # verbose, exit 0
|
|
578
|
+
agentprdiff record suites/{name}.py --case happy_path # re-record after intentional change
|
|
579
|
+
```
|
|
580
|
+
|
|
456
581
|
---
|
|
457
582
|
|
|
458
583
|
<!--
|
|
459
584
|
Template for additional cases. Copy-paste this block, change the heading,
|
|
460
|
-
fill in the
|
|
585
|
+
fill in the five sections plus the run-commands block.
|
|
461
586
|
|
|
462
587
|
### `<case_name>`
|
|
463
588
|
|
|
@@ -475,6 +600,14 @@ fill in the four sections.
|
|
|
475
600
|
|
|
476
601
|
**Application impact.**
|
|
477
602
|
|
|
603
|
+
**How to exercise this case in isolation.**
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
agentprdiff check suites/{name}.py --case <case_name>
|
|
607
|
+
agentprdiff review suites/{name}.py --case <case_name>
|
|
608
|
+
agentprdiff record suites/{name}.py --case <case_name>
|
|
609
|
+
```
|
|
610
|
+
|
|
478
611
|
-->
|
|
479
612
|
'''
|
|
480
613
|
|
|
@@ -505,16 +638,34 @@ jobs:
|
|
|
505
638
|
- run: |
|
|
506
639
|
python -m pip install --upgrade pip
|
|
507
640
|
pip install -r requirements.txt agentprdiff
|
|
641
|
+
# Semantic-judge SDKs. The agentprdiff wheel imports these lazily,
|
|
642
|
+
# so installing only the one matching AGENTGUARD_JUDGE below keeps
|
|
643
|
+
# the CI environment lean. If your suite has no semantic() graders
|
|
644
|
+
# (see suites/README.md "Semantic Judge Keys"), drop both lines.
|
|
645
|
+
# Uncomment exactly one to match the chosen judge mode:
|
|
646
|
+
# pip install anthropic # if AGENTGUARD_JUDGE=anthropic
|
|
647
|
+
# pip install openai # if AGENTGUARD_JUDGE=openai (already installed if your agent uses OpenAI)
|
|
648
|
+
# Without the matching SDK, semantic() raises ImportError at first
|
|
649
|
+
# use and the case fails — louder than the silent fake_judge trap.
|
|
508
650
|
- env:
|
|
509
651
|
# TODO: match the env var your production agent reads.
|
|
510
652
|
# Common names: OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY,
|
|
511
653
|
# or a project-specific name. Add the secret in:
|
|
512
654
|
# Settings -> Secrets and variables -> Actions -> New repository secret.
|
|
513
655
|
OPENAI_API_KEY: ${{{{ secrets.OPENAI_API_KEY }}}}
|
|
514
|
-
#
|
|
515
|
-
#
|
|
516
|
-
#
|
|
517
|
-
#
|
|
656
|
+
# Semantic-judge mode (see AGENTS.md "Step 5b — decide and document
|
|
657
|
+
# the semantic-judge mode"). Pick one and uncomment; do NOT rely on
|
|
658
|
+
# the implicit selection order — it makes "which provider did I
|
|
659
|
+
# get?" ambiguous in CI logs. If your suite has no semantic()
|
|
660
|
+
# graders, leave all three commented and CI runs free.
|
|
661
|
+
#
|
|
662
|
+
# AGENTGUARD_JUDGE: fake # keyword matching, free
|
|
663
|
+
# AGENTGUARD_JUDGE: anthropic # real judge, cheaper; pair with ANTHROPIC_API_KEY below
|
|
664
|
+
# AGENTGUARD_JUDGE: openai # real judge; pair with OPENAI_API_KEY above
|
|
665
|
+
#
|
|
666
|
+
# Pair the chosen judge with its key. Without a key, semantic()
|
|
667
|
+
# falls back to fake_judge silently and you'll see PASS without
|
|
668
|
+
# the LLM judge ever running.
|
|
518
669
|
ANTHROPIC_API_KEY: ${{{{ secrets.ANTHROPIC_API_KEY }}}}
|
|
519
670
|
run: |
|
|
520
671
|
if [ -z "${{OPENAI_API_KEY}}" ]; then
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|