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.
- modelroster-0.1.0/.gitignore +12 -0
- modelroster-0.1.0/CHANGELOG.md +38 -0
- modelroster-0.1.0/LICENSE +21 -0
- modelroster-0.1.0/MAINTAINERS.md +128 -0
- modelroster-0.1.0/PKG-INFO +258 -0
- modelroster-0.1.0/README.md +228 -0
- modelroster-0.1.0/docs/DESIGN.md +135 -0
- modelroster-0.1.0/docs/DIFFERENCES.md +140 -0
- modelroster-0.1.0/pyproject.toml +58 -0
- modelroster-0.1.0/src/modelroster/__init__.py +144 -0
- modelroster-0.1.0/src/modelroster/_version.py +1 -0
- modelroster-0.1.0/src/modelroster/cli.py +383 -0
- modelroster-0.1.0/src/modelroster/data/anthropic.drift.json +42 -0
- modelroster-0.1.0/src/modelroster/data/anthropic.json +2175 -0
- modelroster-0.1.0/src/modelroster/data/inception.drift.json +24 -0
- modelroster-0.1.0/src/modelroster/data/inception.json +145 -0
- modelroster-0.1.0/src/modelroster/data/nvidia.drift.json +123 -0
- modelroster-0.1.0/src/modelroster/data/nvidia.json +7873 -0
- modelroster-0.1.0/src/modelroster/data/openai.drift.json +330 -0
- modelroster-0.1.0/src/modelroster/data/openai.json +63047 -0
- modelroster-0.1.0/src/modelroster/discover/__init__.py +44 -0
- modelroster-0.1.0/src/modelroster/discover/base.py +24 -0
- modelroster-0.1.0/src/modelroster/discover/huggingface.py +56 -0
- modelroster-0.1.0/src/modelroster/discover/nvidia_nim.py +67 -0
- modelroster-0.1.0/src/modelroster/discover/ollama_library.py +39 -0
- modelroster-0.1.0/src/modelroster/emit.py +121 -0
- modelroster-0.1.0/src/modelroster/http.py +293 -0
- modelroster-0.1.0/src/modelroster/providers/__init__.py +88 -0
- modelroster-0.1.0/src/modelroster/providers/anthropic.py +168 -0
- modelroster-0.1.0/src/modelroster/providers/base.py +108 -0
- modelroster-0.1.0/src/modelroster/providers/cohere.py +89 -0
- modelroster-0.1.0/src/modelroster/providers/google.py +99 -0
- modelroster-0.1.0/src/modelroster/providers/inception.py +69 -0
- modelroster-0.1.0/src/modelroster/providers/mistral.py +66 -0
- modelroster-0.1.0/src/modelroster/providers/nvidia.py +20 -0
- modelroster-0.1.0/src/modelroster/providers/ollama.py +109 -0
- modelroster-0.1.0/src/modelroster/providers/openai.py +341 -0
- modelroster-0.1.0/src/modelroster/providers/openai_compat.py +98 -0
- modelroster-0.1.0/src/modelroster/providers/openai_docs.py +405 -0
- modelroster-0.1.0/src/modelroster/providers/xai.py +68 -0
- modelroster-0.1.0/src/modelroster/py.typed +0 -0
- modelroster-0.1.0/src/modelroster/ref.py +164 -0
- modelroster-0.1.0/src/modelroster/registry.py +283 -0
- modelroster-0.1.0/src/modelroster/schema.py +247 -0
- modelroster-0.1.0/src/modelroster/store.py +87 -0
- modelroster-0.1.0/src/modelroster/update.py +201 -0
- modelroster-0.1.0/src/modelroster/validate.py +168 -0
- modelroster-0.1.0/tests/conftest.py +8 -0
- modelroster-0.1.0/tests/fixtures/README.md +41 -0
- modelroster-0.1.0/tests/fixtures/discovery/hf_models.json +1 -0
- modelroster-0.1.0/tests/fixtures/discovery/ngc_nim.json +1 -0
- modelroster-0.1.0/tests/fixtures/discovery/ollama_library.html +14844 -0
- modelroster-0.1.0/tests/fixtures/listings/anthropic_models.json +1 -0
- modelroster-0.1.0/tests/fixtures/listings/cohere_models.json +20 -0
- modelroster-0.1.0/tests/fixtures/listings/google_models.json +6 -0
- modelroster-0.1.0/tests/fixtures/listings/google_native_models.json +16 -0
- modelroster-0.1.0/tests/fixtures/listings/inception_models.json +1 -0
- modelroster-0.1.0/tests/fixtures/listings/mistral_models.json +22 -0
- modelroster-0.1.0/tests/fixtures/listings/nvidia_models.json +1 -0
- modelroster-0.1.0/tests/fixtures/listings/ollama_show/llama3.1--8b.json +5 -0
- modelroster-0.1.0/tests/fixtures/listings/ollama_show/nomic-embed-text--latest.json +5 -0
- modelroster-0.1.0/tests/fixtures/listings/ollama_show/qwen3--8b.json +5 -0
- modelroster-0.1.0/tests/fixtures/listings/ollama_tags.json +11 -0
- modelroster-0.1.0/tests/fixtures/listings/openai_models.json +887 -0
- modelroster-0.1.0/tests/fixtures/listings/xai_language_models.json +10 -0
- modelroster-0.1.0/tests/fixtures/listings/xai_models.json +6 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/babbage-002.md +82 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/chat-latest.md +92 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/chatgpt-4o-latest.md +85 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/chatgpt-image-latest.md +108 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/codex-mini-latest.md +93 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/computer-use-preview.md +86 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/davinci-002.md +82 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/daybreak-blue-latest.md +90 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/daybreak-red-latest.md +91 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-3.5-turbo.md +85 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4-turbo-preview.md +84 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4-turbo.md +85 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4.1-mini.md +118 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4.1-nano.md +118 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4.1.md +120 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4.5-preview.md +91 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4.md +85 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-audio-preview.md +86 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini-audio-preview.md +93 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini-realtime-preview.md +85 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini-search-preview.md +86 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini-transcribe.md +80 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini-tts.md +82 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-mini.md +105 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-realtime-preview.md +87 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-search-preview.md +85 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-transcribe-diarize.md +71 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o-transcribe.md +79 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-4o.md +107 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5-chat-latest.md +101 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5-codex.md +100 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5-mini.md +105 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5-nano.md +107 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5-pro.md +108 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.1-chat-latest.md +100 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.1-codex-max.md +98 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.1-codex-mini.md +98 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.1-codex.md +98 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.1.md +105 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.2-chat-latest.md +91 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.2-codex.md +101 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.2-pro.md +113 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.2.md +107 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.3-chat-latest.md +91 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.3-codex.md +101 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.4-mini.md +113 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.4-nano.md +111 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.4-pro.md +120 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.4.md +124 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.5-pro.md +107 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.5.md +124 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.6-cyber.md +114 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.6-luna.md +112 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.6-sol.md +113 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.6-terra.md +112 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-5.md +105 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-audio-1.5.md +84 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-audio-mini.md +83 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-audio.md +84 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-image-1-mini.md +103 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-image-1.5.md +108 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-image-1.md +107 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-image-2.md +61 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-live-transcribe.md +71 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-oss-120b.md +87 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-oss-20b.md +87 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-1.5.md +92 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-2.1-mini.md +100 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-2.1.md +102 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-2.md +102 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-mini.md +85 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-translate.md +76 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime-whisper.md +76 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-realtime.md +92 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/gpt-transcribe.md +71 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/models.md +118 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/o1-mini.md +95 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/o1-preview.md +89 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/o1-pro.md +100 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/o1.md +98 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/o3-deep-research.md +104 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/o3-mini.md +99 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/o3-pro.md +104 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/o3.md +120 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/o4-mini-deep-research.md +105 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/o4-mini.md +109 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/omni-moderation-latest.md +64 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/sora-2-pro.md +84 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/sora-2.md +81 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/text-embedding-3-large.md +77 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/text-embedding-3-small.md +77 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/text-embedding-ada-002.md +77 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/text-moderation-latest.md +45 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/text-moderation-stable.md +45 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/tts-1-hd.md +77 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/tts-1.md +77 -0
- modelroster-0.1.0/tests/fixtures/openai_docs/whisper-1.md +69 -0
- modelroster-0.1.0/tests/helpers.py +97 -0
- modelroster-0.1.0/tests/test_anthropic.py +85 -0
- modelroster-0.1.0/tests/test_cli.py +68 -0
- modelroster-0.1.0/tests/test_compat_providers.py +144 -0
- modelroster-0.1.0/tests/test_discover.py +47 -0
- modelroster-0.1.0/tests/test_emit.py +23 -0
- modelroster-0.1.0/tests/test_http.py +121 -0
- modelroster-0.1.0/tests/test_live.py +33 -0
- modelroster-0.1.0/tests/test_openai_docs.py +243 -0
- modelroster-0.1.0/tests/test_openai_provider.py +181 -0
- modelroster-0.1.0/tests/test_registry.py +233 -0
- modelroster-0.1.0/tests/test_update.py +131 -0
|
@@ -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.
|