Files
transformerlab-api/install.sh
2025-04-14 15:35:42 -04:00

456 lines
14 KiB
Bash
Executable File

#!/bin/bash
set -eu
ENV_NAME="transformerlab"
TLAB_DIR="$HOME/.transformerlab"
TLAB_CODE_DIR="${TLAB_DIR}/src"
TLAB_STATIC_WEB_DIR="${TLAB_DIR}/webapp"
MINICONDA_ROOT=${TLAB_DIR}/miniconda3
CONDA_BIN=${MINICONDA_ROOT}/bin/conda
ENV_DIR=${TLAB_DIR}/envs/${ENV_NAME}
RUN_DIR=$(pwd)
##############################
# Helper Functions
##############################
err_report() {
echo "Error on line $1"
}
trap 'err_report $LINENO' ERR
abort() {
printf "%s\n" "$@" >&2
exit 1
}
if [[ -t 1 ]]
then
tty_escape() { printf "\033[%sm" "$1"; }
else
tty_escape() { :; }
fi
tty_mkbold() { tty_escape "1;$1"; }
tty_underline="$(tty_escape "4;39")"
tty_blue="$(tty_mkbold 34)"
tty_red="$(tty_mkbold 31)"
tty_bold="$(tty_mkbold 39)"
tty_reset="$(tty_escape 0)"
shell_join() {
local arg
printf "%s" "$1"
shift
for arg in "$@"
do
printf " "
printf "%s" "${arg// /\ }"
done
}
chomp() {
printf "%s" "${1/"$'\n'"/}"
}
ohai() {
printf "${tty_blue}==>${tty_bold} %s${tty_reset}\n" "$(shell_join "$@")"
}
warn() {
printf "${tty_red}Warning${tty_reset}: %s\n" "$(chomp "$1")" >&2
}
title() {
echo ""
printf "${tty_blue}#########################################################################${tty_reset}\n"
printf "${tty_blue}#### ${tty_bold} %s${tty_reset}\n" "$(shell_join "$@")"
printf "${tty_blue}#########################################################################${tty_reset}\n"
}
check_conda() {
if ! command -v "${CONDA_BIN}" &> /dev/null; then
abort "❌ Conda is not installed at ${MINICONDA_ROOT}. Please install Conda using '${TLAB_DIR}/src/install.sh install_conda' and try again."
else
ohai "✅ Conda is installed at ${MINICONDA_ROOT}."
fi
}
check_python() {
if ! command -v python &> /dev/null; then
abort "❌ Python is not installed as 'python'. Please install Python and try again or it could be installed as 'python3'"
else
# store python version in variable:
PYTHON_VERSION=$(python --version)
ohai "✅ Python is installed: $PYTHON_VERSION"
fi
}
unset_conda_for_sure() {
{ conda deactivate && conda deactivate && conda deactivate; } 2> /dev/null
export PYTHONNOUSERSITE=1
unset PYTHONPATH
unset PYTHONHOME
}
# We've seen users who installed conda using root have problems if their
# ~/.conda directory is not writable. This checks for that.
check_if_conda_envronments_dot_text_is_writable() {
# Check if a file called ~/.conda/environments.txt exists:
if [ -f "$HOME/.conda/environments.txt" ]; then
# Now check if it is writable:
if [ -w "$HOME/.conda/environments.txt" ]; then
echo -n
# echo "✅ The file ~/.conda/environments.txt is writable."
else
abort "❌ The file $HOME/.conda/environments.txt exists but is not writable. Please run [sudo chown -R \$USER ~/.conda] in the terminal to fix conda permissions."
fi
else
echo -n
# echo "The file $HOME/.conda/environments.txt does not exist. No problem we will create it below"
fi
}
# First check OS.
# WSL will return "Linux" which is OK. We will check KERNEL to detect WSL.
OS="$(uname)"
KERNEL=$(uname -r)
if [[ "${OS}" == "Linux" ]]
then
TLAB_ON_LINUX=1
elif [[ "${OS}" == "Darwin" ]]
then
TLAB_ON_MACOS=1
else
abort "Transformer Lab is only supported on macOS and Linux, you are running ${OS}."
fi
# Stack Overflow says the best way to check for WSL is looking for Microsoft in the uname kernel
if [[ -n $(echo ${KERNEL} | sed -n 's/.*\( *Microsoft *\).*/\1/ip') ]]; then
TLAB_ON_WSL=1
fi
##############################
## Step 1: Download Transformer Lab
## and place it in the ~/.transformerlab/src directory.
##############################
download_transformer_lab() {
title "Step 1: Download the latest release of Transformer Lab"
echo "🌘 Step 1: START"
# First check that curl is installed:
if ! command -v curl &> /dev/null; then
abort "❌ curl is not installed on the remote host. Please install curl and try again."
else
ohai "✅ curl is installed."
fi
# Figure out the path to the lastest release of Transformer Lab
LATEST_RELEASE_VERSION=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/transformerlab/transformerlab-api/releases/latest)
LATEST_RELEASE_VERSION=$(basename "$LATEST_RELEASE_VERSION")
LATEST_RELEASE_VERSION_WITHOUT_V=$(echo "$LATEST_RELEASE_VERSION" | sed 's/v//g')
echo "Latest Release of the API on Github: $LATEST_RELEASE_VERSION"
TLAB_URL="https://github.com/transformerlab/transformerlab-api/archive/refs/tags/${LATEST_RELEASE_VERSION}.tar.gz"
echo "Download Location: $TLAB_URL"
# If the user has not installed Transformer Lab, then we should install it.
ohai "Installing Transformer Lab ${LATEST_RELEASE_VERSION}..."
# Fetch the latest version of Transformer Lab from GitHub:
mkdir -p "${TLAB_DIR}"
curl -L "${TLAB_URL}" -o "${TLAB_DIR}/transformerlab.tar.gz"
NEW_DIRECTORY_NAME="transformerlab-api-${LATEST_RELEASE_VERSION_WITHOUT_V}"
rm -rf "${TLAB_DIR}/${NEW_DIRECTORY_NAME}"
rm -rf "${TLAB_CODE_DIR}"
tar -xf "${TLAB_DIR}/transformerlab.tar.gz" -C "${TLAB_DIR}"
mv "${TLAB_DIR}/${NEW_DIRECTORY_NAME}" "${TLAB_CODE_DIR}"
rm "${TLAB_DIR}/transformerlab.tar.gz"
# Create a file called LATEST_VERSION that contains the latest version of Transformer Lab.
echo "${LATEST_RELEASE_VERSION}" > "${TLAB_CODE_DIR}/LATEST_VERSION"
# Now do the same thing for the web app which is in a different repo called https://github.com/transformerlab/transformerlab-app
# Step 1: First get the latest release version:
TLAB_APP_URL="https://github.com/transformerlab/transformerlab-app/releases/latest/download/transformerlab_web.tar.gz"
echo "APP Download Location: $TLAB_APP_URL"
# Delete and recreate the target static files directory
echo "Creating clean directory at ${TLAB_STATIC_WEB_DIR}"
rm -rf "${TLAB_STATIC_WEB_DIR:?}" 2>/dev/null || true
mkdir -p "${TLAB_STATIC_WEB_DIR}"
# Download and extract, handling possible failure
if curl -L --fail "${TLAB_APP_URL}" -o /tmp/transformerlab_web.tar.gz; then
# Extraction succeeded, proceed with unpacking
tar -xzf /tmp/transformerlab_web.tar.gz -C "${TLAB_STATIC_WEB_DIR}"
# Move contents up one level and clean up
mv "${TLAB_STATIC_WEB_DIR}/transformerlab_web/"* "${TLAB_STATIC_WEB_DIR}/" 2>/dev/null || true
rmdir "${TLAB_STATIC_WEB_DIR}/transformerlab_web" 2>/dev/null || true
# Remove the temporary file
rm /tmp/transformerlab_web.tar.gz
echo "Web app successfully installed."
else
echo "Warning: Could not download web app from ${TLAB_APP_URL}. Continuing without web app installation."
fi
echo "🌕 Step 1: COMPLETE"
}
##############################
## Step 2: Install Conda
##############################
install_conda() {
title "Step 2: Install Conda"
echo "🌘 Step 2: START"
unset_conda_for_sure
# check if conda already exists:
if ! command -v "${CONDA_BIN}" &> /dev/null; then
echo "Conda is not installed at ${MINICONDA_ROOT}."
OS=$(uname -s)
ARCH=$(uname -m)
if [ "$OS" == "Darwin" ]; then
OS="MacOSX"
fi
MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-$OS-$ARCH.sh"
echo Downloading "$MINICONDA_URL"
# Change the directory to the Transformer Lab directory
mkdir -p "$TLAB_DIR"
cd "$TLAB_DIR"
# first check if the MINICONDAROOT exists, and if so, delete it:
if [ -d "$MINICONDA_ROOT" ]; then
echo "Deleting existing Miniconda installation at $MINICONDA_ROOT"
rm -rf "$MINICONDA_ROOT"
fi
curl -o miniconda_installer.sh "$MINICONDA_URL" && bash miniconda_installer.sh -b -p "$MINICONDA_ROOT" && rm miniconda_installer.sh
# Install conda to bash and zsh. We keep these commented out
# to avoid adding our conda to the user's shell as the default.
# $MINICONDA_ROOT/bin/conda init bash
# if [ -n "$(command -v zsh)" ]; then
# $MINICONDA_ROOT/bin/conda init zsh
# fi
else
ohai "Conda is installed at ${MINICONDA_ROOT}, we do not need to install it"
fi
# Enable conda in shell
eval "$(${CONDA_BIN} shell.bash hook)"
check_conda
echo "🌕 Step 2: COMPLETE"
}
##############################
## Step 3: Create the Conda Environment
##############################
create_conda_environment() {
title "Step 3: Create the Conda Environment"
echo "🌘 Step 3: START"
check_if_conda_envronments_dot_text_is_writable
check_conda
unset_conda_for_sure
eval "$(${CONDA_BIN} shell.bash hook)"
conda info --envs
# Create the conda environment for Transformer Lab
if { conda env list | grep "$ENV_DIR"; } >/dev/null 2>&1; then
echo "✅ Conda environment $ENV_DIR already exists."
else
echo conda create -y -n "$ENV_DIR" python=3.11
conda create -y -k --prefix "$ENV_DIR" python=3.11
fi
# Activate the newly created environment
echo conda activate "$ENV_DIR"
conda activate "$ENV_DIR"
echo "🌕 Step 3: COMPLETE"
}
##############################
## Step 4: Install Dependencies
##############################
install_dependencies() {
title "Step 4: Install Dependencies"
echo "Warning: this step may take a while to complete the first time."
echo "In this step, all Python dependencies for a full ML workspace"
echo "will be installed in the conda environment."
echo "🌘 Step 4: START"
unset_conda_for_sure
eval "$(${CONDA_BIN} shell.bash hook)"
conda activate "$ENV_DIR"
check_python
# store if the box has an nvidia graphics card
HAS_GPU=false
if command -v nvidia-smi &> /dev/null; then
# Check if nvidia-smi is available
echo "nvidia-smi is available"
GPU_INFO=$(nvidia-smi --query-gpu=name --format=csv,noheader,nounits) || echo "Issue with NVIDIA SMI"
echo "$GPU_INFO"
if [ -n "$GPU_INFO" ]; then
echo "NVIDIA GPU detected: $GPU_INFO"
HAS_GPU=true
else
echo "Nvidia SMI exists, No NVIDIA GPU detected. Perhaps you need to re-install NVIDIA drivers."
fi
fi
#install uv
pip install uv
echo "HAS_GPU=$HAS_GPU"
if [ "$HAS_GPU" = true ] ; then
echo "Your computer has a GPU; installing cuda:"
conda install -y cuda -c nvidia/label/cuda-12.1.1
echo "Installing requirements:"
# Install the python requirements
if ! ls "$TLAB_CODE_DIR" | grep requirements-uv.txt; then
cp "$RUN_DIR"/requirements-uv.txt "$TLAB_CODE_DIR"/requirements-uv.txt
fi
uv pip install --upgrade -r "$TLAB_CODE_DIR"/requirements-uv.txt
# Install Flash Attention separately - it doesn't play well in requirements file
# Using instructions from https://github.com/Dao-AILab/flash-attention
uv pip install packaging
uv pip install ninja
uv pip install -U flash-attn==2.7.3 --no-build-isolation
###
else
echo "No NVIDIA GPU detected drivers detected. Install NVIDIA drivers to enable GPU support."
echo "https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#pre-installation-actions"
echo "Installing Tranformer Lab requirements without GPU support"
if ! ls "$TLAB_CODE_DIR" | grep requirements-uv.txt; then
cp "$RUN_DIR"/requirements-no-gpu-uv.txt "$TLAB_CODE_DIR"/requirements-no-gpu-uv.txt
fi
uv pip install --upgrade -r "$TLAB_CODE_DIR"/requirements-no-gpu-uv.txt
fi
# Check if the uvicorn command works:
if ! command -v uvicorn &> /dev/null; then
abort "❌ Uvicorn is not installed. This usually means that the installation of dependencies failed."
else
ohai "✅ Uvicorn is installed."
fi
# Record the status after this install for debugging and to check if an install has been attmeped
PIP_LIST=$(pip list --format json)
echo "${PIP_LIST}" > "${TLAB_CODE_DIR}/INSTALLED_DEPENDENCIES"
echo "🌕 Step 4: COMPLETE"
}
list_installed_packages() {
unset_conda_for_sure
eval "$(${CONDA_BIN} shell.bash hook)"
conda activate ${ENV_DIR}
pip list --format json
}
list_environments() {
check_if_conda_envronments_dot_text_is_writable
unset_conda_for_sure
eval "$(${CONDA_BIN} shell.bash hook)"
conda env list
}
doctor() {
title "Doctor"
ohai "Checking if everything is installed correctly."
echo "Your machine is: $OS"
echo "Your shell is: $SHELL"
if command -v "${CONDA_BIN}" &> /dev/null; then
echo "Your conda version is: $(${CONDA_BIN} --version)" || echo "Issue with conda"
echo "Conda is seen in path at at: $(which conda)" || echo "Conda is not in your path"
else
echo "Conda is not installed at ${MINICONDA_ROOT}. Please install Conda using '${TLAB_DIR}/src/install.sh install_conda' and try again."
fi
if command -v nvidia-smi &> /dev/null; then
echo "Your nvidia-smi version is: $(nvidia-smi --version)"
else
echo "nvidia-smi is not installed."
fi
check_conda
check_python
}
print_success_message() {
title "Installation Complete"
echo "------------------------------------------"
echo "Transformer Lab is installed to:"
echo " ${TLAB_DIR}"
echo "Your workspace is located at:"
echo " ${TLAB_DIR}/workspace"
echo "Your conda environment is at:"
echo " ${ENV_DIR}"
echo "You can run Transformer Lab with:"
echo " conda activate ${ENV_DIR}"
echo " cd ${TLAB_CODE_DIR}"
echo " ./run.sh"
echo "------------------------------------------"
echo
}
# Check if there are arguments to this script, and if so, run the appropriate function.
if [[ "$#" -eq 0 ]]; then
title "Performing a full installation of Transformer Lab."
download_transformer_lab
install_conda
create_conda_environment
install_dependencies
print_success_message
else
for arg in "$@"
do
case $arg in
download_transformer_lab)
download_transformer_lab
;;
install_conda)
install_conda
;;
create_conda_environment)
create_conda_environment
;;
install_dependencies)
install_dependencies
;;
doctor)
doctor
;;
list_installed_packages)
list_installed_packages
;;
list_environments)
list_environments
;;
*)
# Print allowed arguments
echo "Allowed arguments: [download_transformer_lab, install_conda, create_conda_environment, install_dependencies] or leave blank to perform a full installation."
abort "❌ Unknown argument: $arg"
;;
esac
done
fi