modelroom 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.
- modelroom-0.1.0/.gitignore +26 -0
- modelroom-0.1.0/AGENTS.md +180 -0
- modelroom-0.1.0/CHANGELOG.md +991 -0
- modelroom-0.1.0/CONTRACTS.md +4367 -0
- modelroom-0.1.0/LICENSE +21 -0
- modelroom-0.1.0/PKG-INFO +390 -0
- modelroom-0.1.0/README.md +365 -0
- modelroom-0.1.0/docs/README.md +28 -0
- modelroom-0.1.0/docs/assets/banner.png +0 -0
- modelroom-0.1.0/docs/assets/banner.svg +41 -0
- modelroom-0.1.0/docs/assets/social-preview.png +0 -0
- modelroom-0.1.0/docs/assets/social-preview.svg +41 -0
- modelroom-0.1.0/modelroom/__init__.py +9 -0
- modelroom-0.1.0/modelroom/answers.py +55 -0
- modelroom-0.1.0/modelroom/binding.py +209 -0
- modelroom-0.1.0/modelroom/catalog.py +185 -0
- modelroom-0.1.0/modelroom/catalog.toml +569 -0
- modelroom-0.1.0/modelroom/cli.py +724 -0
- modelroom-0.1.0/modelroom/config.py +668 -0
- modelroom-0.1.0/modelroom/contracts.py +798 -0
- modelroom-0.1.0/modelroom/daemon.py +205 -0
- modelroom-0.1.0/modelroom/dialog.py +463 -0
- modelroom-0.1.0/modelroom/document.py +169 -0
- modelroom-0.1.0/modelroom/examples.py +551 -0
- modelroom-0.1.0/modelroom/fetch.py +190 -0
- modelroom-0.1.0/modelroom/fetch_types.py +40 -0
- modelroom-0.1.0/modelroom/fit.py +334 -0
- modelroom-0.1.0/modelroom/guided.py +783 -0
- modelroom-0.1.0/modelroom/guided_context.py +379 -0
- modelroom-0.1.0/modelroom/guided_contracts.py +127 -0
- modelroom-0.1.0/modelroom/guided_loadtest.py +295 -0
- modelroom-0.1.0/modelroom/guided_models.py +556 -0
- modelroom-0.1.0/modelroom/guided_search.py +266 -0
- modelroom-0.1.0/modelroom/hf.py +489 -0
- modelroom-0.1.0/modelroom/http.py +321 -0
- modelroom-0.1.0/modelroom/importer.py +495 -0
- modelroom-0.1.0/modelroom/intro.py +456 -0
- modelroom-0.1.0/modelroom/llmfit.py +344 -0
- modelroom-0.1.0/modelroom/loadtest.py +554 -0
- modelroom-0.1.0/modelroom/measure.py +676 -0
- modelroom-0.1.0/modelroom/measurements.py +494 -0
- modelroom-0.1.0/modelroom/migrate.py +234 -0
- modelroom-0.1.0/modelroom/ollama.py +388 -0
- modelroom-0.1.0/modelroom/ollama_local.py +63 -0
- modelroom-0.1.0/modelroom/profile.py +278 -0
- modelroom-0.1.0/modelroom/protocol_v1.toml +31 -0
- modelroom-0.1.0/modelroom/provenance.py +170 -0
- modelroom-0.1.0/modelroom/quantization.py +308 -0
- modelroom-0.1.0/modelroom/ranking.py +134 -0
- modelroom-0.1.0/modelroom/relation.py +113 -0
- modelroom-0.1.0/modelroom/render.py +454 -0
- modelroom-0.1.0/modelroom/render_cmd.py +243 -0
- modelroom-0.1.0/modelroom/screen.py +500 -0
- modelroom-0.1.0/modelroom/search.py +794 -0
- modelroom-0.1.0/modelroom/search_age.py +130 -0
- modelroom-0.1.0/modelroom/search_apply.py +158 -0
- modelroom-0.1.0/modelroom/search_pages.py +353 -0
- modelroom-0.1.0/modelroom/search_word.py +238 -0
- modelroom-0.1.0/modelroom/state.py +519 -0
- modelroom-0.1.0/modelroom/toml_writer.py +71 -0
- modelroom-0.1.0/modelroom/views.py +775 -0
- modelroom-0.1.0/pyproject.toml +68 -0
- modelroom-0.1.0/tests/conftest.py +20 -0
- modelroom-0.1.0/tests/fixture_support.py +544 -0
- modelroom-0.1.0/tests/fixtures/README.md +293 -0
- modelroom-0.1.0/tests/fixtures/catalog_excerpt.toml +27 -0
- modelroom-0.1.0/tests/fixtures/config_v1/config.toml +32 -0
- modelroom-0.1.0/tests/fixtures/config_v2/config.toml +32 -0
- modelroom-0.1.0/tests/fixtures/export_v1.json +127 -0
- modelroom-0.1.0/tests/fixtures/hf_deepseek_r1_qwen3_8b_config.json +35 -0
- modelroom-0.1.0/tests/fixtures/hf_deepseek_r1_qwen3_8b_model.json +80 -0
- modelroom-0.1.0/tests/fixtures/hf_qwen_qwen35_9b_config.json +103 -0
- modelroom-0.1.0/tests/fixtures/hf_qwen_qwen35_9b_model.json +1 -0
- modelroom-0.1.0/tests/fixtures/hf_search_mistral_publisher.json +51 -0
- modelroom-0.1.0/tests/fixtures/hf_search_mistral_unsloth.json +55 -0
- modelroom-0.1.0/tests/fixtures/hf_search_none.json +1 -0
- modelroom-0.1.0/tests/fixtures/hf_search_qwen38_27b_catalog_page.json +155 -0
- modelroom-0.1.0/tests/fixtures/hf_search_qwen_most_downloaded.json +70 -0
- modelroom-0.1.0/tests/fixtures/hf_search_qwen_newest.json +53 -0
- modelroom-0.1.0/tests/fixtures/hf_search_qwen_page_full.json +402 -0
- modelroom-0.1.0/tests/fixtures/hf_search_qwen_publisher.json +31 -0
- modelroom-0.1.0/tests/fixtures/hf_search_qwen_unsloth.json +71 -0
- modelroom-0.1.0/tests/fixtures/hf_search_qwen_unsloth_downloads.json +62 -0
- modelroom-0.1.0/tests/fixtures/hf_synthetic_dense_config.json +12 -0
- modelroom-0.1.0/tests/fixtures/hf_synthetic_paginated_tree_page1.json +20 -0
- modelroom-0.1.0/tests/fixtures/hf_synthetic_paginated_tree_page2.json +18 -0
- modelroom-0.1.0/tests/fixtures/hf_typed_qwen_qwen35_9b.json +37 -0
- modelroom-0.1.0/tests/fixtures/hf_typed_unsloth_qwen35_9b_gguf.json +30 -0
- modelroom-0.1.0/tests/fixtures/hf_unsloth_deepseek_r1_qwen3_8b_gguf_model.json +66 -0
- modelroom-0.1.0/tests/fixtures/hf_unsloth_deepseek_r1_qwen3_8b_gguf_tree.json +56 -0
- modelroom-0.1.0/tests/fixtures/hf_unsloth_does_not_exist_model.json +10 -0
- modelroom-0.1.0/tests/fixtures/hf_unsloth_qwen35_9b_gguf_model.json +1 -0
- modelroom-0.1.0/tests/fixtures/hf_unsloth_qwen35_9b_gguf_tree.json +1 -0
- modelroom-0.1.0/tests/fixtures/llmfit_system_laptop.json +39 -0
- modelroom-0.1.0/tests/fixtures/llmfit_version.txt +1 -0
- modelroom-0.1.0/tests/fixtures/lspci_nn_cpu_server.txt +6 -0
- modelroom-0.1.0/tests/fixtures/lspci_nn_headless.txt +3 -0
- modelroom-0.1.0/tests/fixtures/lspci_nn_intel_laptop.txt +3 -0
- modelroom-0.1.0/tests/fixtures/lspci_nn_nvidia_laptop.txt +2 -0
- modelroom-0.1.0/tests/fixtures/markdown_view_ap9_c2.md +37 -0
- modelroom-0.1.0/tests/fixtures/measurement_v2_invalid.json +60 -0
- modelroom-0.1.0/tests/fixtures/nvidia_smi_one_gpu.csv +1 -0
- modelroom-0.1.0/tests/fixtures/nvidia_smi_two_gpus.csv +2 -0
- modelroom-0.1.0/tests/fixtures/ollama_generate_valid.json +14 -0
- modelroom-0.1.0/tests/fixtures/ollama_manifest_head_9b.json +7 -0
- modelroom-0.1.0/tests/fixtures/ollama_ps_deepseek_loaded.json +23 -0
- modelroom-0.1.0/tests/fixtures/ollama_qwen35_9b-mlx-bf16.json +1 -0
- modelroom-0.1.0/tests/fixtures/ollama_qwen35_9b-q4_K_M.json +1 -0
- modelroom-0.1.0/tests/fixtures/ollama_qwen35_9b.json +1 -0
- modelroom-0.1.0/tests/fixtures/ollama_show_hf_gguf.json +19 -0
- modelroom-0.1.0/tests/fixtures/ollama_tags_local.json +58 -0
- modelroom-0.1.0/tests/fixtures/ollama_tags_local_loadtest.json +127 -0
- modelroom-0.1.0/tests/fixtures/ollama_tags_qwen35.html +4829 -0
- modelroom-0.1.0/tests/fixtures/ollama_version_local.json +3 -0
- modelroom-0.1.0/tests/fixtures/proc_meminfo_linux.txt +5 -0
- modelroom-0.1.0/tests/fixtures/profiles_v1/linux-cpu-server.json +15 -0
- modelroom-0.1.0/tests/fixtures/profiles_v1/unified-memory.json +15 -0
- modelroom-0.1.0/tests/fixtures/profiles_v1/vram-zero.json +15 -0
- modelroom-0.1.0/tests/fixtures/profiles_v1/windows-nvidia-laptop.json +49 -0
- modelroom-0.1.0/tests/golden/guided-screen-measured.txt +70 -0
- modelroom-0.1.0/tests/golden/guided-screen-nothing.txt +45 -0
- modelroom-0.1.0/tests/golden/guided-screen.txt +65 -0
- modelroom-0.1.0/tests/search_testset.toml +62 -0
- modelroom-0.1.0/tests/test_binding.py +175 -0
- modelroom-0.1.0/tests/test_catalog.py +245 -0
- modelroom-0.1.0/tests/test_cli.py +798 -0
- modelroom-0.1.0/tests/test_config.py +922 -0
- modelroom-0.1.0/tests/test_contracts.py +987 -0
- modelroom-0.1.0/tests/test_daemon.py +268 -0
- modelroom-0.1.0/tests/test_dialog.py +510 -0
- modelroom-0.1.0/tests/test_document.py +108 -0
- modelroom-0.1.0/tests/test_fetch.py +608 -0
- modelroom-0.1.0/tests/test_fit.py +322 -0
- modelroom-0.1.0/tests/test_fit_size.py +198 -0
- modelroom-0.1.0/tests/test_fit_v2.py +182 -0
- modelroom-0.1.0/tests/test_guided.py +1918 -0
- modelroom-0.1.0/tests/test_guided_context.py +309 -0
- modelroom-0.1.0/tests/test_guided_contracts.py +156 -0
- modelroom-0.1.0/tests/test_guided_models.py +663 -0
- modelroom-0.1.0/tests/test_guided_screen.py +104 -0
- modelroom-0.1.0/tests/test_guided_search.py +173 -0
- modelroom-0.1.0/tests/test_hardware_cmd.py +763 -0
- modelroom-0.1.0/tests/test_hf.py +1224 -0
- modelroom-0.1.0/tests/test_http.py +463 -0
- modelroom-0.1.0/tests/test_importer.py +1065 -0
- modelroom-0.1.0/tests/test_intro.py +369 -0
- modelroom-0.1.0/tests/test_language_standard.py +276 -0
- modelroom-0.1.0/tests/test_llmfit.py +475 -0
- modelroom-0.1.0/tests/test_loadtest.py +571 -0
- modelroom-0.1.0/tests/test_measure.py +776 -0
- modelroom-0.1.0/tests/test_measurements.py +381 -0
- modelroom-0.1.0/tests/test_migrate.py +405 -0
- modelroom-0.1.0/tests/test_ollama.py +638 -0
- modelroom-0.1.0/tests/test_ollama_local.py +94 -0
- modelroom-0.1.0/tests/test_profile.py +263 -0
- modelroom-0.1.0/tests/test_provenance.py +708 -0
- modelroom-0.1.0/tests/test_public_hygiene.py +73 -0
- modelroom-0.1.0/tests/test_quantization.py +551 -0
- modelroom-0.1.0/tests/test_ranking.py +209 -0
- modelroom-0.1.0/tests/test_relation.py +88 -0
- modelroom-0.1.0/tests/test_release_check.py +500 -0
- modelroom-0.1.0/tests/test_release_smoke.py +157 -0
- modelroom-0.1.0/tests/test_render.py +884 -0
- modelroom-0.1.0/tests/test_screen.py +425 -0
- modelroom-0.1.0/tests/test_search.py +1752 -0
- modelroom-0.1.0/tests/test_search_pages.py +356 -0
- modelroom-0.1.0/tests/test_search_word.py +252 -0
- modelroom-0.1.0/tests/test_selftest.py +649 -0
- modelroom-0.1.0/tests/test_state.py +886 -0
- modelroom-0.1.0/tests/test_version_single_source.py +41 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.venv/
|
|
9
|
+
|
|
10
|
+
# Local environment and secrets never enter the repository
|
|
11
|
+
.env
|
|
12
|
+
.env.*
|
|
13
|
+
!.env.example
|
|
14
|
+
|
|
15
|
+
# Generated artefacts
|
|
16
|
+
_derived/
|
|
17
|
+
_graph_code_modelroom/
|
|
18
|
+
|
|
19
|
+
# Editor and OS noise
|
|
20
|
+
.idea/
|
|
21
|
+
.vscode/
|
|
22
|
+
.DS_Store
|
|
23
|
+
Thumbs.db
|
|
24
|
+
|
|
25
|
+
# Local safety-net for deletes (a developer-machine hook, not part of the repository)
|
|
26
|
+
.trash/
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Rules for every tool and every person working on this repository. Tool-specific files (if any
|
|
4
|
+
ever appear) import this one and stay thin; the truth lives here.
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
`modelroom` finds the local packages (GGUF and tensor builds) that exist for an allow-list of
|
|
9
|
+
model families on Hugging Face and in the Ollama registry, and computes whether each package
|
|
10
|
+
fits a measured machine. It is a command-line tool and a small library, published under MIT.
|
|
11
|
+
Status: alpha, first release 0.1.0; the data shapes are versioned contracts (`CONTRACTS.md`),
|
|
12
|
+
layout and fit rules may still change.
|
|
13
|
+
|
|
14
|
+
The commands:
|
|
15
|
+
|
|
16
|
+
| Command | What it does |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `modelroom` | the guided mode: asks, writes `modelroom.toml`, then runs the three commands below, and between the fetch and the render offers the load test of the packages this machine already has (`CONTRACTS.md`, "Guided mode", "Load test (stage 1)") |
|
|
19
|
+
| `modelroom --answers <file>` | the same run with the dialog's answers from a TOML file, for a self-test or CI |
|
|
20
|
+
| `modelroom --config <file>` | the guided mode on that configuration, rather than the folder it last used |
|
|
21
|
+
| `modelroom hardware --config <file> [--machine <name>] [--cpu-only] [--new-identity \| --same-machine]` | measure this machine and write its schema-2 profile; `--new-identity` writes a new one, `--same-machine` writes the bound profile again under its own id (`CONTRACTS.md`, "Profile binding") |
|
|
22
|
+
| `modelroom fetch --config <file> --machine <name>` | fetch package metadata for every configured base model |
|
|
23
|
+
| `modelroom render --config <file>` | write the ranking per machine: Markdown, and the JSON view next to it |
|
|
24
|
+
| `modelroom migrate --config <file>` | move schema-1 profiles and configuration to schema 2 |
|
|
25
|
+
| `modelroom export-profile --config <file> [--profile <id>] --out <file>` | write one machine's profile and measurements to a file |
|
|
26
|
+
| `modelroom import-profile <file> --config <file>` | read such a file into this results folder |
|
|
27
|
+
|
|
28
|
+
Only the guided mode ever asks a question; every subcommand runs without a terminal.
|
|
29
|
+
|
|
30
|
+
## Stack and language choice
|
|
31
|
+
|
|
32
|
+
Python (3.11 or newer), because the catalog logic and Pydantic contracts it integrates with are
|
|
33
|
+
Python and the run has to be platform neutral. Dependencies are kept to the standard library,
|
|
34
|
+
`pydantic` and `questionary`; `llmfit` is an external tool called as a subprocess, never
|
|
35
|
+
vendored. Everything in this repository is English; the next section says which English, and
|
|
36
|
+
which words.
|
|
37
|
+
|
|
38
|
+
### Terminal dialog library
|
|
39
|
+
|
|
40
|
+
The guided mode's selection lists with check marks are `questionary` (MIT) on `prompt_toolkit`
|
|
41
|
+
(BSD), imported by `modelroom/dialog.py` and by nothing else -- every other module and all six
|
|
42
|
+
subcommands run without it. No dialog layer is written by hand.
|
|
43
|
+
|
|
44
|
+
The pin follows a gate, never convenience: the candidate has to render and read real keys in
|
|
45
|
+
every terminal a user of this package may sit at. Measured 2026-09-24 with `questionary` 2.1.1
|
|
46
|
+
on `prompt_toolkit` 3.0.53, PowerShell 5.1, key events written into the console input buffer
|
|
47
|
+
(`WriteConsoleInputW` on `CONIN$`, the same buffer a keyboard fills) and read back through
|
|
48
|
+
prompt_toolkit's own Win32 input: Windows console host (`conhost`) **pass**, Windows Terminal
|
|
49
|
+
**pass**. A Linux SSH session with a PTY is not part of that measurement and is open. Without a
|
|
50
|
+
passed gate there is no pin.
|
|
51
|
+
|
|
52
|
+
Package manager is `uv`. `uv.lock` is committed and is the truth for dependency versions;
|
|
53
|
+
install with `uv sync --frozen`. To raise a dependency: `uv lock --upgrade-package <name>`,
|
|
54
|
+
run the tests, commit the lock.
|
|
55
|
+
|
|
56
|
+
## Language standard
|
|
57
|
+
|
|
58
|
+
**US English**, everywhere: identifiers, messages, docstrings, comments, commit messages,
|
|
59
|
+
documentation. A British spelling is a finding, not a style choice -- write `color`,
|
|
60
|
+
`behavior`, `initialize`, `optimize`, `normalize`, `center`, `catalog`, `analyze`, `favor`,
|
|
61
|
+
`license` (noun and verb alike), `modeling`, `labeled`. No German ever reaches a user of the
|
|
62
|
+
package, so none of it is written here either.
|
|
63
|
+
|
|
64
|
+
One word per idea, so that a message, a field value and a paragraph of documentation say the
|
|
65
|
+
same thing in the same way:
|
|
66
|
+
|
|
67
|
+
| Write | For | Never |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `latest`, `legacy` (with its `successor`), `unknown` | where a model stands in its family | `older`, `newer`, `outdated` |
|
|
70
|
+
| `measured`, `entered`, `computed` | where a value came from | "detected", "estimated", "real" |
|
|
71
|
+
| `publisher`, `listed packager`, `other` | who owns a repository | `untrusted`, `trustworthy`, or any other verdict on a packager; only the class is stated |
|
|
72
|
+
| `unknown` | a fact with no evidence behind it | an empty cell, `n/a`, or a plausible guess |
|
|
73
|
+
| `–` | the same fact in a **column of a list or a table**, where the word would read as a statement about the row (decided 2026-09-25) | an empty cell, `n/a`, `-` where the console can encode `–` |
|
|
74
|
+
| `not comparable` | two measurements from different scenarios | "slower", "worse" |
|
|
75
|
+
| `display adapter only` | a GPU that is present but cannot carry a fit | "no GPU" |
|
|
76
|
+
| `none known` | no Ollama name is mapped to this package, in prose and in a log | "missing", "not available"; in a column of a list it is `–`, see above |
|
|
77
|
+
|
|
78
|
+
An age statement is always positive evidence about one model (a successor named at the
|
|
79
|
+
publisher's repository, or the shipped catalog), never a comparison of two version numbers.
|
|
80
|
+
|
|
81
|
+
`tests/test_language_standard.py` is this section in executable form: it reads every module
|
|
82
|
+
under `modelroom/` and the prose files, and names file, line, word and replacement. Its
|
|
83
|
+
exception list carries a reason per entry and has to shrink, never grow quietly.
|
|
84
|
+
|
|
85
|
+
## Working rules
|
|
86
|
+
|
|
87
|
+
- Test first (red, green, refactor). Real dependencies or simple in-memory fakes; no mocking
|
|
88
|
+
frameworks. Network-facing code is tested against recorded fixtures, never against the live
|
|
89
|
+
API in the default test run.
|
|
90
|
+
- Conventional Commits in English: `feat(scope): ...`, `fix(scope): ...`, `docs: ...`,
|
|
91
|
+
`chore: ...`, `test: ...`, `refactor: ...`.
|
|
92
|
+
- YAGNI. No placeholder code, no unimplemented TODOs, no dead code. A function does one thing.
|
|
93
|
+
- Explicit error handling. Every failure that a user can act on becomes a message and an exit
|
|
94
|
+
code; nothing is swallowed.
|
|
95
|
+
- A cross-process lock is a kernel file lock held on a stable file (`msvcrt.locking` on Windows,
|
|
96
|
+
`fcntl.flock` elsewhere, non-blocking), never a create-rename-delete choreography with an age
|
|
97
|
+
rule: the kernel releases a crashed holder's lock, a live holder keeps it regardless of age, and
|
|
98
|
+
the lock file is never renamed or deleted. Three review rounds showed that no file-name
|
|
99
|
+
choreography closes every race; see the "Lock file" section of `CONTRACTS.md`.
|
|
100
|
+
- Exit codes are part of the contract: `0` success (`hardware`: measured and written, even when
|
|
101
|
+
`llmfit` was not there to cross-check the readings), `1` at least one `fetch` area incomplete
|
|
102
|
+
(or a command stopped at another process's lock, or this run is not newer than the stored
|
|
103
|
+
snapshot -- both leave the state directory untouched -- or `hardware` wrote the profile but
|
|
104
|
+
could not bind this machine to it), `2` the configuration is missing or
|
|
105
|
+
invalid, the named machine is not a writer (`fetch`) or not configured at all (`hardware`),
|
|
106
|
+
`hardware`'s bound profile belongs to another machine, or an external tool a command requires
|
|
107
|
+
is missing or too old, `3` an input file has an unsupported schema version. The guided mode
|
|
108
|
+
adds two of its own: `2` as well when there is no terminal and no `--answers`, when the input
|
|
109
|
+
ends, or when an answer is missing or unusable, and `130` when the user presses Ctrl-C.
|
|
110
|
+
Details: `CONTRACTS.md`.
|
|
111
|
+
|
|
112
|
+
## Hard boundaries
|
|
113
|
+
|
|
114
|
+
This repository is public. Nothing operator-specific belongs in it:
|
|
115
|
+
|
|
116
|
+
- No secrets, ever. Not in code, not in tests, not in fixtures, not in the history.
|
|
117
|
+
- No personal names, no e-mail addresses, no absolute paths from anyone's machine. The only
|
|
118
|
+
organization named is the maintainer, in `README.md`, `LICENSE` and `pyproject.toml`.
|
|
119
|
+
- No references to an internal workspace, its folder layout, its personas or its chronicle.
|
|
120
|
+
`tests/test_public_hygiene.py` enforces a negative list over every tracked file and runs
|
|
121
|
+
from the git hooks in `.githooks/`.
|
|
122
|
+
- Configuration is data the user brings (`modelroom.toml` or a `Configuration` object). The
|
|
123
|
+
package never guesses paths relative to the current working directory: paths in a
|
|
124
|
+
configuration file resolve relative to that file, programmatic callers pass absolute paths.
|
|
125
|
+
|
|
126
|
+
## Contracts
|
|
127
|
+
|
|
128
|
+
Shared data shapes are defined once, as Pydantic models in `modelroom/contracts.py`
|
|
129
|
+
(`extra="forbid"`, value ranges, coupled fields validated), and described for readers in
|
|
130
|
+
`CONTRACTS.md` with the same examples. Contract tests keep the two in step. Every persisted
|
|
131
|
+
file (configuration, snapshot, hardware profile) carries a `schema_version`; readers declare
|
|
132
|
+
the range they accept and refuse anything else before they write. See `CONTRACTS.md`.
|
|
133
|
+
|
|
134
|
+
## Documentation chain
|
|
135
|
+
|
|
136
|
+
`docs/` holds the repository's own record, one folder per genre, created with its first real
|
|
137
|
+
entry and never as an empty shell:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
idea -> decision -> execution -> measurement -> operation -> incident
|
|
141
|
+
docs/rfc/ docs/adr/ docs/plans/ docs/slo/ docs/runbooks/ docs/postmortems/
|
|
142
|
+
docs/playbooks/
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
An ADR is immutable; a changed situation gets a new ADR that supersedes the old one. Each
|
|
146
|
+
genre folder carries a `README.md` index with one line per entry. `docs/README.md` is the map.
|
|
147
|
+
|
|
148
|
+
## Security, definition of done
|
|
149
|
+
|
|
150
|
+
Attack surface of this tool: outbound HTTPS to `huggingface.co`, `ollama.com` and `registry.ollama.ai`,
|
|
151
|
+
HTTP to the Ollama daemon on `127.0.0.1:11434` -- five read and generate calls, never `/api/pull`
|
|
152
|
+
or `/api/delete` -- subprocess calls to `llmfit` and `nvidia-smi` with fixed argument lists, and
|
|
153
|
+
file writes under the configured state directory. No user-facing web surface, no uploads, no HTML
|
|
154
|
+
rendering of whatever a registry returns. The tests therefore have to prove, with a deliberately
|
|
155
|
+
broken input each:
|
|
156
|
+
|
|
157
|
+
- a configuration that points the state directory outside its own folder tree is rejected
|
|
158
|
+
before anything is written;
|
|
159
|
+
- responses from the two registries are validated against the contracts and an unexpected
|
|
160
|
+
shape ends the area as incomplete instead of corrupting the snapshot;
|
|
161
|
+
- a snapshot or hardware file with an unsupported `schema_version` is refused before any
|
|
162
|
+
write (exit `3`);
|
|
163
|
+
- a second command against the same state directory stops at the lock instead of waiting;
|
|
164
|
+
- the `llmfit` subprocess is called with a fixed argument list, never with user-controlled
|
|
165
|
+
strings, and a missing or too old `llmfit` ends with exit `2` and an install hint;
|
|
166
|
+
- the daemon transport refuses every origin but `http://127.0.0.1:11434` and every call outside
|
|
167
|
+
its five, carries no proxy handler and follows no redirect, and no module of the package names
|
|
168
|
+
`/api/pull` or `/api/delete`.
|
|
169
|
+
|
|
170
|
+
## Versioning and releases
|
|
171
|
+
|
|
172
|
+
One version source: `[project] version` in `pyproject.toml`, Semver, starting at `0.1.0`.
|
|
173
|
+
`modelroom.__version__` reads it from package metadata; a test fails if a second version
|
|
174
|
+
literal appears anywhere. Releases are annotated tags `vX.Y.Z` on `main`. Release procedure:
|
|
175
|
+
|
|
176
|
+
1. Gate: `uv run --frozen python scripts/release-check.py` ends with `RELEASE-CHECK: OK <version>`.
|
|
177
|
+
2. Smoke: `uv run --frozen python scripts/release-smoke.py` ends with `RELEASE-SMOKE: OK`.
|
|
178
|
+
3. Tag: `git tag -a vX.Y.Z -m "modelroom X.Y.Z"`, then the gate again with `--tag vX.Y.Z`.
|
|
179
|
+
4. Review: an independent second-model review of the release diff, findings closed or answered.
|
|
180
|
+
5. Upload: `uv build` into an empty folder and `uv publish` by the maintainer, then push the tag.
|