diff --git a/.github/workflows/studio-windows-api-smoke.yml b/.github/workflows/studio-windows-api-smoke.yml index 1d12ea6f90..eee61516c7 100644 --- a/.github/workflows/studio-windows-api-smoke.yml +++ b/.github/workflows/studio-windows-api-smoke.yml @@ -170,20 +170,6 @@ jobs: fi cygpath -w "$SHIM_DIR" >> "$GITHUB_PATH" - - name: Patch Studio venv with full typer / pydantic dep trees - # Belt-and-suspenders: install.ps1's --no-deps install of - # no-torch-runtime.txt drops typer's and pydantic's runtime - # deps unless explicitly pinned. Re-install the ones whose - # deps don't pull torch. - run: | - STUDIO_PY=~/.unsloth/studio/unsloth_studio/Scripts/python.exe - if [ ! -f "$STUDIO_PY" ]; then - echo "::error::Studio venv python not at $STUDIO_PY" - ls -la ~/.unsloth/studio/ || true - exit 1 - fi - "$STUDIO_PY" -m pip install --upgrade typer pydantic huggingface_hub - - name: Install pyjwt for the JWT-expiry forge test run: python -m pip install 'pyjwt>=2.6' diff --git a/.github/workflows/studio-windows-inference-smoke.yml b/.github/workflows/studio-windows-inference-smoke.yml index ad739dd529..6253b9d213 100644 --- a/.github/workflows/studio-windows-inference-smoke.yml +++ b/.github/workflows/studio-windows-inference-smoke.yml @@ -190,20 +190,6 @@ jobs: fi cygpath -w "$SHIM_DIR" >> "$GITHUB_PATH" - - name: Patch Studio venv with full typer / pydantic dep trees - # Belt-and-suspenders: install.ps1's --no-deps install of - # no-torch-runtime.txt drops typer's and pydantic's runtime - # deps unless explicitly pinned. Re-install the ones whose - # deps don't pull torch. - run: | - STUDIO_PY=~/.unsloth/studio/unsloth_studio/Scripts/python.exe - if [ ! -f "$STUDIO_PY" ]; then - echo "::error::Studio venv python not at $STUDIO_PY" - ls -la ~/.unsloth/studio/ || true - exit 1 - fi - "$STUDIO_PY" -m pip install --upgrade typer pydantic huggingface_hub - - name: Install OpenAI + Anthropic Python SDKs run: python -m pip install 'openai>=1.50' 'anthropic>=0.40' @@ -543,20 +529,6 @@ jobs: fi cygpath -w "$SHIM_DIR" >> "$GITHUB_PATH" - - name: Patch Studio venv with full typer / pydantic dep trees - # Belt-and-suspenders: install.ps1's --no-deps install of - # no-torch-runtime.txt drops typer's and pydantic's runtime - # deps unless explicitly pinned. Re-install the ones whose - # deps don't pull torch. - run: | - STUDIO_PY=~/.unsloth/studio/unsloth_studio/Scripts/python.exe - if [ ! -f "$STUDIO_PY" ]; then - echo "::error::Studio venv python not at $STUDIO_PY" - ls -la ~/.unsloth/studio/ || true - exit 1 - fi - "$STUDIO_PY" -m pip install --upgrade typer pydantic huggingface_hub - - name: Reset auth + boot Studio (API-only, default tool policy) run: | unsloth studio reset-password @@ -982,20 +954,6 @@ jobs: fi cygpath -w "$SHIM_DIR" >> "$GITHUB_PATH" - - name: Patch Studio venv with full typer / pydantic dep trees - # Belt-and-suspenders: install.ps1's --no-deps install of - # no-torch-runtime.txt drops typer's and pydantic's runtime - # deps unless explicitly pinned. Re-install the ones whose - # deps don't pull torch. - run: | - STUDIO_PY=~/.unsloth/studio/unsloth_studio/Scripts/python.exe - if [ ! -f "$STUDIO_PY" ]; then - echo "::error::Studio venv python not at $STUDIO_PY" - ls -la ~/.unsloth/studio/ || true - exit 1 - fi - "$STUDIO_PY" -m pip install --upgrade typer pydantic huggingface_hub - - name: Install OpenAI + Anthropic Python SDKs run: python -m pip install 'openai>=1.50' 'anthropic>=0.40' diff --git a/.github/workflows/studio-windows-ui-smoke.yml b/.github/workflows/studio-windows-ui-smoke.yml index e5ab9f8ab7..e3c8642122 100644 --- a/.github/workflows/studio-windows-ui-smoke.yml +++ b/.github/workflows/studio-windows-ui-smoke.yml @@ -208,20 +208,6 @@ jobs: cygpath -w "$SHIM_DIR" >> "$GITHUB_PATH" echo "Added Studio shim dir to PATH: $(cygpath -w "$SHIM_DIR")" - - name: Patch Studio venv with full typer / pydantic dep trees - # Belt-and-suspenders: install.ps1's --no-deps install of - # no-torch-runtime.txt drops typer's and pydantic's runtime - # deps unless explicitly pinned. Re-install the ones whose - # deps don't pull torch. - run: | - STUDIO_PY=~/.unsloth/studio/unsloth_studio/Scripts/python.exe - if [ ! -f "$STUDIO_PY" ]; then - echo "::error::Studio venv python not at $STUDIO_PY" - ls -la ~/.unsloth/studio/ || true - exit 1 - fi - "$STUDIO_PY" -m pip install --upgrade typer pydantic huggingface_hub - - name: Install Playwright + Chromium # No --with-deps on Windows: that flag installs Linux apt # packages. windows-latest ships the system frameworks diff --git a/.github/workflows/studio-windows-update-smoke.yml b/.github/workflows/studio-windows-update-smoke.yml index b477b3fa11..aa3e35f052 100644 --- a/.github/workflows/studio-windows-update-smoke.yml +++ b/.github/workflows/studio-windows-update-smoke.yml @@ -176,32 +176,6 @@ jobs: fi cygpath -w "$SHIM_DIR" >> "$GITHUB_PATH" - - name: Patch Studio venv with full typer / pydantic dep trees - # install.ps1 runs `uv pip install --no-deps -r - # no-torch-runtime.txt` to keep torch out of transitive - # resolution from accelerate/peft/trl. That also drops - # typer's and pydantic's runtime deps unless they're - # explicitly pinned in no-torch-runtime.txt. We pin the - # known ones (click, shellingham, annotated-doc, rich, - # pydantic-core, annotated-types, typing-inspection, ...) - # but typer / pydantic minor versions can introduce new - # transitive deps that are NOT in our pin list. - # - # Belt-and-suspenders: re-install typer + pydantic + - # huggingface_hub WITH their deps into the Studio venv. - # `pip install --upgrade` only adds missing packages; it - # never down-shifts an installed version. Cannot pull - # torch (none of typer / pydantic / huggingface_hub depend - # on it). - run: | - STUDIO_PY=~/.unsloth/studio/unsloth_studio/Scripts/python.exe - if [ ! -f "$STUDIO_PY" ]; then - echo "::error::Studio venv python not at $STUDIO_PY" - ls -la ~/.unsloth/studio/ || true - exit 1 - fi - "$STUDIO_PY" -m pip install --upgrade typer pydantic huggingface_hub - - name: First update should be a no-op (prebuilt already validated) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/install.ps1 b/install.ps1 index 0dad877645..b26566cc3d 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1301,6 +1301,16 @@ shell.Run cmd, 0, False # No-torch: install unsloth + unsloth-zoo with --no-deps, then # runtime deps (typer, safetensors, transformers, etc.) with --no-deps. $baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --no-deps --reinstall-package unsloth --reinstall-package unsloth-zoo "unsloth>=2026.5.6" unsloth-zoo } + if ($baseInstallExit -eq 0) { + # Install pydantic WITH deps so pip pins pydantic-core to + # the exact version pydantic's metadata requires. The + # --no-deps install of no-torch-runtime.txt below would + # otherwise pick the latest of each independently and + # trip pydantic's _ensure_pydantic_core_version check. + # pydantic's deps (annotated-types, pydantic-core, + # typing-extensions, typing-inspection) are torch-free. + $baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython pydantic } + } if ($baseInstallExit -eq 0) { $NoTorchReq = Find-NoTorchRuntimeFile if ($NoTorchReq) { @@ -1347,6 +1357,11 @@ shell.Run cmd, 0, False # No-torch: install unsloth + unsloth-zoo with --no-deps, then # runtime deps (typer, safetensors, transformers, etc.) with --no-deps. $baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --no-deps --upgrade-package unsloth --upgrade-package unsloth-zoo "unsloth>=2026.5.6" unsloth-zoo } + if ($baseInstallExit -eq 0) { + # Install pydantic WITH deps so pip pins pydantic-core to + # the matching version (see migrated branch above). + $baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython pydantic } + } if ($baseInstallExit -eq 0) { $NoTorchReq = Find-NoTorchRuntimeFile if ($NoTorchReq) { diff --git a/install.sh b/install.sh index 384e9640d0..9bdd935171 100755 --- a/install.sh +++ b/install.sh @@ -1866,6 +1866,15 @@ if [ "$_MIGRATED" = true ]; then run_install_cmd "install unsloth (migrated no-torch)" uv pip install --python "$_VENV_PY" --no-deps \ --reinstall-package unsloth --reinstall-package unsloth-zoo \ "unsloth>=2026.5.6" unsloth-zoo + # Install pydantic WITH deps so pip pins pydantic-core to the + # exact version pydantic's own metadata requires. The --no-deps + # install below would otherwise pick the latest of each + # independently and trip pydantic's _ensure_pydantic_core_version + # check on the next import. pydantic's deps (annotated-types, + # pydantic-core, typing-extensions, typing-inspection) are + # torch-free, so this is safe on the no-torch path. + run_install_cmd "install pydantic (with deps for compatible core)" \ + uv pip install --python "$_VENV_PY" pydantic _NO_TORCH_RT="$(_find_no_torch_runtime)" if [ -n "$_NO_TORCH_RT" ]; then run_install_cmd "install no-torch runtime deps" uv pip install --python "$_VENV_PY" --no-deps -r "$_NO_TORCH_RT" @@ -2042,6 +2051,10 @@ elif [ -n "$TORCH_INDEX_URL" ]; then run_install_cmd "install unsloth (no-torch)" uv pip install --python "$_VENV_PY" --no-deps \ --upgrade-package unsloth --upgrade-package unsloth-zoo \ "unsloth>=2026.5.6" unsloth-zoo + # Install pydantic WITH deps so pip pins pydantic-core to the + # exact version pydantic requires (see migrated branch above). + run_install_cmd "install pydantic (with deps for compatible core)" \ + uv pip install --python "$_VENV_PY" pydantic _NO_TORCH_RT="$(_find_no_torch_runtime)" if [ -n "$_NO_TORCH_RT" ]; then run_install_cmd "install no-torch runtime deps" uv pip install --python "$_VENV_PY" --no-deps -r "$_NO_TORCH_RT" diff --git a/studio/backend/requirements/no-torch-runtime.txt b/studio/backend/requirements/no-torch-runtime.txt index a39666495e..c33ebf4d94 100644 --- a/studio/backend/requirements/no-torch-runtime.txt +++ b/studio/backend/requirements/no-torch-runtime.txt @@ -22,14 +22,19 @@ rich>=13.0 markdown-it-py>=3.0 mdurl>=0.1 pygments>=2.0 -pydantic -# pydantic 2.x deps. With --no-deps, `import pydantic` blows up -# with `ModuleNotFoundError: 'pydantic_core'` (compiled Rust core, -# separate wheel), then `'annotated_types'`, then -# `'typing_inspection'` (used by pydantic 2.10+ for fields). -pydantic-core -annotated-types>=0.6 -typing-inspection>=0.4 +# pydantic is intentionally NOT installed via this --no-deps file. +# install.sh / install.ps1 / install_python_stack.py run a separate +# `pip install pydantic` (with deps) just before this file is +# applied, so pip resolves `pydantic-core` to the exact version +# pydantic's `_ensure_pydantic_core_version` check expects. Listing +# pydantic + pydantic-core unpinned here and resolving them under +# --no-deps used to pick the latest of each independently and trip +# `SystemError: pydantic-core 2.X.Y is incompatible with the current +# pydantic version` on the first import (Windows fresh-venv repro +# was the canonical case). pydantic's transitive deps +# (annotated-types, pydantic-core, typing-extensions, +# typing-inspection) are torch-free, so installing it WITH deps +# does not pull torch. pyyaml nest-asyncio diff --git a/studio/frontend/src/features/auth/components/auth-form.tsx b/studio/frontend/src/features/auth/components/auth-form.tsx index 3ac97929d8..d753e1aab5 100644 --- a/studio/frontend/src/features/auth/components/auth-form.tsx +++ b/studio/frontend/src/features/auth/components/auth-form.tsx @@ -194,7 +194,10 @@ export function AuthForm({ mode }: AuthFormProps): ReactElement | null { const hasBootstrapPassword = Boolean(window.__UNSLOTH_BOOTSTRAP__?.password); const invalidChangePasswordForm = !isLoginMode && - (newPassword.length < 8 || newPassword !== confirmPassword || currentPassword === newPassword); + (currentPassword.length < 8 || + newPassword.length < 8 || + newPassword !== confirmPassword || + currentPassword === newPassword); const showPasswordMismatchWarning = !isLoginMode && newPassword.length > 0 && @@ -206,8 +209,13 @@ export function AuthForm({ mode }: AuthFormProps): ReactElement | null { setError(null); if (!isLoginMode) { - if (!currentPassword) { - setError("Unable to initialize setup. Reload the page and try again."); + // Mirror the disable gate: Enter / autofill can bypass the button. + if (currentPassword.length < 8) { + setError( + currentPassword + ? "Current password must be at least 8 characters." + : "Unable to initialize setup. Reload the page and try again.", + ); return; } if (newPassword.length < 8) { diff --git a/studio/install_llama_prebuilt.py b/studio/install_llama_prebuilt.py index dd44f19691..91076a4743 100644 --- a/studio/install_llama_prebuilt.py +++ b/studio/install_llama_prebuilt.py @@ -3833,6 +3833,19 @@ def runtime_patterns_for_choice(choice: AssetChoice) -> list[str]: "llama-quantize", "libllama-common.so*", "libllama.so*", + # Upstream llama.cpp split the per-binary entry code into + # paired ``libllama--impl.so`` shared libraries + # around release b9261. ``llama-server`` and + # ``llama-quantize`` are NEEDED-linked against + # ``libllama-server-impl.so`` / ``libllama-quantize-impl.so`` + # respectively, with RUNPATH ``$ORIGIN``. Without copying + # the impl ``.so`` files alongside the binaries, ldd + # reports them missing, preflight rejects the install, and + # the installer falls back to a source build on a fresh + # Linux install. Glob the whole family so future bundles + # that split additional binaries (e.g. ``llama-cli``, + # ``llama-bench``) keep working. + "libllama-*-impl.so*", "libggml.so*", "libggml-base.so*", "libmtmd.so*", diff --git a/studio/install_python_stack.py b/studio/install_python_stack.py index ab234ad566..4dfa20032b 100644 --- a/studio/install_python_stack.py +++ b/studio/install_python_stack.py @@ -979,6 +979,27 @@ def install_python_stack() -> int: package_name, "unsloth-zoo", ) + # Pydantic ships its core as a separate compiled wheel + # (pydantic-core), and pydantic's ``_ensure_pydantic_core_version`` + # checks the installed core matches the exact version pinned in + # its own metadata. With ``--no-deps`` plus an unpinned + # ``pydantic`` / ``pydantic-core`` pair in no-torch-runtime.txt, + # pip resolved each to the newest available version and the two + # drifted (pydantic 2.13.4 pins pydantic-core==2.46.4 today, but + # pydantic-core 2.47.0 was the latest). On a fresh Windows venv + # the next ``import pydantic`` raised ``SystemError: ... + # incompatible with the current pydantic version``. + # + # Resolve them WITH deps in a focused pip call so pip picks a + # compatible pair. pydantic's own deps are + # ``annotated-types``, ``pydantic-core``, ``typing-extensions``, + # ``typing-inspection`` -- none of which transitively pull + # torch, so this is safe for the no-torch path. + pip_install( + "Installing pydantic (with deps for compatible core)", + "--no-cache-dir", + "pydantic", + ) pip_install( "Installing no-torch runtime deps", "--no-cache-dir", diff --git a/tests/studio/install/test_rocm_support.py b/tests/studio/install/test_rocm_support.py index 13df049c2d..698625e59b 100644 --- a/tests/studio/install/test_rocm_support.py +++ b/tests/studio/install/test_rocm_support.py @@ -346,6 +346,14 @@ def test_linux_cpu_patterns(self): patterns = runtime_patterns_for_choice(choice) assert "llama-server" in patterns assert "llama-quantize" in patterns + # Upstream split entry code into ``libllama--impl.so`` + # shared libraries (b9261+). llama-server and llama-quantize + # are NEEDED-linked against ``libllama-server-impl.so`` and + # ``libllama-quantize-impl.so`` respectively with RUNPATH + # ``$ORIGIN``, so the prebuilt overlay MUST copy them + # alongside the binaries or ldd reports them missing and + # preflight forces a source-build fallback. + assert "libllama-*-impl.so*" in patterns def test_linux_cuda_patterns(self): choice = AssetChoice(