#!/usr/bin/env bash set -Eeuo pipefail script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" release_name=Ling-3.0-Flash-CIRU-int4-Strix-native export PATH="$HOME/.local/bin:$PATH" usage() { cat <&2 exit 2 } absolute_path() { local path="$1" case "$path" in '~') path="$HOME" ;; [~]/*) path="$HOME/${path#\~/}" ;; '$HOME') path="$HOME" ;; '$HOME/'*) path="$HOME/${path#\$HOME/}" ;; '${HOME}') path="$HOME" ;; '${HOME}/'*) path="$HOME/${path#\$\{HOME\}/}" ;; '$PWD') path="$PWD" ;; '$PWD/'*) path="$PWD/${path#\$PWD/}" ;; '${PWD}') path="$PWD" ;; '${PWD}/'*) path="$PWD/${path#\$\{PWD\}/}" ;; esac case "$path" in *'$'*) die "unexpanded shell variable in path: $path" ;; esac if command -v realpath >/dev/null 2>&1; then realpath -m -- "$path" elif [[ "$path" = /* ]]; then printf '%s\n' "$path" else printf '%s/%s\n' "$PWD" "${path#./}" fi } write_runtime_env() { local temporary_file="$env_file.tmp.$$" mkdir -p "$install_root" { printf '# Generated by %s/install.sh\n' "$script_dir" printf 'export VENV=%q\n' "$venv" printf 'export MODEL_PATH=%q\n' "$model_path" } >"$temporary_file" mv -f -- "$temporary_file" "$env_file" } prompt_value() { local variable_name="$1" local label="$2" local default_value="$3" local reply read -r -p "$label [$default_value]: " reply printf -v "$variable_name" '%s' "${reply:-$default_value}" } ask_yes_no() { local label="$1" local default_answer="$2" local suffix reply if [ "$default_answer" = yes ]; then suffix='[Y/n]' else suffix='[y/N]' fi while true; do read -r -p "$label $suffix: " reply reply="${reply:-$default_answer}" case "${reply,,}" in y|yes) return 0 ;; n|no) return 1 ;; *) printf 'Please answer yes or no.\n' >&2 ;; esac done } interactive=0 install_root= model_path= install_host_deps=0 download_model=0 build_runtime=1 dry_run=0 print_env=0 upgrade=0 if [ "$#" -eq 0 ]; then usage exit 0 fi while [ "$#" -gt 0 ]; do case "$1" in --interactive) interactive=1 shift ;; --install-root) [ "$#" -ge 2 ] || die '--install-root requires a path' [ -n "$2" ] || die '--install-root requires a non-empty path' install_root="$2" shift 2 ;; --model-path) [ "$#" -ge 2 ] || die '--model-path requires a path' [ -n "$2" ] || die '--model-path requires a non-empty path' model_path="$2" shift 2 ;; --install-host-deps) install_host_deps=1 shift ;; --download-model) download_model=1 shift ;; --upgrade) upgrade=1 shift ;; --skip-build) build_runtime=0 shift ;; --dry-run) dry_run=1 shift ;; --print-env) print_env=1 shift ;; -h|--help) usage exit 0 ;; *) die "unknown option: $1" ;; esac done if [ "$upgrade" = 1 ]; then [ -z "$model_path" ] || die '--model-path is not used with --upgrade' [ "$download_model" = 0 ] || die '--download-model is not used with --upgrade' [ "$build_runtime" = 1 ] || die '--skip-build is not used with --upgrade' [ "$print_env" = 0 ] || die '--print-env is not used with --upgrade' fi if [ "$interactive" = 1 ]; then [ -t 0 ] || die '--interactive requires a terminal; use explicit flags for automation' prompt_value install_root 'Runtime install directory' "${install_root:-$PWD}" if [ "$upgrade" != 1 ]; then prompt_value model_path 'Ling checkpoint directory' \ "${model_path:-$PWD/$release_name}" fi if ask_yes_no 'Run the distro host-dependency installer (may use sudo)?' no; then install_host_deps=1 fi if [ "$upgrade" != 1 ] && [ ! -f "$model_path/config.json" ]; then if ask_yes_no 'Checkpoint not found there. Download it from this repository (~77 GB)?' no; then download_model=1 fi fi fi install_root="$(absolute_path "${install_root:-$PWD}")" case "$install_root" in *[[:space:]:]*) die "install root cannot contain whitespace or ':' because ROCr is loaded through LD_PRELOAD: $install_root" ;; esac venv="$install_root/.venv" env_file="$install_root/ling3-runtime.env" if [ "$upgrade" != 1 ]; then model_path="$(absolute_path "${model_path:-$PWD/$release_name}")" fi if [ "$print_env" = 1 ]; then printf 'export VENV=%q\n' "$venv" printf 'export MODEL_PATH=%q\n' "$model_path" exit 0 fi if [ "$upgrade" = 1 ]; then printf 'CIRU Ling engine-only upgrade\n' printf ' package: %s\n install root: %s\n venv: %s\n' \ "$script_dir" "$install_root" "$venv" printf ' host dependencies: %s\n model files: preserved\n' "$install_host_deps" else printf 'CIRU Ling runtime installation\n' printf ' package: %s\n install root: %s\n venv: %s\n model: %s\n' \ "$script_dir" "$install_root" "$venv" "$model_path" printf ' host dependencies: %s\n download model: %s\n build runtime: %s\n' \ "$install_host_deps" "$download_model" "$build_runtime" fi if [ "$interactive" = 1 ] && ! ask_yes_no 'Continue with these settings?' yes; then printf 'Installation cancelled.\n' exit 0 fi if [ "$dry_run" = 1 ]; then if [ "$install_host_deps" = 1 ]; then printf 'DRY RUN: bash %q\n' "$script_dir/scripts/install-host-deps.sh" fi if [ "$upgrade" = 1 ]; then printf 'DRY RUN: validate %q and preserve model files/configuration\n' "$venv" printf 'DRY RUN: bash %q %q\n' \ "$script_dir/scripts/install-rocr-idle-fix.sh" "$install_root" exit 0 fi if [ "$download_model" = 1 ]; then printf 'DRY RUN: bash %q --model-path %q\n' \ "$script_dir/scripts/download-model.sh" "$model_path" fi if [ "$build_runtime" = 1 ]; then printf 'DRY RUN: bash %q %q\n' \ "$script_dir/scripts/build-vllm-gfx1151.sh" "$install_root" fi printf 'DRY RUN: bash %q %q\n' \ "$script_dir/scripts/install-rocr-idle-fix.sh" "$install_root" printf 'DRY RUN: write %q\n' "$env_file" exit 0 fi if [ "$upgrade" = 1 ]; then [ -x "$venv/bin/python" ] || \ die "existing CIRU runtime is missing Python: $venv/bin/python" [ -x "$venv/bin/vllm" ] || \ die "existing CIRU runtime is missing vLLM: $venv/bin/vllm" if [ "$install_host_deps" = 1 ]; then bash "$script_dir/scripts/install-host-deps.sh" fi bash "$script_dir/scripts/install-rocr-idle-fix.sh" "$install_root" printf '\nEngine upgrade complete. Model files and ling3-runtime.env were unchanged.\n' printf 'Restart the CIRU launcher or service to load the corrected ROCr runtime.\n' printf 'Emergency rollback: set CIRU_DISABLE_ROCR_IDLE_FIX=1 before launching.\n' exit 0 fi # The environment file contains only resolved paths, so create it before any # download or build. A failed dependency/build step must still leave users a # correct, sourceable record of the intended runtime and model locations. write_runtime_env printf 'Prepared environment file: %s\n' "$env_file" if [ "$download_model" != 1 ] && [ ! -f "$model_path/config.json" ]; then printf 'Model download skipped; the runtime will be installed without weights.\n' printf 'Expected future model path: %s\n' "$model_path" fi if [ "$install_host_deps" = 1 ]; then bash "$script_dir/scripts/install-host-deps.sh" fi if [ "$download_model" = 1 ]; then bash "$script_dir/scripts/download-model.sh" --model-path "$model_path" fi if [ "$build_runtime" = 1 ]; then bash "$script_dir/scripts/build-vllm-gfx1151.sh" "$install_root" fi bash "$script_dir/scripts/install-rocr-idle-fix.sh" "$install_root" [ -x "$venv/bin/python" ] || die "runtime Python is missing after installation: $venv/bin/python" [ -x "$venv/bin/vllm" ] || die "vLLM launcher is missing after installation: $venv/bin/vllm" if [ -f "$model_path/config.json" ]; then model_ready=1 elif [ "$download_model" = 1 ]; then die "model download completed without the expected config: $model_path/config.json" else model_ready=0 fi printf '\nInstallation complete.\n' printf 'Environment file: %s\n\n' "$env_file" if [ "$model_ready" = 1 ]; then printf 'Launch the native 256K profile:\n' printf ' source %q\n bash %q\n\n' "$env_file" "$script_dir/scripts/run-256k.sh" else printf 'The optional model download was skipped. Download it later with:\n' printf ' bash %q --model-path %q\n\n' \ "$script_dir/scripts/download-model.sh" "$model_path" printf 'Then launch with:\n' printf ' source %q\n bash %q\n\n' "$env_file" "$script_dir/scripts/run-256k.sh" fi printf 'For agents or automation:\n' printf ' export VENV=%q\n export MODEL_PATH=%q\n' "$venv" "$model_path" if [ "$model_ready" = 1 ]; then printf ' bash %q\n' "$script_dir/scripts/run-256k.sh" else printf ' # Provide the checkpoint before running %q\n' "$script_dir/scripts/run-256k.sh" fi