lastorders 0.4.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 (34) hide show
  1. lastorders-0.4.0/CHANGELOG.md +50 -0
  2. lastorders-0.4.0/MANIFEST.in +6 -0
  3. lastorders-0.4.0/PKG-INFO +212 -0
  4. lastorders-0.4.0/README.md +186 -0
  5. lastorders-0.4.0/lastorders.egg-info/PKG-INFO +212 -0
  6. lastorders-0.4.0/lastorders.egg-info/SOURCES.txt +32 -0
  7. lastorders-0.4.0/lastorders.egg-info/dependency_links.txt +1 -0
  8. lastorders-0.4.0/lastorders.egg-info/entry_points.txt +2 -0
  9. lastorders-0.4.0/lastorders.egg-info/requires.txt +8 -0
  10. lastorders-0.4.0/lastorders.egg-info/top_level.txt +1 -0
  11. lastorders-0.4.0/migration_kit/__init__.py +2 -0
  12. lastorders-0.4.0/migration_kit/__main__.py +3 -0
  13. lastorders-0.4.0/migration_kit/brand.py +27 -0
  14. lastorders-0.4.0/migration_kit/cli.py +365 -0
  15. lastorders-0.4.0/migration_kit/evaluator.py +526 -0
  16. lastorders-0.4.0/migration_kit/fixer/__init__.py +12 -0
  17. lastorders-0.4.0/migration_kit/fixer/callsite.py +65 -0
  18. lastorders-0.4.0/migration_kit/fixer/engine.py +463 -0
  19. lastorders-0.4.0/migration_kit/fixer/jsfix.py +373 -0
  20. lastorders-0.4.0/migration_kit/fixer/pyfix.py +260 -0
  21. lastorders-0.4.0/migration_kit/fixer/verify.py +292 -0
  22. lastorders-0.4.0/migration_kit/initevals.py +278 -0
  23. lastorders-0.4.0/migration_kit/licence.py +337 -0
  24. lastorders-0.4.0/migration_kit/pack_data/__init__.py +0 -0
  25. lastorders-0.4.0/migration_kit/pack_data/anthropic.json +271 -0
  26. lastorders-0.4.0/migration_kit/pack_data/openai.json +1230 -0
  27. lastorders-0.4.0/migration_kit/pack_data/pack.schema.json +615 -0
  28. lastorders-0.4.0/migration_kit/packs.py +299 -0
  29. lastorders-0.4.0/migration_kit/report.py +355 -0
  30. lastorders-0.4.0/migration_kit/scanner.py +398 -0
  31. lastorders-0.4.0/migration_kit/site.py +60 -0
  32. lastorders-0.4.0/migration_kit/swapper.py +127 -0
  33. lastorders-0.4.0/pyproject.toml +47 -0
  34. lastorders-0.4.0/setup.cfg +4 -0
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ ## 0.4.0 (unreleased)
4
+ - **Renamed to Last Orders.** Package, CLI and PyPI name `lastorders`; env vars `LASTORDERS_*` (was the placeholder
5
+ `modelmigrate` / `MODELMIGRATE_*`); output marker `.lastorders-output`; Action `burntandtoast/lastorders@v0`;
6
+ Worker `lastorders-licence`, D1 database `lastorders-licences`, Stripe metadata product `lastorders-repo`.
7
+ - **Licence server** (`server/`). This is a Cloudflare Worker with D1:
8
+ - Stripe Checkout at £149 per repo (configurable);
9
+ - a signed webhook that issues `BTM-XXXX-XXXX-XXXX` keys, stores them with the buyer's email, and emails them through Resend (the email provider is pluggable);
10
+ - the repo is bound on first activation;
11
+ - full refunds revoke the licence;
12
+ - a success page and admin rebind/revoke tools;
13
+ - it also serves the pack site.
14
+
15
+ Not deployed. See docs/LICENCE-SERVER.md.
16
+ - **Real licence check in the CLI**, replacing the format-only placeholder:
17
+ - an online verify sends only the key and repo;
18
+ - a 7-day Ed25519-signed token is cached locally;
19
+ - a 14-day grace period applies if the server is unreachable;
20
+ - `--offline` uses the cached token only;
21
+ - the free `scan` is unaffected.
22
+ - New `licence status|buy|forget` command and the `LASTORDERS_REPO` fallback. New dependency: `cryptography`.
23
+
24
+ ## 0.3.0 (unreleased)
25
+ - `fix`: automatic code fixes driven entirely by pack rules. It renames `max_tokens` per endpoint, adds
26
+ `reasoning_effort`/`reasoning.effort` "none" where the new model needs it, and flags legacy functions,
27
+ Assistants, legacy Completions, fine-tunes, fine-tuning jobs, LangChain, `**kwargs`/spread and unknown models
28
+ for a manual decision. Python uses libcst (lossless); JS/TS use a built-in lexer. Outputs `fix.diff`, `fix-plan.md` and `fix-plan.json`.
29
+ - `fix --verify`: applies the patch to a scratch copy, checks syntax and idempotence, and replays every fixed call
30
+ old vs new. With `--live` it also runs a control (unpatched arguments on the new model). `--evals` reruns the customer suite.
31
+ - `fix --apply` is gated behind the licence and refuses files with uncommitted changes unless `--force` is passed.
32
+ - Pack format: `signals[].call` (call-level detection) and structured `rules[].fix`
33
+ (`per_endpoint`, `requires_new_model`, `keep_model`). OpenAI pack 2026.10.15.4, Anthropic 2026.10.15.2.
34
+ - Live finding (15 Oct 2026): GPT-4o and GPT-4.1 reject `reasoning_effort`, so that fix must ship with the model switch.
35
+ - Client report: an "Automatic fixes" section with the verification results.
36
+
37
+ ## 0.2.0 (unreleased)
38
+ - Free `scan` packaged for `pip`/`pipx` (entry point then called `modelmigrate`, a placeholder name); no licence or API key needed.
39
+ - Provider-neutral **migration packs** (JSON + JSON Schema): OpenAI (full), Anthropic (stub). Fetched from a
40
+ hosted URL at runtime with sha256 check, 24h cache and bundled offline fallback.
41
+ - Scanner, swap and report are generic engines driven by pack data (model IDs, aliases, families, shutdown dates,
42
+ replacements, SDK/call-site signals, parameter/endpoint/tool-calling rules).
43
+ - GitHub Action (`action.yml`, composite): job summary, PR annotations, artifact upload, optional fail on high risk.
44
+ - Placeholder licence gate for `swap --apply`, `eval` and `report` (`LASTORDERS_LICENCE` / `--licence`). One licence = one GitHub repo (monorepos included).
45
+ - `init-evals`: generate a starter regression suite from the scan's call sites (prompts, schemas, tools read statically).
46
+ - Network policy: only the pack fetch (and, later, the licence check) contacts us; no telemetry. `--offline` / `LASTORDERS_OFFLINE=1`.
47
+ - `packs list|validate|build-site`; deployable static pack site for Vercel / Cloudflare Pages.
48
+
49
+ ## 0.1.0
50
+ - Internal migration kit: scan, swap, eval (mock + live), client report.
@@ -0,0 +1,6 @@
1
+ include README.md CHANGELOG.md
2
+ recursive-include migration_kit/pack_data *.json
3
+ prune examples
4
+ prune probes
5
+ prune site
6
+ prune tests
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.4
2
+ Name: lastorders
3
+ Version: 0.4.0
4
+ Summary: Free scanner for retiring AI model IDs (OpenAI, Anthropic, ...) in your code, with CI support.
5
+ Author: Burnt & Toast Studios
6
+ Project-URL: Homepage, https://burntandtoast.com
7
+ Keywords: openai,anthropic,llm,deprecation,migration,gpt-4o,scanner,github-action
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Software Development :: Quality Assurance
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: pyyaml>=6
20
+ Requires-Dist: jsonschema>=4
21
+ Requires-Dist: libcst>=1.1
22
+ Requires-Dist: cryptography>=41
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8; extra == "dev"
25
+ Requires-Dist: build>=1; extra == "dev"
26
+
27
+ # Last Orders (working name)
28
+
29
+ **Find retiring AI model IDs in your code before they break.** One command scans your repo for OpenAI
30
+ (and, as a preview, Anthropic) model IDs that are being shut down. For each one it tells you the
31
+ shutdown date, the replacement the provider recommends, and the code changes the new model needs.
32
+
33
+ > **Heads-up:** OpenAI removes **GPT-4o, GPT-4o mini, GPT-4.1 and GPT-4.1 mini** from the API on
34
+ > **14 January 2027** and recommends `gpt-6-luna`. In our tests, `gpt-6-luna` rejects `max_tokens`, and it
35
+ > rejects tool calls on Chat Completions unless `reasoning_effort` is `"none"`. So changing the model name
36
+ > isn't enough. The scan flags both.
37
+
38
+ The scan is free. You don't need an account, a licence or an API key, and your code never leaves your machine.
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ pipx install lastorders # or: pip install lastorders
44
+ ```
45
+
46
+ You need Python 3.10 or later.
47
+
48
+ ## Scan
49
+
50
+ ```bash
51
+ lastorders scan . # scan the current repo
52
+ lastorders scan path/to/repo --out scan-results
53
+ lastorders scan . --offline # no network at all (uses the bundled migration pack)
54
+ ```
55
+
56
+ You get `scan.md`, a readable report, and `scan.json`, the same data for tools. They go in `lastorders-out/`, or wherever `--out` points. Each finding includes:
57
+
58
+ - **File, line and model ID**, from code (Python, JS, TS), `.env` files, YAML, JSON, TOML and docs
59
+ - **Shutdown date and recommended replacement**, from the provider's published deprecations
60
+ - **Risk level**. High means the date is close or has passed, or the new model needs a code change your
61
+ code doesn't have yet, such as `max_tokens` becoming `max_completion_tokens`, `reasoning_effort` for tools
62
+ and `temperature`, or an unsupported endpoint
63
+ - **Fine-tuned models** (`ft:...`), listed separately. They shut down with their base model and can't be
64
+ swapped automatically.
65
+ - **Models chosen by environment variable** (`os.getenv("OPENAI_MODEL")`), so you can check the
66
+ values you have deployed
67
+ - **SDKs detected**: the official `openai` and `anthropic` clients, LangChain and plain HTTP
68
+
69
+ Useful flags:
70
+
71
+ | Flag | What it does |
72
+ |---|---|
73
+ | `--fail-on-high` | exit code 2 if anything is high risk (for CI) |
74
+ | `--no-docs` | skip `.md` / `.txt` files |
75
+ | `--pack openai` | only use some providers' packs (default: all) |
76
+ | `--offline` | use the bundled pack only; makes no network calls |
77
+ | `--format json` | print the summary as JSON |
78
+ | `--summary-file F` / `--annotations` | CI outputs (used by the GitHub Action) |
79
+
80
+ ## Fix (patch preview)
81
+
82
+ ```bash
83
+ lastorders fix . # writes lastorders-out/fix.diff + fix-plan.md; changes nothing on disk
84
+ lastorders fix . --verify # also checks the patch on a scratch copy (see below)
85
+ git apply lastorders-out/fix.diff
86
+ ```
87
+
88
+ `fix` swaps the retiring model IDs and then makes the code changes the new model needs at each API call:
89
+
90
+ - **Renames `max_tokens`**, to `max_completion_tokens` on Chat Completions or `max_output_tokens` on Responses.
91
+ - **Adds `reasoning_effort: "none"`** where gpt-6-luna needs it: Chat Completions calls with tools, and
92
+ calls that set `temperature`, `top_p` or `logprobs`. On Responses it adds `reasoning.effort`.
93
+ - **Flags anything it can't decide safely** in `fix-plan.md`: legacy `functions` calls, Assistants and legacy
94
+ Completions calls (these stay on their current model in the patch), fine-tunes and fine-tuning jobs,
95
+ LangChain wrappers, calls that pass `**kwargs` or a spread object, conflicting values, and calls whose model
96
+ isn't visible in the code (use `--assume-model`).
97
+
98
+ Every change comes from the migration pack's rules. Python is edited with
99
+ [libcst](https://github.com/Instagram/LibCST), so your formatting and comments are kept. JS and TS are edited
100
+ with a built-in parser. Rules the pack marks `inferred` are only suggested unless you pass `--include-inferred`.
101
+
102
+ **Old models reject `reasoning_effort`.** We tested gpt-4o and gpt-4.1 against the live API, and both return
103
+ HTTP 400. Any fix that adds it has to ship together with the model switch. When the model comes from an
104
+ environment variable, the plan marks the fix "ship with the model switch". `--no-model-swap` applies only
105
+ the changes that are safe on the old model.
106
+
107
+ `--verify` applies the patch to a temporary copy of your repo, then:
108
+
109
+ 1. checks the changed files still parse;
110
+ 2. re-runs the fixer and checks it changes nothing more;
111
+ 3. replays each fixed call twice: once as it was (old model, old arguments) and once as patched (new model,
112
+ new arguments).
113
+
114
+ Without `--live`, the replay uses simulated responses. With `--live` and your own `OPENAI_API_KEY` (licence),
115
+ the calls go straight to OpenAI. A control run also sends the unpatched arguments to the new model, to show
116
+ each fix was needed. `--evals evals/regression.yaml` reruns your own suite with the fixed arguments.
117
+
118
+ ## GitHub Action
119
+
120
+ Add `.github/workflows/model-scan.yml`:
121
+
122
+ ```yaml
123
+ name: Model retirement scan
124
+ on:
125
+ pull_request:
126
+ schedule: [{ cron: "0 7 * * 1" }] # weekly, so new deprecations show up without code changes
127
+ permissions:
128
+ contents: read
129
+ jobs:
130
+ scan:
131
+ runs-on: ubuntu-latest
132
+ steps:
133
+ - uses: actions/checkout@v4
134
+ - uses: burntandtoast/lastorders@v0 # available once the repo is published
135
+ with:
136
+ fail-on-high: "true"
137
+ ```
138
+
139
+ The Action posts a summary table to the job page, marks findings inline on the PR and uploads `scan.json`
140
+ as an artifact. Inputs:
141
+
142
+ | Input | Default | |
143
+ |---|---|---|
144
+ | `path` | `.` | folder to scan |
145
+ | `fail-on-high` | `false` | fail the job on high-risk findings |
146
+ | `include-docs` | `true` | scan `.md`/`.txt` |
147
+ | `annotations` | `true` | inline PR annotations |
148
+ | `packs` | all | e.g. `openai,anthropic` |
149
+ | `offline` | `false` | bundled pack only, no network |
150
+ | `packs-url` | built-in | override the pack URL |
151
+ | `version` | action source | install this version from PyPI instead |
152
+ | `python-version` | `3.12` | |
153
+ | `fix-preview` | `false` | also attach the automatic-fix patch + plan (simulated verify, no key) |
154
+ | `artifact-name` | `lastorders-scan` | |
155
+
156
+ Outputs: `total`, `high` and `report-dir`.
157
+
158
+ ## Migration packs: where the data comes from
159
+
160
+ What the scanner knows lives in **migration packs**, versioned JSON files with one pack per provider. Each pack
161
+ holds the model IDs and aliases, shutdown dates, replacements, call-site patterns, and parameter and endpoint
162
+ rules. Every fact links to the provider's documentation and the date we checked it. Rules we confirmed
163
+ against the live API are marked `live-tested`, and anything we assumed is marked `inferred`.
164
+
165
+ - **OpenAI**: full pack
166
+ - **Anthropic**: preview (stub). It covers Claude API retirement dates from Anthropic's deprecations page.
167
+ Amazon Bedrock and Google Cloud dates are not covered yet.
168
+ - Gemini, Azure OpenAI and Bedrock: planned
169
+
170
+ The tool checks for a newer pack once a day and falls back to the copy bundled with the package.
171
+ `lastorders packs list` shows which version and source you're using. The format is documented in
172
+ [docs/PACKS.md](docs/PACKS.md), with a JSON Schema in `migration_kit/pack_data/pack.schema.json`.
173
+
174
+ ## Privacy and network use
175
+
176
+ - The scan runs **entirely on your machine**. Your code and prompts are never uploaded.
177
+ - **The tool has no telemetry, analytics or tracking.** (Our website counts visits anonymously with Cloudflare Web Analytics: no cookies, no ads, no tracking across sites.)
178
+ - The tool only ever contacts us to **download the latest migration pack** (a plain GET of a static JSON file,
179
+ with no identifiers) and, for paid features, to **check a licence key**. That check sends only the key and the
180
+ repo name (`github.com/owner/repo`), and the signed answer is cached for a week, with a 14-day offline grace period after that.
181
+ - `--offline` (or `LASTORDERS_OFFLINE=1`) turns off all network access and uses the bundled pack.
182
+ - The paid `eval` command calls the AI provider directly **with your own API key**. Nothing goes through us.
183
+
184
+ ## Paid features
185
+
186
+ | | Free | Licence (per repo) | Done for you |
187
+ |---|---|---|---|
188
+ | `scan`, GitHub Action, patch preview (`fix` / `swap` write a `.diff`), `fix --verify` (simulated replay) | ✓ | ✓ | ✓ |
189
+ | `fix --apply` / `swap --apply`, `fix --verify --live`, `init-evals` + `eval` regression testing, client-ready `report` | | ✓ | ✓ |
190
+ | We do the migration, evals and handoff | | | ✓ |
191
+
192
+ One licence covers **one GitHub repository, monorepos included**. It's bound to the repo the first time
193
+ you use it. Buy one with `lastorders licence buy` (it prints a Stripe Checkout link for this repo), then pass the key
194
+ with `LASTORDERS_LICENCE` (a repository secret in Actions) or `--licence`. `lastorders licence status` shows
195
+ whether it's verified. Paid commands work `--offline` from the cached check. Repos without an `origin` remote
196
+ can set `LASTORDERS_REPO=github.com/owner/repo`. The licence server lives in `server/`; see
197
+ [docs/LICENCE-SERVER.md](docs/LICENCE-SERVER.md).
198
+
199
+ `init-evals` reads the call sites the scan found, including prompts, JSON schemas and tool definitions where
200
+ they can be read from the code, and writes a starter `evals/regression.yaml`. You then edit it into 10 to 20
201
+ real examples. `eval` runs them against the old and new model with your own key.
202
+
203
+ ## Limitations
204
+
205
+ - Detection uses pattern matching. Model names built at runtime or stored in a database won't be found;
206
+ env-var lookups are listed so you can check them.
207
+ - A pack is only as current as its `updated` date. Always check the provider's documentation before acting.
208
+ - The scan suggests changes; it doesn't decide them for you. Review every patch.
209
+
210
+ ---
211
+
212
+ Made by [Burnt & Toast Studios](https://burntandtoast.com). Not affiliated with OpenAI or Anthropic.
@@ -0,0 +1,186 @@
1
+ # Last Orders (working name)
2
+
3
+ **Find retiring AI model IDs in your code before they break.** One command scans your repo for OpenAI
4
+ (and, as a preview, Anthropic) model IDs that are being shut down. For each one it tells you the
5
+ shutdown date, the replacement the provider recommends, and the code changes the new model needs.
6
+
7
+ > **Heads-up:** OpenAI removes **GPT-4o, GPT-4o mini, GPT-4.1 and GPT-4.1 mini** from the API on
8
+ > **14 January 2027** and recommends `gpt-6-luna`. In our tests, `gpt-6-luna` rejects `max_tokens`, and it
9
+ > rejects tool calls on Chat Completions unless `reasoning_effort` is `"none"`. So changing the model name
10
+ > isn't enough. The scan flags both.
11
+
12
+ The scan is free. You don't need an account, a licence or an API key, and your code never leaves your machine.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pipx install lastorders # or: pip install lastorders
18
+ ```
19
+
20
+ You need Python 3.10 or later.
21
+
22
+ ## Scan
23
+
24
+ ```bash
25
+ lastorders scan . # scan the current repo
26
+ lastorders scan path/to/repo --out scan-results
27
+ lastorders scan . --offline # no network at all (uses the bundled migration pack)
28
+ ```
29
+
30
+ You get `scan.md`, a readable report, and `scan.json`, the same data for tools. They go in `lastorders-out/`, or wherever `--out` points. Each finding includes:
31
+
32
+ - **File, line and model ID**, from code (Python, JS, TS), `.env` files, YAML, JSON, TOML and docs
33
+ - **Shutdown date and recommended replacement**, from the provider's published deprecations
34
+ - **Risk level**. High means the date is close or has passed, or the new model needs a code change your
35
+ code doesn't have yet, such as `max_tokens` becoming `max_completion_tokens`, `reasoning_effort` for tools
36
+ and `temperature`, or an unsupported endpoint
37
+ - **Fine-tuned models** (`ft:...`), listed separately. They shut down with their base model and can't be
38
+ swapped automatically.
39
+ - **Models chosen by environment variable** (`os.getenv("OPENAI_MODEL")`), so you can check the
40
+ values you have deployed
41
+ - **SDKs detected**: the official `openai` and `anthropic` clients, LangChain and plain HTTP
42
+
43
+ Useful flags:
44
+
45
+ | Flag | What it does |
46
+ |---|---|
47
+ | `--fail-on-high` | exit code 2 if anything is high risk (for CI) |
48
+ | `--no-docs` | skip `.md` / `.txt` files |
49
+ | `--pack openai` | only use some providers' packs (default: all) |
50
+ | `--offline` | use the bundled pack only; makes no network calls |
51
+ | `--format json` | print the summary as JSON |
52
+ | `--summary-file F` / `--annotations` | CI outputs (used by the GitHub Action) |
53
+
54
+ ## Fix (patch preview)
55
+
56
+ ```bash
57
+ lastorders fix . # writes lastorders-out/fix.diff + fix-plan.md; changes nothing on disk
58
+ lastorders fix . --verify # also checks the patch on a scratch copy (see below)
59
+ git apply lastorders-out/fix.diff
60
+ ```
61
+
62
+ `fix` swaps the retiring model IDs and then makes the code changes the new model needs at each API call:
63
+
64
+ - **Renames `max_tokens`**, to `max_completion_tokens` on Chat Completions or `max_output_tokens` on Responses.
65
+ - **Adds `reasoning_effort: "none"`** where gpt-6-luna needs it: Chat Completions calls with tools, and
66
+ calls that set `temperature`, `top_p` or `logprobs`. On Responses it adds `reasoning.effort`.
67
+ - **Flags anything it can't decide safely** in `fix-plan.md`: legacy `functions` calls, Assistants and legacy
68
+ Completions calls (these stay on their current model in the patch), fine-tunes and fine-tuning jobs,
69
+ LangChain wrappers, calls that pass `**kwargs` or a spread object, conflicting values, and calls whose model
70
+ isn't visible in the code (use `--assume-model`).
71
+
72
+ Every change comes from the migration pack's rules. Python is edited with
73
+ [libcst](https://github.com/Instagram/LibCST), so your formatting and comments are kept. JS and TS are edited
74
+ with a built-in parser. Rules the pack marks `inferred` are only suggested unless you pass `--include-inferred`.
75
+
76
+ **Old models reject `reasoning_effort`.** We tested gpt-4o and gpt-4.1 against the live API, and both return
77
+ HTTP 400. Any fix that adds it has to ship together with the model switch. When the model comes from an
78
+ environment variable, the plan marks the fix "ship with the model switch". `--no-model-swap` applies only
79
+ the changes that are safe on the old model.
80
+
81
+ `--verify` applies the patch to a temporary copy of your repo, then:
82
+
83
+ 1. checks the changed files still parse;
84
+ 2. re-runs the fixer and checks it changes nothing more;
85
+ 3. replays each fixed call twice: once as it was (old model, old arguments) and once as patched (new model,
86
+ new arguments).
87
+
88
+ Without `--live`, the replay uses simulated responses. With `--live` and your own `OPENAI_API_KEY` (licence),
89
+ the calls go straight to OpenAI. A control run also sends the unpatched arguments to the new model, to show
90
+ each fix was needed. `--evals evals/regression.yaml` reruns your own suite with the fixed arguments.
91
+
92
+ ## GitHub Action
93
+
94
+ Add `.github/workflows/model-scan.yml`:
95
+
96
+ ```yaml
97
+ name: Model retirement scan
98
+ on:
99
+ pull_request:
100
+ schedule: [{ cron: "0 7 * * 1" }] # weekly, so new deprecations show up without code changes
101
+ permissions:
102
+ contents: read
103
+ jobs:
104
+ scan:
105
+ runs-on: ubuntu-latest
106
+ steps:
107
+ - uses: actions/checkout@v4
108
+ - uses: burntandtoast/lastorders@v0 # available once the repo is published
109
+ with:
110
+ fail-on-high: "true"
111
+ ```
112
+
113
+ The Action posts a summary table to the job page, marks findings inline on the PR and uploads `scan.json`
114
+ as an artifact. Inputs:
115
+
116
+ | Input | Default | |
117
+ |---|---|---|
118
+ | `path` | `.` | folder to scan |
119
+ | `fail-on-high` | `false` | fail the job on high-risk findings |
120
+ | `include-docs` | `true` | scan `.md`/`.txt` |
121
+ | `annotations` | `true` | inline PR annotations |
122
+ | `packs` | all | e.g. `openai,anthropic` |
123
+ | `offline` | `false` | bundled pack only, no network |
124
+ | `packs-url` | built-in | override the pack URL |
125
+ | `version` | action source | install this version from PyPI instead |
126
+ | `python-version` | `3.12` | |
127
+ | `fix-preview` | `false` | also attach the automatic-fix patch + plan (simulated verify, no key) |
128
+ | `artifact-name` | `lastorders-scan` | |
129
+
130
+ Outputs: `total`, `high` and `report-dir`.
131
+
132
+ ## Migration packs: where the data comes from
133
+
134
+ What the scanner knows lives in **migration packs**, versioned JSON files with one pack per provider. Each pack
135
+ holds the model IDs and aliases, shutdown dates, replacements, call-site patterns, and parameter and endpoint
136
+ rules. Every fact links to the provider's documentation and the date we checked it. Rules we confirmed
137
+ against the live API are marked `live-tested`, and anything we assumed is marked `inferred`.
138
+
139
+ - **OpenAI**: full pack
140
+ - **Anthropic**: preview (stub). It covers Claude API retirement dates from Anthropic's deprecations page.
141
+ Amazon Bedrock and Google Cloud dates are not covered yet.
142
+ - Gemini, Azure OpenAI and Bedrock: planned
143
+
144
+ The tool checks for a newer pack once a day and falls back to the copy bundled with the package.
145
+ `lastorders packs list` shows which version and source you're using. The format is documented in
146
+ [docs/PACKS.md](docs/PACKS.md), with a JSON Schema in `migration_kit/pack_data/pack.schema.json`.
147
+
148
+ ## Privacy and network use
149
+
150
+ - The scan runs **entirely on your machine**. Your code and prompts are never uploaded.
151
+ - **The tool has no telemetry, analytics or tracking.** (Our website counts visits anonymously with Cloudflare Web Analytics: no cookies, no ads, no tracking across sites.)
152
+ - The tool only ever contacts us to **download the latest migration pack** (a plain GET of a static JSON file,
153
+ with no identifiers) and, for paid features, to **check a licence key**. That check sends only the key and the
154
+ repo name (`github.com/owner/repo`), and the signed answer is cached for a week, with a 14-day offline grace period after that.
155
+ - `--offline` (or `LASTORDERS_OFFLINE=1`) turns off all network access and uses the bundled pack.
156
+ - The paid `eval` command calls the AI provider directly **with your own API key**. Nothing goes through us.
157
+
158
+ ## Paid features
159
+
160
+ | | Free | Licence (per repo) | Done for you |
161
+ |---|---|---|---|
162
+ | `scan`, GitHub Action, patch preview (`fix` / `swap` write a `.diff`), `fix --verify` (simulated replay) | ✓ | ✓ | ✓ |
163
+ | `fix --apply` / `swap --apply`, `fix --verify --live`, `init-evals` + `eval` regression testing, client-ready `report` | | ✓ | ✓ |
164
+ | We do the migration, evals and handoff | | | ✓ |
165
+
166
+ One licence covers **one GitHub repository, monorepos included**. It's bound to the repo the first time
167
+ you use it. Buy one with `lastorders licence buy` (it prints a Stripe Checkout link for this repo), then pass the key
168
+ with `LASTORDERS_LICENCE` (a repository secret in Actions) or `--licence`. `lastorders licence status` shows
169
+ whether it's verified. Paid commands work `--offline` from the cached check. Repos without an `origin` remote
170
+ can set `LASTORDERS_REPO=github.com/owner/repo`. The licence server lives in `server/`; see
171
+ [docs/LICENCE-SERVER.md](docs/LICENCE-SERVER.md).
172
+
173
+ `init-evals` reads the call sites the scan found, including prompts, JSON schemas and tool definitions where
174
+ they can be read from the code, and writes a starter `evals/regression.yaml`. You then edit it into 10 to 20
175
+ real examples. `eval` runs them against the old and new model with your own key.
176
+
177
+ ## Limitations
178
+
179
+ - Detection uses pattern matching. Model names built at runtime or stored in a database won't be found;
180
+ env-var lookups are listed so you can check them.
181
+ - A pack is only as current as its `updated` date. Always check the provider's documentation before acting.
182
+ - The scan suggests changes; it doesn't decide them for you. Review every patch.
183
+
184
+ ---
185
+
186
+ Made by [Burnt & Toast Studios](https://burntandtoast.com). Not affiliated with OpenAI or Anthropic.
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.4
2
+ Name: lastorders
3
+ Version: 0.4.0
4
+ Summary: Free scanner for retiring AI model IDs (OpenAI, Anthropic, ...) in your code, with CI support.
5
+ Author: Burnt & Toast Studios
6
+ Project-URL: Homepage, https://burntandtoast.com
7
+ Keywords: openai,anthropic,llm,deprecation,migration,gpt-4o,scanner,github-action
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Software Development :: Quality Assurance
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: pyyaml>=6
20
+ Requires-Dist: jsonschema>=4
21
+ Requires-Dist: libcst>=1.1
22
+ Requires-Dist: cryptography>=41
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8; extra == "dev"
25
+ Requires-Dist: build>=1; extra == "dev"
26
+
27
+ # Last Orders (working name)
28
+
29
+ **Find retiring AI model IDs in your code before they break.** One command scans your repo for OpenAI
30
+ (and, as a preview, Anthropic) model IDs that are being shut down. For each one it tells you the
31
+ shutdown date, the replacement the provider recommends, and the code changes the new model needs.
32
+
33
+ > **Heads-up:** OpenAI removes **GPT-4o, GPT-4o mini, GPT-4.1 and GPT-4.1 mini** from the API on
34
+ > **14 January 2027** and recommends `gpt-6-luna`. In our tests, `gpt-6-luna` rejects `max_tokens`, and it
35
+ > rejects tool calls on Chat Completions unless `reasoning_effort` is `"none"`. So changing the model name
36
+ > isn't enough. The scan flags both.
37
+
38
+ The scan is free. You don't need an account, a licence or an API key, and your code never leaves your machine.
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ pipx install lastorders # or: pip install lastorders
44
+ ```
45
+
46
+ You need Python 3.10 or later.
47
+
48
+ ## Scan
49
+
50
+ ```bash
51
+ lastorders scan . # scan the current repo
52
+ lastorders scan path/to/repo --out scan-results
53
+ lastorders scan . --offline # no network at all (uses the bundled migration pack)
54
+ ```
55
+
56
+ You get `scan.md`, a readable report, and `scan.json`, the same data for tools. They go in `lastorders-out/`, or wherever `--out` points. Each finding includes:
57
+
58
+ - **File, line and model ID**, from code (Python, JS, TS), `.env` files, YAML, JSON, TOML and docs
59
+ - **Shutdown date and recommended replacement**, from the provider's published deprecations
60
+ - **Risk level**. High means the date is close or has passed, or the new model needs a code change your
61
+ code doesn't have yet, such as `max_tokens` becoming `max_completion_tokens`, `reasoning_effort` for tools
62
+ and `temperature`, or an unsupported endpoint
63
+ - **Fine-tuned models** (`ft:...`), listed separately. They shut down with their base model and can't be
64
+ swapped automatically.
65
+ - **Models chosen by environment variable** (`os.getenv("OPENAI_MODEL")`), so you can check the
66
+ values you have deployed
67
+ - **SDKs detected**: the official `openai` and `anthropic` clients, LangChain and plain HTTP
68
+
69
+ Useful flags:
70
+
71
+ | Flag | What it does |
72
+ |---|---|
73
+ | `--fail-on-high` | exit code 2 if anything is high risk (for CI) |
74
+ | `--no-docs` | skip `.md` / `.txt` files |
75
+ | `--pack openai` | only use some providers' packs (default: all) |
76
+ | `--offline` | use the bundled pack only; makes no network calls |
77
+ | `--format json` | print the summary as JSON |
78
+ | `--summary-file F` / `--annotations` | CI outputs (used by the GitHub Action) |
79
+
80
+ ## Fix (patch preview)
81
+
82
+ ```bash
83
+ lastorders fix . # writes lastorders-out/fix.diff + fix-plan.md; changes nothing on disk
84
+ lastorders fix . --verify # also checks the patch on a scratch copy (see below)
85
+ git apply lastorders-out/fix.diff
86
+ ```
87
+
88
+ `fix` swaps the retiring model IDs and then makes the code changes the new model needs at each API call:
89
+
90
+ - **Renames `max_tokens`**, to `max_completion_tokens` on Chat Completions or `max_output_tokens` on Responses.
91
+ - **Adds `reasoning_effort: "none"`** where gpt-6-luna needs it: Chat Completions calls with tools, and
92
+ calls that set `temperature`, `top_p` or `logprobs`. On Responses it adds `reasoning.effort`.
93
+ - **Flags anything it can't decide safely** in `fix-plan.md`: legacy `functions` calls, Assistants and legacy
94
+ Completions calls (these stay on their current model in the patch), fine-tunes and fine-tuning jobs,
95
+ LangChain wrappers, calls that pass `**kwargs` or a spread object, conflicting values, and calls whose model
96
+ isn't visible in the code (use `--assume-model`).
97
+
98
+ Every change comes from the migration pack's rules. Python is edited with
99
+ [libcst](https://github.com/Instagram/LibCST), so your formatting and comments are kept. JS and TS are edited
100
+ with a built-in parser. Rules the pack marks `inferred` are only suggested unless you pass `--include-inferred`.
101
+
102
+ **Old models reject `reasoning_effort`.** We tested gpt-4o and gpt-4.1 against the live API, and both return
103
+ HTTP 400. Any fix that adds it has to ship together with the model switch. When the model comes from an
104
+ environment variable, the plan marks the fix "ship with the model switch". `--no-model-swap` applies only
105
+ the changes that are safe on the old model.
106
+
107
+ `--verify` applies the patch to a temporary copy of your repo, then:
108
+
109
+ 1. checks the changed files still parse;
110
+ 2. re-runs the fixer and checks it changes nothing more;
111
+ 3. replays each fixed call twice: once as it was (old model, old arguments) and once as patched (new model,
112
+ new arguments).
113
+
114
+ Without `--live`, the replay uses simulated responses. With `--live` and your own `OPENAI_API_KEY` (licence),
115
+ the calls go straight to OpenAI. A control run also sends the unpatched arguments to the new model, to show
116
+ each fix was needed. `--evals evals/regression.yaml` reruns your own suite with the fixed arguments.
117
+
118
+ ## GitHub Action
119
+
120
+ Add `.github/workflows/model-scan.yml`:
121
+
122
+ ```yaml
123
+ name: Model retirement scan
124
+ on:
125
+ pull_request:
126
+ schedule: [{ cron: "0 7 * * 1" }] # weekly, so new deprecations show up without code changes
127
+ permissions:
128
+ contents: read
129
+ jobs:
130
+ scan:
131
+ runs-on: ubuntu-latest
132
+ steps:
133
+ - uses: actions/checkout@v4
134
+ - uses: burntandtoast/lastorders@v0 # available once the repo is published
135
+ with:
136
+ fail-on-high: "true"
137
+ ```
138
+
139
+ The Action posts a summary table to the job page, marks findings inline on the PR and uploads `scan.json`
140
+ as an artifact. Inputs:
141
+
142
+ | Input | Default | |
143
+ |---|---|---|
144
+ | `path` | `.` | folder to scan |
145
+ | `fail-on-high` | `false` | fail the job on high-risk findings |
146
+ | `include-docs` | `true` | scan `.md`/`.txt` |
147
+ | `annotations` | `true` | inline PR annotations |
148
+ | `packs` | all | e.g. `openai,anthropic` |
149
+ | `offline` | `false` | bundled pack only, no network |
150
+ | `packs-url` | built-in | override the pack URL |
151
+ | `version` | action source | install this version from PyPI instead |
152
+ | `python-version` | `3.12` | |
153
+ | `fix-preview` | `false` | also attach the automatic-fix patch + plan (simulated verify, no key) |
154
+ | `artifact-name` | `lastorders-scan` | |
155
+
156
+ Outputs: `total`, `high` and `report-dir`.
157
+
158
+ ## Migration packs: where the data comes from
159
+
160
+ What the scanner knows lives in **migration packs**, versioned JSON files with one pack per provider. Each pack
161
+ holds the model IDs and aliases, shutdown dates, replacements, call-site patterns, and parameter and endpoint
162
+ rules. Every fact links to the provider's documentation and the date we checked it. Rules we confirmed
163
+ against the live API are marked `live-tested`, and anything we assumed is marked `inferred`.
164
+
165
+ - **OpenAI**: full pack
166
+ - **Anthropic**: preview (stub). It covers Claude API retirement dates from Anthropic's deprecations page.
167
+ Amazon Bedrock and Google Cloud dates are not covered yet.
168
+ - Gemini, Azure OpenAI and Bedrock: planned
169
+
170
+ The tool checks for a newer pack once a day and falls back to the copy bundled with the package.
171
+ `lastorders packs list` shows which version and source you're using. The format is documented in
172
+ [docs/PACKS.md](docs/PACKS.md), with a JSON Schema in `migration_kit/pack_data/pack.schema.json`.
173
+
174
+ ## Privacy and network use
175
+
176
+ - The scan runs **entirely on your machine**. Your code and prompts are never uploaded.
177
+ - **The tool has no telemetry, analytics or tracking.** (Our website counts visits anonymously with Cloudflare Web Analytics: no cookies, no ads, no tracking across sites.)
178
+ - The tool only ever contacts us to **download the latest migration pack** (a plain GET of a static JSON file,
179
+ with no identifiers) and, for paid features, to **check a licence key**. That check sends only the key and the
180
+ repo name (`github.com/owner/repo`), and the signed answer is cached for a week, with a 14-day offline grace period after that.
181
+ - `--offline` (or `LASTORDERS_OFFLINE=1`) turns off all network access and uses the bundled pack.
182
+ - The paid `eval` command calls the AI provider directly **with your own API key**. Nothing goes through us.
183
+
184
+ ## Paid features
185
+
186
+ | | Free | Licence (per repo) | Done for you |
187
+ |---|---|---|---|
188
+ | `scan`, GitHub Action, patch preview (`fix` / `swap` write a `.diff`), `fix --verify` (simulated replay) | ✓ | ✓ | ✓ |
189
+ | `fix --apply` / `swap --apply`, `fix --verify --live`, `init-evals` + `eval` regression testing, client-ready `report` | | ✓ | ✓ |
190
+ | We do the migration, evals and handoff | | | ✓ |
191
+
192
+ One licence covers **one GitHub repository, monorepos included**. It's bound to the repo the first time
193
+ you use it. Buy one with `lastorders licence buy` (it prints a Stripe Checkout link for this repo), then pass the key
194
+ with `LASTORDERS_LICENCE` (a repository secret in Actions) or `--licence`. `lastorders licence status` shows
195
+ whether it's verified. Paid commands work `--offline` from the cached check. Repos without an `origin` remote
196
+ can set `LASTORDERS_REPO=github.com/owner/repo`. The licence server lives in `server/`; see
197
+ [docs/LICENCE-SERVER.md](docs/LICENCE-SERVER.md).
198
+
199
+ `init-evals` reads the call sites the scan found, including prompts, JSON schemas and tool definitions where
200
+ they can be read from the code, and writes a starter `evals/regression.yaml`. You then edit it into 10 to 20
201
+ real examples. `eval` runs them against the old and new model with your own key.
202
+
203
+ ## Limitations
204
+
205
+ - Detection uses pattern matching. Model names built at runtime or stored in a database won't be found;
206
+ env-var lookups are listed so you can check them.
207
+ - A pack is only as current as its `updated` date. Always check the provider's documentation before acting.
208
+ - The scan suggests changes; it doesn't decide them for you. Review every patch.
209
+
210
+ ---
211
+
212
+ Made by [Burnt & Toast Studios](https://burntandtoast.com). Not affiliated with OpenAI or Anthropic.