gpt4all/gpt4all-chat/main.qml

1280 lines
48 KiB
QML
Raw Normal View History

import QtCore
2023-04-09 03:28:39 +00:00
import QtQuick
import QtQuick.Controls
2023-04-10 19:03:00 +00:00
import QtQuick.Controls.Basic
2023-04-16 05:14:42 +00:00
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
2023-04-09 03:28:39 +00:00
import llm
2023-06-22 19:44:49 +00:00
import chatlistmodel
2023-04-28 14:54:05 +00:00
import download
2023-06-22 19:44:49 +00:00
import modellist
import network
import gpt4all
import mysettings
2023-04-09 03:28:39 +00:00
Window {
id: window
width: 1920
height: 1080
minimumWidth: 1280
minimumHeight: 720
2023-04-09 03:28:39 +00:00
visible: true
title: qsTr("GPT4All v") + Qt.application.version
Theme {
id: theme
}
2023-06-22 19:44:49 +00:00
property var currentChat: ChatListModel.currentChat
2023-05-01 21:13:20 +00:00
property var chatModel: currentChat.chatModel
property bool hasSaved: false
onClosing: function(close) {
if (window.hasSaved)
return;
savingPopup.open();
2023-06-22 19:44:49 +00:00
ChatListModel.saveChats();
close.accepted = false
}
Connections {
2023-06-22 19:44:49 +00:00
target: ChatListModel
function onSaveChatsFinished() {
window.hasSaved = true;
savingPopup.close();
window.close()
}
}
2023-04-24 01:05:38 +00:00
color: theme.black
2023-04-28 14:54:05 +00:00
// Startup code
Component.onCompleted: {
startupDialogs();
2023-04-28 14:54:05 +00:00
}
Connections {
target: firstStartDialog
function onClosed() {
startupDialogs();
}
}
Connections {
target: downloadNewModels
function onClosed() {
startupDialogs();
}
}
Connections {
target: Download
function onHasNewerReleaseChanged() {
startupDialogs();
}
}
Connections {
target: currentChat
function onResponseInProgressChanged() {
if (MySettings.networkIsActive && !currentChat.responseInProgress)
Network.sendConversation(currentChat.id, getConversationJson());
}
function onModelLoadingErrorChanged() {
if (currentChat.modelLoadingError !== "")
modelLoadingErrorPopup.open()
}
}
property bool hasShownModelDownload: false
2023-07-11 22:54:26 +00:00
property bool hasShownFirstStart: false
property bool hasShownSettingsAccess: false
2023-04-28 14:54:05 +00:00
function startupDialogs() {
if (!LLM.compatHardware()) {
Network.sendNonCompatHardware();
errorCompatHardware.open();
return;
}
// check if we have access to settings and if not show an error
if (!hasShownSettingsAccess && !LLM.hasSettingsAccess()) {
errorSettingsAccess.open();
hasShownSettingsAccess = true;
return;
}
2023-04-28 14:54:05 +00:00
// check for first time start of this version
2023-07-11 22:54:26 +00:00
if (!hasShownFirstStart && Download.isFirstStart()) {
2023-04-28 14:54:05 +00:00
firstStartDialog.open();
2023-07-11 22:54:26 +00:00
hasShownFirstStart = true;
2023-04-28 14:54:05 +00:00
return;
}
// check for any current models and if not, open download dialog once
if (!hasShownModelDownload && ModelList.installedModels.count === 0 && !firstStartDialog.opened) {
2023-04-28 14:54:05 +00:00
downloadNewModels.open();
hasShownModelDownload = true;
2023-04-28 14:54:05 +00:00
return;
}
// check for new version
if (Download.hasNewerRelease && !firstStartDialog.opened && !downloadNewModels.opened) {
2023-04-28 14:54:05 +00:00
newVersionDialog.open();
return;
}
}
PopupDialog {
id: errorCompatHardware
anchors.centerIn: parent
shouldTimeOut: false
shouldShowBusy: false
closePolicy: Popup.NoAutoClose
modal: true
text: qsTr("<h3>Encountered an error starting up:</h3><br>")
+ qsTr("<i>\"Incompatible hardware detected.\"</i>")
+ qsTr("<br><br>Unfortunately, your CPU does not meet the minimal requirements to run ")
+ qsTr("this program. In particular, it does not support AVX intrinsics which this ")
+ qsTr("program requires to successfully run a modern large language model. ")
+ qsTr("The only solution at this time is to upgrade your hardware to a more modern CPU.")
2023-06-26 17:37:18 +00:00
+ qsTr("<br><br>See here for more information: <a href=\"https://en.wikipedia.org/wiki/Advanced_Vector_Extensions\">")
+ qsTr("https://en.wikipedia.org/wiki/Advanced_Vector_Extensions</a>")
}
PopupDialog {
id: errorSettingsAccess
anchors.centerIn: parent
shouldTimeOut: false
shouldShowBusy: false
modal: true
text: qsTr("<h3>Encountered an error starting up:</h3><br>")
+ qsTr("<i>\"Inability to access settings file.\"</i>")
+ qsTr("<br><br>Unfortunately, something is preventing the program from accessing ")
+ qsTr("the settings file. This could be caused by incorrect permissions in the local ")
+ qsTr("app config directory where the settings file is located. ")
+ qsTr("Check out our <a href=\"https://discord.gg/4M2QFmTt2k\">discord channel</a> for help.")
}
2023-04-28 14:54:05 +00:00
StartupDialog {
id: firstStartDialog
anchors.centerIn: parent
}
NewVersionDialog {
id: newVersionDialog
anchors.centerIn: parent
}
2023-05-05 14:47:05 +00:00
AboutDialog {
id: aboutDialog
anchors.centerIn: parent
2023-06-03 00:05:47 +00:00
width: Math.min(1024, window.width - (window.width * .2))
height: Math.min(600, window.height - (window.height * .2))
2023-05-05 14:47:05 +00:00
}
2023-04-18 12:39:48 +00:00
Item {
Accessible.role: Accessible.Window
Accessible.name: title
}
PopupDialog {
id: modelLoadingErrorPopup
anchors.centerIn: parent
shouldTimeOut: false
2023-06-22 19:44:49 +00:00
text: qsTr("<h3>Encountered an error loading model:</h3><br>")
+ "<i>\"" + currentChat.modelLoadingError + "\"</i>"
+ qsTr("<br><br>Model loading failures can happen for a variety of reasons, but the most common "
+ "causes include a bad file format, an incomplete or corrupted download, the wrong file "
2023-07-11 16:09:33 +00:00
+ "type, not enough system RAM or an incompatible model type. Here are some suggestions for resolving the problem:"
2023-06-22 19:44:49 +00:00
+ "<br><ul>"
+ "<li>Ensure the model file has a compatible format and type"
2023-06-22 19:44:49 +00:00
+ "<li>Check the model file is complete in the download folder"
+ "<li>You can find the download folder in the settings dialog"
+ "<li>If you've sideloaded the model ensure the file is not corrupt by checking md5sum"
2023-06-26 17:37:18 +00:00
+ "<li>Read more about what models are supported in our <a href=\"https://docs.gpt4all.io/gpt4all_chat.html\">documentation</a> for the gui"
2023-06-22 19:44:49 +00:00
+ "<li>Check out our <a href=\"https://discord.gg/4M2QFmTt2k\">discord channel</a> for help")
}
Rectangle {
id: yellowRibbon
anchors.left: parent.left
anchors.right: parent.right
anchors.top: header.bottom
height: 3
color: theme.yellowAccent
}
Rectangle {
id: titleBar
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 5
z: 200
height: 25
color: "transparent"
RowLayout {
anchors.left: parent.left
anchors.leftMargin: 30
Text {
textFormat: Text.StyledText
text: "<a href=\"https://gpt4all.io\">gpt4all.io</a> |"
horizontalAlignment: Text.AlignLeft
font.pixelSize: theme.fontSizeFixedSmall
color: theme.gray300
linkColor: hoverHandler1.hovered ? theme.yellowAccent : theme.gray300
HoverHandler { id: hoverHandler1 }
onLinkActivated: { Qt.openUrlExternally("https://gpt4all.io") }
}
Text {
textFormat: Text.StyledText
text: "<a href=\"https://github.com/nomic-ai/gpt4all\">github</a>"
horizontalAlignment: Text.AlignLeft
font.pixelSize: theme.fontSizeFixedSmall
color: theme.gray300
linkColor: hoverHandler2.hovered ? theme.yellowAccent : theme.gray300
HoverHandler { id: hoverHandler2 }
onLinkActivated: { Qt.openUrlExternally("https://github.com/nomic-ai/gpt4all") }
}
}
RowLayout {
anchors.right: parent.right
anchors.rightMargin: 30
Text {
textFormat: Text.StyledText
text: "<a href=\"https://nomic.ai\">nomic.ai</a> |"
horizontalAlignment: Text.AlignRight
font.pixelSize: theme.fontSizeFixedSmall
color: theme.gray300
linkColor: hoverHandler3.hovered ? theme.yellowAccent : theme.gray300
HoverHandler { id: hoverHandler3 }
onLinkActivated: { Qt.openUrlExternally("https://nomic.ai") }
}
Text {
textFormat: Text.StyledText
text: "<a href=\"https://twitter.com/nomic_ai\">twitter</a> |"
horizontalAlignment: Text.AlignRight
font.pixelSize: theme.fontSizeFixedSmall
color: theme.gray300
linkColor: hoverHandler4.hovered ? theme.yellowAccent : theme.gray300
HoverHandler { id: hoverHandler4 }
onLinkActivated: { Qt.openUrlExternally("https://twitter.com/nomic_ai") }
}
Text {
textFormat: Text.StyledText
text: "<a href=\"https://discord.gg/4M2QFmTt2k\">discord</a>"
horizontalAlignment: Text.AlignRight
font.pixelSize: theme.fontSizeFixedSmall
color: theme.gray300
linkColor: hoverHandler5.hovered ? theme.yellowAccent : theme.gray300
HoverHandler { id: hoverHandler5 }
onLinkActivated: { Qt.openUrlExternally("https://discord.gg/4M2QFmTt2k") }
}
}
}
Rectangle {
2023-04-11 03:34:34 +00:00
id: header
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: 100
color: theme.mainHeader
Item {
anchors.centerIn: parent
height: childrenRect.height
visible: currentChat.isModelLoaded || currentChat.modelLoadingError !== "" || currentChat.isServer
Label {
id: modelLabel
color: theme.textColor
padding: 20
2023-05-01 21:13:20 +00:00
font.pixelSize: theme.fontSizeLarger
text: ""
background: Rectangle {
color: theme.mainHeader
}
horizontalAlignment: TextInput.AlignRight
}
2023-05-22 13:01:46 +00:00
MyComboBox {
id: comboBox
implicitWidth: 375
width: window.width >= 750 ? implicitWidth : implicitWidth - ((750 - window.width))
anchors.top: modelLabel.top
anchors.bottom: modelLabel.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenterOffset: window.width >= 950 ? 0 : Math.max(-((950 - window.width) / 2), -99.5)
2023-05-11 20:46:25 +00:00
enabled: !currentChat.isServer
2023-06-22 19:44:49 +00:00
model: ModelList.installedModels
valueRole: "id"
2023-06-22 19:44:49 +00:00
textRole: "name"
property string currentModelName: ""
function updateCurrentModelName() {
var info = ModelList.modelInfo(currentChat.modelInfo.id);
comboBox.currentModelName = info.name;
}
2023-06-22 19:44:49 +00:00
Connections {
target: currentChat
function onModelInfoChanged() {
comboBox.updateCurrentModelName();
2023-06-22 19:44:49 +00:00
}
}
Connections {
target: window
function onCurrentChatChanged() {
comboBox.updateCurrentModelName();
}
}
background: Rectangle {
color: theme.mainComboBackground
radius: 10
}
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
leftPadding: 10
rightPadding: 20
text: currentChat.modelLoadingError !== ""
? qsTr("Model loading error...")
: comboBox.currentModelName
font.pixelSize: theme.fontSizeLarger
color: theme.white
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
delegate: ItemDelegate {
width: comboBox.width
contentItem: Text {
text: name
color: theme.textColor
font: comboBox.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: (index % 2 === 0 ? theme.darkContrast : theme.lightContrast)
border.width: highlighted
border.color: theme.yellowAccent
}
highlighted: comboBox.highlightedIndex === index
}
Accessible.role: Accessible.ComboBox
2023-10-22 00:15:50 +00:00
Accessible.name: qsTr("List of available models")
Accessible.description: qsTr("The top item is the current model")
onActivated: function (index) {
2023-05-01 21:13:20 +00:00
currentChat.stopGenerating()
currentChat.reset();
currentChat.modelInfo = ModelList.modelInfo(comboBox.valueAt(index))
}
}
2023-04-11 03:34:34 +00:00
}
2023-04-09 03:28:39 +00:00
Item {
anchors.centerIn: parent
visible: ModelList.installedModels.count
&& !currentChat.isModelLoaded
&& currentChat.modelLoadingError === ""
&& !currentChat.isServer
width: childrenRect.width
height: childrenRect.height
Row {
spacing: 5
MyBusyIndicator {
anchors.verticalCenter: parent.verticalCenter
running: parent.visible
Accessible.role: Accessible.Animation
Accessible.name: qsTr("Busy indicator")
Accessible.description: qsTr("loading model...")
}
Label {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Loading model...")
2023-08-07 17:54:13 +00:00
font.pixelSize: theme.fontSizeLarge
color: theme.oppositeTextColor
}
}
}
2023-04-11 03:34:34 +00:00
}
2023-04-23 10:58:07 +00:00
SettingsDialog {
2023-04-16 05:14:42 +00:00
id: settingsDialog
anchors.centerIn: parent
width: Math.min(1280, window.width - (window.width * .1))
height: window.height - (window.height * .1)
onDownloadClicked: {
downloadNewModels.showEmbeddingModels = true
downloadNewModels.open()
}
2023-04-16 05:14:42 +00:00
}
2023-04-11 03:34:34 +00:00
Button {
id: drawerButton
anchors.left: parent.left
2023-04-11 03:34:34 +00:00
anchors.top: parent.top
anchors.topMargin: 42.5
2023-04-11 03:34:34 +00:00
anchors.leftMargin: 30
width: 40
2023-04-11 03:34:34 +00:00
height: 40
z: 200
padding: 15
2023-04-18 12:39:48 +00:00
Accessible.role: Accessible.ButtonMenu
Accessible.name: qsTr("Main menu")
Accessible.description: qsTr("Navigation drawer with options")
2023-04-18 12:39:48 +00:00
2023-04-11 03:34:34 +00:00
background: Item {
anchors.centerIn: parent
width: 30
height: 30
2023-04-11 03:34:34 +00:00
Rectangle {
id: bar1
color: drawerButton.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight
2023-04-11 03:34:34 +00:00
width: parent.width
height: 6
2023-04-11 03:34:34 +00:00
radius: 2
antialiasing: true
}
Rectangle {
id: bar2
anchors.centerIn: parent
color: drawerButton.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight
2023-04-11 03:34:34 +00:00
width: parent.width
height: 6
2023-04-11 03:34:34 +00:00
radius: 2
antialiasing: true
}
Rectangle {
id: bar3
anchors.bottom: parent.bottom
color: drawerButton.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight
2023-04-11 03:34:34 +00:00
width: parent.width
height: 6
2023-04-11 03:34:34 +00:00
radius: 2
antialiasing: true
}
}
onClicked: {
drawer.visible = !drawer.visible
}
}
NetworkDialog {
id: networkDialog
anchors.centerIn: parent
width: Math.min(1024, window.width - (window.width * .2))
height: Math.min(600, window.height - (window.height * .2))
Item {
Accessible.role: Accessible.Dialog
Accessible.name: qsTr("Network dialog")
Accessible.description: qsTr("opt-in to share feedback/conversations")
}
}
MyToolButton {
id: networkButton
backgroundColor: theme.iconBackgroundLight
2023-04-09 03:28:39 +00:00
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.rightMargin: 30
2023-04-24 04:25:57 +00:00
width: 40
2023-04-24 04:31:39 +00:00
height: 40
z: 200
padding: 15
toggled: MySettings.networkIsActive
source: "qrc:/gpt4all/icons/network.svg"
Accessible.name: qsTr("Network")
Accessible.description: qsTr("Reveals a dialogue where you can opt-in for sharing data over network")
onClicked: {
if (MySettings.networkIsActive) {
MySettings.networkIsActive = false
Network.sendNetworkToggled(false);
} else
networkDialog.open()
}
}
Connections {
target: Network
function onHealthCheckFailed(code) {
healthCheckFailed.open();
}
}
CollectionsDialog {
id: collectionsDialog
anchors.centerIn: parent
onAddRemoveClicked: {
settingsDialog.pageToDisplay = 2;
settingsDialog.open();
}
}
MyToolButton {
id: collectionsButton
backgroundColor: theme.iconBackgroundLight
anchors.right: networkButton.left
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.rightMargin: 10
2023-04-24 04:25:57 +00:00
width: 40
2023-04-11 03:34:34 +00:00
height: 40
z: 200
padding: 15
toggled: currentChat.collectionList.length
source: "qrc:/gpt4all/icons/db.svg"
Accessible.name: qsTr("Add documents")
Accessible.description: qsTr("add collections of documents to the chat")
onClicked: {
collectionsDialog.open()
}
}
MyToolButton {
id: settingsButton
backgroundColor: theme.iconBackgroundLight
anchors.right: collectionsButton.left
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.rightMargin: 10
width: 40
height: 40
z: 200
padding: 15
source: "qrc:/gpt4all/icons/settings.svg"
Accessible.name: qsTr("Settings")
Accessible.description: qsTr("Reveals a dialogue with settings")
2023-04-18 12:39:48 +00:00
onClicked: {
settingsDialog.open()
}
}
PopupDialog {
id: copyMessage
anchors.centerIn: parent
text: qsTr("Conversation copied to clipboard.")
2023-08-07 17:54:13 +00:00
font.pixelSize: theme.fontSizeLarge
}
PopupDialog {
id: copyCodeMessage
anchors.centerIn: parent
text: qsTr("Code copied to clipboard.")
2023-08-07 17:54:13 +00:00
font.pixelSize: theme.fontSizeLarge
}
PopupDialog {
id: healthCheckFailed
anchors.centerIn: parent
text: qsTr("Connection to datalake failed.")
2023-08-07 17:54:13 +00:00
font.pixelSize: theme.fontSizeLarge
}
PopupDialog {
id: recalcPopup
anchors.centerIn: parent
shouldTimeOut: false
shouldShowBusy: true
text: qsTr("Recalculating context.")
2023-08-07 17:54:13 +00:00
font.pixelSize: theme.fontSizeLarge
Connections {
target: currentChat
function onRecalcChanged() {
if (currentChat.isRecalc)
recalcPopup.open()
else
recalcPopup.close()
}
}
}
PopupDialog {
id: savingPopup
anchors.centerIn: parent
shouldTimeOut: false
shouldShowBusy: true
text: qsTr("Saving chats.")
2023-08-07 17:54:13 +00:00
font.pixelSize: theme.fontSizeLarge
}
MyToolButton {
id: copyButton
backgroundColor: theme.iconBackgroundLight
anchors.right: settingsButton.left
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.rightMargin: 10
2023-04-24 04:25:57 +00:00
width: 40
height: 40
z: 200
padding: 15
source: "qrc:/gpt4all/icons/copy.svg"
Accessible.name: qsTr("Copy")
2023-04-18 12:39:48 +00:00
Accessible.description: qsTr("Copy the conversation to the clipboard")
2023-04-11 12:54:57 +00:00
TextEdit{
id: copyEdit
visible: false
}
onClicked: {
var conversation = getConversation()
2023-04-11 12:54:57 +00:00
copyEdit.text = conversation
copyEdit.selectAll()
copyEdit.copy()
copyMessage.open()
2023-04-11 12:54:57 +00:00
}
}
function getConversation() {
var conversation = "";
for (var i = 0; i < chatModel.count; i++) {
var item = chatModel.get(i)
var string = item.name;
var isResponse = item.name === qsTr("Response: ")
string += chatModel.get(i).value
if (isResponse && item.stopped)
string += " <stopped>"
string += "\n"
conversation += string
}
return conversation
}
function getConversationJson() {
var str = "{\"conversation\": [";
for (var i = 0; i < chatModel.count; i++) {
var item = chatModel.get(i)
var isResponse = item.name === qsTr("Response: ")
str += "{\"content\": ";
str += JSON.stringify(item.value)
str += ", \"role\": \"" + (isResponse ? "assistant" : "user") + "\"";
if (isResponse && item.thumbsUpState !== item.thumbsDownState)
str += ", \"rating\": \"" + (item.thumbsUpState ? "positive" : "negative") + "\"";
if (isResponse && item.newResponse !== "")
2023-04-26 02:49:23 +00:00
str += ", \"edited_content\": " + JSON.stringify(item.newResponse);
if (isResponse && item.stopped)
str += ", \"stopped\": \"true\""
if (!isResponse)
str += "},"
else
str += ((i < chatModel.count - 1) ? "}," : "}")
}
return str + "]}"
}
MyToolButton {
2023-04-11 12:54:57 +00:00
id: resetContextButton
backgroundColor: theme.iconBackgroundLight
2023-04-11 12:54:57 +00:00
anchors.right: copyButton.left
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.rightMargin: 10
2023-04-24 04:25:57 +00:00
width: 40
2023-04-11 12:54:57 +00:00
height: 40
z: 200
padding: 15
source: "qrc:/gpt4all/icons/regenerate.svg"
2023-04-11 12:54:57 +00:00
2023-04-18 12:39:48 +00:00
Accessible.name: text
Accessible.description: qsTr("Reset the context and erase current conversation")
2023-04-18 12:39:48 +00:00
2023-04-11 03:34:34 +00:00
onClicked: {
Network.sendResetContext(chatModel.count)
2023-05-01 21:13:20 +00:00
currentChat.reset();
currentChat.processSystemPrompt();
2023-04-11 03:34:34 +00:00
}
}
Dialog {
id: checkForUpdatesError
anchors.centerIn: parent
modal: false
padding: 20
2023-04-11 03:34:34 +00:00
Text {
horizontalAlignment: Text.AlignJustify
text: qsTr("ERROR: Update system could not find the MaintenanceTool used<br>
to check for updates!<br><br>
Did you install this application using the online installer? If so,<br>
the MaintenanceTool executable should be located one directory<br>
above where this application resides on your filesystem.<br><br>
If you can't start it manually, then I'm afraid you'll have to<br>
reinstall.")
color: theme.textErrorColor
2023-08-07 17:54:13 +00:00
font.pixelSize: theme.fontSizeLarge
2023-04-18 12:39:48 +00:00
Accessible.role: Accessible.Dialog
Accessible.name: text
Accessible.description: qsTr("Error dialog")
2023-04-11 03:34:34 +00:00
}
background: Rectangle {
anchors.fill: parent
color: theme.containerBackground
2023-04-11 03:34:34 +00:00
border.width: 1
border.color: theme.dialogBorder
2023-04-11 03:34:34 +00:00
radius: 10
}
}
2023-04-19 01:10:06 +00:00
ModelDownloaderDialog {
id: downloadNewModels
anchors.centerIn: parent
width: Math.min(1280, window.width - (window.width * .1))
height: window.height - (window.height * .1)
2023-04-19 01:10:06 +00:00
Item {
Accessible.role: Accessible.Dialog
Accessible.name: qsTr("Download new models")
2023-04-19 01:10:06 +00:00
Accessible.description: qsTr("Dialog for downloading new models")
}
}
ChatDrawer {
2023-04-11 03:34:34 +00:00
id: drawer
y: header.height + yellowRibbon.height
width: Math.min(600, 0.3 * window.width)
2023-04-11 03:34:34 +00:00
height: window.height - y
onDownloadClicked: {
downloadNewModels.showEmbeddingModels = false
downloadNewModels.open()
}
2023-05-05 14:47:05 +00:00
onAboutClicked: {
aboutDialog.open()
}
2023-04-11 03:34:34 +00:00
}
2023-05-25 14:40:10 +00:00
PopupDialog {
id: referenceContextDialog
anchors.centerIn: parent
shouldTimeOut: false
shouldShowBusy: false
modal: true
}
2023-04-11 03:34:34 +00:00
Rectangle {
id: conversation
color: theme.containerBackground
2023-04-11 03:34:34 +00:00
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.top: yellowRibbon.bottom
2023-04-09 03:28:39 +00:00
ScrollView {
id: scrollView
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
2023-05-11 20:46:25 +00:00
anchors.bottom: !currentChat.isServer ? textInputView.top : parent.bottom
anchors.bottomMargin: !currentChat.isServer ? 30 : 0
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
2023-04-09 03:28:39 +00:00
Rectangle {
anchors.fill: parent
color: currentChat.isServer ? theme.black : theme.containerBackground
Rectangle {
id: homePage
color: "transparent"//theme.green200
anchors.fill: parent
visible: (ModelList.installedModels.count === 0 || chatModel.count === 0) && !currentChat.isServer
ColumnLayout {
anchors.centerIn: parent
spacing: 0
Text {
Layout.alignment: Qt.AlignHCenter
text: qsTr("GPT4All")
color: theme.titleTextColor
font.pixelSize: theme.fontSizeLargest + 15
font.bold: true
horizontalAlignment: Qt.AlignHCenter
wrapMode: Text.WordWrap
}
2023-07-09 19:51:59 +00:00
Text {
Layout.alignment: Qt.AlignHCenter
textFormat: Text.StyledText
text: qsTr(
"<ul>
<li>Run privacy-aware local chatbots.
<li>No internet required to use.
<li>CPU and GPU acceleration.
<li>Chat with your local data and documents.
<li>Built by Nomic AI and forever open-source.
</ul>
")
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
wrapMode: Text.WordWrap
}
RowLayout {
spacing: 10
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30
MySlug {
text: "MISTRAL"
color: theme.red600
}
MySlug {
text: "FALCON"
color: theme.green600
}
MySlug {
text: "LLAMA"
color: theme.purple500
}
MySlug {
text: "LLAMA2"
color: theme.red400
}
MySlug {
text: "MPT"
color: theme.green700
}
MySlug {
text: "REPLIT"
color: theme.yellow700
}
MySlug {
text: "STARCODER"
color: theme.purple400
}
MySlug {
text: "SBERT"
color: theme.yellow600
}
MySlug {
text: "GPT-J"
color: theme.gray600
}
}
MyButton {
id: downloadButton
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 40
text: qsTr("Download models")
fontPixelSize: theme.fontSizeLargest + 10
padding: 18
leftPadding: 50
Image {
id: image
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 15
width: 24
height: 24
mipmap: true
source: "qrc:/gpt4all/icons/download.svg"
}
ColorOverlay {
anchors.fill: image
source: image
color: theme.yellowAccent
}
onClicked: {
downloadNewModels.open();
}
}
2023-07-09 19:51:59 +00:00
}
}
2023-04-09 03:28:39 +00:00
ListView {
id: listView
visible: ModelList.installedModels.count !== 0 && chatModel.count !== 0
2023-04-09 03:28:39 +00:00
anchors.fill: parent
model: chatModel
ScrollBar.vertical: ScrollBar {
parent: listView.parent
anchors.top: listView.top
anchors.left: listView.right
anchors.bottom: listView.bottom
}
2023-04-18 12:39:48 +00:00
Accessible.role: Accessible.List
Accessible.name: qsTr("Conversation with the model")
Accessible.description: qsTr("prompt / response pairs from the conversation")
2023-04-18 12:39:48 +00:00
2023-04-09 03:28:39 +00:00
delegate: TextArea {
id: myTextArea
text: value + references
width: listView.width
color: theme.textColor
2023-04-09 03:28:39 +00:00
wrapMode: Text.WordWrap
textFormat: TextEdit.PlainText
2023-04-09 03:28:39 +00:00
focus: false
readOnly: true
2023-04-23 15:23:02 +00:00
font.pixelSize: theme.fontSizeLarge
cursorVisible: currentResponse ? currentChat.responseInProgress : false
2023-04-09 03:28:39 +00:00
cursorPosition: text.length
background: Rectangle {
opacity: 1.0
2023-05-11 20:46:25 +00:00
color: name === qsTr("Response: ")
? (currentChat.isServer ? theme.black : theme.lightContrast)
: (currentChat.isServer ? theme.white : theme.darkContrast)
2023-04-09 03:28:39 +00:00
}
2023-06-11 17:24:56 +00:00
TapHandler {
id: tapHandler
onTapped: function(eventPoint, button) {
var clickedPos = myTextArea.positionAt(eventPoint.position.x, eventPoint.position.y);
var link = responseText.getLinkAtPosition(clickedPos);
2023-06-11 17:24:56 +00:00
if (link.startsWith("context://")) {
var integer = parseInt(link.split("://")[1]);
referenceContextDialog.text = referencesContext[integer - 1];
referenceContextDialog.open();
} else {
var success = responseText.tryCopyAtPosition(clickedPos);
if (success)
copyCodeMessage.open();
2023-06-11 17:24:56 +00:00
}
}
}
ResponseText {
id: responseText
}
Component.onCompleted: {
responseText.setLinkColor(theme.linkColor);
responseText.setHeaderColor(name === qsTr("Response: ") ? theme.darkContrast : theme.lightContrast);
responseText.textDocument = textDocument
}
2023-04-18 12:39:48 +00:00
Accessible.role: Accessible.Paragraph
Accessible.name: name
Accessible.description: name === qsTr("Response: ") ? "The response by the model" : "The prompt by the user"
topPadding: 20
bottomPadding: 20
leftPadding: 70
rightPadding: 100
2023-04-09 03:28:39 +00:00
Item {
anchors.left: parent.left
anchors.leftMargin: 60
y: parent.topPadding + (parent.positionToRectangle(0).height / 2) - (height / 2)
visible: (currentResponse ? true : false) && value === "" && currentChat.responseInProgress
width: childrenRect.width
height: childrenRect.height
Row {
spacing: 5
MyBusyIndicator {
anchors.verticalCenter: parent.verticalCenter
running: (currentResponse ? true : false) && value === "" && currentChat.responseInProgress
Accessible.role: Accessible.Animation
Accessible.name: qsTr("Busy indicator")
Accessible.description: qsTr("The model is thinking")
}
Label {
anchors.verticalCenter: parent.verticalCenter
color: theme.mutedTextColor
2023-10-29 22:34:42 +00:00
text: {
switch (currentChat.responseState) {
case Chat.ResponseStopped: return qsTr("response stopped ...");
case Chat.LocalDocsRetrieval: return qsTr("retrieving localdocs: ") + currentChat.collectionList.join(", ") + " ...";
case Chat.LocalDocsProcessing: return qsTr("searching localdocs: ") + currentChat.collectionList.join(", ") + " ...";
case Chat.PromptProcessing: return qsTr("processing ...")
case Chat.ResponseGeneration: return qsTr("generating response ...");
2023-10-29 22:34:42 +00:00
default: return ""; // handle unexpected values
}
}
}
}
}
2023-04-09 03:28:39 +00:00
Rectangle {
anchors.left: parent.left
anchors.leftMargin: 20
y: parent.topPadding + (parent.positionToRectangle(0).height / 2) - (height / 2)
2023-04-09 03:28:39 +00:00
width: 30
height: 30
radius: 5
color: name === qsTr("Response: ") ? theme.assistantColor : theme.userColor
2023-04-09 03:28:39 +00:00
Text {
anchors.centerIn: parent
text: name === qsTr("Response: ") ? "R" : "P"
color: "white"
}
}
ThumbsDownDialog {
id: thumbsDownDialog
property point globalPoint: mapFromItem(window,
window.width / 2 - width / 2,
window.height / 2 - height / 2)
x: globalPoint.x
y: globalPoint.y
property string text: value
2023-04-27 15:44:41 +00:00
response: newResponse === undefined || newResponse === "" ? text : newResponse
onAccepted: {
var responseHasChanged = response !== text && response !== newResponse
if (thumbsDownState && !thumbsUpState && !responseHasChanged)
return
chatModel.updateNewResponse(index, response)
chatModel.updateThumbsUpState(index, false)
chatModel.updateThumbsDownState(index, true)
2023-05-01 21:13:20 +00:00
Network.sendConversation(currentChat.id, getConversationJson());
}
}
Column {
visible: name === qsTr("Response: ") &&
(!currentResponse || !currentChat.responseInProgress) && MySettings.networkIsActive
anchors.right: parent.right
anchors.rightMargin: 20
y: parent.topPadding + (parent.positionToRectangle(0).height / 2) - (height / 2)
spacing: 10
Item {
width: childrenRect.width
height: childrenRect.height
MyToolButton {
id: thumbsUp
width: 30
height: 30
opacity: thumbsUpState || thumbsUpState == thumbsDownState ? 1.0 : 0.2
source: "qrc:/gpt4all/icons/thumbs_up.svg"
2023-05-23 22:19:36 +00:00
Accessible.name: qsTr("Thumbs up")
Accessible.description: qsTr("Gives a thumbs up to the response")
onClicked: {
if (thumbsUpState && !thumbsDownState)
return
chatModel.updateNewResponse(index, "")
chatModel.updateThumbsUpState(index, true)
chatModel.updateThumbsDownState(index, false)
2023-05-01 21:13:20 +00:00
Network.sendConversation(currentChat.id, getConversationJson());
}
}
MyToolButton {
id: thumbsDown
anchors.top: thumbsUp.top
anchors.topMargin: 10
anchors.left: thumbsUp.right
anchors.leftMargin: 2
width: 30
height: 30
checked: thumbsDownState
opacity: thumbsDownState || thumbsUpState == thumbsDownState ? 1.0 : 0.2
transform: [
Matrix4x4 {
matrix: Qt.matrix4x4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
},
Translate {
x: thumbsDown.width
}
]
source: "qrc:/gpt4all/icons/thumbs_down.svg"
2023-05-23 22:19:36 +00:00
Accessible.name: qsTr("Thumbs down")
Accessible.description: qsTr("Opens thumbs down dialog")
onClicked: {
thumbsDownDialog.open()
}
}
}
}
2023-04-09 03:28:39 +00:00
}
property bool shouldAutoScroll: true
property bool isAutoScrolling: false
Connections {
target: currentChat
2023-04-09 03:28:39 +00:00
function onResponseChanged() {
if (listView.shouldAutoScroll) {
listView.isAutoScrolling = true
listView.positionViewAtEnd()
listView.isAutoScrolling = false
}
}
}
onContentYChanged: {
if (!isAutoScrolling)
shouldAutoScroll = atYEnd
}
Component.onCompleted: {
shouldAutoScroll = true
positionViewAtEnd()
}
footer: Item {
id: bottomPadding
width: parent.width
height: 60
}
}
Image {
2023-06-22 19:44:49 +00:00
visible: currentChat.isServer || currentChat.modelInfo.isChatGPT
anchors.fill: parent
sourceSize.width: 1024
sourceSize.height: 1024
fillMode: Image.PreserveAspectFit
opacity: 0.15
source: "qrc:/gpt4all/icons/network.svg"
}
2023-04-09 03:28:39 +00:00
}
}
MyButton {
id: myButton
2023-05-11 20:46:25 +00:00
visible: chatModel.count && !currentChat.isServer
textColor: theme.textColor
2023-04-09 03:28:39 +00:00
Image {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 15
2023-05-01 21:13:20 +00:00
source: currentChat.responseInProgress ? "qrc:/gpt4all/icons/stop_generating.svg" : "qrc:/gpt4all/icons/regenerate.svg"
2023-04-09 03:28:39 +00:00
}
2023-04-09 11:38:25 +00:00
leftPadding: 50
2023-04-09 03:28:39 +00:00
onClicked: {
var index = Math.max(0, chatModel.count - 1);
var listElement = chatModel.get(index);
2023-05-01 21:13:20 +00:00
if (currentChat.responseInProgress) {
listElement.stopped = true
2023-05-01 21:13:20 +00:00
currentChat.stopGenerating()
} else {
2023-05-01 21:13:20 +00:00
currentChat.regenerateResponse()
2023-04-09 03:28:39 +00:00
if (chatModel.count) {
if (listElement.name === qsTr("Response: ")) {
chatModel.updateCurrentResponse(index, true);
chatModel.updateStopped(index, false);
chatModel.updateThumbsUpState(index, false);
chatModel.updateThumbsDownState(index, false);
chatModel.updateNewResponse(index, "");
currentChat.prompt(listElement.prompt)
2023-04-09 03:28:39 +00:00
}
}
}
}
background: Rectangle {
border.color: theme.conversationButtonBorder
border.width: 2
radius: 10
color: myButton.hovered ? theme.conversationButtonBackgroundHovered : theme.conversationButtonBackground
}
2023-04-20 12:31:33 +00:00
anchors.bottom: textInputView.top
anchors.horizontalCenter: textInputView.horizontalCenter
anchors.bottomMargin: 20
2023-04-09 03:28:39 +00:00
padding: 15
text: currentChat.responseInProgress ? qsTr("Stop generating") : qsTr("Regenerate response")
Accessible.description: qsTr("Controls generation of the response")
2023-04-09 03:28:39 +00:00
}
Text {
2023-09-13 19:24:33 +00:00
id: device
anchors.bottom: textInputView.top
anchors.bottomMargin: 20
anchors.right: parent.right
anchors.rightMargin: 30
color: theme.mutedTextColor
2023-09-13 19:48:55 +00:00
visible: currentChat.tokenSpeed !== ""
text: qsTr("Speed: ") + currentChat.tokenSpeed + "<br>" + qsTr("Device: ") + currentChat.device + currentChat.fallbackReason
2023-08-07 17:54:13 +00:00
font.pixelSize: theme.fontSizeLarge
}
RectangularGlow {
id: effect
visible: !currentChat.isServer
anchors.fill: textInputView
glowRadius: 50
spread: 0
color: theme.sendGlow
cornerRadius: 10
opacity: 0.1
}
2023-04-20 12:31:33 +00:00
ScrollView {
id: textInputView
2023-04-09 03:28:39 +00:00
anchors.left: parent.left
anchors.right: parent.right
2023-04-09 03:28:39 +00:00
anchors.bottom: parent.bottom
anchors.margins: 30
2023-04-20 12:31:33 +00:00
height: Math.min(contentHeight, 200)
2023-05-11 20:46:25 +00:00
visible: !currentChat.isServer
MyTextArea {
2023-04-20 12:31:33 +00:00
id: textInput
color: theme.textColor
topPadding: 30
bottomPadding: 30
leftPadding: 20
rightPadding: 40
2023-05-11 20:46:25 +00:00
enabled: currentChat.isModelLoaded && !currentChat.isServer
font.pixelSize: theme.fontSizeLarger
2023-04-20 12:31:33 +00:00
placeholderText: qsTr("Send a message...")
Accessible.role: Accessible.EditableText
Accessible.name: placeholderText
Accessible.description: qsTr("Send messages/prompts to the model")
Keys.onReturnPressed: (event)=> {
2023-04-20 12:31:33 +00:00
if (event.modifiers & Qt.ControlModifier || event.modifiers & Qt.ShiftModifier)
event.accepted = false;
else {
2023-04-20 12:31:33 +00:00
editingFinished();
sendMessage()
}
2023-04-09 03:28:39 +00:00
}
function sendMessage() {
2023-04-20 12:31:33 +00:00
if (textInput.text === "")
return
2023-05-01 21:13:20 +00:00
currentChat.stopGenerating()
currentChat.newPromptResponsePair(textInput.text);
currentChat.prompt(textInput.text,
MySettings.promptTemplate,
MySettings.maxLength,
MySettings.topK,
MySettings.topP,
MySettings.temperature,
MySettings.promptBatchSize,
MySettings.repeatPenalty,
MySettings.repeatPenaltyTokens)
2023-04-20 12:31:33 +00:00
textInput.text = ""
2023-04-09 03:28:39 +00:00
}
2023-04-20 12:31:33 +00:00
}
}
2023-04-09 03:28:39 +00:00
MyToolButton {
backgroundColor: theme.sendButtonBackground
backgroundColorHovered: theme.sendButtonBackgroundHovered
2023-04-20 12:31:33 +00:00
anchors.right: textInputView.right
anchors.verticalCenter: textInputView.verticalCenter
anchors.rightMargin: 15
width: 30
height: 30
2023-05-11 20:46:25 +00:00
visible: !currentChat.isServer
source: "qrc:/gpt4all/icons/send_message.svg"
Accessible.name: qsTr("Send message")
2023-04-20 12:31:33 +00:00
Accessible.description: qsTr("Sends the message/prompt contained in textfield to the model")
onClicked: {
textInput.sendMessage()
2023-04-09 03:28:39 +00:00
}
}
}
}