[Sync] Lua Hyprland chain, licence cleanup
This commit is contained in:
1 parent
93b4f695ce
commit
47166d877f
183 files changed
+5618
-5839
No files matched your search
@@ -1,21 +0,0 @@
|
||||
|
||||
# ▄▀█ █▄░█ █ █▀▄▀█ ▄▀█ ▀█▀ █ █▀█ █▄░█
|
||||
# █▀█ █░▀█ █ █░▀░█ █▀█ ░█░ █ █▄█ █░▀█
|
||||
|
||||
# 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
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
|
||||
# ░▒▒▒░░░░░▓▓ ___________
|
||||
# ░░▒▒▒░░░░░▓▓ //___________/
|
||||
# ░░▒▒▒░░░░░▓▓ _ _ _ _ _____
|
||||
# ░░▒▒░░░░░▓▓▓▓▓▓ | | | | | | | __/
|
||||
# ░▒▒░░░░▓▓ ▓▓ | |_| | |_/ /| |___
|
||||
# ░▒▒░░▓▓ ▓▓ \__ |____/ |____/
|
||||
# ░▒▓▓ ▓▓ //____/
|
||||
|
||||
|
||||
$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
|
||||
@@ -1,221 +0,0 @@
|
||||
|
||||
# █▄▀ █▀▀ █▄█ █▄▄ █ █▄░█ █▀▄ █ █▄░█ █▀▀ █▀
|
||||
# █░█ ██▄ ░█░ █▄█ █ █░▀█ █▄▀ █ █░▀█ █▄█ ▄█
|
||||
|
||||
# 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
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
|
||||
# █▀▄▀█ █▀█ █▄░█ █ ▀█▀ █▀█ █▀█ █▀
|
||||
# █░▀░█ █▄█ █░▀█ █ ░█░ █▄█ █▀▄ ▄█
|
||||
|
||||
# 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
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
|
||||
# █░█ █▀ █▀▀ █▀█ █▀█ █▀█ █▀▀ █▀▀ █▀
|
||||
# █▄█ ▄█ ██▄ █▀▄ █▀▀ █▀▄ ██▄ █▀░ ▄█
|
||||
|
||||
# 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
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
|
||||
# █░█░█ █ █▄░█ █▀▄ █▀█ █░█░█ █▀█ █░█ █░░ █▀▀ █▀
|
||||
# ▀▄▀▄▀ █ █░▀█ █▄▀ █▄█ ▀▄▀▄▀ █▀▄ █▄█ █▄▄ ██▄ ▄█
|
||||
|
||||
# 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
|
||||
@@ -11,10 +11,6 @@ Switch to Last-Used Keyboard Layout=none,Meta+Alt+L,Switch to Last-Used Keyboard
|
||||
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
|
||||
@@ -53,8 +49,8 @@ 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
|
||||
MoveMouseToCenter=Meta+F6,Meta+F6,Center Pointer in Screen
|
||||
MoveMouseToFocus=Meta+F5,Meta+F5,Center Pointer in Focused Window
|
||||
MoveZoomDown=none,none,Move Zoomed Area Downwards
|
||||
MoveZoomLeft=none,none,Move Zoomed Area to Left
|
||||
MoveZoomRight=none,none,Move Zoomed Area to Right
|
||||
@@ -62,15 +58,15 @@ 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 One Desktop Down=Meta+Ctrl+Down,Meta+Ctrl+Down,Switch One Desktop Down
|
||||
Switch One Desktop Up=Meta+Ctrl+Up\tMeta+Ctrl+Shift+Up,Meta+Ctrl+Up,Switch One Desktop Up
|
||||
Switch One Desktop to the Left=Meta+Ctrl+Left,Meta+Ctrl+Left,Switch One Desktop to the Left
|
||||
Switch One Desktop to the Right=Meta+Ctrl+Right,Meta+Ctrl+Right,Switch One Desktop to the Right
|
||||
Switch Window Down=Ctrl+Alt+J\tCtrl+Alt+Down\tMeta+Shift+K\tMeta+Shift+Down\tMeta+Ctrl+J,Meta+Alt+Down,Switch to Window Below
|
||||
Switch Window Left=Ctrl+Alt+H\tCtrl+Alt+Left\tMeta+Shift+H\tMeta+Shift+Left\tMeta+Ctrl+H,Meta+Alt+Left,Switch to Window to the Left
|
||||
Switch Window Right=Ctrl+Alt+L\tCtrl+Alt+Right\tMeta+Shift+L\tMeta+Shift+Right\tMeta+Ctrl+L,Meta+Alt+Right,Switch to Window to the Right
|
||||
Switch Window Up=Ctrl+Alt+K\tCtrl+Alt+Up\tMeta+Shift+J\tMeta+Shift+Up\tMeta+Ctrl+K,Meta+Alt+Up,Switch to Window Above
|
||||
Switch to Desktop 1=Ctrl+F1\tMeta+1,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
|
||||
@@ -81,7 +77,7 @@ 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 2=Ctrl+F2\tMeta+2,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
|
||||
@@ -95,9 +91,9 @@ 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 Desktop=Meta+Ctrl+Shift+L\tMeta+Ctrl+Shift+Right,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 Desktop=Meta+Ctrl+Shift+H\tMeta+Ctrl+Shift+Left,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
|
||||
@@ -112,10 +108,11 @@ 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 Show Compositing=none,none,Toggle “Show Compositing” effect
|
||||
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 (Reverse)=Alt+Shift+Tab,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
|
||||
@@ -129,7 +126,7 @@ 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 Fullscreen=Shift+F11\tAlt+Return,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
|
||||
@@ -192,9 +189,9 @@ 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 Desktop=Meta+Ctrl+Alt+L\tMeta+Ctrl+Alt+Right,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 Desktop=Meta+Ctrl+Alt+H\tMeta+Ctrl+Alt+Left,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
|
||||
@@ -210,6 +207,36 @@ 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
|
||||
|
||||
[kwin][$InverseAction]
|
||||
Cycle Overview=Cycle Overview Opposite
|
||||
Increase Opacity=Decrease Opacity
|
||||
MoveZoomDown=MoveZoomUp
|
||||
MoveZoomLeft=MoveZoomRight
|
||||
Switch One Desktop Down=Switch One Desktop Up
|
||||
Switch One Desktop to the Left=Switch One Desktop to the Right
|
||||
Switch Window Right=Switch Window Left
|
||||
Switch Window Up=Switch Window Down
|
||||
Switch to Next Screen=Switch to Previous Screen
|
||||
Switch to Previous Desktop=Switch to Next Desktop
|
||||
Switch to Screen Below=Switch to Screen Above
|
||||
Switch to Screen to the Right=Switch to Screen to the Left
|
||||
Walk Through Windows (Reverse)=Walk Through Windows
|
||||
Walk Through Windows Alternative=Walk Through Windows Alternative (Reverse)
|
||||
Walk Through Windows of Current Application (Reverse)=Walk Through Windows of Current Application
|
||||
Walk Through Windows of Current Application Alternative (Reverse)=Walk Through Windows of Current Application Alternative
|
||||
Window Grow Horizontal=Window Shrink Horizontal
|
||||
Window Grow Vertical=Window Shrink Vertical
|
||||
Window One Desktop Up=Window One Desktop Down
|
||||
Window One Desktop to the Left=Window One Desktop to the Right
|
||||
Window One Screen Up=Window One Screen Down
|
||||
Window One Screen to the Right=Window One Screen to the Left
|
||||
Window Pack Right=Window Pack Left
|
||||
Window Pack Up=Window Pack Down
|
||||
Window Raise=Window Lower
|
||||
Window to Next Screen=Window to Previous Screen
|
||||
Window to Previous Desktop=Window to Next Desktop
|
||||
view_zoom_in=view_zoom_out
|
||||
|
||||
[mediacontrol]
|
||||
_k_friendly_name=Media Controller
|
||||
mediavolumedown=none,none,Media volume down
|
||||
@@ -245,6 +272,8 @@ powerProfile=Battery,Battery\tMeta+B,Switch Power Profile
|
||||
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 next task manager entry=Meta+Ctrl+PgDown,Meta+Ctrl+PgDown,Activate Next Task Manager Entry
|
||||
activate previous task manager entry=Meta+Ctrl+PgUp,Meta+Ctrl+PgUp,Activate Previous Task Manager Entry
|
||||
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
|
||||
@@ -261,12 +290,14 @@ 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
|
||||
clipboard_action=none,none,Toggle Automatic Popping Up of Action 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
|
||||
move active task to left=Meta+Ctrl+Shift+PgUp,Meta+Ctrl+Shift+PgUp,Move Active Task Manager Entry Backward
|
||||
move active task to right=Meta+Ctrl+Shift+PgDown,Meta+Ctrl+Shift+PgDown,Move Active Task Manager Entry Forward
|
||||
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
|
||||
@@ -335,7 +366,7 @@ _launch=Meta+K
|
||||
_launch=Meta+Ctrl+T
|
||||
|
||||
[services][systemsettings.desktop]
|
||||
_launch=\tTools
|
||||
_launch=Tools
|
||||
|
||||
[services][thorium-browser.desktop]
|
||||
_launch=Meta+Ctrl+B
|
||||
@@ -70,9 +70,9 @@ starves the desktop and freezes the PC (reboot). Hard-won rules:
|
||||
| `GLM-4.7-Flash-UD-Q4_K_XL` | **21.5** @moe22 | 24k | reasoning | quality coder (opencode subagents); KV fix + moe22 ✓ verified clean 08-08 |
|
||||
| `GLM-4.7-Flash-UD-Q3_K_XL` | **26.0** @moe12 (2.0 daytime baseline; ~28 clean) | 24k | reasoning | **fast coder** — re-added 09-21 because Q4's 21.5 dragged in agent loops; 3-bit, evening-safe at moe12 (13.4 used / 3.8 free @2.0); Q4 stays for hard reviews |
|
||||
| `Qwen3-Coder-Next-UD-IQ3_XXS` | 16.0 | **128k** | 80B-A3B | long-session coder (128k ctx) |
|
||||
| `Qwen3.8-27B-UD-IQ3_XXS` | **15.5** (10.9 w/o MTP) | 24k | vision, MTP, hybrid reasoner @ **xhigh** | ⚠ **clean-desktop specialty** (kept 09-10 over the ≥22 gate) — dense-27B quality, slow: its Gated-DeltaNet layers are Vulkan-bound (prefill ~35). 2.5 GB free @1.7 baseline @24k (✓ 10-02 on b11146, `-n 512`, acceptance 0.84). The *felt* slowness is the **xhigh thinking preamble** (17.5 s before the first answer token on a trivial prompt; `reasoning_effort=low` measured 6.45 s at the same decode on 10-02 — siblings benched then removed by decision, the kwarg is one line if ever wanted); **never on a busy evening desktop** (~1 GB free = GTT freeze) |
|
||||
| `Qwen3.8-27B-UD-IQ3_XXS` | **36–37** on b11429 (10-22 ×2; was 15.5 on b11146, 10.9 base) | 24k | vision, MTP, hybrid reasoner @ **xhigh** | ⚠ **clean-desktop specialty** (kept 09-10 over the ≥22 gate — **the speed half is now passed 1.7× over**, since llama.cpp 0.6.0 halved the per-step MTP cost; role re-rank pending the user) — dense-27B quality; prefill still GDN-bound (~20–35). 2.7 GB free @1.3 baseline @24k (✓ 10-22 on b11429, `-n 512`, acceptance 0.89, len 2.78). The *felt* slowness is the **xhigh thinking preamble** (17.5 s before the first answer token on a trivial prompt; `reasoning_effort=low` measured 6.45 s at the same decode on 10-02 — siblings benched then removed by decision, the kwarg is one line if ever wanted); **never on a busy evening desktop** (~1 GB free = GTT freeze) |
|
||||
| `Qwen3-Embedding-0.6B` | 33 (CPU) | 8k | CPU-only, `/v1/embeddings` | RAG/search embedder (not a chat model) |
|
||||
| `Ornith-1.5-35B-A3B-IQ3_XXS` | **32.1** @moe16 | 24k | MTP, vision, thinking | **agentic coder** (added 09-21) — Qwen3.5-MoE derivative claiming +15 Terminal-Bench / +5.6 SWE-bench over Qwen3.6-35B; the only coder with MTP; 13.8 used / 3.3 free @2.0 |
|
||||
| `Ornith-1.5-35B-A3B-IQ3_XXS` | **32.1** @moe16 | **32k** (10-22, unbenched) | MTP, vision, thinking | **agentic coder** (added 09-21) — Qwen3.5-MoE derivative claiming +15 Terminal-Bench / +5.6 SWE-bench over Qwen3.6-35B; the only coder with MTP; 13.8 used / 3.3 free @2.0 |
|
||||
|
||||
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.
|
||||
@@ -94,9 +94,9 @@ Treat clean night runs as the reference; don't retune on daytime deltas.
|
||||
- **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).
|
||||
- **Best single-model quality, speed no object** (quiet desktop only): `Qwen3.8-27B` — dense
|
||||
27B hybrid reasoner + vision at ~14.5 t/s; it cannot go faster on this card (GDN layers on
|
||||
Vulkan), and a busy evening desktop pushes it into GTT-freeze territory — check `rocm-smi` first.
|
||||
- **Best single-model quality** (quiet desktop only): `Qwen3.8-27B` — dense 27B hybrid reasoner +
|
||||
vision at **37 t/s since llama.cpp 0.6.0** (was ~15 on 0.5.0; prefill still GDN-bound on Vulkan),
|
||||
but a busy evening desktop pushes it into GTT-freeze territory — check `rocm-smi` first.
|
||||
- **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
|
||||
@@ -150,8 +150,13 @@ the router, but chats/presets saved against the old id need re-picking.
|
||||
2026-09-10: kept as ⚠ clean-desktop specialty after the gate bench finally ran on the v3 gguf
|
||||
(14.5 t/s MTP / 10.9 base, 2.8 GB free @1.5); server ctx restored to 24576 = client ctx, no client edits.
|
||||
2026-09-21: `GLM-4.7-Flash-UD-Q3_K_XL` (⚡ fast coder) added to all six client files next to the Q4.
|
||||
2026-10-22: Qwen3.8-27B quant race opened (ISTA GSQ-RCO IQ3_XXS-mtp + DFlash2 sibling) — bench-only,
|
||||
NO client edits until a candidate wins; see the watch list. Same night: fl found on llama.cpp 0.6.0
|
||||
(b11429) and the Qwen3.8 incumbent re-measured 37.1 t/s (15.5 on b11146) — every MTP/hybrid preset
|
||||
on the roster needs a re-sweep on this build; ledger numbers above it are from 0.5.0 unless dated 10-22.
|
||||
2026-09-21: `Ornith-1.5-35B-A3B-IQ3_XXS` (agentic coder, thinking + vision) added to all six client files after passing its gate (32.1 t/s @moe16).
|
||||
2026-09-21: default model → `Qwen3.6-35B-A3B-Thinking` everywhere (opencode global + plan/build/auto, pi defaultModel + thinking level medium, aichat common/lw/wm) — gemma-4-26B refused routine sysadmin work. Thinking preset gets `reasoning-budget 8192`. wm aichat caught up (GLM Q3, Ornith).
|
||||
2026-10-22: Ornith in pi: `Compacting in OVERFLOW recovery` right after a truncated reply is pi's *truncated-reply* recovery, not a full window — pi clamps each request's `max_tokens` to `contextWindow − estimate − 4096`, which on 24k/4096 starts exactly at the compaction threshold, so the first request after a compaction (estimate gains the system prompt + tool schemas) can leave a thinking reply ~1024 answer tokens → `length` → compact + retry. `compaction-watch.ts` now names the case with numbers; `common/.pi/CLAUDE.md` > Compaction tuning has the arithmetic. Applied the same day, UNBENCHED: Ornith `ctx-size` 24576 → 32768 + the seven client files (pi/opencode/aichat, common+lw+wm; predicted ≈ 0.1 GB of KV on this hybrid arch) — restart llama.service, then `./bench.py -m Ornith-1.5-35B-A3B-IQ3_XXS`; gate and the revert path are in the preset comment.
|
||||
2026-10-12: Ornith in pi: "Response was truncated before completion." = `finish_reason: length` — the `<think>` block counts against pi's 4096 `max_tokens` and the preset has no reasoning-budget. Fixed client-side: pi sends `thinking_budget_tokens` per thinking level on Ornith (`compat.thinkingTokenBudgetField` + `settings.json > thinkingBudgets`, common + lw). The preset deliberately keeps NO CLI reasoning-budget — it would override the per-request field (see the preset comment).
|
||||
|
||||
## Tuning cheat-sheet
|
||||
@@ -200,9 +205,27 @@ reasoning-effort siblings; for stable MTP decode numbers use `-n 512`.
|
||||
## Watch list
|
||||
|
||||
- **GLM MTP in llama.cpp** — the single biggest pending win (primary coder ~22 → ~30+).
|
||||
- **DFlash2 for Qwen3.8-27B** — `z-lab/Qwen3.8-27B-DFlash2-GGUF` drafter, upstream 1.81× vs the
|
||||
MTP head's 1.33× here (b11146 has the support). Blockers: #27408 (spec + mmproj → image requests
|
||||
500, so only as a text-only sibling preset) and #28158 (Vulkan OOB draft token, open). Trial =
|
||||
`spec-type draft-dflash`, `spec-draft-n-max 3`, no mmproj; n-max 7 does not fit the VRAM envelope.
|
||||
- **Qwen3.8-27B quant race (opened 2026-10-22, bench-only, not client-wired)** — two candidate
|
||||
sections in `config.ini` after the incumbent: `Qwen3.8-27B-GSQ-RCO-IQ3_XXS` (ISTA-DASLab GSQ-RCO
|
||||
file, 10.44 GB with MTP head = −0.49 GB vs unsloth v3) and its `-dflash` text-only sibling
|
||||
(`z-lab/Qwen3.8-27B-DFlash2-GGUF` Q4_K_M drafter, `spec-draft-n-max 3`). Expected: +4 % decode and
|
||||
+0.5 GB free from the file; +5–15 % over MTP from DFlash2 on RDNA3/Vulkan (not the paper's 1.8×).
|
||||
Quality is unmeasured against our file — the user's own prompts decide. First run 10-22 03:14: both
|
||||
candidates HTTP 400 = fl's config.ini not pulled yet (router: "model not found"); the incumbent in
|
||||
the same run read **37.1 t/s on b11429** (see its row). Re-run 03:25, one session: incumbent 36.2 /
|
||||
2.7 free, **GSQ 37.5 / 3.2 free** (+3.6 %, +0.5 GB — speed+VRAM passed, **quality call open**),
|
||||
**DFlash2 15.4 / 1.9 free → dropped** (tombstoned in `config.ini`). Ruled out by the same
|
||||
research: ByteShape IQ4_XS (13.1 GB), ISTA IQ3_S (12.1 GB with MTP, under the floor), the
|
||||
adaptive KV-cache-streaming fork (CUDA-only, PCIe 5.0). Still open: #27408 (spec + mmproj → 500),
|
||||
#28158 (Vulkan OOB draft token). Research + verdicts + download lines: the config block.
|
||||
- **llama.cpp 0.7.0 (b11669) — HOLD fl at 0.6.0**: #30688, Vulkan draft-mtp decode 4× / prefill 6×
|
||||
slower (target compute buffer lands in GTT = this box's freeze precursor). Every MTP preset on the
|
||||
roster is exposed. When `pacman -Si llama-cpp` on fl shows 0.7.0, `IgnorePkg` it until fixed.
|
||||
The 0.5.0 → 0.6.0 jump itself (Qwen3.8 15.5 → 37) is llama-side, best explained by #29856 (hybrid
|
||||
graph reserve; the old 10.9 base was probably GTT-starved, not kernel-bound — see the preset block).
|
||||
**ggml 0.26.0** (next `-Syu`): #30039 reports a −45 % prefill regression on a GDN hybrid — bench
|
||||
Qwen3.8 before and after; downgrade ggml/ggml-vulkan from the pacman cache if prefill tanks.
|
||||
- **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).
|
||||
|
||||
> **vLLM on this box** (bench rig, docker/ROCm, never resident together with the router): see `../vllm/README.md`.
|
||||
@@ -102,7 +102,9 @@ def force_unload(base, key):
|
||||
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
|
||||
st = m.get("status", "loaded") # b11146 sends {'value': 'loaded'|'sleeping'|'unloaded'}; older a string
|
||||
st = st.get("value", "loaded") if isinstance(st, dict) else st
|
||||
if st not in ("loaded", "loading"): # builds without 'status': try them all
|
||||
continue
|
||||
try:
|
||||
_open(root + "/models/unload", key, {"model": m["id"]}, timeout=30).read()
|
||||
|
||||
@@ -379,6 +379,36 @@ sleep-idle-seconds = 60
|
||||
# stall/500) → any trial is a separate TEXT-ONLY preset; this one keeps MTP + vision.
|
||||
# Open on Vulkan for MTP and DFlash alike: #28158 — intermittent draft token id 248320
|
||||
# (= n_vocab) → HTTP 500 "Invalid input batch", mostly after very long prompts. If seen: that.
|
||||
# ✦ 2026-10-22 QUANT RACE OPENED (reddit tip: ByteShape / ISTA 3-bit quants + DFlash2 + KV streaming):
|
||||
# two bench-only candidate sections follow this preset — [Qwen3.8-27B-GSQ-RCO-IQ3_XXS] (ISTA file,
|
||||
# −0.49 GB, MTP) and its -dflash text-only sibling; the research + verdicts live in that block.
|
||||
# ⚠ llama.cpp 0.7.0 (b11669) = #30688, Vulkan draft decode 4× slower (compute buffer lands in GTT):
|
||||
# hold fl at 0.6.0 until fixed — hits EVERY MTP preset here, not only this one.
|
||||
# ✦ BUILD DATUM 2026-10-22 03:15 — fl turned out to be on 0.6.0 (b11429) + ggml-vulkan 0.25.3-2 (the
|
||||
# SAME kernels as 10-02): bench 37.1 t/s / 21 pp / TTFT 3.3 / 14.4 used / 2.7 free @1.3 (-n 512).
|
||||
# Server: 221 tok @ 37.07, draft acceptance 0.892 (141/158), mean len 2.78 → ~75 ms per
|
||||
# speculative step vs ~173 on b11146 — a llama.cpp-side change (b11147..b11429), NOT the Vulkan
|
||||
# kernels (ggml is a separate package, unchanged). A 4096-token think-run: 27.3 t/s @ acceptance
|
||||
# 0.51 / len 2.02 = the same ~74 ms step; acceptance sags over long thinking. Footprint 13.1,
|
||||
# unchanged. → the ≥22 gate is PASSED 1.7× over; the 'clean-desktop specialty' verdict's SPEED half
|
||||
# is obsolete (the VRAM half — never on a busy desktop — still stands). Role re-rank = user's call.
|
||||
# 03:25 re-run: 36.2 / 45 pp / TTFT 8.6 / 14.5 / 2.7 — reproducible.
|
||||
# CAUSE (researched 10-22; a hypothesis — no PR in b11147..b11429 claims any such gain): Arch's llama-cpp
|
||||
# links the SYSTEM ggml (-DLLAMA_USE_SYSTEM_GGML=ON), so every ggml-side Vulkan PR (#29476 GDN tune,
|
||||
# #29280 descriptor reuse, FA work) is EXCLUDED — those ship in ggml 0.26.0, not on fl yet. Best fit:
|
||||
# #29856 (10-03) "gather the recurrent states once so the reserve covers every split" — on 0.5.0 the
|
||||
# hybrid graph was silently re-reserved after the first speculative step and the target compute
|
||||
# buffer could land partly in GTT = the #30688 mechanism seen from the other side. The arithmetic
|
||||
# agrees: the old no-MTP base (10.9 t/s = 92 ms/token) was SLOWER than today's whole MTP step (75 ms
|
||||
# for ≥3 verified tokens + 2 draft passes), so the "10.9 base = the GDN kernel wall" story above is
|
||||
# SUSPECT: that base was most likely GTT-starved, not kernel-bound. Cheap check whenever the card is
|
||||
# free: a TEMP -nomtp alias on 0.6.0 — ~22–26 t/s confirms it, ~11 refutes it. #27694 (probabilistic
|
||||
# drafter, 10-02) explains acceptance 0.84 → 0.89, not the step time. (Also learned: the 9B is hybrid
|
||||
# too, 24 GDN + 8 attn per #30039 — the "regular-attention qwen35" remark further down is wrong.)
|
||||
# ⚠ ggml 0.26.0 (the next -Syu brings ggml + ggml-vulkan 0.26.0-1, SOVERSION-compatible, unrebuilt):
|
||||
# #30039 reports −45 % PREFILL on a GDN hybrid with b11429 binaries, TG flat → bench this preset right
|
||||
# before and after that upgrade; if prefill tanks, downgrade ggml/ggml-vulkan from the pacman cache.
|
||||
# REFERENCE from here: 37.1 (b11429). Every other MTP/hybrid preset needs a re-sweep on this build.
|
||||
# ⚠ Needs a CURRENT llama.cpp build: first "qwen35" file HERE with ssm.*/hybrid-attn keys
|
||||
# (the 9B is regular-attention qwen35). If the load fails with unknown-architecture /
|
||||
# missing-tensor errors, update llama.cpp on fl before touching this preset.
|
||||
@@ -418,6 +448,150 @@ min-p = 0
|
||||
jinja = on
|
||||
sleep-idle-seconds = 120
|
||||
|
||||
# ═══ QUANT RACE 2026-10-22 — Qwen3.8-27B third-party 3-bit quants + DFlash2 (bench-only, NOT client-wired) ═══
|
||||
# Trigger: r/LocalLLaMA 2026-09-24 "Qwen-3.8-27B is good enough that I stopped using API": a 16 GB user
|
||||
# (RTX 5060 Ti, CUDA, PCIe 5.0 x8, display on the iGPU) runs ByteShape IQ4_XS + DFlash2 + Raymond's
|
||||
# KV-cache streaming; a reply says "try the ISTA IQ3_S / XXS — more speed and context, couldn't tell a
|
||||
# difference in quality". What survives contact with THIS box (16 GB + display, Vulkan/RADV, PCIe 4.0 x8):
|
||||
# · KV-cache streaming (troed → RaymondHuang210129/llama.cpp-adaptive-kv-streaming): OUT. CUDA-only —
|
||||
# the README lists Vulkan as "memory-infrastructure tests only" (no attention path), ROCm = one
|
||||
# unreviewed PR for gfx1201; both authors say PCIe 5.0 is required (PCIe 4.0 users: 2–4 t/s @192k);
|
||||
# no upstream PR (discussion #28216 only). And it is a 160k+ tool — clients run this model at 24k.
|
||||
# · ByteShape (byteshape/Qwen3.8-27B-GGUF, proprietary "ShapeLearn" per-tensor mixed types, the IQ*
|
||||
# labels are size classes; MTP head kept, quantized IQ3/IQ4 vs Q6_K/Q8_0 elsewhere): IQ4_XS-3.84bpw
|
||||
# 13.08 GB → OUT (ceiling here ≈ 11 GB file); the commenter's own file is troed's 12.25 GB
|
||||
# ASCII-vocab-pruned build (248320 → 129006 tokens) → OUT twice. Same size class as the incumbent:
|
||||
# IQ3_S-3.23bpw 11.035 GB (vendor blog: 98.7 % of BF16 task avg vs 93.6 % for unsloth UD-IQ3_XXS —
|
||||
# vendor numbers; the one independent KLD run ranks their IQ4_XS BELOW unsloth's UD-IQ4_XS, 0.050 vs
|
||||
# 0.019), IQ3_XS-3.01bpw 10.353 GB, IQ3_XXS-2.88bpw 9.919 GB. Optional third runner, see below.
|
||||
# · ISTA (ISTA-DASLab/Qwen3.8-27B-GSQ-RCO-GGUF: GSQ = learned per-coordinate grids, RCO = per-tensor
|
||||
# type under a size budget, standard GGUF block types, imatrix ~90 % English — discussion #7 reports
|
||||
# degraded Chinese; "-mtp" files carry the head, +0.35 GB, weights identical):
|
||||
# IQ3_S-mtp 12.12 GB → footprint ≈ 14.2 → ~1.4 free @1.6 = UNDER THE 1.5 FLOOR. Plain IQ3_S 11.77
|
||||
# fits only WITHOUT any drafter (~3.4 free) at ~10 t/s → no. Both reddit comments measured "more
|
||||
# speed and context" against a 13 GB file, not against a 3-bit one.
|
||||
# IQ3_XXS-mtp 10.443 GB = 0.49 GB under the incumbent → the one ISTA file with a case here.
|
||||
# Quality evidence (nobody measured it against OUR unsloth v3 file): ISTA card wiki-ppl 7.20 vs
|
||||
# BF16 7.05, AIME25/GPQA-D/LCBv6 within 1–2 pts of BF16; ByteShape's blog scores it 94.4 vs 93.6
|
||||
# for UD-IQ3_XXS (= equal); Agention's KLD table puts it at 0.11, ~2× their own IQ3_XXS. Thread:
|
||||
# one user "ISTA even at lower quants did better", troed "tried it, not as good for my use".
|
||||
# → bench.py settles speed + VRAM only; the quality half is the user's own prompts, both files.
|
||||
# · DFlash2 (z-lab/Qwen3.8-27B-DFlash2-GGUF: 1.92B block-diffusion drafter, 5 layers, 2048-token
|
||||
# sliding window, no own embeddings/output head; Q4_K_M 1.143 GB; llama.cpp support since b10658 =
|
||||
# already in fl's b11146): acceptance length ~5.4 vs the MTP head's 2.6 here. RDNA3/Vulkan reports
|
||||
# (PR #27342 thread): 7900 XTX RADV n-max 3 = +4–12 % decode over MTP, prefill −1 to −12 %; Strix
|
||||
# Halo RADV n-max 4 = 19–23 t/s vs MTP 20; R9700 n-max 4 = 52 vs 47; a net LOSS once layers spill.
|
||||
# Expect +5–15 % over the incumbent (37.1 on b11429, 10-22), NOT the paper's 1.8× — every verified draft token
|
||||
# re-runs the 48 GDN layers. Acceptance falls with context (5090 datum: 31–36 % at 23–63k vs MTP
|
||||
# 55 %) = why the redditor says "at low context".
|
||||
# VRAM: drafter 1.14 GB + (1 + n-max) × ~150 MiB recurrent-state snapshots (n-max 3 ≈ 0.6 GB) +
|
||||
# drafter KV — the MTP head measured 1.5 GB against a ~0.7 estimate, so budget ~2 GB and let the
|
||||
# bench.py guard decide; the ISTA file's −0.49 GB is what buys the margin. Default n-max 7 = OUT.
|
||||
# Text-only: #27408 (spec + mmproj → image requests 500) still open → NO mmproj on that preset.
|
||||
# #28158 (Vulkan: draft token id 248320 → HTTP 500, rare, very long prompts) still open.
|
||||
# Build window: #29226 (b11132) broke drafters WITH embeddings but WITHOUT an output head (fixed by
|
||||
# #30111, b11512); the z-lab drafter has neither → should not be hit. FIRST GATE regardless:
|
||||
# "draft acceptance" in the server log > 0 (0.00 = that class of breakage → drop the sibling).
|
||||
# ⚠ BUILD: fl = llama-cpp 0.6.0-1 (b11429) + ggml 0.25.3-2 + ggml-vulkan 0.25.3-2, CONFIRMED 10-22 (the
|
||||
# b11146 → b11429 move alone took the incumbent 15.5 → 37.1, see its block). Arch extra also has
|
||||
# ggml/ggml-vulkan 0.26.0 (#27952 int8 coopmat prefill, #29476 GDN tune) — the next -Syu brings them
|
||||
# = another variable, bench before/after. Upstream 0.7.0 (b11669) = #30688: Vulkan
|
||||
# draft-mtp decode 4× / prefill 6× SLOWER, the target compute buffer re-allocated into GTT — on this
|
||||
# box that is the freeze precursor, and it hits every MTP preset on the roster. When
|
||||
# `pacman -Si llama-cpp` on fl shows 0.7.0: HOLD (IgnorePkg = llama-cpp ggml ggml-vulkan) until fixed.
|
||||
# Race on the current build first (one variable per run); the ggml 0.26.0 bump is a separate round.
|
||||
# PLAN (clean baseline, -n 512, incumbent re-run in the SAME session — the 10-02 band was 12.5–15.5):
|
||||
# 1. download (below) · 2. `sudo systemctl restart llama.service` ·
|
||||
# 3. ./bench.py -n 512 -m Qwen3.8-27B-UD-IQ3_XXS,Qwen3.8-27B-GSQ-RCO-IQ3_XXS,Qwen3.8-27B-GSQ-RCO-IQ3_XXS-dflash
|
||||
# 4. quality: the same 3–4 real prompts (incl. non-English) against incumbent vs GSQ.
|
||||
# GATE: GSQ-mtp ≥ incumbent decode (expected ~+4 %) with free ≥ 2.5 → quality is the user's call →
|
||||
# if it wins, either point the incumbent section's `model =` at the GSQ file (keeps the client id,
|
||||
# accepted misnomer) or rename = the seven-client-file round. DFlash sibling: acceptance > 0 AND
|
||||
# decode ≥ 1.10 × the SAME-SESSION incumbent AND free ≥ 2.5 → keep as the text-only fast sibling
|
||||
# (wire into clients then); else drop. Loser files: delete from ~/software/models (10.4 + 1.1 GB).
|
||||
# ✦ RACE RUN 2026-10-22 03:25 @1.3 baseline, -n 512, all three in ONE session (rows in bench-history.md):
|
||||
# incumbent UD-IQ3_XXS ....... 36.2 t/s / 45 pp / TTFT 8.6 / 14.5 used / 2.7 free
|
||||
# GSQ-RCO-IQ3_XXS (MTP) ...... 37.5 / 44 / 7.0 / 14.0 / 3.2 → +3.6 % decode, +0.5 GB free = exactly the
|
||||
# file-size prediction. Speed + VRAM half of the gate PASSED. OPEN: the quality half (same 3–4 real
|
||||
# prompts, one non-English, against both; nobody has measured ISTA vs unsloth v3 at this size).
|
||||
# GSQ -dflash ................ 15.4 / 32 / 18.0 / 15.3 / 1.9 → −59 % vs its own MTP row and under the
|
||||
# 2.5 target → DROPPED (tombstone below). Read the acceptance line once: `journalctl -u llama.service
|
||||
# --since 03:25 | grep 'draft acceptance'` — 0.00 = the #29226-window bug (fix #30111 = b11512 sits in
|
||||
# 0.7.0 behind #30688 → re-probe only on a fixed 0.7.x); > 0 = block-drafting re-runs the 48 GDN
|
||||
# layers per verified block and loses on this card (the RX 9070 XT datum in #25117 said the same)
|
||||
# → closed for good. Either way nothing to tune: n-max 2 only shrinks the loss.
|
||||
# NEXT: quality call → if GSQ wins, point the incumbent's `model =` at the GSQ file (keeps the client
|
||||
# id; the section name becomes a misnomer) or do the seven-client rename round, then delete the
|
||||
# unsloth file; if not, delete the GSQ file and the section. The DFlash2 drafter can go now.
|
||||
# ⬇ DOWNLOAD on fl — curl straight into the models dir (the HF cache has root-owned dirs from docker):
|
||||
# cd ~/software/models
|
||||
# curl -L -o Qwen3.8-27B-GSQ-RCO-IQ3_XXS-mtp.gguf https://huggingface.co/ISTA-DASLab/Qwen3.8-27B-GSQ-RCO-GGUF/resolve/main/Qwen3.8-27B-GSQ-RCO-IQ3_XXS-mtp.gguf
|
||||
# (dropped 10-22, only for a re-probe) curl -L -o Qwen3.8-27B-DFlash2-Q4_K_M.gguf https://huggingface.co/z-lab/Qwen3.8-27B-DFlash2-GGUF/resolve/main/Qwen3.8-27B-DFlash2-Q4_K_M.gguf
|
||||
# stat -c '%s %n' Qwen3.8-27B-GSQ-RCO-IQ3_XXS-mtp.gguf Qwen3.8-27B-DFlash2-Q4_K_M.gguf # 10442827840 · 1143006816
|
||||
# (mmproj: ISTA ships the same BF16 encoder — the existing mmproj-Qwen3.8-27B-F16.gguf is reused)
|
||||
|
||||
[Qwen3.8-27B-GSQ-RCO-IQ3_XXS]
|
||||
# ⚗ RACE CANDIDATE 2026-10-22 — bench-only, NOT in any client file. = the incumbent preset with the ISTA
|
||||
# file swapped in (MTP head Q6_K, same as unsloth's). Predicted @1.6 baseline @24k: ~14.1 used /
|
||||
# ~3.0 free (incumbent 14.4 / 2.7), decode ~38–39 (incumbent 37.1 on b11429) — bandwidth × file size, nothing
|
||||
# else moves. Knobs deliberately identical to the incumbent (n-max 2) for a clean A/B.
|
||||
# ✓ MEASURED 2026-10-22 03:25 @1.3: 37.5 t/s / 44 pp / TTFT 7.0 / 14.0 used / 3.2 free (incumbent 36.2 /
|
||||
# 2.7 in the same run). Speed + VRAM passed; QUALITY CALL PENDING — not client-wired until then.
|
||||
model = /home/anon/software/models/Qwen3.8-27B-GSQ-RCO-IQ3_XXS-mtp.gguf
|
||||
mmproj = /home/anon/software/models/mmproj-Qwen3.8-27B-F16.gguf
|
||||
spec-type = draft-mtp
|
||||
spec-draft-n-max = 2
|
||||
ctx-size = 24576
|
||||
n-gpu-layers = 99
|
||||
threads = 6
|
||||
flash-attn = on
|
||||
cache-type-k = q8_0
|
||||
cache-type-v = q4_0
|
||||
batch-size = 2048
|
||||
ubatch-size = 256
|
||||
cache-reuse = 256
|
||||
defrag-thold = 0.1
|
||||
no-mmproj-offload = true
|
||||
temp = 1.0
|
||||
top-p = 0.95
|
||||
top-k = 20
|
||||
min-p = 0
|
||||
jinja = on
|
||||
sleep-idle-seconds = 120
|
||||
|
||||
# ─── DROPPED 2026-10-22 03:25: [Qwen3.8-27B-GSQ-RCO-IQ3_XXS-dflash] (DFlash2 text-only sibling) ─────
|
||||
# 15.4 t/s / 32 pp / 15.3 used / 1.9 free vs 37.5 / 3.2 for the same file with its MTP head — the
|
||||
# z-lab block drafter is a net loss on this card (see the RACE RUN entry above for the two possible
|
||||
# causes and the only re-probe condition: a 0.7.x build that carries #30111 without #30688).
|
||||
# Removed as a loadable section on purpose: OWUI lists every router id, and 1.9 free on a busy
|
||||
# desktop is the GTT freeze. File can be deleted from the models dir: Qwen3.8-27B-DFlash2-Q4_K_M.gguf
|
||||
# Keys as benched (n-max 3, no mmproj — #27408):
|
||||
# model = /home/anon/software/models/Qwen3.8-27B-GSQ-RCO-IQ3_XXS-mtp.gguf
|
||||
# model-draft = /home/anon/software/models/Qwen3.8-27B-DFlash2-Q4_K_M.gguf
|
||||
# spec-type = draft-dflash
|
||||
# spec-draft-n-max = 3
|
||||
# ctx-size = 24576
|
||||
# n-gpu-layers = 99
|
||||
# threads = 6
|
||||
# flash-attn = on
|
||||
# cache-type-k = q8_0
|
||||
# cache-type-v = q4_0
|
||||
# batch-size = 2048
|
||||
# ubatch-size = 256
|
||||
# cache-reuse = 256
|
||||
# defrag-thold = 0.1
|
||||
# temp = 1.0
|
||||
# top-p = 0.95
|
||||
# top-k = 20
|
||||
# min-p = 0
|
||||
# jinja = on
|
||||
# sleep-idle-seconds = 120
|
||||
|
||||
# ─── optional 3rd runner (not added — copy the GSQ section if wanted): ByteShape IQ3_S-3.23bpw ────
|
||||
# 11.035 GB = the incumbent's size, vendor-claimed best quality in the class, MTP head kept (IQ3-quantized).
|
||||
# model = /home/anon/software/models/Qwen3.8-27B-IQ3_S-3.23bpw.gguf
|
||||
# curl -L -o Qwen3.8-27B-IQ3_S-3.23bpw.gguf https://huggingface.co/byteshape/Qwen3.8-27B-GGUF/resolve/main/Qwen3.8-27B-IQ3_S-3.23bpw.gguf # 11035354496 B
|
||||
|
||||
# ─── 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.
|
||||
@@ -552,6 +726,22 @@ sleep-idle-seconds = 30
|
||||
# here "like the Thinking preset" silently disables the pi knob and brings back pi's
|
||||
# "Response was truncated before completion." (= finish_reason length: think block + answer
|
||||
# > the client's 4096 max_tokens). An instruct alias (budget 0) in a SEPARATE section is fine.
|
||||
# ⚠ 2026-10-22: pi's "Compacting in OVERFLOW recovery" on this preset is NOT this window filling
|
||||
# up — pi clamps each request's max_tokens to contextWindow − estimate − 4096, which on
|
||||
# 24k/4096 starts exactly at its compaction threshold; the first request after a compaction
|
||||
# (estimate gains the system prompt + tool schemas) can leave the thinking reply ~1024 answer
|
||||
# tokens → finish_reason length → pi compacts again and retries. Arithmetic + options in
|
||||
# common/.pi/CLAUDE.md > Compaction tuning > max_tokens clamp.
|
||||
# APPLIED 2026-10-22 (same day, unbenched): ctx-size 24576 → 32768 here + 32768 in the seven
|
||||
# client files (pi contextWindow both blocks common+lw, opencode limit.context common+lw,
|
||||
# aichat max_input_tokens 30000 common+lw+wm). Threshold and clamp in pi now sit at 24576,
|
||||
# above anything a fresh post-compaction context reaches. Predicted cost ≈ 0.1 GB: hybrid
|
||||
# GDN 3:1 = 10 attention layers with small GQA, ~8 KB/token of q8/q4 KV → ~70 MB for +8192,
|
||||
# plus a few MB of FA scratch — read the "KV self size" line in the load log to confirm.
|
||||
# GATE on the first load: `sudo systemctl restart llama.service`, then
|
||||
# `./bench.py -m Ornith-1.5-35B-A3B-IQ3_XXS` — free ≥ 2.5 at the 2.0 baseline and decode
|
||||
# ≈ 32 t/s → keep; else back to 24576 in all eight files (the router reads presets once at
|
||||
# startup, and pi at 32k against a 24k server produces REAL overflow recoveries).
|
||||
# ⬇ DOWNLOAD from bartowski/Ornith-1.5-35B-A3B-GGUF (imatrix; the official repo has only Q4_K_M+
|
||||
# ≥ 21.7 GB, unsloth has no 1.5 quants yet):
|
||||
# Ornith-1.5-35B-A3B-IQ3_XXS.gguf 15,340,447,392 B · sha256 8918ccb9ee29abe3875efec0c3e86f0e35ef0b8a03e3f0e1c422869859a518d0
|
||||
@@ -560,7 +750,9 @@ model = /home/anon/software/models/Ornith-1.5-35B-A3B-IQ3_XXS.gguf
|
||||
mmproj = /home/anon/software/models/mmproj-Ornith-1.5-35B-A3B-f16.gguf
|
||||
spec-type = draft-mtp
|
||||
spec-draft-n-max = 2
|
||||
ctx-size = 24576 # = the daily driver; n_ctx_train 262144 — raise only after a bench
|
||||
ctx-size = 32768 # 24576 → 32768 on 2026-10-22 for pi's max_tokens clamp (⚠ note above);
|
||||
# UNBENCHED until the next restart — gate free ≥ 2.5 @2.0, decode unchanged;
|
||||
# n_ctx_train 262144, KV is cheap here (hybrid GDN, ~8 KB/token)
|
||||
n-gpu-layers = 99
|
||||
n-cpu-moe = 16 # ✓ KEEP (09-21: 32.1 t/s, 13.8 used / 3.3 free @2.0; moe20 was 27.9 /
|
||||
# 12.4 / 4.8). = daily-driver envelope; moe14 not worth the margin.
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
# vLLM on fl (docker, ROCm) — side by side with the llama.cpp router, never at the same time
|
||||
|
||||
Added 2026-10-21 to answer one question: **how does vLLM compare with llama.cpp on this card, on the
|
||||
same models?** It is a bench rig, not a second production endpoint. Nothing here was run on fl yet —
|
||||
fl has no sshd, so every command below is for you to run there; the first run will find the rough edges.
|
||||
|
||||
- **Box**: the same fl as the router — RX 7600 XT 16 GB (gfx1102, RDNA3), 5600X, 48 GB RAM. The card
|
||||
drives the display. llama.cpp runs the Arch Vulkan build; vLLM needs ROCm, which comes **inside the
|
||||
docker image** — nothing ROCm is installed on the host.
|
||||
- **Files**: `vllm.ini` (presets, same `key = --flag` convention as `../llamacpp/config.ini`),
|
||||
`vllmctl.py` (start/stop with the VRAM guards), `bench-vs.py` (one table for both engines).
|
||||
- **Port 11348**. Not in the router, not in any client list (opencode/pi/aichat/OWUI) on purpose.
|
||||
|
||||
## ⚠ The one rule: one engine resident at a time
|
||||
|
||||
vLLM pre-allocates `gpu-memory-utilization` × 16 GiB at startup and keeps it, idle or not. The router
|
||||
loads models lazily on request. So while vLLM is up, **any client request to :11343** makes the router
|
||||
load a model on top of it → overcommit → GTT spill → frozen PC (the same failure `--models-max 1`
|
||||
guards against). Unloading resident models is not enough; the clients on the LAN are the problem.
|
||||
|
||||
`vllmctl.py up` therefore refuses while `llama.service` is active. The sequence is always:
|
||||
|
||||
```fish
|
||||
~/.config/vllm/vllmctl.py up <preset> --stop-router # sudo systemctl stop llama.service, then start vLLM
|
||||
# … use / bench vLLM …
|
||||
~/.config/vllm/vllmctl.py down # stop container, wait for VRAM to drain
|
||||
sudo systemctl start llama.service # router back for the LAN
|
||||
```
|
||||
|
||||
The other order is the only one that should fail safely: vLLM compares its requested budget against
|
||||
free VRAM at startup and aborts when a router model is resident ("free memory … is less than desired
|
||||
GPU memory utilization" — reported by users, not something verified on this card). Everything else freezes.
|
||||
|
||||
## What "same models" means here, and the size surprise
|
||||
|
||||
llama.cpp runs GGUF; vLLM runs Hugging Face safetensors (bf16 / AWQ / GPTQ / compressed-tensors).
|
||||
The GGUF files cannot be loaded natively, so the pairs below are **the same model at a vLLM-native
|
||||
quant**, not the same files (same-file GGUF loading is phase 2, see the end).
|
||||
|
||||
Sizes from the HF API, 2026-10-21 — the "4-bit" vLLM checkpoints are **10–11.5 GB** because embeddings,
|
||||
lm_head and the vision tower stay bf16. They are bigger than the Q6/Q8 GGUFs they pair with, so on
|
||||
16 GB they are **clean-desktop only**, at `gpu-memory-utilization` ≈ 0.78 and 16k context:
|
||||
|
||||
| pair | llama.cpp preset (ledger) | vLLM preset (`vllm.ini`) | vLLM weights | fits? |
|
||||
|---|---|---|---|---|
|
||||
| smoke | — | `Qwen3-0.6B-smoke` = `Qwen/Qwen3-0.6B` | 1.5 GB | yes — proves docker+GPU+image |
|
||||
| gemma-4-E4B | `gemma-4-E4B-it-UD-Q8_K_XL` — 57 t/s **with MTP**, 96k ctx, ~7 GB | ~~`google/gemma-4-E4B-it-qat-w4a16-ct`~~ | 11.5 GB | **✗ no** (2026-10-21): vLLM needs ~16 GiB before any KV — the bf16 per-layer embedding table (262144 × 42 × 256 × 2 B = 5.6 GB) is held twice. Tombstone in the ini. |
|
||||
| Qwen3.5-9B | `Qwen3.5-9B-UD-Q6_K_XL` — 32 t/s **with MTP**, 32k ctx, 9 GB | `Qwen3.5-9B-w4a16` = `RedHatAI/Qwen3.5-9B-quantized.w4a16` | 11.0 GB | the last viable pair: 0.75 on a 2.1 GB desktop, 0.78 clean; prefill batch 512 |
|
||||
|
||||
**Cannot be compared on this card at all**: every 30B+ MoE (Coder-30B, GLM-4.7-Flash, Qwen3.6-35B,
|
||||
Ornith, gemma-4-26B), Coder-Next 80B and Qwen3.8-27B — they only fit llama.cpp through `n-cpu-moe`
|
||||
expert offload into RAM, which vLLM does not have (its MoE CPU-offload RFC died stale; the only
|
||||
option is whole-weight `cpu_offload_gb`, which is slow). gpt-oss-20b (MXFP4, ~13 GB) is marginal and
|
||||
its RDNA3 kernel status is unknown — not in the ini.
|
||||
|
||||
**Why the "docker should just work" expectation does not hold here**: of the three first-day failures,
|
||||
one was the image (no gfx1102 kernels → the override, solved) and two were memory. vLLM keeps
|
||||
everything in VRAM — bf16 embeddings, fp32 logits during profiling, CUDA-graph memory — on a card that
|
||||
also drives the desktop. No image changes a model's memory need; the ROCm images on Docker Hub are the
|
||||
right ones.
|
||||
|
||||
So the comparison is **not apples to apples** and the table says so: llama.cpp rows run Q8/Q6 with a
|
||||
speculative MTP draft at a bigger context; vLLM rows run int4 without speculation at 16k. Read it as
|
||||
"what each engine delivers on this card for this model", not as a kernel shoot-out.
|
||||
|
||||
## Setup on fl (once)
|
||||
|
||||
```fish
|
||||
sudo pacman -S docker # ~20 GB free disk for the ROCm image + 25 GB for two models
|
||||
sudo systemctl enable --now docker
|
||||
sudo usermod -aG docker anon # then log out / in (or `newgrp docker`)
|
||||
cd ~/.dots && ./install.sh # links ~/.config/vllm/* (new files are not live until re-stow)
|
||||
~/.config/vllm/vllmctl.py pull # vllm/vllm-openai-rocm:v0.32.0 (ROCm 10.0; 11.6 GB download, ~25 GB on disk)
|
||||
~/.config/vllm/vllmctl.py gpu-check # torch sees the card? prints device name + gfx arch
|
||||
```
|
||||
|
||||
`gpu-check` must print `cuda available: True`, a `gfx1100` device (that is the override at work) and
|
||||
`torch kernel: ok`. **The override is required** (found 2026-10-21): `v0.32.0` carries no gfx1102 code
|
||||
objects, and a kernel launch without one aborts the process *silently* — the engine core vanishes, the
|
||||
API server only says `Failed core proc(s): {}`, and even the `print`s before the abort are lost to
|
||||
stdout buffering. `[global] env.HSA_OVERRIDE_GFX_VERSION = 11.0.0` presents the card as gfx1100 (RX 7900),
|
||||
whose RDNA3 code runs on this chip. Nothing else on the host changes.
|
||||
|
||||
Then the smoke test (tiny model, 30 % of the card, proves the whole path before any big download —
|
||||
✓ passed 2026-10-21: 154.9 t/s decode, 1462 t/s prefill, TTFT 0.02 s, 7.7 GB used incl. a 2.9 GB desktop):
|
||||
|
||||
```fish
|
||||
~/.config/vllm/vllmctl.py up Qwen3-0.6B-smoke --stop-router
|
||||
curl -s localhost:11348/v1/chat/completions -H 'content-type: application/json' \
|
||||
-d '{"model":"Qwen3-0.6B-smoke","messages":[{"role":"user","content":"hi"}],"max_tokens":20}'
|
||||
~/.config/vllm/vllmctl.py down; sudo systemctl start llama.service
|
||||
```
|
||||
|
||||
First start of any model compiles Triton kernels: minutes, with `vllmctl.py up` echoing the log tail
|
||||
every 30 s.
|
||||
|
||||
**Tags and updates.** `<tag>` is the part after the colon: `latest` = newest stable release (was
|
||||
`v0.32.0` on 2026-10-18, ROCm 10.0), `nightly` = today's main branch (18 GB), `latest-rocm72` /
|
||||
`nightly-rocm72` = the same on the older ROCm 7.2 stack (smaller, 12 GB), `…-base` = without vLLM
|
||||
itself. The ini pins `v0.32.0` so a re-pull cannot silently change the engine under a bench run. To
|
||||
update: pick the new `vX.Y.Z` on hub.docker.com/r/vllm/vllm-openai-rocm/tags, put it in `[global] image`,
|
||||
`vllmctl.py pull`, then `vllmctl.py down` / `up` — the container is recreated from the image on every
|
||||
`up`. Afterwards `docker image prune -f` drops the superseded layers (`docker images` shows what is kept).
|
||||
`HF_TOKEN` is only needed for gated repos; none of the three presets is gated. If one ever is,
|
||||
`hf auth login` on fl writes `~/.cache/huggingface/token`, which the mounted cache hands to the container.
|
||||
|
||||
## Running the benchmark
|
||||
|
||||
Two phases, because the engines cannot be resident together. Do both on a **clean desktop** (close the
|
||||
browser; `vramx` must show ≤ ~1.5 GB used) — the vLLM presets leave ~2 GB free at 0.78 and an evening
|
||||
desktop does not fit.
|
||||
|
||||
```fish
|
||||
# phase 1 — vLLM (router stopped by --stop-router; first run also downloads the model)
|
||||
~/.config/vllm/vllmctl.py up gemma-4-E4B-it-w4a16 --stop-router
|
||||
~/.config/vllm/bench-vs.py -t vllm:gemma-4-E4B-it-w4a16 --ctx 8000 --concurrency 4
|
||||
~/.config/vllm/vllmctl.py down
|
||||
~/.config/vllm/vllmctl.py up Qwen3.5-9B-w4a16 # router is still stopped
|
||||
~/.config/vllm/bench-vs.py -t vllm:Qwen3.5-9B-w4a16 --ctx 8000 --concurrency 4
|
||||
~/.config/vllm/vllmctl.py down
|
||||
|
||||
# phase 2 — llama.cpp (vLLM down, router back)
|
||||
sudo systemctl start llama.service
|
||||
~/.config/vllm/bench-vs.py -t llamacpp:gemma-4-E4B-it-UD-Q8_K_XL -t llamacpp:Qwen3.5-9B-UD-Q6_K_XL --ctx 8000 --concurrency 4
|
||||
```
|
||||
|
||||
Every row lands in `bench-vs-history.md` (append) and the latest run in `bench-vs-results.md`, same
|
||||
folder. The guards: a `llamacpp` target is skipped while vLLM answers `/health`; a `vllm` target is
|
||||
skipped while the router has a model loaded or `llama.service` is active (`--force` overrides both).
|
||||
|
||||
**How to read it**
|
||||
|
||||
- `decode t/s` is usage tokens over wall time after the first token, measured the same way on both
|
||||
engines; llama.cpp's own `timings` number is in parentheses so rows match `../llamacpp/bench-results.md`.
|
||||
- Thinking is **off** on both (`chat_template_kwargs.enable_thinking=false`) and a reasoning delta counts
|
||||
as first token, so TTFT compares the engines, not the preamble lengths. `--think` reverts that.
|
||||
- `spec` shows the MTP draft on the llama.cpp side; vLLM rows have none unless the preset sets
|
||||
`speculative-config`. Expect the gemma/Qwen llama.cpp rows to carry a 1.5–2× draft bonus.
|
||||
- `agg t/s @N` is continuous batching's home turf — and llama-server still won it (85 vs 61 at 4
|
||||
streams): its default slot count on b11146 is evidently > 1, so no `parallel` key is needed.
|
||||
- `--ctx 8000` adds decode at an 8k prompt — the number that matters for agent loops.
|
||||
|
||||
## Results
|
||||
|
||||
**2026-10-21, Qwen3.5-9B, same evening, same script, thinking off, 256 tok, 0.6 GB desktop:**
|
||||
|
||||
| engine · checkpoint | spec | decode t/s | prefill t/s | TTFT s | decode @8k prompt | 4 streams agg t/s | VRAM used |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| vLLM · RedHatAI w4a16 @0.80, 16k, batch 512 | — | 29.4 | **181** | **0.17** | 16.0 | 60.6 | 12.5 GB |
|
||||
| llama.cpp · UD-Q6_K_XL, 32k | MTP draft | **61.3** | 114 | 0.27 | **51.1** | **85.4** | 10.9 GB |
|
||||
|
||||
What it says, for this card:
|
||||
|
||||
- **Decode**: llama.cpp wins 2× single-stream and 3× at an 8k prompt. Most of the 2× is the MTP draft
|
||||
(the ledger's pre-MTP Q8 number was 17 t/s in July; a no-draft Q6 today is probably ~30, i.e. parity
|
||||
with vLLM). The 8k gap is not MTP: vLLM's decode halves with context here (16.0 vs 29.4) while
|
||||
llama.cpp loses 17 % — on RDNA3 vLLM's attention path is the Triton generic one, llama.cpp's is a
|
||||
tuned Vulkan kernel.
|
||||
- **Prefill**: vLLM +59 % even with its batch capped at 512 for memory. This is its real strength on
|
||||
the box, and it shows in TTFT.
|
||||
- **Concurrency**: the column that was supposed to be vLLM's — it batched 4 streams to 2× its single
|
||||
(60.6), but llama-server served the same 4 at 85.4. Its default slot count is evidently > 1 on this
|
||||
build, so the README's "llama.cpp serializes" caveat does not hold; it does not need a `parallel` key.
|
||||
- **Memory**: vLLM holds 1.6 GB more for a model with 2 GB less context, and only runs on a ≤ 0.6 GB
|
||||
desktop. 0.75 was 0.11 GiB short of KV room; 0.80 is the setting.
|
||||
|
||||
**Verdict**: no vLLM model earns a roster slot on fl; the bench rig stays, the router stays, llama-swap
|
||||
is not needed. The one follow-up that could move the decode column is MTP in vLLM (the knob is in
|
||||
the ini) — but it cannot touch the memory column or the MoE roster, so it is curiosity, not a decision.
|
||||
|
||||
## When it does not start
|
||||
|
||||
- **`No available memory for the cache blocks`** (seen 2026-10-21, gemma @0.73/8k on a 2.1 GB desktop):
|
||||
the model loaded, but budget − weights − activation reservations left nothing for KV. What vLLM
|
||||
reserved is in the log: `docker logs vllm 2>&1 | grep -iE 'profil|kv cache|weights take|peak'`.
|
||||
Cheapest fixes first: text-only (`limit-mm-per-prompt` all zero — skips the image/audio encoder
|
||||
warmup, now in both presets), smaller `max-num-batched-tokens` (2048, now in both presets), lower
|
||||
`max-num-seqs`; then a cleaner desktop so the projection allows 0.78–0.82; last resort the smaller
|
||||
GPTQ checkpoint. `--set key=val` tries any of these without editing the ini.
|
||||
- **`ABORT: projected free …`**: the desktop is using more than the preset assumes. Close GPU apps or
|
||||
take the suggested utilization it prints. `--force` exists; the freeze is real.
|
||||
- **compressed-tensors / "no kernel" for int4 on ROCm**: both real presets use compressed-tensors
|
||||
pack-quantized. If this image lacks a ROCm kernel for it, swap in the GPTQ alternative noted in the
|
||||
ini (`Vishva007/gemma-4-E4B-it-W4A16-AutoRound-GPTQ`, 10.0 GB) — different kernel path.
|
||||
- **Hangs / garbage output**: RDNA3 attention backend. Set `env.VLLM_ATTENTION_BACKEND = TRITON_ATTN`
|
||||
explicitly; option names moved between versions — `vllmctl.py exec vllm serve --help` shows this image's.
|
||||
- **GTT growing during a run** (`vramx`, the GTT line): the budget is leaking into system RAM — stop,
|
||||
lower the utilization. Do not wait for the desktop to stutter.
|
||||
- **Root-owned files in `~/.cache/huggingface`**: the container runs as root, so its downloads land
|
||||
root-owned in anon's HF cache. Harmless for the rig; `sudo chown -R anon: ~/.cache/huggingface` if
|
||||
the host-side `hf` tooling complains.
|
||||
- **Container died**: `docker logs vllm` (the container is kept after a crash; `vllmctl.py up` shows
|
||||
the tail and removes it on the next start).
|
||||
|
||||
## Phase 2 — if the numbers justify more
|
||||
|
||||
1. **Same-file GGUF in vLLM**: GGUF loading moved to an out-of-tree `vllm-gguf-plugin`; it is
|
||||
"highly experimental and under-optimized" per vLLM's docs and needs a custom image layer
|
||||
(`pip install vllm-gguf-plugin`), plus `--tokenizer <base repo>`. Untested on ROCm. It would make the
|
||||
weights identical — and show vLLM's unoptimized path, so interpret carefully.
|
||||
2. **MTP in vLLM**: `RedHatAI/Qwen3.5-9B-quantized.w4a16` ships `model_mtp.safetensors`; the
|
||||
commented `speculative-config` in the ini is the knob (syntax per this image's `--help`).
|
||||
3. **Permanent coexistence**: replace the router's `--models-preset` mode with llama-swap (AUR
|
||||
`llama-swap-bin`), which spawns llama-server *or* the vLLM container per model id, one at a time,
|
||||
with a `ttl` idle unload. Same host/port/model ids for every client. Only worth it if a vLLM model
|
||||
earns a place in the daily roster.
|
||||
Executable
+258
@@ -0,0 +1,258 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
bench-vs — the SAME prompt through llama.cpp (router :11343) and vLLM (:11348), one table.
|
||||
|
||||
Measures per target: decode t/s, prefill t/s, TTFT, VRAM used/free (amdgpu sysfs, no root needed),
|
||||
optionally decode at a long prompt (--ctx) and aggregate throughput under N parallel requests
|
||||
(--concurrency). Method = ../llamacpp/bench.py (streamed chat completion, usage.completion_tokens
|
||||
over wall time after the first token), applied identically to both engines; llama.cpp's own
|
||||
`timings` figure is shown in parentheses so rows stay comparable with bench-results.md.
|
||||
|
||||
Thinking is OFF by default on both engines (chat_template_kwargs.enable_thinking=false) and the
|
||||
first reasoning_content delta counts as the first token anyway — a reasoning preamble of different
|
||||
length per engine would otherwise poison TTFT. --think re-enables it.
|
||||
|
||||
GUARDS (the card drives the display; two resident engines = GTT spill = frozen PC):
|
||||
- a llamacpp target is refused while vLLM answers /health (stop it: ./vllmctl.py down)
|
||||
- a vllm target is refused while the router reports a loaded model or llama.service is active
|
||||
|
||||
./bench-vs.py -t vllm:gemma-4-E4B-it-w4a16 # vLLM up (vllmctl.py up …), router stopped
|
||||
./bench-vs.py -t llamacpp:gemma-4-E4B-it-UD-Q8_K_XL # vLLM down, router started
|
||||
./bench-vs.py -t llamacpp:Qwen3.5-9B-UD-Q6_K_XL --ctx 8000 --concurrency 4
|
||||
Output: bench-vs-results.md (latest) + bench-vs-history.md (append), next to this script. Stdlib only.
|
||||
"""
|
||||
import argparse, json, os, re, subprocess, sys, threading, time, urllib.request, urllib.error
|
||||
from datetime import datetime
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
DRM = "/sys/class/drm/renderD128/device"
|
||||
URLS = {"llamacpp": "http://127.0.0.1:11343/v1", "vllm": "http://127.0.0.1:11348/v1"}
|
||||
TASK = "Write a Python function that merges two sorted lists, with a short docstring and one example."
|
||||
|
||||
|
||||
def sysfs(name):
|
||||
try:
|
||||
return int(open(os.path.join(DRM, name)).read().strip())
|
||||
except (OSError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def gb(b):
|
||||
return f"{b / 1e9:.1f} GB" if b is not None else "-"
|
||||
|
||||
|
||||
def http(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 parse_ini(path):
|
||||
sections, sec = {}, None
|
||||
try:
|
||||
lines = open(path, encoding="utf-8").read().splitlines()
|
||||
except OSError:
|
||||
return {}
|
||||
for ln in lines:
|
||||
s = ln.strip()
|
||||
if not s or s.startswith("#"):
|
||||
continue
|
||||
if s.startswith("[") and s.endswith("]"):
|
||||
sec = s[1:-1]; sections[sec] = {}
|
||||
elif sec and "=" in s:
|
||||
k, v = s.split("=", 1)
|
||||
sections[sec][k.strip()] = v.split(" #", 1)[0].split("\t#", 1)[0].strip()
|
||||
return sections
|
||||
|
||||
|
||||
def run(base, key, model, prompt, n, temp, timeout, think):
|
||||
payload = {"model": model, "messages": [{"role": "user", "content": prompt}],
|
||||
"max_tokens": n, "temperature": temp, "stream": True,
|
||||
"stream_options": {"include_usage": True},
|
||||
"timings_per_token": True} # llama.cpp extension, ignored by vLLM
|
||||
if not think:
|
||||
payload["chat_template_kwargs"] = {"enable_thinking": False} # both engines accept it; templates without it ignore it
|
||||
t0 = time.perf_counter(); ttft = None; ntok = 0; usage = None; timings = None
|
||||
for raw in http(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 [{}]
|
||||
d = ch[0].get("delta", {}) if ch else {}
|
||||
if d.get("content") or d.get("reasoning_content") or d.get("reasoning"):
|
||||
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
|
||||
srv = timings.get("predicted_per_second") if timings else None # llama.cpp's own number, for the ledger
|
||||
return {"tg": tg, "pp": pp, "ttft": ttft or 0.0, "ptok": ptok or 0, "ctok": comp, "wall": total, "srv": srv,
|
||||
"t0": t0, "t1": t0 + total}
|
||||
|
||||
|
||||
def with_retries(fn, retries, wait, label=""):
|
||||
last = None
|
||||
for i in range(retries + 1):
|
||||
try:
|
||||
return fn()
|
||||
except urllib.error.HTTPError as e:
|
||||
body = e.read().decode("utf-8", "ignore")[:300]
|
||||
if e.code < 500 or "failed to load" in body.lower():
|
||||
raise
|
||||
last = e
|
||||
except (urllib.error.URLError, ConnectionError, TimeoutError) as e:
|
||||
last = e
|
||||
if i < retries:
|
||||
print(f" … {label}: not ready ({last}); retry in {wait}s", file=sys.stderr)
|
||||
time.sleep(wait)
|
||||
raise last
|
||||
|
||||
|
||||
def vllm_up(url):
|
||||
try:
|
||||
return http(re.sub(r"/v1/?$", "", url) + "/health", "x", timeout=3).status == 200
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _status(m):
|
||||
"""Router /models status: newer builds send {'value': 'loaded'|'sleeping'|'unloaded'|…}, older a string."""
|
||||
st = m.get("status", "loaded")
|
||||
return st.get("value", "loaded") if isinstance(st, dict) else st
|
||||
|
||||
|
||||
def router_loaded(url):
|
||||
try:
|
||||
d = json.load(http(re.sub(r"/v1/?$", "", url) + "/models", "x", timeout=3))
|
||||
return [m["id"] for m in d.get("data", []) if _status(m) in ("loaded", "loading")]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def router_active():
|
||||
try:
|
||||
r = subprocess.run(["systemctl", "is-active", "llama.service"], capture_output=True, text=True, timeout=10)
|
||||
except (OSError, subprocess.TimeoutExpired):
|
||||
return False
|
||||
return r.stdout.strip() == "active"
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("-t", "--target", action="append", required=True,
|
||||
help="engine:model, engine = llamacpp | vllm (repeatable)")
|
||||
ap.add_argument("--url", action="append", default=[], help="engine=URL override, e.g. vllm=http://host:11348/v1")
|
||||
ap.add_argument("-k", "--key", default="no-key-required")
|
||||
ap.add_argument("-n", "--tokens", type=int, default=256)
|
||||
ap.add_argument("--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("--concurrency", type=int, default=0, help="also record aggregate t/s with N parallel requests")
|
||||
ap.add_argument("--think", action="store_true", help="leave the model's thinking ON (default: off on both engines)")
|
||||
ap.add_argument("--timeout", type=int, default=600)
|
||||
ap.add_argument("--retries", type=int, default=4); ap.add_argument("--retry-wait", type=int, default=15)
|
||||
ap.add_argument("--force", action="store_true", help="skip the two-engines-resident guards")
|
||||
ap.add_argument("--out", default=os.path.join(HERE, "bench-vs-results.md"))
|
||||
ap.add_argument("--history", default=os.path.join(HERE, "bench-vs-history.md"))
|
||||
a = ap.parse_args()
|
||||
|
||||
urls = dict(URLS)
|
||||
for u in a.url:
|
||||
k, v = u.split("=", 1); urls[k] = v
|
||||
llcfg = parse_ini(os.path.join(HERE, "..", "llamacpp", "config.ini"))
|
||||
vlcfg = parse_ini(os.path.join(HERE, "vllm.ini"))
|
||||
total = sysfs("mem_info_vram_total")
|
||||
if total is None:
|
||||
print(" (amdgpu sysfs not readable here — VRAM columns are OFF; run this ON fl for them)", file=sys.stderr)
|
||||
|
||||
filler = ("The quick brown fox jumps over the lazy dog. " * max(1, a.ctx // 9)) if a.ctx else None
|
||||
cols = ["engine", "model", "cfg ctx", "spec", "decode t/s", "prefill t/s", "TTFT s", "VRAM used", "VRAM free"]
|
||||
if a.ctx:
|
||||
cols.append(f"decode@{a.ctx // 1000}k")
|
||||
if a.concurrency:
|
||||
cols.append(f"agg t/s @{a.concurrency}")
|
||||
|
||||
latest = open(a.out, "w", encoding="utf-8"); hist = open(a.history, "a", encoding="utf-8")
|
||||
|
||||
def emit(line):
|
||||
print(line); latest.write(line + "\n"); latest.flush(); hist.write(line + "\n"); hist.flush()
|
||||
|
||||
latest.write("# llama.cpp vs vLLM (latest run)\n\n"); hist.write(f"\n## run {datetime.now():%Y-%m-%d %H:%M}\n\n")
|
||||
emit(f"_Run {datetime.now():%Y-%m-%d %H:%M} · gen {a.tokens} tok · temp {a.temp} · thinking "
|
||||
f"{'ON' if a.think else 'OFF'} · VRAM total {gb(total)} · baseline used {gb(sysfs('mem_info_vram_used'))} · "
|
||||
f"decode = usage tokens / wall after first token on BOTH engines (llama.cpp's own timings in parentheses)_\n")
|
||||
emit("| " + " | ".join(cols) + " |"); emit("|" + "|".join(["---"] * len(cols)) + "|")
|
||||
|
||||
for t in a.target:
|
||||
eng, _, model = t.partition(":")
|
||||
if eng not in urls or not model:
|
||||
emit(f"| {eng} | {model} | bad target — use engine:model with engine in {list(urls)} |"); continue
|
||||
base = urls[eng]
|
||||
# guards
|
||||
if not a.force:
|
||||
if eng == "llamacpp" and vllm_up(urls["vllm"]):
|
||||
emit(f"| llamacpp | {model} | ⚠ SKIPPED: vLLM is up on {urls['vllm']} — loading a router model on top = freeze. "
|
||||
f"`vllmctl.py down` first |"); continue
|
||||
if eng == "vllm":
|
||||
loaded = router_loaded(urls["llamacpp"])
|
||||
if loaded or router_active():
|
||||
emit(f"| vllm | {model} | ⚠ SKIPPED: router loaded={loaded or '-'} active={router_active()} — "
|
||||
f"stop llama.service before benching vLLM |"); continue
|
||||
if eng == "llamacpp":
|
||||
c = llcfg.get(model, {})
|
||||
cfgctx, spec = c.get("ctx-size", "-"), (c.get("spec-type") or "-")
|
||||
else:
|
||||
c = vlcfg.get(model, {})
|
||||
cfgctx = c.get("max-model-len", "-")
|
||||
spec = "mtp" if c.get("speculative-config") else "-"
|
||||
cells = [eng, model, cfgctx, spec]
|
||||
try:
|
||||
with_retries(lambda: run(base, a.key, model, "hi", 8, a.temp, a.timeout, a.think), a.retries, a.retry_wait, model)
|
||||
used = sysfs("mem_info_vram_used")
|
||||
r = with_retries(lambda: run(base, a.key, model, TASK, a.tokens, a.temp, a.timeout, a.think), a.retries, a.retry_wait, model)
|
||||
dec = f"{r['tg']:.1f}" + (f" ({r['srv']:.1f})" if r["srv"] else "")
|
||||
cells += [dec, f"{r['pp']:.0f}" if r["pp"] else "-", f"{r['ttft']:.2f}", gb(used),
|
||||
gb(total - used) if (total and used is not None) else "-"]
|
||||
if a.ctx:
|
||||
rc = run(base, a.key, model, filler + "\n\n" + TASK, a.tokens, a.temp, a.timeout, a.think)
|
||||
cells.append(f"{rc['tg']:.1f}")
|
||||
if a.concurrency:
|
||||
res = [None] * a.concurrency
|
||||
def worker(i):
|
||||
res[i] = run(base, a.key, model, TASK + f" (variant {i})", a.tokens, a.temp, a.timeout, a.think)
|
||||
th = [threading.Thread(target=worker, args=(i,)) for i in range(a.concurrency)]
|
||||
for x in th: x.start()
|
||||
for x in th: x.join()
|
||||
ok = [x for x in res if x]
|
||||
wall = max(x["t1"] for x in ok) - min(x["t0"] for x in ok)
|
||||
cells.append(f"{sum(x['ctok'] for x in ok) / wall:.1f}" if wall > 0 else "-")
|
||||
except Exception as e:
|
||||
err = f"HTTP {e.code} {e.read().decode('utf-8', 'ignore')[:120]!r}" if isinstance(e, urllib.error.HTTPError) else f"ERROR {e}"
|
||||
cells = cells[:4] + [err] + [""] * (len(cols) - 5) # same width whether it died early or in the --ctx/--concurrency pass
|
||||
emit("| " + " | ".join(str(x) for x in cells) + " |")
|
||||
|
||||
latest.write("\n_llama.cpp rows run with the preset's MTP draft when `spec` says so; vLLM rows have no speculation "
|
||||
"unless `spec` = mtp. `agg t/s @N` = N parallel requests, completion tokens over the wall time of "
|
||||
"the whole batch (llama-server's default slot count serves them in parallel too)._\n")
|
||||
latest.close(); hist.close()
|
||||
print(f"\nwrote {a.out} (+ appended {a.history})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,92 @@
|
||||
# vLLM presets for fl — AMD RX 7600 XT · 16 GB · docker image vllm/vllm-openai-rocm
|
||||
# Same convention as ../llamacpp/config.ini: section name = served model id, `key = value` = `--key value`.
|
||||
#
|
||||
# ⚠ ONE ENGINE AT A TIME. vLLM pre-allocates `gpu-memory-utilization` × 16 GiB at startup and KEEPS it.
|
||||
# Any llama.cpp load on top (a client hitting :11343 is enough) overcommits → GTT spill → PC freeze.
|
||||
# `vllmctl.py up` refuses to start while llama.service is active — stop the router first
|
||||
# (`vllmctl.py up <preset> --stop-router`), start it again after `vllmctl.py down`.
|
||||
#
|
||||
# Sections:
|
||||
# [global] container/runtime knobs (NOT vllm flags) — see vllmctl.py
|
||||
# [<preset>] one `vllmctl.py up <preset>`:
|
||||
# model HF repo id, or /models/<dir> (models-dir is mounted at /models, read-only)
|
||||
# any other key → `--key value` for `vllm serve`; `true` → bare `--key`; `false` → omitted
|
||||
# served-model-name defaults to the section name (what clients and bench-vs.py use)
|
||||
# x-* vllmctl-only metadata (x-weights-gb feeds the VRAM projection)
|
||||
# env.NAME = value container environment (per preset; [global] env.* apply to all)
|
||||
#
|
||||
# VRAM MATH (vllmctl prints it before every start, aborts under reserve-gb unless --force):
|
||||
# budget = gpu-memory-utilization × 17.16 GB (vLLM's total = the card's total, same as sysfs)
|
||||
# budget must hold weights + activations + KV cache; everything above it + the desktop must stay
|
||||
# under 16 GB with ≥ reserve-gb free. 0.75 → 12.9 GB budget → ~2.9 GB free on a 1.3 GB clean desktop.
|
||||
# A busy evening desktop (3+ GB) does NOT fit any of the real models below — clean desktop only.
|
||||
# Sizes measured 2026-10-21 from the HF API: the 4-bit vLLM checkpoints are 10-11.5 GB because
|
||||
# embeddings, lm_head and the vision tower stay bf16 — bigger than the Q6/Q8 GGUFs they pair with.
|
||||
|
||||
[global]
|
||||
image = vllm/vllm-openai-rocm:v0.32.0 # = `latest` on 2026-10-18 (ROCm 10.0, 11.6 GB download); update = bump this + `vllmctl.py pull`
|
||||
container = vllm
|
||||
port = 11348 # whisper = 11345/11346, kokoro = 11347
|
||||
models-dir = /home/anon/software/models # mounted at /models (ro) — share the GGUF dir
|
||||
hf-cache = /home/anon/.cache/huggingface # mounted at /root/.cache/huggingface (downloads + token)
|
||||
router = http://127.0.0.1:11343 # llama.cpp router root (for the resident-model gate)
|
||||
reserve-gb = 2.0 # abort if projected free VRAM < this (bench.py floor 1.5, target 2.5)
|
||||
drain-gb = 4.0 # "card is clean" threshold before starting (same as bench.py)
|
||||
health-timeout = 1800 # s — first start compiles Triton kernels, be patient
|
||||
gpu-memory-utilization = 0.75 # default; presets override
|
||||
# REQUIRED on this card (✓ verified 2026-10-21): v0.32.0 ships no gfx1102 code objects — without the
|
||||
# override every GPU kernel launch aborts the process SILENTLY (the engine core just vanishes, the
|
||||
# API server reports "Failed core proc(s): {}"). gfx1100 (RX 7900) code runs on gfx1102, same RDNA3 ISA.
|
||||
env.HSA_OVERRIDE_GFX_VERSION = 11.0.0
|
||||
# Attention backend knobs (ROCm; names moved between versions — `vllmctl.py exec vllm serve --help`):
|
||||
# env.VLLM_ATTENTION_BACKEND = TRITON_ATTN # the default on RDNA; set explicitly if a hang points at attention
|
||||
|
||||
# ─── 1. smoke test — tiny, certain to be supported; proves docker + GPU + image + override ────
|
||||
# ✓ 2026-10-21: 154.9 t/s decode · 1462 t/s prefill · TTFT 0.02 s · 7.7 GB used incl. a 2.9 GB desktop.
|
||||
[Qwen3-0.6B-smoke]
|
||||
model = Qwen/Qwen3-0.6B
|
||||
gpu-memory-utilization = 0.30
|
||||
max-model-len = 4096
|
||||
x-weights-gb = 1.5
|
||||
|
||||
# ─── 2. gemma-4-E4B — ✗ DOES NOT FIT vLLM ON THIS CARD (2026-10-21), tombstone kept for the math ──
|
||||
# Pairs with llama.cpp [gemma-4-E4B-it-UD-Q8_K_XL] (57 t/s with MTP, 96k ctx). google/gemma-4-E4B-it-qat-w4a16-ct
|
||||
# is 11.5 GB, and the second try (text-only, batch 2048, @0.73 on a 2.1 GB desktop) ended with
|
||||
# "Available KV cache memory: -4.44 GiB" after an OOM on a 5,637,144,576-byte allocation. That is exactly
|
||||
# Gemma 4's per-layer embedding table: 262144 vocab × 42 layers × 256 × 2 bytes (bf16). vLLM holds it
|
||||
# in bf16 AND needs a second buffer of the same size → ~16 GiB before any KV block. No util, batch or
|
||||
# desktop fixes that; the GPTQ/AWQ alternatives carry the same bf16 table. llama.cpp fits because the
|
||||
# GGUF stores the table quantized, once. A different image changes nothing here.
|
||||
# [gemma-4-E4B-it-w4a16]
|
||||
# model = google/gemma-4-E4B-it-qat-w4a16-ct
|
||||
# gpu-memory-utilization = 0.78
|
||||
# max-model-len = 16384
|
||||
# max-num-seqs = 4
|
||||
# max-num-batched-tokens = 2048
|
||||
# limit-mm-per-prompt = {"image": 0, "audio": 0, "video": 0}
|
||||
# x-weights-gb = 11.5
|
||||
|
||||
# ─── 3. Qwen3.5-9B — pairs with llama.cpp [Qwen3.5-9B-UD-Q6_K_XL] (32 t/s with MTP, 32k ctx) ──
|
||||
# Red Hat's llm-compressor int4 (compressed-tensors). 10.95 GB + a 0.49 GB MTP head file.
|
||||
# Alternative (real AWQ, Triton kernels on ROCm): QuantTrio/Qwen3.5-9B-AWQ — 12.4 GB, too big for 0.78.
|
||||
# The last viable "same model" pair: no per-layer table, but a 248k vocab → the fp32 logits buffer
|
||||
# during profiling is max-num-batched-tokens × 248k × 4 B (2048 → 2.0 GB, 512 → 0.5 GB). Start small.
|
||||
# On a 2.1 GB desktop the projection allows 0.75 (`--set gpu-memory-utilization=0.75`) → ~1.9 GB for
|
||||
# activations + graphs + KV. If "No available memory for the cache blocks" again: `--set enforce-eager=true`
|
||||
# (drops the CUDA-graph memory, costs decode speed), then a cleaner desktop for 0.78.
|
||||
# ✓ RUNS 2026-10-21 @0.80 (0.75 was 0.11 GiB short of KV room): 29.4 t/s decode · 181 prefill · TTFT 0.17 ·
|
||||
# decode@8k 16.0 · 4-stream aggregate 60.6 · 12.5 GB used on a 0.6 GB desktop. Same evening, llama.cpp
|
||||
# [Qwen3.5-9B-UD-Q6_K_XL] with its MTP draft: 61.3 · 114 · 0.27 · 51.1 · 85.4 · 10.9 GB. See README.
|
||||
[Qwen3.5-9B-w4a16]
|
||||
model = RedHatAI/Qwen3.5-9B-quantized.w4a16
|
||||
gpu-memory-utilization = 0.80 # needs a ≤ 0.6 GB desktop (projection: 2.9 GB free); 0.75 fails on KV room
|
||||
max-model-len = 16384
|
||||
max-num-seqs = 4
|
||||
max-num-batched-tokens = 512 # caps the profiling logits buffer (prefill t/s cost — note it in the ledger)
|
||||
limit-mm-per-prompt = {"image": 0, "video": 0} # text-only bench (Qwen3.5 has no audio input)
|
||||
x-weights-gb = 11.0
|
||||
# NEXT (the one knob that could change the verdict): MTP speculative decoding — the repo ships
|
||||
# model_mtp.safetensors and llama.cpp's 2× comes from exactly this. Costs ~0.5 GB weights + draft
|
||||
# state → try @0.82 on a clean desktop. Syntax per `vllmctl.py exec vllm serve --help`:
|
||||
# ./vllmctl.py up Qwen3.5-9B-w4a16 --set gpu-memory-utilization=0.82 --set 'speculative-config={"method": "mtp", "num_speculative_tokens": 2}'
|
||||
# speculative-config = {"method": "mtp", "num_speculative_tokens": 2}
|
||||
Executable
+398
@@ -0,0 +1,398 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
vllmctl — run vLLM (docker, ROCm) on fl WITHOUT freezing the box.
|
||||
|
||||
The RX 7600 XT drives the display; VRAM overcommit spills to GTT and freezes the PC. vLLM grabs
|
||||
`gpu-memory-utilization` × total VRAM at startup and keeps it, so this script gates every start:
|
||||
|
||||
1. llama.service must be STOPPED (or --stop-router): a LAN client hitting :11343 would make the
|
||||
router load a model on top of vLLM = the freeze. --unsafe-keep-router skips this gate.
|
||||
2. the card must be clean (VRAM used ≤ drain-gb; resident router models are unloaded via its API).
|
||||
3. projected free VRAM = total − used − budget must be ≥ reserve-gb (else abort, --force overrides).
|
||||
|
||||
Presets live in vllm.ini next to this file (same key = --flag convention as llamacpp/config.ini).
|
||||
|
||||
./vllmctl.py up <preset> [--stop-router] [--force] [--dry-run] start, wait for /health
|
||||
./vllmctl.py down stop, wait for VRAM to drain
|
||||
./vllmctl.py status | logs | pull | gpu-check | exec <cmd...>
|
||||
Stdlib only. Run ON fl.
|
||||
"""
|
||||
import argparse, grp, json, os, re, shlex, subprocess, sys, time, urllib.request, urllib.error
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
INI = os.path.join(HERE, "vllm.ini")
|
||||
DRM = "/sys/class/drm/renderD128/device"
|
||||
|
||||
|
||||
# ─── ini ────────────────────────────────────────────────────────────────────────────────────
|
||||
def parse_ini(path):
|
||||
"""{section: {key: value}} — inline `# comments` stripped, order kept."""
|
||||
sections, sec = {}, None
|
||||
for ln in open(path, encoding="utf-8").read().splitlines():
|
||||
s = ln.strip()
|
||||
if not s or s.startswith("#"):
|
||||
continue
|
||||
if s.startswith("[") and s.endswith("]"):
|
||||
sec = s[1:-1]; sections[sec] = {}
|
||||
elif sec and "=" in s:
|
||||
k, v = s.split("=", 1)
|
||||
sections[sec][k.strip()] = v.split(" #", 1)[0].split("\t#", 1)[0].strip()
|
||||
return sections
|
||||
|
||||
|
||||
def build_args(preset):
|
||||
"""vllm serve args from a preset section (x-* and env.* are not flags)."""
|
||||
args = []
|
||||
for k, v in preset.items():
|
||||
if k.startswith("x-") or k.startswith("env."):
|
||||
continue
|
||||
if v.lower() == "false":
|
||||
continue
|
||||
if v.lower() == "true":
|
||||
args.append(f"--{k}")
|
||||
else:
|
||||
args += [f"--{k}", v]
|
||||
return args
|
||||
|
||||
|
||||
# ─── gpu / router ───────────────────────────────────────────────────────────────────────────
|
||||
def sysfs(name):
|
||||
try:
|
||||
return int(open(os.path.join(DRM, name)).read().strip())
|
||||
except (OSError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def vram():
|
||||
"""(used, total, gtt_used) bytes from amdgpu sysfs; None where unreadable."""
|
||||
return sysfs("mem_info_vram_used"), sysfs("mem_info_vram_total"), sysfs("mem_info_gtt_used")
|
||||
|
||||
|
||||
def gb(b):
|
||||
return f"{b / 1e9:.1f} GB" if b is not None else "-"
|
||||
|
||||
|
||||
def http(url, payload=None, timeout=10):
|
||||
data = json.dumps(payload).encode() if payload is not None else None
|
||||
hdrs = {"Content-Type": "application/json"} if data else {}
|
||||
return urllib.request.urlopen(urllib.request.Request(url, data=data, headers=hdrs), timeout=timeout)
|
||||
|
||||
|
||||
def _status(m):
|
||||
"""Router /models status: newer builds send {'value': 'loaded'|'sleeping'|'unloaded'|…}, older a string."""
|
||||
st = m.get("status", "loaded")
|
||||
return st.get("value", "loaded") if isinstance(st, dict) else st
|
||||
|
||||
|
||||
def router_loaded(root):
|
||||
"""ids the llama.cpp router reports as loaded ([] if unreachable)."""
|
||||
try:
|
||||
d = json.load(http(root.rstrip("/") + "/models", timeout=5))
|
||||
except Exception:
|
||||
return []
|
||||
return [m["id"] for m in d.get("data", []) if _status(m) in ("loaded", "loading")]
|
||||
|
||||
|
||||
def router_unload(root):
|
||||
for m in router_loaded(root):
|
||||
try:
|
||||
http(root.rstrip("/") + "/models/unload", {"model": m}, timeout=30).read()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def router_active():
|
||||
try:
|
||||
r = subprocess.run(["systemctl", "is-active", "llama.service"], capture_output=True, text=True, timeout=10)
|
||||
except (OSError, subprocess.TimeoutExpired):
|
||||
return False
|
||||
return r.stdout.strip() == "active"
|
||||
|
||||
|
||||
def router_hint():
|
||||
return "" if router_active() else "\n (llama.service is STOPPED → sudo systemctl start llama.service to bring the router back)"
|
||||
|
||||
|
||||
def wait_drain(drain_gb, window_s, label):
|
||||
used, _, _ = vram()
|
||||
end = time.time() + window_s
|
||||
said = False
|
||||
while used is not None and used > drain_gb * 1e9 and time.time() < end:
|
||||
if not said:
|
||||
print(f" … waiting for VRAM to drain {label} ({gb(used)} used)"); said = True
|
||||
time.sleep(5)
|
||||
used, _, _ = vram()
|
||||
return used
|
||||
|
||||
|
||||
# ─── docker ─────────────────────────────────────────────────────────────────────────────────
|
||||
def docker(*args, check=True, capture=True):
|
||||
return subprocess.run(["docker", *args], check=check, capture_output=capture, text=True)
|
||||
|
||||
|
||||
def container_state(name):
|
||||
r = docker("inspect", "-f", "{{.State.Status}}", name, check=False)
|
||||
return r.stdout.strip() if r.returncode == 0 else None
|
||||
|
||||
|
||||
def gpu_flags():
|
||||
flags = ["--device", "/dev/kfd", "--device", "/dev/dri", "--group-add", "video",
|
||||
"--cap-add", "SYS_PTRACE", "--security-opt", "seccomp=unconfined", "--ipc=host"]
|
||||
try:
|
||||
flags += ["--group-add", str(grp.getgrnam("render").gr_gid)] # Arch: /dev/dri/renderD128 is group render
|
||||
except KeyError:
|
||||
pass
|
||||
return flags
|
||||
|
||||
|
||||
def run_cmd(g, name, preset, envs):
|
||||
cmd = ["docker", "run", "-d", "--name", g["container"], *gpu_flags(),
|
||||
"-p", f'{g["port"]}:8000',
|
||||
"-v", f'{g["models-dir"]}:/models:ro',
|
||||
"-v", f'{g["hf-cache"]}:/root/.cache/huggingface']
|
||||
for k, v in envs.items():
|
||||
cmd += ["-e", f"{k}={v}"]
|
||||
cmd.append(g["image"])
|
||||
cmd += ["--host", "0.0.0.0", "--port", "8000", "--served-model-name", preset.get("served-model-name", name)]
|
||||
cmd += build_args({k: v for k, v in preset.items() if k != "served-model-name"})
|
||||
return cmd
|
||||
|
||||
|
||||
ROOT_CAUSE = re.compile(r"hipError|HSA_STATUS|code object|gfx\d+|no kernel image|EngineCore.*(ERROR|Error|error)"
|
||||
r"|^\S*(ERROR|CRITICAL)|RuntimeError|ValueError|NotImplementedError|OutOfMemory|Killed|Segmentation"
|
||||
r"|free memory|not supported|Unsupported|Traceback|memory profiling|kv cache memory|weights take|peak", re.I)
|
||||
|
||||
|
||||
def root_cause(lines):
|
||||
"""The lines that explain a dead engine core: known ROCm/vLLM failure signatures, the FIRST
|
||||
traceback's error line, and the last 8 lines — not the parent's 40-line stack trace."""
|
||||
out, seen = [], set()
|
||||
for l in lines:
|
||||
if ROOT_CAUSE.search(l) and "api_server" not in l and "core_client" not in l and "launch_core_engines" not in l:
|
||||
key = l.strip()[:120]
|
||||
if key not in seen:
|
||||
seen.add(key); out.append(l.rstrip())
|
||||
if len(out) >= 30:
|
||||
break
|
||||
return out + ["…"] + [l.rstrip() for l in lines[-8:]]
|
||||
|
||||
|
||||
# ─── commands ───────────────────────────────────────────────────────────────────────────────
|
||||
def cmd_up(a, cfg):
|
||||
g = cfg["global"]
|
||||
if a.preset not in cfg or a.preset == "global":
|
||||
sys.exit(f"unknown preset {a.preset!r}; have: {', '.join(k for k in cfg if k != 'global')}")
|
||||
preset = dict(cfg[a.preset])
|
||||
preset.update(dict(kv.split("=", 1) for kv in a.set)) # --set gpu-memory-utilization=0.73 --set max-model-len=8192
|
||||
preset.setdefault("gpu-memory-utilization", g.get("gpu-memory-utilization", "0.75"))
|
||||
envs = {k[4:]: v for k, v in g.items() if k.startswith("env.")}
|
||||
envs.update({k[4:]: v for k, v in preset.items() if k.startswith("env.")})
|
||||
envs.update(dict(e.split("=", 1) for e in a.env))
|
||||
util = float(preset["gpu-memory-utilization"])
|
||||
reserve = float(a.reserve if a.reserve is not None else g.get("reserve-gb", 2.0))
|
||||
drain = float(g.get("drain-gb", 4.0))
|
||||
|
||||
cmd = run_cmd(g, a.preset, preset, envs)
|
||||
print("docker command:\n " + " ".join(shlex.quote(c) for c in cmd) + "\n")
|
||||
if a.dry_run:
|
||||
return
|
||||
|
||||
st = container_state(g["container"])
|
||||
if st == "running":
|
||||
sys.exit(f"container {g['container']} is already running — `vllmctl.py down` first")
|
||||
if st: # leftover from a crash: its log is the evidence, show the tail, then clear the name
|
||||
r = docker("logs", "--tail", "15", g["container"], check=False)
|
||||
print(f"removing old container ({st}); its last lines were:\n" + "\n".join(" | " + l for l in (r.stdout + r.stderr).strip().splitlines()[-15:]))
|
||||
docker("rm", g["container"], check=False)
|
||||
|
||||
# 1. clean card — unload resident router models through its API while it is still up, then drain
|
||||
if router_active():
|
||||
router_unload(g["router"])
|
||||
used = wait_drain(drain, 180, "before start")
|
||||
used, total, gtt = vram()
|
||||
if used is None or total is None:
|
||||
if not a.force:
|
||||
sys.exit(f"ABORT: cannot read {DRM}/mem_info_vram_* — no VRAM guard, not starting blind (--force overrides)")
|
||||
print("⚠ FORCED without a VRAM guard (sysfs unreadable)"); used, total = 0, 1
|
||||
if used > drain * 1e9 and not a.force:
|
||||
sys.exit(f"ABORT: card not clean — {gb(used)} VRAM used (> drain-gb {drain}). Leftover model / GPU apps?")
|
||||
|
||||
# 2. projection — abort here costs nothing, llama.service is still running
|
||||
budget = util * total
|
||||
free_after = total - used - budget
|
||||
weights = float(preset.get("x-weights-gb", 0) or 0) * 1e9
|
||||
print(f"VRAM: total {gb(total)} · used now {gb(used)} (GTT {gb(gtt)}) · vLLM budget {util:.2f} × total = {gb(budget)}"
|
||||
f"\n → projected free {gb(free_after)} (reserve {reserve} GB)")
|
||||
if weights:
|
||||
room = budget - weights
|
||||
print(f" budget − weights {gb(weights)} = {gb(room)} for activations + KV cache"
|
||||
+ (" ⚠ < 1 GB: expect 'no memory for KV cache' → raise util or lower max-model-len" if room < 1e9 else ""))
|
||||
if free_after < reserve * 1e9:
|
||||
suggest = max(0.0, (total - used - reserve * 1e9) / total)
|
||||
msg = (f"projected free {gb(free_after)} < reserve {reserve} GB. Max safe gpu-memory-utilization right now "
|
||||
f"= {suggest:.2f} (desktop is using {gb(used)}).")
|
||||
if not a.force:
|
||||
sys.exit("ABORT: " + msg + f"\n → free the desktop (vramx) or `--set gpu-memory-utilization={suggest:.2f}` "
|
||||
f"(+ `--set max-model-len=8192` if weights leave < 1 GB); --force to start anyway.")
|
||||
print("⚠ FORCED: " + msg)
|
||||
|
||||
# 3. router gate — the only thing that stops a LAN client from loading a model on top of vLLM
|
||||
if router_active():
|
||||
if a.stop_router:
|
||||
print("stopping llama.service (sudo) …")
|
||||
subprocess.run(["sudo", "systemctl", "stop", "llama.service"], check=True)
|
||||
elif a.unsafe_keep_router:
|
||||
print("⚠ llama.service stays ACTIVE: any client request to :11343 while vLLM runs = GTT freeze.")
|
||||
else:
|
||||
sys.exit("ABORT: llama.service is active. Any client request to :11343 would load a model on top "
|
||||
"of vLLM and freeze the PC.\n → re-run with --stop-router (sudo systemctl stop llama.service), "
|
||||
"or --unsafe-keep-router if you really know nothing will call the router.")
|
||||
used2, _, _ = vram()
|
||||
if used2 is not None and used2 > used + 1e9: # a client loaded something between projection and stop
|
||||
print(f"⚠ VRAM grew to {gb(used2)} meanwhile — vLLM's own free-memory check should refuse; watch the start")
|
||||
|
||||
# 4. start + wait
|
||||
cid = subprocess.run(cmd, check=True, capture_output=True, text=True).stdout.strip()
|
||||
print(f"started {cid[:12]} — waiting for http://127.0.0.1:{g['port']}/health (timeout {g.get('health-timeout', 1800)} s; "
|
||||
f"first start compiles kernels)")
|
||||
end = time.time() + int(g.get("health-timeout", 1800))
|
||||
last_log, shown = 0, set()
|
||||
while time.time() < end:
|
||||
st = container_state(g["container"])
|
||||
if st != "running":
|
||||
r = docker("logs", g["container"], check=False)
|
||||
print("root cause candidates (full log: docker logs " + g["container"] + "):")
|
||||
for l in root_cause((r.stdout + r.stderr).splitlines()):
|
||||
print(" | " + l[:200])
|
||||
sys.exit(f"ABORT: container is {st or 'gone'}. If a line names gfx1102 / 'code object' / hipErrorNoBinaryForGpu: "
|
||||
f"uncomment env.HSA_OVERRIDE_GFX_VERSION = 11.0.0 in [global] and retry." + router_hint())
|
||||
try:
|
||||
if http(f"http://127.0.0.1:{g['port']}/health", timeout=3).status == 200:
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
if time.time() - last_log > 30:
|
||||
r = docker("logs", "--tail", "3", g["container"], check=False)
|
||||
tail = (r.stdout + r.stderr).strip().splitlines()[-3:]
|
||||
for t in tail:
|
||||
if t not in shown: # only lines not printed before (compile phases are quiet for minutes)
|
||||
print(" | " + t[:160]); shown.add(t)
|
||||
last_log = time.time()
|
||||
time.sleep(3)
|
||||
else:
|
||||
sys.exit("ABORT: no /health within the timeout — `vllmctl.py logs`, then `vllmctl.py down`" + router_hint())
|
||||
used, total, gtt = vram()
|
||||
print(f"UP. VRAM used {gb(used)} / free {gb(total - used)} · GTT {gb(gtt)}")
|
||||
try:
|
||||
ids = [m["id"] for m in json.load(http(f"http://127.0.0.1:{g['port']}/v1/models")).get("data", [])]
|
||||
print(f"models: {', '.join(ids)} → http://192.168.0.204:{g['port']}/v1")
|
||||
except Exception as e:
|
||||
print(f"(/v1/models not readable yet: {e})")
|
||||
print(f"next: ./bench-vs.py -t vllm:{preset.get('served-model-name', a.preset)} · when done: ./vllmctl.py down")
|
||||
|
||||
|
||||
def cmd_down(a, cfg):
|
||||
g = cfg["global"]
|
||||
st = container_state(g["container"])
|
||||
if st:
|
||||
print(f"stopping {g['container']} ({st}) …")
|
||||
docker("stop", "-t", "60", g["container"], check=False)
|
||||
docker("rm", g["container"], check=False)
|
||||
else:
|
||||
print("container not running")
|
||||
used = wait_drain(float(g.get("drain-gb", 4.0)), 120, "after stop")
|
||||
used, total, gtt = vram()
|
||||
print(f"VRAM used {gb(used)} / free {gb(total - used) if total and used is not None else '-'} · GTT {gb(gtt)}")
|
||||
if not router_active():
|
||||
print("llama.service is stopped → `sudo systemctl start llama.service` to bring the router back")
|
||||
|
||||
|
||||
def cmd_status(a, cfg):
|
||||
g = cfg["global"]
|
||||
used, total, gtt = vram()
|
||||
print(f"container {g['container']}: {container_state(g['container']) or 'not running'}")
|
||||
print(f"VRAM used {gb(used)} / total {gb(total)} · GTT {gb(gtt)}")
|
||||
print(f"llama.service: {'ACTIVE' if router_active() else 'stopped'} · loaded: {router_loaded(g['router']) or '-'}")
|
||||
try:
|
||||
ids = [m["id"] for m in json.load(http(f"http://127.0.0.1:{g['port']}/v1/models", timeout=3)).get("data", [])]
|
||||
print(f"vLLM :{g['port']} serving: {', '.join(ids)}")
|
||||
except Exception:
|
||||
print(f"vLLM :{g['port']}: not answering")
|
||||
|
||||
|
||||
def cmd_logs(a, cfg):
|
||||
os.execvp("docker", ["docker", "logs", "-f", "--tail", "100", cfg["global"]["container"]])
|
||||
|
||||
|
||||
def cmd_pull(a, cfg):
|
||||
os.execvp("docker", ["docker", "pull", cfg["global"]["image"]])
|
||||
|
||||
|
||||
GPU_CHECK = r"""
|
||||
import os, torch
|
||||
print('HSA_OVERRIDE_GFX_VERSION =', os.environ.get('HSA_OVERRIDE_GFX_VERSION', '(unset)'))
|
||||
print('cuda available:', torch.cuda.is_available())
|
||||
p = torch.cuda.get_device_properties(0)
|
||||
print('device:', torch.cuda.get_device_name(0) or '(no name)', p.gcnArchName, f'{p.total_memory / 1e9:.1f} GB')
|
||||
print('torch kernel …', end=' ', flush=True) # a hard abort here (no code object for this arch) prints nothing more
|
||||
x = torch.ones(1024, device='cuda'); torch.cuda.synchronize(); print('ok', x.sum().item())
|
||||
import vllm._C # noqa
|
||||
ops = sorted({s.name.split('::', 1)[1] for s in torch._C._jit_get_all_schemas() if s.name.startswith('_C::')})
|
||||
print(f'vllm _C ops registered: {len(ops)}')
|
||||
f16 = dict(device='cuda', dtype=torch.float16)
|
||||
probes = {'silu_and_mul': lambda: torch.ops._C.silu_and_mul(torch.empty(4, 8, **f16), torch.randn(4, 16, **f16)),
|
||||
'gelu_fast': lambda: torch.ops._C.gelu_fast(torch.empty(4, 8, **f16), torch.randn(4, 8, **f16)),
|
||||
'rms_norm': lambda: torch.ops._C.rms_norm(torch.empty(4, 8, **f16), torch.randn(4, 8, **f16), torch.ones(8, **f16), 1e-6)}
|
||||
for name, fn in probes.items():
|
||||
if name in ops:
|
||||
print(f'vllm custom op {name} …', end=' ', flush=True); fn(); torch.cuda.synchronize(); print('ok -> the image has kernels for this arch')
|
||||
break
|
||||
else:
|
||||
print('none of the probe ops exist in this build; first ops:', ops[:15])
|
||||
"""
|
||||
|
||||
|
||||
def cmd_gpu_check(a, cfg):
|
||||
"""Does the image see the card? Prints torch's device name + arch (needs no model)."""
|
||||
g = cfg["global"]
|
||||
envs = [x for k, v in g.items() if k.startswith("env.") for x in ("-e", f"{k[4:]}={v}")]
|
||||
envs += [x for e in a.env for x in ("-e", e)]
|
||||
code = GPU_CHECK
|
||||
cmd = ["docker", "run", "--rm", *gpu_flags(), *envs, "--entrypoint", "python3", g["image"], "-u", "-c", code]
|
||||
print(" ".join(shlex.quote(c) for c in cmd))
|
||||
os.execvp("docker", cmd)
|
||||
|
||||
|
||||
def cmd_exec(a, cfg):
|
||||
g = cfg["global"]
|
||||
if container_state(g["container"]) == "running":
|
||||
os.execvp("docker", ["docker", "exec", "-it", g["container"], *a.cmd])
|
||||
envs = [x for k, v in g.items() if k.startswith("env.") for x in ("-e", f"{k[4:]}={v}")]
|
||||
os.execvp("docker", ["docker", "run", "--rm", "-it", *gpu_flags(), *envs, "--entrypoint", a.cmd[0], g["image"], *a.cmd[1:]])
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("--ini", default=INI)
|
||||
sub = ap.add_subparsers(dest="cmd", required=True)
|
||||
up = sub.add_parser("up"); up.add_argument("preset")
|
||||
up.add_argument("--stop-router", action="store_true", help="sudo systemctl stop llama.service first")
|
||||
up.add_argument("--unsafe-keep-router", action="store_true", help="leave llama.service running (freeze risk)")
|
||||
up.add_argument("--force", action="store_true", help="ignore the VRAM projection / clean-card checks")
|
||||
up.add_argument("--reserve", type=float, help="override reserve-gb")
|
||||
up.add_argument("--dry-run", action="store_true", help="print the docker command only")
|
||||
up.add_argument("--env", action="append", default=[], metavar="KEY=VAL", help="extra container env (e.g. HSA_OVERRIDE_GFX_VERSION=11.0.0)")
|
||||
up.add_argument("--set", action="append", default=[], metavar="KEY=VAL", help="override a preset key for this start (e.g. gpu-memory-utilization=0.73)")
|
||||
sub.add_parser("down"); sub.add_parser("status"); sub.add_parser("logs"); sub.add_parser("pull")
|
||||
gc = sub.add_parser("gpu-check"); gc.add_argument("--env", action="append", default=[], metavar="KEY=VAL")
|
||||
ex = sub.add_parser("exec"); ex.add_argument("cmd", nargs=argparse.REMAINDER)
|
||||
a = ap.parse_args()
|
||||
cfg = parse_ini(a.ini)
|
||||
if "global" not in cfg:
|
||||
sys.exit(f"{a.ini}: no [global] section")
|
||||
{"up": cmd_up, "down": cmd_down, "status": cmd_status, "logs": cmd_logs, "pull": cmd_pull,
|
||||
"gpu-check": cmd_gpu_check, "exec": cmd_exec}[a.cmd](a, cfg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -28,7 +28,7 @@ WHISPER_LANG=auto
|
||||
|
||||
which expands to:
|
||||
|
||||
```
|
||||
```sh
|
||||
whisper-server --model $WHISPER_MODEL \
|
||||
--host 0.0.0.0 --port $WHISPER_PORT \
|
||||
--inference-path /v1/audio/transcriptions \
|
||||
@@ -56,7 +56,7 @@ with auth in front — whisper-server has no `--api-key`.
|
||||
`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:
|
||||
|
||||
```
|
||||
```sh
|
||||
dotsync # nudges you if this dir changed
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart whisper@turbo whisper@small
|
||||
@@ -66,7 +66,7 @@ Editing only an `.env` needs no `daemon-reload` — just restart that instance.
|
||||
|
||||
First install (stow does not install units):
|
||||
|
||||
```
|
||||
```sh
|
||||
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
|
||||
@@ -99,7 +99,7 @@ transcribing it, and the `gemma-4-E4B` preset samples at temp 1.0. Whisper is pu
|
||||
|
||||
**Still available as a fallback** for wav/mp3/flac (no config change needed), if these services are down:
|
||||
|
||||
```
|
||||
```sh
|
||||
curl -F file=@clip.wav -F model=gemma-4-E4B-it-UD-Q8_K_XL \
|
||||
http://$SERVER:11343/v1/audio/transcriptions
|
||||
```
|
||||
@@ -122,7 +122,7 @@ with line breaks mid-sentence.
|
||||
|
||||
## Verifying
|
||||
|
||||
```
|
||||
```sh
|
||||
systemctl status whisper@turbo whisper@small
|
||||
rocm-smi --showmeminfo vram # must be UNCHANGED vs before the services started
|
||||
|
||||
@@ -159,7 +159,7 @@ have no server integration, so **model size is the only real dial**.
|
||||
|
||||
A/B any candidate without touching a service:
|
||||
|
||||
```
|
||||
```sh
|
||||
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
|
||||
|
||||
Reference in new issue
Block a user