modelroster 0.1.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 (175) hide show
  1. modelroster-0.1.0/.gitignore +12 -0
  2. modelroster-0.1.0/CHANGELOG.md +38 -0
  3. modelroster-0.1.0/LICENSE +21 -0
  4. modelroster-0.1.0/MAINTAINERS.md +128 -0
  5. modelroster-0.1.0/PKG-INFO +258 -0
  6. modelroster-0.1.0/README.md +228 -0
  7. modelroster-0.1.0/docs/DESIGN.md +135 -0
  8. modelroster-0.1.0/docs/DIFFERENCES.md +140 -0
  9. modelroster-0.1.0/pyproject.toml +58 -0
  10. modelroster-0.1.0/src/modelroster/__init__.py +144 -0
  11. modelroster-0.1.0/src/modelroster/_version.py +1 -0
  12. modelroster-0.1.0/src/modelroster/cli.py +383 -0
  13. modelroster-0.1.0/src/modelroster/data/anthropic.drift.json +42 -0
  14. modelroster-0.1.0/src/modelroster/data/anthropic.json +2175 -0
  15. modelroster-0.1.0/src/modelroster/data/inception.drift.json +24 -0
  16. modelroster-0.1.0/src/modelroster/data/inception.json +145 -0
  17. modelroster-0.1.0/src/modelroster/data/nvidia.drift.json +123 -0
  18. modelroster-0.1.0/src/modelroster/data/nvidia.json +7873 -0
  19. modelroster-0.1.0/src/modelroster/data/openai.drift.json +330 -0
  20. modelroster-0.1.0/src/modelroster/data/openai.json +63047 -0
  21. modelroster-0.1.0/src/modelroster/discover/__init__.py +44 -0
  22. modelroster-0.1.0/src/modelroster/discover/base.py +24 -0
  23. modelroster-0.1.0/src/modelroster/discover/huggingface.py +56 -0
  24. modelroster-0.1.0/src/modelroster/discover/nvidia_nim.py +67 -0
  25. modelroster-0.1.0/src/modelroster/discover/ollama_library.py +39 -0
  26. modelroster-0.1.0/src/modelroster/emit.py +121 -0
  27. modelroster-0.1.0/src/modelroster/http.py +293 -0
  28. modelroster-0.1.0/src/modelroster/providers/__init__.py +88 -0
  29. modelroster-0.1.0/src/modelroster/providers/anthropic.py +168 -0
  30. modelroster-0.1.0/src/modelroster/providers/base.py +108 -0
  31. modelroster-0.1.0/src/modelroster/providers/cohere.py +89 -0
  32. modelroster-0.1.0/src/modelroster/providers/google.py +99 -0
  33. modelroster-0.1.0/src/modelroster/providers/inception.py +69 -0
  34. modelroster-0.1.0/src/modelroster/providers/mistral.py +66 -0
  35. modelroster-0.1.0/src/modelroster/providers/nvidia.py +20 -0
  36. modelroster-0.1.0/src/modelroster/providers/ollama.py +109 -0
  37. modelroster-0.1.0/src/modelroster/providers/openai.py +341 -0
  38. modelroster-0.1.0/src/modelroster/providers/openai_compat.py +98 -0
  39. modelroster-0.1.0/src/modelroster/providers/openai_docs.py +405 -0
  40. modelroster-0.1.0/src/modelroster/providers/xai.py +68 -0
  41. modelroster-0.1.0/src/modelroster/py.typed +0 -0
  42. modelroster-0.1.0/src/modelroster/ref.py +164 -0
  43. modelroster-0.1.0/src/modelroster/registry.py +283 -0
  44. modelroster-0.1.0/src/modelroster/schema.py +247 -0
  45. modelroster-0.1.0/src/modelroster/store.py +87 -0
  46. modelroster-0.1.0/src/modelroster/update.py +201 -0
  47. modelroster-0.1.0/src/modelroster/validate.py +168 -0
  48. modelroster-0.1.0/tests/conftest.py +8 -0
  49. modelroster-0.1.0/tests/fixtures/README.md +41 -0
  50. modelroster-0.1.0/tests/fixtures/discovery/hf_models.json +1 -0
  51. modelroster-0.1.0/tests/fixtures/discovery/ngc_nim.json +1 -0
  52. modelroster-0.1.0/tests/fixtures/discovery/ollama_library.html +14844 -0
  53. modelroster-0.1.0/tests/fixtures/listings/anthropic_models.json +1 -0
  54. modelroster-0.1.0/tests/fixtures/listings/cohere_models.json +20 -0
  55. modelroster-0.1.0/tests/fixtures/listings/google_models.json +6 -0
  56. modelroster-0.1.0/tests/fixtures/listings/google_native_models.json +16 -0
  57. modelroster-0.1.0/tests/fixtures/listings/inception_models.json +1 -0
  58. modelroster-0.1.0/tests/fixtures/listings/mistral_models.json +22 -0
  59. modelroster-0.1.0/tests/fixtures/listings/nvidia_models.json +1 -0
  60. modelroster-0.1.0/tests/fixtures/listings/ollama_show/llama3.1--8b.json +5 -0
  61. modelroster-0.1.0/tests/fixtures/listings/ollama_show/nomic-embed-text--latest.json +5 -0
  62. modelroster-0.1.0/tests/fixtures/listings/ollama_show/qwen3--8b.json +5 -0
  63. modelroster-0.1.0/tests/fixtures/listings/ollama_tags.json +11 -0
  64. modelroster-0.1.0/tests/fixtures/listings/openai_models.json +887 -0
  65. modelroster-0.1.0/tests/fixtures/listings/xai_language_models.json +10 -0
  66. modelroster-0.1.0/tests/fixtures/listings/xai_models.json +6 -0
  67. modelroster-0.1.0/tests/fixtures/openai_docs/babbage-002.md +82 -0
  68. modelroster-0.1.0/tests/fixtures/openai_docs/chat-latest.md +92 -0
  69. modelroster-0.1.0/tests/fixtures/openai_docs/chatgpt-4o-latest.md +85 -0
  70. modelroster-0.1.0/tests/fixtures/openai_docs/chatgpt-image-latest.md +108 -0
  71. modelroster-0.1.0/tests/fixtures/openai_docs/codex-mini-latest.md +93 -0
  72. modelroster-0.1.0/tests/fixtures/openai_docs/computer-use-preview.md +86 -0
  73. modelroster-0.1.0/tests/fixtures/openai_docs/davinci-002.md +82 -0
  74. modelroster-0.1.0/tests/fixtures/openai_docs/daybreak-blue-latest.md +90 -0
  75. modelroster-0.1.0/tests/fixtures/openai_docs/daybreak-red-latest.md +91 -0
  76. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-3.5-turbo.md +85 -0
  77. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4-turbo-preview.md +84 -0
  78. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4-turbo.md +85 -0
  79. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4.1-mini.md +118 -0
  80. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4.1-nano.md +118 -0
  81. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4.1.md +120 -0
  82. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4.5-preview.md +91 -0
  83. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4.md +85 -0
  84. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-audio-preview.md +86 -0
  85. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini-audio-preview.md +93 -0
  86. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini-realtime-preview.md +85 -0
  87. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini-search-preview.md +86 -0
  88. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini-transcribe.md +80 -0
  89. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini-tts.md +82 -0
  90. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini.md +105 -0
  91. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-realtime-preview.md +87 -0
  92. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-search-preview.md +85 -0
  93. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-transcribe-diarize.md +71 -0
  94. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-transcribe.md +79 -0
  95. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o.md +107 -0
  96. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5-chat-latest.md +101 -0
  97. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5-codex.md +100 -0
  98. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5-mini.md +105 -0
  99. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5-nano.md +107 -0
  100. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5-pro.md +108 -0
  101. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.1-chat-latest.md +100 -0
  102. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.1-codex-max.md +98 -0
  103. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.1-codex-mini.md +98 -0
  104. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.1-codex.md +98 -0
  105. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.1.md +105 -0
  106. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.2-chat-latest.md +91 -0
  107. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.2-codex.md +101 -0
  108. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.2-pro.md +113 -0
  109. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.2.md +107 -0
  110. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.3-chat-latest.md +91 -0
  111. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.3-codex.md +101 -0
  112. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.4-mini.md +113 -0
  113. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.4-nano.md +111 -0
  114. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.4-pro.md +120 -0
  115. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.4.md +124 -0
  116. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.5-pro.md +107 -0
  117. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.5.md +124 -0
  118. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.6-cyber.md +114 -0
  119. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.6-luna.md +112 -0
  120. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.6-sol.md +113 -0
  121. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.6-terra.md +112 -0
  122. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.md +105 -0
  123. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-audio-1.5.md +84 -0
  124. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-audio-mini.md +83 -0
  125. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-audio.md +84 -0
  126. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-image-1-mini.md +103 -0
  127. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-image-1.5.md +108 -0
  128. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-image-1.md +107 -0
  129. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-image-2.md +61 -0
  130. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-live-transcribe.md +71 -0
  131. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-oss-120b.md +87 -0
  132. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-oss-20b.md +87 -0
  133. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-1.5.md +92 -0
  134. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-2.1-mini.md +100 -0
  135. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-2.1.md +102 -0
  136. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-2.md +102 -0
  137. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-mini.md +85 -0
  138. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-translate.md +76 -0
  139. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-whisper.md +76 -0
  140. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime.md +92 -0
  141. modelroster-0.1.0/tests/fixtures/openai_docs/gpt-transcribe.md +71 -0
  142. modelroster-0.1.0/tests/fixtures/openai_docs/models.md +118 -0
  143. modelroster-0.1.0/tests/fixtures/openai_docs/o1-mini.md +95 -0
  144. modelroster-0.1.0/tests/fixtures/openai_docs/o1-preview.md +89 -0
  145. modelroster-0.1.0/tests/fixtures/openai_docs/o1-pro.md +100 -0
  146. modelroster-0.1.0/tests/fixtures/openai_docs/o1.md +98 -0
  147. modelroster-0.1.0/tests/fixtures/openai_docs/o3-deep-research.md +104 -0
  148. modelroster-0.1.0/tests/fixtures/openai_docs/o3-mini.md +99 -0
  149. modelroster-0.1.0/tests/fixtures/openai_docs/o3-pro.md +104 -0
  150. modelroster-0.1.0/tests/fixtures/openai_docs/o3.md +120 -0
  151. modelroster-0.1.0/tests/fixtures/openai_docs/o4-mini-deep-research.md +105 -0
  152. modelroster-0.1.0/tests/fixtures/openai_docs/o4-mini.md +109 -0
  153. modelroster-0.1.0/tests/fixtures/openai_docs/omni-moderation-latest.md +64 -0
  154. modelroster-0.1.0/tests/fixtures/openai_docs/sora-2-pro.md +84 -0
  155. modelroster-0.1.0/tests/fixtures/openai_docs/sora-2.md +81 -0
  156. modelroster-0.1.0/tests/fixtures/openai_docs/text-embedding-3-large.md +77 -0
  157. modelroster-0.1.0/tests/fixtures/openai_docs/text-embedding-3-small.md +77 -0
  158. modelroster-0.1.0/tests/fixtures/openai_docs/text-embedding-ada-002.md +77 -0
  159. modelroster-0.1.0/tests/fixtures/openai_docs/text-moderation-latest.md +45 -0
  160. modelroster-0.1.0/tests/fixtures/openai_docs/text-moderation-stable.md +45 -0
  161. modelroster-0.1.0/tests/fixtures/openai_docs/tts-1-hd.md +77 -0
  162. modelroster-0.1.0/tests/fixtures/openai_docs/tts-1.md +77 -0
  163. modelroster-0.1.0/tests/fixtures/openai_docs/whisper-1.md +69 -0
  164. modelroster-0.1.0/tests/helpers.py +97 -0
  165. modelroster-0.1.0/tests/test_anthropic.py +85 -0
  166. modelroster-0.1.0/tests/test_cli.py +68 -0
  167. modelroster-0.1.0/tests/test_compat_providers.py +144 -0
  168. modelroster-0.1.0/tests/test_discover.py +47 -0
  169. modelroster-0.1.0/tests/test_emit.py +23 -0
  170. modelroster-0.1.0/tests/test_http.py +121 -0
  171. modelroster-0.1.0/tests/test_live.py +33 -0
  172. modelroster-0.1.0/tests/test_openai_docs.py +243 -0
  173. modelroster-0.1.0/tests/test_openai_provider.py +181 -0
  174. modelroster-0.1.0/tests/test_registry.py +233 -0
  175. modelroster-0.1.0/tests/test_update.py +131 -0
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ src/modelroster/data/cache/
9
+ src/modelroster/data/*.previous.json
10
+ src/modelroster/data/discovered/
11
+ src/modelroster/data/*.tmp
12
+ .env
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/) and the project uses
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ Two version numbers matter:
8
+
9
+ * the **package version** (`modelroster.__version__`) — API/CLI compatibility;
10
+ * the **parser version** (`modelroster.PARSER_VERSION`, stamped into every
11
+ record) — bumped whenever any adapter's output for the same input could change.
12
+
13
+ ## [Unreleased]
14
+
15
+ ## [0.1.0] — 2026-08-23
16
+
17
+ Initial release, ported from the `model_registry` prototype.
18
+
19
+ ### Added
20
+ - One normalized `ModelRecord` schema with tri-state capabilities, modalities,
21
+ endpoints, built-in tools, pricing, provenance, and warnings.
22
+ - Providers: anthropic, openai (docs parser + listing), xai, mistral, google,
23
+ cohere, nvidia, inception, ollama; `modelroster.providers` entry-point group.
24
+ - Discovery tier: huggingface, ollama_library, nvidia_nim.
25
+ - Consumer API: `load()`, `Registry.models(**filters)`, `ModelRef`, predicates.
26
+ - CLI: `update`, `list`, `show`, `diff`, `validate`, `emit`, `discover`,
27
+ `providers`, `capture`.
28
+ - Shared HTTP layer with retries, caching, content-hash change detection,
29
+ full `--offline` coverage, and fixture replay.
30
+ - Validation gates (page failures, parser regression, catalog shrink,
31
+ header-fact loss, generic shrink) with byte-for-byte preservation on refusal.
32
+ - Shipped data for anthropic, openai, nvidia, inception (retrieved 2026-08-23);
33
+ xai, mistral, cohere, google populate via `modelroster update` with keys.
34
+ - Runtime alias index: documented aliases, snapshots, and `ft:` bases resolve
35
+ through `Registry.get`/`resolve`/`ModelRef` even when the listing does not
36
+ carry them.
37
+ - `py.typed` marker for downstream type-checkers.
38
+ - GitHub Actions: offline tests on push, daily live refresh, PyPI publish on tags.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zach Hanson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,128 @@
1
+ # Maintaining modelroster
2
+
3
+ This file is written for the agent (or person) operating the refresh loop.
4
+ Everything below is scriptable; nothing requires judgment until a gate fires.
5
+
6
+ ## The loop
7
+
8
+ ```
9
+ modelroster update --all # 1. refresh every provider you have keys for
10
+ modelroster diff # 2. read the drift reports
11
+ modelroster validate -v # 3. inspect warnings
12
+ ```
13
+
14
+ 1. **Run the update.** One provider failing never blocks the others. Exit
15
+ status is the worst stage: `0` everything written, `2` at least one provider
16
+ refused (its previous data is untouched), `3` a fetch failed, `4` usage.
17
+ A provider whose key is absent is *skipped* with exit 0 — check the summary
18
+ line (`xai: skipped (...)`) to make sure that is what you expected.
19
+ 2. **Read the drift report** (`modelroster diff`, or `<provider>.drift.json`).
20
+ New/removed models and snapshots are normal. Post the report where the team
21
+ will see it. A `changed_capabilities` entry that flips a documented value
22
+ (`true -> false`) on many models at once is a parser problem until proven
23
+ otherwise — see "Deciding whether the parser broke".
24
+ 3. **Inspect warnings.** The categories you will see:
25
+ * `No official model documentation record matched this API model ID` —
26
+ the listing has ids the docs don't (search/tts variants). Informational.
27
+ * `id X claimed by A, B; resolved to A` — alias pages claiming another
28
+ family's snapshot; resolved deterministically. Informational.
29
+ * `contradiction: ...` — the page says two things. Informational, but
30
+ worth a glance.
31
+ * `unrecognised <thing> ... (kept)` — **new vocabulary**. The value is
32
+ retained, so nothing is lost, but the known-vocabulary tuples in
33
+ `schema.py` / the provider module should be extended so the value gets a
34
+ stable key and absence starts meaning `False` where a positive list is
35
+ present. This is a parser change → bump `PARSER_VERSION`.
36
+ * `unparseable knowledge cutoff` — extend `parse_cutoff`. Parser change.
37
+ * `no Endpoints section found` / `no Model details section found` on
38
+ pages that used to have them — format change. See below.
39
+
40
+ ## Deciding whether the parser broke
41
+
42
+ A gate firing (exit 2) means the data on disk is still the last good run. Do
43
+ **not** delete `<provider>.json` or `.previous.json` to "fix" it.
44
+
45
+ * `PARSER REGRESSION suspected` / `HEADER PARSE REGRESSION suspected` —
46
+ fetch one affected page (`curl https://developers.openai.com/api/docs/models/<slug>.md`)
47
+ and compare with `tests/fixtures/openai_docs/<slug>.md`. If a heading was
48
+ renamed or a bullet format changed, update the regexes / section constants
49
+ in `providers/openai_docs.py`, refresh the fixture, add a test, bump
50
+ `PARSER_VERSION`.
51
+ * `documentation catalog shrank` — open `models.md`; the link format probably
52
+ changed (`discover_model_pages`).
53
+ * `N/M documentation pages failed to fetch/parse` — transient (rerun, the
54
+ fetcher retries and falls back to cache) unless the failures are `parse:` —
55
+ then a format change.
56
+ * `model list shrank` — compare the raw listing (`modelroster capture`) with
57
+ the previous data. Real mass retirements happen; if it is real, the gate can
58
+ be overridden by deleting *only* `<provider>.previous.json`… but first post
59
+ the diff and get a human ack.
60
+
61
+ ## Parser version and releases
62
+
63
+ `PARSER_VERSION` (`schema.py`) is stamped into every record. Bump it whenever
64
+ an adapter's output for the same input could change: new vocabulary, a new
65
+ regex, a changed mapping, a new provider-wide fact. The date-based format is
66
+ `YYYY.MM.DD-N`.
67
+
68
+ Release when data or code changed in a way consumers should pick up:
69
+
70
+ 1. `CHANGELOG.md`: move items from Unreleased under a new version heading.
71
+ 2. Bump `src/modelroster/_version.py` (semver: data-only refresh = patch;
72
+ new provider/field = minor; schema or API break = major — also bump
73
+ `SCHEMA_VERSION`).
74
+ 3. `pytest -q && modelroster validate`.
75
+ 4. Tag `vX.Y.Z` and push the tag — `publish.yml` builds, re-validates, checks
76
+ the tag matches `__version__`, and publishes via PyPI trusted publishing
77
+ (configure the `pypi` environment once in the repository settings).
78
+
79
+ Before the **first** publish, confirm the name is still free:
80
+ `pip index versions modelroster` should report nothing.
81
+
82
+ ## Automation in place
83
+
84
+ * `ci.yml` — offline suite on every push (3.11–3.13), fixture replay of the
85
+ full OpenAI/Anthropic pipeline, shipped-data validation, and a clean-venv
86
+ install test of the built wheel.
87
+ * `refresh.yml` — daily at 06:17 UTC: `modelroster update --all --no-cache`
88
+ with whatever secrets are configured, the live test suite, then commits
89
+ `src/modelroster/data/*.json` (data + drift reports) on success. On exit ≠ 0
90
+ it opens a GitHub issue labelled `refresh-failure` with the log tail and
91
+ fails the run.
92
+ * `publish.yml` — on tags.
93
+
94
+ Secrets to configure for the refresh: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
95
+ and optionally `XAI_API_KEY`, `MISTRAL_API_KEY`, `GOOGLE_API_KEY`,
96
+ `COHERE_API_KEY`, `NVIDIA_API_KEY`, `INCEPTION_API_KEY`. Missing ones skip
97
+ the provider.
98
+
99
+ ## Fixtures
100
+
101
+ `tests/fixtures/README.md` records the provenance of every fixture. When a
102
+ provider's response shape changes or a key becomes available for a provider
103
+ whose fixture is reference-shaped, run `modelroster capture --provider <name>`
104
+ (writes under `tests/fixtures/listings/`) and commit the result. OpenAI
105
+ documentation pages are refreshed by copying `<data-dir>/cache/openai/*.body`
106
+ files to `tests/fixtures/openai_docs/<slug>.md` (the `.meta.json` beside each
107
+ body records its URL).
108
+
109
+ ## Adding a provider
110
+
111
+ 1. `src/modelroster/providers/<name>.py` — subclass `OpenAICompatProvider`
112
+ or `BaseProvider`; set `name`, `auth`, `describe`; implement
113
+ `enrich_record`/`enrich`; point `fixtures()` at captured responses.
114
+ 2. Add it to `_BUILTIN` in `providers/__init__.py` (third parties use the
115
+ entry-point group instead).
116
+ 3. `tests/fixtures/listings/<name>_models.json` + a test in
117
+ `tests/test_compat_providers.py` that asserts the tri-state mapping.
118
+ 4. Document the row in the README table; add the key to `refresh.yml`.
119
+
120
+ ## Things that must stay true
121
+
122
+ * No generation probes. Availability from listings, capabilities from
123
+ official docs/metadata only.
124
+ * `None` is never turned into `False`.
125
+ * Exact-id matching only.
126
+ * A refused update leaves the previous file byte-identical.
127
+ * No key is ever read from anywhere but the environment / `.env`, and never
128
+ written to disk (the HTTP cache stores response bodies, not request headers).
@@ -0,0 +1,258 @@
1
+ Metadata-Version: 2.5
2
+ Name: modelroster
3
+ Version: 0.1.0
4
+ Summary: Accurate, current LLM model identifiers and capabilities for every provider — shipped as data, refreshed from official sources.
5
+ Project-URL: Homepage, https://github.com/ZlaylowZ/modelroster
6
+ Project-URL: Repository, https://github.com/ZlaylowZ/modelroster
7
+ Project-URL: Changelog, https://github.com/ZlaylowZ/modelroster/blob/main/CHANGELOG.md
8
+ Author-email: Zach Hanson <zhanson1@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: anthropic,capabilities,llm,models,openai,registry
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: httpx>=0.25
22
+ Provides-Extra: dev
23
+ Requires-Dist: build; extra == 'dev'
24
+ Requires-Dist: pytest>=8; extra == 'dev'
25
+ Requires-Dist: python-dotenv>=1.0; extra == 'dev'
26
+ Requires-Dist: twine; extra == 'dev'
27
+ Provides-Extra: dotenv
28
+ Requires-Dist: python-dotenv>=1.0; extra == 'dotenv'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # modelroster
32
+
33
+ **Accurate, current LLM model identifiers and capabilities for every provider —
34
+ shipped as data, refreshed from official sources, never guessed.**
35
+
36
+ ```bash
37
+ pip install modelroster
38
+ ```
39
+
40
+ ```python
41
+ import modelroster
42
+
43
+ r = modelroster.load() # works offline, no keys
44
+ for m in r.models(tool_calling=True, reasoning=True):
45
+ print(m.ref, m.context_window, m.capabilities.reasoning_efforts)
46
+
47
+ ref = modelroster.ModelRef.parse("openai/gpt-5.4").validate() # raises on unknown/retired ids
48
+ modelroster.context_window("claude-opus-5") # 1000000
49
+ modelroster.supports_tool_calling("text-embedding-3-small") # None (not documented — NOT False)
50
+ ```
51
+
52
+ ## Why
53
+
54
+ Providers add, rename, alias, snapshot and retire models constantly, and each
55
+ publishes its list differently. Every project that calls an LLM ends up with a
56
+ hand-maintained model table that silently rots. `modelroster` is one package
57
+ that any project — or any agent — installs to get the exact id strings a
58
+ provider accepts today and what each model supports, with enough validation
59
+ that an upstream format change fails loudly instead of shipping a broken
60
+ catalog.
61
+
62
+ ## Principles
63
+
64
+ 1. **Tri-state capabilities.** Every capability is `True` / `False` / `None`.
65
+ `None` means *the source does not say* and is never collapsed into `False`.
66
+ `False` only arises from an explicit "not supported" statement, or from
67
+ absence in a positively enumerated list whose section is present.
68
+ 2. **No generation probes.** Availability comes from listing endpoints;
69
+ capabilities come from official documentation or official API metadata.
70
+ The registry never sends a completion request to find out what a model does.
71
+ 3. **Provenance on every fact.** Each field carries `{section, evidence, ...}`
72
+ naming the document section or API field it came from
73
+ (`modelroster show gpt-5.4 --provenance`).
74
+ 4. **Exact ids only.** Aliases, snapshots and fine-tune bases resolve through an
75
+ explicit index built from provider statements; there is no fuzzy matching and
76
+ no inference from date suffixes.
77
+ 5. **Refuse rather than rot.** Validation gates refuse to overwrite good data
78
+ when a parser stops understanding a page; the previous data survives
79
+ byte-for-byte and a drift report says what changed.
80
+
81
+ ## What is in the box
82
+
83
+ | Provider | Availability | Capabilities | Key |
84
+ |---|---|---|---|
85
+ | `anthropic` | `GET /v1/models` (paginated) | the same call's `capabilities` object | `ANTHROPIC_API_KEY` |
86
+ | `openai` | `GET /v1/models` | official Markdown docs (`developers.openai.com/api/docs/models/*.md`), 96 pages | `OPENAI_API_KEY` |
87
+ | `xai` | `GET /v1/models` | `GET /v1/language-models` (modalities, aliases) | `XAI_API_KEY` |
88
+ | `mistral` | `GET /v1/models` | the listing's `capabilities` object | `MISTRAL_API_KEY` |
89
+ | `google` | OpenAI-compat shim `/v1beta/openai/models` | native `/v1beta/models` (limits, methods) | `GOOGLE_API_KEY` / `GEMINI_API_KEY` |
90
+ | `cohere` | `GET /v1/models` | the same call (endpoints, features, context) | `COHERE_API_KEY` |
91
+ | `nvidia` | `GET integrate.api.nvidia.com/v1/models` (public) | — (ids only) | optional |
92
+ | `inception` | `GET api.inceptionlabs.ai/v1/models` (public) | the same call (modalities, limits, features, pricing) | optional |
93
+ | `ollama` | local `GET /api/tags` | local `POST /api/show` (capabilities, context) | none (`OLLAMA_HOST`) |
94
+
95
+ **Data coverage.** The wheel ships *live* data for **anthropic, openai,
96
+ nvidia, and inception** — the providers refreshable at release time with the
97
+ keys at hand or public listings (`modelroster providers` shows what is loaded
98
+ and when it was retrieved). **xai, mistral, cohere, and google** currently
99
+ have no shipped data: their test fixtures are reference-shaped (hand-written
100
+ to each provider's public API reference, see `tests/fixtures/README.md`)
101
+ pending real keys — the Google key available at build time was rejected with
102
+ HTTP 400 and needs replacement. Run `modelroster update` with your own keys to
103
+ populate them; a missing key skips that provider, it is never an error, and
104
+ the daily refresh workflow behaves the same way.
105
+
106
+ A separate **discovery tier** (`modelroster discover huggingface|ollama_library|nvidia_nim`)
107
+ lists candidate models from broad registries with mostly-unknown capabilities.
108
+ They are labelled `tier="discovered"` and never enter the verified catalog.
109
+
110
+ ## Consumer API
111
+
112
+ ```python
113
+ r = modelroster.load() # every provider with data
114
+ r = modelroster.load("openai") # one provider
115
+ r = modelroster.load(data_dir="~/my/data") # a refreshed copy (also $MODELROSTER_DATA_DIR)
116
+
117
+ r.providers(); r.info() # retrieved_at, parser_version, counts
118
+ r.get("gpt-5.4"); r.get("openai/gpt-5.4"); r.get(ModelRef("openai", "gpt-5.4"))
119
+ r.resolve("gpt-4o-2024-08-06") # -> the gpt-4o family record
120
+ r.models(provider="openai", tool_calling=True, image_input=True, endpoint="responses")
121
+ r.models(reasoning=True, unknown_ok=True) # let None pass too
122
+ r.models(relationship="canonical", include_retired=False, strict=True)
123
+ r.ids(...); r.refs(...) # plain ids / ModelRefs
124
+ ```
125
+
126
+ Filters accept every capability name (`reasoning`, `reasoning_efforts`,
127
+ `extended_thinking`, `tool_calling`, `structured_outputs`, `streaming`,
128
+ `prompt_caching`, `fine_tuning`, `batch`, `citations`, `code_execution`,
129
+ `pdf_input`), modality flags (`image_input`, `audio_output`, …), any
130
+ `capabilities.extra` key, and `endpoint=` / `builtin_tool=`. A `True`/`False`
131
+ filter matches only a *documented* value; pass `unknown_ok=True` to let `None`
132
+ through.
133
+
134
+ Module-level predicates mirror the record fields and are provider-agnostic:
135
+ `supports(model, cap)`, `supports_tool_calling`, `supports_reasoning`,
136
+ `supported_reasoning_efforts`, `supports_endpoint`, `supports_builtin_tool`,
137
+ `supports_modality`, `context_window`, `max_input_tokens`, `max_output_tokens`,
138
+ `models_supporting(cap, provider)`, `available_models(provider)`.
139
+
140
+ ### `ModelRef` — a type for model names
141
+
142
+ ```python
143
+ from modelroster import ModelRef, UnknownModelError, RetiredModelError
144
+
145
+ ModelRef.parse("openai/gpt-5.4") # explicit
146
+ ModelRef.parse("gpt-5.4") # provider found by exact lookup in the registry
147
+ ModelRef.parse("gpt-99").inferred # True: only the documented prefix heuristic matched
148
+ ModelRef("openai", "gpt-5.4").validate() # raises UnknownModelError / RetiredModelError
149
+ ModelRef("openai", "gpt-4o-2024-08-06").resolve() # canonical family record
150
+ ```
151
+
152
+ ### Dependency-free snapshot
153
+
154
+ ```bash
155
+ modelroster emit --out my_models.py --provider openai --provider anthropic -c tool_calling
156
+ ```
157
+
158
+ writes a compiled-checked module with `MODELS`, `OPENAI_MODELS`,
159
+ `OPENAI_MODEL_IDS`, … for projects that vendor a file instead of depending on
160
+ `modelroster`.
161
+
162
+ ### Scheduled refresh from an agent
163
+
164
+ ```python
165
+ report = modelroster.refresh(["openai", "anthropic"]) # {provider: {code, drift, errors, ...}}
166
+ ```
167
+
168
+ ## CLI
169
+
170
+ ```
171
+ modelroster update [--provider X ...] [--offline] [--dry-run] [--no-cache] [--fixtures DIR] [--emit FILE]
172
+ modelroster list [--provider X] [-c reasoning -c tool_calling[=true|false|unknown]] [--endpoint K] [--json]
173
+ modelroster show <id | provider/id> [--provenance] [--json]
174
+ modelroster diff [--provider X] # last drift report
175
+ modelroster validate [--provider X] [-v] # re-run the gates on stored data
176
+ modelroster emit --out FILE [--provider X] [-c ...]
177
+ modelroster discover <huggingface|ollama_library|nvidia_nim> [--limit N] [--write]
178
+ modelroster providers [-v]
179
+ modelroster capture --provider X # save live listing responses as test fixtures
180
+ ```
181
+
182
+ Exit status: `0` ok · `2` validation refused the write (previous data preserved)
183
+ · `3` fetch failure · `4` usage. Providers run independently; the exit status
184
+ is the worst stage.
185
+
186
+ `--offline` serves *every* request — documentation pages and listing calls —
187
+ from the on-disk cache (`<data-dir>/cache/<provider>/`) and never opens a
188
+ socket. `--fixtures tests/fixtures` replays the captured fixtures instead.
189
+
190
+ Keys are read from the environment (`<PROVIDER>_API_KEY`), optionally from a
191
+ `.env` in the working directory when `python-dotenv` is installed
192
+ (`pip install modelroster[dotenv]`); existing environment variables are never
193
+ overridden. No key is ever written anywhere.
194
+
195
+ ## Record shape
196
+
197
+ ```
198
+ ModelRecord
199
+ provider, model_id exact string the API accepts
200
+ display_name, description, family (canonical id), aliases, snapshots,
201
+ default_snapshot, routes_to, relationship (canonical|snapshot|alias|fine_tune_inherited|unknown)
202
+ released, deprecated, shutdown_date
203
+ context_window, max_input_tokens, max_output_tokens
204
+ knowledge_cutoff (ISO), knowledge_cutoff_raw
205
+ modalities {text,image,audio,video} x {input,output}
206
+ capabilities reasoning, reasoning_efforts, default_effort, extended_thinking, tool_calling,
207
+ structured_outputs, streaming, prompt_caching, fine_tuning, batch, citations,
208
+ code_execution, pdf_input, extra{provider-specific keys}
209
+ endpoints {key: tri}, builtin_tools {key: tri} | None, pricing {input, output, cached_input} | None
210
+ tier (verified|discovered), provenance, sources, raw, retrieved_at, parser_version, warnings
211
+ ```
212
+
213
+ **Provider-wide facts.** An adapter may set a capability from provider-wide
214
+ official documentation (rather than a per-model source) only when the statement
215
+ covers every model the listing returns; such values carry
216
+ `provenance = {"section": "provider_docs", "evidence": "provider-wide statement", "url": ...}`.
217
+ Anthropic uses this for `tool_calling` and `streaming`. Nothing else is inferred.
218
+
219
+ ## Validation gates
220
+
221
+ Generic: empty listing; model count shrinks by more than half when the previous
222
+ run had at least 5 models. OpenAI: more than 10 % of documentation pages fail
223
+ to fetch/parse; parser regression (more than 25 % or at least 10
224
+ previously-understood pages now parse to nothing); documentation catalog shrinks
225
+ by more than half; loss of header-region facts (reasoning-effort sentence, prose
226
+ alias) on at least 25 % of the pages that previously carried them. The emitted
227
+ snapshot module must compile. On refusal the previous file is untouched and the
228
+ CLI exits 2.
229
+
230
+ Every successful update writes `<provider>.drift.json` beside the data: added /
231
+ removed models and families, new / removed snapshots, per-model capability
232
+ deltas (`None` printed as `unknown`), and warnings.
233
+
234
+ ## Adding a provider
235
+
236
+ Subclass `OpenAICompatProvider` (or `BaseProvider`), set `name`, `base_url`,
237
+ `auth`, override `enrich_record` if the provider publishes per-model metadata,
238
+ and point `fixtures()` at a captured response. Register it with
239
+ `modelroster.providers.register(MyProvider())` or via the entry-point group
240
+ `modelroster.providers`. No core file changes are needed (see
241
+ `tests/test_compat_providers.py::test_plugin_provider_needs_no_core_edits`).
242
+
243
+ ## Development
244
+
245
+ ```bash
246
+ pip install -e ".[dev]"
247
+ pytest -q # offline, ~1 s
248
+ MODELROSTER_LIVE=1 pytest tests/test_live.py -q # hits the real endpoints
249
+ modelroster update --fixtures tests/fixtures --dry-run # full pipeline on fixtures
250
+ ```
251
+
252
+ See [MAINTAINERS.md](MAINTAINERS.md) for the refresh loop, `docs/DESIGN.md`
253
+ for the design note, and `docs/DIFFERENCES.md` for where this package
254
+ intentionally differs from the prototype it was ported from.
255
+
256
+ ## License
257
+
258
+ MIT.