Guest User

Ollama Symlink Manager

a guest
Aug 19th, 2025
20
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.20 KB | Source Code | 0 0
  1. #!/usr/bin/env bash
  2. #
  3. # sync.sh
  4. #
  5. # Description:
  6. # - Lists discovered model manifests (only showing model names in whiptail),
  7. # - Uses an associative array to store hashed-file info (not shown in the menu),
  8. # - Links checked models (symlinks), unlinks unchecked ones,
  9. # - Leaves real/copied files intact (removes only symlinks).
  10. #
  11. # Requirements:
  12. # - Bash 4+ (associative arrays)
  13. # - whiptail (usually in the 'newt' package)
  14. # - jq
  15.  
  16. ###############################################################################
  17. # ADJUST THESE PATHS AS NEEDED
  18. ###############################################################################
  19. koboldcpp_dir="/data/LLM/kobold"
  20. ollama_dir="/data/LLM/ollama"
  21.  
  22. ###############################################################################
  23. # GLOBALS
  24. ###############################################################################
  25. declare -A MODEL_HASH_MAP # key = model_name, value = hashed_file
  26. declare -a MENU_ARRAY=() # array for whiptail arguments
  27. declare -a ALL_MODELS=() # array of all discovered model names
  28.  
  29. ###############################################################################
  30. # Check dependencies & directories
  31. ###############################################################################
  32. if ! command -v jq &>/dev/null; then
  33. echo "ERROR: 'jq' is not installed."
  34. exit 1
  35. fi
  36.  
  37. if [[ ! -d "$koboldcpp_dir" ]]; then
  38. echo "ERROR: koboldcpp_dir does not exist: $koboldcpp_dir"
  39. exit 1
  40. fi
  41.  
  42. if [[ ! -d "$ollama_dir" ]]; then
  43. echo "ERROR: ollama_dir does not exist: $ollama_dir"
  44. exit 1
  45. fi
  46.  
  47. ###############################################################################
  48. # Function to find manifest files
  49. ###############################################################################
  50. get_model_files() {
  51. find "$ollama_dir/models/manifests" -type f
  52. }
  53.  
  54. ###############################################################################
  55. # Build whiptail checklist items
  56. # We'll store hashed_file in MODEL_HASH_MAP, but whiptail sees only:
  57. # <model_name> <dummy_display> <ON|OFF>
  58. ###############################################################################
  59. generate_menu_options() {
  60. local files="$1"
  61.  
  62. while IFS= read -r file_path; do
  63. # Example: /data/LLM/ollama/models/manifests/Anubis-70B-v1-GGUF/Q4_K_L
  64. parent_dir=$(basename "$(dirname "$file_path")")
  65. quant=$(basename "$file_path")
  66.  
  67. # Extract hashed file from JSON
  68. hashed_file=$(jq -r '
  69. .layers[]
  70. | select(.mediaType == "application/vnd.ollama.image.model")
  71. | .digest
  72. ' "$file_path")
  73. hashed_file="${hashed_file#sha256:}" # strip "sha256:"
  74.  
  75. # Construct final model name (no spaces or quotes)
  76. model_name="${parent_dir}-${quant}"
  77.  
  78. # Symlink name & target
  79. symlink_name="${koboldcpp_dir}/${model_name}.gguf"
  80. target_file="${ollama_dir}/models/blobs/sha256-${hashed_file}"
  81.  
  82. # Default ON if there's already a symlink pointing to that target
  83. if [[ -L "$symlink_name" && "$(readlink -f "$symlink_name")" == "$target_file" ]]; then
  84. default_state="ON"
  85. else
  86. default_state="OFF"
  87. fi
  88.  
  89. # Save hashed_file in an associative array
  90. MODEL_HASH_MAP["$model_name"]="$hashed_file"
  91. # Keep track of this model (for linking/unlinking later)
  92. ALL_MODELS+=("$model_name")
  93.  
  94. # Add three arguments to MENU_ARRAY for whiptail:
  95. # 1) The "tag" (model_name)
  96. # 2) The "item" for display (we use a dash, so user sees e.g. "Midnight-Miqu-70B ... - ON")
  97. # 3) Default ON/OFF
  98. MENU_ARRAY+=( "$model_name" "" "$default_state" )
  99. done <<< "$files"
  100. }
  101.  
  102. ###############################################################################
  103. # MAIN SCRIPT
  104. ###############################################################################
  105.  
  106. # 1) Gather all manifest files
  107. model_files=$(get_model_files)
  108. if [[ -z "$model_files" ]]; then
  109. echo "No manifest files found under '$ollama_dir/models/manifests'."
  110. exit 0
  111. fi
  112.  
  113. # 2) Build an array of menu options
  114. generate_menu_options "$model_files"
  115.  
  116. # 3) Show the whiptail checklist, passing the array
  117. selection=$(
  118. whiptail --title "Manage Koboldcpp Models" \
  119. --checklist "Select Ollama models to link or unlink:" \
  120. 20 78 12 \
  121. "${MENU_ARRAY[@]}" \
  122. 3>&1 1>&2 2>&3
  123. )
  124.  
  125. # If user hits Cancel or whiptail fails
  126. if [[ $? -ne 0 ]]; then
  127. echo "Operation canceled."
  128. exit 0
  129. fi
  130.  
  131. # Whiptail returns a string of double-quoted model names, e.g.:
  132. # "\"Midnight-Miqu-70B-v1.5-GGUF-IQ4_XS\" \"Mistral-Small-22B-ArliAI-RPMax...\""
  133. # We'll parse that into a bash array:
  134. IFS=' ' read -r -a selected_array <<< "$selection"
  135.  
  136. # Remove leading/trailing quotes from each selected item
  137. selected_array_no_quotes=()
  138. for sel in "${selected_array[@]}"; do
  139. tmp="${sel#\"}" # remove leading quote
  140. tmp="${tmp%\"}" # remove trailing quote
  141. selected_array_no_quotes+=( "$tmp" )
  142. done
  143.  
  144. echo "User selected: ${selected_array_no_quotes[*]}"
  145.  
  146. ###############################################################################
  147. # 4) For every discovered model, link if selected, unlink if not selected
  148. ###############################################################################
  149. for model in "${ALL_MODELS[@]}"; do
  150. symlink_name="${koboldcpp_dir}/${model}.gguf"
  151. hashed_file="${MODEL_HASH_MAP["$model"]}"
  152. target_file="${ollama_dir}/models/blobs/sha256-${hashed_file}"
  153.  
  154. # Determine if user checked this model
  155. found=false
  156. for sel_item in "${selected_array_no_quotes[@]}"; do
  157. if [[ "$sel_item" == "$model" ]]; then
  158. found=true
  159. break
  160. fi
  161. done
  162.  
  163. if $found; then
  164. # Link
  165. echo "Linking: $model -> $target_file"
  166. ln -sf "$target_file" "$symlink_name"
  167. else
  168. # Unlink if it's a symlink
  169. if [[ -L "$symlink_name" ]]; then
  170. echo "Unlinking symlink: $model"
  171. rm -f "$symlink_name"
  172. else
  173. echo "Skipping removal of '$symlink_name' (not a symlink)."
  174. fi
  175. fi
  176. done
  177.  
  178. echo "Script completed."
Advertisement
Add Comment
Please, Sign In to add comment