gpt4all/gpt4all-chat/qml/SettingsDialog.qml
Adam Treat 705b480d72 Start moving toward a single authoritative class for all settings. This
is necessary to get rid of technical debt before we drastically increase
the complexity of settings by adding per model settings and mirostat and
other fun things. Right now the settings are divided between QML and C++
and some convenience methods to deal with settings sync and so on that are
in other singletons. This change consolidates all the logic for settings
into a single class with a single API for both C++ and QML.
2023-06-28 20:11:24 -03:00

784 lines
33 KiB
QML

import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Dialogs
import QtQuick.Layouts
import Qt.labs.folderlistmodel
import chatlistmodel
import download
import modellist
import network
import llm
import mysettings
Dialog {
id: settingsDialog
modal: true
opacity: 0.9
padding: 20
bottomPadding: 30
background: Rectangle {
anchors.fill: parent
color: theme.backgroundDarkest
border.width: 1
border.color: theme.dialogBorder
radius: 10
}
onOpened: {
Network.sendSettingsDialog();
}
property var currentChat: ChatListModel.currentChat
Theme {
id: theme
}
function restoreGenerationDefaults() {
MySettings.restoreGenerationDefaults();
templateTextArea.text = MySettings.promptTemplate
}
function restoreApplicationDefaults() {
MySettings.restoreApplicationDefaults();
ModelList.localModelsPath = MySettings.modelPath
LLM.threadCount = MySettings.threadCount
LLM.serverEnabled = MySettings.serverChat
ChatListModel.shouldSaveChats = MySettings.saveChats
ChatListModel.shouldSaveChatGPTChats = MySettings.saveChatGPTChats
MySettings.forceMetal = false
}
Component.onCompleted: {
LLM.threadCount = MySettings.threadCount
LLM.serverEnabled = MySettings.serverChat
ChatListModel.shouldSaveChats = MySettings.saveChats
ChatListModel.shouldSaveChatGPTChats = MySettings.saveChatGPTChats
ModelList.localModelsPath = MySettings.modelPath
}
Item {
Accessible.role: Accessible.Dialog
Accessible.name: qsTr("Settings dialog")
Accessible.description: qsTr("Dialog containing various application settings")
}
TabBar {
id: settingsTabBar
width: parent.width / 1.25
z: 200
TabButton {
id: genSettingsButton
contentItem: IconLabel {
color: theme.textColor
font.bold: genSettingsButton.checked
text: qsTr("Generation")
}
background: Rectangle {
color: genSettingsButton.checked ? theme.backgroundDarkest : theme.backgroundLight
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: genSettingsButton.checked
color: theme.tabBorder
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: !genSettingsButton.checked
color: theme.tabBorder
}
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: genSettingsButton.checked
color: theme.tabBorder
}
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: genSettingsButton.checked
color: theme.tabBorder
}
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Generation settings")
Accessible.description: qsTr("Settings related to how the model generates text")
}
TabButton {
id: appSettingsButton
contentItem: IconLabel {
color: theme.textColor
font.bold: appSettingsButton.checked
text: qsTr("Application")
}
background: Rectangle {
color: appSettingsButton.checked ? theme.backgroundDarkest : theme.backgroundLight
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: appSettingsButton.checked
color: theme.tabBorder
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: !appSettingsButton.checked
color: theme.tabBorder
}
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: appSettingsButton.checked
color: theme.tabBorder
}
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: appSettingsButton.checked
color: theme.tabBorder
}
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Application settings")
Accessible.description: qsTr("Settings related to general behavior of the application")
}
TabButton {
id: localDocsButton
contentItem: IconLabel {
color: theme.textColor
font.bold: localDocsButton.checked
text: qsTr("LocalDocs Plugin (BETA)")
}
background: Rectangle {
color: localDocsButton.checked ? theme.backgroundDarkest : theme.backgroundLight
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: localDocsButton.checked
color: theme.tabBorder
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: !localDocsButton.checked
color: theme.tabBorder
}
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: localDocsButton.checked
color: theme.tabBorder
}
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: localDocsButton.checked
color: theme.tabBorder
}
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("LocalDocs settings")
Accessible.description: qsTr("Settings related to localdocs plugin")
}
}
StackLayout {
anchors.top: settingsTabBar.bottom
anchors.topMargin: -1
width: parent.width
height: availableHeight
currentIndex: settingsTabBar.currentIndex
Item {
id: generationSettingsTab
ScrollView {
background: Rectangle {
color: 'transparent'
border.color: theme.tabBorder
border.width: 1
radius: 2
}
padding: 10
width: parent.width
height: parent.height - 30
contentWidth: availableWidth - 20
contentHeight: generationSettingsTabInner.implicitHeight + 40
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
GridLayout {
id: generationSettingsTabInner
anchors.margins: 10
columns: 2
rowSpacing: 10
columnSpacing: 10
anchors.fill: parent
Label {
id: tempLabel
text: qsTr("Temperature:")
color: theme.textColor
Layout.row: 0
Layout.column: 0
}
MyTextField {
text: MySettings.temperature
color: theme.textColor
ToolTip.text: qsTr("Temperature increases the chances of choosing less likely tokens.\nNOTE: Higher temperature gives more creative but less predictable outputs.")
ToolTip.visible: hovered
Layout.row: 0
Layout.column: 1
validator: DoubleValidator {
locale: "C"
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.temperature = val
focus = false
} else {
text = MySettings.temperature
}
}
Accessible.role: Accessible.EditableText
Accessible.name: tempLabel.text
Accessible.description: ToolTip.text
}
Label {
id: topPLabel
text: qsTr("Top P:")
color: theme.textColor
Layout.row: 1
Layout.column: 0
}
MyTextField {
text: MySettings.topP
color: theme.textColor
ToolTip.text: qsTr("Only the most likely tokens up to a total probability of top_p can be chosen.\nNOTE: Prevents choosing highly unlikely tokens, aka Nucleus Sampling")
ToolTip.visible: hovered
Layout.row: 1
Layout.column: 1
validator: DoubleValidator {
locale: "C"
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.topP = val
focus = false
} else {
text = MySettings.topP
}
}
Accessible.role: Accessible.EditableText
Accessible.name: topPLabel.text
Accessible.description: ToolTip.text
}
Label {
id: topKLabel
text: qsTr("Top K:")
color: theme.textColor
Layout.row: 2
Layout.column: 0
}
MyTextField {
text: MySettings.topK
color: theme.textColor
ToolTip.text: qsTr("Only the top K most likely tokens will be chosen from")
ToolTip.visible: hovered
Layout.row: 2
Layout.column: 1
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.topK = val
focus = false
} else {
text = MySettings.topK
}
}
Accessible.role: Accessible.EditableText
Accessible.name: topKLabel.text
Accessible.description: ToolTip.text
}
Label {
id: maxLengthLabel
text: qsTr("Max Length:")
color: theme.textColor
Layout.row: 3
Layout.column: 0
}
MyTextField {
text: MySettings.maxLength
color: theme.textColor
ToolTip.text: qsTr("Maximum length of response in tokens")
ToolTip.visible: hovered
Layout.row: 3
Layout.column: 1
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.maxLength = val
focus = false
} else {
text = MySettings.maxLength
}
}
Accessible.role: Accessible.EditableText
Accessible.name: maxLengthLabel.text
Accessible.description: ToolTip.text
}
Label {
id: batchSizeLabel
text: qsTr("Prompt Batch Size:")
color: theme.textColor
Layout.row: 4
Layout.column: 0
}
MyTextField {
text: MySettings.promptBatchSize
color: theme.textColor
ToolTip.text: qsTr("Amount of prompt tokens to process at once.\nNOTE: Higher values can speed up reading prompts but will use more RAM")
ToolTip.visible: hovered
Layout.row: 4
Layout.column: 1
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.promptBatchSize = val
focus = false
} else {
text = MySettings.promptBatchSize
}
}
Accessible.role: Accessible.EditableText
Accessible.name: batchSizeLabel.text
Accessible.description: ToolTip.text
}
Label {
id: repeatPenaltyLabel
text: qsTr("Repeat Penalty:")
color: theme.textColor
Layout.row: 5
Layout.column: 0
}
MyTextField {
text: MySettings.repeatPenalty
color: theme.textColor
ToolTip.text: qsTr("Amount to penalize repetitiveness of the output")
ToolTip.visible: hovered
Layout.row: 5
Layout.column: 1
validator: DoubleValidator {
locale: "C"
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.repeatPenalty = val
focus = false
} else {
text = MySettings.repeatPenalty
}
}
Accessible.role: Accessible.EditableText
Accessible.name: repeatPenaltyLabel.text
Accessible.description: ToolTip.text
}
Label {
id: repeatPenaltyTokensLabel
text: qsTr("Repeat Penalty Tokens:")
color: theme.textColor
Layout.row: 6
Layout.column: 0
}
MyTextField {
text: MySettings.repeatPenaltyTokens
color: theme.textColor
ToolTip.text: qsTr("How far back in output to apply repeat penalty")
ToolTip.visible: hovered
Layout.row: 6
Layout.column: 1
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.repeatPenaltyTokens = val
focus = false
} else {
text = MySettings.repeatPenaltyTokens
}
}
Accessible.role: Accessible.EditableText
Accessible.name: repeatPenaltyTokensLabel.text
Accessible.description: ToolTip.text
}
ColumnLayout {
Layout.row: 7
Layout.column: 0
Layout.topMargin: 10
Layout.alignment: Qt.AlignTop
spacing: 20
Label {
id: promptTemplateLabel
text: qsTr("Prompt Template:")
color: theme.textColor
}
Label {
id: promptTemplateLabelHelp
Layout.maximumWidth: promptTemplateLabel.width
visible: templateTextArea.text.indexOf(
"%1") === -1
color: theme.textErrorColor
text: qsTr("Must contain the string \"%1\" to be replaced with the user's input.")
wrapMode: TextArea.Wrap
Accessible.role: Accessible.EditableText
Accessible.name: text
}
}
Rectangle {
Layout.row: 7
Layout.column: 1
Layout.fillWidth: true
height: 200
color: "transparent"
clip: true
ScrollView {
id: templateScrollView
anchors.fill: parent
TextArea {
id: templateTextArea
text: MySettings.promptTemplate
color: theme.textColor
background: Rectangle {
implicitWidth: 150
color: theme.backgroundLighter
radius: 10
}
padding: 10
wrapMode: TextArea.Wrap
onTextChanged: {
if (templateTextArea.text.indexOf("%1") !== -1) {
MySettings.promptTemplate = text
}
}
bottomPadding: 10
Accessible.role: Accessible.EditableText
Accessible.name: promptTemplateLabel.text
Accessible.description: promptTemplateLabelHelp.text
ToolTip.text: qsTr("The prompt template partially determines how models will respond to prompts.\nNOTE: A longer, detailed template can lead to higher quality answers, but can also slow down generation.")
ToolTip.visible: hovered
}
}
}
MyButton {
Layout.row: 8
Layout.column: 1
Layout.fillWidth: true
text: qsTr("Restore Defaults")
Accessible.description: qsTr("Restores the settings dialog to a default state")
onClicked: {
settingsDialog.restoreGenerationDefaults()
}
}
}
}
}
Item {
id: applicationSettingsTab
ScrollView {
background: Rectangle {
color: 'transparent'
border.color: theme.tabBorder
border.width: 1
radius: 2
}
padding: 10
width: parent.width
height: parent.height - 30
contentWidth: availableWidth - 20
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
GridLayout {
anchors.margins: 10
columns: 3
rowSpacing: 10
columnSpacing: 10
anchors.fill: parent
Label {
id: defaultModelLabel
text: qsTr("Default model:")
color: theme.textColor
Layout.row: 1
Layout.column: 0
}
MyComboBox {
id: comboBox
Layout.row: 1
Layout.column: 1
Layout.minimumWidth: 350
model: ModelList.userDefaultModelList
Accessible.role: Accessible.ComboBox
Accessible.name: qsTr("ComboBox for displaying/picking the default model")
Accessible.description: qsTr("Use this for picking the default model to use; the first item is the current default model")
function updateModel() {
comboBox.currentIndex = comboBox.indexOfValue(MySettings.userDefaultModel);
}
Component.onCompleted: {
comboBox.updateModel()
}
Connections {
target: MySettings
function onUserDefaultModelChanged() {
comboBox.updateModel()
}
}
onActivated: {
MySettings.userDefaultModel = comboBox.currentText
}
}
FolderDialog {
id: modelPathDialog
title: "Please choose a directory"
currentFolder: "file://" + ModelList.localModelsPath
onAccepted: {
modelPathDisplayField.text = selectedFolder
ModelList.localModelsPath = modelPathDisplayField.text
MySettings.modelPath = ModelList.localModelsPath
}
}
Label {
id: modelPathLabel
text: qsTr("Download path:")
color: theme.textColor
Layout.row: 2
Layout.column: 0
}
MyDirectoryField {
id: modelPathDisplayField
text: ModelList.localModelsPath
implicitWidth: 300
Layout.row: 2
Layout.column: 1
Layout.fillWidth: true
ToolTip.text: qsTr("Path where model files will be downloaded to")
ToolTip.visible: hovered
Accessible.role: Accessible.ToolTip
Accessible.name: modelPathDisplayField.text
Accessible.description: ToolTip.text
onEditingFinished: {
if (isValid) {
ModelList.localModelsPath = modelPathDisplayField.text
MySettings.modelPath = ModelList.localModelsPath
} else {
text = ModelList.localModelsPath
}
}
}
MyButton {
Layout.row: 2
Layout.column: 2
text: qsTr("Browse")
Accessible.description: qsTr("Opens a folder picker dialog to choose where to save model files")
onClicked: modelPathDialog.open()
}
Label {
id: nThreadsLabel
text: qsTr("CPU Threads:")
color: theme.textColor
Layout.row: 3
Layout.column: 0
}
MyTextField {
text: MySettings.threadCount
color: theme.textColor
ToolTip.text: qsTr("Amount of processing threads to use, a setting of 0 will use the lesser of 4 or your number of CPU threads")
ToolTip.visible: hovered
Layout.row: 3
Layout.column: 1
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.threadCount = val
LLM.threadCount = val
focus = false
} else {
text = MySettings.threadCount
}
}
Accessible.role: Accessible.EditableText
Accessible.name: nThreadsLabel.text
Accessible.description: ToolTip.text
}
Label {
id: saveChatsLabel
text: qsTr("Save chats to disk:")
color: theme.textColor
Layout.row: 4
Layout.column: 0
}
MyCheckBox {
id: saveChatsBox
Layout.row: 4
Layout.column: 1
checked: MySettings.saveChats
onClicked: {
Network.sendSaveChatsToggled(saveChatsBox.checked);
MySettings.saveChats = !MySettings.saveChats
ChatListModel.shouldSaveChats = saveChatsBox.checked
}
ToolTip.text: qsTr("WARNING: Saving chats to disk can be ~2GB per chat")
ToolTip.visible: hovered
}
Label {
id: saveChatGPTChatsLabel
text: qsTr("Save ChatGPT chats to disk:")
color: theme.textColor
Layout.row: 5
Layout.column: 0
}
MyCheckBox {
id: saveChatGPTChatsBox
Layout.row: 5
Layout.column: 1
checked: MySettings.saveChatGPTChats
onClicked: {
MySettings.saveChatGPTChats = !MySettings.saveChatGPTChats
ChatListModel.shouldSaveChatGPTChats = saveChatGPTChatsBox.checked
}
}
Label {
id: serverChatLabel
text: qsTr("Enable API server:")
color: theme.textColor
Layout.row: 6
Layout.column: 0
}
MyCheckBox {
id: serverChatBox
Layout.row: 6
Layout.column: 1
checked: MySettings.serverChat
onClicked: {
MySettings.serverChat = !MySettings.serverChat
LLM.serverEnabled = serverChatBox.checked
}
ToolTip.text: qsTr("WARNING: This enables the gui to act as a local REST web server(OpenAI API compliant) for API requests and will increase your RAM usage as well")
ToolTip.visible: hovered
}
Rectangle {
Layout.row: 7
Layout.column: 0
Layout.columnSpan: 3
Layout.fillWidth: true
height: 1
color: theme.dialogBorder
}
Rectangle {
Layout.row: 9
Layout.column: 0
Layout.fillWidth: true
Layout.columnSpan: 3
height: 1
color: theme.dialogBorder
}
Label {
id: gpuOverrideLabel
text: qsTr("Force Metal (macOS+arm):")
color: theme.textColor
Layout.row: 8
Layout.column: 0
}
RowLayout {
Layout.row: 8
Layout.column: 1
Layout.columnSpan: 2
MyCheckBox {
id: gpuOverrideBox
checked: MySettings.forceMetal
onClicked: {
MySettings.forceMetal = !MySettings.forceMetal
}
}
Label {
id: warningLabel
Layout.maximumWidth: 730
Layout.alignment: Qt.AlignTop
color: theme.textErrorColor
wrapMode: Text.WordWrap
text: qsTr("WARNING: On macOS with arm (M1+) this setting forces usage of the GPU. Can cause crashes if the model requires more RAM than the system supports. Because of crash possibility the setting will not persist across restarts of the application. This has no effect on non-macs or intel.")
}
}
MyButton {
Layout.row: 10
Layout.column: 1
Layout.columnSpan: 2
Layout.fillWidth: true
text: qsTr("Restore Defaults")
Accessible.description: qsTr("Restores the settings dialog to a default state")
onClicked: {
settingsDialog.restoreApplicationDefaults()
}
}
}
}
}
Item {
id: localDocsTab
ScrollView {
background: Rectangle {
color: 'transparent'
border.color: theme.tabBorder
border.width: 1
radius: 2
}
padding: 10
width: parent.width
height: parent.height - 30
contentWidth: availableWidth - 20
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
LocalDocs {
anchors.margins: 10
anchors.fill: parent
}
}
}
}
}