driftless 0.2.7__tar.gz → 0.2.8__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.
- {driftless-0.2.7 → driftless-0.2.8}/CHANGELOG.md +14 -3
- {driftless-0.2.7 → driftless-0.2.8}/PKG-INFO +2 -2
- {driftless-0.2.7 → driftless-0.2.8}/README.md +1 -1
- {driftless-0.2.7 → driftless-0.2.8}/docs/RELEASE.md +4 -4
- {driftless-0.2.7 → driftless-0.2.8}/site/docs.html +1 -1
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/__init__.py +1 -1
- driftless-0.2.8/tests/test_fetch_provider_models.py +111 -0
- {driftless-0.2.7 → driftless-0.2.8}/.gitignore +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/LICENSE +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/docs/repair-and-generators.md +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/pyproject.toml +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/site/assets/app.js +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/site/assets/hero-workflow.png +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/site/assets/landing.css +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/site/assets/runs.css +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/site/assets/runs.js +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/site/assets/sample-run.json +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/site/assets/styles.css +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/site/index.html +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/site/runs.html +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/calibrate.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/cli.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/compare.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/configure.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/contract.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/data/model_lifecycle.json +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/datasource.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/datastate.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/discovery.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/engine.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/errors.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/evaluation.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/generators.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/github.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/harness.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/init_ci.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/judges.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/label_audit.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/lifecycle.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/policy.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/preflight.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/progress.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/report.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/scanner.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/splits.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/templates.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/src/driftless/view.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/fixtures/live_eval_baseline.json +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/fixtures/smoke/driftless.yml +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/fixtures/smoke/inputs.jsonl +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/fixtures/smoke/labels.jsonl +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/regression_metrics.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/scenarios.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_cli.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_compare.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_contract.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_data_change_gate.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_data_change_regression.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_datasource.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_datastate.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_discovery.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_endpoint.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_engine.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_evaluation.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_extraction.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_generators.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_github.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_grading_loop.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_harness.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_init_ci.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_judge.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_judge_loop.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_label_audit.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_lifecycle.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_migration_live.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_migration_regression.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_plan_act.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_policy.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_poll_act.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_preflight.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_progress.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_refine.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_refresh_catalog.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_regression_metrics.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_repair_prompt.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_report.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_scanner.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_splits.py +0 -0
- {driftless-0.2.7 → driftless-0.2.8}/tests/test_view.py +0 -0
|
@@ -17,6 +17,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
+
## [0.2.8] - 2026-07-01
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **P1.1 provider model discovery** — `tools/fetch_provider_models.py` queries
|
|
25
|
+
OpenAI and Anthropic `/models` APIs and emits new catalog entries only (never
|
|
26
|
+
overwrites lifecycle on existing ids). The scheduled `refresh-catalog.yml`
|
|
27
|
+
job merges discoveries when API keys are configured.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
20
31
|
## [0.2.7] - 2026-07-01
|
|
21
32
|
|
|
22
33
|
### Added
|
|
@@ -153,9 +164,9 @@ First public release on [PyPI](https://pypi.org/project/driftless/0.1.0/).
|
|
|
153
164
|
- **Docs** — project overview, repair algorithm spec, 2×2 migration methodology,
|
|
154
165
|
Poetry + Dependabot product framing.
|
|
155
166
|
|
|
156
|
-
[Unreleased]: https://github.com/driftless-dev/driftless/compare/v0.2.
|
|
157
|
-
[0.2.
|
|
158
|
-
[0.2.
|
|
167
|
+
[Unreleased]: https://github.com/driftless-dev/driftless/compare/v0.2.8...HEAD
|
|
168
|
+
[0.2.8]: https://github.com/driftless-dev/driftless/releases/tag/v0.2.8
|
|
169
|
+
[0.2.7]: https://github.com/driftless-dev/driftless/compare/v0.2.7...v0.2.8
|
|
159
170
|
[0.2.4]: https://github.com/driftless-dev/driftless/compare/v0.2.4...v0.2.5
|
|
160
171
|
[0.2.3]: https://github.com/driftless-dev/driftless/compare/v0.2.3...v0.2.4
|
|
161
172
|
[0.2.2]: https://github.com/driftless-dev/driftless/compare/v0.2.2...v0.2.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: driftless
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Keep prompts in sync when model or eval data changes — Poetry-style lock regeneration, Dependabot-style PRs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/driftless-dev/driftless
|
|
6
6
|
Project-URL: Repository, https://github.com/driftless-dev/driftless
|
|
@@ -133,7 +133,7 @@ can run in CI. See `.github/workflows/` for a scheduled deprecation scan, weekly
|
|
|
133
133
|
`plan --act` triage, and manually-triggered migration workflows.
|
|
134
134
|
|
|
135
135
|
```yaml
|
|
136
|
-
- uses: driftless-dev/driftless@v0.2.
|
|
136
|
+
- uses: driftless-dev/driftless@v0.2.8
|
|
137
137
|
with:
|
|
138
138
|
command: scan
|
|
139
139
|
```
|
|
@@ -94,7 +94,7 @@ can run in CI. See `.github/workflows/` for a scheduled deprecation scan, weekly
|
|
|
94
94
|
`plan --act` triage, and manually-triggered migration workflows.
|
|
95
95
|
|
|
96
96
|
```yaml
|
|
97
|
-
- uses: driftless-dev/driftless@v0.2.
|
|
97
|
+
- uses: driftless-dev/driftless@v0.2.8
|
|
98
98
|
with:
|
|
99
99
|
command: scan
|
|
100
100
|
```
|
|
@@ -153,7 +153,7 @@ After a release, users can pin the composite Action by release tag
|
|
|
153
153
|
(`action.yml` lives at the repo root — no `/action` path segment):
|
|
154
154
|
|
|
155
155
|
```yaml
|
|
156
|
-
- uses: driftless-dev/driftless@v0.2.
|
|
156
|
+
- uses: driftless-dev/driftless@v0.2.8
|
|
157
157
|
with:
|
|
158
158
|
command: scan
|
|
159
159
|
```
|
|
@@ -161,9 +161,9 @@ After a release, users can pin the composite Action by release tag
|
|
|
161
161
|
Or pin the PyPI package in the Action input:
|
|
162
162
|
|
|
163
163
|
```yaml
|
|
164
|
-
- uses: driftless-dev/driftless@v0.2.
|
|
164
|
+
- uses: driftless-dev/driftless@v0.2.8
|
|
165
165
|
with:
|
|
166
|
-
version: "==0.2.
|
|
166
|
+
version: "==0.2.8"
|
|
167
167
|
command: migrate
|
|
168
168
|
```
|
|
169
169
|
|
|
@@ -171,7 +171,7 @@ Optionally maintain a floating **`v1`** tag on the latest stable minor release
|
|
|
171
171
|
(point it at the current release tag after each publish):
|
|
172
172
|
|
|
173
173
|
```bash
|
|
174
|
-
git tag -f v1 v0.2.
|
|
174
|
+
git tag -f v1 v0.2.8 && git push origin v1 --force
|
|
175
175
|
```
|
|
176
176
|
|
|
177
177
|
Update [`action.yml`](../action.yml) default `version` input when cutting releases.
|
|
@@ -428,7 +428,7 @@ driftless view -w support_classifier</code></pre>
|
|
|
428
428
|
<span class="tok-k">runs-on</span>: ubuntu-latest
|
|
429
429
|
<span class="tok-k">steps</span>:
|
|
430
430
|
- <span class="tok-k">uses</span>: actions/checkout@v4
|
|
431
|
-
- <span class="tok-k">uses</span>: driftless-dev/driftless@v0.2.
|
|
431
|
+
- <span class="tok-k">uses</span>: driftless-dev/driftless@v0.2.8
|
|
432
432
|
<span class="tok-k">with</span>:
|
|
433
433
|
<span class="tok-k">command</span>: <span class="tok-s">plan</span></code></pre>
|
|
434
434
|
<p>A scheduled <code class="inline">plan</code> gates CI when a deprecated model needs attention; a manually-triggered <code class="inline">migrate</code> opens a PR (or an issue when blocked) with the evidence attached.</p>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "tools"))
|
|
8
|
+
|
|
9
|
+
import fetch_provider_models as fpm # noqa: E402
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _catalog(models) -> Path:
|
|
13
|
+
import tempfile
|
|
14
|
+
|
|
15
|
+
p = Path(tempfile.mkdtemp()) / "cat.json"
|
|
16
|
+
p.write_text(json.dumps({"models": models}), encoding="utf-8")
|
|
17
|
+
return p
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_discover_new_models_skips_known_and_filters_openai(tmp_path):
|
|
21
|
+
cat = _catalog(
|
|
22
|
+
[
|
|
23
|
+
{"model": "gpt-4o", "provider": "openai"},
|
|
24
|
+
{"model": "claude-3-5-sonnet", "provider": "anthropic"},
|
|
25
|
+
]
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def fake_fetch(_key):
|
|
29
|
+
return [
|
|
30
|
+
"gpt-4o", # known
|
|
31
|
+
"gpt-5-mini", # new
|
|
32
|
+
"ft:gpt-4o:org:123", # fine-tune — skip
|
|
33
|
+
"tts-1", # infra — skip
|
|
34
|
+
"whisper-1",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
updates = fpm.discover_new_models(
|
|
38
|
+
provider="openai",
|
|
39
|
+
catalog_path=cat,
|
|
40
|
+
fetch_ids=fake_fetch,
|
|
41
|
+
keep=fpm._keep_openai,
|
|
42
|
+
api_key="k",
|
|
43
|
+
)
|
|
44
|
+
assert [u["model"] for u in updates] == ["gpt-5-mini"]
|
|
45
|
+
assert updates[0]["status"] == "active"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_discover_new_models_anthropic_claude_only(tmp_path):
|
|
49
|
+
cat = _catalog([{"model": "claude-3-5-sonnet", "provider": "anthropic"}])
|
|
50
|
+
|
|
51
|
+
updates = fpm.discover_new_models(
|
|
52
|
+
provider="anthropic",
|
|
53
|
+
catalog_path=cat,
|
|
54
|
+
fetch_ids=lambda _k: ["claude-3-5-sonnet", "claude-3-7-sonnet", "not-a-model"],
|
|
55
|
+
keep=fpm._keep_anthropic,
|
|
56
|
+
api_key="k",
|
|
57
|
+
)
|
|
58
|
+
assert [u["model"] for u in updates] == ["claude-3-7-sonnet"]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_fetch_updates_merges_providers_and_skips_missing_keys(tmp_path, monkeypatch):
|
|
62
|
+
cat = _catalog([{"model": "gpt-4o", "provider": "openai"}])
|
|
63
|
+
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
64
|
+
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
|
|
65
|
+
|
|
66
|
+
updates = fpm.fetch_updates(["openai", "anthropic"], catalog_path=cat)
|
|
67
|
+
assert updates == []
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_fetch_updates_openai(monkeypatch, tmp_path):
|
|
71
|
+
cat = _catalog([{"model": "gpt-4o", "provider": "openai"}])
|
|
72
|
+
monkeypatch.setenv("OPENAI_API_KEY", "sekret")
|
|
73
|
+
monkeypatch.setattr(
|
|
74
|
+
fpm,
|
|
75
|
+
"_openai_model_ids",
|
|
76
|
+
lambda key: (["gpt-4o", "o3-mini"] if key == "sekret" else []),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
updates = fpm.fetch_updates(["openai"], catalog_path=cat)
|
|
80
|
+
assert [u["model"] for u in updates] == ["o3-mini"]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_cli_writes_output(tmp_path, monkeypatch):
|
|
84
|
+
cat = tmp_path / "cat.json"
|
|
85
|
+
cat.write_text(json.dumps({"models": []}), encoding="utf-8")
|
|
86
|
+
out = tmp_path / "updates.json"
|
|
87
|
+
monkeypatch.setattr(
|
|
88
|
+
fpm,
|
|
89
|
+
"fetch_updates",
|
|
90
|
+
lambda providers, catalog_path: [
|
|
91
|
+
{"model": "gpt-5", "provider": "openai", "status": "active"}
|
|
92
|
+
],
|
|
93
|
+
)
|
|
94
|
+
assert fpm.main(["--provider", "openai", "--catalog", str(cat), "-o", str(out)]) == 0
|
|
95
|
+
data = json.loads(out.read_text(encoding="utf-8"))
|
|
96
|
+
assert data[0]["model"] == "gpt-5"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_http_get_json_raises_on_http_error(monkeypatch):
|
|
100
|
+
import urllib.error
|
|
101
|
+
|
|
102
|
+
class FakeHTTPError(urllib.error.HTTPError):
|
|
103
|
+
def __init__(self):
|
|
104
|
+
super().__init__(url="http://x", code=401, msg="nope", hdrs={}, fp=None)
|
|
105
|
+
|
|
106
|
+
def boom(*a, **k):
|
|
107
|
+
raise FakeHTTPError()
|
|
108
|
+
|
|
109
|
+
monkeypatch.setattr(fpm.urllib.request, "urlopen", boom)
|
|
110
|
+
with pytest.raises(RuntimeError, match="HTTP 401"):
|
|
111
|
+
fpm._http_get_json("http://x", {})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|