Rename cli/ to unsloth_cli/ to fix namespace collision with stringzilla (#4393)

* Rename cli/ to unsloth_cli/ to fix namespace collision with stringzilla

stringzilla installs a namespace package at cli/ (cli/split.py, cli/wc.py)
in site-packages without an __init__.py. When unsloth is installed as an
editable package (pip install -e .), the entry point script does
`from cli import app` which finds stringzilla's namespace cli/ first and
fails with `ImportError: cannot import name 'app' from 'cli'`.

Non-editable installs happened to work because unsloth's cli/__init__.py
overwrites the namespace directory, but this is fragile and breaks if
stringzilla is installed after unsloth.

Renaming to unsloth_cli/ avoids the collision entirely and fixes both
editable and non-editable install paths.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update stale cli/ references in comments and license files

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Daniel Han 2026-03-17 20:40:21 -07:00 committed by GitHub
parent 75da2e00c2
commit 0c8d407793
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 18 additions and 14 deletions

View file

@ -661,4 +661,4 @@ For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
Files under unsloth/*, tests/*, scripts/* are Apache 2.0 licensed.
Files under studio/*, cli/* which is optional to install are AGPLv3 licensed.
Files under studio/*, unsloth_cli/* which is optional to install are AGPLv3 licensed.

View file

@ -188,7 +188,7 @@
Copyright [2024-] [Unsloth AI. Inc team, Daniel Han-Chen & Michael Han-Chen]
Files under unsloth/*, tests/*, scripts/* are Apache 2.0 licensed.
Files under studio/*, cli/* which is optional to install are AGPLv3 licensed.
Files under studio/*, unsloth_cli/* which is optional to install are AGPLv3 licensed.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

2
cli.py
View file

@ -1,7 +1,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
from cli import app
from unsloth_cli import app
if __name__ == "__main__":
app()

View file

@ -32,7 +32,7 @@ dependencies = [
]
[project.scripts]
unsloth = "cli:app"
unsloth = "unsloth_cli:app"
[tool.setuptools.dynamic]
version = {attr = "unsloth.models._utils.__version__"}

View file

@ -3,11 +3,11 @@
import typer
from cli.commands.train import train
from cli.commands.inference import inference
from cli.commands.export import export, list_checkpoints
from cli.commands.ui import ui
from cli.commands.studio import studio_app
from unsloth_cli.commands.train import train
from unsloth_cli.commands.inference import inference
from unsloth_cli.commands.export import export, list_checkpoints
from unsloth_cli.commands.ui import ui
from unsloth_cli.commands.studio import studio_app
app = typer.Typer(
help = "Command-line interface for Unsloth training, inference, and export.",

View file

@ -14,7 +14,7 @@ studio_app = typer.Typer(help = "Unsloth Studio commands.")
STUDIO_HOME = Path.home() / ".unsloth" / "studio"
# __file__ is cli/commands/studio.py — two parents up is the package root
# __file__ is unsloth_cli/commands/studio.py -- two parents up is the package root
# (either site-packages or the repo root for editable installs).
_PACKAGE_ROOT = Path(__file__).resolve().parent.parent.parent
@ -33,7 +33,7 @@ def _find_run_py() -> Optional[Path]:
No CWD dependency works from any directory.
Since studio/ is now a proper package (has __init__.py), it lives in
site-packages after pip install, right next to cli/.
site-packages after pip install, right next to unsloth_cli/.
"""
# 1. Relative to __file__ (site-packages or editable repo root)
run_py = _PACKAGE_ROOT / "studio" / "backend" / "run.py"

View file

@ -7,8 +7,8 @@ from typing import Optional
import typer
from cli.config import Config, load_config
from cli.options import add_options_from_config
from unsloth_cli.config import Config, load_config
from unsloth_cli.options import add_options_from_config
@add_options_from_config(Config)

View file

@ -25,7 +25,11 @@ def ui(
),
):
"""Launch the Unsloth web UI backend server (alias for 'unsloth studio')."""
from cli.commands.studio import _studio_venv_python, _find_run_py, STUDIO_HOME
from unsloth_cli.commands.studio import (
_studio_venv_python,
_find_run_py,
STUDIO_HOME,
)
# Re-execute in studio venv if available and not already inside it
studio_venv_dir = STUDIO_HOME / ".venv"