Compare commits

...
Sign in to create a new pull request.

107 commits

Author SHA1 Message Date
LeoBorcherding
910a657d80 pip: cite the 0.50.0 ROCm work accurately in the bnb floor comment
The comment credited bnb PR #1887 as "the ROCm 4-bit GEMV fix" for every
AMD GPU. #1887 decouples blocksize from warp size and fixes a hardcoded
warp size of 32 in kgemm_4bit_inference_naive, which is a CDNA problem by
construction. The RDNA-side work is #1979 (fused 4-bit SIMT GEMM) and
#2012 (RDNA3/4 workgroup resonance). All three first ship in 0.50.0, so
the >=0.50.0 floor is unchanged; only the justification was wrong.
2026-07-28 13:48:39 -05:00
LeoBorcherding
c254d82f9d Merge branch 'pip' into fix/pip-amd-extra 2026-07-27 23:04:34 -05:00
LeoBorcherding
1e3190e2e2 pip: require bitsandbytes>=0.50.0 in the amd extra
bnb <= 0.49.2 NaNs at decode shape on every AMD GPU. The ROCm 4-bit GEMV
fix (bnb PR #1887) merged after 0.49.2 and its merge commit is an ancestor
of the 0.50.0 tag, so 0.50.0 is the first PyPI release carrying it.

The PyPI wheels ship the ROCm binaries for the platforms these markers
select: libbitsandbytes_rocm{64,70,71,714,72}.so on manylinux x86_64 and
libbitsandbytes_rocm{72,714}.dll on win_amd64. 0.49.1 already required
Python >=3.10, same as 0.50.0, so the Python floor is unchanged.
2026-07-27 17:25:29 -05:00
Daniel Han
2bea7862ef
pip: declare click, which typer no longer pulls in (#7504)
unsloth_cli/commands/start.py imports click at module scope, and
unsloth_cli/__init__.py imports that module, so every unsloth command needs
it. typer carried click through 0.19 and dropped it in 0.27, and the declared
floor is typer>=0.12.0, so a fresh resolve gets none. The wheel still works
today only because huggingface_hub requires click<9,>=8.4.2, which is luck
rather than a declaration.

Verified on a wheel built from a dependency list without that transitive
provider: every command, including `unsloth --help`, died with
ModuleNotFoundError for click. Same class as the structlog gap in #7493, so
the drift test covers both now.
2026-07-27 06:50:10 -07:00
Daniel Han
5ab9e7dbd4 Merge branch 'pip' into fix/pip-amd-extra
Resolves 85 conflicted files, all of which trace to the branch's
pre-commit.ci commit 18673a5 rather than to any change this PR authored.

Why the code side takes 'pip':

At the merge base the pip branch's [tool.ruff] table had no line-length,
so ruff-format fell back to its 88 column default and the pre-commit.ci
run reformatted 682 files to 88 columns. Since then 3455b45 ("pip: track
main's packaging and tooling config") added line-length = 100 to pip,
matching main. The bot commit is therefore churn produced under a tooling
config the base branch has since corrected, and re-running the hook on the
merged tree reproduces pip's 100 column formatting, not the branch's.

It also carries no behaviour: all 682 files it touches are .py, and every
one of them is AST identical before and after, so dropping the reformat
drops formatting only. The resolved tree is byte identical to pip
everywhere except pyproject.toml.

pyproject.toml (no textual conflict, but two semantic points):

- torch>=2.4.0,<2.11.0 -> <2.12.0 was made independently on both sides,
  by this PR in cd50f9e and on pip in b22b243, with the identical value.
  The merged file keeps <2.12.0, so neither side is undone. This also
  matches the exclusive _TORCH_CEILING="2.12.0" that #7256 introduces in
  install.sh, which expands to torch>=2.4,<2.12.0.

- The huggingfacenotorch and amd extras are this PR's remaining novel
  content; pip has neither. They are re-applied on top of pip's file.
  The one deliberate rewrite is huggingfacenotorch's unsloth_zoo floor,
  from >=2026.7.4 to >=2026.7.6: b22b243 raised every other unsloth_zoo
  pin on pip to 2026.7.6, and main's own huggingfacenotorch already says
  2026.7.6, so keeping 2026.7.4 would have reintroduced a stale floor that
  the base branch had just retired. The amd extra is unchanged from main.

Line level preservation was checked in both directions: all 90477 lines
pip added since the merge base survive, and all 26 lines this branch added
survive except the single unsloth_zoo floor noted above.
2026-07-27 13:37:06 +00:00
Daniel Han
735fcde44c
pip: declare the Studio dependencies the wheel's own modules import (#7493)
* pip: declare the Studio dependencies the wheel's own modules import

The wheel packages studio/ and studio.backend*, so pip install unsloth puts
five commands on PATH -- train, export, chat, inference, studio -- and every one
of them imports studio.backend.*. None of those imports were declared, so all
five ended in a rich traceback at ModuleNotFoundError: No module named
'structlog' (#4701, #5260, #7147). --help rendered fine for all of them because
typer defers the import, which is why this went unnoticed.

Walking module-level, non-try-guarded imports from each entry point shows
structlog is the only hard requirement they share, once starlette's
annotation-only import in loggers/handlers.py moves under TYPE_CHECKING. So
structlog becomes a core dependency and the rest of the server stack (fastapi,
uvicorn, matplotlib, pandas, pymupdf, ...) becomes a [studio] extra mirroring
studio/backend/requirements/studio.txt, with a test that fails if the two drift.

pip install unsloth           -> train / export work
pip install "unsloth[studio]" -> the server works

* Apply ruff-format quote normalisation

* Trim the comments added in this PR
2026-07-27 03:44:07 -07:00
Daniel Han
8d38165771 Merge branch 'main' into pip 2026-07-27 10:34:51 +00:00
Daniel Han
3455b45977 pip: track main's packaging and tooling config
The merge keeps this branch's own dependencies and extras, which are
deliberately reduced, but the rest of pyproject.toml should not diverge.
Missing here and present on main:

- unsloth_cli package-data, node_prebuilt_pins.json and the backend/assets
  jinja/html globs, so the wheel was not shipping files the code loads
- ruff line-length and pytest pythonpath

test_pins_manifest_is_declared_in_package_data now passes on this branch.
2026-07-27 10:25:55 +00:00
Daniel Han
b1b499c743 Merge branch 'main' into pip 2026-07-27 10:17:31 +00:00
Daniel Han
b22b243e1b Update pyproject.toml 2026-07-23 03:07:15 -07:00
Daniel Han
ef4894c559 Merge branch 'main' into pip 2026-07-23 03:05:55 -07:00
pre-commit-ci[bot]
18673a562a [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-21 23:18:50 +00:00
Daniel Han
cd50f9e51d pip: allow torch<2.12 and add amd/huggingfacenotorch extras
The released torch>=2.4.0,<2.11.0 pin excludes the AMD 2.11.0+rocm wheel, so a
follow-up pip resolve downgrades torch to 2.10.0 and breaks the ROCm install
(#7275). Raise the ceiling to <2.12.0 so the ROCm 2.11 wheel is preserved, and
add an amd extra (pulls huggingfacenotorch, no torch pin) plus a torch-free
huggingfacenotorch extra so AMD users keep their ROCm torch.

[pre-commit.ci skip]
2026-07-21 16:18:20 -07:00
Daniel Han
6c3c01c0c9 Update pyproject.toml 2026-07-20 07:08:29 -07:00
Daniel Han
5dec1284eb Merge branch 'main' into pip 2026-07-20 07:08:24 -07:00
Daniel Han
7beecd279e Update pyproject.toml 2026-07-15 11:43:23 -07:00
Daniel Han
75f831616e Merge branch 'main' into pip 2026-07-15 11:42:44 -07:00
Daniel Han
4373fdbaee Merge branch 'main' into pip 2026-07-08 07:38:08 -07:00
Daniel Han
3f55e7a98c Update pyproject.toml 2026-07-08 07:20:22 -07:00
Daniel Han
a9b3321915 Merge branch 'main' into pip 2026-07-08 07:20:07 -07:00
Daniel Han
a42720636b Merge branch 'main' into pip 2026-07-07 07:44:05 -07:00
Daniel Han
2f111da0f6 Update pyproject.toml 2026-07-07 06:20:52 -07:00
Daniel Han
2c9587d9fc Merge branch 'main' into pip 2026-07-07 06:20:44 -07:00
Daniel Han
c436f3ca67 Merge branch 'main' into pip 2026-06-22 09:06:48 -07:00
Daniel Han
12f34025dc Update pyproject.toml 2026-06-22 09:01:36 -07:00
Daniel Han
5e5c6c8fed Merge branch 'main' into pip 2026-06-22 09:00:34 -07:00
Daniel Han
287243a8fd Update pyproject.toml 2026-06-18 10:37:48 -07:00
Daniel Han
4aa7dd5b18 Merge branch 'main' into pip 2026-06-18 10:37:40 -07:00
Daniel Han
c9aa094c91 Update pyproject.toml 2026-06-13 04:43:58 -07:00
Daniel Han
1b1807ef9a Merge branch 'main' into pip 2026-06-13 04:43:50 -07:00
Daniel Han
a370521879 Merge branch 'main' into pip 2026-06-12 11:20:56 -07:00
Daniel Han
b712f9f557 Merge branch 'main' into pip 2026-06-12 07:37:43 -07:00
Daniel Han
667467a315 Merge branch 'main' into pip 2026-06-12 07:36:20 -07:00
Daniel Han
55075f691e Merge branch 'main' into pip 2026-06-12 06:55:57 -07:00
Daniel Han
0e929cddac Update pyproject.toml 2026-06-12 06:35:51 -07:00
Daniel Han
ecaf3dde2a Merge branch 'main' into pip 2026-06-12 06:35:37 -07:00
Daniel Han
fc9d82f6bf Update pyproject.toml 2026-06-11 09:22:35 -07:00
Daniel Han
4d2afc62a0 Merge branch 'main' into pip 2026-06-11 09:22:24 -07:00
Daniel Han
43c669e6ec Update pyproject.toml 2026-06-10 11:11:36 -07:00
Daniel Han
11b69c1051 Merge branch 'main' into pip 2026-06-10 11:11:29 -07:00
Daniel Han
39c7a4d290 Merge branch 'main' into pip 2026-06-03 07:30:11 -07:00
Daniel Han
ff1088af40 Update pyproject.toml 2026-06-03 06:36:36 -07:00
Daniel Han
6d3849b821 Merge branch 'main' into pip 2026-06-03 06:36:15 -07:00
Daniel Han
8e26a368e1 Merge branch 'main' into pip 2026-06-01 08:22:08 -07:00
Daniel Han
e31d4c6aea Update pyproject.toml 2026-05-31 07:11:09 -07:00
Daniel Han
5688072af6 Merge branch 'main' into pip 2026-05-31 07:10:55 -07:00
Daniel Han
d3ac7447eb Merge branch 'main' into pip 2026-05-26 07:26:37 -07:00
Daniel Han
2fdfe24fb1 Merge branch 'main' into pip 2026-05-24 07:11:32 -07:00
Daniel Han
e2215c9d11 Update pyproject.toml 2026-05-22 09:24:40 -07:00
Daniel Han
b11f13a710 Merge branch 'main' into pip 2026-05-22 09:24:32 -07:00
Daniel Han
66ae2d416c Merge branch 'main' into pip 2026-05-19 07:27:07 -07:00
Daniel Han
b0a35ddeab Merge branch 'main' into pip 2026-05-19 07:24:07 -07:00
Daniel Han
405add94a1 Update pyproject.toml 2026-05-19 07:00:38 -07:00
Daniel Han
64dc11faa2 Merge branch 'main' into pip 2026-05-19 07:00:31 -07:00
Daniel Han
07e2fccf38 Merge branch 'main' into pip 2026-05-18 08:43:14 -07:00
Daniel Han
d482382a92
Sync pyproject.toml fixes from main into pip (#5555)
- Add missing cu124onlytorch240 extras (xformers==0.0.28.post1)
- Add sentence-transformers to huggingface and colab-new extras
- Add triton extras group (split out from main deps)
- Add windows extras group
- Drop xformers<0.0.27 cap in colab-no-deps; gate on linux/win + x86_64
- Add [tool.ruff] config (target py311, lint select/ignore, extend-exclude)
- Add [tool.pytest.ini_options] testpaths = ["tests/security"]

Only PyPI-compatible (version-pin) entries copied; URL-based intel/amd/rocm/
flashattentiontorch* extras from main are intentionally skipped.
2026-05-18 06:39:41 -07:00
Daniel Han
342bbc2333 Merge branch 'main' into pip 2026-05-18 06:31:17 -07:00
Daniel Han
e1fe3be939 Update pyproject.toml 2026-05-18 06:30:03 -07:00
Daniel Han
d459f60458 Merge branch 'main' into pip 2026-05-18 06:26:54 -07:00
Daniel Han
2c4bf9ae35 Route CPU-only Linux x86_64 to ggml-org/llama.cpp prebuilts (#5302)
* Route CPU-only Linux x86_64 to ggml-org/llama.cpp prebuilts

setup.sh hard-coded _HELPER_RELEASE_REPO=unslothai/llama.cpp for every
non-Darwin host. unslothai/llama.cpp only publishes Linux CUDA bundles
(app-*-linux-x64-cuda*.tar.gz), so a CPU-only Linux host walked ~30
releases looking for a non-existent app-*-linux-x64-cpu asset, exited
the prebuilt planner with "no compatible Linux prebuilt asset was
found", and fell through to a source build. Free CI runners
(ubuntu-latest with no GPU) hit this on every install, and anyone
running Studio on a Linux laptop without an NVIDIA GPU paid the
~3 minute cmake+make cost on first install.

ggml-org publishes llama-<tag>-bin-ubuntu-x64.tar.gz on every release
and install_llama_prebuilt.py already knows how to fetch it: when
called with --published-repo ggml-org/llama.cpp, the Linux x86_64 +
not has_usable_nvidia branch in direct_upstream_release_plan picks up
that asset directly. The fix is purely on the routing side.

Tighten the gate so a Linux host routes to ggml-org only when it is
x86_64 and has no GPU detection tool installed (nvidia-smi, rocminfo,
amd-smi, hipconfig, hipinfo). Everything else stays on the current
path:

  - macOS: already on ggml-org, unchanged
  - Windows: already on ggml-org via setup.ps1, unchanged
  - Linux CUDA: nvidia-smi present -> unslothai/llama.cpp, unchanged
  - Linux ROCm: rocminfo / amd-smi / hipconfig / hipinfo present
                -> unslothai/llama.cpp -> source build with HIP,
                unchanged
  - Linux Intel / Vulkan / SYCL: no NVIDIA / AMD tools, hits the new
                ggml-org route, gets upstream CPU asset (same as
                today's source-build CPU output, ~3 min faster)
  - Linux arm64 / s390x: not x86_64 -> unslothai/llama.cpp ->
                source build, unchanged

* Tighten routing comment in studio/setup.sh
2026-05-06 06:22:56 +00:00
Daniel Han
a87a08610e Merge branch 'main' into pip 2026-05-06 00:27:15 +00:00
danielhanchen
41abe89041 Bump version to 2026.5.2
Cuts a new PyPI release that ships PR #5296: Studio chat history and
attachments work again with newer @assistant-ui/react, plus the pinned
assistant-ui surface and frontend package-lock.json so future installs
cannot drift back onto a broken bundle.
2026-05-06 00:25:04 +00:00
Daniel Han
d638d1bd6f Merge branch 'main' into pip 2026-05-06 00:24:48 +00:00
Daniel Han
2e3b2bdc27 Update pyproject.toml 2026-05-05 05:27:54 -07:00
Daniel Han
9cc539c1b4 Merge branch 'main' into pip 2026-05-05 05:27:25 -07:00
Daniel Han
973c7d80c2 Merge branch 'main' into pip 2026-04-23 06:58:47 -07:00
Daniel Han
c27f9b99e9 Merge branch 'main' into pip 2026-04-22 09:18:09 -07:00
Daniel Han
0c24d61708 Update pyproject.toml 2026-04-16 12:06:55 -07:00
Daniel Han
e9a2b5c010 Merge branch 'main' into pip 2026-04-16 12:06:45 -07:00
Daniel Han
e8355451ea Update pyproject.toml 2026-04-15 08:07:33 -07:00
Daniel Han
a7b4ae19ee Merge branch 'main' into pip 2026-04-15 08:07:11 -07:00
Daniel Han
9066946615 Merge branch 'main' into pip 2026-04-06 09:39:11 -07:00
Daniel Han
b2580ae32b Update pyproject.toml 2026-04-06 09:21:23 -07:00
Daniel Han
7ae9580ce6 Merge branch 'main' into pip 2026-04-06 09:21:07 -07:00
Daniel Han
9ad3b761ee Merge branch 'main' into pip 2026-04-03 15:02:41 -07:00
Daniel Han
8f721d28d6 Merge branch 'main' into pip 2026-04-02 12:28:40 -07:00
Daniel Han
396aa05ead Update pyproject.toml 2026-04-02 12:03:10 -07:00
Daniel Han
2297f73cad Merge branch 'main' into pip 2026-04-02 12:02:48 -07:00
Daniel Han
a728f7c308 Update pyproject.toml 2026-03-31 06:51:58 -07:00
Daniel Han
884152daee Merge branch 'main' into pip 2026-03-31 06:51:48 -07:00
Daniel Han
7437af2e44 Merge branch 'main' into pip 2026-03-27 08:42:05 -07:00
Daniel Han
67678d2c29 Merge branch 'main' into pip 2026-03-27 07:24:42 -07:00
Daniel Han
e568000a92 Update pyproject.toml 2026-03-27 07:23:42 -07:00
Daniel Han
6700dd60f0 Merge branch 'main' into pip 2026-03-27 07:23:27 -07:00
Daniel Han
abf578327c Merge branch 'main' into pip 2026-03-27 03:35:13 -07:00
Daniel Han
a4ae80cd6a Merge branch 'main' into pip 2026-03-25 09:40:21 -07:00
Daniel Han
ec47b2984d Merge branch 'main' into pip 2026-03-25 09:38:41 -07:00
Daniel Han
d9d1a63397 Fix Colab huggingface-hub conflict, ensurepip fallback, bump to 2026.3.14
- Strip version constraints on Colab dep install
- Upgrade huggingface-hub>=1.0 if is_offline_mode is missing
- ensurepip fallback for uv venvs without pip
- Bump installer pins to 2026.3.14
2026-03-25 16:36:25 +00:00
Daniel Han
0bb6379aad Fix Colab huggingface-hub conflict and pip bootstrap on uv venvs
- colab.py / setup.sh: relax == pins to >= when installing studio.txt
  on Colab so huggingface-hub 0.36.2 does not clobber Colab's bundled
  version (which breaks transformers is_offline_mode import)
- install_python_stack.py: when uv is unavailable and pip is missing
  (uv-created venvs), bootstrap via ensurepip before attempting upgrade
- Bump version to 2026.3.14
2026-03-25 16:15:29 +00:00
Daniel Han
1c608e8ff7 Fix Colab dep install: relax == pins to >= to avoid breaking transformers
studio.txt pins huggingface-hub==0.36.2 and datasets==4.3.0 which
overwrite Colab's pre-installed versions and break its bundled
transformers (is_offline_mode was removed in newer huggingface-hub).

Relax == to >= in both colab.py and setup.sh Colab paths so pip keeps
existing compatible versions instead of force-upgrading.
2026-03-25 16:13:30 +00:00
Daniel Han
f9adf6834d Merge branch 'main' into pip 2026-03-25 09:04:26 -07:00
Daniel Han
1fd5853741 Merge branch 'main' into pip 2026-03-25 09:01:49 -07:00
Daniel Han
93a70fbe4e Merge branch 'main' into pip 2026-03-25 08:34:48 -07:00
Daniel Han
8b77451e75 Update pyproject.toml 2026-03-25 07:31:31 -07:00
Daniel Han
481f0618ff Merge branch 'main' into pip 2026-03-25 07:30:50 -07:00
Daniel Han
ddf6f6d1f9 fix(studio): remove litellm dep (quarantined on PyPI) (#4553)
litellm has been quarantined on PyPI due to a supply chain attack
in version 1.82.8 (malicious credential-stealing .pth file).
No versions are currently installable, which blocks
`unsloth studio setup` at step 8/11 (data-designer deps).

Remove litellm from the single-env data-designer requirements
so setup completes. litellm can be re-added once PyPI lifts the
quarantine.

Ref: https://github.com/BerriAI/litellm/issues/24512
2026-03-24 14:21:57 +00:00
Daniel Han
dddc9eac92 Merge branch 'main' into pip 2026-03-24 06:51:20 -07:00
Daniel Han
da81c94510 Merge branch 'main' into pip 2026-03-22 08:23:44 -07:00
Daniel Han
4c139503c1 Merge branch 'main' into pip 2026-03-22 06:14:40 -07:00
Daniel Han
9d1e3c38bc Merge branch 'main' into pip 2026-03-19 02:31:50 -07:00
Daniel Han
8bc26f4e1d Merge branch 'main' into pip 2026-03-18 11:13:18 -07:00
Daniel Han
044e67f5aa Merge branch 'main' into pip 2026-03-18 10:40:39 -07:00
Daniel Han
ba36c12240 Merge branch 'main' into pip 2026-03-18 09:10:55 -07:00
Daniel Han
0a5652281f Merge branch 'main' into pip 2026-03-18 08:33:28 -07:00
Daniel Han
bf63f79414 Merge branch 'main' into pip 2026-03-18 08:31:20 -07:00
Daniel Han
2973bea3d0 Merge branch 'main' into pip 2026-03-17 07:58:59 -07:00
Daniel Han
ddbbfe52cf Update pyproject.toml 2026-03-17 07:58:28 -07:00
3 changed files with 247 additions and 659 deletions

File diff suppressed because it is too large Load diff

View file

@ -8,12 +8,19 @@ filter_sensitive_data (structlog processor for sanitization), and
get_logger (factory for structured loggers). get_logger (factory for structured loggers).
""" """
from __future__ import annotations
import os import os
import re import re
import time import time
from typing import TYPE_CHECKING
import structlog import structlog
from starlette.types import ASGIApp, Message, Receive, Scope, Send
# Annotations only: importing at runtime would make the ASGI stack a hard
# dependency of every CLI command.
if TYPE_CHECKING:
from starlette.types import ASGIApp, Message, Receive, Scope, Send
from utils.native_path_leases import redact_native_paths from utils.native_path_leases import redact_native_paths

View file

@ -0,0 +1,85 @@
# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
"""The studio extra must mirror studio/backend/requirements/studio.txt.
Nothing else keeps them in sync, and drift reintroduces #4701 / #5260 / #7147.
"""
from __future__ import annotations
import pathlib
import sys
import pytest
REPO_ROOT = pathlib.Path(__file__).resolve().parents[3]
PYPROJECT = REPO_ROOT / "pyproject.toml"
STUDIO_TXT = REPO_ROOT / "studio" / "backend" / "requirements" / "studio.txt"
# Imported at module scope by the chain every CLI command walks: structlog via
# studio.backend, click via unsloth_cli/commands/start.py.
CORE_RUNTIME_PACKAGES = ("structlog", "click")
def _load_pyproject() -> dict:
if sys.version_info >= (3, 11):
import tomllib
else:
tomllib = pytest.importorskip("tomli")
return tomllib.loads(PYPROJECT.read_text(encoding = "utf-8"))
def _requirement_lines(path: pathlib.Path) -> list[str]:
out = []
for line in path.read_text(encoding = "utf-8").splitlines():
text = line.split("#", 1)[0].strip()
if text and not text.startswith("-"):
out.append(text)
return out
def _normalise(name: str) -> str:
"""PEP 503 normalisation, so PyJWT/pyjwt and nest_asyncio/nest-asyncio match."""
head = name
for sep in ("===", "==", ">=", "<=", "~=", "!=", ">", "<", "[", ";", " "):
idx = head.find(sep)
if idx > 0:
head = head[:idx]
return head.strip().lower().replace("_", "-").replace(".", "-")
def test_studio_extra_exists():
extras = _load_pyproject()["project"]["optional-dependencies"]
assert "studio" in extras, (
"pyproject.toml has no `studio` extra. The wheel ships studio/ and "
"studio.backend*, so their dependencies need a pip-installable home."
)
def test_studio_extra_matches_requirements_file():
extras = _load_pyproject()["project"]["optional-dependencies"]
extra = sorted(_normalise(entry) for entry in extras["studio"])
required = sorted(_normalise(entry) for entry in _requirement_lines(STUDIO_TXT))
missing = sorted(set(required) - set(extra))
surplus = sorted(set(extra) - set(required))
assert not missing, (
f"studio.txt lists {missing} but the `studio` extra does not. "
'`pip install "unsloth[studio]"` would build a venv the Studio server '
"cannot boot in. Add them to [project.optional-dependencies] studio."
)
assert not surplus, (
f"The `studio` extra lists {surplus} but studio.txt does not. "
"Remove them, or add them to studio.txt if install.sh needs them too."
)
@pytest.mark.parametrize("package", CORE_RUNTIME_PACKAGES)
def test_cli_runtime_packages_are_core_dependencies(package):
core = [_normalise(entry) for entry in _load_pyproject()["project"]["dependencies"]]
assert _normalise(package) in core, (
f"{package} is imported at module scope by the studio.backend chain "
f"`unsloth train` / `unsloth export` walk, so a plain `pip install "
f"unsloth` must provide it or they die with ModuleNotFoundError."
)