agentprdiff 0.2.4__tar.gz → 0.3.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 (28) hide show
  1. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/.gitignore +4 -0
  2. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/CHANGELOG.md +52 -0
  3. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/PKG-INFO +11 -2
  4. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/README.md +10 -1
  5. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/pyproject.toml +1 -1
  6. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/__init__.py +1 -1
  7. agentprdiff-0.3.0/src/agentprdiff/adapters/__init__.py +86 -0
  8. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/adapters/anthropic.py +9 -0
  9. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/adapters/openai.py +26 -0
  10. agentprdiff-0.3.0/studio/README.md +108 -0
  11. agentprdiff-0.3.0/studio/backend/README.md +34 -0
  12. agentprdiff-0.2.4/src/agentprdiff/adapters/__init__.py +0 -43
  13. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/LICENSE +0 -0
  14. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/examples/quickstart/README.md +0 -0
  15. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/examples/regression-tour/README.md +0 -0
  16. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/adapters/pricing.py +0 -0
  17. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/cli.py +0 -0
  18. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/core.py +0 -0
  19. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/differ.py +0 -0
  20. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/filtering.py +0 -0
  21. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/graders/__init__.py +0 -0
  22. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/graders/deterministic.py +0 -0
  23. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/graders/semantic.py +0 -0
  24. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/loader.py +0 -0
  25. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/reporters.py +0 -0
  26. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/runner.py +0 -0
  27. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/scaffold.py +0 -0
  28. {agentprdiff-0.2.4 → agentprdiff-0.3.0}/src/agentprdiff/store.py +0 -0
@@ -31,3 +31,7 @@ env/
31
31
  .agentprdiff/runs/
32
32
  .agentprdiff/cache/
33
33
  .pypirc
34
+
35
+ # Engine worktree nested inside Studio. Not committed — engine source
36
+ # already lives at the repo root.
37
+ studio/.claude/
@@ -8,6 +8,58 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.3.0] — 2026-05-17
12
+
13
+ Adds a model-override hook on the adapters package. This unblocks
14
+ tooling that wants to run a single suite against multiple models — e.g.
15
+ the Studio's multi-model benchmark — without forcing user-written agent
16
+ code to take a `model` parameter.
17
+
18
+ The change is purely additive: no existing call site has to change, and
19
+ the 155-test suite passes unchanged. The override is module-level and
20
+ process-wide, read at call time inside the patched `create()` function.
21
+
22
+ ### Added
23
+
24
+ - `agentprdiff.adapters.set_default_model(model: str | None) -> None`:
25
+ set a process-wide override that rewrites `kwargs["model"]` on every
26
+ subsequent `instrument_client`-patched `create()` call. Pass `None` to
27
+ clear.
28
+ - `agentprdiff.adapters.get_default_model() -> str | None`: read the
29
+ current override.
30
+ - The OpenAI adapter's sync + async patched-create paths and the
31
+ Anthropic adapter's patched-create now consult the override at call
32
+ time and rewrite kwargs before delegating to the SDK. No-op when no
33
+ override is active.
34
+
35
+ ### Notes
36
+
37
+ - The override is process-wide. Sequencing `set_default_model("A"); run();
38
+ set_default_model("B"); run(); set_default_model(None)` works as
39
+ expected. Concurrent runs in the same process share the override —
40
+ isolate per-leg benchmarks in fresh subprocesses if you need
41
+ concurrency.
42
+ - Only rewrites when the caller actually passed a `model=` kwarg, so the
43
+ SDK's "no model provided" error path still surfaces correctly.
44
+ - The override is exported from `agentprdiff.adapters` so callers don't
45
+ need to reach into submodules.
46
+
47
+ ## [0.2.5] — 2026-04-30
48
+
49
+ Infrastructure-only release. Code is identical to 0.2.4 — this exists
50
+ solely to publish via PyPI's Trusted Publishing flow (OIDC from GitHub
51
+ Actions) so the project page shows verified details for the source
52
+ repository. No API token is used to upload this release.
53
+
54
+ ### Internal
55
+
56
+ - Added `.github/workflows/release.yml` that builds sdist + wheel and
57
+ publishes via `pypa/gh-action-pypi-publish` with OIDC. Triggered on
58
+ GitHub `release: published` and `workflow_dispatch`. Uses the `pypi`
59
+ GitHub environment for tag-restricted deploys (`v*`).
60
+ - First release published through the configured PyPI Trusted Publisher
61
+ for `vnageshwaran-de/agentprdiff` → `release.yml` → `pypi` env.
62
+
11
63
  ## [0.2.4] — 2026-04-29
12
64
 
13
65
  Metadata-only release. Code is identical to 0.2.3 — this exists solely
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentprdiff
3
- Version: 0.2.4
3
+ Version: 0.3.0
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://agentprdiff.dev
6
6
  Project-URL: Documentation, https://agentprdiff.dev
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
42
42
 
43
43
  **Guard your LLM agents in CI.** Snapshot tests that catch behavioral regressions when models, prompts, or vendors change.
44
44
 
45
- 📚 **[Documentation: agentprdiff.dev](https://agentprdiff.dev/)**  ·  ⚡ [Quickstart](https://agentprdiff.dev/quickstart/)  ·  🤖 [AI-agent adoption](https://agentprdiff.dev/quickstart/#path-a--let-an-ai-agent-adopt-the-package-for-you-recommended)  ·  📦 [PyPI](https://pypi.org/project/agentprdiff/)
45
+ 📚 **[Documentation: agentprdiff.dev](https://agentprdiff.dev/)**  ·  ⚡ [Quickstart](https://agentprdiff.dev/quickstart/)  ·  🤖 [AI-agent adoption](https://agentprdiff.dev/quickstart/#path-a)  ·  📦 [PyPI](https://pypi.org/project/agentprdiff/)
46
46
 
47
47
  > You upgraded Claude. You tweaked a system prompt. You swapped `gpt-4o` for `gpt-4o-mini` in the cheap path. Which of your agent's behaviors just changed? `agentprdiff` tells you — before the PR merges.
48
48
 
@@ -50,6 +50,15 @@ Description-Content-Type: text/markdown
50
50
  pip install agentprdiff
51
51
  ```
52
52
 
53
+ > **Don't have Python 3.10+ yet?** Step-by-step install instructions for
54
+ > [macOS, Windows, and Linux](https://agentprdiff.dev/installation/#install-python-310-first-if-you-dont-have-it).
55
+ >
56
+ > **Multiple Python versions on your machine?** If `pip install` reports
57
+ > `No matching distribution found` even after installing Python 3.10+,
58
+ > use `python3.12 -m pip install agentprdiff` (substitute your installed
59
+ > 3.10+ binary). Sidesteps `$PATH` confusion when Homebrew's Python and
60
+ > the system Python coexist. Full troubleshooting: [Installation guide](https://agentprdiff.dev/installation/).
61
+
53
62
  [![CI](https://github.com/vnageshwaran-de/agentprdiff/actions/workflows/ci.yml/badge.svg)](https://github.com/vnageshwaran-de/agentprdiff/actions/workflows/ci.yml)
54
63
  [![PyPI](https://img.shields.io/pypi/v/agentprdiff.svg)](https://pypi.org/project/agentprdiff/)
55
64
  [![Python](https://img.shields.io/pypi/pyversions/agentprdiff.svg)](https://pypi.org/project/agentprdiff/)
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Guard your LLM agents in CI.** Snapshot tests that catch behavioral regressions when models, prompts, or vendors change.
4
4
 
5
- 📚 **[Documentation: agentprdiff.dev](https://agentprdiff.dev/)**  ·  ⚡ [Quickstart](https://agentprdiff.dev/quickstart/)  ·  🤖 [AI-agent adoption](https://agentprdiff.dev/quickstart/#path-a--let-an-ai-agent-adopt-the-package-for-you-recommended)  ·  📦 [PyPI](https://pypi.org/project/agentprdiff/)
5
+ 📚 **[Documentation: agentprdiff.dev](https://agentprdiff.dev/)**  ·  ⚡ [Quickstart](https://agentprdiff.dev/quickstart/)  ·  🤖 [AI-agent adoption](https://agentprdiff.dev/quickstart/#path-a)  ·  📦 [PyPI](https://pypi.org/project/agentprdiff/)
6
6
 
7
7
  > You upgraded Claude. You tweaked a system prompt. You swapped `gpt-4o` for `gpt-4o-mini` in the cheap path. Which of your agent's behaviors just changed? `agentprdiff` tells you — before the PR merges.
8
8
 
@@ -10,6 +10,15 @@
10
10
  pip install agentprdiff
11
11
  ```
12
12
 
13
+ > **Don't have Python 3.10+ yet?** Step-by-step install instructions for
14
+ > [macOS, Windows, and Linux](https://agentprdiff.dev/installation/#install-python-310-first-if-you-dont-have-it).
15
+ >
16
+ > **Multiple Python versions on your machine?** If `pip install` reports
17
+ > `No matching distribution found` even after installing Python 3.10+,
18
+ > use `python3.12 -m pip install agentprdiff` (substitute your installed
19
+ > 3.10+ binary). Sidesteps `$PATH` confusion when Homebrew's Python and
20
+ > the system Python coexist. Full troubleshooting: [Installation guide](https://agentprdiff.dev/installation/).
21
+
13
22
  [![CI](https://github.com/vnageshwaran-de/agentprdiff/actions/workflows/ci.yml/badge.svg)](https://github.com/vnageshwaran-de/agentprdiff/actions/workflows/ci.yml)
14
23
  [![PyPI](https://img.shields.io/pypi/v/agentprdiff.svg)](https://pypi.org/project/agentprdiff/)
15
24
  [![Python](https://img.shields.io/pypi/pyversions/agentprdiff.svg)](https://pypi.org/project/agentprdiff/)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentprdiff"
7
- version = "0.2.4"
7
+ version = "0.3.0"
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"
@@ -67,7 +67,7 @@ from .differ import AssertionChange, TraceDelta, diff_traces
67
67
  from .runner import CaseReport, Runner, RunReport
68
68
  from .store import BaselineStore
69
69
 
70
- __version__ = "0.2.4"
70
+ __version__ = "0.2.5"
71
71
 
72
72
  __all__ = [
73
73
  # core
@@ -0,0 +1,86 @@
1
+ """SDK adapters for agentprdiff.
2
+
3
+ The adapters take an agent that uses a real LLM SDK (OpenAI, Anthropic, or any
4
+ OpenAI-compatible provider like Groq, Gemini's openai-compat endpoint,
5
+ OpenRouter, Ollama, or vLLM) and capture every model call as an `LLMCall` on a
6
+ `Trace` — without forcing the user to rewrite their agent loop.
7
+
8
+ The pattern is::
9
+
10
+ from agentprdiff.adapters.openai import instrument_client, instrument_tools
11
+
12
+ def my_agent(query: str):
13
+ client = OpenAI(...)
14
+ with instrument_client(client) as trace:
15
+ tools = instrument_tools(TOOL_MAP, trace)
16
+ # ... user's existing tool-calling loop, untouched ...
17
+ return final_text, trace
18
+
19
+ Submodules are imported lazily so the base `agentprdiff` install doesn't pull
20
+ in `openai` / `anthropic` unless the user opts in via the extras::
21
+
22
+ pip install "agentprdiff[openai]"
23
+ pip install "agentprdiff[anthropic]"
24
+
25
+ See `docs/adapters.md` for the full reference.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ # Re-export pricing helpers — these are pure-Python and have no SDK dependency.
31
+ from .pricing import (
32
+ DEFAULT_PRICES,
33
+ PriceTable,
34
+ estimate_cost_usd,
35
+ register_prices,
36
+ )
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # Optional global model override.
40
+ #
41
+ # When set via ``set_default_model("gpt-4o-mini")``, every subsequent
42
+ # ``instrument_client``-patched ``create()`` call rewrites the ``model``
43
+ # keyword argument before delegating to the underlying SDK. Pass ``None``
44
+ # to clear.
45
+ #
46
+ # Module-level + process-wide on purpose — this is a knob for tooling
47
+ # (Studio's multi-model benchmark, or anyone doing batch comparisons
48
+ # from a notebook), not for production agent code. Production agents
49
+ # should keep passing ``model=`` explicitly.
50
+ #
51
+ # Reads happen at call time (the patched_create looks up the current
52
+ # value each invocation), so a single-threaded sequence of:
53
+ #
54
+ # set_default_model("gpt-4o-mini"); run_suite(); \
55
+ # set_default_model("claude-haiku-4-5"); run_suite(); \
56
+ # set_default_model(None)
57
+ #
58
+ # does exactly what you'd expect. Concurrent runs in the same process
59
+ # share the override — if you need per-task isolation, run each in a
60
+ # fresh subprocess.
61
+
62
+ _DEFAULT_MODEL_OVERRIDE: str | None = None
63
+
64
+
65
+ def set_default_model(model: str | None) -> None:
66
+ """Override the model on every subsequent patched ``create()`` call.
67
+
68
+ Pass ``None`` to clear. See module docstring for the semantics.
69
+ """
70
+ global _DEFAULT_MODEL_OVERRIDE
71
+ _DEFAULT_MODEL_OVERRIDE = model
72
+
73
+
74
+ def get_default_model() -> str | None:
75
+ """Read the current model override (``None`` if not set)."""
76
+ return _DEFAULT_MODEL_OVERRIDE
77
+
78
+
79
+ __all__ = [
80
+ "DEFAULT_PRICES",
81
+ "PriceTable",
82
+ "estimate_cost_usd",
83
+ "register_prices",
84
+ "set_default_model",
85
+ "get_default_model",
86
+ ]
@@ -106,6 +106,15 @@ def instrument_client(
106
106
  instance_attr_value = vars(messages_attr).get("create")
107
107
 
108
108
  def patched_create(*args: Any, **kwargs: Any) -> Any:
109
+ # Honor the global model override if set — same contract as the
110
+ # OpenAI adapter. Lookup happens at call time so the value reflects
111
+ # whatever set_default_model() last set.
112
+ from . import get_default_model
113
+
114
+ override = get_default_model()
115
+ if override is not None and "model" in kwargs:
116
+ kwargs = dict(kwargs)
117
+ kwargs["model"] = override
109
118
  start = time.perf_counter()
110
119
  try:
111
120
  response = original_create(*args, **kwargs)
@@ -311,6 +311,30 @@ def _record_failure(
311
311
  )
312
312
 
313
313
 
314
+ def _apply_model_override(kwargs: dict[str, Any]) -> dict[str, Any]:
315
+ """If a global model override is set, return a new kwargs dict with
316
+ ``model`` rewritten. Returns the original dict when no override is active.
317
+
318
+ The lookup happens at call time so multi-leg benchmarks can flip the
319
+ override between runs in the same process.
320
+ """
321
+ # Import at call time to avoid an import cycle (adapters/__init__.py
322
+ # itself imports from .pricing, which is fine; this module imports from
323
+ # adapters at package level but we want the *live* value).
324
+ from . import get_default_model
325
+
326
+ override = get_default_model()
327
+ if override is None:
328
+ return kwargs
329
+ # Only rewrite if the caller actually passed a model — preserves the
330
+ # SDK's own error path when model is missing.
331
+ if "model" not in kwargs:
332
+ return kwargs
333
+ new_kwargs = dict(kwargs)
334
+ new_kwargs["model"] = override
335
+ return new_kwargs
336
+
337
+
314
338
  def _make_sync_patched_create(
315
339
  original_create: Callable[..., Any],
316
340
  *,
@@ -319,6 +343,7 @@ def _make_sync_patched_create(
319
343
  prices: PriceTable | None,
320
344
  ) -> Callable[..., Any]:
321
345
  def patched_create(*args: Any, **kwargs: Any) -> Any:
346
+ kwargs = _apply_model_override(kwargs)
322
347
  start = time.perf_counter()
323
348
  try:
324
349
  response = original_create(*args, **kwargs)
@@ -350,6 +375,7 @@ def _make_async_patched_create(
350
375
  prices: PriceTable | None,
351
376
  ) -> Callable[..., Awaitable[Any]]:
352
377
  async def patched_create(*args: Any, **kwargs: Any) -> Any:
378
+ kwargs = _apply_model_override(kwargs)
353
379
  start = time.perf_counter()
354
380
  try:
355
381
  response = await original_create(*args, **kwargs)
@@ -0,0 +1,108 @@
1
+ # agentprdiff Studio
2
+
3
+ Web UI on top of the [agentprdiff](https://github.com/vnageshwaran-de/agentprdiff) engine.
4
+
5
+ The CLI (`pip install agentprdiff`, write `suite.py`, `agentprdiff check`) still works exactly the same. Studio is a parallel surface for non-dev users (PMs, QA, vibecoders) who want to trigger runs, watch progress live, diff cases, and approve baselines in a browser.
6
+
7
+ ## Quick start (Docker)
8
+
9
+ ```bash
10
+ # SQLite, single container, zero config:
11
+ docker compose up --build
12
+
13
+ # Open the UI:
14
+ open http://localhost:8080
15
+ ```
16
+
17
+ That spins up one container, persists state to a `studio-data` volume, and serves both the API (under `/api/*`) and the SPA on port 8080.
18
+
19
+ For shared deployments swap to Postgres:
20
+
21
+ ```bash
22
+ docker compose --profile postgres up --build
23
+ # also export STUDIO_DATABASE_URL=postgresql+asyncpg://studio:studio@db:5432/studio
24
+ # (or set it in studio.environment in compose.yml).
25
+ ```
26
+
27
+ ## Run modes
28
+
29
+ | Mode | Storage | Best for |
30
+ |---|---|---|
31
+ | Default (SQLite) | `studio-data` volume → `/data/studio.db` | Solo / small team, single host |
32
+ | `--profile postgres` | Postgres container + `studio-pg` volume | Multiple users, durable history |
33
+
34
+ Plus the orthogonal **intake modes** for projects you create inside Studio:
35
+
36
+ * **git** — clone a remote repo, walk it for suites, run via subprocess+venv.
37
+ * **zip** — upload an archive, same execution path as git.
38
+ * **http** — point Studio at a deployed endpoint, author suites as JSON, runs go in-process via httpx (no Python execution, baselines stored in the DB).
39
+
40
+ ## Local development (without Docker)
41
+
42
+ ```bash
43
+ # Backend (Python).
44
+ pip install -e . # the engine, from the repo root
45
+ pip install -e studio/backend
46
+ uvicorn agentprdiff_studio.main:app --reload --port 8080
47
+
48
+ # Frontend (separate terminal).
49
+ cd studio/frontend
50
+ npm install
51
+ npm run dev # Vite on :5173, proxies /api → :8080
52
+ open http://localhost:5173
53
+ ```
54
+
55
+ In dev the frontend lives on Vite. In Docker / prod the FastAPI app serves the built `dist/` on the same port as the API.
56
+
57
+ ## Configuration
58
+
59
+ Everything is env-driven. The most useful knobs:
60
+
61
+ | Env var | Default | Notes |
62
+ |---|---|---|
63
+ | `STUDIO_DATA_DIR` | `./.studio-data` | DB + cloned repos + uploaded zips |
64
+ | `STUDIO_DATABASE_URL` | `sqlite+aiosqlite:///<data>/studio.db` | Set to `postgresql+asyncpg://…` for Postgres |
65
+ | `STUDIO_FRONTEND_DIR` | unset (dev) / `/opt/studio/frontend` (Docker) | If set + exists, FastAPI serves the SPA there |
66
+ | `STUDIO_SECRET_KEY` | generated and persisted to `<data>/.secret_key` | Fernet key for encrypting secrets at rest |
67
+ | `STUDIO_ENGINE_REQ` | `agentprdiff>=0.2.5` | What pip installs into per-project venvs |
68
+ | `STUDIO_RUN_WALLTIME_SECONDS` | `300` | Per-run hard wall-time |
69
+ | `STUDIO_RUN_MEMORY_MB` | `1024` | Per-run memory cap (POSIX rlimit) |
70
+ | `STUDIO_CORS_ORIGINS` | `["*"]` | Pass as JSON array via env if tightening |
71
+
72
+ ## What's in the image
73
+
74
+ * Engine (`agentprdiff`) installed from PyPI (or a path, via `STUDIO_ENGINE_REQ`).
75
+ * Studio backend (FastAPI + SQLAlchemy + httpx + GitPython).
76
+ * Built SPA (Vite output) at `/opt/studio/frontend`.
77
+ * `git`, `build-essential`, `tini` for clean signal handling.
78
+
79
+ The image runs as `uvicorn agentprdiff_studio.main:app --host 0.0.0.0 --port 8080` under `tini`. A healthcheck hits `/api/health` every 30s.
80
+
81
+ ## Persistence
82
+
83
+ All durable state lives under `/data` inside the container, mounted via the named volume `studio-data`:
84
+
85
+ ```
86
+ /data
87
+ ├── studio.db # SQLite (default mode)
88
+ ├── .secret_key # Fernet key (created on first boot)
89
+ └── projects/<id>/
90
+ ├── repo/ # git intake
91
+ │ └── .studio-venv/ # per-project venv (provisioned on first run)
92
+ └── upload/ # zip intake
93
+ ```
94
+
95
+ For Postgres deployments the DB lives in `studio-pg`; the `studio-data` volume still holds the per-project workspaces and venvs.
96
+
97
+ ## Where things land in the repo
98
+
99
+ ```
100
+ studio/
101
+ ├── Dockerfile multi-stage: node build → python runtime
102
+ ├── docker-compose.yml default + postgres profile
103
+ ├── .dockerignore keeps the build context small
104
+ ├── backend/
105
+ │ └── src/agentprdiff_studio/ FastAPI app + executor + DB layer
106
+ └── frontend/
107
+ └── src/ Vite + React + TS SPA
108
+ ```
@@ -0,0 +1,34 @@
1
+ # agentprdiff-studio (backend)
2
+
3
+ Web server for **agentprdiff Studio** — a browser UI on top of the [agentprdiff](https://github.com/vnageshwaran-de/agentprdiff) engine.
4
+
5
+ The CLI workflow (`pip install agentprdiff`, write `suite.py`, `agentprdiff check`) is unchanged. Studio is a parallel surface for non-dev users (PMs, QA, vibecoders) who want to trigger runs, review diffs, and approve baselines in a browser.
6
+
7
+ ## Status
8
+
9
+ **M1** — backend skeleton + git intake + executor. No UI yet; everything is driven by `curl`.
10
+
11
+ ## Run it locally
12
+
13
+ ```bash
14
+ cd studio/backend
15
+ pip install -e ../.. # install the engine from the monorepo
16
+ pip install -e ".[dev]" # install studio backend + dev tools
17
+ uvicorn agentprdiff_studio.main:app --reload --port 8080
18
+ ```
19
+
20
+ Default config writes to `./.studio-data/` (SQLite + cloned project workspaces). Override with env vars — see `src/agentprdiff_studio/settings.py`.
21
+
22
+ ## API (M1)
23
+
24
+ ```
25
+ POST /api/projects create + git-clone
26
+ POST /api/projects/{id}/sync re-pull + rediscover suites
27
+ GET /api/projects/{id} detail
28
+ GET /api/projects/{id}/suites list discovered suites
29
+ POST /api/runs {project_id, suite_id, command}
30
+ GET /api/runs/{id} status + case summary
31
+ GET /api/runs/{id}/cases full per-case results
32
+ ```
33
+
34
+ Zip / HTTP intake, secrets, SSE, and the React UI come in M2–M5.
@@ -1,43 +0,0 @@
1
- """SDK adapters for agentprdiff.
2
-
3
- The adapters take an agent that uses a real LLM SDK (OpenAI, Anthropic, or any
4
- OpenAI-compatible provider like Groq, Gemini's openai-compat endpoint,
5
- OpenRouter, Ollama, or vLLM) and capture every model call as an `LLMCall` on a
6
- `Trace` — without forcing the user to rewrite their agent loop.
7
-
8
- The pattern is::
9
-
10
- from agentprdiff.adapters.openai import instrument_client, instrument_tools
11
-
12
- def my_agent(query: str):
13
- client = OpenAI(...)
14
- with instrument_client(client) as trace:
15
- tools = instrument_tools(TOOL_MAP, trace)
16
- # ... user's existing tool-calling loop, untouched ...
17
- return final_text, trace
18
-
19
- Submodules are imported lazily so the base `agentprdiff` install doesn't pull
20
- in `openai` / `anthropic` unless the user opts in via the extras::
21
-
22
- pip install "agentprdiff[openai]"
23
- pip install "agentprdiff[anthropic]"
24
-
25
- See `docs/adapters.md` for the full reference.
26
- """
27
-
28
- from __future__ import annotations
29
-
30
- # Re-export pricing helpers — these are pure-Python and have no SDK dependency.
31
- from .pricing import (
32
- DEFAULT_PRICES,
33
- PriceTable,
34
- estimate_cost_usd,
35
- register_prices,
36
- )
37
-
38
- __all__ = [
39
- "DEFAULT_PRICES",
40
- "PriceTable",
41
- "estimate_cost_usd",
42
- "register_prices",
43
- ]
File without changes