llmfit/llmfit-python/pyproject.toml
Alex Jones c9a567883e
fix(ollama): stop one sized install marking a whole model family installed (#863)
* fix(ollama): stop one sized install marking a whole model family installed

`build_installed_set` inserted the bare family stem alongside every tag, and
the heuristic half of `hf_name_to_ollama_candidates` offered that same bare
stem as a candidate for any model with no `OLLAMA_MAPPINGS` entry. The two met
in the middle: a single `qwen3:8b` marked 238 of the 9,250 catalog entries
installed — `Qwen3-235B-A22B` and hundreds of community fine-tunes among them.
Because "installed" gates the download action, those models became
undownloadable in the TUI (discussion #861).

Size is now the discriminator:

- A sized install contributes its tag and nothing else. `qwen3:8b` says exactly
  which weights are on disk.
- Only an untagged / `:latest` install contributes a family stem, plus the
  sized alias implied by the parameter count Ollama already reports in
  `/api/tags` (`qwen3:latest` → "8.2B" → `qwen3:8b`), so it still resolves to
  one model rather than a family.
- A candidate derived from a sized HF name no longer includes the bare family.
- Size-less candidates — `OLLAMA_MAPPINGS` tags like `phi-4` → `phi4`, or HF
  names with no size to parse — now match any tag of that family, which is what
  keeps `phi4:14b` detecting `microsoft/phi-4`.

The bare-family candidate came in with #492 (fixing #481, "installed flag
always false for models without explicit mapping"); this keeps that fix and
drops its blast radius. Same over-match seen from another angle in #619.

Regression cover: every one of the 134 `OLLAMA_MAPPINGS` entries is still
detected from its own tag, and the reported scenario asserts its siblings stay
uninstalled.

* fix(ollama): also alias the verbatim parameter size for `:latest` installs

`qwen2.5:14b` reports "14.8B", so a `:latest` install has to be aliased to the
truncated marketing size. But some families are tagged with the decimal itself
— `solar:10.7b`, `qwen3:1.7b`, `lfm2:1.2b`, 20 entries in `OLLAMA_MAPPINGS` —
and for those the truncated alias alone reports the model as absent.

Emit both forms. They stay inside the family that is genuinely installed, so
the extra alias cannot resurrect the cross-family match this branch removes;
one of the two is simply dead weight per install.

* fix(python): read the package readme through the metadata hook

`readme = "../README.md"` is rejected by current hatchling ("Readme path must
be within the project directory"), which fails `uv sync` and takes the Test
Suite job down on all three platforms — including on main, independently of
this branch.

The README belongs at the repository root and should not be duplicated or
symlinked into `llmfit-python` (a symlink would checkout as a text stub on
Windows runners). Hatchling accepts readme *contents* without a path
constraint, so the existing custom metadata hook now supplies them: `readme`
joins `version` and `license-expression` as dynamic metadata.

Verified the built metadata is unchanged in substance — `Description-Content-Type:
text/markdown` with the full README body.
2026-08-11 13:18:38 +01:00

84 lines
2.6 KiB
TOML

[project]
name = "llmfit"
# `readme` is dynamic because it lives in the repository root, outside this
# project directory — hatchling rejects a `../README.md` path but accepts the
# text the metadata hook reads from there. See `hatch_build.py`.
dynamic = ["version", "license-expression", "readme"]
license-files = ["LICENSE"]
description = "Hundreds of models & providers. One command to find what runs on your hardware."
authors = [
{ name = "Alex Jones", email = "alexsimonjones@gmail.com" }
]
requires-python = ">=3.8"
keywords = ["llm", "cli", "machine-learning", "model-management"]
classifiers = [
"Programming Language :: Python :: 3",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
]
[project.urls]
Homepage = "https://github.com/AlexsJones/llmfit"
"Bug Tracker" = "https://github.com/AlexsJones/llmfit/issues"
[build-system]
requires = ["hatchling", "packaging", "tomli"]
build-backend = "hatchling.build"
[tool.hatch.metadata.hooks.custom]
path = "hatch_build.py"
[tool.hatch.build.hooks.custom]
path = "hatch_build.py"
[dependency-groups]
dev = [
"hatchling",
"ipython>=8.12.3",
"pytest>=8",
"ruff>=0.15.8",
"tomli",
"ty>=0.0.27",
]
[tool.pytest.ini_options]
testpaths = ["tests"]
markers = [
"rust_integration: marks tests that exercise the compiled Rust binary (run when Rust code changes)",
]
[tool.ruff]
line-length = 120
[tool.ruff.lint]
select = ["ALL"]
ignore = [
"E501", # Handled by ruff format (line-too-long)
"D100", # Missing docstring in public module
"D104", # Missing docstring in public package
"D203", # Conflicts with D211
"D212", # Conflicts with D213 (code uses second-line summary style)
"S603", # Gave too many false positives with safe uses of subprocess.run
"COM812", # This rule is redundant when using the formatter, which enforces trailing commas
"TD002", # Don't need author info on todo items
"TD003", # Don't need issues links on todo items
]
[tool.ruff.lint.per-file-ignores]
"hatch_build.py" = [
"INP001", # Standalone build script, not a package module
"T201", # Calls to print() are intentional in our build hooks
"TRY003", # Inline exception messages are fine in a build utility
"EM101", # In-line exception messages fine in a build utility
"EM102", # Same as EM101, but with f-strings
]
"tests/*" = [
"S101", # Assert statements are allowed in tests
"INP001", # No __init__.py needed in tests/
"PLR2004", # Magic values are fine in tests
]
[tool.ruff.lint.pydocstyle]
convention = "numpy"
[tool.ty.environment]
python-version = "3.8"