evalshift 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 (123) hide show
  1. evalshift-0.3.0/.gitignore +77 -0
  2. evalshift-0.3.0/CHANGELOG.md +193 -0
  3. evalshift-0.3.0/LICENSE +21 -0
  4. evalshift-0.3.0/PKG-INFO +185 -0
  5. evalshift-0.3.0/README.md +133 -0
  6. evalshift-0.3.0/examples/agent/README.md +41 -0
  7. evalshift-0.3.0/examples/showcase/README.md +79 -0
  8. evalshift-0.3.0/pyproject.toml +106 -0
  9. evalshift-0.3.0/src/evalshift/__init__.py +12 -0
  10. evalshift-0.3.0/src/evalshift/__main__.py +17 -0
  11. evalshift-0.3.0/src/evalshift/analysis/__init__.py +1 -0
  12. evalshift-0.3.0/src/evalshift/analysis/slicing.py +143 -0
  13. evalshift-0.3.0/src/evalshift/analysis/statistics.py +382 -0
  14. evalshift-0.3.0/src/evalshift/cache/__init__.py +1 -0
  15. evalshift-0.3.0/src/evalshift/cache/schema.py +102 -0
  16. evalshift-0.3.0/src/evalshift/cache/store.py +214 -0
  17. evalshift-0.3.0/src/evalshift/cli/__init__.py +1 -0
  18. evalshift-0.3.0/src/evalshift/cli/commands/__init__.py +1 -0
  19. evalshift-0.3.0/src/evalshift/cli/commands/all.py +591 -0
  20. evalshift-0.3.0/src/evalshift/cli/commands/analyze.py +334 -0
  21. evalshift-0.3.0/src/evalshift/cli/commands/cache.py +42 -0
  22. evalshift-0.3.0/src/evalshift/cli/commands/doctor.py +232 -0
  23. evalshift-0.3.0/src/evalshift/cli/commands/evaluate.py +434 -0
  24. evalshift-0.3.0/src/evalshift/cli/commands/init.py +415 -0
  25. evalshift-0.3.0/src/evalshift/cli/commands/report.py +137 -0
  26. evalshift-0.3.0/src/evalshift/cli/commands/run.py +266 -0
  27. evalshift-0.3.0/src/evalshift/cli/commands/test_call.py +254 -0
  28. evalshift-0.3.0/src/evalshift/cli/commands/validate.py +128 -0
  29. evalshift-0.3.0/src/evalshift/cli/main.py +72 -0
  30. evalshift-0.3.0/src/evalshift/config/__init__.py +1 -0
  31. evalshift-0.3.0/src/evalshift/config/loader.py +217 -0
  32. evalshift-0.3.0/src/evalshift/config/models.py +338 -0
  33. evalshift-0.3.0/src/evalshift/evaluators/__init__.py +1 -0
  34. evalshift-0.3.0/src/evalshift/evaluators/base.py +93 -0
  35. evalshift-0.3.0/src/evalshift/evaluators/llm_judge.py +154 -0
  36. evalshift-0.3.0/src/evalshift/evaluators/semantic.py +93 -0
  37. evalshift-0.3.0/src/evalshift/evaluators/structural.py +131 -0
  38. evalshift-0.3.0/src/evalshift/evaluators/tool_arguments.py +216 -0
  39. evalshift-0.3.0/src/evalshift/evaluators/tool_loader.py +198 -0
  40. evalshift-0.3.0/src/evalshift/evaluators/tool_models.py +194 -0
  41. evalshift-0.3.0/src/evalshift/evaluators/tool_parser.py +328 -0
  42. evalshift-0.3.0/src/evalshift/evaluators/tool_selection.py +294 -0
  43. evalshift-0.3.0/src/evalshift/evaluators/tool_trace_structure.py +108 -0
  44. evalshift-0.3.0/src/evalshift/models/__init__.py +1 -0
  45. evalshift-0.3.0/src/evalshift/models/client.py +456 -0
  46. evalshift-0.3.0/src/evalshift/models/registry.py +272 -0
  47. evalshift-0.3.0/src/evalshift/models/replay_client.py +203 -0
  48. evalshift-0.3.0/src/evalshift/parsers/__init__.py +1 -0
  49. evalshift-0.3.0/src/evalshift/parsers/base.py +144 -0
  50. evalshift-0.3.0/src/evalshift/parsers/manual.py +45 -0
  51. evalshift-0.3.0/src/evalshift/parsers/python_string.py +204 -0
  52. evalshift-0.3.0/src/evalshift/py.typed +0 -0
  53. evalshift-0.3.0/src/evalshift/reports/__init__.py +1 -0
  54. evalshift-0.3.0/src/evalshift/reports/html.py +69 -0
  55. evalshift-0.3.0/src/evalshift/reports/json.py +523 -0
  56. evalshift-0.3.0/src/evalshift/reports/templates/report.css +203 -0
  57. evalshift-0.3.0/src/evalshift/reports/templates/report.html.j2 +268 -0
  58. evalshift-0.3.0/src/evalshift/runner/__init__.py +1 -0
  59. evalshift-0.3.0/src/evalshift/runner/checkpoint.py +280 -0
  60. evalshift-0.3.0/src/evalshift/runner/models.py +152 -0
  61. evalshift-0.3.0/src/evalshift/runner/orchestrator.py +703 -0
  62. evalshift-0.3.0/src/evalshift/suite/__init__.py +1 -0
  63. evalshift-0.3.0/src/evalshift/suite/loader.py +216 -0
  64. evalshift-0.3.0/src/evalshift/suite/models.py +156 -0
  65. evalshift-0.3.0/src/evalshift/utils/__init__.py +1 -0
  66. evalshift-0.3.0/src/evalshift/utils/cost.py +195 -0
  67. evalshift-0.3.0/src/evalshift/utils/templating.py +213 -0
  68. evalshift-0.3.0/tests/__init__.py +0 -0
  69. evalshift-0.3.0/tests/conftest.py +7 -0
  70. evalshift-0.3.0/tests/integration/__init__.py +0 -0
  71. evalshift-0.3.0/tests/integration/fixtures/validate_missing_var/evalshift.yaml +6 -0
  72. evalshift-0.3.0/tests/integration/fixtures/validate_missing_var/golden.jsonl +2 -0
  73. evalshift-0.3.0/tests/integration/fixtures/validate_non_literal/evalshift.yaml +7 -0
  74. evalshift-0.3.0/tests/integration/fixtures/validate_non_literal/golden.jsonl +1 -0
  75. evalshift-0.3.0/tests/integration/fixtures/validate_non_literal/prompts.py +8 -0
  76. evalshift-0.3.0/tests/integration/fixtures/validate_ok/evalshift.yaml +7 -0
  77. evalshift-0.3.0/tests/integration/fixtures/validate_ok/golden.jsonl +2 -0
  78. evalshift-0.3.0/tests/integration/fixtures/validate_ok/prompts.py +3 -0
  79. evalshift-0.3.0/tests/integration/test_tool_pipeline.py +343 -0
  80. evalshift-0.3.0/tests/integration/test_validate_command.py +96 -0
  81. evalshift-0.3.0/tests/unit/__init__.py +0 -0
  82. evalshift-0.3.0/tests/unit/fixtures/tool_responses/anthropic/parallel_tool_calls.json +30 -0
  83. evalshift-0.3.0/tests/unit/fixtures/tool_responses/anthropic/refusal.json +19 -0
  84. evalshift-0.3.0/tests/unit/fixtures/tool_responses/anthropic/single_tool_call.json +24 -0
  85. evalshift-0.3.0/tests/unit/fixtures/tool_responses/anthropic/text_only.json +19 -0
  86. evalshift-0.3.0/tests/unit/fixtures/tool_responses/anthropic/tool_call_with_text.json +25 -0
  87. evalshift-0.3.0/tests/unit/fixtures/tool_responses/gemini/single_tool_call.json +26 -0
  88. evalshift-0.3.0/tests/unit/fixtures/tool_responses/gemini/text_only.json +16 -0
  89. evalshift-0.3.0/tests/unit/fixtures/tool_responses/openai/parallel_tool_calls.json +34 -0
  90. evalshift-0.3.0/tests/unit/fixtures/tool_responses/openai/refusal.json +17 -0
  91. evalshift-0.3.0/tests/unit/fixtures/tool_responses/openai/single_tool_call.json +26 -0
  92. evalshift-0.3.0/tests/unit/fixtures/tool_responses/openai/text_only.json +16 -0
  93. evalshift-0.3.0/tests/unit/test_all_command.py +271 -0
  94. evalshift-0.3.0/tests/unit/test_analysis.py +310 -0
  95. evalshift-0.3.0/tests/unit/test_analyze_command.py +222 -0
  96. evalshift-0.3.0/tests/unit/test_cache.py +225 -0
  97. evalshift-0.3.0/tests/unit/test_checkpoint.py +302 -0
  98. evalshift-0.3.0/tests/unit/test_cli_smoke.py +34 -0
  99. evalshift-0.3.0/tests/unit/test_config_loader.py +295 -0
  100. evalshift-0.3.0/tests/unit/test_config_models.py +312 -0
  101. evalshift-0.3.0/tests/unit/test_cost.py +199 -0
  102. evalshift-0.3.0/tests/unit/test_doctor.py +188 -0
  103. evalshift-0.3.0/tests/unit/test_evaluate_command.py +261 -0
  104. evalshift-0.3.0/tests/unit/test_evaluators.py +366 -0
  105. evalshift-0.3.0/tests/unit/test_init.py +267 -0
  106. evalshift-0.3.0/tests/unit/test_model_client.py +523 -0
  107. evalshift-0.3.0/tests/unit/test_model_registry.py +125 -0
  108. evalshift-0.3.0/tests/unit/test_orchestrator.py +462 -0
  109. evalshift-0.3.0/tests/unit/test_parsers.py +305 -0
  110. evalshift-0.3.0/tests/unit/test_reports.py +476 -0
  111. evalshift-0.3.0/tests/unit/test_run_command.py +301 -0
  112. evalshift-0.3.0/tests/unit/test_runner_models.py +197 -0
  113. evalshift-0.3.0/tests/unit/test_suite_loader.py +243 -0
  114. evalshift-0.3.0/tests/unit/test_suite_models.py +147 -0
  115. evalshift-0.3.0/tests/unit/test_templating.py +214 -0
  116. evalshift-0.3.0/tests/unit/test_test_call_command.py +295 -0
  117. evalshift-0.3.0/tests/unit/test_tool_arguments.py +216 -0
  118. evalshift-0.3.0/tests/unit/test_tool_loader.py +139 -0
  119. evalshift-0.3.0/tests/unit/test_tool_models.py +250 -0
  120. evalshift-0.3.0/tests/unit/test_tool_parser.py +403 -0
  121. evalshift-0.3.0/tests/unit/test_tool_selection.py +232 -0
  122. evalshift-0.3.0/tests/unit/test_tool_trace_structure.py +177 -0
  123. evalshift-0.3.0/tests/unit/test_version.py +12 -0
@@ -0,0 +1,77 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Distribution / packaging
8
+ .Python
9
+ build/
10
+ develop-eggs/
11
+ dist/
12
+ downloads/
13
+ eggs/
14
+ .eggs/
15
+ lib/
16
+ lib64/
17
+ parts/
18
+ sdist/
19
+ var/
20
+ wheels/
21
+ share/python-wheels/
22
+ *.egg-info/
23
+ .installed.cfg
24
+ *.egg
25
+ MANIFEST
26
+
27
+ # Virtual environments
28
+ .venv/
29
+ venv/
30
+ ENV/
31
+ env/
32
+
33
+ # Testing
34
+ .tox/
35
+ .nox/
36
+ .coverage
37
+ .coverage.*
38
+ .cache
39
+ htmlcov/
40
+ .pytest_cache/
41
+ .hypothesis/
42
+ coverage.xml
43
+ *.cover
44
+
45
+ # Type checking
46
+ .mypy_cache/
47
+ .dmypy.json
48
+ dmypy.json
49
+ .pyre/
50
+ .pytype/
51
+
52
+ # Linting
53
+ .ruff_cache/
54
+
55
+ # IDE
56
+ .vscode/
57
+ .idea/
58
+ *.swp
59
+ *.swo
60
+ *~
61
+ .DS_Store
62
+
63
+ # EvalShift runtime
64
+ .evalshift/
65
+ *.db
66
+ *.sqlite
67
+ *.sqlite3
68
+
69
+ # Docs build
70
+ site/
71
+
72
+ # uv
73
+ uv.lock
74
+
75
+ # Build plan source (kept locally, not in git)
76
+ evalshift-cli-mvp-build-plan.md.pdf
77
+ .claude/agents/brainstormer.md
@@ -0,0 +1,193 @@
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.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.3.0] — 2026-05-12
11
+
12
+ ### Added
13
+
14
+ - `evalshift all` runs the full `doctor → run → evaluate → analyze →
15
+ report` pipeline in one command, with a single Rich Live region:
16
+ stacked status rows, an inline block-bar for the run stage, and a
17
+ final verdict block (`✓ candidate is significantly better` /
18
+ `✗ candidate regressed` / `~ no significant change`) plus a minor-
19
+ regression callout. Forwards every flag the underlying commands
20
+ accept (`--from/--to`, `--config`, `--suite`, `--offline`,
21
+ `--fixtures`, `--yes`, `--resume`, `--gate`, `--open`).
22
+ - `evaluate`, `analyze`, and `report` now expose reusable cores
23
+ (`run_evaluate`, `run_analyze`, `run_report`) returning typed
24
+ result dataclasses; the existing Typer commands are thin wrappers.
25
+ - Orchestrator gained an optional `on_progress` callback
26
+ (`ProgressEvent`) so a caller can drive its own progress UI in
27
+ place of the built-in Rich `Progress`. `preflight_cost(...)`
28
+ exposes the cost estimate without dispatching any work.
29
+
30
+ ### Changed
31
+
32
+ - `evalshift all`: the cost preflight row is now labelled
33
+ `max cost ........... ≤ $X.XX` instead of `estimated cost ... $X.XX`.
34
+ The underlying figure is unchanged — it has always been a
35
+ worst-case ceiling (priced as if every call emits the model's full
36
+ `default_max_tokens`) — but the new wording makes that explicit so
37
+ users don't read it as a forecast. The interactive cost-confirmation
38
+ prompt also now reads "at most $X" instead of "estimated cost $X".
39
+ - `evalshift init` now always scaffolds an agent project. The brief
40
+ `--agent` flag introduced in the previous unreleased entry has been
41
+ removed — there's only one starter, and it's the v0.2 agent flow.
42
+ Existing v0.1-style projects are unaffected (no breaking change to
43
+ the config schema or runner).
44
+ - Agent scaffold ships 6 customer-support tools (`search_orders`,
45
+ `lookup_customer`, `issue_refund`, `update_order_status`,
46
+ `send_email`, `notify_security_team`) and 40 suite rows across 5
47
+ slices (security, routine, refund, customer_lookup, text_only) so
48
+ the analysis layer produces real severity badges on a first run
49
+ instead of "insufficient" / zero-variance warnings.
50
+ - Dropped `structural.length` from the scaffolded `evalshift.yaml`.
51
+ Agent runs frequently produce empty `final_text` (model returned
52
+ only tool calls) which made length scores 0/0 across every routine
53
+ + security row — pure noise. Users who want length checks can
54
+ add the block back manually for prompts that produce text.
55
+
56
+ ## [0.2.0] — Tool-call evaluation
57
+
58
+ Adds agent-migration support: detects regressions in *which* tools the
59
+ new model calls, *what* arguments it passes, and *how* it sequences
60
+ them. v0.1 backward compatible — every existing example keeps working
61
+ without changes.
62
+
63
+ ### Added
64
+
65
+ - `evalshift.evaluators.tool_models`: provider-agnostic `ToolSpec`,
66
+ `ToolCall`, `ToolTrace`. `ToolSpec.to_anthropic` / `to_openai` /
67
+ `from_dict` adapters keep wire-format details out of user code.
68
+ - `evalshift.evaluators.tool_parser`: `parse_response_to_trace`
69
+ dispatcher with three provider parsers. Handles the LiteLLM
70
+ Anthropic-normalised-to-OpenAI shape transparently. Malformed
71
+ argument JSON marked `_parse_error` instead of crashing.
72
+ - `evalshift.evaluators.tool_loader`: `load_tools(yaml or json)` with
73
+ the same plain/rich error rendering pattern as `ConfigError`.
74
+ - `evalshift.models.client.complete_with_tools` and
75
+ `ToolCompletionResult` — tool-aware call path that doesn't replace
76
+ the existing `complete` / `CompletionResult`.
77
+ - `evalshift test-call --tools <path>` smoke command extension.
78
+ - `scripts/smoke_live_tools.py` for fixture capture (manual; not in CI).
79
+ - Three new evaluators wired into `evalshift evaluate`:
80
+ - `ToolSelectionEvaluator` (modes: exact / set / first / expected)
81
+ - `ToolArgumentsEvaluator` (per-field strategies: exact / subset /
82
+ numeric / semantic; greedy nearest-index call matching)
83
+ - `ToolTraceStructureEvaluator` (call_count / parallelism /
84
+ refusal_alignment / expected_count_alignment; refusal mismatches
85
+ force severity_floor: high)
86
+ - Suite extension: optional `expected_tools` (with per-call
87
+ `match_strategy`), `expected_tool_count`, `expected_no_tools`,
88
+ `expected_parallel`. Mutual-exclusion checks where appropriate.
89
+ - Config extension: `prompts[].tools_path` makes a prompt agent-style;
90
+ `evaluators.{tool_selection, tool_arguments, tool_trace_structure}`
91
+ blocks added.
92
+ - Orchestrator dispatch: agent prompts route through
93
+ `complete_with_tools`; the resulting `Call.trace` round-trips via
94
+ pydantic so `raw.jsonl` Just Works.
95
+ - HTML report extension: per-prompt "Top regressions" section now
96
+ renders side-by-side trace diffs for tool-evaluator regressions
97
+ with green / yellow / red colour coding for matching / different-
98
+ args / missing-extra calls. CSS stays inlined.
99
+ - `evalshift doctor` warns about common agent-config mistakes:
100
+ prompts with `tools_path` but no tool evaluators, and suite
101
+ examples with `expected_tools` but no agent prompts.
102
+ - Docs: `docs/agents.md` walkthrough, plus configuration / evaluators
103
+ / faq updates.
104
+ - `examples/agent/` runnable customer-routing example with 12 golden
105
+ rows across security / routine / text-only slices.
106
+
107
+ ### Quality
108
+
109
+ - 512+ tests, including end-to-end integration tests covering the
110
+ PRD §9.3 failure-mode grid. ruff + format + mypy --strict clean.
111
+ - v0.1 backward compatibility verified: existing 462 tests still
112
+ green; `examples/simple/` unchanged.
113
+
114
+ ## [0.1.0]
115
+
116
+ First alpha release. Every command in the planned MVP pipeline is shipped.
117
+
118
+ ### Live commands
119
+
120
+ | Command | What it does |
121
+ | ---------------------- | ----------------------------------------------------------------------- |
122
+ | `evalshift doctor` | Check Python version, provider API keys, and `evalshift.yaml` validity. |
123
+ | `evalshift init` | Scaffold a starter project (yaml + prompts.py + golden.jsonl). |
124
+ | `evalshift run` | Call source + target models on every (prompt, example) → `raw.jsonl`. |
125
+ | `evalshift evaluate` | Score every (source, target) pair → `scores.jsonl`. |
126
+ | `evalshift analyze` | Paired tests + BH correction → `analysis.json`. |
127
+ | `evalshift report` | Single-file HTML report → `report.html` + `report.json`. |
128
+ | `evalshift cache clear`| Wipe the local SQLite response cache. |
129
+ | `evalshift validate` | (hidden dev) Verify config + suite + prompts are compatible. |
130
+ | `evalshift test-call` | (hidden dev) One-shot smoke call to confirm provider connectivity. |
131
+
132
+ ### Added — Phase 0–1
133
+
134
+ - `pyproject.toml` (Python ≥3.14, hatchling, MIT, pinned deps), `ruff.toml`, `mypy.ini`, pytest config, `.pre-commit-config.yaml`, GitHub Actions CI.
135
+ - Full `src/evalshift` package skeleton across cli/config/parsers/models/suite/runner/evaluators/analysis/reports/cache/utils with module docstrings and a `py.typed` marker.
136
+ - `evalshift.config.models`: pydantic v2 schema for `evalshift.yaml` (prompts, evaluators, slices, defaults) with strict validation, `extra='forbid'`, and detection-mode field invariants.
137
+ - `evalshift.config.loader`: `load_config()` plus a structured `ConfigError` with both plain-text and Rich panel rendering.
138
+ - `evalshift doctor`: reports Python version, provider API keys, and `evalshift.yaml` validity. Exits 1 only on hard failures.
139
+ - `evalshift init`: scaffolds `evalshift.yaml` + `prompts.py` + `golden.jsonl`. `--force` overwrite, `--directory` target dir.
140
+
141
+ ### Added — Phase 2 (suite + prompt loading)
142
+
143
+ - `evalshift.suite` package: pydantic `SuiteExample` + `Suite` models, JSONL loader with line-numbered errors, blank-line tolerance, multi-error collection.
144
+ - `evalshift.parsers` package: `ManualParser` (inline content) and `PythonStringParser` (AST-walks `.py` for module-level string literals; rejects f-strings, concatenation, function calls, attribute access, name references — never runs user code).
145
+ - `evalshift.utils.templating`: `extract_variables`, `render` with strict missing-var detection, and a bulk `validate_suite_against_prompts` pre-flight check.
146
+ - `evalshift validate` (hidden): end-to-end pre-flight that confirms config + suite + prompts are mutually compatible.
147
+
148
+ ### Added — Phase 3 (model client + cache)
149
+
150
+ - `evalshift.models.registry`: advisory, not gating. `resolve_model()` accepts any model id, falling back to prefix-inferred provider when not in the curated registry. `get_model()` strict variant kept for tests.
151
+ - `evalshift.cache`: SQLAlchemy 2.0 + async `CacheStore` (sha256 keys, 7-day TTL) at `~/.evalshift/cache.db`. Added `greenlet` runtime dep.
152
+ - `evalshift cache clear` CLI command (under a `cache` sub-app).
153
+ - `evalshift.models.client`: async `ModelClient` wrapping `litellm.acompletion` with uniform error mapping (`RateLimitError` / `AuthError` / `ModelError`), full-jitter exponential backoff retries (auth short-circuits), and per-call cost + token bookkeeping.
154
+ - `evalshift.utils.cost`: `estimate_run_cost(...)` for pre-flight cost estimation with defensive fallbacks for missing LiteLLM pricing/token-counter data.
155
+ - `evalshift test-call` (hidden): one-shot smoke test renderer with response/tokens/cost/latency Rich panel.
156
+
157
+ ### Added — Phase 4 (run orchestrator)
158
+
159
+ - `evalshift.runner` package: pydantic `RunState` + `Call` models, atomic `state.json` checkpointing, append-only `raw.jsonl`, crash-safe iterator, `validate_resume` with hash-drift detection.
160
+ - `run_orchestrator`: async loop with `asyncio.Semaphore(concurrency)`, cache-check → live-call → record per (prompt, example, role), Rich progress bar, $10 cost gate (skip with `--yes`), 50-call checkpoint cadence.
161
+ - `evalshift run` command: `--from`, `--to`, `--config`, `--suite`, `--resume`, `--yes`. Outputs go to `.evalshift/runs/<run-id>/`. Per-call errors are recorded but don't fail the run.
162
+
163
+ ### Added — Phase 5 (evaluators)
164
+
165
+ - `evalshift.evaluators` package: `Evaluator` Protocol, `PairedScore`, `EvalRecord`.
166
+ - Structural: `JsonSchemaEvaluator`, `RegexEvaluator`, `LengthEvaluator` (1.0 inside bounds, distance-decayed outside).
167
+ - Semantic: `CosineSimilarityEvaluator` (target preservation framing — source = 1.0, target = cosine to source).
168
+ - LLM judge: `PairwiseJudgeEvaluator` with order-randomization and defensive JSON parsing.
169
+ - `evalshift evaluate <run-id>` command: pairs source/target calls, runs every configured evaluator, writes `scores.jsonl`.
170
+
171
+ ### Added — Phase 6 (statistics)
172
+
173
+ - `evalshift.analysis.slicing`: `build_slices` groups records by tag (with implicit `"all"` slice); `aggregates()` computes per-slice n/mean/std.
174
+ - `evalshift.analysis.statistics`: per-comparison Shapiro-Wilk → paired t-test or Wilcoxon signed-rank, Cohen's d, 95% CI (analytical for t-test, bootstrap for Wilcoxon), Benjamini-Hochberg correction across the whole run, severity classification (critical/high/medium/low/improved/none/insufficient).
175
+ - `evalshift analyze <run-id>` command: writes `analysis.json` and prints a Rich summary table.
176
+
177
+ ### Added — Phase 7 (report)
178
+
179
+ - `evalshift.reports.json`: `build_report_payload` + `ReportData` stitching state + raw + scores + analysis into a single payload. Persists `report.json`.
180
+ - `evalshift.reports.templates/report.html.j2` + `report.css`: self-contained HTML (CSS inlined, zero external assets, no JS) with executive summary, per-prompt aggregate + slice tables, top-5 regressions side-by-side, methodology appendix.
181
+ - `evalshift report <run-id>` command, with optional `--open` to launch in the user's default browser.
182
+
183
+ ### Added — Phase 8 (polish + OSS)
184
+
185
+ - Documentation site (MkDocs Material) covering Getting Started, Configuration, Evaluators, Methodology, FAQ.
186
+ - `examples/simple/` runnable example.
187
+ - `CODE_OF_CONDUCT.md` (Contributor Covenant 2.1), `SECURITY.md`, GitHub issue + PR templates.
188
+ - README polished with badges, status, full pipeline walkthrough, non-goals.
189
+
190
+ ### Quality
191
+
192
+ - 371+ tests, ≥95% line coverage, `mypy --strict` clean, `ruff check` clean, `ruff format` clean.
193
+ - Mocked CI throughout (no API keys needed); `scripts/smoke_live.py` deferred for the user's manual live verification.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lukas Babaliauskas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: evalshift
3
+ Version: 0.3.0
4
+ Summary: Run your prompts on two LLMs and find out, with statistical confidence, what regressed.
5
+ Project-URL: Homepage, https://github.com/babaliauskas/EvalShift
6
+ Project-URL: Documentation, https://github.com/babaliauskas/EvalShift#readme
7
+ Project-URL: Repository, https://github.com/babaliauskas/EvalShift
8
+ Project-URL: Issues, https://github.com/babaliauskas/EvalShift/issues
9
+ Project-URL: Changelog, https://github.com/babaliauskas/EvalShift/blob/main/CHANGELOG.md
10
+ Author: Lukas Babaliauskas
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: anthropic,evaluation,gemini,llm,migration,openai,regression-testing
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Topic :: Software Development :: Quality Assurance
23
+ Classifier: Topic :: Software Development :: Testing
24
+ Requires-Python: >=3.14
25
+ Requires-Dist: aiosqlite>=0.19
26
+ Requires-Dist: greenlet>=3.0
27
+ Requires-Dist: httpx>=0.27
28
+ Requires-Dist: jinja2>=3.1
29
+ Requires-Dist: jsonschema>=4.22
30
+ Requires-Dist: litellm>=1.40
31
+ Requires-Dist: numpy>=1.26
32
+ Requires-Dist: pydantic>=2.5
33
+ Requires-Dist: pygments>=2.18
34
+ Requires-Dist: pyyaml>=6.0
35
+ Requires-Dist: rich>=13.7
36
+ Requires-Dist: scipy>=1.12
37
+ Requires-Dist: sqlalchemy>=2.0
38
+ Requires-Dist: tiktoken>=0.7
39
+ Requires-Dist: typer>=0.12
40
+ Provides-Extra: dev
41
+ Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
42
+ Requires-Dist: mypy>=1.10; extra == 'dev'
43
+ Requires-Dist: pre-commit>=3.7; extra == 'dev'
44
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
45
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
46
+ Requires-Dist: pytest>=8.0; extra == 'dev'
47
+ Requires-Dist: respx>=0.21; extra == 'dev'
48
+ Requires-Dist: ruff>=0.5; extra == 'dev'
49
+ Requires-Dist: types-jsonschema>=4.22; extra == 'dev'
50
+ Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
51
+ Description-Content-Type: text/markdown
52
+
53
+ # EvalShift
54
+
55
+ > Run your prompts on two LLMs and find out, with statistical confidence, what regressed.
56
+
57
+ [![CI](https://github.com/babaliauskas/EvalShift/actions/workflows/ci.yml/badge.svg)](https://github.com/babaliauskas/EvalShift/actions/workflows/ci.yml)
58
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
59
+ [![Python 3.14+](https://img.shields.io/badge/python-3.14%2B-blue.svg)](https://www.python.org/downloads/)
60
+ [![Status: alpha](https://img.shields.io/badge/status-alpha-orange.svg)](#status)
61
+
62
+ EvalShift is a local-first CLI that helps engineering teams migrate safely between
63
+ LLM versions (e.g. `claude-4.5-sonnet` → `claude-5-sonnet`). Point it at your
64
+ prompts and a golden suite of inputs; it runs both models, scores the outputs
65
+ with structural / semantic / LLM-as-judge evaluators, and produces a single-file
66
+ HTML report with **defensible statistics**: paired tests, Cohen's d, 95% CIs,
67
+ and Benjamini–Hochberg correction across every (prompt × evaluator × slice)
68
+ comparison.
69
+
70
+ ## Status
71
+
72
+ **Alpha.** Every command in the pipeline is shipped and the test suite is at
73
+ 95%+ coverage. APIs may still change as feedback comes in.
74
+
75
+ ## Install
76
+
77
+ Requires Python 3.14+.
78
+
79
+ ```bash
80
+ # Recommended
81
+ uv pip install evalshift # or: pip install evalshift
82
+ ```
83
+
84
+ From source (for contributors):
85
+
86
+ ```bash
87
+ git clone https://github.com/babaliauskas/EvalShift.git
88
+ cd EvalShift
89
+ uv venv --python 3.14
90
+ source .venv/bin/activate
91
+ uv pip install -e ".[dev]"
92
+ ```
93
+
94
+ ## Quick start
95
+
96
+ ```bash
97
+ # 1. Scaffold a starter project. Writes evalshift.yaml + prompts.py +
98
+ # tools.yaml + a 40-row golden.jsonl for a customer-support agent.
99
+ mkdir my-eval && cd my-eval
100
+ evalshift init
101
+
102
+ # 2. Set whichever provider keys you'll use
103
+ export GOOGLE_API_KEY=... # or ANTHROPIC_API_KEY / OPENAI_API_KEY
104
+
105
+ # 3. Run the whole pipeline in one command (doctor → run → evaluate
106
+ # → analyze → report). Pass --open to launch the report.
107
+ evalshift all --yes --open
108
+ ```
109
+
110
+ `evalshift all` drives the full five-stage pipeline under a single
111
+ Rich Live region — stacked status rows, an inline progress bar for
112
+ the run stage, and a final verdict block that tells you whether the
113
+ candidate is significantly better, regressed, or showed no
114
+ significant change.
115
+
116
+ If you want to drive each stage by hand (useful in CI, or when
117
+ re-running just one stage after fixing config):
118
+
119
+ ```bash
120
+ evalshift doctor
121
+ evalshift run --yes
122
+ evalshift evaluate <run-id>
123
+ evalshift analyze <run-id>
124
+ evalshift report <run-id> --open
125
+ ```
126
+
127
+ Every artefact lives under `.evalshift/runs/<run-id>/` — `state.json`,
128
+ `raw.jsonl`, `scores.jsonl`, `analysis.json`, `report.json`,
129
+ `report.html`. None of it leaves your machine.
130
+
131
+ ## Agent migrations (v0.2)
132
+
133
+ Migrating an agent (a prompt that uses tools)? EvalShift v0.2 detects
134
+ regressions in *which* tools the new model calls, *what* arguments it
135
+ passes, and *how* it sequences them. The killer scenario: a routing
136
+ agent that silently stops calling `notify_security_team` after the
137
+ migration — text-only eval reports green, v0.2 marks it CRITICAL.
138
+
139
+ The default `evalshift init` scaffold *is* an agent project — six tools
140
+ plus a 40-row golden suite. Just run the quick-start above and the
141
+ tool-call evaluators kick in automatically.
142
+
143
+ See [`docs/agents.md`](docs/agents.md) for the full walkthrough and
144
+ the [`examples/agent/`](examples/agent/) directory for a runnable
145
+ customer-support example.
146
+
147
+ ## What the report looks like
148
+
149
+ The HTML report (single file, no external assets, works offline) has:
150
+
151
+ * **Executive summary** — one row per prompt with a severity badge.
152
+ * **Per-prompt deep dive** — aggregate stats, per-slice breakdown,
153
+ top-5 worst regressions side-by-side.
154
+ * **Methodology appendix** — every test, p-value, effect size, and
155
+ CI is documented.
156
+
157
+ ## Why local-first?
158
+
159
+ Your prompts and your suite never leave your machine. The only outbound calls
160
+ are to the LLM providers you configure (Anthropic, OpenAI, Google) using your
161
+ own API keys. There is no EvalShift cloud.
162
+
163
+ ## Documentation
164
+
165
+ * [Getting started](docs/getting-started.md) — install + first run walkthrough
166
+ * [Configuration reference](docs/configuration.md) — every `evalshift.yaml` field
167
+ * [Evaluators](docs/evaluators.md) — when to use which family
168
+ * [Methodology](docs/methodology.md) — the statistical machinery
169
+ * [FAQ](docs/faq.md) — common questions
170
+ * [`MVP_TODO.md`](MVP_TODO.md) — the build checklist (every box ticked)
171
+
172
+ ## Non-goals (for v0.1)
173
+
174
+ * Hosted backend / web UI
175
+ * Multi-criterion judge in a single call
176
+ * Custom evaluator plugin system
177
+ * Comparing more than 2 models in one run
178
+ * Auto-detection of LangChain / LlamaIndex prompt patterns
179
+
180
+ These are deferred to v0.2+; see the PDF spec in the repo for the
181
+ full deferred-features list.
182
+
183
+ ## License
184
+
185
+ [MIT](LICENSE) — free for any use.
@@ -0,0 +1,133 @@
1
+ # EvalShift
2
+
3
+ > Run your prompts on two LLMs and find out, with statistical confidence, what regressed.
4
+
5
+ [![CI](https://github.com/babaliauskas/EvalShift/actions/workflows/ci.yml/badge.svg)](https://github.com/babaliauskas/EvalShift/actions/workflows/ci.yml)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+ [![Python 3.14+](https://img.shields.io/badge/python-3.14%2B-blue.svg)](https://www.python.org/downloads/)
8
+ [![Status: alpha](https://img.shields.io/badge/status-alpha-orange.svg)](#status)
9
+
10
+ EvalShift is a local-first CLI that helps engineering teams migrate safely between
11
+ LLM versions (e.g. `claude-4.5-sonnet` → `claude-5-sonnet`). Point it at your
12
+ prompts and a golden suite of inputs; it runs both models, scores the outputs
13
+ with structural / semantic / LLM-as-judge evaluators, and produces a single-file
14
+ HTML report with **defensible statistics**: paired tests, Cohen's d, 95% CIs,
15
+ and Benjamini–Hochberg correction across every (prompt × evaluator × slice)
16
+ comparison.
17
+
18
+ ## Status
19
+
20
+ **Alpha.** Every command in the pipeline is shipped and the test suite is at
21
+ 95%+ coverage. APIs may still change as feedback comes in.
22
+
23
+ ## Install
24
+
25
+ Requires Python 3.14+.
26
+
27
+ ```bash
28
+ # Recommended
29
+ uv pip install evalshift # or: pip install evalshift
30
+ ```
31
+
32
+ From source (for contributors):
33
+
34
+ ```bash
35
+ git clone https://github.com/babaliauskas/EvalShift.git
36
+ cd EvalShift
37
+ uv venv --python 3.14
38
+ source .venv/bin/activate
39
+ uv pip install -e ".[dev]"
40
+ ```
41
+
42
+ ## Quick start
43
+
44
+ ```bash
45
+ # 1. Scaffold a starter project. Writes evalshift.yaml + prompts.py +
46
+ # tools.yaml + a 40-row golden.jsonl for a customer-support agent.
47
+ mkdir my-eval && cd my-eval
48
+ evalshift init
49
+
50
+ # 2. Set whichever provider keys you'll use
51
+ export GOOGLE_API_KEY=... # or ANTHROPIC_API_KEY / OPENAI_API_KEY
52
+
53
+ # 3. Run the whole pipeline in one command (doctor → run → evaluate
54
+ # → analyze → report). Pass --open to launch the report.
55
+ evalshift all --yes --open
56
+ ```
57
+
58
+ `evalshift all` drives the full five-stage pipeline under a single
59
+ Rich Live region — stacked status rows, an inline progress bar for
60
+ the run stage, and a final verdict block that tells you whether the
61
+ candidate is significantly better, regressed, or showed no
62
+ significant change.
63
+
64
+ If you want to drive each stage by hand (useful in CI, or when
65
+ re-running just one stage after fixing config):
66
+
67
+ ```bash
68
+ evalshift doctor
69
+ evalshift run --yes
70
+ evalshift evaluate <run-id>
71
+ evalshift analyze <run-id>
72
+ evalshift report <run-id> --open
73
+ ```
74
+
75
+ Every artefact lives under `.evalshift/runs/<run-id>/` — `state.json`,
76
+ `raw.jsonl`, `scores.jsonl`, `analysis.json`, `report.json`,
77
+ `report.html`. None of it leaves your machine.
78
+
79
+ ## Agent migrations (v0.2)
80
+
81
+ Migrating an agent (a prompt that uses tools)? EvalShift v0.2 detects
82
+ regressions in *which* tools the new model calls, *what* arguments it
83
+ passes, and *how* it sequences them. The killer scenario: a routing
84
+ agent that silently stops calling `notify_security_team` after the
85
+ migration — text-only eval reports green, v0.2 marks it CRITICAL.
86
+
87
+ The default `evalshift init` scaffold *is* an agent project — six tools
88
+ plus a 40-row golden suite. Just run the quick-start above and the
89
+ tool-call evaluators kick in automatically.
90
+
91
+ See [`docs/agents.md`](docs/agents.md) for the full walkthrough and
92
+ the [`examples/agent/`](examples/agent/) directory for a runnable
93
+ customer-support example.
94
+
95
+ ## What the report looks like
96
+
97
+ The HTML report (single file, no external assets, works offline) has:
98
+
99
+ * **Executive summary** — one row per prompt with a severity badge.
100
+ * **Per-prompt deep dive** — aggregate stats, per-slice breakdown,
101
+ top-5 worst regressions side-by-side.
102
+ * **Methodology appendix** — every test, p-value, effect size, and
103
+ CI is documented.
104
+
105
+ ## Why local-first?
106
+
107
+ Your prompts and your suite never leave your machine. The only outbound calls
108
+ are to the LLM providers you configure (Anthropic, OpenAI, Google) using your
109
+ own API keys. There is no EvalShift cloud.
110
+
111
+ ## Documentation
112
+
113
+ * [Getting started](docs/getting-started.md) — install + first run walkthrough
114
+ * [Configuration reference](docs/configuration.md) — every `evalshift.yaml` field
115
+ * [Evaluators](docs/evaluators.md) — when to use which family
116
+ * [Methodology](docs/methodology.md) — the statistical machinery
117
+ * [FAQ](docs/faq.md) — common questions
118
+ * [`MVP_TODO.md`](MVP_TODO.md) — the build checklist (every box ticked)
119
+
120
+ ## Non-goals (for v0.1)
121
+
122
+ * Hosted backend / web UI
123
+ * Multi-criterion judge in a single call
124
+ * Custom evaluator plugin system
125
+ * Comparing more than 2 models in one run
126
+ * Auto-detection of LangChain / LlamaIndex prompt patterns
127
+
128
+ These are deferred to v0.2+; see the PDF spec in the repo for the
129
+ full deferred-features list.
130
+
131
+ ## License
132
+
133
+ [MIT](LICENSE) — free for any use.
@@ -0,0 +1,41 @@
1
+ # Agent example (v0.2)
2
+
3
+ A small customer-routing agent that EvalShift's v0.2 tool-call evaluators
4
+ exercise end to end.
5
+
6
+ ## What's here
7
+
8
+ | File | Purpose |
9
+ | ----------------- | ---------------------------------------------------------------------- |
10
+ | `evalshift.yaml` | Run config with a single agent prompt + the `tool_selection` evaluator.|
11
+ | `prompts.py` | The agent's system prompt, referenced via `python_string`. |
12
+ | `tools.yaml` | Three tools: `search_orders`, `notify_security_team`, `send_email`. |
13
+ | `golden.jsonl` | 12 examples mixing security, routine, and text-only cases. |
14
+
15
+ ## Run it
16
+
17
+ ```bash
18
+ cd examples/agent
19
+ export GOOGLE_API_KEY=... # or any provider you set up
20
+ evalshift run --yes --from gemini-2.5-flash --to gemini-2.5-pro
21
+ RUN_ID=$(ls .evalshift/runs/ | head -1)
22
+ evalshift evaluate $RUN_ID
23
+ evalshift analyze $RUN_ID
24
+ evalshift report $RUN_ID --open
25
+ ```
26
+
27
+ `evalshift run` notices `tools_path` on the prompt and dispatches via
28
+ `ModelClient.complete_with_tools` — each `Call` row in `raw.jsonl` carries
29
+ a parsed `ToolTrace`. `evalshift evaluate` then runs `tool_selection`
30
+ against the per-example `expected_tools` ground truth.
31
+
32
+ ## What the example demonstrates
33
+
34
+ * **Tool-call regression detection.** If you set `target_model` to a
35
+ weaker model, the report will flag examples where the target stops
36
+ calling `notify_security_team` on security-flagged inputs.
37
+ * **Slice analysis.** The `security` and `routine` slices report
38
+ per-segment regressions, so a model that's fine on routine traffic
39
+ but worse on security can't hide.
40
+ * **Text-only fallback.** The `text_only` examples check that the
41
+ model knows when *not* to call a tool — also a real regression mode.