unrent 0.2.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 (37) hide show
  1. unrent-0.2.0/.gitignore +8 -0
  2. unrent-0.2.0/CHANGELOG.md +125 -0
  3. unrent-0.2.0/LICENSE +202 -0
  4. unrent-0.2.0/PKG-INFO +226 -0
  5. unrent-0.2.0/README.md +196 -0
  6. unrent-0.2.0/catalog/alternatives.yaml +857 -0
  7. unrent-0.2.0/catalog/rankings.json +1724 -0
  8. unrent-0.2.0/catalog/retirements.yaml +222 -0
  9. unrent-0.2.0/catalog/services/agents.yaml +646 -0
  10. unrent-0.2.0/catalog/services/llm.yaml +1212 -0
  11. unrent-0.2.0/catalog/services/media.yaml +473 -0
  12. unrent-0.2.0/catalog/services/models-dev.yaml +2040 -0
  13. unrent-0.2.0/catalog/services/retrieval.yaml +549 -0
  14. unrent-0.2.0/catalog/star-history.json +1156 -0
  15. unrent-0.2.0/pyproject.toml +78 -0
  16. unrent-0.2.0/scripts/mcp_smoke.py +41 -0
  17. unrent-0.2.0/scripts/new_services.py +316 -0
  18. unrent-0.2.0/scripts/refresh.py +432 -0
  19. unrent-0.2.0/scripts/retirements.py +278 -0
  20. unrent-0.2.0/scripts/verify_packages.py +394 -0
  21. unrent-0.2.0/skills/unrent/SKILL.md +162 -0
  22. unrent-0.2.0/skills/unrent/scripts/repo_facts.py +177 -0
  23. unrent-0.2.0/skills/unrent/scripts/sweep.py +227 -0
  24. unrent-0.2.0/tests/test_discover.py +113 -0
  25. unrent-0.2.0/tests/test_mcp.py +402 -0
  26. unrent-0.2.0/tests/test_retirements_script.py +106 -0
  27. unrent-0.2.0/tests/test_unrent.py +2538 -0
  28. unrent-0.2.0/unrent/__init__.py +1 -0
  29. unrent-0.2.0/unrent/catalog.py +512 -0
  30. unrent-0.2.0/unrent/cli.py +289 -0
  31. unrent-0.2.0/unrent/detect.py +2070 -0
  32. unrent-0.2.0/unrent/discover.py +367 -0
  33. unrent-0.2.0/unrent/fresh.py +135 -0
  34. unrent-0.2.0/unrent/render.py +551 -0
  35. unrent-0.2.0/unrent/retired.py +150 -0
  36. unrent-0.2.0/unrent/server.py +353 -0
  37. unrent-0.2.0/unrent/terminal.py +430 -0
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ dist/
7
+ build/
8
+ *.egg-info/
@@ -0,0 +1,125 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
+ [Semantic Versioning](https://semver.org/). Weekly ranking refreshes are not listed;
6
+ each release ships the rankings as of its date.
7
+
8
+ ## [0.2.0] - 2026-10-04
9
+
10
+ First public release.
11
+
12
+ ### Added
13
+ - Snapped models: model ids the code selects that the vendor has retired (`snapped`) or
14
+ will retire on an announced date (`snaps`), with the vendor's replacement, followed
15
+ while that one retires too. 204 retirements from OpenAI, Anthropic and Google in
16
+ `catalog/retirements.yaml`, checked weekly against their pages by
17
+ `scripts/retirements.py`, which opens a pull request. In the terminal view, the
18
+ Markdown report (`Models that stop working`), JSON and MCP (`models_retiring`).
19
+ `--why <model id>` and `--as-of YYYY-MM-DD`. A model menu, a price table, a check on
20
+ the user's choice, sample data and Bedrock or Vertex spellings are not counted as
21
+ selecting. A third eval side, `--side all`, scores it against hand-labelled truth
22
+ (`eval/corpus_models.yaml`, `eval/MODEL_TRUTH_RULES.md`): precision 0.939, recall 0.886.
23
+ - A terminal view, the default when the output is a terminal: one row per service,
24
+ `cut` (an open source replacement exists), `held` (none yet) or `runs` (open source in
25
+ use, with its rank), with its strongest location and a count of the rest. Columns stack
26
+ on narrow terminals; colour follows `NO_COLOR` and `FORCE_COLOR`. A pipe or `-o` still
27
+ gets the Markdown report. `--format terminal|markdown|json|auto` and `-f`.
28
+ - `--why SERVICE`: every location behind one service or open source component, and its
29
+ ranked alternatives or standing.
30
+ - `unrent <path>` as short for `unrent scan <path>`, and a status line on stderr while
31
+ a terminal scan runs.
32
+ - `unknown_candidates` in every scan (JSON, Markdown and MCP): API hosts and keys that
33
+ no catalog entry explains and that look like a hosted AI API, grouped by domain with
34
+ their key (`TYPESAFE_API_KEY` with `api.typesafe.ai`). A generated provider catalog
35
+ is one candidate. Never counted as a finding.
36
+ - 139 hosted model providers from [models.dev](https://models.dev), in the generated
37
+ `catalog/services/models-dev.yaml`; a weekly job regenerates it, runs the golden
38
+ corpus and opens a pull request. `scripts/new_services.py` also ranks the
39
+ `unknown_candidates` of the eval corpus in the eval's job summary.
40
+ - 15 hand-written closed services (357 in all): TypeSafe AI (Jev), GitHub Copilot API,
41
+ Ollama Cloud, Ask Sage, Hicap, Nous Research, Huawei ModelArts MaaS, SAP AI Core,
42
+ Oracle Code Assist, BytePlus InfoQuest, Sofya, Tencent Cloud WSA, Unbrowse, Tenki and
43
+ Volcengine Speech. New endpoints and keys for Moonshot (Kimi For Coding), Gitee AI (MoArk),
44
+ Tencent (TokenHub), watsonx, Cloudflare AI Gateway and OCI Generative AI.
45
+ - An agent skill, `skills/unrent`, with `sweep.py` and `repo_facts.py`.
46
+ - `unrent mcp`: an MCP server with `scan`, `alternatives`, `standing` and `catalog`
47
+ tools (install the `mcp` extra). It reads the latest rankings from the repository,
48
+ cached for six hours, and falls back to the shipped snapshot, saying why.
49
+ `UNRENT_OFFLINE=1` keeps it offline.
50
+ - 19 closed services (182 in all): GitHub Models, SageMaker, Qianfan, SiliconFlow,
51
+ Hunyuan, Spark, Baichuan, StepFun, Novita, Clarifai, Aleph Alpha, Reka, GigaChat and
52
+ six managed vector databases.
53
+ - Open source signatures for Java (Spring AI, LangChain4j), .NET, Go, LangChain and
54
+ LlamaIndex integration packages, env vars and more images; four new projects (89).
55
+ - Pools: RAG split into applications and frameworks; new LLM evaluation and answer
56
+ engine pools. Model rows show parameter counts.
57
+ - Package extras named after a package (`qdrant-client[fastembed]`), compose
58
+ `${VAR:-default}` images and `CREATE EXTENSION` statements.
59
+ - The eval corpus labels open source components too (54 repositories), checked in CI.
60
+ - 21 more closed services (203 in all), found by agents in anything-llm and
61
+ gpt-researcher: Gitee AI, PPIO, APIpie, CometAPI, Privatemode, Atlas Cloud, AI/ML API,
62
+ TensorBlock Forge, Avian, NetMind, ModelsLab, Chroma Cloud, the Nomic API, Okahu, and
63
+ the search APIs SearchApi.io, Serply, fastCRW, Keenable, AnySearch, Bocha and
64
+ GroundRoute. The Nomic API is not reported where the code asks for local inference.
65
+ - `open_core` on open source projects whose repo keeps part of the code under another
66
+ licence (LiteLLM, Langfuse, Agenta, Onyx, Meilisearch, Weaviate), shown next to the
67
+ licence.
68
+ - `env_template_only`: a key that appears only in `.env.example` or similar, with
69
+ nothing in the code behind it, is listed apart and not counted as a dependency.
70
+
71
+ - `unrent scan <dir>`: finds closed AI services (about 160: LLM APIs, gateways,
72
+ embeddings, vector databases, RAG, OCR, observability, speech, image generation,
73
+ search, browser automation, sandboxes, agent memory) from packages, imports,
74
+ install commands, framework integrations, API hosts, model ids, environment
75
+ variables and Terraform resources.
76
+ - Markdown and JSON reports, with file, line and masked text for every finding.
77
+ - Open source alternatives per service, ranked weekly by GitHub star momentum and
78
+ Hugging Face trending (`scripts/refresh.py`).
79
+ - `--exclude`, `.unrentignore`, `--skip-tests`, `--top`, `--output`.
80
+ - `unrent catalog` and `unrent catalog --validate`.
81
+ - Optional ripgrep acceleration with identical results.
82
+ - Closed model ids with no SDK, key, host or package behind them are listed
83
+ separately ("closed models named in code") instead of counted as dependencies.
84
+ - "Open source you already run": 88 open source AI components (vector databases,
85
+ inference servers, gateways, document parsers, observability, ...) are recognised
86
+ from packages, imports, SQL and container images (`image:`, Helm values, `FROM`),
87
+ and each is placed in its pool, overall and among projects of the same kind.
88
+ - A golden corpus of hand-labelled repositories (`eval/`), run in CI.
89
+
90
+ ### Changed
91
+ - Faster scans of large repos, with identical results on all 54 corpus repos: from 1,500
92
+ files on, worker processes read the files (one per core, falling back to one process
93
+ where they can't start), each Python file is parsed once and only its statements are
94
+ walked, and ripgrep gets larger batches outside Windows. dify (14,000 files) went
95
+ from 59 s to 14 s, LibreChat (5,500) from 14 s to 6 s, the whole corpus from 193 s
96
+ to 112 s.
97
+ - The MCP instructions state the limits: only catalog services are detected, and each
98
+ pool's `ranked_by` says whether it is ranked by 90-day star gain or total stars.
99
+ - `unrent/report.py` is now `unrent/render.py`.
100
+
101
+ ### Fixed
102
+ - Odd files no longer end a scan: invalid YAML dates, deep JSON or TOML, a `.py` file
103
+ that overflows the parser, or a manifest of the wrong shape (`dependencies = 3`) gives
104
+ no facts, and the rest of the repo is still reported. A malformed `--catalog` is a
105
+ catalog error, not a traceback.
106
+ - A TOML dependency is cited at its own line (`llama-index = "0.9.7"`), not at a
107
+ `keywords` entry higher up that names the same package.
108
+ - ripgrep only reads the files the scan covers: a repo with gigabytes of ignored data
109
+ went from 99 s to about 1 s.
110
+ - Notebooks: source lists joined correctly, non-Python kernels read with their own
111
+ comment syntax.
112
+ - Old Mac (CR-only) files, symlinks, non-UTF-8 paths, saved unrent reports and OpenAPI
113
+ specs no longer produce wrong lines or findings.
114
+ - A repo is never listed as a component of itself.
115
+ - Descriptions containing commas were cut short in the catalog; the loader now rejects
116
+ them unquoted and names the file on any catalog error.
117
+ - `-o` is checked before the scan and excluded from it; `--top` must be positive.
118
+ - Evidence is ordered strongest first; code spans survive backticks.
119
+ - LangChain.js `VoyageEmbeddings` and `VOYAGEAI_API_KEY` are Voyage AI.
120
+ - A vendor prefix needs a model name after it: `github://` URIs and `/^snowflake/i`
121
+ regexes no longer name GitHub Models or Snowflake Cortex.
122
+ - Open source signatures: `SEARX_URL` and `SearxSearch` (SearXNG), vLLM behind an
123
+ OpenAI-compatible base URL, a self-hosted Firecrawl URL, LangChain's `FAISS`.
124
+
125
+ [0.2.0]: https://github.com/stringcutter/unrent/releases/tag/v0.2.0
unrent-0.2.0/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
202
+
unrent-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.5
2
+ Name: unrent
3
+ Version: 0.2.0
4
+ Summary: Find the strings your AI code hangs by: closed AI services to swap for open source, and models their vendor retires.
5
+ Project-URL: Homepage, https://github.com/stringcutter/unrent
6
+ Project-URL: Source, https://github.com/stringcutter/unrent
7
+ Project-URL: Issues, https://github.com/stringcutter/unrent/issues
8
+ Project-URL: Changelog, https://github.com/stringcutter/unrent/blob/main/CHANGELOG.md
9
+ Author: stringcutter
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ Keywords: ai,alternatives,cli,dependency-scanner,llm,mcp,model-deprecation,open-source,openai,static-analysis,vendor-lock-in
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Classifier: Topic :: Software Development :: Quality Assurance
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: pyyaml>=6.0
27
+ Provides-Extra: mcp
28
+ Requires-Dist: mcp<3,>=2.2; extra == 'mcp'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # unrent
32
+
33
+ *by [stringcutter](https://github.com/stringcutter)*
34
+
35
+ Find the strings your AI code hangs by. Cut them before they snap.
36
+
37
+ - **Closed AI services** the code calls, with file and line, and the open source that
38
+ replaces each one, ranked.
39
+ - **Models that stop working**: model ids the code selects that their vendor has retired,
40
+ or will on an announced date, and the vendor's replacement.
41
+ - **Open source AI** the code already runs, and where it ranks in its field.
42
+
43
+ The CLI runs offline. No account. No upload. No telemetry.
44
+
45
+ ![unrent my-app: 3 strings attached, 3 can be cut, 1 will snap. Rows for OpenAI API, Pinecone and ElevenLabs marked cut with their open source replacements, gpt-4-turbo marked snaps with its retirement date and replacement, and faiss marked runs with its rank](https://github.com/stringcutter/unrent/raw/main/docs/terminal.svg)
46
+
47
+ Real output. One row per string, with its strongest location and how many more there
48
+ are:
49
+
50
+ | | |
51
+ |---|---|
52
+ | `╎ cut` | A closed service with an open source replacement. The top of its ranking is on the row. |
53
+ | `│ held` | A closed service with no open source replacement in the catalog yet. |
54
+ | `┆ snapped` | A model id the code selects that its vendor has retired. Requests to it fail now. |
55
+ | `┆ snaps` | The same, on the date shown. |
56
+ | `│ runs` | Open source already in use, and where it ranks among its kind. |
57
+
58
+ `--why` shows every line behind one row, and what replaces it:
59
+
60
+ ![unrent my-app --why gpt-4-turbo: main.py line 9 selects gpt-4-turbo, which retires on 2026-10-23; openai recommends gpt-5.6-sol, with the link to OpenAI's deprecations page](https://github.com/stringcutter/unrent/raw/main/docs/why.svg)
61
+
62
+ In a pipe or a file the same scan is a Markdown report that pastes straight into an
63
+ issue or PR.
64
+
65
+ ## Install
66
+
67
+ ```bash
68
+ uv tool install unrent # or: pipx install unrent
69
+ unrent .
70
+ ```
71
+
72
+ Or once, without installing:
73
+
74
+ ```bash
75
+ uvx unrent .
76
+ ```
77
+
78
+ Python 3.11+. One dependency: PyYAML (the MCP server adds `mcp`). Large repos are read
79
+ on every core. With [ripgrep](https://github.com/BurntSushi/ripgrep) on PATH it is about
80
+ twice as fast again: 6 s for 5,500 files, 14 s for 14,000 (13 s and 26 s without, on 4
81
+ cores). Same results either way.
82
+
83
+ ## Use
84
+
85
+ ```bash
86
+ unrent . # the terminal view; Markdown in a pipe or file
87
+ unrent . --why openai # every location of one service, and what replaces it
88
+ unrent . --why gpt-4-turbo # every line that selects a retiring model
89
+ unrent . --as-of 2026-12-01 # judge retirements as of another day
90
+ unrent . -o report.md # the Markdown report
91
+ unrent . --format json -o r.json # everything, machine-readable
92
+ unrent . --top 5 # more alternatives per kind
93
+ unrent . --skip-tests # ignore test, spec and fixture code
94
+ unrent . --exclude "examples/" # .gitignore syntax; or a .unrentignore file
95
+ unrent catalog # what it knows
96
+ ```
97
+
98
+ `unrent .` is short for `unrent scan .`. Colour follows `NO_COLOR` and `FORCE_COLOR`.
99
+
100
+ On Windows PowerShell, write reports with `-o`, not `>`. The redirect re-encodes the
101
+ file.
102
+
103
+ ## MCP
104
+
105
+ unrent as tools for Claude Code, Cursor, Copilot, or any agent that speaks MCP. The agent
106
+ gets the facts; you decide what to swap.
107
+
108
+ ```bash
109
+ claude mcp add unrent -- uvx --from "unrent[mcp]" unrent mcp
110
+ ```
111
+
112
+ Other clients:
113
+
114
+ ```json
115
+ {
116
+ "mcpServers": {
117
+ "unrent": {
118
+ "command": "uvx",
119
+ "args": ["--from", "unrent[mcp]", "unrent", "mcp"]
120
+ }
121
+ }
122
+ }
123
+ ```
124
+
125
+ | Tool | |
126
+ |---|---|
127
+ | `scan` | Closed services, models that stop working, open source in use, alternatives. The report as JSON. |
128
+ | `alternatives` | Best open source for a category or a closed service: `"pinecone"`, `"speech-to-text"`. |
129
+ | `standing` | Where one project ranks, overall and among its kind: `"qdrant/qdrant"`. |
130
+ | `catalog` | What unrent recognises, and the signatures it looks for. |
131
+
132
+ Rankings come from this repo's `main`, refreshed weekly, not from the install. Cached
133
+ for six hours. When the fetch fails it uses the shipped snapshot and says so.
134
+ `UNRENT_OFFLINE=1` never fetches. Only rankings come in. Your code never goes out.
135
+
136
+ For agents that use skills, `npx skills add stringcutter/unrent` adds a workflow on top:
137
+ check what unrent can't see, sweep for services outside the catalog, and write the report.
138
+
139
+ ## What it detects
140
+
141
+ **357 closed AI services** in 21 categories: LLM APIs, LLM gateways, embeddings, vector
142
+ databases, RAG platforms, document parsing, guardrails, classification and scoring, LLM
143
+ observability, model hosting, fine-tuning, speech-to-text, text-to-speech, voice agents,
144
+ image generation, web search, web scraping, browser automation, code sandboxes, agent
145
+ platforms, agent memory. 218 are written by hand; 139 hosted model providers come from
146
+ [models.dev](https://models.dev) and are regenerated every week
147
+ ([`models-dev.yaml`](https://github.com/stringcutter/unrent/blob/main/catalog/services/models-dev.yaml)).
148
+
149
+ **Models that stop working.** 204 model retirements announced by OpenAI, Anthropic and
150
+ Google for their own APIs, with the date and the vendor's replacement, checked every
151
+ week against their deprecation pages
152
+ ([`retirements.yaml`](https://github.com/stringcutter/unrent/blob/main/catalog/retirements.yaml))
153
+ and shipped with each release.
154
+ A model id counts when a line selects it: a default, a config value, a model passed to
155
+ a call. The same id in a model menu, a price table or a check on what the user picked
156
+ is only counted. Azure OpenAI, Bedrock and Vertex keep their own schedules and are not
157
+ covered. On the golden corpus: precision 0.939, recall 0.886.
158
+
159
+ **Services it does not know yet.** Every scan also lists `unknown_candidates`: API hosts
160
+ and keys that no catalog entry explains and that look like a hosted AI API (a `/v1/...`
161
+ path, an `api.` or `.ai` host, a matching `*_API_KEY`). They are candidates to check, not
162
+ findings, and never counted.
163
+
164
+ **89 open source projects**: vector databases, inference servers, gateways, RAG
165
+ frameworks and applications, document parsers, observability, evals, speech, scraping,
166
+ agent memory.
167
+
168
+ Evidence it reads:
169
+
170
+ | | |
171
+ |---|---|
172
+ | Packages | Python (requirements, pyproject, setup.py/cfg, Pipfile, conda, extras like `qdrant-client[fastembed]`), npm and pnpm catalogs, Go, Cargo, Maven, Gradle, NuGet, RubyGems, Composer, pub, SwiftPM |
173
+ | Imports | Python (AST, notebooks), JavaScript/TypeScript (`import`, `require`, `import()`, `npm:`, `jsr:`) |
174
+ | Install commands | `pip install`, `uv add`, `npm i`, … in Dockerfiles, shell, CI, notebook cells |
175
+ | Container images | `image:` in compose and Kubernetes, `${VAR:-default}`, Helm values, Dockerfile `FROM` |
176
+ | Code and config | API hosts, model ids, env vars, SDK symbols, `CREATE EXTENSION`, Terraform |
177
+
178
+ Every package name in the catalog exists in its registry. Checked weekly.
179
+
180
+ ## What it ignores
181
+
182
+ - **Local servers behind a compatible SDK.** `OpenAI(base_url="http://localhost:11434/v1")` is Ollama, not OpenAI.
183
+ - **Lockfiles.** Your dependencies' dependencies aren't yours.
184
+ - **Comments and docs.** Per-language comment syntax. `"/api/*"` in a string is not a comment.
185
+ - **Ignored and vendored files.** `.gitignore` with or without git. Submodules scanned. `node_modules` not.
186
+ - **Generic names.** `task="transcribe"` isn't Amazon. `import textract` isn't AWS. Your `perplexity.py` isn't Perplexity.
187
+ - **Open-weight models.** `gpt-oss`, `deepseek-v3.2` and Ollama tags aren't closed.
188
+
189
+ Also:
190
+
191
+ - **Secrets are masked.** `OPENAI_API_KEY=****`.
192
+ - **Test-only findings are marked.** `--skip-tests` drops them.
193
+ - **Model names alone don't count.** A model id with no SDK, key, host or package behind it is listed apart, as *closed models named in code*.
194
+ - **Vendor through vendor.** Azure via `openai`, Claude on Bedrock or Vertex via `anthropic`: the report names the one you actually call.
195
+ - **Oversized files are listed, never skipped silently.**
196
+
197
+ Every rule above has a test that fails without it.
198
+
199
+ ## Ranking
200
+
201
+ Pools live in [`catalog/alternatives.yaml`](https://github.com/stringcutter/unrent/blob/main/catalog/alternatives.yaml). Re-ranked every Monday by a
202
+ GitHub Action that merges itself.
203
+
204
+ - **Projects:** GitHub stars gained in the last 90 days, from unrent's own weekly
205
+ snapshots. Until four weeks exist: total stars. The report says which.
206
+ - **Models:** Hugging Face trending. Labs' own releases only, open licence, max two per lab.
207
+ - **Open source only.** OSI licence for code. Apache, MIT, BSD or CC-BY for weights.
208
+ Restricted weights are out, whatever the code licence. Archived projects and a year
209
+ without a push are out. Open core is marked: LiteLLM's `enterprise/` or Langfuse's `ee/`
210
+ is not under the licence shown.
211
+ - **No silent rot.** A renamed, archived or relicensed project blocks the auto-merge and
212
+ waits for a human. A pool that empties or halves is not published.
213
+
214
+ The scanner reads the snapshot shipped with the release. New rankings come with new
215
+ releases.
216
+
217
+ ## Contribute
218
+
219
+ The catalog is YAML. New service: [`catalog/services/`](https://github.com/stringcutter/unrent/tree/main/catalog/services). New alternative: a pool in
220
+ [`catalog/alternatives.yaml`](https://github.com/stringcutter/unrent/blob/main/catalog/alternatives.yaml). False positive or miss: open an issue with the
221
+ line that fooled it. [CONTRIBUTING.md](https://github.com/stringcutter/unrent/blob/main/CONTRIBUTING.md).
222
+
223
+ ## Licence
224
+
225
+ [Apache-2.0](https://github.com/stringcutter/unrent/blob/main/LICENSE). unrent is made by
226
+ [stringcutter](https://github.com/stringcutter).