agent-trajectory-diff 0.3.0__tar.gz → 0.5.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 (59) hide show
  1. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/.gitignore +1 -0
  2. agent_trajectory_diff-0.5.0/PKG-INFO +228 -0
  3. agent_trajectory_diff-0.5.0/README.md +208 -0
  4. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/pyproject.toml +3 -2
  5. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/__init__.py +1 -1
  6. agent_trajectory_diff-0.5.0/src/agentdiff/ci/approve.py +118 -0
  7. agent_trajectory_diff-0.5.0/src/agentdiff/cli.py +722 -0
  8. agent_trajectory_diff-0.5.0/src/agentdiff/config.py +246 -0
  9. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/engine/aligner.py +93 -0
  10. agent_trajectory_diff-0.5.0/src/agentdiff/engine/comparator.py +236 -0
  11. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/engine/explanations.py +13 -9
  12. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/engine/loop_detector.py +50 -0
  13. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/engine/metrics.py +7 -1
  14. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/engine/tree.py +16 -3
  15. agent_trajectory_diff-0.5.0/src/agentdiff/governance.py +135 -0
  16. agent_trajectory_diff-0.5.0/src/agentdiff/init_wizard.py +428 -0
  17. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/loader.py +45 -0
  18. agent_trajectory_diff-0.5.0/src/agentdiff/models/envelope.py +116 -0
  19. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/models/report.py +50 -0
  20. agent_trajectory_diff-0.5.0/src/agentdiff/recorder.py +158 -0
  21. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/reporters/markdown.py +23 -0
  22. agent_trajectory_diff-0.5.0/src/agentdiff/reporters/pr.py +156 -0
  23. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/reporters/terminal.py +34 -2
  24. agent_trajectory_diff-0.5.0/src/agentdiff/staleness.py +61 -0
  25. agent_trajectory_diff-0.5.0/src/agentdiff/testing/__init__.py +13 -0
  26. agent_trajectory_diff-0.5.0/src/agentdiff/testing/assertions.py +237 -0
  27. agent_trajectory_diff-0.3.0/PKG-INFO +0 -264
  28. agent_trajectory_diff-0.3.0/README.md +0 -244
  29. agent_trajectory_diff-0.3.0/src/agentdiff/cli.py +0 -289
  30. agent_trajectory_diff-0.3.0/src/agentdiff/config.py +0 -139
  31. agent_trajectory_diff-0.3.0/src/agentdiff/engine/comparator.py +0 -101
  32. agent_trajectory_diff-0.3.0/src/agentdiff/reporters/pr.py +0 -75
  33. agent_trajectory_diff-0.3.0/src/agentdiff/testing/__init__.py +0 -5
  34. agent_trajectory_diff-0.3.0/src/agentdiff/testing/assertions.py +0 -102
  35. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/LICENSE +0 -0
  36. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/__main__.py +0 -0
  37. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/__init__.py +0 -0
  38. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/_iso.py +0 -0
  39. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/_messages.py +0 -0
  40. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/base.py +0 -0
  41. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/crewai.py +0 -0
  42. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/generic.py +0 -0
  43. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/langfuse.py +0 -0
  44. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/langgraph.py +0 -0
  45. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/langsmith.py +0 -0
  46. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/openai_agents.py +0 -0
  47. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/openinference.py +0 -0
  48. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/adapters/registry.py +0 -0
  49. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/ci/baseline.py +0 -0
  50. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/ci/github.py +0 -0
  51. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/engine/__init__.py +0 -0
  52. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/engine/benchmark.py +0 -0
  53. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/engine/suite.py +0 -0
  54. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/models/__init__.py +0 -0
  55. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/models/step.py +0 -0
  56. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/models/trace.py +0 -0
  57. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/py.typed +0 -0
  58. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/pytest_plugin.py +0 -0
  59. {agent_trajectory_diff-0.3.0 → agent_trajectory_diff-0.5.0}/src/agentdiff/reporters/__init__.py +0 -0
@@ -82,3 +82,4 @@ website/.env.production.local
82
82
 
83
83
  # pytest-benchmark local results
84
84
  .benchmarks/
85
+ _record_demo/
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.5
2
+ Name: agent-trajectory-diff
3
+ Version: 0.5.0
4
+ Summary: A developer-first package to evaluate and find regressions in agent trajectories
5
+ Project-URL: Homepage, https://github.com/kerrshift/agentdiff
6
+ Project-URL: Repository, https://github.com/kerrshift/agentdiff
7
+ Author-email: Sahil Gangurde <sahilgangurde08@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: networkx>=3.0
15
+ Requires-Dist: pydantic>=2.0
16
+ Requires-Dist: rich>=13.0
17
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
18
+ Requires-Dist: typer>=0.9
19
+ Description-Content-Type: text/markdown
20
+
21
+ <div align="center">
22
+
23
+ <img src="docs/assets/logo.svg" alt="AgentDiff Logo" width="84" height="84" />
24
+
25
+ # AgentDiff
26
+
27
+ **Catch silent cost surges and broken agent loops before they ship.**
28
+
29
+ <p align="center">
30
+ <a href="https://github.com/kerrshift/agentdiff/actions/workflows/ci.yml"><img src="https://github.com/kerrshift/agentdiff/actions/workflows/ci.yml/badge.svg" alt="CI Build" /></a>
31
+ <a href="https://pypi.org/project/agent-trajectory-diff/"><img src="https://img.shields.io/pypi/v/agent-trajectory-diff.svg?color=10b981" alt="PyPI version" /></a>
32
+ <a href="https://pypi.org/project/agent-trajectory-diff/"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue.svg" alt="Python Versions" /></a>
33
+ <a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/badge/code%20style-ruff-000000.svg" alt="Code Style: Ruff" /></a>
34
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
35
+ <a href="https://agentdiff.app"><img src="https://img.shields.io/badge/docs-agentdiff.app-emerald.svg" alt="Website" /></a>
36
+ </p>
37
+
38
+ ```bash
39
+ pip install agent-trajectory-diff
40
+ ```
41
+
42
+ [Website & Interactive Docs](https://agentdiff.app) · [Cookbooks](cookbooks/) · [Live Demo Repo](https://github.com/lostmartian/agentdiff-demo) · [Changelog](CHANGELOG.md)
43
+
44
+ </div>
45
+
46
+ **AgentDiff** is a developer-first Python library, pytest plugin, and CLI for **regression testing multi-turn, tool-using AI agents by comparing execution paths (trajectories) head-to-head.**
47
+
48
+ When you change a prompt, tweak a system instruction, or upgrade an LLM, traditional assertions only verify that the final string matches. They miss the silent failures: **the agent took 5 extra tool calls, burned 3× the tokens, entered an infinite retry loop, or drifted from the verified execution path.**
49
+
50
+ AgentDiff aligns candidate execution DAGs against committed golden baselines in `<10ms` without calling any paid LLM judges.
51
+
52
+ ## Highlights
53
+
54
+ - **Deterministic Graph Diffing:** Topological DAG alignment and Longest Common Subsequence (LCS) step comparison in `<10ms` with zero paid LLM-judge calls.
55
+ - **Statistical Baselines & Variance Bands:** Capture N-run envelopes (`record --runs 3`) so non-deterministic agents don't flake CI on harmless jitter.
56
+ - **Zero-Config Setup (`agentdiff init`):** Auto-detects LangGraph, CrewAI, OpenAI Agents SDK, or OpenTelemetry and writes `agentdiff.toml` + CI workflow in seconds.
57
+ - **In-PR Interactive Blessings (`/agentdiff approve`):** Reviewers bless intended trajectory improvements from PR comments as `agentdiff-ci[bot]`.
58
+ - **100% Local & Air-Gapped:** Zero telemetry, no cloud accounts, no outbound network calls during diffs. Raw prompts and tool outputs stay local.
59
+ - **Drop-in CI Merge Gate:** Native exit codes (`0` pass / `1` regression fail) and automated GitHub Action PR comments with human-first verdicts.
60
+ - **Universal Telemetry Adapters:** Seamlessly diff traces from **LangGraph**, **CrewAI**, **OpenAI Agents SDK**, **Langfuse**, **LangSmith**, **OpenInference / OpenTelemetry**, or generic JSON.
61
+
62
+ ## Quickstart
63
+
64
+ ### 1. Initialize with `agentdiff init`
65
+ Auto-detect your agent framework and generate your configuration + CI workflow:
66
+
67
+ ```bash
68
+ agentdiff init --scenario customer_support --runs 3 --with-approve
69
+ ```
70
+
71
+ ### 2. Record a Statistical Baseline Envelope
72
+ Record an N-run baseline envelope from any agent function without writing boilerplate telemetry:
73
+
74
+ ```bash
75
+ agentdiff record my_agent:run \
76
+ --input '{"query": "summarize repo"}' \
77
+ --runs 3 \
78
+ --out baselines/customer_support.envelope.json
79
+ ```
80
+
81
+ ### 3. Compare Traces in CLI
82
+ Compare candidate runs against your baseline envelope:
83
+
84
+ ```bash
85
+ agentdiff diff baselines/customer_support.envelope.json traces/candidate.json --fail-on-regression
86
+ ```
87
+
88
+ ### 4. Pytest Regression Testing
89
+ Enforce trajectory parity directly in your test suite:
90
+
91
+ ```python
92
+ import pytest
93
+ from agentdiff import load_trace, compare
94
+ from agentdiff.testing import assert_no_regressions
95
+
96
+ def test_agent_refactor_efficiency():
97
+ # Load traces (auto-detects telemetry source format)
98
+ baseline = load_trace("tests/baselines/golden.json")
99
+ candidate = load_trace("tests/traces/candidate.json")
100
+
101
+ # Run sub-10ms deterministic comparison
102
+ report = compare(baseline, candidate)
103
+
104
+ # Assert no structural drift, cost surges, or tool loops
105
+ assert_no_regressions(
106
+ report,
107
+ max_divergence=0.25, # Max Trajectory Divergence Index [0.0 - 1.0]
108
+ max_cost_increase_pct=5.0, # Max 5% token cost increase
109
+ allow_loops=False, # Reject repetitive tool call cycles
110
+ max_wasted_effort=0.10, # Max 10% error/retry/abandoned steps
111
+ max_recovery_step_ratio=1.5 # Max recovery steps relative to baseline
112
+ )
113
+ ```
114
+
115
+ ## GitHub Actions CI Gate
116
+
117
+ Block broken agent PRs before they land in production using the official composite action:
118
+
119
+ ```yaml
120
+ name: AgentDiff Regression Gate
121
+
122
+ on:
123
+ pull_request:
124
+
125
+ permissions:
126
+ contents: read
127
+ pull-requests: write # Allows posting automated root-cause PR comments
128
+
129
+ jobs:
130
+ agent-regression-gate:
131
+ runs-on: ubuntu-latest
132
+ steps:
133
+ - uses: actions/checkout@v4
134
+
135
+ - uses: actions/setup-python@v5
136
+ with:
137
+ python-version: "3.11"
138
+
139
+ - uses: kerrshift/agentdiff/.github/actions/agentdiff-check@v0.5.0
140
+ with:
141
+ baseline: baselines/customer_support.envelope.json
142
+ candidate: traces/pr_candidate.json
143
+ pr: ${{ github.event.pull_request.number }}
144
+ github-token: ${{ secrets.GITHUB_TOKEN }}
145
+ ```
146
+
147
+ When a regression occurs, the gate fails with exit code `1` and comments on the PR with culprit identification and a collapsed divergence tree:
148
+
149
+ ```markdown
150
+ ### AgentDiff Gate: REGRESSION DETECTED
151
+
152
+ | Metric | Baseline | Candidate | Threshold | Status |
153
+ | :--- | :--- | :--- | :--- | :--- |
154
+ | **Divergence (TDI)** | 0.00 | 0.42 | ≤ 0.25 | FAIL |
155
+ | **Cost Surge** | $0.0042 | $0.0138 (+228%) | ≤ +5.0% | FAIL |
156
+ | **Loops (LBI)** | 0 | 3 loops | 0 | FAIL |
157
+ | **Wasted Effort (WEI)**| 0.00 | 0.38 | ≤ 0.10 | FAIL |
158
+
159
+ **Culprit Step:** Step 4 `execute_sql` entered a 3× retry loop after schema refactor.
160
+ ```
161
+
162
+ ## Core Metric Mathematics
163
+
164
+ | Metric | Target / Range | Algorithmic Definition | Description |
165
+ | :--- | :--- | :--- | :--- |
166
+ | **Trajectory Divergence Index (TDI)** | `0.0` (Identical) to `1.0` (Divergent) | $$1.0 - \frac{2 \times \vert{}\text{LCS}(\text{Steps}_A, \text{Steps}_B)\vert{}}{\vert{}\text{Steps}_A\vert{} + \vert{}\text{Steps}_B\vert{}}$$ | Structural distance between baseline and candidate execution DAGs using Longest Common Subsequence. |
167
+ | **Wasted Effort Index (WEI)** | `0.0` (Optimal) to `1.0` (Total Waste) | $$\frac{\text{Count}(\text{Steps} \in \{\text{ERROR, RETRY, ABANDONED}\})}{\text{Total Steps}}$$ | Fraction of execution steps spent in failed, retried, or aborted tool operations. |
168
+ | **Loop Buster Index (LBI)** | Integer ($\ge 0$) | Stagnant State Cycle Detection | Counts repeating consecutive tool call patterns where inputs/outputs show no state progression. |
169
+ | **Recovery Step Ratio (RSR)** | `1.0` = Parity; $> 1.0$ = Slower Recovery | $$\text{RSR} = \frac{\text{Recovery Steps}_{\text{candidate}}}{\text{Recovery Steps}_{\text{baseline}}}$$ | Measures the number of steps required to return to the verified golden trajectory path after encountering an error. |
170
+ | **Resource Deltas ($\Delta\text{Res}$)** | Percentage ($\pm\%$) | $\frac{\text{Val}_{\text{candidate}} - \text{Val}_{\text{baseline}}}{\text{Val}_{\text{baseline}}} \times 100$ | Exact percentage deltas for $\Delta\text{Tokens}$, $\Delta\text{Cost}$, and $\Delta\text{Latency}$. |
171
+
172
+ ## Supported Telemetry Formats
173
+
174
+ | Telemetry Framework / Format | Adapter Spec | Ingestion Guide |
175
+ | :--- | :--- | :--- |
176
+ | **LangGraph / LangChain** | `--adapter langgraph` | [`cookbooks/langgraph`](cookbooks/) |
177
+ | **CrewAI** | `--adapter crewai` | [`cookbooks/crewai`](cookbooks/) |
178
+ | **OpenAI Agents SDK** | `--adapter openai_agents` | [`cookbooks/openai_agents`](cookbooks/) |
179
+ | **Langfuse** | `--adapter langfuse` | [`cookbooks/langfuse`](cookbooks/) |
180
+ | **LangSmith** | `--adapter langsmith` | [`cookbooks/langsmith`](cookbooks/) |
181
+ | **OpenInference / OpenTelemetry** | `--adapter openinference` | [`cookbooks/openinference`](cookbooks/) |
182
+ | **Generic JSON Schema** | `--adapter generic` | [`schema/v0.1.0/trace.json`](schema/v0.1.0/trace.json) |
183
+
184
+ ## Local-First Privacy Guarantee
185
+
186
+ Agent trajectories often contain proprietary prompts, sensitive tool payloads, and customer data. AgentDiff is engineered with strict local-first principles:
187
+
188
+ - **Zero Outbound Network Traffic:** Parsing, DAG diffing, metric calculations, and reporting run 100% locally.
189
+ - **Air-Gapped & Firewall Friendly:** Run tests on laptops, in air-gapped VPCs, or under strict enterprise egress policies.
190
+ - **Repo-Committed Baselines:** Your golden trajectories live in Git next to the code they protect.
191
+ - **No Third-Party APM Lock-In:** Switch tracing providers at any time; AgentDiff normalizes all schemas to a unified specification.
192
+
193
+ ## Documentation & Cookbooks
194
+
195
+ - **Official Documentation:** [https://agentdiff.app](https://agentdiff.app)
196
+ - **Live Demo Repository:** [github.com/lostmartian/agentdiff-demo](https://github.com/lostmartian/agentdiff-demo)
197
+ - **Engine Specification:** [Under the Hood](https://agentdiff.app/features)
198
+ - **Interactive Visualizer:** [Diff Playground](https://agentdiff.app/compare)
199
+ - **Adapters Guide:** [Framework Integration](https://agentdiff.app/adapters)
200
+
201
+ ## Development
202
+
203
+ This repository uses [`uv`](https://docs.astral.sh/uv/) for lightning-fast environment and dependency management.
204
+
205
+ ```bash
206
+ # Clone the repository
207
+ git clone https://github.com/kerrshift/agentdiff.git
208
+ cd agentdiff
209
+
210
+ # Install dependencies and sync virtualenv
211
+ uv sync
212
+
213
+ # Run linting and code formatting checks
214
+ make lint
215
+
216
+ # Run the test suite
217
+ make test
218
+
219
+ # Build package distributions
220
+ make build
221
+
222
+ # Launch the website & docs locally
223
+ make website-dev
224
+ ```
225
+
226
+ ## License
227
+
228
+ Distributed under the **MIT License**. See [`LICENSE`](LICENSE) for more information.
@@ -0,0 +1,208 @@
1
+ <div align="center">
2
+
3
+ <img src="docs/assets/logo.svg" alt="AgentDiff Logo" width="84" height="84" />
4
+
5
+ # AgentDiff
6
+
7
+ **Catch silent cost surges and broken agent loops before they ship.**
8
+
9
+ <p align="center">
10
+ <a href="https://github.com/kerrshift/agentdiff/actions/workflows/ci.yml"><img src="https://github.com/kerrshift/agentdiff/actions/workflows/ci.yml/badge.svg" alt="CI Build" /></a>
11
+ <a href="https://pypi.org/project/agent-trajectory-diff/"><img src="https://img.shields.io/pypi/v/agent-trajectory-diff.svg?color=10b981" alt="PyPI version" /></a>
12
+ <a href="https://pypi.org/project/agent-trajectory-diff/"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue.svg" alt="Python Versions" /></a>
13
+ <a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/badge/code%20style-ruff-000000.svg" alt="Code Style: Ruff" /></a>
14
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
15
+ <a href="https://agentdiff.app"><img src="https://img.shields.io/badge/docs-agentdiff.app-emerald.svg" alt="Website" /></a>
16
+ </p>
17
+
18
+ ```bash
19
+ pip install agent-trajectory-diff
20
+ ```
21
+
22
+ [Website & Interactive Docs](https://agentdiff.app) · [Cookbooks](cookbooks/) · [Live Demo Repo](https://github.com/lostmartian/agentdiff-demo) · [Changelog](CHANGELOG.md)
23
+
24
+ </div>
25
+
26
+ **AgentDiff** is a developer-first Python library, pytest plugin, and CLI for **regression testing multi-turn, tool-using AI agents by comparing execution paths (trajectories) head-to-head.**
27
+
28
+ When you change a prompt, tweak a system instruction, or upgrade an LLM, traditional assertions only verify that the final string matches. They miss the silent failures: **the agent took 5 extra tool calls, burned 3× the tokens, entered an infinite retry loop, or drifted from the verified execution path.**
29
+
30
+ AgentDiff aligns candidate execution DAGs against committed golden baselines in `<10ms` without calling any paid LLM judges.
31
+
32
+ ## Highlights
33
+
34
+ - **Deterministic Graph Diffing:** Topological DAG alignment and Longest Common Subsequence (LCS) step comparison in `<10ms` with zero paid LLM-judge calls.
35
+ - **Statistical Baselines & Variance Bands:** Capture N-run envelopes (`record --runs 3`) so non-deterministic agents don't flake CI on harmless jitter.
36
+ - **Zero-Config Setup (`agentdiff init`):** Auto-detects LangGraph, CrewAI, OpenAI Agents SDK, or OpenTelemetry and writes `agentdiff.toml` + CI workflow in seconds.
37
+ - **In-PR Interactive Blessings (`/agentdiff approve`):** Reviewers bless intended trajectory improvements from PR comments as `agentdiff-ci[bot]`.
38
+ - **100% Local & Air-Gapped:** Zero telemetry, no cloud accounts, no outbound network calls during diffs. Raw prompts and tool outputs stay local.
39
+ - **Drop-in CI Merge Gate:** Native exit codes (`0` pass / `1` regression fail) and automated GitHub Action PR comments with human-first verdicts.
40
+ - **Universal Telemetry Adapters:** Seamlessly diff traces from **LangGraph**, **CrewAI**, **OpenAI Agents SDK**, **Langfuse**, **LangSmith**, **OpenInference / OpenTelemetry**, or generic JSON.
41
+
42
+ ## Quickstart
43
+
44
+ ### 1. Initialize with `agentdiff init`
45
+ Auto-detect your agent framework and generate your configuration + CI workflow:
46
+
47
+ ```bash
48
+ agentdiff init --scenario customer_support --runs 3 --with-approve
49
+ ```
50
+
51
+ ### 2. Record a Statistical Baseline Envelope
52
+ Record an N-run baseline envelope from any agent function without writing boilerplate telemetry:
53
+
54
+ ```bash
55
+ agentdiff record my_agent:run \
56
+ --input '{"query": "summarize repo"}' \
57
+ --runs 3 \
58
+ --out baselines/customer_support.envelope.json
59
+ ```
60
+
61
+ ### 3. Compare Traces in CLI
62
+ Compare candidate runs against your baseline envelope:
63
+
64
+ ```bash
65
+ agentdiff diff baselines/customer_support.envelope.json traces/candidate.json --fail-on-regression
66
+ ```
67
+
68
+ ### 4. Pytest Regression Testing
69
+ Enforce trajectory parity directly in your test suite:
70
+
71
+ ```python
72
+ import pytest
73
+ from agentdiff import load_trace, compare
74
+ from agentdiff.testing import assert_no_regressions
75
+
76
+ def test_agent_refactor_efficiency():
77
+ # Load traces (auto-detects telemetry source format)
78
+ baseline = load_trace("tests/baselines/golden.json")
79
+ candidate = load_trace("tests/traces/candidate.json")
80
+
81
+ # Run sub-10ms deterministic comparison
82
+ report = compare(baseline, candidate)
83
+
84
+ # Assert no structural drift, cost surges, or tool loops
85
+ assert_no_regressions(
86
+ report,
87
+ max_divergence=0.25, # Max Trajectory Divergence Index [0.0 - 1.0]
88
+ max_cost_increase_pct=5.0, # Max 5% token cost increase
89
+ allow_loops=False, # Reject repetitive tool call cycles
90
+ max_wasted_effort=0.10, # Max 10% error/retry/abandoned steps
91
+ max_recovery_step_ratio=1.5 # Max recovery steps relative to baseline
92
+ )
93
+ ```
94
+
95
+ ## GitHub Actions CI Gate
96
+
97
+ Block broken agent PRs before they land in production using the official composite action:
98
+
99
+ ```yaml
100
+ name: AgentDiff Regression Gate
101
+
102
+ on:
103
+ pull_request:
104
+
105
+ permissions:
106
+ contents: read
107
+ pull-requests: write # Allows posting automated root-cause PR comments
108
+
109
+ jobs:
110
+ agent-regression-gate:
111
+ runs-on: ubuntu-latest
112
+ steps:
113
+ - uses: actions/checkout@v4
114
+
115
+ - uses: actions/setup-python@v5
116
+ with:
117
+ python-version: "3.11"
118
+
119
+ - uses: kerrshift/agentdiff/.github/actions/agentdiff-check@v0.5.0
120
+ with:
121
+ baseline: baselines/customer_support.envelope.json
122
+ candidate: traces/pr_candidate.json
123
+ pr: ${{ github.event.pull_request.number }}
124
+ github-token: ${{ secrets.GITHUB_TOKEN }}
125
+ ```
126
+
127
+ When a regression occurs, the gate fails with exit code `1` and comments on the PR with culprit identification and a collapsed divergence tree:
128
+
129
+ ```markdown
130
+ ### AgentDiff Gate: REGRESSION DETECTED
131
+
132
+ | Metric | Baseline | Candidate | Threshold | Status |
133
+ | :--- | :--- | :--- | :--- | :--- |
134
+ | **Divergence (TDI)** | 0.00 | 0.42 | ≤ 0.25 | FAIL |
135
+ | **Cost Surge** | $0.0042 | $0.0138 (+228%) | ≤ +5.0% | FAIL |
136
+ | **Loops (LBI)** | 0 | 3 loops | 0 | FAIL |
137
+ | **Wasted Effort (WEI)**| 0.00 | 0.38 | ≤ 0.10 | FAIL |
138
+
139
+ **Culprit Step:** Step 4 `execute_sql` entered a 3× retry loop after schema refactor.
140
+ ```
141
+
142
+ ## Core Metric Mathematics
143
+
144
+ | Metric | Target / Range | Algorithmic Definition | Description |
145
+ | :--- | :--- | :--- | :--- |
146
+ | **Trajectory Divergence Index (TDI)** | `0.0` (Identical) to `1.0` (Divergent) | $$1.0 - \frac{2 \times \vert{}\text{LCS}(\text{Steps}_A, \text{Steps}_B)\vert{}}{\vert{}\text{Steps}_A\vert{} + \vert{}\text{Steps}_B\vert{}}$$ | Structural distance between baseline and candidate execution DAGs using Longest Common Subsequence. |
147
+ | **Wasted Effort Index (WEI)** | `0.0` (Optimal) to `1.0` (Total Waste) | $$\frac{\text{Count}(\text{Steps} \in \{\text{ERROR, RETRY, ABANDONED}\})}{\text{Total Steps}}$$ | Fraction of execution steps spent in failed, retried, or aborted tool operations. |
148
+ | **Loop Buster Index (LBI)** | Integer ($\ge 0$) | Stagnant State Cycle Detection | Counts repeating consecutive tool call patterns where inputs/outputs show no state progression. |
149
+ | **Recovery Step Ratio (RSR)** | `1.0` = Parity; $> 1.0$ = Slower Recovery | $$\text{RSR} = \frac{\text{Recovery Steps}_{\text{candidate}}}{\text{Recovery Steps}_{\text{baseline}}}$$ | Measures the number of steps required to return to the verified golden trajectory path after encountering an error. |
150
+ | **Resource Deltas ($\Delta\text{Res}$)** | Percentage ($\pm\%$) | $\frac{\text{Val}_{\text{candidate}} - \text{Val}_{\text{baseline}}}{\text{Val}_{\text{baseline}}} \times 100$ | Exact percentage deltas for $\Delta\text{Tokens}$, $\Delta\text{Cost}$, and $\Delta\text{Latency}$. |
151
+
152
+ ## Supported Telemetry Formats
153
+
154
+ | Telemetry Framework / Format | Adapter Spec | Ingestion Guide |
155
+ | :--- | :--- | :--- |
156
+ | **LangGraph / LangChain** | `--adapter langgraph` | [`cookbooks/langgraph`](cookbooks/) |
157
+ | **CrewAI** | `--adapter crewai` | [`cookbooks/crewai`](cookbooks/) |
158
+ | **OpenAI Agents SDK** | `--adapter openai_agents` | [`cookbooks/openai_agents`](cookbooks/) |
159
+ | **Langfuse** | `--adapter langfuse` | [`cookbooks/langfuse`](cookbooks/) |
160
+ | **LangSmith** | `--adapter langsmith` | [`cookbooks/langsmith`](cookbooks/) |
161
+ | **OpenInference / OpenTelemetry** | `--adapter openinference` | [`cookbooks/openinference`](cookbooks/) |
162
+ | **Generic JSON Schema** | `--adapter generic` | [`schema/v0.1.0/trace.json`](schema/v0.1.0/trace.json) |
163
+
164
+ ## Local-First Privacy Guarantee
165
+
166
+ Agent trajectories often contain proprietary prompts, sensitive tool payloads, and customer data. AgentDiff is engineered with strict local-first principles:
167
+
168
+ - **Zero Outbound Network Traffic:** Parsing, DAG diffing, metric calculations, and reporting run 100% locally.
169
+ - **Air-Gapped & Firewall Friendly:** Run tests on laptops, in air-gapped VPCs, or under strict enterprise egress policies.
170
+ - **Repo-Committed Baselines:** Your golden trajectories live in Git next to the code they protect.
171
+ - **No Third-Party APM Lock-In:** Switch tracing providers at any time; AgentDiff normalizes all schemas to a unified specification.
172
+
173
+ ## Documentation & Cookbooks
174
+
175
+ - **Official Documentation:** [https://agentdiff.app](https://agentdiff.app)
176
+ - **Live Demo Repository:** [github.com/lostmartian/agentdiff-demo](https://github.com/lostmartian/agentdiff-demo)
177
+ - **Engine Specification:** [Under the Hood](https://agentdiff.app/features)
178
+ - **Interactive Visualizer:** [Diff Playground](https://agentdiff.app/compare)
179
+ - **Adapters Guide:** [Framework Integration](https://agentdiff.app/adapters)
180
+
181
+ ## Development
182
+
183
+ This repository uses [`uv`](https://docs.astral.sh/uv/) for lightning-fast environment and dependency management.
184
+
185
+ ```bash
186
+ # Clone the repository
187
+ git clone https://github.com/kerrshift/agentdiff.git
188
+ cd agentdiff
189
+
190
+ # Install dependencies and sync virtualenv
191
+ uv sync
192
+
193
+ # Run linting and code formatting checks
194
+ make lint
195
+
196
+ # Run the test suite
197
+ make test
198
+
199
+ # Build package distributions
200
+ make build
201
+
202
+ # Launch the website & docs locally
203
+ make website-dev
204
+ ```
205
+
206
+ ## License
207
+
208
+ Distributed under the **MIT License**. See [`LICENSE`](LICENSE) for more information.
@@ -28,8 +28,8 @@ agentdiff = "agentdiff.cli:main"
28
28
  agentdiff = "agentdiff.pytest_plugin"
29
29
 
30
30
  [project.urls]
31
- Homepage = "https://github.com/lostmartian/agentdiff"
32
- Repository = "https://github.com/lostmartian/agentdiff"
31
+ Homepage = "https://github.com/kerrshift/agentdiff"
32
+ Repository = "https://github.com/kerrshift/agentdiff"
33
33
 
34
34
  [dependency-groups]
35
35
  dev = [
@@ -37,6 +37,7 @@ dev = [
37
37
  "pytest>=8.0",
38
38
  "pytest-benchmark>=4.0",
39
39
  "pytest-cov>=7.1.0",
40
+ "pyyaml>=6.0.3",
40
41
  "ruff>=0.16.3",
41
42
  ]
42
43
 
@@ -32,7 +32,7 @@ Typical flow::
32
32
  assert_no_regressions(report)
33
33
  """
34
34
 
35
- __version__ = "0.3.0"
35
+ __version__ = "0.5.0"
36
36
 
37
37
  from agentdiff.adapters import (
38
38
  BaseAdapter,
@@ -0,0 +1,118 @@
1
+ """Pillar 3 — in-PR baseline approval (``/agentdiff approve``).
2
+
3
+ Closes the baseline-maintenance loop entirely inside GitHub: a reviewer
4
+ comments ``/agentdiff approve`` on a flagged PR, the approve workflow calls
5
+ :func:`approve_candidate`, and the candidate trace becomes (or joins) the
6
+ golden baseline. No local checkout, no manual JSON.
7
+
8
+ Policy (decision D3, SPEC-0.5.0 §10): a human may bless **path drift** and
9
+ **cost spikes** — those are product judgments. **Loops are never blessable**:
10
+ a cyclical tool loop or a tool-repeat-cap breach is non-termination, and
11
+ re-baselining it would teach the gate that broken is the new normal.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from dataclasses import dataclass
17
+
18
+ from agentdiff.engine.comparator import compare, compare_envelope
19
+ from agentdiff.loader import load_baseline, load_trace
20
+ from agentdiff.models.envelope import BaselineEnvelope
21
+ from agentdiff.models.report import DiffReport, GateFinding
22
+ from agentdiff.models.trace import AgentTrace
23
+ from agentdiff.testing.assertions import GateResult, evaluate_gate
24
+
25
+ # Loop-family codes: hard invariants that approve must never bless (D3).
26
+ _UNBLESSABLE_CODES = frozenset({"tool_loop", "tool_repeats", "loops"})
27
+
28
+ DEFAULT_SAMPLE_RUNS = 3
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class ApproveDecision:
33
+ """Outcome of an ``/agentdiff approve`` invocation."""
34
+
35
+ approved: bool
36
+ reason: str
37
+ report: DiffReport | None
38
+ unblessable_findings: tuple[GateFinding, ...] = ()
39
+
40
+ @property
41
+ def refusals(self) -> tuple[str, ...]:
42
+ return tuple(f.message for f in self.unblessable_findings)
43
+
44
+
45
+ def _gate_for(
46
+ envelope: BaselineEnvelope, candidate: AgentTrace, scenario_cfg
47
+ ) -> tuple[DiffReport, GateResult]:
48
+ if envelope.mode == "statistical" and envelope.n_runs >= 2:
49
+ tol = getattr(scenario_cfg, "tolerances", None)
50
+ return compare_envelope(
51
+ envelope,
52
+ candidate,
53
+ max_divergence=tol.divergence_ceiling if tol else 0.35,
54
+ max_cost_increase_pct=(
55
+ getattr(scenario_cfg, "max_cost_increase_pct", 20.0)
56
+ if scenario_cfg
57
+ else 20.0
58
+ ),
59
+ step_count_std_dev=tol.step_count_std_dev if tol else 2.0,
60
+ )
61
+ report = compare(envelope.runs[0], candidate)
62
+ return report, evaluate_gate(report)
63
+
64
+
65
+ def approve_candidate(
66
+ baseline_path: str,
67
+ candidate_path: str,
68
+ scenario_cfg=None,
69
+ adapter: str = "auto",
70
+ sample_runs: int = DEFAULT_SAMPLE_RUNS,
71
+ ) -> ApproveDecision:
72
+ """Blesses a candidate run as the new golden baseline.
73
+
74
+ Refuses when a loop-family hard invariant fires (D3). Otherwise rotates
75
+ the baseline — envelopes append the candidate into their rolling window
76
+ and recompute bands; strict baselines are replaced.
77
+
78
+ Raises the same load errors as ``load_baseline``/``load_trace``.
79
+ """
80
+ envelope = load_baseline(baseline_path, adapter)
81
+ candidate = load_trace(candidate_path, adapter)
82
+
83
+ report, gate = _gate_for(envelope, candidate, scenario_cfg)
84
+
85
+ loop_findings = tuple(f for f in gate.violations if f.code in _UNBLESSABLE_CODES)
86
+ if loop_findings:
87
+ return ApproveDecision(
88
+ approved=False,
89
+ reason=(
90
+ "refused: loop violations are never blessable — fix the loop, "
91
+ "then approve (policy: non-termination is not a baseline)"
92
+ ),
93
+ report=report,
94
+ unblessable_findings=loop_findings,
95
+ )
96
+
97
+ if envelope.mode == "statistical" and envelope.n_runs >= 2:
98
+ envelope.runs.append(candidate)
99
+ keep = max(2, sample_runs)
100
+ envelope.runs = envelope.runs[-keep:]
101
+ envelope.refresh()
102
+ else:
103
+ # strict baseline: candidate replaces the run set; mode is preserved
104
+ envelope.runs = [candidate]
105
+ envelope.refresh()
106
+
107
+ with open(baseline_path, "w", encoding="utf-8") as fh:
108
+ fh.write(envelope.model_dump_json())
109
+
110
+ blessed = []
111
+ if gate.violations:
112
+ blessed = [f.code for f in gate.violations]
113
+ reason = (
114
+ "approved with human blessing of: " + ", ".join(blessed)
115
+ if blessed
116
+ else "approved: candidate is clean against its own new baseline"
117
+ )
118
+ return ApproveDecision(approved=True, reason=reason, report=report)