[Fix] cleanup

This commit is contained in:
coja
2026-08-13 01:11:41 +02:00
parent f62cb40499
commit 86726cddce
312 changed files with 12274 additions and 10772 deletions
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=Evremap Service
After=multi-user.target
[Service]
WorkingDirectory=/
# Config path must stay absolute: this is a system unit running as root, so ~ or $HOME would resolve to /root.
ExecStart=bash -c "/usr/bin/evremap remap /home/anon/.config/evremap/evremap.toml -d 0"
Restart=always
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
+60
View File
@@ -0,0 +1,60 @@
# The name of the device to remap.
# Run `sudo evremap list-devices` to see the devices available
# on your system.
device_name = "Logitech ERGO K860"
# If you have multiple devices with the same name, you can optionally
# specify the `phys` value that is printed by the `list-devices` subcommand
# phys = "usb-0000:07:00.3-2.1.1/input0"
# Configure CAPSLOCK as a Dual Role key.
# Holding it produces LEFTCTRL, but tapping it
# will produce ESC.
# Both `tap` and `hold` can expand to multiple output keys.
[[dual_role]]
input = "KEY_CAPSLOCK"
hold = ["KEY_LEFTCTRL"]
tap = ["KEY_ESC"]
# Turn RIGHT ALT into a vim-arrow layer.
# We first neuter it: instead of ever emitting KEY_RIGHTALT (which apps treat
# as "activate menu" on a bare tap), it becomes KEY_F13 -- a real keycode that
# is unbound in every app, so tapping or holding right alt alone does nothing.
# The chords below then turn F13 + h/j/k/l into the arrow keys.
[[dual_role]]
input = "KEY_RIGHTALT"
hold = ["KEY_F13"]
tap = ["KEY_F13"]
[[remap]]
input = ["KEY_F13", "KEY_H"]
output = ["KEY_LEFT"]
[[remap]]
input = ["KEY_F13", "KEY_J"]
output = ["KEY_DOWN"]
[[remap]]
input = ["KEY_F13", "KEY_K"]
output = ["KEY_UP"]
[[remap]]
input = ["KEY_F13", "KEY_L"]
output = ["KEY_RIGHT"]
[[remap]]
input = ["KEY_F13", "KEY_U"]
output = ["KEY_PAGEUP"]
[[remap]]
input = ["KEY_F13", "KEY_D"]
output = ["KEY_PAGEDOWN"]
# Word-wise motion: emit Ctrl+Arrow so it jumps by word.
[[remap]]
input = ["KEY_F13", "KEY_B"]
output = ["KEY_LEFTCTRL", "KEY_LEFT"]
[[remap]]
input = ["KEY_F13", "KEY_W"]
output = ["KEY_LEFTCTRL", "KEY_RIGHT"]
+2
View File
@@ -0,0 +1,2 @@
# fl runs the llama.cpp router as a systemd service (llama.service) — no shell abbr needed.
# Presets: ~/.config/llamacpp/config.ini; see ~/.config/llamacpp/README.md.
+21
View File
@@ -0,0 +1,21 @@
# ▄▀█ █▄░█ █ █▀▄▀█ ▄▀█ ▀█▀ █ █▀█ █▄░█
# █▀█ █░▀█ █ █░▀░█ █▀█ ░█░ █ █▄█ █░▀█
# See https://wiki.hyprland.org/Configuring/Animations/
animations {
enabled = yes
bezier = wind, 0.05, 0.9, 0.1, 1.05
bezier = winIn, 0.1, 1.1, 0.1, 1.1
bezier = winOut, 0.3, -0.3, 0, 1
bezier = liner, 1, 1, 1, 1
animation = windows, 1, 6, wind, slide
animation = windowsIn, 1, 6, winIn, slide
animation = windowsOut, 1, 5, winOut, slide
animation = windowsMove, 1, 5, wind, slide
animation = border, 1, 1, liner
animation = borderangle, 1, 30, liner, loop
animation = fade, 1, 10, default
animation = workspaces, 1, 5, wind
}
+153
View File
@@ -0,0 +1,153 @@
#   ░▒▒▒░░░░░▓▓          ___________
# ░░▒▒▒░░░░░▓▓        //___________/
# ░░▒▒▒░░░░░▓▓     _   _ _    _ _____
# ░░▒▒░░░░░▓▓▓▓▓▓ | | | | |  | |  __/
# ░▒▒░░░░▓▓   ▓▓ | |_| | |_/ /| |___
#  ░▒▒░░▓▓   ▓▓   \__  |____/ |____/
#    ░▒▓▓   ▓▓  //____/
$scrPath = $HOME/.local/share/bin # set scripts path
# █▀▄▀█ █▀█ █▄░█ █ ▀█▀ █▀█ █▀█
# █░▀░█ █▄█ █░▀█ █ ░█░ █▄█ █▀▄
# See https://wiki.hyprland.org/Configuring/Monitors/
# monitor = ,preferred,auto,auto
# █░░ ▄▀█ █░█ █▄░█ █▀▀ █░█
# █▄▄ █▀█ █▄█ █░▀█ █▄▄ █▀█
# See https://wiki.hyprland.org/Configuring/Keywords/
exec-once = $scrPath/resetxdgportal.sh # reset XDPH for screenshare
exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP # for XDPH
exec-once = dbus-update-activation-environment --systemd --all # for XDPH
exec-once = systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP # for XDPH
exec-once = $scrPath/polkitkdeauth.sh # authentication dialogue for GUI apps
exec-once = waybar # launch the system bar
exec-once = blueman-applet # systray app for Bluetooth
exec-once = udiskie --no-automount --smart-tray # front-end that allows to manage removable media
exec-once = nm-applet --indicator # systray app for Network/Wifi
exec-once = dunst # start notification demon
exec-once = wl-paste --type text --watch cliphist store # clipboard store text data
exec-once = wl-paste --type image --watch cliphist store # clipboard store image data
exec-once = $scrPath/swwwallpaper.sh # start wallpaper daemon
exec-once = $scrPath/batterynotify.sh # battery notification
# (fl remaps keys with evremap (see .config/evremap/) — the old xremap exec-once was dead, no config here)
exec-once = swayidle -w timeout 180 'swaylock --timestr %H:%M & brightnessctl -s & brightnessctl set 15' resume 'brightnessctl -r'
# █▀▀ █▄░█ █░█
# ██▄ █░▀█ ▀▄▀
# See https://wiki.hyprland.org/Configuring/Environment-variables/
env = PATH,$PATH:$scrPath
env = XDG_CURRENT_DESKTOP,Hyprland
env = XDG_SESSION_TYPE,wayland
env = XDG_SESSION_DESKTOP,Hyprland
env = QT_QPA_PLATFORM,wayland;xcb
env = QT_QPA_PLATFORMTHEME,qt6ct
env = QT_WAYLAND_DISABLE_WINDOWDECORATION,1
env = QT_AUTO_SCREEN_SCALE_FACTOR,1
env = MOZ_ENABLE_WAYLAND,1
env = GDK_SCALE,1
# █ █▄░█ █▀█ █░█ ▀█▀
# █ █░▀█ █▀▀ █▄█ ░█░
# See https://wiki.hyprland.org/Configuring/Variables/
# keyremap
input {
kb_layout = us
# kb_options=caps:swapescape
# kb_options=caps:ctrl_modifier
# follow_mouse = 1
touchpad {
natural_scroll = no
}
force_no_accel = 0
sensitivity = +1.0
}
# See https://wiki.hyprland.org/Configuring/Keywords/#executing
device {
name = ps/2-generic-mouse
sensitivity = +1.0
middle_button_emulation = true
}
device {
name = synps/2-synaptics-touchpad
sensitivity = 0.1
}
device {
name = xremap
sensitivity = +1.0
}
# See https://wiki.hyprland.org/Configuring/Variables/
# gestures {
# workspace_swipe = true
# workspace_swipe_fingers = 3
# }
# █░░ ▄▀█ █▄█ █▀█ █░█ ▀█▀ █▀
# █▄▄ █▀█ ░█░ █▄█ █▄█ ░█░ ▄█
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/
dwindle {
pseudotile = yes
preserve_split = yes
}
# See https://wiki.hyprland.org/Configuring/Master-Layout/
master {
new_status = master
}
# █▀▄▀█ █ █▀ █▀▀
# █░▀░█ █ ▄█ █▄▄
# See https://wiki.hyprland.org/Configuring/Variables/
misc {
vrr = 0
disable_hyprland_logo = true
disable_splash_rendering = true
force_default_wallpaper = 0
}
xwayland {
force_zero_scaling = true
}
# █▀ █▀█ █░█ █▀█ █▀▀ █▀▀
# ▄█ █▄█ █▄█ █▀▄ █▄▄ ██▄
source = ~/.config/hypr/animations.conf
source = ~/.config/hypr/keybindings.conf
source = ~/.config/hypr/windowrules.conf
source = ~/.config/hypr/themes/common.conf # shared theme settings
source = ~/.config/hypr/themes/theme.conf # theme specific settings
source = ~/.config/hypr/themes/colors.conf # wallbash color override
source = ~/.config/hypr/monitors.conf # initially empty, to be configured by user and remains static
source = ~/.config/hypr/userprefs.conf # initially empty, to be configured by user and remains static
# Note: as userprefs.conf is sourced at the end, settings configured in this file will override the defaults
+221
View File
@@ -0,0 +1,221 @@
# █▄▀ █▀▀ █▄█ █▄▄ █ █▄░█ █▀▄ █ █▄░█ █▀▀ █▀
# █░█ ██▄ ░█░ █▄█ █ █░▀█ █▄▀ █ █░▀█ █▄█ ▄█
# See https://wiki.hyprland.org/Configuring/Keywords/
# & https://wiki.hyprland.org/Configuring/Binds/
# Main modifier
$mainMod = Super # super / meta / windows key
# Assign apps
$term = kitty # fish alacritty
$file = dolphin
$browser = librewolf
$yt = freetube
$wm=Window Management
$d=[$wm]
# Window/Session actions
bind = $mainMod, Q, exec, $scrPath/dontkillsteam.sh # close focused window
bind = Alt, F4, exec, $scrPath/dontkillsteam.sh # close focused window
bind = $mainMod, Delete, exit, # kill hyprland session
bind = $mainMod, W, togglefloating, # toggle the window between focus and float
bind = $mainMod, G, togglegroup, # toggle the window between focus and group
bind = Alt, Return, fullscreen, # toggle the window between focus and fullscreen
bind = $mainMod, L, exec, swaylock --timestr %H:%M # launch lock screen
bind = Ctrl+Alt, W, exec, killall waybar || waybar # toggle waybar
# bind = $mainMod+Shift, F, exec, $scrPath/windowpin.sh # toggle pin on focused window
# bind = $mainMod, Backspace, exec, $scrPath/logoutlaunch.sh # launch logout menu
# Application shortcuts
bind = $mainMod, T, exec, $term
bind = Ctrl+Alt, T, exec, $term # launch terminal emulator
bind = Ctrl+Shift+Alt, T, exec, st # launch tilix terminal emulator - using for ssh
bind = $mainMod+Shift, T, exec, st
bind = $mainMod+Ctrl, T, exec, tilix # launch tilix terminal emulator
bind = $mainMod, E, exec, $file # launch file manager
bind = $mainMod, F, exec, $yt # launch yt alternative
bind = $mainMod, B, exec, $browser # launch web browser
bind = $mainMod, G, exec, gajim # launch messanger
bind = $mainMod, K, exec, keepassxc # launch password manager
bindd = $mainMod, N, Neovim, exec, $term -e nvim
bindd = $mainMod, R, $d pyradio, exec, kitty --session ~/.config/kitty/music.conf
bindd = Ctrl+Shift, Escape, Btop, exec, kitty -e btop
bindd = Ctrl+Alt, Delete, Btop, exec, kitty -e btop
# bind = Ctrl+Shift, Escape, exec, $scrPath/sysmonlaunch.sh # launch system monitor (htop/btop or fallback to top)
# bind = $mainMod+Shift, B, exec, $browser, title: ^(LibreWolf Private Browsing)$ # launch private web browser
# Rofi menus
bind = $mainMod, A, exec, pkill -x rofi || $scrPath/rofilaunch.sh d # launch application launcher
bind = $mainMod, Tab, exec, pkill -x rofi || $scrPath/rofilaunch.sh w # launch window switcher
bind = $mainMod+Shift, E, exec, pkill -x rofi || $scrPath/rofilaunch.sh f # launch file explorer
# Audio control
# bindl = , F10, exec, $scrPath/volumecontrol.sh -o m # toggle audio mute
# bindel = , F11, exec, $scrPath/volumecontrol.sh -o d # decrease volume
# bindel = , F12, exec, $scrPath/volumecontrol.sh -o i # increase volume
bindl = , XF86AudioMute, exec, $scrPath/volumecontrol.sh -o m # toggle audio mute
bindl = , XF86AudioMicMute, exec, $scrPath/volumecontrol.sh -i m # toggle microphone mute
bindel = , XF86AudioLowerVolume, exec, $scrPath/volumecontrol.sh -o d # decrease volume
bindel = , XF86AudioRaiseVolume, exec, $scrPath/volumecontrol.sh -o i # increase volume
# Media control
bindl = , XF86AudioPlay, exec, playerctl play-pause # toggle between media play and pause
bindl = , XF86AudioPause, exec, playerctl play-pause # toggle between media play and pause
bindl = , XF86AudioNext, exec, playerctl next # media next
bindl = , XF86AudioPrev, exec, playerctl previous # media previous
# Brightness control
bindel = , XF86MonBrightnessUp, exec, $scrPath/brightnesscontrol.sh i # increase brightness
bindel = , XF86MonBrightnessDown, exec, $scrPath/brightnesscontrol.sh d # decrease brightness
# Move between grouped windows
# bind = $mainMod CTRL , H, changegroupactive, b
# bind = $mainMod CTRL , L, changegroupactive, f
# Screenshot/Screencapture
bind = $mainMod, P, exec, $scrPath/screenshot.sh s # partial screenshot capture
bind = $mainMod+Ctrl, P, exec, $scrPath/screenshot.sh sf # partial screenshot capture (frozen screen)
bind = $mainMod+Alt, P, exec, $scrPath/screenshot.sh m # monitor screenshot capture
bind = $mainMod+Shift, P, exec, hyprpicker # colorpicker
bind = , Print, exec, $scrPath/screenshot.sh p # all monitors screenshot capture
# Custom scripts
bind = $mainMod+Alt, G, exec, $scrPath/gamemode.sh # disable hypr effects for gamemode
bind = $mainMod+Alt, Right, exec, $scrPath/swwwallpaper.sh -n # next wallpaper
bind = $mainMod+Alt, Left, exec, $scrPath/swwwallpaper.sh -p # previous wallpaper
# bind = $mainMod+Alt, Up, exec, $scrPath/wbarconfgen.sh n # next waybar mode
# bind = $mainMod+Alt, Down, exec, $scrPath/wbarconfgen.sh p # previous waybar mode
bind = $mainMod+Shift, R, exec, pkill -x rofi || $scrPath/wallbashtoggle.sh -m # launch wallbash mode select menu
# bind = $mainMod+Shift, T, exec, pkill -x rofi || $scrPath/themeselect.sh # launch theme select menu
bind = $mainMod+Shift, A, exec, pkill -x rofi || $scrPath/rofiselect.sh # launch select menu
bind = $mainMod+Shift, W, exec, pkill -x rofi || $scrPath/swwwallselect.sh # launch wallpaper select menu
bind = $mainMod, V, exec, pkill -x rofi || $scrPath/cliphist.sh c # launch clipboard
# bind = $mainMod, K, exec, $scrPath/keyboardswitch.sh # switch keyboard layout
bind = $mainMod, slash, exec, pkill -x rofi || $scrPath/keybinds_hint.sh c # launch keybinds hint
# Move/Change window focus
bind = $mainMod, Left, movefocus, l #chage focus to left window
bind = $mainMod, Right, movefocus, r
bind = $mainMod, Up, movefocus, u
bind = $mainMod, Down, movefocus, d
bindd = $mainMod Control, Left, $d focus left, movefocus, l
bindd = $mainMod Control, Right, $d focus right , movefocus, r
bindd = $mainMod Control, Up, $d focus up , movefocus, u
bindd = $mainMod Control, Down, $d focus down, movefocus, d
bindd = $mainMod Control, h, $d focus left, movefocus, l
bindd = $mainMod Control, l, $d focus right , movefocus, r
bindd = $mainMod Control, k, $d focus up , movefocus, u
bindd = $mainMod Control, j , $d focus down, movefocus, d
bindd = Alt Control, Left, $d focus left, movefocus, l
bindd = Alt Control, Right, $d focus right , movefocus, r
bindd = Alt Control, Up, $d focus up , movefocus, u
bindd = Alt Control, Down, $d focus down, movefocus, d
bindd = Alt Control, h, $d focus left, movefocus, l
bindd = Alt Control, l, $d focus right , movefocus, r
bindd = Alt Control, k, $d focus up , movefocus, u
bindd = Alt Control, j , $d focus down, movefocus, d
bindd = Alt, Tab, $d focus , movefocus, d
# Move active window around current workspace with mainMod + Shift + Control [←→↑↓]
$d=[$wm|Move active window across workspace]
$moveactivewindow=grep -q "true" <<< $(hyprctl activewindow -j | jq -r .floating) && hyprctl dispatch moveactive
bindde = $mainMod Alt, left, Move activewindow to the left, exec, $moveactivewindow -30 0 || hyprctl dispatch movewindow l
bindde = $mainMod Alt, right, Move activewindow to the right, exec, $moveactivewindow 30 0 || hyprctl dispatch movewindow r
bindde = $mainMod Alt, up, Move activewindow up, exec, $moveactivewindow 0 -30 || hyprctl dispatch movewindow u
bindde = $mainMod Alt, down, Move activewindow down, exec, $moveactivewindow 0 30 || hyprctl dispatch movewindow d
bindde = $mainMod Alt, h, Move activewindow to the left, exec, $moveactivewindow -30 0 || hyprctl dispatch movewindow l
bindde = $mainMod Alt, l, Move activewindow to the right, exec, $moveactivewindow 30 0 || hyprctl dispatch movewindow r
bindde = $mainMod Alt, k, Move activewindow up, exec, $moveactivewindow 0 -30 || hyprctl dispatch movewindow u
bindde = $mainMod Alt, j, Move activewindow down, exec, $moveactivewindow 0 30 || hyprctl dispatch movewindow d
# Switch workspaces with mod + [0-9]
bind = $mainMod, 1, workspace, r~1
bind = $mainMod, 2, workspace, r~2
bind = $mainMod, 3, workspace, r~3
bind = $mainMod, 4, workspace, r~4
bind = $mainMod, 5, workspace, r~5
bind = $mainMod, 6, workspace, r~6
bind = $mainMod, 7, workspace, r~7
bind = $mainMod, 8, workspace, r~8
bind = $mainMod, 9, workspace, r~9
bind = $mainMod, 0, workspace, r~10
# Move focused window to a workspace
bind = $mainMod+Ctrl, 1, movetoworkspace, r~1
bind = $mainMod+Ctrl, 2, movetoworkspace, r~2
bind = $mainMod+Ctrl, 3, movetoworkspace, r~3
bind = $mainMod+Ctrl, 4, movetoworkspace, r~4
bind = $mainMod+Ctrl, 5, movetoworkspace, r~5
bind = $mainMod+Ctrl, 6, movetoworkspace, r~6
bind = $mainMod+Ctrl, 7, movetoworkspace, r~7
bind = $mainMod+Ctrl, 8, movetoworkspace, r~8
bind = $mainMod+Ctrl, 9, movetoworkspace, r~9
bind = $mainMod+Ctrl, 0, movetoworkspace, r~10
# Move focused window to a workspace silently
bind = $mainMod+Alt, 1, movetoworkspacesilent, r~1
bind = $mainMod+Alt, 2, movetoworkspacesilent, r~2
bind = $mainMod+Alt, 3, movetoworkspacesilent, r~3
bind = $mainMod+Alt, 4, movetoworkspacesilent, r~4
bind = $mainMod+Alt, 5, movetoworkspacesilent, r~5
bind = $mainMod+Alt, 6, movetoworkspacesilent, r~6
bind = $mainMod+Alt, 7, movetoworkspacesilent, r~7
bind = $mainMod+Alt, 8, movetoworkspacesilent, r~8
bind = $mainMod+Alt, 9, movetoworkspacesilent, r~9
bind = $mainMod+Alt, 0, movetoworkspacesilent, r~10
# Move/Switch to special workspace (scratchpad)
bind = $mainMod+Alt, S, movetoworkspacesilent, special
bind = $mainMod, S, togglespecialworkspace,
# Toggle focused window split
bind = $mainMod, J, togglesplit
$d=[$ws|Navigation|Relative workspace]
bindd = $mainMod Control Shift, Right, $d change active workspace forwards , workspace, r+1
bindd = $mainMod Control Shift, Left, $d change active workspace backwards , workspace, r-1
bindd = $mainMod Control Shift, L, $d change active workspace forwards , workspace, r+1
bindd = $mainMod Control Shift, H, $d change active workspace backwards , workspace, r-1
# Resize windows
binde = $mainMod+Shift, Right, resizeactive, 30 0
binde = $mainMod+Shift, Left, resizeactive, -30 0
binde = $mainMod+Shift, Up, resizeactive, 0 -30
binde = $mainMod+Shift, Down, resizeactive, 0 30
bindde = $mainMod Shift, l, $d resize window right , resizeactive, 30 0
bindde = $mainMod Shift, h, $d resize window left, resizeactive, -30 0
bindde = $mainMod Shift, k, $d resize window up, resizeactive, 0 -30
bindde = $mainMod Shift, j, $d resize window down, resizeactive, 0 30
# added that to mimic i3's behavior, where pushing a workspace to another monitor also focuses it
# bind = $mainMod SHIFT, P, focusmonitor, +1
# bind = $mainMod SHIFT, P, movecurrentworkspacetomonitor, +1
# Move active window around current workspace with mainMod + SHIFT + CTRL [←→↑↓]
# $moveactivewindow=grep -q "true" <<< $(hyprctl activewindow -j | jq -r .floating) && hyprctl dispatch moveactive
binded = $mainMod+Shift+Ctrl, left,Move activewindow to the left,exec, $moveactivewindow -30 0 || hyprctl dispatch movewindow l
binded = $mainMod+Shift+Ctrl, right,Move activewindow to the right,exec, $moveactivewindow 30 0 || hyprctl dispatch movewindow r
binded = $mainMod+Shift+Ctrl, up,Move activewindow to the up,exec, $moveactivewindow 0 -30 || hyprctl dispatch movewindow u
binded = $mainMod+Shift+Ctrl, down,Move activewindow to the down,exec, $moveactivewindow 0 30 || hyprctl dispatch movewindow d
binded = $mainMod+Shift+Ctrl, h,Move activewindow to the left,exec, $moveactivewindow -30 0 || hyprctl dispatch movewindow l
binded = $mainMod+Shift+Ctrl, l,Move activewindow to the right,exec, $moveactivewindow 30 0 || hyprctl dispatch movewindow r
binded = $mainMod+Shift+Ctrl, k,Move activewindow to the up,exec, $moveactivewindow 0 -30 || hyprctl dispatch movewindow u
binded = $mainMod+Shift+Ctrl, j,Move activewindow to the down,exec, $moveactivewindow 0 30 || hyprctl dispatch movewindow d
# Scroll through existing workspaces
bind = $mainMod, mouse_down, workspace, e+1
bind = $mainMod, mouse_up, workspace, e-1
# Move/Resize focused window
bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow
bindm = $mainMod, Z, movewindow
bindm = $mainMod, X, resizewindow
+62
View File
@@ -0,0 +1,62 @@
# █▀▄▀█ █▀█ █▄░█ █ ▀█▀ █▀█ █▀█ █▀
# █░▀░█ █▄█ █░▀█ █ ░█░ █▄█ █▀▄ ▄█
# Set your monitor configuration here
# See https://wiki.hyprland.org/Configuring/Monitors/
# For a sample file, please refer to https://github.com/prasanthrangan/hyprdots/blob/main/Configs/.config/hypr/monitors.t2
$mainScreen = eDP-1
$leftScreen = DP-2
$rightScreen = HDMI-A-1
monitor = $mainScreen, 1366x768@60.14, 0x0, 1 # main screen
monitor = $leftScreen, 1920x1080@60, -1920x0, 1 # left
monitor = $rightScreen, 1920x1080@60,1366x0, 1 # right
# monitor=HDMI-A-1,1920x1080@60,-1366x0,1 # hdmi on left
# monitor=HDMI-A-1,1920x1080@60,0x-768,1 # hdmi on top
# monitor=DP-2,640x480@59.94Hz,0x0,1 # vga
# monitor = HDMI-A-1, 1366x768@60.00Hz, auto , 1, mirror, eDP-1 #mirror lapscreen
# monitor = eDP-1, 1920x1080@60.00Hz, auto , 1, mirror, HDMI-A-1 #main hdmi, lapscreen mirrors
# monitor = , preferred, auto, 1, mirror, DP-2 # dmz projector
# monitor = eDP-1, 1920x1080@60, auto , 1, mirror, DP-2 #mirror vga test
#Main Monitor
workspace = 1, monitor:$mainScreen, default:true
workspace = 2, monitor:$mainScreen
workspace = 3, monitor:$mainScreen
workspace = 4, monitor:$mainScreen
workspace = 5, monitor:$mainScreen
workspace = 6, monitor:$mainScreen
workspace = 7, monitor:$mainScreen
workspace = 8, monitor:$mainScreen
workspace = 9, monitor:$mainScreen
workspace = 10,monitor:$mainScreen
# Second Monitor
workspace = 11, monitor:$rightScreen, default:true
workspace = 12, monitor:$rightScreen
workspace = 13, monitor:$rightScreen
workspace = 14, monitor:$rightScreen
workspace = 15, monitor:$rightScreen
workspace = 16, monitor:$rightScreen
workspace = 17, monitor:$rightScreen
workspace = 18, monitor:$rightScreen
workspace = 19, monitor:$rightScreen
workspace = 20, monitor:$rightScreen
# Third Monitor
workspace = 21, monitor:$leftScreen, default:true
workspace = 22, monitor:$leftScreen
workspace = 23, monitor:$leftScreen
workspace = 24, monitor:$leftScreen
workspace = 25, monitor:$leftScreen
workspace = 26, monitor:$leftScreen
workspace = 27, monitor:$leftScreen
workspace = 28, monitor:$leftScreen
workspace = 29, monitor:$leftScreen
workspace = 30, monitor:$leftScreen
+68
View File
@@ -0,0 +1,68 @@
# █░█ █▀ █▀▀ █▀█   █▀█ █▀█ █▀▀ █▀▀ █▀
# █▄█ ▄█ ██▄ █▀▄   █▀▀ █▀▄ ██▄ █▀░ ▄█
# Set your personal hyprland configuration here
# For a sample file, please refer to https://github.com/prasanthrangan/hyprdots/blob/main/Configs/.config/hypr/userprefs.t2
#
########################
# █▀▀ █▄░█ █░█
# ██▄ █░▀█ ▀▄▀
# env = MANGOHUD,1
# env = MANGOHUD_DLSYM,1
# █▄▀ █▀▀ █▄█ █▄▄ █ █▄░█ █▀▄ █ █▄░█ █▀▀ █▀
# █░█ ██▄ ░█░ █▄█ █ █░▀█ █▄▀ █ █░▀█ █▄█ ▄█
# bind = Ctrl+Alt+Shift, R, pass, ^(com\.obsproject\.Studio)$ # toggle obs screen recording // install obs flatpak
# bind = $mainMod+Shift, G, exec, pkill -x rofi || $scrPath/gamelauncher.sh # launch steam game launcher // install steam
# bind = $mainMod, Period, exec, emote # launch emoji selector // install emote
bind = $mainMod+Alt, XF86MonBrightnessDown, exec, hyprshade on blue-light-filter # enable blue light filter // install hyprshade
bind = $mainMod+Alt, XF86MonBrightnessUp, exec, hyprshade off # disable blue light filter // install hyprshade
# █░░ ▄▀█ █░█ █▄░█ █▀▀ █░█
# █▄▄ █▀█ █▄█ █░▀█ █▄▄ █▀█
# exec-once = swayidle -w timeout 600 'swaylock' timeout 900 'hyprctl dispatch dpms off' resume 'hyprctl dispatch dpms on' # lock after 10 mins, sleep after 15 mins // install swayidle
# exec-once = swayidle -w timeout 1200 'swaylock; hyprctl dispatch dpms off' resume 'hyprctl dispatch dpms on' timeout 1800 'systemctl suspend' # lock and sleep after 20 mins, suspend after 30 mins // install swayidle
# exec-once = libinput-gestures // install libinput-gestures
# exec-once = swayosd-server # enable swayosd service
# exec-once = `pkexec swayosd-libinput-backend` # swayosd service for keyboard input (requires to be run in a subshell)
# █░█░█ █ █▄░█ █▀▄ █▀█ █░█░█   █▀█ █░█ █░░ █▀▀ █▀
# ▀▄▀▄▀ █ █░▀█ █▄▀ █▄█ ▀▄▀▄▀   █▀▄ █▄█ █▄▄ ██▄ ▄█
windowrulev2 = opacity 0.90 0.90,class:^(neovide)$
windowrulev2 = opacity 0.90 0.90,class:^(firefox)$
windowrulev2 = opacity 0.80 0.80,class:^(betterbird)$
windowrulev2 = opacity 0.80 0.80,class:^(org.qbittorrent.qBittorrent)$
windowrulev2 = opacity 0.80 0.80,class:^(org.gajim.Gajim)$
windowrulev2 = opacity 0.80 0.80,class:^(Chromium)$
windowrulev2 = opacity 0.80 0.80,class:^(FreeTube)$
windowrulev2 = opacity 0.80 0.80,class:^(LibreWolf)$
windowrulev2 = opacity 0.80 0.60,class:^(obsidian)$
windowrulev2 = opacity 0.80 0.80,class:^(VSCodium)$
windowrulev2 = opacity 0.80 0.80,class:^(codium-url-handler)$
windowrulev2 = opacity 0.80 0.80,class:^(code-oss)$
windowrulev2 = opacity 0.80 0.80,class:^(Code)$
windowrulev2 = opacity 0.80 0.80,class:^(kitty)$
windowrulev2 = opacity 0.70 0.70,class:^(tilix)$
windowrulev2 = opacity 0.70 0.70,class:^(rustdesk)$
windowrulev2 = opacity 0.70 0.70,class:^(Ferdium)$
windowrulev2 = opacity 0.70 0.70,class:^(org.keepassxc.KeePassXC)$
# █░█ █▀▄▀█
# ▀▄▀ █░▀░█
# bind = Ctrl+Alt_L, V, submap, passthrough
# submap = passthrough
# bind = Ctrl+Alt_L, V, submap, reset
# submap = reset
+91
View File
@@ -0,0 +1,91 @@
# █░█░█ █ █▄░█ █▀▄ █▀█ █░█░█   █▀█ █░█ █░░ █▀▀ █▀
# ▀▄▀▄▀ █ █░▀█ █▄▀ █▄█ ▀▄▀▄▀   █▀▄ █▄█ █▄▄ ██▄ ▄█
# See https://wiki.hyprland.org/Configuring/Window-Rules/
# hyprctl clients to get names
windowrulev2 = opacity 0.80 0.80,class:^(code-url-handler)$
windowrulev2 = opacity 0.80 0.80,class:^(code-insiders-url-handler)$
windowrulev2 = opacity 0.80 0.80,class:^(org.kde.dolphin)$
windowrulev2 = opacity 0.80 0.80,class:^(org.kde.ark)$
windowrulev2 = opacity 0.80 0.80,class:^(nwg-look)$
windowrulev2 = opacity 0.80 0.80,class:^(qt5ct)$
windowrulev2 = opacity 0.80 0.80,class:^(qt6ct)$
windowrulev2 = opacity 0.80 0.80,class:^(kvantummanager)$
windowrulev2 = opacity 0.80 0.70,class:^(org.pulseaudio.pavucontrol)$
windowrulev2 = opacity 0.80 0.70,class:^(lagrange)$
windowrulev2 = opacity 0.60 0.60,class:^(com.github.wwmm.easyeffects)$
windowrulev2 = opacity 0.80 0.70,class:^(blueman-manager)$
windowrulev2 = opacity 0.80 0.70,class:^(nm-applet)$
windowrulev2 = opacity 0.80 0.70,class:^(nm-connection-editor)$
windowrulev2 = opacity 0.80 0.70,class:^(org.kde.polkit-kde-authentication-agent-1)$
windowrulev2 = opacity 0.80 0.70,class:^(polkit-gnome-authentication-agent-1)$
windowrulev2 = opacity 0.80 0.70,class:^(org.freedesktop.impl.portal.desktop.gtk)$
windowrulev2 = opacity 0.80 0.70,class:^(org.freedesktop.impl.portal.desktop.hyprland)$
windowrulev2 = opacity 0.70 0.70,class:^([Ss]team)$
windowrulev2 = opacity 0.90 0.90,class:^(com.github.rafostar.Clapper)$ # Clapper-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(com.github.tchx84.Flatseal)$ # Flatseal-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(hu.kramo.Cartridges)$ # Cartridges-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(com.obsproject.Studio)$ # Obs-Qt
windowrulev2 = opacity 0.80 0.80,class:^(gnome-boxes)$ # Boxes-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(discord)$ # Discord-Electron
windowrulev2 = opacity 0.80 0.80,class:^(WebCord)$ # WebCord-Electron
windowrulev2 = opacity 0.80 0.80,class:^(ArmCord)$ # ArmCord-Electron
windowrulev2 = opacity 0.80 0.80,class:^(app.drey.Warp)$ # Warp-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(net.davidotek.pupgui2)$ # ProtonUp-Qt
windowrulev2 = opacity 0.80 0.80,class:^(yad)$ # Protontricks-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(Signal)$ # Signal-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(io.github.alainm23.planify)$ # planify-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(io.gitlab.theevilskeleton.Upscaler)$ # Upscaler-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(com.github.unrud.VideoDownloader)$ # VideoDownloader-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(io.gitlab.adhami3310.Impression)$ # Impression-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(io.missioncenter.MissionCenter)$ # MissionCenter-Gtk
windowrulev2 = opacity 0.80 0.80,class:^(io.github.flattool.Warehouse)$ # Warehouse-Gtk
windowrulev2 = float,class:^(org.kde.dolphin)$,title:^(Progress Dialog — Dolphin)$
windowrulev2 = float,class:^(org.kde.dolphin)$,title:^(Copying — Dolphin)$
windowrulev2 = float,title:^(About Mozilla Firefox)$
windowrulev2 = float,class:^(firefox)$,title:^(Picture-in-Picture)$
windowrulev2 = float,class:^(firefox)$,title:^(Library)$
windowrulev2 = float,class:^(kitty)$,title:^(top)$
windowrulev2 = float,class:^(kitty)$,title:^(btop)$
windowrulev2 = float,class:^(kitty)$,title:^(htop)$
windowrulev2 = float,class:^(vlc)$
windowrulev2 = float,class:^(kvantummanager)$
windowrulev2 = float,class:^(qt5ct)$
windowrulev2 = float,class:^(qt6ct)$
windowrulev2 = float,class:^(nwg-look)$
windowrulev2 = float,class:^(org.kde.ark)$
windowrulev2 = float,class:^(org.pulseaudio.pavucontrol)$
windowrulev2 = float,class:^(blueman-manager)$
windowrulev2 = float,class:^(nm-applet)$
windowrulev2 = float,class:^(nm-connection-editor)$
windowrulev2 = float,class:^(org.kde.polkit-kde-authentication-agent-1)$
windowrulev2 = float,class:^(Signal)$ # Signal-Gtk
windowrulev2 = float,class:^(com.github.rafostar.Clapper)$ # Clapper-Gtk
windowrulev2 = float,class:^(app.drey.Warp)$ # Warp-Gtk
windowrulev2 = float,class:^(net.davidotek.pupgui2)$ # ProtonUp-Qt
windowrulev2 = float,class:^(yad)$ # Protontricks-Gtk
windowrulev2 = float,class:^(eog)$ # Imageviewer-Gtk
windowrulev2 = float,class:^(io.github.alainm23.planify)$ # planify-Gtk
windowrulev2 = float,class:^(io.gitlab.theevilskeleton.Upscaler)$ # Upscaler-Gtk
windowrulev2 = float,class:^(com.github.unrud.VideoDownloader)$ # VideoDownloader-Gkk
windowrulev2 = float,class:^(io.gitlab.adhami3310.Impression)$ # Impression-Gtk
windowrulev2 = float,class:^(io.missioncenter.MissionCenter)$ # MissionCenter-Gtk
# █░░ ▄▀█ █▄█ █▀▀ █▀█   █▀█ █░█ █░░ █▀▀ █▀
# █▄▄ █▀█ ░█░ ██▄ █▀▄   █▀▄ █▄█ █▄▄ ██▄ ▄█
layerrule = blur,rofi
layerrule = ignorezero,rofi
layerrule = blur,notifications
layerrule = ignorezero,notifications
layerrule = blur,swaync-notification-window
layerrule = ignorezero,swaync-notification-window
layerrule = blur,swaync-control-center
layerrule = ignorezero,swaync-control-center
layerrule = blur,logout_dialog
+344
View File
@@ -0,0 +1,344 @@
[ActivityManager]
_k_friendly_name=Activity Manager
switch-to-activity-a8fc2925-fa8d-449a-a9be-48ffe253c900=Meta+Shift+Tab,none,Switch to activity "Work"
switch-to-activity-c0551195-8905-4f5f-9be3-86124cedc90e=Meta+Tab,none,Switch to activity "Personal"
[KDE Keyboard Layout Switcher]
Switch keyboard layout to English (US)=none,none,Switch keyboard layout to English (US)
Switch keyboard layout to Serbian (Cyrillic, ZE and ZHE swapped)=none,none,Switch keyboard layout to Serbian (Cyrillic\\, ZE and ZHE swapped)
Switch keyboard layout to Serbian (Latin, with guillemets)=none,none,Switch keyboard layout to Serbian (Latin\\, with guillemets)
Switch to Last-Used Keyboard Layout=none,Meta+Alt+L,Switch to Last-Used Keyboard Layout
Switch to Next Keyboard Layout=none,Meta+Alt+K,Switch to Next Keyboard Layout
_k_friendly_name=Keyboard Layout Switcher
[kaccess]
Toggle Screen Reader On and Off=Meta+Alt+S,Meta+Alt+S,Toggle Screen Reader On and Off
_k_friendly_name=Accessibility
[kmix]
_k_friendly_name=KMix
decrease_microphone_volume=Microphone Volume Down,Microphone Volume Down,Decrease Microphone Volume
decrease_volume=Volume Down,Volume Down,Decrease Volume
decrease_volume_small=Shift+Volume Down,Shift+Volume Down,Decrease Volume by 1%
increase_microphone_volume=Microphone Volume Up,Microphone Volume Up,Increase Microphone Volume
increase_volume=Volume Up,Volume Up,Increase Volume
increase_volume_small=Shift+Volume Up,Shift+Volume Up,Increase Volume by 1%
mic_mute=Microphone Mute\tMeta+Volume Mute,Microphone Mute\tMeta+Volume Mute,Mute Microphone
mute=Volume Mute,Volume Mute,Mute
push_to_talk=none,none,Push to talk
[ksmserver]
Halt Without Confirmation=none,none,Shut Down Without Confirmation
Lock Session=Meta+L,Screensaver\tMeta+L,Lock Session
Log Out=Ctrl+Alt+Del,Ctrl+Alt+Del,Show Logout Screen
Log Out Without Confirmation=Meta+Del,none,Log Out Without Confirmation
LogOut=none,none,Log Out
Reboot=none,none,Reboot
Reboot Without Confirmation=none,none,Reboot Without Confirmation
Shut Down=none,none,Shut Down
_k_friendly_name=Session Management
[kwin]
Activate Window Demanding Attention=Meta+Ctrl+A,Meta+Ctrl+A,Activate Window Demanding Attention
Cycle Overview=none,none,Cycle through Overview and Grid View
Cycle Overview Opposite=none,none,Cycle through Grid View and Overview
Decrease Opacity=none,none,Decrease Opacity of Active Window by 5%
Edit Tiles=none,Meta+T,Toggle Tiles Editor
Expose=Ctrl+F9\tMeta+F9,Ctrl+F9\tMeta+F9,Toggle Present Windows (Current desktop)
ExposeAll=Launch (C)\tCtrl+F10\tMeta+F10,Launch (C)\tCtrl+F10\tMeta+F10,Toggle Present Windows (All desktops)
ExposeClass=Ctrl+F7\tMeta+F7,Ctrl+F7\tMeta+F7,Toggle Present Windows (Window class)
ExposeClassCurrentDesktop=none,none,Toggle Present Windows (Window class on current desktop)
Grid View=none,Meta+G,Toggle Grid View
Increase Opacity=none,none,Increase Opacity of Active Window by 5%
Kill Window=Meta+Ctrl+Esc,Meta+Ctrl+Esc,Kill Window
Move Tablet to Next LogicalOutput=none,none,Move the tablet to the next output
Move Tablet to Next Output=none,none,Move the tablet to the next output
MoveMouseToCenter=Meta+F6,Meta+F6,Move Mouse to Center
MoveMouseToFocus=Meta+F5,Meta+F5,Move Mouse to Focus
MoveZoomDown=none,none,Move Zoomed Area Downwards
MoveZoomLeft=none,none,Move Zoomed Area to Left
MoveZoomRight=none,none,Move Zoomed Area to Right
MoveZoomUp=none,none,Move Zoomed Area Upwards
Overview=none,Meta+W,Toggle Overview
Setup Window Shortcut=none,none,Setup Window Shortcut
Show Desktop=Meta+D,Meta+D,Peek at Desktop
Switch One Desktop Down=none,Meta+Ctrl+Down,Switch One Desktop Down
Switch One Desktop Up=Meta+Ctrl+Shift+Up,Meta+Ctrl+Up,Switch One Desktop Up
Switch One Desktop to the Left=none,Meta+Ctrl+Left,Switch One Desktop to the Left
Switch One Desktop to the Right=none,Meta+Ctrl+Right,Switch One Desktop to the Right
Switch Window Down=Meta+Shift+K\tMeta+Shift+Down\tMeta+Ctrl+Down\tMeta+Ctrl+J\tCtrl+Alt+Down\tCtrl+Alt+J,Meta+Alt+Down,Switch to Window Below
Switch Window Left=Meta+Shift+H\tMeta+Shift+Left\tMeta+Ctrl+H\tCtrl+Alt+Left\tCtrl+Alt+H,Meta+Alt+Left,Switch to Window to the Left
Switch Window Right=Meta+Shift+L\tMeta+Shift+Right\tMeta+Ctrl+L\tCtrl+Alt+Right\tCtrl+Alt+L,Meta+Alt+Right,Switch to Window to the Right
Switch Window Up=Meta+Shift+J\tMeta+Shift+Up\tMeta+Ctrl+Up\tMeta+Ctrl+K\tCtrl+Alt+Up\tCtrl+Alt+K,Meta+Alt+Up,Switch to Window Above
Switch to Desktop 1=Ctrl+F1\tMeta+1\tMeta+Ctrl+Left,Ctrl+F1\tMeta+F1,Switch to Desktop 1
Switch to Desktop 10=Meta+0,none,Switch to Desktop 10
Switch to Desktop 11=none,none,Switch to Desktop 11
Switch to Desktop 12=none,none,Switch to Desktop 12
Switch to Desktop 13=none,none,Switch to Desktop 13
Switch to Desktop 14=none,none,Switch to Desktop 14
Switch to Desktop 15=none,none,Switch to Desktop 15
Switch to Desktop 16=none,none,Switch to Desktop 16
Switch to Desktop 17=none,none,Switch to Desktop 17
Switch to Desktop 18=none,none,Switch to Desktop 18
Switch to Desktop 19=none,none,Switch to Desktop 19
Switch to Desktop 2=Ctrl+F2\tMeta+2\tMeta+Ctrl+Right,Ctrl+F2\tMeta+F2,Switch to Desktop 2
Switch to Desktop 20=none,none,Switch to Desktop 20
Switch to Desktop 21=none,none,Switch to Desktop 21
Switch to Desktop 22=none,none,Switch to Desktop 22
Switch to Desktop 23=none,none,Switch to Desktop 23
Switch to Desktop 24=none,none,Switch to Desktop 24
Switch to Desktop 25=none,none,Switch to Desktop 25
Switch to Desktop 3=Ctrl+F3\tMeta+3,Ctrl+F3\tMeta+F3,Switch to Desktop 3
Switch to Desktop 4=Ctrl+F4\tMeta+4,Ctrl+F4\tMeta+F4,Switch to Desktop 4
Switch to Desktop 5=Meta+5,none,Switch to Desktop 5
Switch to Desktop 6=Meta+6,none,Switch to Desktop 6
Switch to Desktop 7=Meta+7,none,Switch to Desktop 7
Switch to Desktop 8=Meta+8,none,Switch to Desktop 8
Switch to Desktop 9=Meta+9,none,Switch to Desktop 9
Switch to Next Desktop=Meta+Ctrl+Shift+Right\tMeta+Ctrl+Shift+L,none,Switch to Next Desktop
Switch to Next Screen=none,none,Switch to Next Screen
Switch to Previous Desktop=Meta+Ctrl+Shift+Left\tMeta+Ctrl+Shift+H,none,Switch to Previous Desktop
Switch to Previous Screen=none,none,Switch to Previous Screen
Switch to Screen 0=none,none,Switch to Screen 0
Switch to Screen 1=none,none,Switch to Screen 1
Switch to Screen 2=none,none,Switch to Screen 2
Switch to Screen 3=none,none,Switch to Screen 3
Switch to Screen 4=none,none,Switch to Screen 4
Switch to Screen 5=none,none,Switch to Screen 5
Switch to Screen 6=none,none,Switch to Screen 6
Switch to Screen 7=none,none,Switch to Screen 7
Switch to Screen Above=none,none,Switch to Screen Above
Switch to Screen Below=none,none,Switch to Screen Below
Switch to Screen to the Left=none,none,Switch to Screen to the Left
Switch to Screen to the Right=none,none,Switch to Screen to the Right
Toggle Night Color=none,none,Suspend/Resume Night Light
Toggle Window Raise/Lower=none,none,Toggle Window Raise/Lower
ToggleMouseClick=Meta+*,Meta+*,Toggle Mouse Click Animation
Walk Through Windows=Alt+Tab,Alt+Tab\tMeta+Tab,Walk Through Windows
Walk Through Windows (Reverse)=Alt+Shift+Backtab,Alt+Shift+Tab\tMeta+Shift+Tab,Walk Through Windows (Reverse)
Walk Through Windows Alternative=none,none,Walk Through Windows Alternative
Walk Through Windows Alternative (Reverse)=none,none,Walk Through Windows Alternative (Reverse)
Walk Through Windows of Current Application=Alt+`,Alt+`\tMeta+`,Walk Through Windows of Current Application
Walk Through Windows of Current Application (Reverse)=Alt+~,Alt+~\tMeta+~,Walk Through Windows of Current Application (Reverse)
Walk Through Windows of Current Application Alternative=none,none,Walk Through Windows of Current Application Alternative
Walk Through Windows of Current Application Alternative (Reverse)=none,none,Walk Through Windows of Current Application Alternative (Reverse)
Window Above Other Windows=none,none,Keep Window Above Others
Window Below Other Windows=none,none,Keep Window Below Others
Window Close=Alt+F4\tMeta+Q,Alt+F4,Close Window
Window Custom Quick Tile Bottom=none,none,Custom Quick Tile Window to the Bottom
Window Custom Quick Tile Left=none,none,Custom Quick Tile Window to the Left
Window Custom Quick Tile Right=none,none,Custom Quick Tile Window to the Right
Window Custom Quick Tile Top=none,none,Custom Quick Tile Window to the Top
Window Fullscreen=Alt+Return\tShift+F11,none,Make Window Fullscreen
Window Grow Horizontal=none,none,Expand Window Horizontally
Window Grow Vertical=none,none,Expand Window Vertically
Window Lower=none,none,Lower Window
Window Maximize=Meta+Return\tMeta+PgUp,Meta+PgUp,Maximize Window
Window Maximize Horizontal=none,none,Maximize Window Horizontally
Window Maximize Vertical=none,none,Maximize Window Vertically
Window Minimize=Meta+Esc\tMeta+PgDown,Meta+PgDown,Minimize Window
Window Move=Meta+Z,none,Move Window
Window Move Center=none,none,Move Window to the Center
Window No Border=none,none,Toggle Window Titlebar and Frame
Window On All Desktops=Meta+Shift+F,none,Keep Window on All Desktops
Window One Desktop Down=none,Meta+Ctrl+Shift+Down,Window One Desktop Down
Window One Desktop Up=none,Meta+Ctrl+Shift+Up,Window One Desktop Up
Window One Desktop to the Left=none,Meta+Ctrl+Shift+Left,Window One Desktop to the Left
Window One Desktop to the Right=none,Meta+Ctrl+Shift+Right,Window One Desktop to the Right
Window One Screen Down=none,none,Move Window One Screen Down
Window One Screen Up=none,none,Move Window One Screen Up
Window One Screen to the Left=none,none,Move Window One Screen to the Left
Window One Screen to the Right=none,none,Move Window One Screen to the Right
Window Operations Menu=Alt+F3,Alt+F3,Window Menu
Window Pack Down=Meta+Alt+Down,none,Move Window Down
Window Pack Left=Meta+Alt+Left,none,Move Window Left
Window Pack Right=Meta+Alt+Right,none,Move Window Right
Window Pack Up=Meta+Alt+Up,none,Move Window Up
Window Quick Tile Bottom=Meta+Down\tMeta+Alt+\\,\tMeta+Alt+K,Meta+Down,Quick Tile Window to the Bottom
Window Quick Tile Bottom Left=none,none,Quick Tile Window to the Bottom Left
Window Quick Tile Bottom Right=none,none,Quick Tile Window to the Bottom Right
Window Quick Tile Left=Meta+Left\tMeta+Alt+H,Meta+Left,Quick Tile Window to the Left
Window Quick Tile Right=Meta+Right\tMeta+Alt+L,Meta+Right,Quick Tile Window to the Right
Window Quick Tile Top=Meta+Up\tMeta+Alt+J,Meta+Up,Quick Tile Window to the Top
Window Quick Tile Top Left=none,none,Quick Tile Window to the Top Left
Window Quick Tile Top Right=none,none,Quick Tile Window to the Top Right
Window Raise=none,none,Raise Window
Window Resize=Meta+X,none,Resize Window
Window Restore=Meta+Backspace,Meta+Backspace,Restore Window
Window Shrink Horizontal=none,none,Shrink Window Horizontally
Window Shrink Vertical=none,none,Shrink Window Vertically
Window to Desktop 1=Meta+Ctrl+1,none,Window to Desktop 1
Window to Desktop 10=Meta+Ctrl+0,none,Window to Desktop 10
Window to Desktop 11=none,none,Window to Desktop 11
Window to Desktop 12=none,none,Window to Desktop 12
Window to Desktop 13=none,none,Window to Desktop 13
Window to Desktop 14=none,none,Window to Desktop 14
Window to Desktop 15=none,none,Window to Desktop 15
Window to Desktop 16=none,none,Window to Desktop 16
Window to Desktop 17=none,none,Window to Desktop 17
Window to Desktop 18=none,none,Window to Desktop 18
Window to Desktop 19=none,none,Window to Desktop 19
Window to Desktop 2=Meta+Ctrl+2,none,Window to Desktop 2
Window to Desktop 20=none,none,Window to Desktop 20
Window to Desktop 21=none,none,Window to Desktop 21
Window to Desktop 22=none,none,Window to Desktop 22
Window to Desktop 23=none,none,Window to Desktop 23
Window to Desktop 24=none,none,Window to Desktop 24
Window to Desktop 25=none,none,Window to Desktop 25
Window to Desktop 3=Meta+Ctrl+3,none,Window to Desktop 3
Window to Desktop 4=Meta+Ctrl+4,none,Window to Desktop 4
Window to Desktop 5=Meta+Ctrl+5,none,Window to Desktop 5
Window to Desktop 6=Meta+Ctrl+6,none,Window to Desktop 6
Window to Desktop 7=Meta+Ctrl+7,none,Window to Desktop 7
Window to Desktop 8=Meta+Ctrl+8,none,Window to Desktop 8
Window to Desktop 9=Meta+Ctrl+9,none,Window to Desktop 9
Window to Next Desktop=Meta+Ctrl+Alt+Right\tMeta+Ctrl+Alt+L,none,Window to Next Desktop
Window to Next Screen=none,Meta+Shift+Right,Move Window to Next Screen
Window to Previous Desktop=Meta+Ctrl+Alt+Left\tMeta+Ctrl+Alt+H,none,Window to Previous Desktop
Window to Previous Screen=none,Meta+Shift+Left,Move Window to Previous Screen
Window to Screen 0=none,none,Move Window to Screen 0
Window to Screen 1=none,none,Move Window to Screen 1
Window to Screen 2=none,none,Move Window to Screen 2
Window to Screen 3=none,none,Move Window to Screen 3
Window to Screen 4=none,none,Move Window to Screen 4
Window to Screen 5=none,none,Move Window to Screen 5
Window to Screen 6=none,none,Move Window to Screen 6
Window to Screen 7=none,none,Move Window to Screen 7
_k_friendly_name=KWin
disableInputCapture=Meta+Shift+Esc,Meta+Shift+Esc,Disable Active Input Capture
view_actual_size=Meta+),Meta+0,Zoom to Actual Size
view_zoom_in=Meta++,Meta++\tMeta+=,Zoom In
view_zoom_out=Meta+_,Meta+-,Zoom Out
[mediacontrol]
_k_friendly_name=Media Controller
mediavolumedown=none,none,Media volume down
mediavolumeup=none,none,Media volume up
nextmedia=Media Next,Media Next,Media playback next
pausemedia=Media Pause,Media Pause,Pause media playback
playmedia=none,none,Play media playback
playpausemedia=Media Play,Media Play,Play/Pause media playback
previousmedia=Media Previous,Media Previous,Media playback previous
seekbackwardmedia=Media Rewind,Media Rewind,Media playback seek backward 5s
seekbackwardmedialong=none,none,Media playback seek backward 30s
seekforwardmedia=Media Fast Forward,Media Fast Forward,Media playback seek forward 5s
seekforwardmedialong=none,none,Media playback seek forward 30s
stopmedia=Media Stop,Media Stop,Stop media playback
[org_kde_powerdevil]
Decrease Keyboard Brightness=Keyboard Brightness Down,Keyboard Brightness Down,Decrease Keyboard Brightness
Decrease Screen Brightness=Monitor Brightness Down,Monitor Brightness Down,Decrease Screen Brightness
Decrease Screen Brightness Small=Shift+Monitor Brightness Down,Shift+Monitor Brightness Down,Decrease Screen Brightness by 1%
Hibernate=Hibernate,Hibernate,Hibernate
Increase Keyboard Brightness=Keyboard Brightness Up,Keyboard Brightness Up,Increase Keyboard Brightness
Increase Screen Brightness=Monitor Brightness Up,Monitor Brightness Up,Increase Screen Brightness
Increase Screen Brightness Small=Shift+Monitor Brightness Up,Shift+Monitor Brightness Up,Increase Screen Brightness by 1%
PowerDown=Power Down,Power Down,Power Down
PowerOff=Power Off,Power Off,Power Off
Sleep=Sleep,Sleep,Suspend
Toggle Keyboard Backlight=Keyboard Light On/Off,Keyboard Light On/Off,Toggle Keyboard Backlight
Turn Off Screen=none,none,Turn Off Screen
_k_friendly_name=Power Management
powerProfile=Battery,Battery\tMeta+B,Switch Power Profile
[plasmashell]
Slideshow Wallpaper Next Image=Meta+Ctrl+Alt+Shift+Right,none,Next Wallpaper Image
_k_friendly_name=plasmashell
activate application launcher=Meta,Meta\tAlt+F1,Activate Application Launcher
activate task manager entry 1=none,Meta+1,Activate Task Manager Entry 1
activate task manager entry 10=none,none,Activate Task Manager Entry 10
activate task manager entry 2=none,Meta+2,Activate Task Manager Entry 2
activate task manager entry 3=none,Meta+3,Activate Task Manager Entry 3
activate task manager entry 4=none,Meta+4,Activate Task Manager Entry 4
activate task manager entry 5=none,Meta+5,Activate Task Manager Entry 5
activate task manager entry 6=none,Meta+6,Activate Task Manager Entry 6
activate task manager entry 7=none,Meta+7,Activate Task Manager Entry 7
activate task manager entry 8=none,Meta+8,Activate Task Manager Entry 8
activate task manager entry 9=none,Meta+9,Activate Task Manager Entry 9
activate widget 47=none,none,Activate System Monitor Widget
activate widget 61=none,none,Activate Activity Pager Widget
activate widget 69=none,none,Activate System Monitor Widget
activate widget 70=none,none,Activate System Monitor Widget
clear history=none,none,Clear Notification History
clear-history=none,none,Clear Clipboard History
clipboard_action=Meta+Ctrl+X,Meta+Ctrl+X,Automatic Action Popup Menu
cycle-panels=Meta+Alt+P,Meta+Alt+P,Move keyboard focus between panels
cycleNextAction=none,none,Next History Item
cyclePrevAction=none,none,Previous History Item
edit_clipboard=none,none,Edit Contents…
manage activities=none,Meta+Q,Show Activity Switcher
next activity=none,none,Walk through activities
previous activity=Meta+Shift+A,none,Walk through activities (Reverse)
repeat_action=Meta+Ctrl+R,none,Manually Invoke Action on Current Clipboard
show dashboard=Ctrl+F12,Ctrl+F12,Show Desktop
show-barcode=none,none,Show Barcode…
show-on-mouse-pos=Meta+V,Meta+V,Show Clipboard Items at Mouse Position
switch to next activity=none,none,Switch to Next Activity
switch to previous activity=none,none,Switch to Previous Activity
toggle do not disturb=none,none,Toggle do not disturb
[services][Alacritty.desktop]
_launch=Meta+Shift+T
[services][FreeTube.desktop]
_launch=Meta+F
[services][betterbird.desktop]
_launch=Meta+M
[services][chromium.desktop]
_launch=Meta+Shift+B
[services][com.mitchellh.ghostty.desktop]
_launch=none
[services][ferdium.desktop]
_launch=Meta+Y
[services][kitty.desktop]
_launch=Ctrl+Alt+T\tMeta+T
[services][librewolf.desktop]
_launch=Meta+B
[services][net.local.kitty-2.desktop]
_launch=Meta+I
[services][net.local.kitty-3.desktop]
_launch=Meta+O
[services][net.local.kitty.desktop]
_launch=Meta+R
[services][nvim.desktop]
_launch=Meta+C
[services][org.gajim.Gajim.desktop]
StartChat=Meta+G
[services][org.kde.dolphin.desktop]
_launch=Meta+E
[services][org.kde.krunner.desktop]
_launch=Search\tAlt+Space\tAlt+F2\tCtrl+Alt+S\tMeta+A\tMeta+S
[services][org.kde.plasma-systemmonitor.desktop]
_launch=Ctrl+Shift+Esc
[services][org.kde.spectacle.desktop]
RecordRegion=Meta+Shift+R
RecordWindow=none
RectangularRegionScreenShot=Meta+P
_launch=Print\tMeta+Shift+P\tMeta+Shift+S
[services][org.keepassxc.KeePassXC.desktop]
_launch=Meta+K
[services][st.desktop]
_launch=Meta+Ctrl+T
[services][systemsettings.desktop]
_launch=\tTools
[services][thorium-browser.desktop]
_launch=Meta+Ctrl+B
+166
View File
@@ -0,0 +1,166 @@
# Local LLM server — setup & usage
llama.cpp in **router mode** serving ~10 model presets from one 16 GB GPU.
Last full tune: **2026-07-16** (decode numbers measured then via `bench.py`, unless a preset comment in `config.ini` says otherwise).
Last full clean sweep: **2026-08-08 03:22** — all 11 presets loaded and ran green (baseline 2.0 GB) after the week's coder-race/KV-fix/gemma-sidecar round.
- **Box**: AMD RX 7600 XT 16 GB (ROCm, ~288 GB/s) + Ryzen 5600X (6 cores) + 48 GB RAM.
⚠ The GPU **also drives the display** — see [VRAM safety](#vram-safety-the-golden-rules).
- **Endpoints**: LAN `http://$SERVER:11343/v1` · remote `https://<own-domain>/api/v1`
(own reverse proxy to the same server; requires an API key. Only the LAN endpoint is keyless).
`$SERVER` = the box's LAN address.
- **Files here**: `config.ini` (model presets — the section names ARE the API model ids),
`bench.py` (safety-first benchmark), `bench-results.md` / `bench-history.md` (ledgers, generated).
## How it runs
systemd unit **`llama.service`** (on the server, under a dedicated service user) runs a script wrapping:
```
llama-server --port 11343 --host 0.0.0.0 --models-max 1 --models-preset ~/.config/llamacpp/config.ini
```
- Router mode: each requested model loads on demand in a child process.
- **`--models-max 1` is deliberate and load-bearing**: at most one model resident; requesting
another LRU-evicts the current one *first*. Anything ≥2 lets two ~13 GB models stack →
VRAM overcommit → GTT spill → **whole-PC freeze**. Do not raise it casually.
(`sleep-idle-seconds` per preset is the second line of defense.)
- Models live in `~/software/models/` (paths in `config.ini` must stay absolute — llama-server
does not expand `~` or env vars in preset values). mmproj/draft files must match the exact
filename in the preset (watch for trailing spaces when renaming downloads!).
**Deploying config changes**: this dir is stowed into `~/.config/llamacpp/` as per-file
symlinks, so on the server `dotsync` (pull; re-stows only if files were added/removed) then
`sudo systemctl restart llama.service` — the router reads presets once at startup.
After ANY preset change: restart + `./bench.py -m <changed-ids>` and check the guards.
## VRAM safety (the golden rules)
ROCm does not OOM cleanly — an overcommitted model spills into GTT (system RAM),
starves the desktop and freezes the PC (reboot). Hard-won rules:
1. Keep every preset's benched **VRAM free ≥ 2.5 GB** (idle desktop uses ~1.3 of 17.2).
2. File size > VRAM is **fine** for MoE — `n-cpu-moe N` keeps the first N layers' experts
in system RAM. It is the main tuning knob: higher = safer/slower, lower = faster.
3. Bench with a **clean baseline** (close the browser): a dirty baseline both skews the
margins and costs ~34 t/s of decode (GPU compute contention — measured 2026-07-08).
4. Bench after every change; config-edit without restart = numbers from the *old* config.
## Model roster (reference numbers = clean night sweeps, 2026-07-16)
| model id | decode t/s | ctx | extras | role |
|---|---|---|---|---|
| `gemma-4-E4B-it-UD-Q8_K_XL` | **57.3** | **96k** | vision, MTP, prefill 185 t/s | fast small generalist, long docs, images (ctx 96k ✓ 08-08, n_ctx_train 131072) |
| `Qwen3.6-35B-A3B-Thinking` | 38.8 | 24k | MTP, vision, reasoning | hard problems, slow-but-smart answers |
| `gpt-oss-20b` | 38.4 | **64k** | reasoning, tools | fast reasoning + tool use · fast long-context (37 t/s vs Coder-Next 16; 131k ruled out) |
| `gpt-oss-20b-low` | 37.2 | **64k** | reasoning LOW, TTFT 0.8 s | same model, snappy answers (no long preamble) |
| `Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS` | 35.9 | 24k | MTP, vision, thinking OFF | **daily driver** — compact instant answers |
| `gemma-4-26B-A4B-it-UD-IQ4_XS` | 33.9 | 24k | vision, MTP | quality generalist + best vision |
| `Qwen3.5-9B-UD-Q6_K_XL` | 32.1 | 32k | vision, MTP, reasoning | small Qwen, quick tasks |
| `Qwen3-Coder-30B-Instruct-UD-Q3_K_XL` | **~30** clean / 27.0 evening @moe12 | 32k | — | **main agent coder** — won the 2026-08-06 quant race; moe12 verified evening 08-07 + clean 08-08 |
| `GLM-4.7-Flash-UD-Q4_K_XL` | **21.5** @moe22 | 24k | reasoning | quality coder (opencode subagents); KV fix + moe22 ✓ verified clean 08-08 |
| `Qwen3-Coder-Next-UD-IQ3_XXS` | 16.0 | **128k** | 80B-A3B | long-session coder (128k ctx) |
| `Qwen3-Embedding-0.6B` | 33 (CPU) | 8k | CPU-only, `/v1/embeddings` | RAG/search embedder (not a chat model) |
Expected run-to-run spread: MTP models swing ±15% with draft **acceptance rate** (content-
dependent); CPU-heavy presets (Coder-Next, embedder) dip under daytime CPU contention.
Treat clean night runs as the reference; don't retune on daytime deltas.
## Which model, when
- **Agent coding loops** (edit/test cycles): `Qwen3-Coder-30B` — best speed/quality balance at 32k.
- **Hard code, reviews, tricky bugs**: `GLM-4.7-Flash` — strongest 30B-class coder, slightly slower.
- **Marathon sessions / huge conversation history**: `Qwen3-Coder-Next` — 128k ctx at only
6 GB VRAM (hybrid attention). Caveat: ~33 t/s prefill means it's for *growing* sessions
(`cache-reuse` makes turns incremental), **not** for cold-dumping 100k tokens.
- **Everyday questions**: `Qwen3.6-35B-A3B` — no reasoning preamble, 36 t/s.
- **Hard reasoning**: `35B-Thinking` (quality) or `gpt-oss-20b` (speed + tool use).
Their multi-second TTFT is the reasoning phase streaming first — not a slow load.
For quick interactive gpt-oss answers use `gpt-oss-20b-low` (reasoning effort low).
- **Images**: `gemma-4-26B` for quality, `gemma-4-E4B` or `Qwen3.5-9B` for speed,
`Qwen3.6-35B` when you want the daily driver to see the screenshot.
- **Long one-shot documents**: `gemma-4-E4B` — 185 t/s prefill eats 64k in ~6 min
(Coder-Next would take ~30 min to prefill the same).
- **RAG embeddings**: point Open WebUI etc. at `Qwen3-Embedding-0.6B`. Runs on CPU by
design — 0 VRAM, can never contribute to an overcommit.
## Speech-to-text (separate service)
STT deliberately does **not** live in this router — it is a CPU-only whisper.cpp service on port
11345, documented in `../whisper/README.md`. Two reasons: `models-max 1` would make every
transcription LRU-evict the resident chat model, and llama.cpp decodes audio via miniaudio
(wav/mp3/flac only) while browsers record opus.
The router *does* answer `POST /v1/audio/transcriptions` — it rewrites the request into a chat
completion — so `gemma-4-E4B-it-UD-Q8_K_XL` (audio-capable via its mmproj) transcribes uploaded
wav/mp3/flac with no config change. Keep that as the fallback; mind the eviction.
## Text-to-speech (separate service)
TTS is **not servable from this router at all** — unlike STT, there is no fallback. `llama-server`
has `--model-vocoder` / `--tts-use-guide-tokens`, but exposes **no `/v1/audio/speech` route**:
the router 404s on it exactly like an undefined path, and `grep -rn "audio/speech"` over the
b10216 tree returns nothing — the only audio route registered is `/v1/audio/transcriptions` (STT).
Worse, `--model-vocoder` is accepted by the server and then **silently ignored**: it is registered
in the arg table and documented, but no server code path consumes it. `--help` listing it is not
evidence it works. `llama-tts` is a CLI. (All verified 2026-08-09.)
So TTS is a CPU-only Kokoro-82M service on port **11347**, documented in `../tts/README.md`
(repo-side only — not installed on fl yet). The `models-max 1` argument applies even harder there
than for STT: TTS fires on *every* response, so a resident preset would evict the chat model every
single turn.
## Client wiring
The same catalog is mirrored in every client — when adding/removing a preset, update all:
- `common/.config/opencode/opencode.json` (both providers + agent model overrides)
- `common/.pi/agent/models.json` (both providers; `contextWindow` = server `ctx-size`)
- `common/.config/aichat/config.yaml` (both clients)
Rule: client model **id = config.ini section name**, client context ≤ server `ctx-size`.
The embedder is deliberately absent from chat clients.
2026-08-06: the coder id changed `…-IQ4_XS``…-UD-Q3_K_XL` in all six client files
(common + lw overlays) after the quant race; OWUI picks the new id up automatically from
the router, but chats/presets saved against the old id need re-picking.
## Tuning cheat-sheet
- **`n-cpu-moe`** (MoE only): experts→CPU. The speed/VRAM dial. Measured curve is gentle —
tune in steps of 24 layers, re-bench, keep free ≥2.5 GB.
- **`ctx-size`**: KV cost varies wildly by arch — Coder-Next 64k→128k cost 0.5 GB (hybrid
attn); dense Qwens pay ~1 GB per 16k. Raise only after a bench shows the headroom.
- **Quants**: dense = bandwidth-bound, quant size sets speed directly (9B: Q8→Q6 = +87%
with MTP). MoE = buy quality with a bigger quant, pay in CPU offload (both coders run
4-bit now; 3-bit was only ~2 t/s faster). Coders want ≥4-bit; chat tolerates UD 3-bit.
- **MTP / spec decode** (`spec-type draft-mtp`): ~1.52× decode. Qwen3.5/3.6 embed the
head in the *MTP-repo* GGUF (same filename as plain repo — size is the tell);
gemma-4 uses a separate `mtp-*.gguf` draft file. **No MTP possible yet for**:
GLM-4.7-Flash (conversion drops the head — expected ~1.5× when llama.cpp lands it),
Qwen3-Coder-Next (Qwen3-Next head exists upstream, no GGUF ships it), gpt-oss and
Qwen3-Coder-30B (no head exists). Recheck releases occasionally.
- **KV cache**: `q8_0` K / `q4_0` V everywhere except gpt-oss (attention-sink issues with
quantized KV — keep defaults there).
## Benchmarking
```
./bench.py # full sweep (add -x Qwen3-Embedding-0.6B — it can't chat)
./bench.py -m id1,id2 # just the changed presets
./bench.py -n 512 --ctx 16000 # longer gen + long-context decode column
```
**Benches need the server to themselves**: with `models-max 1`, a request for another
model arriving mid-load LRU **force-kills the loading instance** → fake "failed to load"
rows (and a dirty baseline). Close chat clients / OWUI before sweeping.
Safety-first: force-unloads resident models via the router API (`POST /models/unload`),
verifies a clean card with rocm-smi before every load, records a ⚠ row and skips
generation when a model lands too tight (free < 1.5 GB or GTT ballooning).
Results: `bench-results.md` (latest) + `bench-history.md` (append-only; diff runs there).
## Watch list
- **GLM MTP in llama.cpp** — the single biggest pending win (primary coder ~22 → ~30+).
- **Coder-Next 256k**: if the load log's `n_ctx_train` says 262144, ctx can likely double
again for ~1 GB (KV measured nearly flat).
+320
View File
@@ -0,0 +1,320 @@
#!/usr/bin/env python3
"""
llama.cpp router benchmark → writes a results ledger for later tuning.
SAFETY-FIRST: this GPU also drives the display — VRAM overcommit spills to GTT and can
freeze the whole PC. The script therefore:
- records the BASELINE VRAM before touching anything (leftover model / browser skews everything)
- ensures a CLEAN card before EVERY load (incl. the first): resident models are force-unloaded
via the router API (POST /models/unload), then VRAM is verified with rocm-smi — the poll
covers driver reclaim lag, older builds without the endpoint (sleep-idle fallback), and
desktop apps the router doesn't own. Aborts if the card never drains.
- checks free VRAM right after a model mounts; too tight (--min-free-gb) or GTT ballooning
(--max-gtt-gb, measured against a PER-MODEL baseline — GTT reclaims slowly) → records a
⚠ row and skips generating; the sweep continues after the drain-wait
- refuses to sweep multiple models blind (no rocm-smi) unless --no-vram-ok
Per model it records decode/prefill t/s, TTFT, VRAM used/free, and the preset's knobs
(ctx, n-cpu-moe, spec-type) parsed from config.ini.
Output: bench-results.md (latest run, overwritten) + bench-history.md (every run, appended)
— both next to this script. Run ON the GPU box. Stdlib only.
./bench.py # all models the server lists
./bench.py -m id1,id2 # only these
./bench.py -x Qwen3-Coder-Next-UD-IQ3_XXS # skip some (e.g. not downloaded yet)
./bench.py -n 512 --ctx 16000 # longer gen + a long-context decode column
Compare a change: edit config.ini → reload server → re-run → diff runs in bench-history.md.
"""
import argparse, json, os, re, subprocess, sys, time, urllib.request, urllib.error
from datetime import datetime
HERE = os.path.dirname(os.path.abspath(__file__))
_smi_warned = False
def rocm_mem(kind="vram"):
"""(used_bytes, total_bytes) from rocm-smi for 'vram' or 'gtt'; (None, None) if unavailable."""
global _smi_warned
try:
out = subprocess.run(["rocm-smi", "--showmeminfo", kind],
capture_output=True, text=True, timeout=15).stdout
except Exception:
out = ""
def grab(pat):
m = re.search(pat + r"\s*:?\s*(\d+)", out)
return int(m.group(1)) if m else None
label = "VRAM" if kind == "vram" else "GTT"
used = grab(label + r" Total Used Memory \(B\)") or grab(r"Used Memory \(B\)")
total = grab(label + r" Total Memory \(B\)")
if kind == "vram" and total is None and not _smi_warned:
_smi_warned = True
print(" (rocm-smi not readable here — VRAM columns and freeze guards are OFF)", file=sys.stderr)
return used, total
def with_retries(fn, retries, wait, label=""):
"""Retry on 5xx/connection errors (model still mounting). A deterministic 'failed to
load' from the router is NOT retried — that's a broken preset, not a slow mount."""
last = None
for i in range(retries + 1):
try:
return fn()
except urllib.error.HTTPError as e:
if e.code < 500: # 4xx = real client error
raise
body = ""
try:
body = e.read().decode("utf-8", "ignore")
except Exception:
pass
if "failed to load" in body: # deterministic → don't burn retries
raise RuntimeError("model failed to load — check `journalctl -u llama.service`") from None
last = e
except (urllib.error.URLError, ConnectionError, TimeoutError) as e:
last = e
if i < retries:
print(f"{label} not ready ({last}); waiting {wait}s (try {i + 1}/{retries})", file=sys.stderr)
time.sleep(wait)
raise last
def _open(url, key, payload=None, timeout=600):
data = json.dumps(payload).encode() if payload is not None else None
hdrs = {"Authorization": f"Bearer {key}"}
if data:
hdrs["Content-Type"] = "application/json"
return urllib.request.urlopen(urllib.request.Request(url, data=data, headers=hdrs), timeout=timeout)
def list_models(base, key):
d = json.load(_open(base.rstrip("/") + "/models", key))
return [m["id"] for m in d.get("data", [])]
def force_unload(base, key):
"""Ask the router to unload every resident model (POST /models/unload on the ROOT api,
not /v1). Harmless no-op on builds without the endpoint — callers fall back to polling."""
root = re.sub(r"/v1/?$", "", base.rstrip("/"))
try:
d = json.load(_open(root + "/models", key, timeout=30))
except Exception as e:
print(f" (router /models not readable: {e} — relying on sleep-idle drain)", file=sys.stderr)
return
for m in d.get("data", []):
if m.get("status", "loaded") != "loaded": # builds without 'status': try them all
continue
try:
_open(root + "/models/unload", key, {"model": m["id"]}, timeout=30).read()
except Exception:
pass # not loaded / endpoint missing — poll covers it
def parse_config(path):
"""{preset: {ctx, ncpumoe, spec}} from the router config.ini."""
sections, sec = {}, None
try:
lines = open(path, encoding="utf-8").read().splitlines()
except OSError:
return {}
for ln in lines:
s = ln.strip()
if s.startswith("[") and s.endswith("]"):
sec = s[1:-1]; sections[sec] = {}
elif sec and "=" in s and not s.startswith("#"):
k, v = s.split("=", 1)
sections[sec][k.strip()] = v.split("#", 1)[0].strip()
return {sec: {"ctx": kv.get("ctx-size", "-"),
"ncpumoe": kv.get("n-cpu-moe", "-"),
"spec": kv.get("spec-type", "-") or "-"}
for sec, kv in sections.items()}
def run(base, key, model, prompt, n, temp, timeout):
payload = {"model": model, "messages": [{"role": "user", "content": prompt}],
"max_tokens": n, "temperature": temp, "stream": True,
"stream_options": {"include_usage": True},
# llama.cpp extension: makes the server embed its own timings in the stream →
# the table shows the SAME predicted_per_second as the llama.cpp web UI
# (without it we fall back to wall-clock estimates, which read lower).
"timings_per_token": True}
t0 = time.perf_counter(); ttft = None; ntok = 0; usage = None; timings = None
for raw in _open(base.rstrip("/") + "/chat/completions", key, payload, timeout):
line = raw.decode("utf-8", "ignore").strip()
if not line.startswith("data:"):
continue
body = line[5:].strip()
if body == "[DONE]":
break
try:
c = json.loads(body)
except ValueError:
continue
ch = c.get("choices") or [{}]
if ch and ch[0].get("delta", {}).get("content"):
if ttft is None:
ttft = time.perf_counter() - t0
ntok += 1
if c.get("usage"):
usage = c["usage"]
if c.get("timings"):
timings = c["timings"]
total = time.perf_counter() - t0
comp = (usage or {}).get("completion_tokens") or ntok
ptok = (usage or {}).get("prompt_tokens")
gen_s = total - (ttft or total)
tg = comp / gen_s if gen_s > 0 else 0.0
pp = (ptok / ttft) if (ptok and ttft) else None
if timings: # llama.cpp's own numbers are authoritative when the router forwards them
tg = timings.get("predicted_per_second", tg)
pp = timings.get("prompt_per_second", pp)
ptok = timings.get("prompt_n", ptok)
return {"tg": tg, "pp": pp, "ttft": ttft or 0.0, "ptok": ptok or 0}
def wait_drain(drain_gb, window_s, label="", base=None, key=None):
"""Force-unload resident models via the router API, then poll until VRAM used drops
below drain_gb (card is clean) or the window expires."""
used, _ = rocm_mem()
if base and used is not None and used > drain_gb * 1e9:
force_unload(base, key)
end = time.time() + window_s
announced = False
while used is not None and used > drain_gb * 1e9 and time.time() < end:
if not announced:
print(f" … waiting for VRAM to drain before {label} ({gb(used)} used)", file=sys.stderr)
announced = True
time.sleep(5)
used, _ = rocm_mem()
return used
def gb(b):
return f"{b / 1e9:.1f} GB" if b else "-"
def main():
ap = argparse.ArgumentParser()
ap.add_argument("-u", "--url", default="http://192.168.0.204:11343/v1")
ap.add_argument("-k", "--key", default="no-key-required")
ap.add_argument("-m", "--models", default="all", help="comma-separated ids, or 'all'")
ap.add_argument("-x", "--skip", default="", help="comma-separated ids to skip")
ap.add_argument("-n", "--tokens", type=int, default=256)
ap.add_argument("-t", "--temp", type=float, default=0.3)
ap.add_argument("--ctx", type=int, default=0, help="also record decode at ~this many prompt tokens")
ap.add_argument("--timeout", type=int, default=600, help="per-request stall timeout (s)")
ap.add_argument("--config", default=os.path.join(HERE, "config.ini"))
ap.add_argument("--out", default=os.path.join(HERE, "bench-results.md"))
ap.add_argument("--history", default=os.path.join(HERE, "bench-history.md"))
ap.add_argument("--retries", type=int, default=4, help="retries while a model is still mounting")
ap.add_argument("--retry-wait", type=int, default=15, help="seconds between retries")
ap.add_argument("--drain-gb", type=float, default=4.0,
help="VRAM considered 'clean' below this (raise it if your desktop alone uses more)")
ap.add_argument("--min-free-gb", type=float, default=1.5,
help="SAFETY: stop the sweep if a loaded model leaves less VRAM free than this")
ap.add_argument("--max-gtt-gb", type=float, default=2.0,
help="SAFETY: flag a model if GTT grows more than this over baseline (= VRAM spilling to RAM)")
ap.add_argument("--unload-wait", type=int, default=360,
help="after a too-tight model, wait up to this long for it to unload before continuing "
"(sleep-idle-seconds keeps models resident up to 300s)")
ap.add_argument("--no-vram-ok", action="store_true",
help="allow a multi-model sweep even though rocm-smi (and thus the freeze guards) is unavailable")
a = ap.parse_args()
models = list_models(a.url, a.key) if a.models == "all" else [m.strip() for m in a.models.split(",")]
skip = {s.strip() for s in a.skip.split(",") if s.strip()}
models = [m for m in models if m not in skip]
cfg = parse_config(a.config)
used0, total_vram = rocm_mem("vram")
if total_vram is None and len(models) > 1 and not a.no_vram_ok:
sys.exit("ABORT: rocm-smi is not readable here, so the VRAM/GTT freeze guards can't work.\n"
"Run this ON the GPU box, bench a single model with -m <id>, or pass --no-vram-ok to accept the risk.")
if used0 is not None and used0 > a.drain_gb * 1e9:
print(f"⚠ baseline VRAM used is already {gb(used0)} — a leftover model (sleep-idle keeps them "
f"up to 5 min) or desktop apps (browser!). Results/safety margins will be skewed; "
f"ideally close GPU apps or wait, then re-run.", file=sys.stderr)
task = "Write a Python function that merges two sorted lists, with a short docstring and one example."
filler = ("The quick brown fox jumps over the lazy dog. " * max(1, a.ctx // 9)) if a.ctx else None
cols = ["model", "cfg ctx", "n-cpu-moe", "spec", "decode t/s", "prefill t/s", "TTFT s", "VRAM used", "VRAM free"]
if a.ctx:
cols.append(f"decode@{a.ctx // 1000}k")
latest = open(a.out, "w", encoding="utf-8")
hist = open(a.history, "a", encoding="utf-8")
meta = (f"_Run {datetime.now():%Y-%m-%d %H:%M} · server `{a.url}` · VRAM total {gb(total_vram)} · "
f"baseline used {gb(used0)} · gen {a.tokens} tok · guards: free≥{a.min_free_gb} GB, GTT+≤{a.max_gtt_gb} GB_")
def emit(line, both=True):
print(line)
latest.write(line + "\n"); latest.flush()
if both:
hist.write(line + "\n"); hist.flush()
latest.write("# llama.cpp benchmark results (latest run)\n\n")
hist.write(f"\n## run {datetime.now():%Y-%m-%d %H:%M}\n\n")
emit(meta + "\n")
emit("| " + " | ".join(cols) + " |")
emit("|" + "|".join(["---"] * len(cols)) + "|")
for m in models:
c = cfg.get(m, {})
base_cells = [m, c.get("ctx", "-"), c.get("ncpumoe", "-"), c.get("spec", "-")]
# A CLEAN card before EVERY load (incl. the first): force-unload via the router API,
# then verify with rocm-smi — double residency = bogus numbers or a freeze.
u = wait_drain(a.drain_gb, a.unload_wait, m, a.url, a.key)
if u is not None and u > a.drain_gb * 1e9:
emit("")
emit(f"_⚠ aborted before {m}: VRAM still {gb(u)} used after {a.unload_wait}s — something "
f"won't unload (leftover model / GPU apps). If that's your normal desktop, raise --drain-gb._")
print(f"\n⚠ aborted: card not clean ({gb(u)} used).", file=sys.stderr)
break
gtt_pre, _ = rocm_mem("gtt") # per-model GTT baseline — GTT reclaims slowly across swaps
try:
# warmup — retries wait out the mount; 'failed to load' aborts immediately
with_retries(lambda: run(a.url, a.key, m, "hi", 8, a.temp, a.timeout), a.retries, a.retry_wait, m)
used, _ = rocm_mem("vram")
gtt, _ = rocm_mem("gtt")
free = (total_vram - used) if (total_vram and used) else None
gtt_delta = (gtt - gtt_pre) if (gtt is not None and gtt_pre is not None) else None
reason = None
if free is not None and free < a.min_free_gb * 1e9:
reason = f"only {gb(free)} VRAM free"
elif gtt_delta is not None and gtt_delta > a.max_gtt_gb * 1e9:
reason = f"GTT grew {gb(gtt_delta)} = spilling to system RAM"
if reason:
# Too tight — do NOT generate (allocates more). Record the row; the sweep continues
# once this model has fully unloaded (see below).
hint = ("raise n-cpu-moe" if c.get("ncpumoe", "-") != "-"
else "lower ctx / smaller quant / add n-cpu-moe if MoE")
cells = base_cells + [f"{reason} — SKIPPED ({hint})", "", "", gb(used), gb(free)]
if a.ctx:
cells.append("")
else:
r = with_retries(lambda: run(a.url, a.key, m, task, a.tokens, a.temp, a.timeout),
a.retries, a.retry_wait, m)
cells = base_cells + [f"{r['tg']:.1f}", (f"{r['pp']:.0f}" if r['pp'] else "-"),
f"{r['ttft']:.2f}", gb(used), gb(free)]
if a.ctx:
rc = with_retries(lambda: run(a.url, a.key, m, filler + "\n\n" + task, a.tokens, a.temp, a.timeout),
a.retries, a.retry_wait, m)
cells.append(f"{rc['tg']:.1f}")
except Exception as e:
err = f"HTTP {e.code} (check server log)" if isinstance(e, urllib.error.HTTPError) else f"ERROR {e}"
cells = base_cells + [err] + [""] * (len(cols) - len(base_cells) - 1)
emit("| " + " | ".join(str(x) for x in cells) + " |")
# (no post-row wait needed: the pre-load drain above protects the next iteration,
# including after ⚠-tight rows where generation was skipped)
latest.write("\n_Tuning hints: **VRAM free** = headroom to raise `ctx-size` or lower `n-cpu-moe` "
"(more experts on GPU → faster). Low decode t/s on an offloaded MoE → lower `n-cpu-moe` "
"if free allows. High **baseline used** invalidates the run. For spec/MTP models, check "
"the server log's acceptance rate._\n")
latest.close(); hist.close()
print(f"\nwrote {a.out} (+ appended {a.history})")
if __name__ == "__main__":
main()
+457
View File
@@ -0,0 +1,457 @@
# llama.cpp model config — AMD RX 7600 XT (ROCm) · 16 GB (17.16 GB total)
# ═════════════════════════════════════════════════════════════════════════════
# ⚠⚠ VRAM SAFETY — READ THIS. This GPU ALSO DRIVES THE DISPLAY. If a model asks for
# more VRAM than is free, ROCm does NOT OOM cleanly — it spills into system RAM
# (GTT), which starves the desktop and FREEZES THE WHOLE PC (reboot required).
# → TARGET ≥ ~2.5 GB free (≤ ~14.5 GB used). HARD FLOOR 1.5 GB (bench.py guard stops there).
# Idle desktop uses ~1.3 GB of 17.16 — but a browser/leftover model can hold 1-3 GB more,
# so measure with a CLEAN baseline (bench.py prints "baseline used"; close GPU apps first;
# sleep-idle keeps a model resident up to 5 min after last use).
# → Verify with bench.py → ledger: bench-results.md (latest) + bench-history.md (all runs).
# → If a model is tight: LOWER ctx-size, or RAISE n-cpu-moe (MoE), or use a smaller quant.
# The values below are deliberately CONSERVATIVE after freeze incidents — raise ctx
# only after bench.py confirms the headroom.
# ═════════════════════════════════════════════════════════════════════════════
# Speed notes: decode is memory-bandwidth-bound (~288 GB/s). Prefer MoE (few active
# params). For MoE that doesn't fit, n-cpu-moe offloads expert FFNs to CPU (48 GB RAM) —
# this is FASTER than letting VRAM spill to GTT, and frees VRAM. threads = 6 (5600X cores).
# Runtime: `sudo rocm-smi --setperflevel high`; `watch -n1 rocm-smi --showmeminfo vram`.
# ─────────────────────────────────────────────────────────────────────────────
# Coding models
# ─────────────────────────────────────────────────────────────────────────────
[Qwen3-Coder-Next-UD-IQ3_XXS]
# ⭐ Big-context coder: Qwen3-Coder-Next 80B-A3B (3B active), Qwen3-Next hybrid attn → cheap KV.
# Weights ~30 GB: ALL experts on CPU (n-cpu-moe 99), only attention+KV on GPU → low VRAM, safe.
# Needs a CURRENT llama.cpp build (Qwen3-Next arch). ⬇ DOWNLOAD unsloth/Qwen3-Coder-Next-GGUF:UD-IQ3_XXS.
# Froze the PC before at n-cpu-moe 48 / ctx 131072 (overcommit) — keep it conservative, verify, then raise ctx.
model = /home/anon/software/models/Qwen3-Coder-Next-UD-IQ3_XXS.gguf
ctx-size = 131072 # ✓ VERIFIED @131072/moe99 (2026-07-16): 15.7 t/s, 6.0 GB used / 11.2 free —
# full 128k at a comfortable margin. Settled.
n-gpu-layers = 99
n-cpu-moe = 99 # ALL experts on CPU — settled AGAIN 2026-08-08 after a two-round
# experiment, CLOSED: GPU experts barely move this model.
# moe56 (04:11) = NO-OP: 6.0 used ≈ moe99's 5.8, all numbers flat →
# only ~48 MoE layers exist; moe ≥ 48 ≡ all-on-CPU. (Also: the old
# "moe48/131k froze the PC" lore was ctx-KV overcommit in a pre-
# quantized-KV config, not expert placement.)
# moe40 (04:37) = 8 layers on GPU, 10.2 used / 6.9 free: prefill
# 33→36 (+9%), decode 16.4 (noise) — 4.2 GB for a rounding error.
# The shallow curve says don't chase it: cache-reuse already makes
# growing sessions incremental, and the 6 GB footprint IS the niche.
# MEASURED (07-16): moe99 = 14.3-16.4 t/s vs moe36@65536 = 16.9.
threads = 6
# no-mmap TRIED AND REVERTED 2026-07-18: llama.cpp's log hint suggested it (CPU experts +
# mmap = page-fault churn), but reading 26 GB fully resident exceeds the router's load
# patience → "failed to load". Retry only if the router ever gets a load-timeout knob.
flash-attn = on
cache-type-k = q8_0
cache-type-v = q4_0
batch-size = 2048
ubatch-size = 256 # small compute buffer = less VRAM
cache-reuse = 256
defrag-thold = 0.1
temp = 0.7 # unsloth default 1.0/top-p0.95/top-k40/min-p0.01 — lower = more deterministic
top-p = 0.95
top-k = 40
min-p = 0.01
jinja = on
sleep-idle-seconds = 300
# ─── RETIRED 2026-07-16: [DeepSeek-Coder-V2-Lite-Instruct-Q8_0] ──────────────────────────
# Mid-2024 model, outclassed by GLM-4.7-Flash + both Qwen coders. Bench kept skipping it
# (~15.3 GB fixed footprint; measured 2026-07-03 that ctx/moe knobs barely move it — only fix
# was a Q4_K_M requant, not worth it as the 4th-best coder). Removed from opencode/pi/aichat.
# File can be deleted from the models dir: DeepSeek-Coder-V2-Lite-Instruct-Q5_K_M.gguf
# ─── RETIRED 2026-08-06: [Qwen3-Coder-30B-Instruct-IQ4_XS] ───────────────────────────────
# Lost the 2026-08-06 quant race to UD-Q3_K_XL@moe10 (31.3 vs 23.0 t/s, +36%, three
# consistent sweeps; it also failed twice to load on ~3.2 GB evening baselines at moe20 =
# 14.1-14.3 used). Clients rewired to Qwen3-Coder-30B-Instruct-UD-Q3_K_XL same day — the
# extended quality trial was deliberately skipped (user call; revert path = git, one commit).
# ⚠ OWUI chats/presets saved against this id need re-picking after the restart.
# File can be deleted from the models dir: Qwen3-Coder-30B-A3B-Instruct-IQ4_XS.gguf
# Reference numbers: moe24 = 21.1 t/s, 11.9/5.3 · moe20 = 23.2 (2026-07-16), 22.0-23.0 (2026-08-05/06)
[Qwen3-Coder-30B-Instruct-UD-Q3_K_XL]
# ⭐ MAIN AGENT CODER — took the slot 2026-08-06 after winning the quant race: 31.3 t/s @moe10
# vs IQ4_XS@moe20 = 23.0 (+36%, three consistent sweeps); Q3_K_S dominated (tombstone below).
# Same 32k ctx and sampling as the retired IQ4_XS — clients only needed the id swap.
# ⚠ QUALITY TRIAL SKIPPED (user call, 2026-08-06): Q3-on-MoE damage looks like wrong API
# signatures / off-by-one logic, NOT word salad — if opencode sessions smell off, the
# IQ4_XS preset + client wiring are one `git revert` away (and the .gguf may still be on
# disk). UD protects attn/shared tensors; the experts are still 3-bit.
# RACE NUMBERS: moe24 = 22.2 · moe16 = 25.1 (07-16) / 25.5 / 26.3 · moe10 = 31.3 (15.0 used /
# 2.1 free @3.1 GB baseline; replicate 30.9 @1.8 free under a 13.9 leftover-model baseline).
model = /home/anon/software/models/Qwen3-Coder-30B-A3B-Instruct-UD-Q3_K_XL.gguf
ctx-size = 32768
n-gpu-layers = 99
n-cpu-moe = 12 # ✓ VERIFIED @moe12 (2026-08-07, 2.9 GB EVENING baseline): 27.0 t/s,
# 14.2 used / 3.0 free — loaded fine in exactly the regime that killed
# IQ4_XS@moe20 twice; the insurance works. Cost vs moe10 (31.3 clean race)
# was ~4 t/s, more than the ~1 estimated — evening compute contention
# likely eats 2-3 of that; confirmed: CLEAN (2026-08-08 02:45, 1.9
# baseline) = 29.5 t/s, 13.2 used / 4.0 free — evening cost was indeed
# contention. Settled: moe12 = 29.5 clean / 27.0 evening, always loads.
threads = 6
flash-attn = on
cache-type-k = q8_0
cache-type-v = q4_0
batch-size = 2048
ubatch-size = 1024 # ✓ SETTLED 2026-08-08: prefill 74 → 89-92 t/s (+24%), decode unchanged,
# buffer +0.5 GB. FINAL GATE PASSED (14:43 sweep, 3.0 GB dirty baseline =
# evening-class): loaded and ran at 14.6 used / 2.6 free. If a worse
# evening (3.3+) ever fails the load: revert to 512, or pay one moe
# step (12→13).
cache-reuse = 256
defrag-thold = 0.1
temp = 0.15
top-p = 0.8
top-k = 20
min-p = 0
repeat-penalty = 1.05
jinja = on
sleep-idle-seconds = 300
# ─── RETIRED 2026-08-06: [Qwen3-Coder-30B-Instruct-Q3_K_S] ───────────────────────────────
# Dominated in the quant race: 30.9 t/s @moe8 (15.2 used / 2.0 free, replicate 30.9) = a tie
# with UD-Q3_K_XL@moe10 (31.3) at equal headroom, from a strictly worse quant (plain Q3_K_S,
# no UD-protected attn/shared tensors). No axis where it wins. Was never client-wired.
# File can be deleted from the models dir: Qwen3-Coder-30B-A3B-Instruct-Q3_K_S.gguf
# ─────────────────────────────────────────────────────────────────────────────
# General Qwen models (35B-A3B is fast MoE; the dense 27Bs are inherently VRAM-tight)
# ─────────────────────────────────────────────────────────────────────────────
[Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS]
# ⭐ Fast NON-THINKING daily driver: Qwen3.6-35B-A3B (MoE, 3B active) — the "bigger Qwen3
# Instruct". Compact precise answers, no reasoning preamble. (Alias kept for clients.)
# File is flat + named without "MTP" (unsloth MTP-repo naming). If the load log complains
# there is no MTP head in this file, comment the two spec- lines out (plain repo file).
model = /home/anon/software/models/Qwen3.6-35B-A3B-UD-IQ3_XXS.gguf
mmproj = /home/anon/software/models/mmproj-Qwen3.6-35B-A3B-F16.gguf # vision ✓ VERIFIED 2026-07-18:
# 13.9 used / 3.3 free (2.8 dirty baseline), decode unchanged at 36-37 t/s.
spec-type = draft-mtp
spec-draft-n-max = 2
reasoning-budget = 0 # hard-disable thinking → instant compact answers. Delete to allow /think.
# ⚠ if startup says "option 'reasoning_budget' not recognized" → remove this line.
ctx-size = 24576 # raised from 16384 (bench @16384: 4.8 GB free) — bench to confirm ≥2.5 free
n-gpu-layers = 99
n-cpu-moe = 16 # MEASURED @24: 19.8 t/s (MTP working!), 10.6 used / 6.6 free → pull experts onto GPU
threads = 6
flash-attn = on
cache-type-k = q8_0
cache-type-v = q4_0
batch-size = 2048
ubatch-size = 512
cache-reuse = 256
defrag-thold = 0.1
temp = 0.7
top-p = 0.8
top-k = 20
min-p = 0
repeat-penalty = 1.05
jinja = on
sleep-idle-seconds = 120
[Qwen3.6-35B-A3B-Thinking]
# Same GGUF as above with reasoning ENABLED (no reasoning-budget line) — fills the "thinking
# model" slot at zero disk cost. Qwen thinking-mode sampling: temp 0.6 / top-p 0.95, no
# repeat-penalty. Slower per answer (reasoning preamble) — use for hard problems.
model = /home/anon/software/models/Qwen3.6-35B-A3B-UD-IQ3_XXS.gguf
mmproj = /home/anon/software/models/mmproj-Qwen3.6-35B-A3B-F16.gguf # vision ✓ VERIFIED 2026-07-18:
# 13.9 used / 3.3 free (2.8 dirty baseline), decode unchanged at 36-37 t/s.
spec-type = draft-mtp
spec-draft-n-max = 2
ctx-size = 24576 # thinking eats ctx — same footprint as the non-thinking preset
n-gpu-layers = 99
n-cpu-moe = 16
threads = 6
flash-attn = on
cache-type-k = q8_0
cache-type-v = q4_0
batch-size = 2048
ubatch-size = 512
cache-reuse = 256
defrag-thold = 0.1
temp = 0.6
top-p = 0.95
top-k = 20
min-p = 0
jinja = on
sleep-idle-seconds = 120
# ─── RETIRED 2026-07-03: [Qwen3.6-27B-UD-IQ3_XXS] ────────────────────────────────────────
# The 27B files (plain AND MTP) were deleted from the models dir — preset removed so the
# router doesn't advertise a dead model. To bring it back: re-download (MTP-repo files are
# named WITHOUT "MTP"), restore the preset, re-add the id to opencode/pi/aichat lists.
# ─── RETIRED 2026-07-16: [Qwen3.5-27B-UD-Q3_K_XL] ────────────────────────────────────────
# Dense 27B: 16.2 GB used / 0.9 free even @8192 (under the 1.5 floor) and ~10 t/s ceiling on
# this card. Role covered by Qwen3.6-35B-A3B (MoE, 2× faster). Removed from opencode/pi/aichat.
# File can be deleted from the models dir: Qwen3.5-27B-UD-Q3_K_XL.gguf
[Qwen3.5-9B-UD-Q6_K_XL]
# Small fast Qwen + MTP. Replaces Qwen3.5-9B-Q8_0 (retired 2026-07-16: Q8 was quality-overkill
# and bandwidth-slow at 17.2 t/s — delete Qwen3.5-9B-Q8_0.gguf; also delete the interim
# Qwen3.5-4B-UD-Q6_K_XL.gguf, superseded by this).
# ⬇ FILE MUST COME FROM unsloth/Qwen3.5-9B-MTP-GGUF (8.99 GB — MTP head embedded, filename
# identical to the plain repo!). If the load log complains there is no MTP head in this
# file, it's the plain-repo file — comment the two spec- lines out.
# ✓ VERIFIED (2026-07-16): 32.2 t/s (MTP working — nearly 2× the Q8's 17.2), prefill 83,
# 12.4 used / 4.8 free @32768. TTFT ~3.7 s = the model thinking (reasoning streams first).
model = /home/anon/software/models/Qwen3.5-9B-UD-Q6_K_XL.gguf
mmproj = /home/anon/software/models/Qwen3.5-9B-MTP-mmproj-F16.gguf # vision (from the MTP repo)
spec-type = draft-mtp
spec-draft-n-max = 3 # ⚗→✓ KEPT (04:11): 33.8 t/s vs 32.0-33.1 @n=2 — small real win.
# Revert to 2 if a future sweep drops below 32.
ctx-size = 32768 # raise toward 65536 once bench confirms free ≥2.5 GB
n-gpu-layers = 99
flash-attn = on
cache-type-k = q8_0
cache-type-v = q4_0 # q4 V to save VRAM
batch-size = 2048
ubatch-size = 512
cache-reuse = 256
defrag-thold = 0.1
temp = 0.7
top-p = 0.8
top-k = 20
min-p = 0
repeat-penalty = 1.05
jinja = on
sleep-idle-seconds = 60
# ─── RETIRED 2026-07-16: [Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ3_M] ──────────────
# Dense: 10 t/s, 2.0 GB free (under the 2.5 target) and unfixable — bandwidth-bound, smaller
# quant would cost the quality that justified it. Removed from opencode/pi/aichat.
# File can be deleted from the models dir: Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ3_M.gguf
# ─────────────────────────────────────────────────────────────────────────────
# Gemma models
# ─────────────────────────────────────────────────────────────────────────────
[gemma-4-26B-A4B-it-UD-IQ4_XS]
# MoE (4B active) + MTP + vision. ~13.5 GB weights → tight. Projector on CPU (no-mmproj-offload).
# From unsloth/gemma-4-26B-A4B-it-qat-GGUF: mmproj-F16.gguf + mtp-gemma-4-26B-A4B-it.gguf.
# ⬇ SOURCES — local name ← upstream (PLAIN repo unsloth/gemma-4-26B-A4B-it-GGUF; the qat
# repo has NO IQ4_XS, its only main is qat-UD-Q4_K_XL 14.25 GB — see decision note below):
# gemma-4-26B-A4B-it-UD-IQ4_XS.gguf ← gemma-4-26B-A4B-it-UD-IQ4_XS.gguf (13.6 GB)
# ✓ current: the 2026-08-05 dl has the Jul 17 template
# mmproj-gemma-4-26B-A4B-it-F16.gguf ← mmproj-F16.gguf (1.19 GB)
# mtp-gemma-4-26B-A4B-it.gguf ← MTP/mtp-gemma-4-26B-A4B-it-Q8_0.gguf (462 MB)
# Keep LOCAL names stable; re-fetch the upstream names above when repos update. Pair main
# and drafter from the SAME repo — the two repos' drafters are different extractions.
# ✓ FIXED 2026-08-08 03:15: matched plain-repo sidecars cured the "Invalid input batch."
# 500s — root cause CONFIRMED as the cross-repo drafter mismatch (old qat-repo mtp under
# the new plain main). Re-benched 32.8 t/s @moe13, 12.1 used / 5.0 free, MTP ✓ — same
# speed as pre-update (32.7), template fix now in place. The *.gguf.bak sidecars are
# deletable once vision + opencode tool-calling are smoke-tested.
# (QAT quality upgrade = separate decision: main → qat-UD-Q4_K_XL, +0.7 GB, moe 13→~16, and
# a section/client id rename round. Not part of this fix.)
# ⚠ vision + MTP together is heavy — if it OOMs/freezes, add n-cpu-moe or drop MTP (keep vision).
model = /home/anon/software/models/gemma-4-26B-A4B-it-UD-IQ4_XS.gguf
mmproj = /home/anon/software/models/mmproj-gemma-4-26B-A4B-it-F16.gguf
model-draft = /home/anon/software/models/mtp-gemma-4-26B-A4B-it.gguf
spec-type = draft-mtp
spec-draft-n-max = 2
reasoning-budget = 0 # added 2026-08-09: the Jul-17 official template makes gemma-4 DELIBERATE
# on hard prompts (probe: 700 tok = 100% reasoning_content, content
# EMPTY, finish=length) — that ate pi's 4096 maxTokens cap and returned
# truncated nothing. Same fix as the 35B daily driver. ⚠ if startup says
# option not recognized, remove; if it loads but a hard probe still emits
# reasoning_content, the gemma template ignores the budget → remove and
# raise client maxTokens to 8192 instead.
ctx-size = 24576 # MEASURED @16384/moe16: 29.6 t/s, 10.7 used / 6.5 free → sliding-window KV is
# cheap, take the ctx back (clients already assume 24576)
# n_ctx_train = 262144 (08-08 load log) — huge ctx headroom exists if a
# long-context vision need ever appears; bench stepwise, free ≥ 2.5.
n-gpu-layers = 99
n-cpu-moe = 13 # MEASURED @16: 26.4 t/s, 11.9 used / 5.2 free → pull ~3 expert layers onto GPU for speed
threads = 6
flash-attn = on
jinja = on # OpenAI-style tool calling (opencode build/plan default to this preset)
cache-type-k = q8_0
cache-type-v = q4_0
batch-size = 2048
ubatch-size = 512
cache-reuse = 256
defrag-thold = 0.1
no-mmproj-offload = true # vision encoder on CPU → frees ~1 GB
temp = 1.0
top-p = 0.95
top-k = 64
sleep-idle-seconds = 60
[gemma-4-E4B-it-UD-Q8_K_XL]
# ~5-7 GB — small/fast, lots of headroom, safe at big ctx.
# Vision: the HF repo ships mmproj-{BF16,F16,F32}.gguf — use F16 (990 MB), saved AS the exact
# path below (rename it; if the path doesn't exist the model fails to load / vision silently
# missing depending on router). Verify: startup log shows the mmproj load, and an image request
# answers instead of erroring "image input is not supported".
# NOTE: repo got Google's chat-template update ~2026-05 — if the main gguf predates that, re-download it too.
model = /home/anon/software/models/gemma-4-E4B-it-UD-Q8_K_XL.gguf
mmproj = /home/anon/software/models/mmproj-gemma-4-E4B-it.gguf
model-draft = /home/anon/software/models/mtp-gemma-4-E4B-it.gguf # ✓ MEASURED (2026-07-18): 57.3 t/s —
# +50% over 38.4 without the draft, 10.5 used / 6.6 free. Fastest model on the box.
spec-type = draft-mtp
spec-draft-n-max = 2
reasoning-budget = 0 # added 2026-08-09, same reason as the 26B: gemma-4 deliberates on hard
# prompts since the Jul-17 template (this is what caused the 20-second
# aichat -e "think" preambles on 2026-08-05). Fast models answer NOW.
# ⚠ same caveats as the 26B — remove if not recognized / not honored.
ctx-size = 98304 # ⚗→✓ CLOSED 2026-08-08: 58.2-60.4 t/s, 10.3-10.6 used / 6.5-6.9 free
# @98304, and n_ctx_train = 131072 confirmed (upstream config.json
# max_position_embeddings) — 98304 is inside training range, no RoPE
# risk. 131072 is the hard ceiling if ever needed (~+0.5-1 GB KV).
n-gpu-layers = 99
flash-attn = on
jinja = on # OpenAI-style tool calling (doc-writer agent defaults to this preset)
cache-type-k = q8_0
cache-type-v = q8_0
batch-size = 2048
ubatch-size = 1024
cache-reuse = 256
defrag-thold = 0.1
temp = 1.0
top-p = 0.95
top-k = 64
sleep-idle-seconds = 30
# ─────────────────────────────────────────────────────────────────────────────
# GLM
# ─────────────────────────────────────────────────────────────────────────────
[GLM-4.7-Flash-UD-Q4_K_XL]
# 30B-A3B MoE, primary coder. Upgraded UD-Q3_K_XL (~14 GB) → UD-Q4_K_XL (17.5 GB, unsloth's
# recommended quant) 2026-07-16, benched clean same day → the old GLM-4.7-Flash-UD-Q3_K_XL.gguf
# can be deleted.
# Tuning trajectory: Q3@moe9 = 28.2 t/s → Q4@moe24 = 17.9 (measured 2026-07-16) → now moe18,
# lower stepwise toward 16 while bench keeps free ≥2.5 GB.
model = /home/anon/software/models/GLM-4.7-Flash-UD-Q4_K_XL.gguf
ctx-size = 24576 # MEASURED @16384/moe24 (Q3): 9.5 GB used, 7.6 free
# n_ctx_train = 202752 (08-08 load log) — MLA KV is compact, ctx could
# go far higher if opencode subagent sessions ever outgrow 24k.
n-gpu-layers = 99
n-cpu-moe = 22 # lowered 24→22 on 2026-08-08: both step-down gates passed post-KV-fix —
# clean sweep @24 = 20.5 t/s, 12.0 used / 5.2 free (02:45, 1.9 baseline);
# elevated-baseline load @24 = 18.7, 13.0/4.2 (@2.9). moe22 est ~21.5
# clean / ~19.5-20 evening, ~12.7 used clean / ~14.0 evening → inside the
# coder-proven evening envelope (14.2 loads). ✓ CLEAN VERIFIED (02:58,
# 2.0 baseline): 21.5 t/s, 12.8 used / 4.4 free — est was 21.5, exact.
# Evening observation pending; revert to 24 if an evening bench shows
# free < 2.5. moe20 stays gated (~14.4-14.7 evening = borderline vs the
# coder's 15.2 evening death).
# (08-05/07 "failures" were the mixed-KV rejection below, not VRAM.)
# Trajectory: Q3@moe9 = 28.2 → Q4@moe24 = 17.9 (07-16) → 18.7/20.5 post-KV-fix.
threads = 6
flash-attn = on
cache-type-k = q8_0
cache-type-v = q8_0 # was q4_0 — BROKE 2026-08-08: a llama.cpp upgrade (the one deprecating
# defrag-thold) enforces same-type K/V for this model (deepseek2/MLA
# conversion; MLA KV is compact, mixed types unsupported). Every load
# since failed at context creation regardless of VRAM. Only THIS preset
# was affected — the other k-q8/v-q4 presets are regular-attention archs.
# If q8/q8 still refuses: delete both cache-type lines (f16 default; MLA
# KV is small enough that the cost is minor at 24576 ctx).
batch-size = 2048
ubatch-size = 512
cache-reuse = 256
defrag-thold = 0.1
# MTP: GLM-4.7-Flash HAS an MTP head upstream (num_nextn_predict_layers: 1) but the GGUF
# conversion (deepseek2 arch) drops those tensors and llama.cpp draft-mtp only supports
# Qwen3.5/3.6 + Gemma 4 so far (PRs #22673/#23398). No mtp-*.gguf exists to download —
# recheck when llama.cpp lands GLM MTP support.
temp = 0.6 # unsloth rec: 0.7/top-p 1.0 (tool-calling) or 1.0/0.95 (general); no repeat-penalty
top-p = 0.95
min-p = 0.01
jinja = on
reasoning-preserve = true # ⚗ option ACCEPTED by the build (loaded + ran 20.9 t/s, 04:11 sweep).
# BEHAVIOR flag — judge multi-turn subagent quality in opencode over the
# next days; remove if reasoning bleed ever pollutes final answers.
sleep-idle-seconds = 60
# ─────────────────────────────────────────────────────────────────────────────
# Utility & candidates
# ─────────────────────────────────────────────────────────────────────────────
[Qwen3-Embedding-0.6B]
# Embeddings-only, for RAG/semantic search (OWUI etc. via /v1/embeddings) — NOT a chat model,
# so it is deliberately absent from the opencode/pi/aichat chat lists.
# Qwen3-Embedding requires last-token pooling. ~0.7 GB — tiny, loads fast.
# (Q8_0 is effectively lossless for a 0.6B embedder — the f16 file is redundant, delete it.)
# CPU-ONLY on purpose: a 0.6B embeds fast on 6 cores, and 0 VRAM means it can never stack
# with a chat model into an overcommit (and never suffers/causes eviction pressure).
model = /home/anon/software/models/Qwen3-Embedding-0.6B-Q8_0.gguf
embeddings = true
pooling = last
ctx-size = 8192
n-gpu-layers = 0
flash-attn = on
batch-size = 8192 # embed long chunks in one pass
ubatch-size = 1024
threads = 6
sleep-idle-seconds = 60
[gpt-oss-20b]
# OpenAI gpt-oss-20b — MoE 21B / 3.6B active, reasoning + tool use (enabled 2026-07-16, NOT
# BENCHED yet). OpenAI post-trained it in MXFP4, so there is exactly ONE sensible GGUF:
# ggml-org/gpt-oss-20b-GGUF → gpt-oss-20b-mxfp4.gguf (~12.1 GB). Requants are never better —
# the old gpt-oss-20b-Q3_K_M.gguf can be deleted.
# Harmony chat template → jinja required. Default KV types kept (gpt-oss attention sinks have
# had issues with quantized KV — verify before adding cache-type lines).
model = /home/anon/software/models/gpt-oss-20b-mxfp4.gguf
ctx-size = 65536 # ⚗→✓ VERIFIED @65536 (04:11): 37.3/37.4 t/s unchanged, 14.0 used / 3.2
# free — the fast long-context option is real (37 t/s vs Coder-Next 16).
# 131072 is OFF the table: 32k→65k cost +1.2 GB (KV slope steepens past
# the sliding window) → 131k would land under 2.5 free. Client caps
# raised 32k→64k on 2026-08-08.
n-gpu-layers = 99
n-cpu-moe = 4 # ✓ SETTLED. MEASURED @6: 31.3 t/s · @4 (2026-07-16): 38.1 t/s, 12.7 used /
# 4.5 free — fastest model in the lineup. TTFT (~3-7 s) is the model THINKING
# (harmony reasoning streams as reasoning_content first) — not a slow load.
threads = 6
flash-attn = on
batch-size = 2048
ubatch-size = 512
cache-reuse = 256
defrag-thold = 0.1
temp = 1.0
top-p = 1.0
jinja = on
sleep-idle-seconds = 120
[gpt-oss-20b-low]
# Same GGUF, reasoning effort LOW — zero disk cost (like the 35B-Thinking alias, inverted).
# Cuts the multi-second thinking preamble for light/interactive use; keep [gpt-oss-20b]
# (default = medium effort) for hard problems. ⚠ if startup says the option is not
# recognized, the llama.cpp build predates chat-template-kwargs — comment that line out.
model = /home/anon/software/models/gpt-oss-20b-mxfp4.gguf
chat-template-kwargs = {"reasoning_effort":"low"}
ctx-size = 65536 # ⚗→✓ VERIFIED @65536 (04:11): 37.3/37.4 t/s unchanged, 14.0 used / 3.2
# free — the fast long-context option is real (37 t/s vs Coder-Next 16).
# 131072 is OFF the table: 32k→65k cost +1.2 GB (KV slope steepens past
# the sliding window) → 131k would land under 2.5 free. Client caps
# raised 32k→64k on 2026-08-08.
n-gpu-layers = 99
n-cpu-moe = 4
threads = 6
flash-attn = on
batch-size = 2048
ubatch-size = 512
cache-reuse = 256
defrag-thold = 0.1
temp = 1.0
top-p = 1.0
jinja = on
sleep-idle-seconds = 60
# ─────────────────────────────────────────────────────────────────────────────
# Notes
# ─────────────────────────────────────────────────────────────────────────────
# n-cpu-moe N : offload the first N MoE layers' experts to CPU. Higher = less VRAM (safer) but
# slower. On this display-driving GPU, a bit of offload is FAR better than a freeze.
# Only applies to MoE models (dense models ignore it — reduce ctx instead).
# override-tensor : finer control, e.g. `blk\.(1[2-9]|[2-9][0-9])\.ffn_.*_exps=CPU`.
# After ANY change: reload the server and run bench.py — keep every "VRAM free" ≥ ~2.5 GB.
+193
View File
@@ -0,0 +1,193 @@
# Speech-to-text — setup & usage
whisper.cpp serving Whisper models over an **OpenAI-compatible** transcription endpoint, so Open WebUI's
mic button works against local hardware. Added **2026-08-08**.
- **Box**: same fl server as the LLM router — but this runs **entirely on CPU** (Ryzen 5600X, 6 cores).
- **Files here**: `whisper@.service` (one template unit) + one `<instance>.env` per model.
Models live with the GGUFs in `~/software/models/`.
| instance | endpoint | model | speed / use |
|---|---|---|---|
| **`whisper@turbo`** | `http://$SERVER:11345/v1` | `ggml-large-v3-turbo-q5_0` (574 MB) | **default** — best accuracy, multilingual, ~15 s per request. This is what OWUI points at. |
| `whisper@small` | `http://$SERVER:11346/v1` | `ggml-small.en-q5_1` (181 MB) | English-only, ~5x lighter. Point a client here when latency beats accuracy. |
Both run at once — together they're well under 1 GB of RAM and **zero VRAM**, so there is no reason to
stop one to use the other. Adding a third is a new `.env` file plus `systemctl enable --now whisper@<name>`.
## How it runs
Template unit **`whisper@.service`** (a dedicated service user), from `/usr/bin/whisper-server` (Arch `extra/whisper-cpp`).
Each instance reads `~/.config/whisper/<instance>.env`:
```
WHISPER_MODEL=/home/<user>/software/models/ggml-large-v3-turbo-q5_0.bin
WHISPER_PORT=11345
WHISPER_LANG=auto
```
which expands to:
```
whisper-server --model $WHISPER_MODEL \
--host 0.0.0.0 --port $WHISPER_PORT \
--inference-path /v1/audio/transcriptions \
--convert --tmp-dir /run/whisper-<instance> \
--no-gpu --threads 6 --language $WHISPER_LANG
```
It is a template rather than N copied units on purpose: the `--tmp-dir` bug below had to be found once
and fixed once, and that only stays true while there is exactly one `ExecStart` in the repo.
| flag | why |
|---|---|
| `--no-gpu` | **Load-bearing.** 0 VRAM — see below. |
| `--convert` | ffmpeg transcodes webm/opus/ogg → 16 kHz wav. Browsers record opus; whisper.cpp reads wav. **Requires ffmpeg on the box.** |
| `--inference-path` | `--request-path` defaults to empty, so this alone puts the route at `/v1/audio/transcriptions` — exactly where OWUI posts. |
| `--tmp-dir` | **Required.** Defaults to `"."`, i.e. the CWD — which is `/` here, and unwritable. Without it every request returns `FFmpeg conversion failed.`, plain wav included. `RuntimeDirectory=whisper-%i` creates and owns `/run/whisper-<instance>`. |
| `--language` | `auto` for multilingual models; `en` for `.en` models, where detection is pointless. |
| `--threads 6` | all cores; `Nice=5` yields to the router's CPU-offloaded experts. |
| ports `11345`/`11346` | clear of `11343` (router), **`11344` (the standalone embedding llama-server)** and `11434` (Ollama). Router *children* take random ephemeral ports (32768+), so low ports are safe from them — but run `ss -ltnp` before claiming one. |
Keyless on the LAN, same posture as the router. If this ever leaves the trusted LAN it needs a proxy
with auth in front — whisper-server has no `--api-key`.
**Deploying changes**: `/etc/systemd/system/whisper@.service` is a symlink to the file in the repo, so a
`git pull` edit is on disk immediately — but systemd caches unit files, so it still needs a reload.
(Copying into `/etc` instead would work, but then every repo edit silently drifts.) On the server:
```
dotsync # nudges you if this dir changed
sudo systemctl daemon-reload
sudo systemctl restart whisper@turbo whisper@small
```
Editing only an `.env` needs no `daemon-reload` — just restart that instance.
First install (stow does not install units):
```
yay -S whisper-cpp ffmpeg
curl -L -o ~/software/models/ggml-large-v3-turbo-q5_0.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo-q5_0.bin
curl -L -o ~/software/models/ggml-small.en-q5_1.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en-q5_1.bin
# Point systemd at the REAL file in the repo, NOT the stowed symlink: stow already made
# ~/.config/whisper/whisper@.service a symlink, and systemctl rejects those with
# "Failed to link unit: Refusing to operate on linked unit file"
sudo systemctl link (readlink -f ~/.config/whisper/whisper@.service) # fish
sudo systemctl enable --now whisper@turbo whisper@small
```
## Why this is NOT a llama.cpp router preset
The router *does* expose `POST /v1/audio/transcriptions` (it rewrites the request into a chat completion
with the prompt "Transcribe audio to text"), and `gemma-4-E4B` genuinely accepts audio input. Tempting —
but two reasons it stays a separate service. Don't re-litigate this without re-checking both:
1. **`--models-max 1` eviction.** Every transcription would LRU-evict the resident chat model, and the
next chat message would reload it (~17.5 GB for GLM) with a cold prompt cache. Fine for a one-off
file; unusable for talking to a model.
2. **Format.** llama.cpp decodes audio via miniaudio — **wav/mp3/flac only**. OWUI's mic records
`audio/webm; codecs=opus` and its own transcode-to-mp3 step depends on ffprobe being usable inside
the OWUI AppImage; when that fails it silently forwards the opus file. `--convert` here makes the
question moot.
Third reason, softer: an LLM asked to transcribe can paraphrase or answer the audio instead of
transcribing it, and the `gemma-4-E4B` preset samples at temp 1.0. Whisper is purpose-built and boring.
**Still available as a fallback** for wav/mp3/flac (no config change needed), if these services are down:
```
curl -F file=@clip.wav -F model=gemma-4-E4B-it-UD-Q8_K_XL \
http://$SERVER:11343/v1/audio/transcriptions
```
## Open WebUI wiring
Admin Settings → **Audio** → Speech-to-Text:
| field | value |
|---|---|
| STT Engine | `OpenAI` |
| Base URL | `http://$SERVER:11345/v1` — no trailing path, OWUI appends `/audio/transcriptions`. Swap `11345``11346` for the fast instance. |
| API Key | any non-empty string (`none`) — ignored by whisper-server, but OWUI needs it set |
| STT Model | `whisper-1` — ignored by whisper-server; must not be blank |
| Request format | leave default (`multipart`) |
OWUI sends multipart `model` + optional `language` + `file`; whisper-server reads the fields it knows
and ignores the rest. Note it joins segments with newlines, so long dictations arrive in the composer
with line breaks mid-sentence.
## Verifying
```
systemctl status whisper@turbo whisper@small
rocm-smi --showmeminfo vram # must be UNCHANGED vs before the services started
curl -F file=@sample.wav http://$SERVER:11345/v1/audio/transcriptions # → {"text": "…"}
ffmpeg -i sample.wav -c:a libopus sample.webm # the case that actually matters:
curl -F file=@sample.webm http://$SERVER:11345/v1/audio/transcriptions # proves --convert
curl -F file=@sample.webm -F model=whisper-1 -F language=en \
http://$SERVER:11346/v1/audio/transcriptions # OWUI's exact shape
```
Then: with a chat model loaded, transcribe and confirm `http://$SERVER:11343/v1/models` still
shows it `ready`/`sleeping`**not** `unloaded`. That is the whole point of the separate service.
**Measured 2026-08-08** (6 threads, CPU, 11 s JFK sample, `--convert` in the path):
| model | per request | verdict |
|---|---|---|
| `large-v3-turbo-q5_0` (574 MB) | **~15.2 s** | accurate; too slow to dictate into comfortably |
| `small.en-q5_1` (181 MB) | **~2.2 s** | **~7x faster.** Word-for-word identical to turbo on the JFK sample — only punctuation differs (`And so my fellow Americans` vs turbo's `And so, my fellow Americans,`). |
Runs were tight: small.en 2.16 / 2.16 / 2.20 s over three passes, turbo 15.1-15.2 s over three.
The webm/opus path costs nothing extra (2.19 s with `--convert` + OWUI's field shape).
**On this evidence turbo is hard to justify as the default** — 13 seconds per utterance buys a comma on
clean audio. Where it should still win is names, jargon, accents and non-English speech, which the JFK
sample cannot show. If small.en holds up on real dictation, point OWUI at `:11346` and demote turbo to
the second instance (or just swap the ports in the two `.env` files).
The cost is **flat** for both models: whisper pads every request to a 30 s window, so a 3 s clip costs
what a 30 s one does (turbo 15.1 s vs 15.2 s; small.en 2.08 s vs 2.20 s). It is a fixed toll per
utterance, not a rate — which is why the model is the only thing that moves it.
Flash-attention is already on by default in whisper-server, and the `parakeet-*` binaries in the package
have no server integration, so **model size is the only real dial**.
A/B any candidate without touching a service:
```
curl -LO https://github.com/ggml-org/whisper.cpp/raw/master/samples/jfk.wav # known text
time whisper-cli -m ~/software/models/ggml-small.en-q5_1.bin -f jfk.wav -t 6 -nt
time whisper-cli -m ~/software/models/ggml-base.en-q5_1.bin -f jfk.wav -t 6 -nt
```
## Troubleshooting
- **Every request returns `{"error":"FFmpeg conversion failed."}`, wav included** → the temp dir is not
writable by the service user. Check `--tmp-dir /run/whisper-<instance>` is in ExecStart and that
`RuntimeDirectory=` created it (`ls -ld /run/whisper-turbo`). ffmpeg itself is fine — the startup log
says `ffmpeg is available.` either way.
- **`Failed to load environment files`** → the instance has no `.env` (`whisper@foo` needs `foo.env`),
or the repo was not re-stowed after adding it (`install.sh` links new files; `dotsync` does it for you).
- **webm/opus uploads fail, wav works** → ffmpeg missing or `--convert` dropped. `ffmpeg -version` on
the box; the journal logs the conversion command on each non-wav request.
- **VRAM moved when transcribing** → `--no-gpu` lost, or the package was rebuilt with a GPU backend.
Fix immediately; this is the freeze scenario.
- **Empty / hallucinated text on near-silence** → whisper's classic failure. Try VAD:
download `ggml-silero-v5.1.2.bin` and add `--vad --vad-model …` to the template.
- **Still too slow** → next step down is `ggml-base.en-q5_1.bin` (56 MB, ~4x lighter again) at a real
cost in accuracy on names and jargon. New `.env`, or edit `small.env`.
- **OWUI: "Server Connection Error"** → base URL has a trailing `/audio/transcriptions` (it appends that
itself), or the port is not reachable from the client host.
## Watch list
- `whisper-cpp` also ships **`parakeet-cli`** — NVIDIA Parakeet is much faster than Whisper on CPU for
English. No server front-end upstream yet (`examples/` has `parakeet-cli` but no server path); worth
rechecking, it would be the real fix for the 15 s toll.
- **TTS** (the other half of voice chat) is unconfigured — OWUI can drive its own, or llama.cpp can
serve OuteTTS via `--model-vocoder`. Separate job.
+6
View File
@@ -0,0 +1,6 @@
# FAST instance (port 11346) — English-only, ~5x lighter than turbo.
# Same API; point a client at :11346 instead of :11345 when latency matters more than accuracy.
# For a multilingual equivalent use ggml-small-q5_1.bin and set WHISPER_LANG=auto.
WHISPER_MODEL=/home/anon/software/models/ggml-small.en-q5_1.bin
WHISPER_PORT=11346
WHISPER_LANG=en
+6
View File
@@ -0,0 +1,6 @@
# DEFAULT instance (port 11345) — the one Open WebUI points at.
# Best accuracy, multilingual. Measured 2026-08-08: ~15.1 s per request, FLAT (whisper pads every
# clip to a 30 s window, so a 3 s utterance costs the same as a 30 s one).
WHISPER_MODEL=/home/anon/software/models/ggml-large-v3-turbo-q5_0.bin
WHISPER_PORT=11345
WHISPER_LANG=auto
+48
View File
@@ -0,0 +1,48 @@
[Unit]
Description=whisper.cpp STT server "%i" (CPU-only) — OpenAI-compatible /v1/audio/transcriptions
Documentation=file:///home/anon/.config/whisper/README.md
After=network-online.target
Wants=network-online.target
[Service]
User=anon
Group=anon
WorkingDirectory=/
# TEMPLATE UNIT — one file, N instances. `systemctl start whisper@small` reads its model/port/language
# from ~/.config/whisper/small.env. Deliberately not N copies of this unit: the --tmp-dir bug below had
# to be found once and fixed once, and that only holds if there is exactly one ExecStart in the repo.
EnvironmentFile=/home/anon/.config/whisper/%i.env
# whisper-server's --tmp-dir defaults to "." — with WorkingDirectory=/ that means it tries to write
# uploads and ffmpeg output into the filesystem root, and every request dies with
# {"error":"FFmpeg conversion failed."} (yes, even plain wav: --convert pipes everything to ffmpeg)
# RuntimeDirectory= creates /run/whisper-<instance>, owned by this user, wiped on stop. Per-instance so
# two servers can never collide on a temp filename.
RuntimeDirectory=whisper-%i
RuntimeDirectoryMode=0700
#
# ⚠⚠ --no-gpu IS LOAD-BEARING. The GPU on this box drives the display AND holds a 13-15 GB chat model;
# ROCm does not OOM cleanly, it spills to GTT and freezes the whole PC. Whisper is CPU-only BY
# DESIGN (same rule as the Qwen3-Embedding preset): 0 VRAM means it can never stack into an
# overcommit, and it never evicts / gets evicted by the router. Do not remove.
# --convert shells out to ffmpeg (webm/opus/ogg -> 16 kHz wav). This is the whole reason the Open WebUI
# mic button works — the browser records opus, whisper.cpp reads wav.
# --inference-path puts the route exactly where OWUI posts (--request-path defaults to empty), so a
# client's base URL is http://192.168.0.204:<port>/v1 .
ExecStart=/usr/bin/whisper-server \
--model ${WHISPER_MODEL} \
--host 0.0.0.0 --port ${WHISPER_PORT} \
--inference-path /v1/audio/transcriptions \
--convert \
--tmp-dir /run/whisper-%i \
--no-gpu \
--threads 6 \
--language ${WHISPER_LANG}
Restart=always
RestartSec=2
# Yield to the llama.cpp router: its CPU-offloaded MoE experts (n-cpu-moe) want the same 6 cores.
Nice=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target