Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7bfa4da
Release 1.7.2
EHxuban11 Nov 3, 2026
ab7b206
Free runner disk for the [all] install job and install without pip's …
EHxuban11 Nov 3, 2026
a75e853
Match smoke-test detections one to one so duplicates count as extras …
EHxuban11 Nov 3, 2026
d80bd6c
Merge pull request #992 from LibreYOLO/release-1.7.2
EHxuban11 Nov 3, 2026
bb97084
Quantize TFLite INT8 once instead of twice
milidam Nov 4, 2026
3cc5095
Merge pull request #1003 from milidam/tflite-int8-single-pass
EHxuban11 Nov 5, 2026
c867272
Release 1.7.3: Hub repo-id loading works on a plain install
EHxuban11 Nov 6, 2026
122d2b8
Update the Hub guide for 1.7.3: core dependency and repo-named checkp…
EHxuban11 Nov 6, 2026
e3cc993
Split the TFLite per-channel attribute name for the vocabulary check
EHxuban11 Nov 7, 2026
e4f7647
Merge pull request #1020 from LibreYOLO/hotfix-1.7.3-hub-loading
EHxuban11 Nov 7, 2026
3efc007
Release 1.7.4: extras no longer downgrade LibreYOLO on new Pythons
EHxuban11 Nov 7, 2026
d4216dd
Update dependency tests for the Python version markers
EHxuban11 Nov 7, 2026
275489b
Explain the 3.14 TensorRT skip; pin Python in the CoreML platform test
EHxuban11 Nov 7, 2026
20a6a97
Merge pull request #1026 from LibreYOLO/hotfix-1.7.4-install-fixes
EHxuban11 Nov 7, 2026
327f252
Merge release into dev: port the 1.7.3 and 1.7.4 fixes
EHxuban11 Nov 11, 2026
4f1b1eb
Merge dev (#1036) into the port branch
EHxuban11 Nov 11, 2026
ab83e60
Merge remote-tracking branch 'origin/dev' into chore/port-1.7.3-1.7.4…
EHxuban11 Nov 11, 2026
8479be7
Validate TFLite INT8 options before preflight touches the model; scop…
EHxuban11 Nov 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .github/workflows/dependency-resolution.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,24 @@ jobs:
if: runner.os == 'Linux'
run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV"

# [all] with CUDA torch is several GB on Linux; the runner image's
# unused SDKs leave too little room on some runs.
- name: Free disk space
if: runner.os == 'Linux'
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
df -h /

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

# Plain pip on purpose: this is the path users take.
# Plain pip on purpose: this is the path users take. No cache: it would
# keep a second copy of every wheel on the runner's disk.
- name: pip install ".[all]"
run: |
python -m pip install --upgrade pip
python -m pip install --progress-bar off ".[all]"
python -m pip install --no-cache-dir --progress-bar off ".[all]"

# decord 0.6.0's Linux wheel is named py3-none but its WHEEL file
# declares cp36-cp36m, so pip check flags it on every other Python
Expand Down
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,46 @@ before 1.4.0 are documented in the
- **`DepthMap` can carry camera geometry.** New optional keyword-only fields `metric`, `point_map`, `intrinsics`, and `valid_mask` (ADR 0006 amendment). Existing depth families are unchanged.
- **MoGe-2 no longer claims MoGe-3 checkpoints**, which share its encoder, neck, and head names.

## [1.7.4] - 2026-11-07

### Fixed

- **Extras no longer install an old LibreYOLO on Python 3.13 and 3.14.**
On Python 3.14, `pip install "libreyolo[all]"` installed LibreYOLO 1.2.0, `[tflite]` 1.2.0, `[litert]` 1.3.1 and `[executorch]` 1.7.0; `[paddle]` installed 1.4.0 on Python 3.13 and 3.14. A pinned dependency had no wheels for that Python, so pip went back through older releases until one resolved. Environment markers now skip those dependencies there, and the current release installs without that backend: the onnx2tf TFLite converter on 3.14 (it pins ai-edge-litert 2.1.2 and numpy 2.2.6; TFLite inference still installs), ExecuTorch 1.2 on 3.14, paddlepaddle 2.6.2 and X2Paddle on 3.13+, `tensorrt-cu12` 10.16 on 3.14, and coremltools on 3.14. Using a skipped backend says which Python it needs. The dependency-resolution CI check now covers Python 3.14.

- **Missing-extra messages quote the install command.**
Messages such as `pip install libreyolo[rfdetr]` failed in zsh, the macOS default shell, which reads the brackets as a glob. All of them now read `pip install "libreyolo[rfdetr]"`, including the hints in `libreyolo models`.

- **ONNX export without the `onnx` extra points at `pip install "libreyolo[onnx]"`.**
It suggested `uv sync --extra onnx`, which only works in a repo clone, or `pip install onnx`, which leaves out onnxruntime and onnxsim.

### Changed

- **README: CPU-only install on Linux, a Hub example, and links that work on PyPI.**
The README, also the PyPI page, shows how to install the CPU build of PyTorch on Linux to skip the CUDA wheels, shows `LibreYOLO("LibreYOLO/LibreYOLO9t")`, and uses absolute links and image paths. Same in the Chinese README.

## [1.7.3] - 2026-11-07

### Fixed

- **`LibreYOLO("owner/repo")` works on a plain `pip install libreyolo`.**
`huggingface_hub` is now a core dependency, so loading a model by its Hugging Face repo id no longer needs the `hf` extra. The extra still exists for pushing to the Hub and gated downloads.

- **Loading `LibreYOLO/LibreYOLO9t`, `LibreYOLO/LibreYOLOXs` and the other YOLO9 and YOLOX repos by repo id.**
These repos also hold a legacy lowercase checkpoint, so the loader stopped with "multiple checkpoints". When a repo holds several checkpoints, the one named after the repo is now chosen.

- **A `libreyolo/` folder in the working directory no longer hides Hub repo ids on macOS and Windows.**
The check for a local `LibreYOLO/` folder ignored case on those filesystems, so running from a folder containing a LibreYOLO clone made `LibreYOLO("LibreYOLO/<repo>")` look for a local file.

### Changed

- **TFLite INT8 export quantizes once instead of twice.**
`onnx2tf -oiqt` builds the integer model twice — float32 I/O, then int8 I/O — and only the second is shipped, although the I/O dtype does not change the calibrated scales. `export(format="tflite", int8=True, ...)` now builds the SavedModel with onnx2tf and runs a single in-process `TFLiteConverter` pass; `int8_backend="onnx2tf"` keeps the previous route, and `int8_output_dtype="float32"` writes float outputs for host-dequantizing runtimes (in-process backend only).

## [1.7.2] - 2026-11-03

### Changed

- **`libreyolo[all]` no longer includes `paddle`. (#989)**
X2Paddle needs ONNX 1.17 or earlier while the TFLite exporter in `all` needs ONNX 1.20, so the two cannot share an environment. Install `libreyolo[paddle]` on its own; Paddle export needs Python 3.10 or 3.11.

Expand Down
14 changes: 10 additions & 4 deletions docs/hf_hub.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ LibreYOLO can load checkpoints from any Hugging Face Hub repository and push
your own fine-tuned checkpoints back, in both cases using the standard
LibreYOLO metadata schema (v1.0, see `docs/checkpoint_schema.md`).

The integration is optional:
Loading from the Hub works on a plain `pip install libreyolo` (1.7.3 and
newer): `huggingface_hub` is a core dependency. Pushing to the Hub and gated
downloads need the `hf` extra, which requires `huggingface_hub` 1.0 or newer:

```bash
pip install "libreyolo[hf]"
Expand All @@ -28,16 +30,20 @@ model = LibreYOLO("hf://someuser/repo@main/best.pt") # pin revision and file
Resolution rules:

- A local file or directory with the same name always wins over the bare
`owner/repo` form. Use the `hf://` form to bypass that precedence.
`owner/repo` form. Use the `hf://` form to bypass that precedence. The
folder check is case-sensitive, so a `libreyolo/` folder does not hide
`LibreYOLO/<repo>` on macOS or Windows.
- The bare form is only treated as a Hub reference when the last segment has
no file extension, so paths like `weights/model.pt` or
`ckpts/model_best.pth.tar` keep their meaning. A dot followed by a digit is
a version, not an extension: `owner/model-v1.5` is still a Hub reference.
`librefacerec-*` names such as `models/librefacerec-l` are never Hub
references; they auto-download from the LibreYOLO org as before.
- Without an explicit filename, the single `*.pt` (or `*.safetensors`) file
in the repo is used. Repos with several checkpoints raise an error listing
them; select one with `hf://owner/repo/<filename>`.
in the repo is used. When there are several, the one named after the repo
is chosen (`LibreYOLO/LibreYOLO9t` loads `LibreYOLO9t.pt`). Otherwise the
load raises an error listing them; select one with
`hf://owner/repo/<filename>`.
- Some repos in the `LibreYOLO` org are not `LibreYOLO()` checkpoints:
vision-language, OCR, SAM, open-vocabulary, generation and 3D models, plus
pipeline components and retired families. They are listed in
Expand Down
9 changes: 8 additions & 1 deletion libreyolo/backends/coreml.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,16 @@ def __init__(
try:
import coremltools as ct
except ImportError as e:
if sys.version_info >= (3, 14):
raise ImportError(
"CoreML inference needs Python 3.13 or earlier: "
"coremltools has no wheels for Python "
f"{sys.version_info[0]}.{sys.version_info[1]} yet, so "
'`pip install "libreyolo[coreml]"` skips it here.'
) from e
raise ImportError(
"CoreML inference requires coremltools. "
"Install with: pip install libreyolo[coreml]"
'Install with: pip install "libreyolo[coreml]"'
) from e

path = Path(model_path)
Expand Down
10 changes: 9 additions & 1 deletion libreyolo/backends/executorch.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import json
import logging
import sys
from pathlib import Path

import numpy as np
Expand Down Expand Up @@ -36,9 +37,16 @@ def __init__(
try:
from executorch.runtime import Runtime
except (ImportError, OSError) as exc:
if sys.version_info >= (3, 14):
raise ImportError(
"ExecuTorch inference needs Python 3.13 or earlier: "
"executorch 1.2 has no wheels for Python "
f"{sys.version_info[0]}.{sys.version_info[1]} yet, so "
'`pip install "libreyolo[executorch]"` skips it here.'
) from exc
raise ImportError(
"ExecuTorch inference requires the optional runtime. "
"Install it with: pip install libreyolo[executorch]"
'Install it with: pip install "libreyolo[executorch]"'
) from exc

if str(device).lower() not in {"auto", "cpu"}:
Expand Down
2 changes: 1 addition & 1 deletion libreyolo/backends/mnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def import_mnn(purpose: str):
) from exc
raise ImportError(
f"{purpose} requires the optional MNN package. "
"Install with: pip install libreyolo[mnn]"
'Install with: pip install "libreyolo[mnn]"'
) from exc
return MNN

Expand Down
10 changes: 9 additions & 1 deletion libreyolo/backends/paddle.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import logging
import sys
from pathlib import Path

import numpy as np
Expand Down Expand Up @@ -49,9 +50,16 @@ def __init__(
try:
import paddle.inference as paddle_infer
except ImportError as exc:
if sys.version_info >= (3, 13):
raise ImportError(
"Paddle inference needs Python 3.12 or earlier: "
"paddlepaddle 2.6.2 has no wheels for Python "
f"{sys.version_info[0]}.{sys.version_info[1]}, so "
'`pip install "libreyolo[paddle]"` skips it here.'
) from exc
raise ImportError(
"Paddle inference requires paddlepaddle. Install with: "
"pip install libreyolo[paddle]"
'pip install "libreyolo[paddle]"'
) from exc

metadata_path = path / "metadata.yaml"
Expand Down
5 changes: 4 additions & 1 deletion libreyolo/backends/tensorrt.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,12 @@ def __init__(
try:
import tensorrt as trt
except ImportError as e:
from ..export.tensorrt import tensorrt_python_version_note

note = tensorrt_python_version_note()
raise ImportError(
"TensorRT inference requires tensorrt. "
"Install with: pip install tensorrt"
+ (note or "Install with: pip install tensorrt")
) from e

if not torch.cuda.is_available():
Expand Down
2 changes: 1 addition & 1 deletion libreyolo/backends/tflite.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def __init__(
except ImportError as exc:
raise ImportError(
"TFLite inference requires ai-edge-litert. "
"Install with: pip install libreyolo[tflite]"
'Install with: pip install "libreyolo[tflite]"'
) from exc

metadata_path = Path(str(path) + ".json")
Expand Down
2 changes: 1 addition & 1 deletion libreyolo/cli/command_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ def resolve_model_or_exit(out: OutputHandler, model: str) -> str:
out,
"model_load_failed",
f"'{model}' needs RF-DETR support, which requires extra dependencies.",
suggestion="pip install libreyolo[rfdetr]",
suggestion='pip install "libreyolo[rfdetr]"',
)

from libreyolo.utils.ultralytics_hints import MODELS_URL, ultralytics_weight_hint
Expand Down
2 changes: 1 addition & 1 deletion libreyolo/cli/commands/special.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ def models_cmd(
"python_class": metadata["class"],
"available": metadata["available"],
"optional_extra": extra,
"install_hint": f"pip install libreyolo[{extra}]" if extra else None,
"install_hint": f'pip install "libreyolo[{extra}]"' if extra else None,
}
)

Expand Down
4 changes: 2 additions & 2 deletions libreyolo/export/coreai.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,15 +375,15 @@ def _require_coreai():
"Core AI export requires Python 3.11 or newer: coreai-torch is not "
"published for Python "
f"{sys.version_info[0]}.{sys.version_info[1]}, so "
"`pip install libreyolo[coreai]` installs nothing here. Use a "
'`pip install "libreyolo[coreai]"` installs nothing here. Use a '
"Python 3.11+ environment on macOS."
)
try:
from coreai_torch import TorchConverter, get_decomp_table
except ImportError as exc: # pragma: no cover - environment dependent
raise ImportError(
"Core AI export requires the 'coreai-torch' package. Install it "
"with `pip install libreyolo[coreai]`. Note that coreai-torch "
'with `pip install "libreyolo[coreai]"`. Note that coreai-torch '
"pins torch to 2.11.x, and that Core AI artifacts can only be "
"produced and executed on macOS."
) from exc
Expand Down
11 changes: 9 additions & 2 deletions libreyolo/export/coreml.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,17 @@ def _require_coremltools():
import coremltools
except ImportError as exc:
machine = platform.machine().lower()
if sys.platform == "darwin" or (
if sys.version_info >= (3, 14):
hint = (
"coremltools has no wheels for Python "
f"{sys.version_info[0]}.{sys.version_info[1]} yet, so "
"`libreyolo[coreml]` does not install it here. Use Python "
"3.13 or earlier."
)
elif sys.platform == "darwin" or (
sys.platform.startswith("linux") and machine == "x86_64"
):
hint = "Install it with `pip install libreyolo[coreml]`."
hint = 'Install it with `pip install "libreyolo[coreml]"`.'
else:
# The coreml extra's marker skips coremltools here (#989).
hint = (
Expand Down
10 changes: 9 additions & 1 deletion libreyolo/export/executorch.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import importlib.metadata
import json
import os
import sys
import tempfile
from contextlib import contextmanager
from pathlib import Path
Expand Down Expand Up @@ -44,9 +45,16 @@ def check_executorch_available() -> None:
)
from executorch.exir import to_edge_transform_and_lower # noqa: F401
except (ImportError, OSError) as exc:
if sys.version_info >= (3, 14):
raise ImportError(
"ExecuTorch export needs Python 3.13 or earlier: executorch "
"1.2, the release LibreYOLO is validated with, has no wheels "
f"for Python {sys.version_info[0]}.{sys.version_info[1]} yet, "
'so `pip install "libreyolo[executorch]"` skips it here.'
) from exc
raise ImportError(
"ExecuTorch export requires the optional ExecuTorch toolchain. "
"Install it with: pip install libreyolo[executorch]"
'Install it with: pip install "libreyolo[executorch]"'
) from exc


Expand Down
6 changes: 5 additions & 1 deletion libreyolo/export/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ def _preflight(self, *, half: bool, int8: bool, data: Optional[str], **kwargs):
if self.requires_onnx and importlib.util.find_spec("onnx") is None:
raise ImportError(
"ONNX export requires the 'onnx' package. "
"Install with: uv sync --extra onnx or pip install onnx"
'Install with: pip install "libreyolo[onnx]"'
)

def _square_fallback_for_restored_rect(
Expand Down Expand Up @@ -2784,6 +2784,8 @@ def _export(
calibration_data,
verbose,
onnx2tf_args=None,
int8_backend: str = "converter",
int8_output_dtype: str = "int8",
**kwargs,
):
from .tflite import ensure_tflite_family_supported, export_tflite
Expand All @@ -2803,6 +2805,8 @@ def _export(
verbose=verbose,
onnx2tf_args=onnx2tf_args,
metadata=metadata,
int8_backend=int8_backend,
int8_output_dtype=int8_output_dtype,
)


Expand Down
4 changes: 2 additions & 2 deletions libreyolo/export/mnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def _find_mnnconvert() -> Path:
return found.resolve()
raise ImportError(
"MNN export requires the mnnconvert tool shipped by the MNN package. "
"Install with: pip install libreyolo[mnn]"
'Install with: pip install "libreyolo[mnn]"'
)


Expand All @@ -51,7 +51,7 @@ def check_mnn_available() -> Path:
except importlib.metadata.PackageNotFoundError as exc:
raise ImportError(
"MNN export requires the optional MNN package. "
"Install with: pip install libreyolo[mnn]"
'Install with: pip install "libreyolo[mnn]"'
) from exc
return _find_mnnconvert()

Expand Down
2 changes: 1 addition & 1 deletion libreyolo/export/ncnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def check_ncnn_export_available() -> None:
"Installation:\n"
" pip install pnnx\n\n"
"Or install with the libreyolo extras:\n"
" pip install libreyolo[ncnn]"
' pip install "libreyolo[ncnn]"'
)


Expand Down
8 changes: 4 additions & 4 deletions libreyolo/export/onnx.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def export_onnx(
if importlib.util.find_spec("onnx") is None:
raise ImportError(
"ONNX export requires the 'onnx' package. "
"Install with: uv sync --extra onnx or pip install onnx"
'Install with: pip install "libreyolo[onnx]"'
)

task = metadata.get("task")
Expand Down Expand Up @@ -690,19 +690,19 @@ def check_onnx_int8_available() -> None:
if importlib.util.find_spec("onnx") is None:
raise ImportError(
"ONNX INT8 export requires the 'onnx' package. "
"Install with: uv sync --extra onnx or pip install onnx"
'Install with: pip install "libreyolo[onnx]"'
)
if importlib.util.find_spec("onnxruntime") is None:
raise ImportError(
"ONNX INT8 export requires the 'onnxruntime' package. "
"Install with: uv sync --extra onnx or pip install onnxruntime"
'Install with: pip install "libreyolo[onnx]"'
)
try:
from onnxruntime.quantization import quantize_static # noqa: F401
except ImportError as exc:
raise ImportError(
"ONNX INT8 export requires ONNX Runtime quantization support. "
"Install with: uv sync --extra onnx or pip install onnxruntime"
'Install with: pip install "libreyolo[onnx]"'
) from exc


Expand Down
2 changes: 1 addition & 1 deletion libreyolo/export/openvino.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def check_openvino_available() -> None:
"Installation:\n"
" pip install openvino\n\n"
"Or install with the libreyolo extras:\n"
" pip install libreyolo[openvino]"
' pip install "libreyolo[openvino]"'
)


Expand Down
Loading
Loading