Ctrl K
ComunidadGuías…

install-local-ai.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# shellcheck source=/dev/null
. "$SCRIPT_DIR/lib/bootstrap.sh" 2>/dev/null ||
  . "$SCRIPT_DIR/../lib/bootstrap.sh" 2>/dev/null ||
  { echo "Error: missing LocalAI bootstrap library: lib/bootstrap.sh" >&2; exit 1; }
source_localai_lib install.sh
source_localai_lib cuda.sh

AI_DIR="${LOCALAI_DIR:-}"
BIN_DIR=""
LIB_DIR=""
CONF_DIR=""
MODELS_DIR=""
LOCALAI_CLI_PATH=""
LOCALAI_CLI_LINK=""
LLAMA_CPP_BACKEND="${LLAMA_CPP_BACKEND:-$LOCALAI_DEFAULT_BACKEND}"
LLAMA_CPP_ASSET_RE=""
LLAMA_CPP_URL=""
LLAMA_CPP_JSON=""
LLAMA_SWAP_JSON=""

log() {
  printf '\n==> %s\n' "$*"
}

fail() {
  echo "Error: $*" >&2
  exit 1
}

usage() {
  cat <<EOF
Usage: $0 [--dir PATH]

Installs LocalAI into the selected directory.

Options:
  --dir PATH     Install LocalAI into PATH. Same as LOCALAI_DIR=PATH.
  -h, --help     Show this help
EOF
}

parse_args() {
  while [ "$#" -gt 0 ]; do
    case "$1" in
      --dir)
        [ "$#" -ge 2 ] || fail "missing path after --dir"
        AI_DIR="$2"
        shift 2
        ;;
      --dir=*)
        AI_DIR="${1#--dir=}"
        shift
        ;;
      -h|--help)
        usage
        exit 0
        ;;
      *)
        usage >&2
        exit 2
        ;;
    esac
  done
}

select_ai_dir() {
  local answer

  if [ -n "$AI_DIR" ]; then
    AI_DIR="$(expand_path "$AI_DIR")"
    log "Using LocalAI install directory: $AI_DIR"
  elif [ -t 0 ]; then
    printf 'LocalAI install directory [%s]: ' "$LOCALAI_DEFAULT_DIR_DISPLAY"
    read -r answer
  else
    answer=""
    log "No interactive terminal detected. Using default install directory: $LOCALAI_DEFAULT_DIR_DISPLAY"
  fi

  AI_DIR="${AI_DIR:-$(expand_path "${answer:-$LOCALAI_DEFAULT_DIR}")}"
  case "$AI_DIR" in
    /*) ;;
    *) fail "install directory must be an absolute path or start with ~" ;;
  esac
  case "$AI_DIR" in
    *[[:space:]%]*) fail "install directory cannot contain spaces or percent signs because it is used in a systemd service" ;;
  esac
}

install_system_dependencies() {
  local packages
  local sudo_cmd=()

  log "Installing system dependencies"

  if [ "$(id -u)" -ne 0 ]; then
    command -v sudo >/dev/null 2>&1 || fail "sudo is required to install system dependencies when not running as root"
    sudo_cmd=(sudo)
  fi

  if command -v apt-get >/dev/null 2>&1; then
    "${sudo_cmd[@]}" apt-get update
    read -r -a packages <<< "$LOCALAI_APT_PACKAGES"
    [ "$LLAMA_CPP_BACKEND" = "cuda" ] && read -r -a packages <<< "$LOCALAI_APT_PACKAGES $LOCALAI_CUDA_APT_PACKAGES"
    "${sudo_cmd[@]}" apt-get install -y "${packages[@]}"
  elif command -v dnf >/dev/null 2>&1; then
    read -r -a packages <<< "$LOCALAI_DNF_PACKAGES"
    [ "$LLAMA_CPP_BACKEND" = "cuda" ] && read -r -a packages <<< "$LOCALAI_DNF_PACKAGES $LOCALAI_CUDA_DNF_PACKAGES"
    "${sudo_cmd[@]}" dnf install -y "${packages[@]}" ||
      fail "failed to install dnf packages. On RHEL 7/8, enable EPEL if jq is unavailable in the enabled repositories."
  elif command -v yum >/dev/null 2>&1; then
    read -r -a packages <<< "$LOCALAI_YUM_PACKAGES"
    [ "$LLAMA_CPP_BACKEND" = "cuda" ] && read -r -a packages <<< "$LOCALAI_YUM_PACKAGES $LOCALAI_CUDA_YUM_PACKAGES"
    "${sudo_cmd[@]}" yum install -y "${packages[@]}" ||
      fail "failed to install yum packages. On RHEL 7/8, enable EPEL if jq is unavailable in the enabled repositories."
  else
    fail "supported package manager not found. Install required packages listed in localai.conf, then rerun this installer."
  fi
}

resolve_llama_cpp_url() {
  local release_api

  log "Finding llama.cpp $LLAMA_CPP_VERSION asset for backend: $LLAMA_CPP_BACKEND"
  if [ "$LLAMA_CPP_VERSION" = "latest" ]; then
    LLAMA_CPP_JSON="$(resolve_llama_cpp_latest_json)"
    LLAMA_CPP_VERSION="$(jq -er '.tag_name' <<<"$LLAMA_CPP_JSON")"
  else
    release_api="$(release_api_for_version "$LLAMA_CPP_VERSION" "$LLAMA_CPP_RELEASE_API" "$LLAMA_CPP_LATEST_API")"
    LLAMA_CPP_JSON="$(github_api_get "$release_api")"
  fi

  # cuda has no prebuilt release asset -- it's a source build pinned to this
  # same resolved $LLAMA_CPP_VERSION tag (a real git tag either way, stable
  # or bleeding-edge), so cuda and the other backends never silently drift to
  # different llama.cpp revisions.
  [ "$LLAMA_CPP_BACKEND" = "cuda" ] && return 0

  # A stable vX.Y.Z release carries no binaries of its own -- resolve to the
  # bleeding-edge release its assets actually live in.
  LLAMA_CPP_BINARY_JSON="$(resolve_llama_cpp_binary_json "$LLAMA_CPP_JSON")"
  LLAMA_CPP_URL="$(release_asset_url "$LLAMA_CPP_BINARY_JSON" "$LLAMA_CPP_ASSET_RE")"

  [ -n "$LLAMA_CPP_URL" ] || fail "no llama.cpp asset found for backend '$LLAMA_CPP_BACKEND' in release $LLAMA_CPP_VERSION"
}

resolve_llama_swap_release() {
  local release_api

  log "Finding llama-swap $LLAMA_SWAP_VERSION asset"
  if [ "$LLAMA_SWAP_VERSION" = "latest" ]; then
    LLAMA_SWAP_JSON="$(github_api_get "$LLAMA_SWAP_LATEST_API")"
    LLAMA_SWAP_VERSION="$(jq -er '.tag_name' <<<"$LLAMA_SWAP_JSON")"
  else
    release_api="$(release_api_for_version "$LLAMA_SWAP_VERSION" "$LLAMA_SWAP_RELEASE_API" "$LLAMA_SWAP_LATEST_API")"
    LLAMA_SWAP_JSON="$(github_api_get "$release_api")"
  fi
  LLAMA_SWAP_URL="${LLAMA_SWAP_URL:-$(release_asset_url "$LLAMA_SWAP_JSON" "$LLAMA_SWAP_ASSET_RE")}"

  [ -n "$LLAMA_SWAP_URL" ] || fail "no llama-swap Linux amd64 asset found in release $LLAMA_SWAP_VERSION"
}

explain_llama_server_failure() {
  local hint
  hint="$(backend_dependency_hint "$LLAMA_CPP_BACKEND" 2>/dev/null)" ||
    hint="Fix:
  Install the missing runtime libraries for your selected backend and rerun."

  cat >&2 <<EOF
Error: installed llama.cpp backend '$LLAMA_CPP_BACKEND' did not run on this system.

This installer uses upstream llama.cpp Linux x64 release archives. Their file
names include "ubuntu" because that is how upstream publishes them; they can
work on other glibc Linux distributions when runtime libraries are available.

$hint

Or, try another backend:
  LLAMA_CPP_BACKEND=cpu $0
  LLAMA_CPP_BACKEND=vulkan $0
  LLAMA_CPP_BACKEND=auto $0
EOF
}

install_llama_cpp_archive() {
  local archive="$1"

  rm -rf "$DOWNLOAD_DIR/llama.cpp"
  mkdir -p "$DOWNLOAD_DIR/llama.cpp"
  tar -xzf "$archive" \
    -C "$DOWNLOAD_DIR/llama.cpp" \
    || fail "failed to extract llama.cpp"

  install_llama_cpp_release_dir "$DOWNLOAD_DIR/llama.cpp" "$DOWNLOAD_DIR"
}

fallback_to_cpu_backend() {
  local failed_backend="$LLAMA_CPP_BACKEND"

  # An explicit cuda request must fail rather than silently substitute CPU
  # (matching cuda_resolve_explicit_backend and the updater). Only `auto`,
  # which chose cuda on its own, or an explicit LOCALAI_CUDA_FALLBACK=1 may
  # fall back here.
  if [ "$failed_backend" = "cuda" ] &&
    [ "${ORIGINAL_LLAMA_CPP_BACKEND:-}" != "auto" ] &&
    [ "${LOCALAI_CUDA_FALLBACK:-0}" != "1" ]; then
    explain_llama_server_failure
    exit 1
  fi

  if [ "$LOCALAI_CPU_FALLBACK" != "1" ] || [ "$LLAMA_CPP_BACKEND" = "cpu" ]; then
    explain_llama_server_failure
    exit 1
  fi

  echo "Warning: llama.cpp backend '$failed_backend' did not run; retrying with CPU backend." >&2
  LLAMA_CPP_BACKEND="cpu"
  select_llama_cpp_asset_regex ""
  resolve_llama_cpp_url

  log "Downloading llama.cpp $LLAMA_CPP_VERSION (cpu fallback backend)"
  download_verified_asset "$LLAMA_CPP_BINARY_JSON" "$LLAMA_CPP_URL" "$DOWNLOAD_DIR/llama.cpp.cpu.tar.gz" "llama.cpp cpu fallback"

  log "Installing llama.cpp CPU fallback"
  install_llama_cpp_archive "$DOWNLOAD_DIR/llama.cpp.cpu.tar.gz"
  if ! llama_server_runs; then
    explain_llama_server_failure
    exit 1
  fi
}

cleanup_bin_artifacts() {
  log "Cleaning old llama.cpp folders and archives"

  find "$BIN_DIR" -mindepth 1 -maxdepth 1 -type d ! -name llama.cpp ! -name llama.cpp.d ! -name "${LOCALAI_PREVIOUS_SUBDIR:-previous}" ! -name "${LOCALAI_PREVIOUS_SUBDIR:-previous}.pending" -exec rm -rf -- {} +
  find "$BIN_DIR" -mindepth 1 -maxdepth 1 -type f -name '*.tar.gz' -delete
}

[ "$(uname -m)" = "x86_64" ] || fail "this installer currently supports x86_64 Linux only"

parse_args "$@"
select_ai_dir
BIN_DIR="$AI_DIR/$LOCALAI_BIN_SUBDIR"
LIB_DIR="$AI_DIR/$LOCALAI_LIB_SUBDIR"
CONF_DIR="$AI_DIR/$LOCALAI_CONF_SUBDIR"
MODELS_DIR="$AI_DIR/$LOCALAI_MODELS_SUBDIR"
LOCALAI_CLI_PATH="$BIN_DIR/$LOCALAI_CLI_NAME"
LOCALAI_CLI_LINK="$LOCALAI_USER_BIN_DIR/$LOCALAI_CLI_NAME"
resolve_llama_swap_paths
ORIGINAL_LLAMA_CPP_BACKEND="$LLAMA_CPP_BACKEND"
select_llama_cpp_asset_regex ""

mkdir -p "$AI_DIR" "$BIN_DIR" "$LIB_DIR" "$CONF_DIR" "$MODELS_DIR" "$CONF_DIR/$LOCALAI_MODELS_OVERRIDE_SUBDIR" "$CONF_DIR/$LOCALAI_KEYS_SUBDIR"
if [ ! -f "$CONF_DIR/$LOCALAI_PORT_FILE" ] && [ -f "$AI_DIR/$LOCALAI_PORT_FILE" ]; then
  cp "$AI_DIR/$LOCALAI_PORT_FILE" "$CONF_DIR/$LOCALAI_PORT_FILE"
fi

###############################################################################
# INSTALL SYSTEM DEPENDENCIES
###############################################################################

install_system_dependencies
# Recorded in the install manifest before "latest" resolves to a tag.
LLAMA_CPP_POLICY="$LLAMA_CPP_VERSION"
LLAMA_SWAP_POLICY="$LLAMA_SWAP_VERSION"
resolve_llama_cpp_url
resolve_llama_swap_release

###############################################################################
# DOWNLOAD RELEASE ARCHIVES
###############################################################################

DOWNLOAD_DIR=$(mktemp -d)
SERVICE_WAS_RUNNING=0
INSTALL_COMPLETE=0
cleanup_install() {
  local status=$?

  trap - EXIT
  [ -z "${DOWNLOAD_DIR:-}" ] || rm -rf -- "$DOWNLOAD_DIR"
  if [ "${SERVICE_WAS_RUNNING:-0}" -eq 1 ] && [ "${INSTALL_COMPLETE:-0}" -eq 0 ]; then
    echo "Install failed; restarting the previous LocalAI service..." >&2
    if has_user_service; then
      systemctl --user start "$LOCALAI_SERVICE_NAME" || true
    elif [ -x "$BIN_DIR/start.sh" ]; then
      "$BIN_DIR/start.sh" || true
    fi
  fi
  exit "$status"
}
trap cleanup_install EXIT

if [ "$LLAMA_CPP_BACKEND" != "cuda" ]; then
  log "Downloading llama.cpp $LLAMA_CPP_VERSION ($LLAMA_CPP_BACKEND backend)"
  download_verified_asset "$LLAMA_CPP_BINARY_JSON" "$LLAMA_CPP_URL" "$DOWNLOAD_DIR/llama.cpp.tar.gz" "llama.cpp"
fi

log "Downloading llama-swap $LLAMA_SWAP_VERSION"

download_verified_asset "$LLAMA_SWAP_JSON" "$LLAMA_SWAP_URL" "$DOWNLOAD_DIR/llama-swap.tar.gz" "llama-swap"

if pid_file_matches_process "$CONF_DIR/$LOCALAI_PID_FILE" "$CONF_DIR/$LOCALAI_PID_FILE.start"; then
  SERVICE_WAS_RUNNING=1
elif [ -f "$AI_DIR/$LOCALAI_PID_FILE" ] &&
  pid_file_matches_process "$AI_DIR/$LOCALAI_PID_FILE" "$AI_DIR/$LOCALAI_PID_FILE.start"; then
  SERVICE_WAS_RUNNING=1
elif has_user_service && systemctl --user is-active --quiet "$LOCALAI_SERVICE_NAME"; then
  SERVICE_WAS_RUNNING=1
fi

if [ "$SERVICE_WAS_RUNNING" -eq 1 ]; then
  if [ -x "$BIN_DIR/stop.sh" ]; then
    log "Stopping the existing LocalAI service"
    "$BIN_DIR/stop.sh"
  elif [ -x "$AI_DIR/stop.sh" ]; then
    log "Stopping the existing LocalAI service"
    "$AI_DIR/stop.sh"
  fi
fi

###############################################################################
# INSTALL LLAMA.CPP
###############################################################################

log "Installing llama.cpp"

if [ "$LLAMA_CPP_BACKEND" = "cuda" ]; then
  cuda_build_and_install "$DOWNLOAD_DIR" || fallback_to_cpu_backend
else
  install_llama_cpp_archive "$DOWNLOAD_DIR/llama.cpp.tar.gz"
  if ! llama_server_runs; then
    fallback_to_cpu_backend
  fi
fi
cleanup_bin_artifacts

###############################################################################
# INSTALL LLAMA-SWAP
###############################################################################

log "Installing llama-swap"

mkdir -p "$DOWNLOAD_DIR/llama-swap"
tar -xzf "$DOWNLOAD_DIR/llama-swap.tar.gz" \
  -C "$DOWNLOAD_DIR/llama-swap" \
  || fail "failed to extract llama-swap"

LLAMA_SWAP_REAL=$(
  find "$DOWNLOAD_DIR/llama-swap" -type f -name llama-swap -print -quit
)
[ -n "$LLAMA_SWAP_REAL" ] || fail "llama-swap was not found in the archive"

install -m755 "$LLAMA_SWAP_REAL" "$LLAMA_SWAP_INSTALL_PATH"

###############################################################################
# SELECT API PORT
###############################################################################

if [ ! -f "$CONF_DIR/$LOCALAI_PORT_FILE" ]; then
  PORT="$LOCALAI_DEFAULT_PORT"
  while port_is_listening "$PORT"; do
    PORT=$((PORT + 1))
  done
  echo "$PORT" > "$CONF_DIR/$LOCALAI_PORT_FILE"
fi

###############################################################################
# INSTALL HELPER SCRIPTS AND SYSTEMD SERVICE
###############################################################################

log "Installing helper scripts and systemd service"

mkdir -p "$LOCALAI_SYSTEMD_USER_DIR"
mkdir -p "$LOCALAI_USER_BIN_DIR"

write_systemd_user_service "$LOCALAI_SYSTEMD_USER_DIR/$LOCALAI_SERVICE_NAME" "$BIN_DIR" "$CONF_DIR/$LOCALAI_PID_FILE"

install -m755 "$SCRIPT_DIR/start.sh" "$BIN_DIR/start.sh"
install -m755 "$SCRIPT_DIR/stop.sh" "$BIN_DIR/stop.sh"
install -m755 "$SCRIPT_DIR/rebuild-config.sh" "$BIN_DIR/rebuild-config.sh"
install -m755 "$SCRIPT_DIR/update-local-ai.sh" "$BIN_DIR/update-local-ai.sh"
install -m755 "$SCRIPT_DIR/uninstall-local-ai.sh" "$BIN_DIR/uninstall-local-ai.sh"
install -m755 "$SCRIPT_DIR/localai" "$LOCALAI_CLI_PATH"
install_localai_libs "$SCRIPT_DIR" "$LIB_DIR"
PREVIOUS_LOCALAI_CONF="$CONF_DIR/localai.conf"
if [ -f "$PREVIOUS_LOCALAI_CONF" ]; then
  cp "$PREVIOUS_LOCALAI_CONF" "$DOWNLOAD_DIR/localai.conf.previous"
  PREVIOUS_LOCALAI_CONF="$DOWNLOAD_DIR/localai.conf.previous"
elif [ -f "$AI_DIR/localai.conf" ]; then
  cp "$AI_DIR/localai.conf" "$DOWNLOAD_DIR/localai.conf.previous"
  PREVIOUS_LOCALAI_CONF="$DOWNLOAD_DIR/localai.conf.previous"
fi
install -m644 "$SCRIPT_DIR/localai.conf" "$CONF_DIR/localai.conf"
append_runtime_tuning "$PREVIOUS_LOCALAI_CONF" "installer"

for OLD_HELPER in start.sh stop.sh rebuild-config.sh update-local-ai.sh uninstall-local-ai.sh "$LOCALAI_CLI_NAME"; do
  rm -f -- "$AI_DIR/$OLD_HELPER"
done
for OLD_CONFIG in localai.conf "$LOCALAI_BACKEND_FILE" "$LOCALAI_CONFIG_FILE" "$LOCALAI_PORT_FILE" "$LOCALAI_PID_FILE"; do
  rm -f -- "$AI_DIR/$OLD_CONFIG"
done

ln -sfn "$LOCALAI_CLI_PATH" "$LOCALAI_CLI_LINK"

if ! systemctl --user daemon-reload; then
  echo "Warning: could not reload the systemd user manager." >&2
  echo "You can still use the scripts in $AI_DIR directly." >&2
fi

if [ "$SERVICE_WAS_RUNNING" -eq 1 ]; then
  log "Starting LocalAI service"
  if has_user_service; then
    systemctl --user start "$LOCALAI_SERVICE_NAME"
  elif [ -x "$BIN_DIR/start.sh" ]; then
    "$BIN_DIR/start.sh"
  fi
fi
INSTALL_COMPLETE=1

if [ "$LLAMA_CPP_BACKEND" = "cuda" ]; then
  MANIFEST_CPP_URL=""
  MANIFEST_CPP_DIGEST=""
else
  MANIFEST_CPP_URL="$LLAMA_CPP_URL"
  MANIFEST_CPP_DIGEST="$(release_asset_digest "$LLAMA_CPP_BINARY_JSON" "$LLAMA_CPP_URL")"
fi
localai_write_manifest "$LLAMA_CPP_POLICY" "$LLAMA_CPP_VERSION" "$MANIFEST_CPP_URL" "$MANIFEST_CPP_DIGEST" \
  "$LLAMA_SWAP_POLICY" "$LLAMA_SWAP_VERSION" "$LLAMA_SWAP_URL" ||
  echo "Warning: could not write $CONF_DIR/$LOCALAI_MANIFEST_FILE" >&2

echo
echo "============================================================"
echo " LocalAI installation completed"
echo "============================================================"
echo
echo "LocalAI commands:"
echo
echo "  Start service:"
echo "    localai start"
echo
echo "  Stop service:"
echo "    localai stop"
echo
echo "  Restart service:"
echo "    localai restart"
echo
echo "  Check status:"
echo "    localai status"
echo
echo "  Check API health:"
echo "    localai check"
echo
echo "  View logs:"
echo "    localai logs"
echo
echo "  List models:"
echo "    localai models"
echo
echo "  Load models:"
echo "    localai load MODEL"
echo "    localai load all"
echo
echo "  Unload models:"
echo "    localai unload MODEL"
echo "    localai unload all"
echo
echo "  Update components:"
echo "    localai update"
echo
echo "  Show versions:"
echo "    localai version"
echo
echo "  Uninstall:"
echo "    localai uninstall"
echo
echo "  Enable auto-start on login:"
echo "    systemctl --user enable localai"
echo
echo "  Disable auto-start:"
echo "    systemctl --user disable localai"
echo
echo "Helper scripts:"
echo
echo "  Start manually:"
echo "    $BIN_DIR/start.sh"
echo
echo "  Stop manually:"
echo "    $BIN_DIR/stop.sh"
echo
echo "  Rebuild config:"
echo "    $BIN_DIR/rebuild-config.sh"
echo
echo "API endpoint:"
echo "  http://localhost:$(cat "$CONF_DIR/$LOCALAI_PORT_FILE")"
echo
echo "Service file:"
echo "  $LOCALAI_SYSTEMD_USER_DIR/$LOCALAI_SERVICE_NAME"
echo "  ExecStart=$BIN_DIR/start.sh"
echo "  ExecStop=$BIN_DIR/stop.sh"
echo
echo "Models directory:"
echo "  $MODELS_DIR"
echo
echo "CLI command:"
echo "  $LOCALAI_CLI_LINK"
echo
echo "Current versions:"
llama_cpp_display_version "$LLAMA_CPP_BACKEND"
echo "llama.cpp backend: $LLAMA_CPP_BACKEND"
"$LLAMA_SWAP_BIN" --version 2>&1 | awk 'NR == 1 {print; exit}'
echo
if ! localai_has_model_entries "$MODELS_DIR"; then
  echo "No GGUF models found in:"
  echo "  $MODELS_DIR"
  echo
  echo "LocalAI is installed, but chat requests will not work until you add a model."
  echo "Download or copy a .gguf model into the models directory, then start LocalAI."
  echo
fi
echo "============================================================"
ENSURE_PATH_STATUS=0
ensure_cli_on_path || ENSURE_PATH_STATUS=$?
reconnect_shell_on_path "$ENSURE_PATH_STATUS"