mirror of
https://github.com/nomic-ai/gpt4all
synced 2024-11-08 07:10:32 +00:00
01e582f15b
Limitations: 1) Context is not restored for gpt-j models 2) When you switch between different model types in an existing chat the context and all the conversation is lost 3) The settings are not chat or conversation specific 4) The sizes of the chat persisted files are very large due to how much data the llama.cpp backend tries to persist. Need to investigate how we can shrink this.
383 lines
16 KiB
QML
383 lines
16 KiB
QML
import QtCore
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Controls.Basic
|
|
import QtQuick.Dialogs
|
|
import QtQuick.Layouts
|
|
import download
|
|
import llm
|
|
import network
|
|
|
|
Dialog {
|
|
id: modelDownloaderDialog
|
|
modal: true
|
|
opacity: 0.9
|
|
closePolicy: LLM.chatListModel.currentChat.modelList.length === 0 ? Popup.NoAutoClose : (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
|
|
background: Rectangle {
|
|
anchors.fill: parent
|
|
anchors.margins: -20
|
|
color: theme.backgroundDarkest
|
|
border.width: 1
|
|
border.color: theme.dialogBorder
|
|
radius: 10
|
|
}
|
|
|
|
onOpened: {
|
|
Network.sendModelDownloaderDialog();
|
|
}
|
|
|
|
property string defaultModelPath: Download.defaultLocalModelsPath()
|
|
property alias modelPath: settings.modelPath
|
|
Settings {
|
|
id: settings
|
|
property string modelPath: modelDownloaderDialog.defaultModelPath
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
Download.downloadLocalModelsPath = settings.modelPath
|
|
}
|
|
|
|
Component.onDestruction: {
|
|
settings.sync()
|
|
}
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: 20
|
|
spacing: 30
|
|
|
|
Label {
|
|
id: listLabel
|
|
text: "Available Models:"
|
|
Layout.alignment: Qt.AlignLeft
|
|
Layout.fillWidth: true
|
|
color: theme.textColor
|
|
}
|
|
|
|
ScrollView {
|
|
id: scrollView
|
|
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
clip: true
|
|
|
|
ListView {
|
|
id: modelList
|
|
model: Download.modelList
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
|
|
delegate: Item {
|
|
id: delegateItem
|
|
width: modelList.width
|
|
height: modelName.height + modelName.padding
|
|
+ description.height + description.padding
|
|
objectName: "delegateItem"
|
|
property bool downloading: false
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: index % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter
|
|
}
|
|
|
|
Text {
|
|
id: modelName
|
|
objectName: "modelName"
|
|
property string filename: modelData.filename
|
|
text: filename.slice(5, filename.length - 4)
|
|
padding: 20
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
font.bold: modelData.isDefault || modelData.bestGPTJ || modelData.bestLlama
|
|
color: theme.assistantColor
|
|
Accessible.role: Accessible.Paragraph
|
|
Accessible.name: qsTr("Model file")
|
|
Accessible.description: qsTr("Model file to be downloaded")
|
|
}
|
|
|
|
Text {
|
|
id: description
|
|
text: " - " + modelData.description
|
|
leftPadding: 20
|
|
rightPadding: 20
|
|
anchors.top: modelName.bottom
|
|
anchors.left: modelName.left
|
|
anchors.right: parent.right
|
|
wrapMode: Text.WordWrap
|
|
color: theme.textColor
|
|
Accessible.role: Accessible.Paragraph
|
|
Accessible.name: qsTr("Description")
|
|
Accessible.description: qsTr("The description of the file")
|
|
}
|
|
|
|
Text {
|
|
id: isDefault
|
|
text: qsTr("(default)")
|
|
visible: modelData.isDefault
|
|
anchors.top: modelName.top
|
|
anchors.left: modelName.right
|
|
padding: 20
|
|
color: theme.textColor
|
|
Accessible.role: Accessible.Paragraph
|
|
Accessible.name: qsTr("Default file")
|
|
Accessible.description: qsTr("Whether the file is the default model")
|
|
}
|
|
|
|
Text {
|
|
text: modelData.filesize
|
|
anchors.top: modelName.top
|
|
anchors.left: isDefault.visible ? isDefault.right : modelName.right
|
|
padding: 20
|
|
color: theme.textColor
|
|
Accessible.role: Accessible.Paragraph
|
|
Accessible.name: qsTr("File size")
|
|
Accessible.description: qsTr("The size of the file")
|
|
}
|
|
|
|
Label {
|
|
id: speedLabel
|
|
anchors.top: modelName.top
|
|
anchors.right: itemProgressBar.left
|
|
padding: 20
|
|
objectName: "speedLabel"
|
|
color: theme.textColor
|
|
text: ""
|
|
visible: downloading
|
|
Accessible.role: Accessible.Paragraph
|
|
Accessible.name: qsTr("Download speed")
|
|
Accessible.description: qsTr("Download speed in bytes/kilobytes/megabytes per second")
|
|
}
|
|
|
|
ProgressBar {
|
|
id: itemProgressBar
|
|
objectName: "itemProgressBar"
|
|
anchors.top: modelName.top
|
|
anchors.right: downloadButton.left
|
|
anchors.topMargin: 20
|
|
anchors.rightMargin: 20
|
|
width: 100
|
|
visible: downloading
|
|
background: Rectangle {
|
|
implicitWidth: 200
|
|
implicitHeight: 30
|
|
color: theme.backgroundDarkest
|
|
radius: 3
|
|
}
|
|
|
|
contentItem: Item {
|
|
implicitWidth: 200
|
|
implicitHeight: 25
|
|
|
|
Rectangle {
|
|
width: itemProgressBar.visualPosition * parent.width
|
|
height: parent.height
|
|
radius: 2
|
|
color: theme.assistantColor
|
|
}
|
|
}
|
|
Accessible.role: Accessible.ProgressBar
|
|
Accessible.name: qsTr("Download progressBar")
|
|
Accessible.description: qsTr("Shows the progress made in the download")
|
|
}
|
|
|
|
Item {
|
|
visible: modelData.calcHash
|
|
anchors.top: modelName.top
|
|
anchors.right: parent.right
|
|
|
|
Label {
|
|
id: calcHashLabel
|
|
anchors.right: busyCalcHash.left
|
|
padding: 20
|
|
objectName: "calcHashLabel"
|
|
color: theme.textColor
|
|
text: qsTr("Calculating MD5...")
|
|
Accessible.role: Accessible.Paragraph
|
|
Accessible.name: text
|
|
Accessible.description: qsTr("Whether the file hash is being calculated")
|
|
}
|
|
|
|
BusyIndicator {
|
|
id: busyCalcHash
|
|
anchors.right: parent.right
|
|
padding: 20
|
|
running: modelData.calcHash
|
|
Accessible.role: Accessible.Animation
|
|
Accessible.name: qsTr("Busy indicator")
|
|
Accessible.description: qsTr("Displayed when the file hash is being calculated")
|
|
}
|
|
}
|
|
|
|
Label {
|
|
id: installedLabel
|
|
anchors.top: modelName.top
|
|
anchors.right: parent.right
|
|
padding: 20
|
|
objectName: "installedLabel"
|
|
color: theme.textColor
|
|
text: qsTr("Already installed")
|
|
visible: modelData.installed
|
|
Accessible.role: Accessible.Paragraph
|
|
Accessible.name: text
|
|
Accessible.description: qsTr("Whether the file is already installed on your system")
|
|
}
|
|
|
|
Button {
|
|
id: downloadButton
|
|
contentItem: Text {
|
|
color: theme.textColor
|
|
text: downloading ? "Cancel" : "Download"
|
|
}
|
|
anchors.top: modelName.top
|
|
anchors.right: parent.right
|
|
anchors.topMargin: 15
|
|
anchors.rightMargin: 20
|
|
visible: !modelData.installed && !modelData.calcHash
|
|
onClicked: {
|
|
if (!downloading) {
|
|
downloading = true;
|
|
Download.downloadModel(modelData.filename);
|
|
} else {
|
|
downloading = false;
|
|
Download.cancelDownload(modelData.filename);
|
|
}
|
|
}
|
|
background: Rectangle {
|
|
opacity: .5
|
|
border.color: theme.backgroundLightest
|
|
border.width: 1
|
|
radius: 10
|
|
color: theme.backgroundLight
|
|
}
|
|
Accessible.role: Accessible.Button
|
|
Accessible.name: text
|
|
Accessible.description: qsTr("Cancel/Download button to stop/start the download")
|
|
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
Download.downloadProgress.connect(updateProgress);
|
|
Download.downloadFinished.connect(resetProgress);
|
|
}
|
|
|
|
property var lastUpdate: ({})
|
|
|
|
function updateProgress(bytesReceived, bytesTotal, modelName) {
|
|
let currentTime = new Date().getTime();
|
|
|
|
for (let i = 0; i < modelList.contentItem.children.length; i++) {
|
|
let delegateItem = modelList.contentItem.children[i];
|
|
if (delegateItem.objectName === "delegateItem") {
|
|
let modelNameText = delegateItem.children.find(child => child.objectName === "modelName").filename;
|
|
if (modelNameText === modelName) {
|
|
let progressBar = delegateItem.children.find(child => child.objectName === "itemProgressBar");
|
|
progressBar.value = bytesReceived / bytesTotal;
|
|
|
|
// Calculate the download speed
|
|
if (lastUpdate[modelName] && lastUpdate[modelName].timestamp) {
|
|
let timeDifference = currentTime - lastUpdate[modelName].timestamp;
|
|
let bytesDifference = bytesReceived - lastUpdate[modelName].bytesReceived;
|
|
let speed = (bytesDifference / timeDifference) * 1000; // bytes per second
|
|
|
|
// Update the speed label
|
|
let speedLabel = delegateItem.children.find(child => child.objectName === "speedLabel");
|
|
if (speed < 1024) {
|
|
speedLabel.text = speed.toFixed(2) + " B/s";
|
|
} else if (speed < 1024 * 1024) {
|
|
speedLabel.text = (speed / 1024).toFixed(2) + " KB/s";
|
|
} else {
|
|
speedLabel.text = (speed / (1024 * 1024)).toFixed(2) + " MB/s";
|
|
}
|
|
}
|
|
|
|
// Update the lastUpdate object for the current model
|
|
lastUpdate[modelName] = {"timestamp": currentTime, "bytesReceived": bytesReceived};
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function resetProgress(modelName) {
|
|
for (let i = 0; i < modelList.contentItem.children.length; i++) {
|
|
let delegateItem = modelList.contentItem.children[i];
|
|
if (delegateItem.objectName === "delegateItem") {
|
|
let modelNameText = delegateItem.children.find(child => child.objectName === "modelName").filename;
|
|
if (modelNameText === modelName) {
|
|
let progressBar = delegateItem.children.find(child => child.objectName === "itemProgressBar");
|
|
progressBar.value = 0;
|
|
delegateItem.downloading = false;
|
|
|
|
// Remove speed label text
|
|
let speedLabel = delegateItem.children.find(child => child.objectName === "speedLabel");
|
|
speedLabel.text = "";
|
|
|
|
// Remove the lastUpdate object for the canceled model
|
|
delete lastUpdate[modelName];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.alignment: Qt.AlignCenter
|
|
Layout.fillWidth: true
|
|
spacing: 20
|
|
FolderDialog {
|
|
id: modelPathDialog
|
|
title: "Please choose a directory"
|
|
currentFolder: Download.downloadLocalModelsPath
|
|
onAccepted: {
|
|
Download.downloadLocalModelsPath = selectedFolder
|
|
settings.modelPath = Download.downloadLocalModelsPath
|
|
settings.sync()
|
|
}
|
|
}
|
|
Label {
|
|
id: modelPathLabel
|
|
text: qsTr("Download path:")
|
|
color: theme.textColor
|
|
Layout.row: 1
|
|
Layout.column: 0
|
|
}
|
|
TextField {
|
|
id: modelPathDisplayLabel
|
|
text: Download.downloadLocalModelsPath
|
|
readOnly: true
|
|
color: theme.textColor
|
|
Layout.fillWidth: true
|
|
ToolTip.text: qsTr("Path where model files will be downloaded to")
|
|
ToolTip.visible: hovered
|
|
Accessible.role: Accessible.ToolTip
|
|
Accessible.name: modelPathDisplayLabel.text
|
|
Accessible.description: ToolTip.text
|
|
background: Rectangle {
|
|
color: theme.backgroundLighter
|
|
radius: 10
|
|
}
|
|
}
|
|
Button {
|
|
text: qsTr("Browse")
|
|
contentItem: Text {
|
|
text: qsTr("Browse")
|
|
horizontalAlignment: Text.AlignHCenter
|
|
color: theme.textColor
|
|
Accessible.role: Accessible.Button
|
|
Accessible.name: text
|
|
Accessible.description: qsTr("Opens a folder picker dialog to choose where to save model files")
|
|
}
|
|
background: Rectangle {
|
|
opacity: .5
|
|
border.color: theme.backgroundLightest
|
|
border.width: 1
|
|
radius: 10
|
|
color: theme.backgroundLight
|
|
}
|
|
onClicked: modelPathDialog.open()
|
|
}
|
|
}
|
|
}
|
|
}
|