gpt4all/gpt4all-chat/qml/LocalDocsSettings.qml
Jared Van Bortel 6506ba161b
UI tweaks for GPT4All v3.0.0-rc2 (#2474)
* clickable link to get API key with hand-style mouse cursor
* remove "Force Metal" setting
* allow typing incorrect API keys (but don't accept them), add placeholder text

Signed-off-by: Jared Van Bortel <jared@nomic.ai>
2024-06-27 11:08:32 -04:00

267 lines
9.7 KiB
QML

import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import QtQuick.Dialogs
import localdocs
import modellist
import mysettings
import network
MySettingsTab {
onRestoreDefaultsClicked: {
MySettings.restoreLocalDocsDefaults();
}
showRestoreDefaultsButton: true
title: qsTr("LocalDocs")
contentItem: ColumnLayout {
id: root
spacing: 10
Label {
color: theme.styledTextColor
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: "Indexing"
}
Rectangle {
Layout.bottomMargin: 15
Layout.fillWidth: true
height: 1
color: theme.settingsDivider
}
RowLayout {
MySettingsLabel {
id: extsLabel
text: qsTr("Allowed File Extensions")
helpText: qsTr("Comma-separated list. LocalDocs will only attempt to process files with these extensions.")
}
MyTextField {
id: extsField
text: MySettings.localDocsFileExtensions.join(',')
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
Layout.alignment: Qt.AlignRight
Layout.minimumWidth: 200
validator: RegularExpressionValidator {
regularExpression: /([^ ,\/"']+,?)*/
}
onEditingFinished: {
// split and remove empty elements
var exts = text.split(',').filter(e => e);
// normalize and deduplicate
exts = exts.map(e => e.toLowerCase());
exts = Array.from(new Set(exts));
/* Blacklist common unsupported file extensions. We only support plain text and PDFs, and although we
* reject binary data, we don't want to waste time trying to index files that we don't support. */
exts = exts.filter(e => ![
/* Microsoft documents */ "rtf", "docx", "ppt", "pptx", "xls", "xlsx",
/* OpenOffice */ "odt", "ods", "odp", "odg",
/* photos */ "jpg", "jpeg", "png", "gif", "bmp", "tif", "tiff", "webp",
/* audio */ "mp3", "wma", "m4a", "wav", "flac",
/* videos */ "mp4", "mov", "webm", "mkv", "avi", "flv", "wmv",
/* executables */ "exe", "com", "dll", "so", "dylib", "msi",
/* binary images */ "iso", "img", "dmg",
/* archives */ "zip", "jar", "apk", "rar", "7z", "tar", "gz", "xz", "bz2", "tar.gz",
"tgz", "tar.xz", "tar.bz2",
/* misc */ "bin",
].includes(e));
MySettings.localDocsFileExtensions = exts;
extsField.text = exts.join(',');
focus = false;
}
Accessible.role: Accessible.EditableText
Accessible.name: extsLabel.text
Accessible.description: extsLabel.helpText
}
}
Label {
Layout.topMargin: 15
color: theme.grayRed900
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: "Embedding"
}
Rectangle {
Layout.bottomMargin: 15
Layout.fillWidth: true
height: 1
color: theme.grayRed500
}
RowLayout {
MySettingsLabel {
text: qsTr("Use Nomic Embed API")
helpText: qsTr("Embed documents using the fast Nomic API instead of a private local model.")
}
MyCheckBox {
id: useNomicAPIBox
Component.onCompleted: {
useNomicAPIBox.checked = MySettings.localDocsUseRemoteEmbed;
}
onClicked: {
MySettings.localDocsUseRemoteEmbed = useNomicAPIBox.checked && MySettings.localDocsNomicAPIKey !== "";
}
}
}
RowLayout {
MySettingsLabel {
id: apiKeyLabel
text: qsTr("Nomic API Key")
helpText: qsTr('API key to use for Nomic Embed. Get one from the Atlas <a href="https://atlas.nomic.ai/cli-login">API keys page</a>.')
onLinkActivated: function(link) { Qt.openUrlExternally(link) }
}
MyTextField {
id: apiKeyField
property bool isValid: validate()
onTextChanged: { isValid = validate(); }
function validate() { return /^(nk-[a-zA-Z0-9_-]{43})?$/.test(apiKeyField.text); }
placeholderText: "nk-" + "X".repeat(43)
text: MySettings.localDocsNomicAPIKey
color: apiKeyField.isValid ? theme.textColor : theme.textErrorColor
font.pixelSize: theme.fontSizeLarge
Layout.alignment: Qt.AlignRight
Layout.minimumWidth: 200
enabled: useNomicAPIBox.checked
onEditingFinished: {
if (apiKeyField.isValid) {
MySettings.localDocsNomicAPIKey = apiKeyField.text;
MySettings.localDocsUseRemoteEmbed = useNomicAPIBox.checked && MySettings.localDocsNomicAPIKey !== "";
}
focus = false;
}
Accessible.role: Accessible.EditableText
Accessible.name: apiKeyLabel.text
Accessible.description: apiKeyLabel.helpText
}
}
Label {
Layout.topMargin: 15
color: theme.grayRed900
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: "Display"
}
Rectangle {
Layout.bottomMargin: 15
Layout.fillWidth: true
height: 1
color: theme.grayRed500
}
RowLayout {
MySettingsLabel {
id: showReferencesLabel
text: qsTr("Show sources")
helpText: qsTr("Shows sources in GUI generated by localdocs")
}
MyCheckBox {
id: showReferencesBox
checked: MySettings.localDocsShowReferences
onClicked: {
MySettings.localDocsShowReferences = !MySettings.localDocsShowReferences
}
}
}
Label {
Layout.topMargin: 15
color: theme.styledTextColor
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: "Advanced"
}
Rectangle {
Layout.bottomMargin: 15
Layout.fillWidth: true
height: 1
color: theme.settingsDivider
}
MySettingsLabel {
id: warningLabel
Layout.bottomMargin: 15
Layout.fillWidth: true
color: theme.textErrorColor
wrapMode: Text.WordWrap
text: qsTr("Warning: Advanced usage only.")
helpText: qsTr("Values too large may cause localdocs failure, extremely slow responses or failure to respond at all. Roughly speaking, the {N chars x N snippets} are added to the model's context window. More info <a href=\"https://docs.gpt4all.io/gpt4all_chat.html#localdocs-beta-plugin-chat-with-your-data\">here.</a>")
onLinkActivated: function(link) { Qt.openUrlExternally(link) }
}
RowLayout {
MySettingsLabel {
id: chunkLabel
Layout.fillWidth: true
text: qsTr("Document snippet size (characters)")
helpText: qsTr("Number of characters per document snippet. Larger numbers increase likelihood of factual responses, but also result in slower generation.")
}
MyTextField {
id: chunkSizeTextField
text: MySettings.localDocsChunkSize
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.localDocsChunkSize = val
focus = false
} else {
text = MySettings.localDocsChunkSize
}
}
}
}
RowLayout {
Layout.topMargin: 15
MySettingsLabel {
id: contextItemsPerPrompt
text: qsTr("Max document snippets per prompt")
helpText: qsTr("Max best N matches of retrieved document snippets to add to the context for prompt. Larger numbers increase likelihood of factual responses, but also result in slower generation.")
}
MyTextField {
text: MySettings.localDocsRetrievalSize
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.localDocsRetrievalSize = val
focus = false
} else {
text = MySettings.localDocsRetrievalSize
}
}
}
}
Rectangle {
Layout.topMargin: 15
Layout.fillWidth: true
height: 1
color: theme.settingsDivider
}
}
}