Compare commits

...
17 Commits
Author SHA1 Message Date
me 487f1f46f3 llm, add Swap Stopper as a service based on custom shell script. 2026-10-10 18:41:50 -07:00
me 0776e89d26 Add shell script which stops LLMs from causing system swap. 2026-10-10 18:41:25 -07:00
me ccd8355946 llm, fix ollama-stop function. 2026-10-10 17:27:47 -07:00
me 942780e6d1 Refactor redundant names, do not need to say "Setup". 2026-10-10 16:48:08 -07:00
me 019066c4dc llm, further reduce contexts based on testing, now using 2.5:14b for Complete successfully alongside the RAM needs of 3:30b on Cline. 2026-10-10 16:42:19 -07:00
me 1f16053042 llm, continue tuning the model context and tokens to prevent swapping. 2026-10-10 16:04:09 -07:00
me bafa669bb5 Update spacing in main YML files. 2026-10-10 15:59:38 -07:00
me 5595ea0888 test, add comment, add names to tasks. 2026-10-10 14:59:12 -07:00
me 3aee0feb39 footer, name tasks and add comment. 2026-10-10 14:56:08 -07:00
me 5d352bbb53 llm, add section comments, ensure ollama allows Codium workflows with concurrency and parallel. 2026-10-10 14:32:28 -07:00
me 6c46439eb6 llm, add shortcuts for stopping all ollama models. 2026-10-10 13:42:36 -07:00
me 648501b7c0 llm, use pipefail on the final lint error. 2026-10-10 13:35:34 -07:00
me e0288ce4d5 llm, filter models by purpose, use chat in Zeroclaw and code in Continue. 2026-10-10 13:33:48 -07:00
me c6cc631d8c llm, tuning the context and token parameters. 2026-10-10 13:10:29 -07:00
me 3381eb6d70 loop, output the command before starting the loop, just to make sure all looks okay. 2026-10-10 12:28:45 -07:00
me 53351fde28 llm, add a finish for open-webui, fix config.yaml for Continue, possibly fix pull-models causing terminals to close when cancelled. 2026-10-10 11:12:25 -07:00
me 7922225b26 Remove unecessary update_cache which eat up time. 2026-10-10 11:11:15 -07:00
13 changed files with 365 additions and 121 deletions

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
#!/bin/sh -e
# 2026-10-10 Hyperling
# Stop a system from going into swap due to LLMs.
### Tuning ###
typeset -i sleep=10 ram_pct_restart=98
### Functions ###
restart_service() {
if [ -z $1 ]; then
echo "ERROR: Service name must be passed to restart_service()."
return 1
fi
sv restart $1 || echo "$1 failed to restart, ignoring."
}
# While we could loop over `ollama ps` and stop them, this ensures that the UIs
# do not sit around waiting for responses from the dead LLMs. It bring them up
# in a fresh state so that work can continue from a more clean slate.
restart_llms() {
restart_service ollama
restart_service zeroclaw
restart_service open-webui
}
### Main ###
typeset -i {total,used}_{ram,swap}=0 \
used_{ram,swap}_pct=0 \
last_{ram,swap}_pct=100 \
count=0
while [[ true ]]; do
count=$(expr $count + 1 )
echo "$(date +'%Y-%m-%d @ %H:%M:%S') - Loop $count"
# RAM #
total_ram=$(free | awk '/^Mem:/ {print $2}')
used_ram=$(free | awk '/^Mem:/ {print $3}')
used_ram_pct=$( \
awk -v used="$used_ram" -v total="$total_ram" \
'BEGIN {printf "%d",(used/total)*100}' \
)
echo "Current RAM usage is $used_ram_pct%, was $last_ram_pct% last loop."
# Check if RAM is over 95%
if [ $used_ram_pct -gt $ram_pct_restart ]; then
echo "WARNING: RAM is over ${ram_pct_restart}% used. Restarting LLMs."
restart_llms
continue
fi
# Swap #
total_swap=$(free | awk '/^Swap:/ {print $2}')
used_swap=$(free | awk '/^Swap:/ {print $3}')
used_swap_pct=$( \
awk -v used="$used_swap" -v total="$total_swap" \
'BEGIN {printf "%d",(used/total)*100}' \
)
echo "Current swap usage is $used_swap_pct%, was $last_swap_pct% last loop."
# Check if swap is growing
if [ $used_swap_pct -gt $last_swap_pct ]; then
echo "WARNING: Swap is growing. Restarting LLMs."
restart_llms
continue
fi
# Finish #
echo -e "Check finished, sleeping for ${sleep}...\n"
last_ram_pct=$used_ram_pct
last_swap_pct=$used_swap_pct
sleep $sleep
done
+41 -16
View File
@@ -2,7 +2,7 @@
# Harmonize systems rather than doing everything manually. :)
# Setup of any device connected to this repo.
- name: Main | Default Setup
- name: Local | Main
hosts: localhost
connection: local
become: true
@@ -10,13 +10,14 @@
tasks:
####### Header #######
- name: Main | Header
ansible.builtin.include_tasks: tasks/header.yml
####### Workstations #######
# Additional setup for systems with GUI.
- name: Main | Workstation Setup
- name: Main | Workstation
block:
- ansible.builtin.include_tasks: facts/workstation/package.yml
@@ -30,6 +31,7 @@
# Window Managers without true Desktop Environments #
- ansible.builtin.include_tasks: tasks/workstation/shared/software/dwm.yml
when: internet
- ansible.builtin.include_tasks: tasks/workstation/shared/software/i3.yml
when: internet
@@ -43,7 +45,9 @@
- ansible.builtin.include_tasks: tasks/workstation/linux/software/gnome.yml
when: ansible_service_mgr == "systemd"
- ansible.builtin.include_tasks: tasks/workstation/linux/software/cinnamon.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/lxqt.yml
when: ansible_system == "Linux"
@@ -52,16 +56,23 @@
## Software Tasks ##
- name: Main | Workstation Setup | Linux
- name: Main | Workstation | Linux
block:
- ansible.builtin.include_tasks: tasks/workstation/linux/software/packages.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/tlp.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/lutris.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/vscodium.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/vpn.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/nodejs.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/hugo.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/brave.yml
when: ansible_pkg_mgr in ("apt", "dnf", "zypper") and not mobile
@@ -77,11 +88,14 @@
and internet
## Configuration Tasks ##
- name: Main | Workstation Setup | Configuration
- name: Main | Workstation | Configuration
block:
- ansible.builtin.include_tasks: tasks/workstation/shared/settings/gnome.yml
- ansible.builtin.include_tasks: tasks/workstation/shared/settings/cinnamon.yml
- ansible.builtin.include_tasks: tasks/workstation/shared/settings/xdg_autostart.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/cron/ansible.yml
@@ -98,17 +112,23 @@
when: vnc
- ansible.builtin.include_tasks: tasks/workstation/shared/settings/services.yml
- ansible.builtin.include_tasks: tasks/workstation/shared/settings/templates.yml
## Final Tasks (SLOW) ##
- name: Main | Workstation Setup | Linux [Slow]
- name: Main | Workstation | Linux [Slow]
block:
- ansible.builtin.include_tasks: tasks/workstation/linux/software/flatpaks.yml
when: flatpak_distro
- ansible.builtin.include_tasks: tasks/workstation/linux/software/java.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/flutter.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/android.yml
- ansible.builtin.include_tasks: tasks/workstation/linux/software/llm.yml
when: ansible_system == "Linux"
@@ -116,12 +136,11 @@
when: workstation
# No longer mining, this is now considered deprecated.
# ####### Miners #######
#
# # Additional setup for miners.
# - name: Main | Miner Setup
# - name: Main | Miner
# block:
#
# ##### Setup #####
@@ -157,32 +176,36 @@
#
# when: miner
####### Servers #######
# Easy to deploy server configurations.
- name: Main | Server Setup
- name: Main | Server
block:
- name: Main | Server Setup | Services
- name: Main | Server | Services
ansible.builtin.include_tasks: tasks/server/software/services.yml
- block:
- name: Main | Server Setup | Certbot
- name: Main | Server | Certbot
ansible.builtin.include_tasks: tasks/server/software/certbot.yml
- name: Main | Server Setup | Certbot Cron
- name: Main | Server | Certbot Cron
ansible.builtin.include_tasks: tasks/server/cron/certbot.yml
when: certbot
- block:
- name: Main | Server Setup | OnlyOffice
- name: Main | Server | OnlyOffice
ansible.builtin.include_tasks: tasks/server/software/onlyoffice.yml
when: onlyoffice
- ansible.builtin.include_tasks: tasks/server/software/influxdb1.yml
when: influxdb1
- ansible.builtin.include_tasks: tasks/server/software/influxdb2.yml
when: influxdb2
- ansible.builtin.include_tasks: tasks/server/software/grafana.yml
when: grafana
@@ -193,8 +216,8 @@
when: server
####### Reporting #######
# Provide information for analysis.
- name: Main | Reporting
block:
@@ -203,10 +226,12 @@
when: internet
- ansible.builtin.include_tasks: tasks/general/tests/services.yml
- ansible.builtin.include_tasks: tasks/general/tests/lynis.yml
- ansible.builtin.include_tasks: tasks/general/tests/nmap.yml
####### Footer #######
- name: Main | Footer
ansible.builtin.include_tasks: tasks/footer.yml
+3 -1
View File
@@ -1,4 +1,5 @@
---
# All the tasks which need run to successfully end the playbook.
####### Footer #######
- name: Footer
@@ -9,4 +10,5 @@
vars:
notify_message: "Provisioning Finished"
- ansible.builtin.include_tasks: tasks/general/tests/lock_remove.yml
- name: Footer | Lock | Remove
ansible.builtin.include_tasks: tasks/general/tests/lock_remove.yml
+1
View File
@@ -1146,6 +1146,7 @@
sleep='echo -e "`date` - Sleeping for $sleep_time... \n"; sleep $sleep_time'
fi
count=0
echo "Command='$cmd'"
while true; do
(( count++ ))
echo "`date` - Loop $count"
+32 -13
View File
@@ -1,40 +1,46 @@
---
####### Lock #######
# Ensure a process is not already running.
- name: Main | Lock
block:
- name: Main | Lock | Check
ansible.builtin.include_tasks: tasks/general/tests/lock_check.yml
- name: Main | Lock | Create
ansible.builtin.include_tasks: tasks/general/tests/lock_create.yml
####### Gather #######
# Load facts for all systems.
- name: Main | Gather
block:
- name: Main | Gather | Facts
ansible.builtin.include_tasks: facts/general/gather.yml
- name: Main | Gather | Account Management
ansible.builtin.include_tasks: tasks/general/acct_mgmt/provision_config.yml
- name: Main | Gather | System
ansible.builtin.include_tasks: facts/general/system.yml
- name: Main | Gather | Package
ansible.builtin.include_tasks: facts/general/package.yml
- name: Main | Gather | Service
ansible.builtin.include_tasks: facts/general/service.yml
- name: Main | Gather | User
ansible.builtin.include_tasks: facts/general/user.yml
- name: Main | Gather | Internet Check
ansible.builtin.include_tasks: tasks/general/tests/internet_check.yml
####### Start #######
- name: Main | Start
block:
@@ -46,25 +52,31 @@
when: workstation
####### NixOS #######
# Install the .nix files and do a little data setup.
- name: Main | NixOS Setup
- name: Main | NixOS
block:
- name: Main | NixOS Setup | NixOS
- name: Main | NixOS | Config
ansible.builtin.include_tasks: tasks/general/software/nixos.yml
# TBD if we are going to use this many, then just have IF statements
# and use blocks within the General area for NixOS vs not-NixOS.
- name: Main | NixOS Setup | Swap
- name: Main | NixOS | Swap
ansible.builtin.include_tasks: tasks/general/software/swap.yml
- ansible.builtin.include_tasks: tasks/general/acct_mgmt/groups.yml
- ansible.builtin.include_tasks: tasks/general/acct_mgmt/users.yml
- ansible.builtin.include_tasks: tasks/general/acct_mgmt/sudo.yml
- ansible.builtin.include_tasks: tasks/general/acct_mgmt/doas.yml
- ansible.builtin.include_tasks: tasks/general/scripts/root.yml
- ansible.builtin.include_tasks: tasks/general/scripts/user.yml
- ansible.builtin.include_tasks: tasks/general/scripts/install.yml
- ansible.builtin.include_tasks: tasks/general/cron/ansible.yml
@@ -78,34 +90,36 @@
# Try including these. Really need to get NixOS working with the rest!
#- ansible.builtin.include_tasks: tasks/general/software/telegraf.yml # "Could not find a module for unknown.""
- ansible.builtin.include_tasks: tasks/general/tests/services.yml
- ansible.builtin.include_tasks: tasks/general/tests/lynis.yml
- ansible.builtin.include_tasks: tasks/general/tests/nmap.yml
- name: NixOS | Complete
- name: Main | NixOS | Complete
ansible.builtin.meta: end_play
when: ansible_distribution == "NixOS"
####### General #######
# Basic setup for all enabled systems.
- name: Main | General Setup
- name: Main | General
block:
- name: Main | General Setup | Swap
- name: Main | General | Swap
ansible.builtin.include_tasks: tasks/general/software/swap.yml
- name: Main | General Setup | runit
- name: Main | General | runit
ansible.builtin.include_tasks: tasks/general/software/runit.yml
- name: Main | General Setup | Packages
- name: Main | General | Packages
ansible.builtin.include_tasks: tasks/general/software/packages.yml
when: internet
- name: Main | General Setup | Services
- name: Main | General | Services
ansible.builtin.include_tasks: tasks/general/software/services.yml
- name: Main | General Setup | Sendmail
- name: Main | General | Sendmail
ansible.builtin.include_tasks: tasks/general/software/sendmail.yml
when: ansible_system == "FreeBSD"
and internet
@@ -119,8 +133,11 @@
- ansible.builtin.include_tasks: tasks/general/settings/keyboard.yml
- ansible.builtin.include_tasks: tasks/general/acct_mgmt/groups.yml
- ansible.builtin.include_tasks: tasks/general/acct_mgmt/users.yml
- ansible.builtin.include_tasks: tasks/general/acct_mgmt/sudo.yml
- ansible.builtin.include_tasks: tasks/general/acct_mgmt/doas.yml
when: internet
@@ -128,7 +145,9 @@
when: internet
- ansible.builtin.include_tasks: tasks/general/scripts/root.yml
- ansible.builtin.include_tasks: tasks/general/scripts/user.yml
- ansible.builtin.include_tasks: tasks/general/scripts/install.yml
- ansible.builtin.include_tasks: tasks/general/cron/ansible.yml
@@ -21,4 +21,3 @@
- "{{ cinnamon_package }}"
- "{{ lightdm }}"
state: present
update_cache: true
@@ -19,7 +19,6 @@
- name: Workstation | Linux | Flatpak Distro | Flatpak | Install
ansible.builtin.package:
name: flatpak
update_cache: true
- name: Workstation | Linux | Flatpak Distro | Flatpak | Add Flathub for System
flatpak_remote:
+192 -79
View File
@@ -26,6 +26,8 @@
- name: Workstation | Linux | Software | LLMs
block:
# Facts - Start #
- name: Workstation | Linux | Software | LLMs | Facts | Provisioned
ansible.builtin.debug:
var: "{{ item }}"
@@ -47,10 +49,12 @@
qwen_code: qwen-code
dummy_service: 'cronie'
dummy_website: 'https://hyperling.com'
# Good to keep low to prevent swap usage.
llm_concurrency: "{{ (ansible_memtotal_mb / 64000) | round(0, method='ceil') | int }}"
# Good to keep low to prevent swap usage. Going to have a minimum of 3
# though, one for Cline, two for Continue (Agent + Autocomplete).
llm_concurrency: "{{ [(ansible_memtotal_mb / 32000) | round(0, method='ceil'), 3] | max | int }}"
# Allow having multiple chats open without them just waiting each other.
llm_parallel: "{{ (ansible_processor_vcpus / 8) | round(0, method='ceil') | int }}"
# Also setting this to a minimum of 3 for using Codium with extensions.
llm_parallel: "{{ [(ansible_processor_vcpus / 2) | round(0, method='ceil'), 3] | max | int }}"
# Most LLMs take a coefficient of about 1.2 to 1.5 of their file size in
# RAM, which is usually less than guessing a usage of weight*1000.
# Example:
@@ -58,8 +62,8 @@
# Conversely:
# - qwen3.5:4b would be 3.3GB * 1.3 = 4290, rather than 4000.
llm_size_coef: 1.3
# Take the GB of RAM and use it as the MB of context.
llm_context: "{{ ansible_memtotal_mb | int }}"
# Use the MB of RAM as the context, with a maximum to prevent swapping.
llm_context: "{{ [(ansible_memtotal_mb | round(-3, method='ceil')), 32000] | min | int }}"
- name: Workstation | Linux | Software | LLMs | Facts 02
ansible.builtin.set_fact:
@@ -67,6 +71,8 @@
ollama_runit_log_dest: "{{ global_log }}/{{ ollama }}"
zeroclaw_runit_dir: "{{ runit_script_dir }}/{{ zeroclaw }}"
zeroclaw_runit_log_dest: "{{ global_log }}/{{ zeroclaw }}"
# Amount of output tokens allowed per response. Half of context is ok.
llm_tokens: "{{ (llm_context / 4) | int }}"
- name: Workstation | Linux | Software | LLMs | Facts 03
ansible.builtin.set_fact:
@@ -86,33 +92,33 @@
ansible.builtin.set_fact:
llm_installs: []
llm_removals:
- { "model": "qwen3:4b", "provider": "qwen_30_0040", "size_mb": 4000 }
- { "model": "qwen3.6:27b", "provider": "qwen_36_0270", "size_mb": 27000 }
- { "model": "qwen3.5:27b", "provider": "qwen_35_0270", "size_mb": "{{ llm_size_coef * 17000 }}" }
- { "purpose": "chat", "model": "qwen3:4b", "provider": "qwen_30_0040", "size_mb": 4000, "context": 123456 }
- { "purpose": "chat", "model": "qwen3.6:27b", "provider": "qwen_36_0270", "size_mb": 27000, "context": 123456 }
- { "purpose": "chat", "model": "qwen3.5:27b", "provider": "qwen_35_0270", "size_mb": "{{ llm_size_coef * 17000 }}", "context": 123456 }
llm_choices:
### QWEN >3.5 - Need to try!
- { "model": "qwen3.8:27b", "provider": "qwen_38_0270", "size_mb": 27000 }
- { "model": "qwen3.6:35b", "provider": "qwen_36_0350", "size_mb": 35000 }
- { "purpose": ["chat", code], "model": "qwen3.8:27b", "provider": "qwen_38_0270", "size_mb": 27000, "context": 123456 }
- { "purpose": ["chat", code], "model": "qwen3.6:35b", "provider": "qwen_36_0350", "size_mb": 35000, "context": 123456 }
### QWEN 3.5 - Good for generic usage and a little coding.
- { "model": "qwen3.5:0.8b", "provider": "qwen_35_0008", "size_mb": "{{ llm_size_coef * 1200 }}" }
- { "model": "qwen3.5:2b", "provider": "qwen_35_0020", "size_mb": "{{ llm_size_coef * 2700 }}" }
- { "model": "qwen3.5:4b", "provider": "qwen_35_0040", "size_mb": "{{ llm_size_coef * 3300 }}" }
- { "model": "qwen3.5:9b", "provider": "qwen_35_0090", "size_mb": "{{ llm_size_coef * 6600 }}" }
- { "purpose": "chat", "model": "qwen3.5:0.8b", "provider": "qwen_35_0008", "size_mb": "{{ llm_size_coef * 1200 }}", "context": 256000 }
- { "purpose": "chat", "model": "qwen3.5:2b", "provider": "qwen_35_0020", "size_mb": "{{ llm_size_coef * 2700 }}", "context": 256000 }
- { "purpose": "chat", "model": "qwen3.5:4b", "provider": "qwen_35_0040", "size_mb": "{{ llm_size_coef * 3300 }}", "context": 256000 }
- { "purpose": "chat", "model": "qwen3.5:9b", "provider": "qwen_35_0090", "size_mb": "{{ llm_size_coef * 6600 }}", "context": 256000 }
# skip 27b and use 35b
- { "model": "qwen3.5:35b", "provider": "qwen_35_0350", "size_mb": "{{ llm_size_coef * 22000 }}" }
- { "model": "qwen3.5:122b", "provider": "qwen_35_1220", "size_mb": "{{ llm_size_coef * 81000 }}" }
- { "purpose": ["chat", code], "model": "qwen3.5:35b", "provider": "qwen_35_0350", "size_mb": "{{ llm_size_coef * 22000 }}", "context": 256000 }
- { "purpose": ["chat", code], "model": "qwen3.5:122b", "provider": "qwen_35_1220", "size_mb": "{{ llm_size_coef * 81000 }}", "context": 256000 }
### QWEN 3.0 Coder - Supposed to be better than 2.5 for machines that can handle it.
- { "model": "qwen3-coder:30b", "provider": "qwen_30_coder_0300", "size_mb": "{{ llm_size_coef * 19000 }}" }
- { "model": "qwen3-coder:420b", "provider": "qwen_30_coder_4200", "size_mb": "{{ llm_size_coef * 290000 }}" }
- { "purpose": "code", "model": "qwen3-coder:30b", "provider": "qwen_30_coder_0300", "size_mb": "{{ llm_size_coef * 19000 }}", "context": 123456 }
- { "purpose": "code", "model": "qwen3-coder:420b", "provider": "qwen_30_coder_4200", "size_mb": "{{ llm_size_coef * 290000 }}", "context": 123456 }
### QWEN 2.5 Coder - Ol'reliable!
- { "model": "qwen2.5-coder:0.5b", "provider": "qwen_25_coder_0005", "size_mb": "{{ llm_size_coef * 398 }}" }
- { "model": "qwen2.5-coder:1.5b", "provider": "qwen_25_coder_0015", "size_mb": "{{ llm_size_coef * 986 }}" }
- { "model": "qwen2.5-coder:3b", "provider": "qwen_25_coder_0030", "size_mb": "{{ llm_size_coef * 1900 }}" }
- { "model": "qwen2.5-coder:7b", "provider": "qwen_25_coder_0070", "size_mb": "{{ llm_size_coef * 4700 }}" }
- { "model": "qwen2.5-coder:14b", "provider": "qwen_25_coder_0140", "size_mb": "{{ llm_size_coef * 9000 }}" }
- { "model": "qwen2.5-coder:32b", "provider": "qwen_25_coder_0320", "size_mb": "{{ llm_size_coef * 20000 }}" }
- { "purpose": "code", "model": "qwen2.5-coder:0.5b", "provider": "qwen_25_coder_0005", "size_mb": "{{ llm_size_coef * 398 }}", "context": 32000 }
- { "purpose": "code", "model": "qwen2.5-coder:1.5b", "provider": "qwen_25_coder_0015", "size_mb": "{{ llm_size_coef * 986 }}", "context": 32000 }
- { "purpose": "code", "model": "qwen2.5-coder:3b", "provider": "qwen_25_coder_0030", "size_mb": "{{ llm_size_coef * 1900 }}", "context": 32000 }
- { "purpose": "code", "model": "qwen2.5-coder:7b", "provider": "qwen_25_coder_0070", "size_mb": "{{ llm_size_coef * 4700 }}", "context": 32000 }
- { "purpose": "code", "model": "qwen2.5-coder:14b", "provider": "qwen_25_coder_0140", "size_mb": "{{ llm_size_coef * 9000 }}", "context": 32000 }
- { "purpose": "code", "model": "qwen2.5-coder:32b", "provider": "qwen_25_coder_0320", "size_mb": "{{ llm_size_coef * 20000 }}", "context": 32000 }
### Mistral, Ministral, and Devstral - By a French company?
- { "model": "devstral-small-2:24b", "provider": "devstral_20_0240_small", "size_mb": 24000 }
- { "purpose": "code", "model": "devstral-small-2:24b", "provider": "devstral_20_0240_small", "size_mb": 24000, "context": 123456 }
#- mistral:7b
#- ministral-3:3b
#- ministral-3:8b
@@ -141,6 +147,9 @@
- ansible_memtotal_mb
- llm_concurrency
- llm_parallel
- llm_size_coef
- llm_context
- llm_tokens
- llm_installs
- llm_removals
- llm_choices
@@ -159,6 +168,8 @@
- llm_installs
- llm_removals
# Facts - End #
- name: Workstation | Linux | Software | LLMs | Packages | Install
ansible.builtin.package:
name:
@@ -171,6 +182,8 @@
- name: Workstation | Linux | Software | LLMs | runit
block:
# Ollama - Start #
- name: Workstation | Linux | Software | LLMs | runit | ollama | Dir | Run
ansible.builtin.file:
name: "{{ ollama_runit_dir }}"
@@ -208,7 +221,7 @@
export OLLAMA_REQUEST_TIMEOUT=3600 # default 300 (unconfirmed, not in the list of --help vars)
### Resource Usage ###
export OLLAMA_KEEP_ALIVE=20m # default is 5m
export OLLAMA_KEEP_ALIVE=2h # default is 5m
export OLLAMA_MAX_LOADED_MODELS={{ llm_concurrency }} # default is 3 if using CPUs, or 3 * NumGPUs
export OLLAMA_NUM_PARALLEL={{ llm_parallel }} # default is 1 and applies per-model (unconfirmed)
export OLLAMA_CONTEXT_LENGTH={{ llm_context }} # default is 4096, https://docs.ollama.com/faq#how-can-i-specify-the-context-window-size
@@ -245,6 +258,10 @@
owner: "{{ user }}"
mode: "0755"
# Ollama - End #
# Zeroclaw - Start #
- name: Workstation | Linux | Software | LLMs | runit | zeroclaw | Dir | Run
ansible.builtin.file:
name: "{{ zeroclaw_runit_dir }}"
@@ -295,6 +312,9 @@
dateformat %Y-%m-%d_%H-%M-%S # each run gets its own file.
mode: "0755"
# Zeroclaw - End #
# Open WebUI - Start #
- name: Workstation | Linux | Software | LLMs | runit | open-webui | Packages
ansible.builtin.package:
@@ -325,7 +345,7 @@
owner: "{{ user }}"
mode: "0755"
- name: Workstation | Linux | Software | LLMs | runit | open-webui | Script | File
- name: Workstation | Linux | Software | LLMs | runit | open-webui | Script | Run
ansible.builtin.copy:
dest: /etc/sv/open-webui/run
content: |
@@ -336,14 +356,14 @@
while [ ! -S /var/run/docker.sock ]; do sleep 1; done
# Ensure the old container is gone.
echo "Stopping..."
echo "RUN: Stopping..."
docker stop open-webui &&
echo "Container stopped" ||
echo "Container already stopped."
echo "Deleting..."
echo "RUN: Container stopped" ||
echo "RUN: Container already stopped."
echo "RUN: Deleting..."
docker rm -f open-webui &&
echo "Container deleted" ||
echo "Container already deleted."
echo "RUN: Container deleted" ||
echo "RUN: Container already deleted."
# Start the open-webui program.
set -e
@@ -358,6 +378,33 @@
ghcr.io/open-webui/open-webui:main
mode: "0755"
- name: Workstation | Linux | Software | LLMs | runit | open-webui | Script | Finish
ansible.builtin.copy:
dest: /etc/sv/open-webui/finish
content: |
#!/bin/sh
exec 2>&1
# $1 is the exit code, $2 is the exit status byte (signal number if killed)
echo "FINISH: Service returned with exit code: $1 and status: $2"
echo "FINISH: Stopping..."
docker stop open-webui &&
echo "FINISH: Container stopped" ||
echo "FINISH: Container already stopped."
echo "FINISH: Deleting..."
docker rm -f open-webui &&
echo "FINISH: Container deleted" ||
echo "FINISH: Container already deleted."
sleep=30
if [[ "$1" != 0 ]]; then
echo "FINISH: Sleeping for $sleep seconds due to error..."
sleep $sleep
fi
echo "FINISH: Done!"
mode: "0755"
- name: Workstation | Linux | Software | LLMs | runit | open-webui | Script | Log
ansible.builtin.copy:
dest: /etc/sv/open-webui/log/run
@@ -375,8 +422,72 @@
dateformat %Y-%m-%d_%H-%M-%S # each run gets its own file.
mode: "0755"
# Open WebUI - End #
# Swap Stopper - Start #
- name: Workstation | Linux | Software | LLMs | runit | swap-stopper | Script | Executable
ansible.builtin.copy:
dest: "{{ global_bin }}/llm_swap_stopper.sh"
src: files/scripts/llm_swap_stopper.sh
mode: "0755"
- name: Workstation | Linux | Software | LLMs | runit | swap-stopper | Dir | /etc/sv
ansible.builtin.file:
name: "/etc/sv/swap-stopper/log"
state: directory
owner: root
mode: "0755"
- name: Workstation | Linux | Software | LLMs | runit | swap-stopper | Dir | /var/log
ansible.builtin.file:
name: "/var/log/swap-stopper"
state: directory
owner: "{{ user }}"
mode: "0755"
- name: Workstation | Linux | Software | LLMs | runit | swap-stopper | Script | Run
ansible.builtin.copy:
dest: /etc/sv/swap-stopper/run
content: |
#!/bin/sh -e
exec 2>&1
exec {{ global_bin }}/llm_swap_stopper.sh
mode: "0755"
- name: Workstation | Linux | Software | LLMs | runit | swap-stopper | Script | Finish
ansible.builtin.copy:
dest: /etc/sv/swap-stopper/finish
content: |
#!/bin/sh -e
exec 2>&1
# $1 is the exit code, $2 is the exit status byte (signal number if killed)
echo "FINISH: Service returned with exit code: $1 and status: $2"
mode: "0755"
- name: Workstation | Linux | Software | LLMs | runit | swap-stopper | Script | Log
ansible.builtin.copy:
dest: /etc/sv/swap-stopper/log/run
content: |
#!/bin/sh -e
exec svlogd -tt /var/log/swap-stopper
mode: "0755"
- name: Workstation | Linux | Software | LLMs | runit | swap-stopper | Script | Log Config
ansible.builtin.copy:
dest: /var/log/swap-stopper/config
content: |
s1000000 # rotate at 1 MB
n5 # keep 5 old files
dateformat %Y-%m-%d_%H-%M-%S # each run gets its own file.
mode: "0755"
# Swap Saver - End #
when: ansible_service_mgr == "runit"
# Services - Start #
- name: Workstation | Linux | Software | LLMs | Services | Enable
ansible.builtin.service:
name: "{{ item.name }}"
@@ -387,6 +498,7 @@
- { 'name': "{{ zeroclaw }}", 'status': "{{ zeroclaw_enabled }}" }
- { 'name': 'docker' , 'status': true }
- { 'name': 'open-webui' , 'status': "{{ open_webui_enabled }}" }
- { 'name': 'swap-stopper' , 'status': true }
- name: Workstation | Linux | Software | LLMs | Services | Verify
ansible.builtin.uri:
@@ -414,42 +526,11 @@
- "{{ 'http://127.0.0.1:3000/health' if open_webui_enabled else dummy_website }}"
ignore_errors: true
#- name: Workstation | Linux | Software | LLMs | Config | zeroclaw | Root
# ansible.builtin.blockinfile:
# path: "{{ user_user.home }}/.zeroclaw/config.toml"
# marker: "# {mark} ANSIBLE MANAGED BLOCK | zeroclaw | Defaults"
# insertbefore: '(?m)^schema_version'
# block: |
# default_provider = "ollama"
# default_model = "qwen3.5:4b"
# provider_timeout_secs = 3600
# state: present
# create: false
# backup: true
# Services - End #
#- name: Workstation | Linux | Software | LLMs | Config | zeroclaw | Channels | header
# ansible.builtin.lineinfile:
# path: "{{ user_user.home }}/.zeroclaw/config.toml"
# insertbefore: '(?m)^[channels]'
# block: |
# message_timeout_secs = 3600
# state: present
# create: false
# backup: true
# Models - Start #
#- name: Workstation | Linux | Software | LLMs | Config | zeroclaw | Channels
# ansible.builtin.blockinfile:
# path: "{{ user_user.home }}/.zeroclaw/config.toml"
# marker: "# {mark} ANSIBLE MANAGED BLOCK | zeroclaw | Channels"
# insertbefore: '(?m)^[channels]'
# block: |
# message_timeout_secs = 3600
# state: present
# create: false
# backup: true
- name: Workstation | Linux | Software | LLMs | Models | ollama | Manual Script
- name: Workstation | Linux | Software | LLMs | Models | ollama | User Shell Functions
ansible.builtin.blockinfile:
path: "{{ user_user.home }}/{{ item }}"
marker: "# {mark} ANSIBLE MANAGED BLOCK | ollama | Manual Script"
@@ -459,17 +540,29 @@
{% for llm in llm_installs %}
echo -e "\n\n Starting {{ llm.model }} \n\n"
ollama pull {{ llm.model }} || {
status=$?
echo "ERROR: Pull failed with status $status, exiting."
ollama pull {{ llm.model }} ||
status=$? &&
echo "ERROR: Pull failed with status $status, exiting." &&
return $status
}
{% endfor %}
echo "Done!!"
}
alias pull-llms="pull_llms "
alias pull-models="pull_llms "
alias pull-ai="pull_llms "
function ollama_stop {
for model in `ollama ps | tail -n +2 | awk '{print $1}'`; do
echo "Stopping ${model}..."
ollama stop $model
done
}
alias ollama-stop="ollama_stop"
alias ollama-kill="ollama_stop"
alias ollama-killall="ollama_stop"
alias ollama-kill-all="ollama_stop"
alias ollama-stop-all="ollama_stop"
state: present
create: false
backup: true
@@ -535,7 +628,9 @@
- name: Workstation | Linux | Software | LLMs | Models | ollama | Get Models
ansible.builtin.shell:
cmd: ollama ls | tail -n +2 | awk '{print $1}'
cmd: |
set -o pipefail
ollama ls | tail -n +2 | awk '{print $1}'
register: llm_models
changed_when: llm_models.rc == 0
@@ -552,10 +647,15 @@
block: |
{% for llm in llm_installs %}
{% if llm.model in llm_models.stdout %}
{% if "chat" in llm.purpose %}
[providers.models.ollama.{{ llm.provider }}]
model = "{{ llm.model }}"
timeout_secs = 3600 # 1 hour, default is 120 (2 minutes), issue happens between 5 and 6 minutes though.
context_window = {{ llm_context }}
context_window = {{ [llm_context, llm.context, 16000] | min | int }}
max_tokens = {{ [llm_tokens, (llm.context / 4), 4000] | min | int }}
{% endif %}
{% endif %}
{% endfor %}
state: present
@@ -572,25 +672,38 @@
version: 1.0.0
schema: v1
models:
# | Role | `contextLength` | `maxTokens` | Notes |
# |------|-----------------|-------------|-------|
# | Chat / Edit | 131072 (128K) | 16384 - 65536 | Qwen recommends 65,536 output for instruct models; use lower if VRAM is tight |
# | Autocomplete | 32768 - 65536 | 1024 - 4096 | Keep low for speed; autocomplete doesn't need long outputs |
{% for llm in llm_installs %}
{% if llm.model in llm_models.stdout %}
- name: {{ llm.provider }}
{% if "code" in llm.purpose %}
- name: {{ llm.provider }}_chat-edit
provider: ollama
model: {{ llm.model }}
defaultCompletionOptions:
contextLength: {{ llm_context }}
maxTokens: {{ llm_context }}
- name: {{ llm.provider }}-auto
contextLength: {{ [llm_context, llm.context, 24000] | min | int }}
maxTokens: {{ [llm_tokens, (llm.context / 4), 8000] | min | int }}
- name: {{ llm.provider }}_autocomplete
provider: ollama
model: {{ llm.model }}
defaultCompletionOptions:
contextLength: {{ llm_context }}
maxTokens: {{ llm_context }}
contextLength: {{ [llm_context, llm.context, 12000] | min | int }}
maxTokens: {{ [llm_tokens, (llm.context / 4), 2000] | min | int }}
roles:
- autocomplete
{% endif %}
{% endif %}
{% endfor %}
mode: "0644"
when: codium_config
# Models - End #
when: ansible_pkg_mgr == "pacman"
@@ -17,4 +17,3 @@
- "{{ lxqt_package }}"
- "{{ sddm }}"
state: present
update_cache: true
-1
View File
@@ -53,7 +53,6 @@
- "{{ tlp_rdw }}"
- "{{ tlp_smart }}"
- "{{ tlp_run_script }}"
update_cache: true
## Configuration ##
@@ -17,7 +17,6 @@
- "{{ make }}"
- "{{ dmenu }}"
- "{{ font_awesome }}"
update_cache: true
## Download ##
-1
View File
@@ -66,7 +66,6 @@
- "{{ i3_redshift }}"
- "{{ i3_redshift_gui }}"
state: present
update_cache: true
- name: Workstation | Shared | Software | i3 | Install (yay)
ansible.builtin.shell: "yay -Qs {{ item }} || yay --noconfirm -S {{ item }}"
+12 -6
View File
@@ -1,15 +1,16 @@
---
# Harmonize systems rather than doing everything manually. :)
# Used to single out plays which are either near the end of the playbook or are
# brand new and not safe to add to the full mix of things until finished.
# Setup of any device connected to this repo.
- name: Main | Default Setup
- name: Main | TEST Setup
hosts: localhost
connection: local
become: true
tasks:
- ansible.builtin.include_tasks: tasks/header.yml
- name: TEST| Header
ansible.builtin.include_tasks: tasks/header.yml
#######################
#######################
@@ -17,7 +18,11 @@
#######################
#######################
- ansible.builtin.include_tasks: tasks/workstation/linux/software/llm.yml
- name: TEST | Main
block:
- name: LLMs
ansible.builtin.include_tasks: tasks/workstation/linux/software/llm.yml
#######################
#######################
@@ -25,4 +30,5 @@
#######################
#######################
- ansible.builtin.include_tasks: tasks/footer.yml
- name: TEST | Footer
ansible.builtin.include_tasks: tasks/footer.yml