New UI using customtkinter (#284)

* Initial conversion to customtkinter.

* Initial conversion to customtkinter.

* Additions to UI, still non-functional

* UI now functional, untested

* UI now functional, untested

* Added saving configs

* Saving and loading now functional

* Fixed sliders not loading

* Cleaned up duplicate arrays

* Cleaned up duplicate arrays

* Fixed loading bugs

* wip fixing all the broken parameters. PLEASE test before you commit

* further cleaning

* bugfix completed for gui. now evaluating save and load

* cleanup prepare to merge

---------

Co-authored-by: Concedo <39025047+LostRuins@users.noreply.github.com>
This commit is contained in:
Vali-98 2023-07-06 15:00:57 +08:00 committed by GitHub
parent ea79e549f0
commit 1c80002310
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -588,8 +588,403 @@ def RunServerMultiThreaded(addr, port, embedded_kailite = None):
threadArr[i].stop()
sys.exit(0)
# note: customtkinter-5.2.0
def show_new_gui():
import customtkinter as ctk
from tkinter.filedialog import askopenfilename
from tkinter.filedialog import asksaveasfile
def show_gui():
# if args received, launch
if len(sys.argv) != 1:
root = ctk.CTk()
#we dont want the useless window to be visible, but we want it in taskbar
root.attributes("-alpha", 0)
args.model_param = askopenfilename(title="Select ggml model .bin files")
root.destroy()
if not args.model_param:
print("\nNo ggml model file was selected. Exiting.")
time.sleep(2)
sys.exit(2)
return
nextstate = 0 #0=exit, 1=launch, 2=oldgui
windowwidth = 480
windowheight = 480
ctk.set_appearance_mode("dark")
root = ctk.CTk()
root.iconbitmap("niko.ico")
root.geometry(str(windowwidth) + "x" + str(windowheight))
root.title("KoboldCpp v"+KcppVersion)
root.resizable(False,False)
tabs = ctk.CTkFrame(root, corner_radius = 0, width=windowwidth, height=windowheight-40)
tabs.grid(row=0, stick="nsew")
tabnames= ["Quick Launch", "Hardware", "Tokens", "Model", "Network"]
navbuttons = {}
navbuttonframe = ctk.CTkFrame(tabs, width=100, height=int(tabs.cget("height")))
navbuttonframe.grid(row=0, column=0, padx=2,pady=2)
navbuttonframe.grid_propagate(False)
tabcontentframe = ctk.CTkFrame(tabs, width=windowwidth - int(navbuttonframe.cget("width")), height=int(tabs.cget("height")))
tabcontentframe.grid(row=0, column=1, sticky="nsew", padx=2, pady=2)
tabcontentframe.grid_propagate(False)
tabcontent = {}
# slider data
blasbatchsize_values = ["-1", "32", "64", "128", "256", "512", "1024"]
blasbatchsize_text = ["Don't Batch BLAS","32","64","128","256","512","1024"]
contextsize_text = ["512", "1024", "2048", "3072", "4096", "6144", "8192"]
runopts = ["Use OpenBLAS","Use CLBlast", "Use CuBLAS", "Use No BLAS","Use OpenBLAS (Old CPU, noavx2)","Failsafe Mode (Old CPU, noavx)"]
def tabbuttonaction(name):
for t in tabcontent:
if name == t:
tabcontent[t].grid(row=0, column=0)
navbuttons[t].configure(fg_color="#6f727b")
else:
tabcontent[t].grid_forget()
navbuttons[t].configure(fg_color="transparent")
# Dynamically create tabs + buttons based on values of [tabnames]
for idx, name in enumerate(tabnames):
tabcontent[name] = ctk.CTkFrame(tabcontentframe, width=int(tabcontentframe.cget("width")), height=int(tabcontentframe.cget("height")), fg_color="transparent")
tabcontent[name].grid_propagate(False)
if idx == 0:
tabcontent[name].grid(row=idx, sticky="nsew")
ctk.CTkLabel(tabcontent[name], text= name, font=ctk.CTkFont(None, 14, 'bold')).grid(row=0, padx=12, pady = 5, stick='nw')
navbuttons[name] = ctk.CTkButton(navbuttonframe, text=name, width = 100, corner_radius=0 , command = lambda d=name:tabbuttonaction(d), hover_color="#868a94" )
navbuttons[name].grid(row=idx)
tabbuttonaction(tabnames[0])
# helper functions
def makecheckbox(parent, text, variable=None, row=0, column=0, command=None, onvalue=1, offvalue=0):
temp = ctk.CTkCheckBox(parent, text=text,variable=variable, onvalue=onvalue, offvalue=offvalue)
if command is not None and variable is not None:
variable.trace("w", command)
temp.grid(row=row,column=column, padx=8, pady=1, stick="nw")
return temp
def makelabel(parent, text, row, column=0):
temp = ctk.CTkLabel(parent, text=text)
temp.grid(row=row, column=column, padx=8, pady=1, stick="nw")
return temp
def makeslider(parent, label, options, var, from_ , to, row=0, width=160, height=10, set=0):
sliderLabel = makelabel(parent, options[set], row + 1, 1)
makelabel(parent, label, row)
def sliderUpdate(a,b,c):
sliderLabel.configure(text = options[int(var.get())])
var.trace("w", sliderUpdate)
slider = ctk.CTkSlider(parent, from_=from_, to=to, variable = var, width = width, height=height, border_width=5,number_of_steps=len(options) - 1)
slider.grid(row=row+1, column=0, padx = 8, stick="w")
slider.set(set)
return slider
def makelabelentry(parent, text, var, row=0, width= 50):
label = makelabel(parent, text, row)
entry = ctk.CTkEntry(parent, width=width, textvariable=var) #you cannot set placeholder text for SHARED variables
entry.grid(row=row, column=1, padx= 8, stick="nw")
return entry, label
def makefileentry(parent, text, searchtext, var, row=0, width=250):
makelabel(parent, text, row)
def getfilename(var, text):
var.set(askopenfilename(title=text))
entry = ctk.CTkEntry(parent, width, textvariable=var)
entry.grid(row=row+1, column=0, padx=8, stick="nw")
button = ctk.CTkButton(parent, 50, text="Browse", command= lambda a=var,b=searchtext:getfilename(a,b))
button.grid(row=row+1, column=1, stick="nw")
return
# Vars - should be in scope to be used by multiple widgets
gpulayers_var = ctk.StringVar(value="0")
threads_var = ctk.StringVar(value=str(default_threads))
runopts_var = ctk.StringVar()
gpu_choice_var = ctk.StringVar(value="1")
launchbrowser = ctk.IntVar(value=1)
highpriority = ctk.IntVar()
disablemmap = ctk.IntVar()
psutil = ctk.IntVar()
usemlock = ctk.IntVar()
debugmode = ctk.IntVar()
lowvram_var = ctk.IntVar()
blas_threads_var = ctk.StringVar()
blas_size_var = ctk.IntVar()
version_var =ctk.StringVar(value="0")
stream = ctk.IntVar()
smartcontext = ctk.IntVar()
unbantokens = ctk.IntVar()
usemirostat = ctk.IntVar()
mirostat_var = ctk.StringVar(value="2")
mirostat_tau = ctk.StringVar(value="5.0")
mirostat_eta = ctk.StringVar(value="0.1")
context_var = ctk.IntVar()
model_var = ctk.StringVar()
lora_var = ctk.StringVar()
lora_base_var = ctk.StringVar()
port_var = ctk.StringVar(value=defaultport)
host_var = ctk.StringVar(value="")
horde_name_var = ctk.StringVar(value="koboldcpp")
horde_gen_var = ctk.StringVar(value=maxhordelen)
horde_context_var = ctk.StringVar(value=maxhordectx)
usehorde_var = ctk.IntVar()
# Quick Launch Tab
quick_tab = tabcontent["Quick Launch"]
# gpu options
quick_gpu_layers_entry,quick_gpu_layers_label = makelabelentry(quick_tab,"GPU Layers:", gpulayers_var, 4, 50)
quick_gpu_selector_label = makelabel(quick_tab, "GPU ID:", 3)
quick_gpu_selector_box = ctk.CTkComboBox(quick_tab, values=["1","2","3"], width=60, variable=gpu_choice_var, state="readonly")
quick_lowvram_box = makecheckbox(quick_tab, "Low VRAM", lowvram_var, 5)
# hides gpu options when CLBlast is not chosen
def changerunmode(a,b,c):
index = runopts_var.get()
if index == "Use CLBlast":
gpu_selector_label.grid(row=3, column=0, padx = 8, pady=1, stick="nw")
gpu_selector_box .grid(row=3, column=1, padx=8, pady=1, stick="nw")
quick_gpu_selector_label.grid(row=3, column=0, padx = 8, pady=1, stick="nw")
quick_gpu_selector_box .grid(row=3, column=1, padx=8, pady=1, stick="nw")
else:
gpu_selector_label.grid_forget()
gpu_selector_box.grid_forget()
quick_gpu_selector_label.grid_forget()
quick_gpu_selector_box.grid_forget()
if index == "Use CuBLAS":
lowvram_box.grid(row=3, column=0, padx=8, pady=1, stick="nw")
quick_lowvram_box.grid(row=3, column=0, padx=8, pady=1, stick="nw")
else:
lowvram_box.grid_forget()
quick_lowvram_box.grid_forget()
if index == "Use CLBlast" or index == "Use CuBLAS":
gpu_layers_label.grid(row=4, column=0, padx = 8, pady=1, stick="nw")
gpu_layers_entry.grid(row=4, column=1, padx=8, pady=1, stick="nw")
quick_gpu_layers_label.grid(row=4, column=0, padx = 8, pady=1, stick="nw")
quick_gpu_layers_entry.grid(row=4, column=1, padx=8, pady=1, stick="nw")
else:
gpu_layers_label.grid_forget()
gpu_layers_entry.grid_forget()
quick_gpu_layers_label.grid_forget()
quick_gpu_layers_entry.grid_forget()
# presets selector
makelabel(quick_tab, "Presets:", 1)
runoptbox = ctk.CTkComboBox(quick_tab, values=runopts, width=150,variable=runopts_var, state="readonly")
runoptbox.grid(row=1, column=1,padx=8, stick="nw")
runoptbox.set("Use OpenBLAS")
# threads
makelabelentry(quick_tab, "Threads:" , threads_var, 8, 50)
# blas batch size
makeslider(quick_tab, "BLAS Batch Size:", blasbatchsize_text, blas_size_var, 0, 6, 12, set=5)
# quick boxes
quick_boxes = {"Launch Browser": launchbrowser , "High Priority" : highpriority, "Streaming Mode":stream, "Use SmartContext":smartcontext, "Unban Tokens":unbantokens, "Disable MMAP":disablemmap,}
for idx, name, in enumerate(quick_boxes):
makecheckbox(quick_tab, name, quick_boxes[name], int(idx/2) +20, idx%2)
# context size
makeslider(quick_tab, "Context Size:", contextsize_text, context_var, 0, len(contextsize_text)-1, 30, set=2)
# load model
makefileentry(quick_tab, "Model:", "Select Model File", model_var, 40, 170)
# Hardware Tab
hardware_tab = tabcontent["Hardware"]
# gpu options
gpu_layers_entry,gpu_layers_label = makelabelentry(hardware_tab,"GPU Layers:", gpulayers_var, 4, 50)
gpu_selector_label = makelabel(hardware_tab, "GPU ID:", 3)
gpu_selector_box = ctk.CTkComboBox(hardware_tab, values=["1","2","3"], width=60, variable=gpu_choice_var, state="readonly")
lowvram_box = makecheckbox(hardware_tab, "Low VRAM", lowvram_var, 5)
# presets selector
makelabel(hardware_tab, "Presets:", 1)
runoptbox = ctk.CTkComboBox(hardware_tab, values=runopts, width=150,variable=runopts_var, state="readonly")
runoptbox.grid(row=1, column=1,padx=8, stick="nw")
runoptbox.set("Use OpenBLAS")
runopts_var.trace('w', changerunmode)
changerunmode(1,1,1)
# threads
makelabelentry(hardware_tab, "Threads:" , threads_var, 8, 50)
# hardware checkboxes
hardware_boxes = {"Launch Browser": launchbrowser , "High Priority" : highpriority, "Disable MMAP":disablemmap, "Use mlock":usemlock, "PSUtil Set Threads":psutil, "Debug Mode":debugmode,}
for idx, name, in enumerate(hardware_boxes):
makecheckbox(hardware_tab, name, hardware_boxes[name], int(idx/2) +30, idx%2)
# blas thread specifier
makelabelentry(hardware_tab, "BLAS threads:" , blas_threads_var, 11, 50)
# blas batch size
makeslider(hardware_tab, "BLAS Batch Size:", blasbatchsize_text, blas_size_var, 0, 6, 12, set=5)
# force version
makelabelentry(hardware_tab, "Force Version:" , version_var, 100, 50)
# Tokens Tab
tokens_tab = tabcontent["Tokens"]
# tokens checkboxes
token_boxes = {"Streaming Mode":stream, "Use SmartContext":smartcontext, "Unban Tokens":unbantokens}
for idx, name, in enumerate(token_boxes):
makecheckbox(tokens_tab, name, token_boxes[name], idx + 1)
mirostat_entry, mirostate_label = makelabelentry(tokens_tab, "Mirostat:", mirostat_var)
mirostat_tau_entry, mirostat_tau_label = makelabelentry(tokens_tab, "Mirostat Tau:", mirostat_tau)
mirostat_eta_entry, mirostat_eta_label = makelabelentry(tokens_tab, "Mirostat Eta:", mirostat_eta)
def togglemiro(a,b,c):
items = [mirostate_label, mirostat_entry, mirostat_tau_label, mirostat_tau_entry, mirostat_eta_label, mirostat_eta_entry]
for idx, item in enumerate(items):
if usemirostat.get() == 1:
item.grid(row=11 + int(idx/2), column=idx%2, padx=8, stick="nw")
else:
item.grid_forget()
mirostat_box = makecheckbox(tokens_tab, "Use Mirostat", row=10, variable=usemirostat, command=togglemiro)
togglemiro(1,1,1)
# context size
makeslider(tokens_tab, "Context Size:",contextsize_text, context_var, 0, 4, 20, set=2)
# Model Tab
model_tab = tabcontent["Model"]
makefileentry(model_tab, "Model:", "Select Model File", model_var, 1)
makefileentry(model_tab, "Lora:", "Select Lora File",lora_var, 3)
makefileentry(model_tab, "Lora Base:", "Select Lora Base File", lora_base_var, 5)
# Network Tab
network_tab = tabcontent["Network"]
# interfaces
makelabelentry(network_tab, "Port: ", port_var, 1, 150)
makelabelentry(network_tab, "Host: ", host_var, 2, 150)
# horde
makelabel(network_tab, "Horde:", 3).grid(pady=10)
horde_name_entry, horde_name_label = makelabelentry(network_tab, "Horde Name:", horde_name_var, 5, 200)
horde_gen_entry, horde_gen_label = makelabelentry(network_tab, "Gen. Length:", horde_gen_var, 6, 50)
horde_context_entry, horde_context_label = makelabelentry(network_tab, "Max Context:",horde_context_var, 7, 50)
def togglehorde(a,b,c):
labels = [horde_name_label, horde_gen_label, horde_context_label]
for idx, item in enumerate([horde_name_entry, horde_gen_entry, horde_context_entry]):
if usehorde_var.get() == 1:
item.grid(row=5 + idx, column = 1, padx=8, pady=1, stick="nw")
labels[idx].grid(row=5 + idx, padx=8, pady=1, stick="nw")
else:
item.grid_forget()
labels[idx].grid_forget()
usehorde_box = makecheckbox(network_tab, "Configure for Horde", usehorde_var, 4, command=togglehorde)
togglehorde(1,1,1)
# launch
def guilaunch():
if model_var.get() == "":
tmp = askopenfilename(title="Select ggml model .bin files")
model_var.set(tmp)
nonlocal nextstate
nextstate = 1
root.destroy()
pass
def switch_old_gui():
nonlocal nextstate
nextstate = 2
root.destroy()
pass
ctk.CTkButton(tabs , text = "Launch", fg_color="#2f8d3c", command = guilaunch, width=50, height = 25 ).grid(row=1,column=1, stick="se", padx= 25, pady=5)
# ctk.CTkButton(tabs , text = "Save", fg_color="#084a66", command = save_config, width=60, height = 25 ).grid(row=1,column=1, stick="sw", padx= 5, pady=5)
# ctk.CTkButton(tabs , text = "Load", fg_color="#084a66", command = load_config, width=60, height = 25 ).grid(row=1,column=1, stick="sw", padx= 70, pady=5)
ctk.CTkButton(tabs , text = "Old GUI", fg_color="#084a66", command = switch_old_gui, width=100, height = 25 ).grid(row=1,column=0, stick="sw", padx= 5, pady=5)
# runs main loop until closed or launch clicked
root.mainloop()
if nextstate==0:
print("Exiting by user request.")
time.sleep(2)
sys.exit()
elif nextstate==2:
time.sleep(0.1)
show_old_gui()
else:
# processing vars
args.threads = int(threads_var.get())
args.usemlock = usemlock.get() == 1
args.debugmode = debugmode.get() == 1
args.launch = launchbrowser.get()==1
args.highpriority = highpriority.get()==1
args.nommap = disablemmap.get()==1
args.psutil_set_threads = psutil.get()==1
args.stream = stream.get()==1
args.smartcontext = smartcontext.get()==1
args.unbantokens = unbantokens.get()==1
if runopts_var.get() == runopts[1]:
args.useclblast = [[0,0], [1,0], [0,1]][int(gpu_choice_var.get())-1]
if runopts_var.get() == runopts[2]:
args.usecublas = "lowvram" if lowvram_var.get() == 1 else "normal"
if gpulayers_var.get():
args.gpulayers = int(gpulayers_var.get())
if runopts_var.get()==runopts[3]:
args.noblas = True
if runopts_var.get()==runopts[4]:
args.noavx2 = True
if runopts_var.get()==runopts[5]:
args.noavx2 = True
args.noblas = True
args.nommap = True
print("[Failsafe Mode : mmap is disabled.]")
args.blasthreads = None if blas_threads_var.get()=="" else int(blas_threads_var.get())
args.blasbatchsize = int(blasbatchsize_values[int(blas_size_var.get())])
args.forceversion = 0 if version_var.get()=="" else int(version_var.get())
args.mirostat = [int(mirostat_var.get()), float(mirostat_tau.get()), float(mirostat_eta.get())] if usemirostat.get()==1 else None
args.contextsize = int(contextsize_text[context_var.get()])
args.model_param = None if model_var.get() == "" else model_var.get()
args.lora = None if lora_var.get() == "" else ([lora_var.get()] if lora_base_var.get()=="" else [lora_var.get(), lora_base_var.get()])
args.port_param = defaultport if port_var.get()=="" else int(port_var.get())
args.host = host_var.get()
args.hordeconfig = None if usehorde_var.get() == 0 else [horde_name_var.get(), horde_gen_var.get(), horde_context_var.get()]
if not args.model_param:
print("\nNo ggml model file was selected. Exiting.")
time.sleep(2)
sys.exit(2)
def show_old_gui():
import tkinter as tk
from tkinter.filedialog import askopenfilename
@ -668,7 +1063,7 @@ def show_gui():
frameD.grid(row=5,column=0,pady=4)
# Create button, it will change label text
tk.Button( root , text = "Launch", font = ("Impact", 18), bg='#54FA9B', command = guilaunch ).grid(row=6,column=0)
tk.Button(root , text = "Launch", font = ("Impact", 18), bg='#54FA9B', command = guilaunch ).grid(row=6,column=0)
tk.Label(root, text = "(Please use the Command Line for more advanced options)",
font = ("Arial", 9)).grid(row=7,column=0)
@ -752,12 +1147,18 @@ def main(args):
if not args.model_param:
#give them a chance to pick a file
print("For command line arguments, please refer to --help")
print("Otherwise, please manually select ggml file:")
print("***")
try:
show_gui()
show_new_gui()
except Exception as ex:
print("Failed to use new GUI. Reason: " + str(ex))
print("Attempting to us old GUI...")
if not args.model_param:
try:
show_old_gui()
except Exception as ex2:
print("File selection GUI unsupported. Please check command line: script.py --help")
print("Reason for no GUI: " + str(ex))
print("Reason for no GUI: " + str(ex2))
time.sleep(2)
sys.exit(2)
@ -829,7 +1230,8 @@ def main(args):
args.blasthreads = args.threads
modelname = os.path.abspath(args.model_param)
print(f"Loading model: {modelname} \n[Threads: {args.threads}, BlasThreads: {args.blasthreads}, SmartContext: {args.smartcontext}]")
print(args)
print(f"==========\nLoading model: {modelname} \n[Threads: {args.threads}, BlasThreads: {args.blasthreads}, SmartContext: {args.smartcontext}]")
loadok = load_model(modelname)
print("Load Model OK: " + str(loadok))
@ -864,7 +1266,7 @@ def main(args):
asyncio.run(RunServerMultiThreaded(args.host, args.port, embedded_kailite))
if __name__ == '__main__':
print("Welcome to KoboldCpp - Version " + KcppVersion) # just update version manually
print("***\nWelcome to KoboldCpp - Version " + KcppVersion) # just update version manually
# print("Python version: " + sys.version)
parser = argparse.ArgumentParser(description='KoboldCpp Server')
modelgroup = parser.add_mutually_exclusive_group() #we want to be backwards compatible with the unnamed positional args