ciagent 0.6.0__tar.gz → 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. ciagent-0.8.0/CHANGELOG.md +264 -0
  2. ciagent-0.8.0/PKG-INFO +307 -0
  3. ciagent-0.8.0/README.md +264 -0
  4. {ciagent-0.6.0 → ciagent-0.8.0}/pyproject.toml +3 -2
  5. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/capture.py +35 -15
  6. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/cli.py +769 -24
  7. ciagent-0.8.0/src/agentci/engine/check_generator.py +330 -0
  8. ciagent-0.8.0/src/agentci/engine/judge_audit.py +427 -0
  9. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/mock_runner.py +37 -5
  10. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/parallel.py +50 -7
  11. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/path.py +15 -3
  12. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/reporter.py +269 -6
  13. ciagent-0.8.0/src/agentci/engine/stability.py +375 -0
  14. ciagent-0.8.0/src/agentci/examples/__init__.py +3 -0
  15. ciagent-0.8.0/src/agentci/examples/demo_spec.yaml +73 -0
  16. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/schema/spec_models.py +6 -2
  17. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/templates/report.html.j2 +45 -0
  18. ciagent-0.6.0/CHANGELOG.md +0 -123
  19. ciagent-0.6.0/PKG-INFO +0 -156
  20. ciagent-0.6.0/README.md +0 -113
  21. ciagent-0.6.0/src/agentci/_version.py +0 -3
  22. {ciagent-0.6.0 → ciagent-0.8.0}/.gitignore +0 -0
  23. {ciagent-0.6.0 → ciagent-0.8.0}/LICENSE +0 -0
  24. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/__init__.py +0 -0
  25. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/adapters/__init__.py +0 -0
  26. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/adapters/base.py +0 -0
  27. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/adapters/generic.py +0 -0
  28. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/adapters/langgraph.py +0 -0
  29. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/adapters/openai_agents.py +0 -0
  30. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/assertions.py +0 -0
  31. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/baselines.py +0 -0
  32. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/config.py +0 -0
  33. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/cost.py +0 -0
  34. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/diff_engine.py +0 -0
  35. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/__init__.py +0 -0
  36. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/correctness.py +0 -0
  37. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/cost.py +0 -0
  38. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/cost_estimator.py +0 -0
  39. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/diff.py +0 -0
  40. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/judge.py +0 -0
  41. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/metrics.py +0 -0
  42. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/results.py +0 -0
  43. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/runner.py +0 -0
  44. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/engine/span_assertions.py +0 -0
  45. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/exceptions.py +0 -0
  46. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/loader.py +0 -0
  47. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/mocks.py +0 -0
  48. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/models.py +0 -0
  49. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/py.typed +0 -0
  50. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/pytest_plugin.py +0 -0
  51. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/report.py +0 -0
  52. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/runner.py +0 -0
  53. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/schema/__init__.py +0 -0
  54. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/schema/agentci_spec.schema.json +0 -0
  55. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/schema/generate_schema.py +0 -0
  56. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/templates/__init__.py +0 -0
  57. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/templates/github_action.yml.j2 +0 -0
  58. {ciagent-0.6.0 → ciagent-0.8.0}/src/agentci/templates/pre_push_hook.sh.j2 +0 -0
@@ -0,0 +1,264 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.8.0] - 2026-07-06
9
+
10
+ ### Added — Claude Code plugin (F5: your coding agent onboards and operates AgentCI)
11
+ - The repo is now a Claude Code plugin marketplace
12
+ (`/plugin marketplace add suniel12/AgentCI`, `/plugin install agentci@agentci`)
13
+ with two skills: **onboard** (write the runner, record goldens via
14
+ `bootstrap --yes`, generate + tighten the spec, verify with `test --runs 3`)
15
+ and **check** (run the right check after agent changes; route failures by
16
+ flip source; never weaken a correct check to go green)
17
+ - `agentci bootstrap --yes`: fully non-interactive golden recording (requires
18
+ `--queries`); bootstrap now runs the runner through the same TraceContext
19
+ capture path as `agentci test`, so **runners returning plain strings work**
20
+ (previously crashed on anything but a Trace)
21
+ - `--format json` output now includes the agent's `answer` text per query
22
+ (eng-review work item: JSON consumers need what the agent said, not just
23
+ verdicts; also unblocks JSON as a judge-audit answers source)
24
+ - Tests: skills are lint-gated — every `agentci` command and flag a skill
25
+ teaches is asserted to exist in the CLI, so skill docs cannot rot silently
26
+
27
+ ### Fixed — from Codex cross-model review of this branch
28
+ - **Nested `TraceContext` no longer double-records or loses capture**: a
29
+ Trace-returning runner that uses `TraceContext` internally, wrapped again by
30
+ the executor, used to stack SDK patches (every LLM call recorded once per
31
+ wrapper) and its exit cleared the outer context entirely. Patches now install
32
+ only in the outermost context, and exits restore the enclosing context via
33
+ contextvar tokens. Affected `agentci test` as well as the new bootstrap path
34
+ - JSON `answer` field now uses the correctness layer's extractor (metadata →
35
+ last span output), so trace shapes the evaluator can grade also serialize
36
+ their answer instead of `null`
37
+
38
+ ### Added — zero-key demo (`uvx ciagent test --mock --runs 3`)
39
+ - `agentci test --mock` with no `agentci_spec.yaml` in the working directory now
40
+ falls back to a bundled demo spec (8 synthetic support-agent queries), clearly
41
+ labeled as demo mode with synthetic data. An explicitly passed `--config` that
42
+ is missing remains an error — the fallback only applies to the default path
43
+ - Demo multi-run sessions simulate a pseudo-flaky agent by default with the new
44
+ `"spread"` style (one query breaks per run), so the aggregate score stays
45
+ constant while individual verdicts flip — the report the demo exists to show.
46
+ `AGENTCI_MOCK_FLAKY=0` turns the simulation off; non-demo specs are unaffected
47
+ (flakiness stays env-var opt-in, `"alternate"` style unchanged)
48
+ - Missing spec without `--mock` now exits with a hint pointing at both
49
+ `agentci init` and the zero-key demo command
50
+
51
+ ### Changed — stability report hardening (pre-launch fixes from eng review)
52
+ - **Flip attribution now compares per-layer sub-verdicts first**: if every
53
+ deterministic check returned the same outcome across runs and only the LLM judge's
54
+ verdict changed, the flip is `judge-flake` even when the answer text was
55
+ paraphrased (previously mislabeled `agent-variance`)
56
+ - **New flip source `infra-error`**: a judge API failure counted as a fail no longer
57
+ reads as `judge-flake` — one transient hiccup must not say "fix your rubric"
58
+ - **Console shows observed facts only** (verdict history + pass rate); pass@k /
59
+ pass^k move to JSON output as clearly labeled estimates (at small k they restate
60
+ the pass rate)
61
+ - Duplicate query texts in a spec are flagged (they merge into one stability
62
+ record); queries missing from some runs are marked `partial (k/N runs)`
63
+ - `agentci run` (legacy suite runner) now prints a deprecation warning pointing at
64
+ `agentci test`; removal planned for 0.9.0
65
+ - Removed stale `src/agentci/_version.py` (said 0.1.0; version comes from package
66
+ metadata)
67
+
68
+ ## [0.7.0] - 2026-07-05
69
+
70
+ ### Added
71
+
72
+ #### KB-Derived Fact Checks — `agentci generate-checks`
73
+ Mines the knowledge base for hard facts (prices, rates, SKUs, versions,
74
+ explicit quantities) and proposes them as deterministic assertions on
75
+ existing spec queries. One LLM call at authoring time; the checks run
76
+ deterministically forever at zero cost.
77
+
78
+ - **Brittleness gate**: every candidate is validated against recorded golden
79
+ answers before it is offered — a check that would fail a known-good answer
80
+ is rejected automatically with the failing answer shown
81
+ - Only non-paraphrasable facts; prose facts become variant sets
82
+ (`any_expected_in_answer`), never single literal strings; only
83
+ `any_expected_in_answer` / `not_in_answer` / `regex_match` are proposed
84
+ - Candidates without a golden answer are `unvalidated` and never
85
+ auto-accepted, even with `--yes`; interactive review for everything else
86
+ - Merge never overwrites user-written assertions; `.bak` backup on write;
87
+ `--dry-run` mode
88
+ - New module `agentci.engine.check_generator`; 21 new tests
89
+
90
+ #### Judge Audit — `agentci judge-audit`
91
+ Meta-evaluation of the LLM judge against ground truth you already have,
92
+ re-scoring recorded golden baselines (the agent is never re-run):
93
+
94
+ - **Mode 1 — judge vs. deterministic checks**: independent verdicts on the
95
+ same recorded answer; reports the disagreement matrix. The killer row:
96
+ answers the judge PASSED that a deterministic fact-check FAILED
97
+ (shared-context judge blindness, detected automatically)
98
+ - **Mode 2 — retest stability** (`--repeats K`, default 3): same answer
99
+ judged K times; verdict flips on identical input are the judge's noise floor
100
+ - **Mode 3 — hand labels** (`--labels FILE`): agreement + Cohen's κ against
101
+ human review, with standard trust thresholds
102
+ - Scoped claim stated in the report itself: measured on fact-checkable
103
+ queries; one-directional (disqualifying) signal for judgment-only queries
104
+ - Verdicts: TRUSTWORTHY / NEEDS CALIBRATION / UNRELIABLE (exit 1) /
105
+ ERROR when the judge never ran (exit 2 — a judge that couldn't run is
106
+ never scored)
107
+ - `--sample N` cost cap; console + JSON output
108
+ - New module `agentci.engine.judge_audit`; 27 new tests
109
+
110
+ #### Stability Report — `agentci test --runs N`
111
+ A stable suite score can hide per-query verdict flips: the aggregate holds
112
+ because the errors move around. `--runs N` executes the whole suite N times
113
+ and reports what a single run cannot show:
114
+
115
+ - Per-query verdict history (✅❌✅), pass rate, pass@k and pass^k estimates
116
+ - Suite score per run printed side by side with the queries that flipped
117
+ - **Flip-source attribution** — every flip is labelled:
118
+ - `agent-variance`: the agent's output changed → fix the agent
119
+ - `judge-flake`: same output, the LLM judge changed its verdict → fix the eval
120
+ - `mixed`: near-identical paraphrase with a judge configured — not guessed
121
+ Attribution rests on a structural fact: deterministic checks cannot flip on
122
+ identical output, so identical answer + tools + flipped verdict = judge.
123
+ - Exit semantics: flaky-but-passing exits 0 (warnings only); queries failing
124
+ in EVERY run exit 1; `--fail-on-flaky` escalates flips to exit 1
125
+ - Works in every format: console section, GitHub `::warning` annotations,
126
+ `stability` block in JSON, stability card in the HTML report
127
+ - Mock mode support: `AGENTCI_MOCK_FLAKY=1 agentci test --mock --runs 3`
128
+ demonstrates the report with zero API keys
129
+ - New module `agentci.engine.stability` (`build_stability_report`,
130
+ `StabilityReport`, `QueryStability`, `FlipSource`); 21 new tests
131
+
132
+ ### Changed
133
+ - **`expected_tools` now asserts by default**: a missing expected tool produces a WARN
134
+ (tool recall gates at 1.0 unless `min_tool_recall` explicitly loosens it). Previously,
135
+ without `min_tool_recall`, a recall of 0.0 displayed as PASS with a checkmark.
136
+ - **`expected_tools: []` now asserts that no tools are called**: an explicit empty list
137
+ produces a WARN if the agent called any tool. Previously it was silently skipped.
138
+ - `agentci doctor` no longer reports numpy as a required dependency (it was never used
139
+ by AgentCI, so every fresh install showed a false failure).
140
+
141
+ ### Docs
142
+ - README: fixed the quickstart spec example (was missing the required `agent:` field),
143
+ added CI badge, "Check facts in code" section, docs index
144
+ - Rewrote `docs/quickstart.md`, `docs/ci-cd.md`, `docs/cost-tracking.md`, and
145
+ `docs/golden-traces.md` to match the current `agentci test` workflow
146
+ - Fixed dead clone URLs in CONTRIBUTING.md and quickstart
147
+ - Removed unused demo GIF variants and cast recordings (6 files, ~2 MB)
148
+
149
+ ## [0.6.0] - 2026-03-05
150
+
151
+ ### Added
152
+
153
+ #### `final_output` Auto-Capture
154
+ - `TraceContext._auto_extract_final_output()` called in `__exit__()` — automatically extracts the agent's answer from traces
155
+ - Extraction priority: LangGraph state messages > span `output_data` (string) > span `output_data` (dict with `content`/`message`/`text`/`output` keys) > last LLM call `output_text`
156
+ - Manual `trace.metadata["final_output"]` still takes precedence (no overwrite)
157
+ - LangGraph adapter: auto-sets `final_output` from last AI message in `parse_state()`
158
+ - OpenAI Agents adapter: auto-sets `final_output` from last span output in `on_trace_end()`
159
+
160
+ #### `agentci calibrate` Command
161
+ - Runs N sample queries against the live agent, measures actual metrics, shows Rich comparison table
162
+ - Updates spec budgets with headroom: +50% for LLM/tool calls, +100% for tokens/cost
163
+ - Flags: `--samples N` (default 2), `--dry-run`, `--yes`, `--spec PATH`
164
+
165
+ #### Strict Tool Sequence Assertions
166
+ - `PathSpec.expected_tool_sequence: Optional[list[str]]` — strict ordered tool call check
167
+ - Mismatch = WARN (soft warning) with position-level diff via `_format_sequence_diff()`
168
+
169
+ #### HTML Trace Report
170
+ - Self-contained `report.html.j2` Jinja2 template with dark theme
171
+ - Summary dashboard (pass/fail/warn counts, total cost)
172
+ - Per-query cards with status badges, answer preview, three-layer details
173
+ - Collapsible trace tree with JS toggle
174
+ - Available via `agentci test --format html --output report.html` or `agentci report -i results.json`
175
+
176
+ ### Changed
177
+ - `max_llm_calls` default in spec generator raised from 8 to 10 (better headroom for real agents)
178
+ - `max_llm_calls` fallback in mock runner raised from 3 to 10
179
+ - Calibrate command floor raised from 8 to 10
180
+ - `--format` choices in `test` and `eval` commands now include `html`
181
+ - `--output / -o` option added to `test` and `eval` commands for HTML file path
182
+ - `agentci report` command fully implemented (was stub) — converts JSON results to HTML
183
+ - 22 new tests added (570 total, up from 548 in v0.5.1)
184
+
185
+ ## [0.5.0] - 2026-03-01
186
+
187
+ ### Added
188
+
189
+ #### Three-Layer Evaluation Engine
190
+ - **Correctness layer** (Layer 1 — hard fail): keyword matching, LLM-as-a-judge, safety checks, hallucination checks, regex/exact match, JSON schema validation
191
+ - **Path layer** (Layer 2 — soft warn): tool trajectory validation, loop detection (default `max_loops=3`), routing assertions, handoff expectations
192
+ - **Cost layer** (Layer 3 — soft warn): token budgets, cost caps, LLM call limits, latency thresholds
193
+ - `runner.py` orchestrates all three layers per query
194
+ - `parallel.py` for parallel query execution across specs
195
+
196
+ #### OR-Logic Keywords
197
+ - `any_expected_in_answer` field — at least one keyword must match (complementing `expected_in_answer` which requires all)
198
+
199
+ #### LLM Judge Enhancements
200
+ - `context_file` support in `JudgeRubric` — doc-grounded judging against reference documents
201
+ - `refutes_premise` flag — injects built-in premise-correction rubric for trick questions
202
+
203
+ #### Span Assertions
204
+ - `SpanAssertionSpec` schema model for span-level assertions
205
+ - `Span.attributes: dict[str, Any]` — OTel-style span-level data propagation
206
+ - Span-level LLM judge support
207
+
208
+ #### Mock Testing Mode
209
+ - `agentci test --mock` — generates synthetic traces, zero API cost
210
+ - `mock_runner.py` — synthetic trace generation from spec expectations
211
+ - `--golden-file` flag — load Q&A pairs from JSON/CSV for mock mode
212
+
213
+ #### Cost Estimator
214
+ - `cost_estimator.py` — pre-execution cost estimates with pricing table
215
+ - Cost estimate shown before live test runs; `--yes`/`-y` skips confirmation
216
+
217
+ #### CLI Improvements
218
+ - `agentci init --generate` — AI-assisted spec generation with guided interview
219
+ - `agentci doctor` — health-check command (spec, runner, API keys, deps, CI)
220
+ - Scan-first flow: auto-scan project before questions, show summary
221
+ - Agent type auto-detected via `_detect_agent_type_from_code()`
222
+ - Skeleton template generation with TODO placeholders for zero-API-key usage
223
+ - Context-aware "Next Steps" based on mode (mock vs live)
224
+ - Non-interactive flags: `--kb-path`, `--mode`
225
+
226
+ #### Diff Engine
227
+ - Three-tier diff engine for baseline comparison
228
+ - 11 `DiffType` categories including `ROUTING_CHANGED`, `GUARDRAILS_CHANGED`
229
+ - `agentci diff` CLI command
230
+
231
+ #### Reporting
232
+ - GitHub annotations with budget cap (`MAX_INLINE_ANNOTATIONS = 10` for warnings; errors uncapped)
233
+ - JSON output format
234
+ - Prometheus metrics export
235
+
236
+ #### Trace Helpers
237
+ - `Trace.called(tool)` / `never_called(tool)` / `loop_count(tool)` — readable assertion helpers
238
+ - `Trace.cost_under(usd)` / `llm_calls_under(n)` — budget assertion helpers
239
+ - `langgraph_trace(agent_name)` — context manager shortcut for LangGraph
240
+ - `TraceContext.attach(state)` — alias for `attach_langgraph_state()`
241
+
242
+ #### Adapters
243
+ - OpenAI Agents SDK adapter (`openai_agents.py`)
244
+ - LangGraph adapter (`langgraph.py`)
245
+
246
+ #### Other
247
+ - `python-dotenv` added as core dependency
248
+ - Deep KB sampling: 2000 chars/file for spec generation
249
+ - Progressive spec building: smoke queries (3) then full queries (10-12)
250
+ - Pytest plugin entry point (`pytest11: agentci`)
251
+ - GUARDRAIL span type, HANDOFF span type
252
+
253
+ ### Changed
254
+ - `PathSpec.max_loops` now defaults to `3` (was `None`)
255
+ - Development status upgraded from Alpha to Beta
256
+ - Package version bumped to 0.5.0
257
+
258
+ ## [0.4.1] - 2026-02-20
259
+
260
+ ### Added
261
+ - Initial project structure and core models
262
+ - Basic trace capture and assertion framework
263
+ - CLI scaffolding with `agentci init`
264
+ - PyPI publishing as `ciagent`
ciagent-0.8.0/PKG-INFO ADDED
@@ -0,0 +1,307 @@
1
+ Metadata-Version: 2.4
2
+ Name: ciagent
3
+ Version: 0.8.0
4
+ Summary: Continuous Integration for AI Agents. Catch cost spikes and logic regressions before production.
5
+ Project-URL: Documentation, https://github.com/suniel12/AgentCI#readme
6
+ Project-URL: Repository, https://github.com/suniel12/AgentCI.git
7
+ Project-URL: Issues, https://github.com/suniel12/AgentCI/issues
8
+ License: Apache-2.0
9
+ License-File: LICENSE
10
+ Keywords: agents,ai,ci-cd,llm,regression,testing
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Framework :: Pytest
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Software Development :: Testing
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: click>=8.0
17
+ Requires-Dist: jinja2>=3.0
18
+ Requires-Dist: opentelemetry-api>=1.20
19
+ Requires-Dist: opentelemetry-sdk>=1.20
20
+ Requires-Dist: pydantic>=2.0
21
+ Requires-Dist: pytest>=7.0
22
+ Requires-Dist: python-dotenv>=0.19
23
+ Requires-Dist: pyyaml>=6.0
24
+ Requires-Dist: rich>=13.0
25
+ Provides-Extra: all
26
+ Requires-Dist: anthropic>=0.20; extra == 'all'
27
+ Requires-Dist: jsonschema>=4.0; extra == 'all'
28
+ Requires-Dist: langgraph>=0.1; extra == 'all'
29
+ Requires-Dist: openai>=1.0; extra == 'all'
30
+ Provides-Extra: anthropic
31
+ Requires-Dist: anthropic>=0.20; extra == 'anthropic'
32
+ Provides-Extra: dev
33
+ Requires-Dist: mypy; extra == 'dev'
34
+ Requires-Dist: pytest>=8.0; extra == 'dev'
35
+ Requires-Dist: ruff; extra == 'dev'
36
+ Provides-Extra: jsonschema
37
+ Requires-Dist: jsonschema>=4.0; extra == 'jsonschema'
38
+ Provides-Extra: langgraph
39
+ Requires-Dist: langgraph>=0.1; extra == 'langgraph'
40
+ Provides-Extra: openai
41
+ Requires-Dist: openai>=1.0; extra == 'openai'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # AgentCI
45
+
46
+ **Pytest-native regression testing for AI agents.** Catch routing changes, tool call drift, and cost spikes before production.
47
+
48
+ [![PyPI](https://img.shields.io/pypi/v/ciagent)](https://pypi.org/project/ciagent/)
49
+ [![CI](https://github.com/suniel12/AgentCI/actions/workflows/ci.yml/badge.svg)](https://github.com/suniel12/AgentCI/actions/workflows/ci.yml)
50
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
51
+ [![AGENTS.md](https://img.shields.io/badge/AGENTS.md-supported-blue)](AGENTS.md)
52
+
53
+ You changed a prompt. Your agent broke in production. Three days later, a user complained. You had no tests, no diff, no idea what went wrong.
54
+
55
+ Works with OpenAI, Anthropic, and LangGraph. Runs inside pytest.
56
+
57
+ ## See It in 30 Seconds
58
+
59
+ No install, no API keys, no config — one command runs a bundled demo suite three times on synthetic traces:
60
+
61
+ ```bash
62
+ uvx ciagent test --mock --runs 3
63
+ ```
64
+
65
+ ```
66
+ Run 1/3: 7/8 passed
67
+ Run 2/3: 7/8 passed
68
+ Run 3/3: 7/8 passed
69
+
70
+ ────────────────────────────────────────────────────────────
71
+ Stability Report
72
+ ────────────────────────────────────────────────────────────
73
+ Suite score across 3 runs: 88% / 88% / 88% ← looks stable
74
+
75
+ ⚠️ FLAKY — 3/8 queries flipped verdicts across runs: ← is not
76
+ "What's your return window?" ❌✅✅ pass_rate=0.67 source: agent-variance
77
+ "Do you ship internationally?" ✅❌✅ pass_rate=0.67 source: agent-variance
78
+ "How do I reset my password?" ✅✅❌ pass_rate=0.67 source: agent-variance
79
+
80
+ Flip sources: 3 agent-variance (fix the agent) │ 0 judge-flake (fix the eval)
81
+ ```
82
+
83
+ The aggregate score is identical every run. Three of the eight verdicts underneath it flipped. A single-run eval score would never tell you — the stability report does, and attributes every flip to the layer that caused it. (The demo simulates a flaky agent; point it at your own with the spec below.)
84
+
85
+ ## Add to Your Project
86
+
87
+ ```bash
88
+ pip install ciagent
89
+ ```
90
+
91
+ Write your golden queries — what should your agent handle, and what should it refuse?
92
+
93
+ ```yaml
94
+ # agentci_spec.yaml
95
+ agent: my-agent
96
+ # runner: any function that takes a query string and returns a response
97
+ runner: my_app.agent:run_for_agentci
98
+ queries:
99
+ - query: "How do I install AgentCI?"
100
+ correctness:
101
+ any_expected_in_answer: ["pip install", "ciagent"]
102
+ path:
103
+ expected_tools: [retrieve_docs]
104
+ cost:
105
+ max_llm_calls: 8
106
+
107
+ - query: "What's the CEO's favorite restaurant?"
108
+ correctness:
109
+ not_in_answer: ["restaurant", "favorite"]
110
+ path:
111
+ expected_tools: [] # expect no tools called for out-of-scope queries
112
+ ```
113
+
114
+ Run:
115
+
116
+ ```bash
117
+ agentci test --mock # start here: zero-cost with synthetic traces
118
+ agentci test # run live against your real agent
119
+ ```
120
+
121
+ `agentci test` evaluates each query through 3 layers — correctness, path, and cost:
122
+
123
+ ```
124
+ ============================================================
125
+
126
+ Query: How do I install AgentCI?
127
+ Answer: To install AgentCI, you can use pip with the following command:
128
+ pip install ciagent. Make sure you have Python 3.10 or later.
129
+
130
+ ✅ CORRECTNESS: PASS
131
+ ✓ Found keywords: "pip install ciagent"
132
+ ✓ LLM judge passed (score: 5 ≥ 0.6)
133
+ 📈 PATH: PASS
134
+ ✓ Tool recall: 1.000 (expected: [retrieve_docs])
135
+ ✓ Tool precision: 0.500
136
+ ✓ No loops detected
137
+ 💰 COST: PASS
138
+ ✓ LLM calls: 8 ≤ max 8
139
+
140
+ ============================================================
141
+
142
+ Query: What Python version does AgentCI require and what frameworks does it support?
143
+ Answer: AgentCI currently does not specify a required Python version
144
+ in the provided context, so I don't have that information...
145
+
146
+ ❌ CORRECTNESS: FAIL
147
+ • Expected '3.10' not found in answer
148
+ 📈 PATH: PASS
149
+ ✓ Tool recall: 1.000 (expected: [retrieve_docs])
150
+ ✓ Loops: 1 ≤ max 3
151
+ 💰 COST: PASS
152
+ ✓ LLM calls: 4 ≤ max 5
153
+
154
+ ============================================================
155
+ ```
156
+
157
+ Don't have golden queries yet? `agentci init --generate` scans your code and generates a starter spec.
158
+
159
+ ## A stable score is not a stable system
160
+
161
+ Run the identical eval three times and you can get 96% / 95% / 96% — rock solid — while
162
+ individual queries flip verdicts every run. The aggregate holds because the errors move
163
+ around. `--runs N` shows what a single run can't:
164
+
165
+ ```bash
166
+ agentci test --runs 3
167
+ ```
168
+
169
+ ```
170
+ Run 1/3: 18/19 passed
171
+ Run 2/3: 18/19 passed
172
+ Run 3/3: 18/19 passed
173
+
174
+ ────────────────────────────────────────────────────────────
175
+ Stability Report
176
+ ────────────────────────────────────────────────────────────
177
+ Suite score across 3 runs: 95% / 95% / 95%
178
+
179
+ ⚠️ FLAKY — 2/19 queries flipped verdicts across runs:
180
+ "What's your return window?" ✅❌✅ pass_rate=0.67 source: agent-variance (answer changed)
181
+ "Do you sell gift cards?" ❌✅✅ pass_rate=0.67 source: judge-flake (same answer, verdict flipped)
182
+
183
+ Flip sources: 1 agent-variance (fix the agent) │ 1 judge-flake (fix the eval) │ 0 infra-error (retry) │ 0 mixed
184
+
185
+ Stability verdict: FLAKY
186
+ ```
187
+
188
+ Every flip is attributed to its source, so it's a routed work item, not a scary number:
189
+ **agent-variance** means the agent produced different output (fix the prompt, retrieval, or
190
+ temperature); **judge-flake** means the output — or every deterministic check's outcome —
191
+ was identical but the LLM judge changed its mind (fix the rubric, or replace the judge with
192
+ a deterministic check); **infra-error** means a judge API call failed (retry, fix nothing).
193
+ Attribution is structural, not guessed: deterministic checks cannot flip on identical
194
+ output, and per-layer sub-verdicts are compared across runs. The console shows observed
195
+ facts; pass@k/pass^k estimates live in the JSON output, labeled as estimates.
196
+
197
+ Flaky-but-passing exits 0 so adoption won't break your CI; add `--fail-on-flaky` when
198
+ you're ready to gate on it. Try it with zero API keys:
199
+ `AGENTCI_MOCK_FLAKY=1 agentci test --mock --runs 3`. Details: [docs/stability.md](docs/stability.md).
200
+
201
+ ## Audit the judge itself
202
+
203
+ An LLM judge that shares your agent's context inherits your agent's blind spots: when
204
+ retrieval comes up empty, the agent answers from nothing — and the judge, reading the same
205
+ nothing, passes it. `judge-audit` measures your judge against ground truth you already have,
206
+ by re-scoring recorded baselines (the agent is never re-run):
207
+
208
+ ```bash
209
+ agentci judge-audit
210
+ ```
211
+
212
+ 1. **Judge vs. deterministic checks** — the disagreement matrix. The row that matters:
213
+ answers the judge PASSED that a hard fact-check FAILED.
214
+ 2. **Retest stability** — the same answer judged `--repeats` times; flips on identical
215
+ input are the judge's own noise floor.
216
+ 3. **Hand labels** (`--labels`) — agreement + Cohen's κ against your own review.
217
+
218
+ The claim is deliberately one-directional: a judge that fails where you *can* check it
219
+ shouldn't be trusted where you can't. Verdict: `TRUSTWORTHY` / `NEEDS CALIBRATION` /
220
+ `UNRELIABLE`. Details: [docs/judge-audit.md](docs/judge-audit.md).
221
+
222
+ ## Check facts in code. Save the judge for judgment.
223
+
224
+ Most agent failures that matter involve a hard fact — a product name, a price, a version number. Those are checkable deterministically, for free. And an LLM judge grading against the same context as your agent inherits your agent's blind spots: when retrieval comes up empty, the agent answers from nothing and the judge — reading the same nothing — passes it.
225
+
226
+ So AgentCI runs deterministic checks first and treats the judge as the last resort, not the default:
227
+
228
+ 1. **Fact checks in code** — `expected_in_answer`, `not_in_answer`, `regex_match`, `json_schema`. Zero LLM calls, zero flakiness, same verdict every run.
229
+ 2. **Path checks** — did the agent call the tools it should have? A missing expected tool warns; a forbidden tool fails.
230
+ 3. **Cost budgets** — LLM calls, tokens, dollars per query.
231
+ 4. **LLM judge** (`llm_judge` rubrics, optional) — only for answers that genuinely need judgment, evaluated after every deterministic check has run.
232
+
233
+ Don't write the fact checks by hand — mine them from your knowledge base:
234
+
235
+ ```bash
236
+ agentci generate-checks
237
+ ```
238
+
239
+ It extracts hard facts (prices, rates, SKUs, "30 days") as variant-set assertions, and
240
+ **validates every candidate against your recorded golden answers first** — a check that
241
+ would fail a known-good answer is rejected before you ever see it. One LLM call at
242
+ authoring time; the checks run free forever. Details: [docs/generate-checks.md](docs/generate-checks.md).
243
+
244
+ ## Let your coding agent set it up
245
+
246
+ AgentCI ships as a Claude Code plugin. Two skills: **onboard** (writes the runner,
247
+ records golden baselines, generates the spec, verifies it) and **check** (runs the
248
+ right test after every change to your agent and routes failures by flip source).
249
+
250
+ ```
251
+ /plugin marketplace add suniel12/AgentCI
252
+ /plugin install agentci@agentci
253
+ ```
254
+
255
+ Then ask your coding agent to "set up AgentCI for this repo." It records goldens with
256
+ `agentci bootstrap --yes` and verifies with `agentci test --runs 3` — no human CLI use
257
+ needed. The runner it writes is one function: `(query: str) -> str`; trace capture is
258
+ automatic.
259
+
260
+ ## Demo
261
+
262
+ Here's a RAG agent demo where someone "optimizes for latency" by reducing retriever docs from 8 to 1. AgentCI catches the correctness regression:
263
+
264
+ ![AgentCI Demo](demo/agentci-rag-demo.gif)
265
+
266
+ ## CLI
267
+
268
+ ```bash
269
+ agentci init --generate # Scan project, generate test spec
270
+ agentci init # Generate GitHub Actions workflow + pre-push hook
271
+ agentci test --mock --yes # Zero-cost synthetic traces, CI-friendly (no keys, no prompts)
272
+ agentci test # Run 3-layer evaluation (correctness → path → cost)
273
+ agentci test --runs 3 # Stability report: verdict flips + flip-source attribution
274
+ agentci judge-audit # Audit the LLM judge against checks, retests, hand labels
275
+ agentci generate-checks # Mine KB facts into deterministic assertions (gated)
276
+ agentci test --format html -o report.html # HTML report with per-query details
277
+ agentci calibrate # Measure real agent metrics, auto-tune spec budgets
278
+ agentci doctor # Health check: spec, deps, API keys
279
+ agentci record <test> # Record golden baseline
280
+ agentci diff # Diff against baseline
281
+ agentci report -i results.json # Generate HTML report from JSON results
282
+ ```
283
+ ## Docs
284
+
285
+ - [Quickstart](docs/quickstart.md) — install to first green run
286
+ - [Stability testing](docs/stability.md) — `--runs N`, flip-source attribution
287
+ - [Judge audit](docs/judge-audit.md) — is your LLM judge lying to you?
288
+ - [Generate checks](docs/generate-checks.md) — mine KB facts into gated assertions
289
+ - [Writing tests](docs/writing-tests.md) — the full spec reference
290
+ - [Cost tracking](docs/cost-tracking.md) — budgets and spike detection
291
+ - [Golden traces](docs/golden-traces.md) — record baselines, diff regressions
292
+ - [CI/CD integration](docs/ci-cd.md) — GitHub Actions setup
293
+ - [LangGraph](docs/langgraph.md) — graph-based agent support
294
+ - [Metrics reference](docs/metrics_reference.md) — every metric, defined
295
+
296
+ ## Why not just an LLM judge?
297
+
298
+ Judge-only evals are expensive, flaky, and blind to their own context. AgentCI is pytest-native regression testing: deterministic checks catch the factual failures, golden traces catch behavioral drift, cost budgets catch spend regressions — and the judge handles only what genuinely needs judgment. Mock mode (`agentci test --mock`) runs the whole suite with zero API keys and zero cost, so it can gate every PR.
299
+
300
+ ## Contributing
301
+
302
+ [GitHub Issues](https://github.com/suniel12/AgentCI/issues) ·
303
+ [DemoAgents](https://github.com/suniel12/DemoAgents) — working examples for OpenAI, Anthropic, and LangGraph agents
304
+
305
+ Apache 2.0. If you build an agent and test it with AgentCI, I'd love to hear about it.
306
+
307
+ ---