import QtCore import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic import QtQuick.Dialogs import QtQuick.Layouts import chatlistmodel import download import llm import modellist import network import mysettings MyDialog { id: modelDownloaderDialog modal: true closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside padding: 10 onOpened: { Network.sendModelDownloaderDialog(); } PopupDialog { id: downloadingErrorPopup anchors.centerIn: parent shouldTimeOut: false } ColumnLayout { anchors.fill: parent anchors.margins: 10 spacing: 30 Label { id: listLabel text: qsTr("Available Models:") Layout.alignment: Qt.AlignLeft Layout.fillWidth: true color: theme.textColor } Label { visible: !ModelList.downloadableModels.count && !ModelList.asyncModelRequestOngoing Layout.fillWidth: true Layout.fillHeight: true horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter text: qsTr("Network error: could not retrieve http://gpt4all.io/models/models.json") color: theme.mutedTextColor } MyBusyIndicator { visible: !ModelList.downloadableModels.count && ModelList.asyncModelRequestOngoing running: ModelList.asyncModelRequestOngoing Accessible.role: Accessible.Animation Layout.alignment: Qt.AlignCenter Accessible.name: qsTr("Busy indicator") Accessible.description: qsTr("Displayed when the models request is ongoing") } ScrollView { id: scrollView ScrollBar.vertical.policy: ScrollBar.AlwaysOn Layout.fillWidth: true Layout.fillHeight: true clip: true ListView { id: modelListView model: ModelList.downloadableModels boundsBehavior: Flickable.StopAtBounds delegate: Rectangle { id: delegateItem width: modelListView.width height: childrenRect.height color: index % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter GridLayout { columns: 2 width: parent.width Text { textFormat: Text.StyledText text: "

" + name + "

" Layout.row: 0 Layout.column: 0 Layout.topMargin: 20 Layout.leftMargin: 20 Layout.columnSpan: 2 color: theme.assistantColor Accessible.role: Accessible.Paragraph Accessible.name: qsTr("Model file") Accessible.description: qsTr("Model file to be downloaded") } Rectangle { id: actionBox width: childrenRect.width + 20 color: theme.backgroundDark border.width: 1 radius: 10 Layout.row: 1 Layout.column: 1 Layout.rightMargin: 20 Layout.bottomMargin: 20 Layout.fillHeight: true Layout.minimumHeight: childrenRect.height + 20 Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.rowSpan: 2 ColumnLayout { spacing: 0 MyButton { id: downloadButton text: isDownloading ? qsTr("Cancel") : isIncomplete ? qsTr("Resume") : qsTr("Download") Layout.topMargin: 20 Layout.leftMargin: 20 Layout.minimumWidth: openaiKey.width Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter visible: !isChatGPT && !installed && !calcHash && downloadError === "" Accessible.description: qsTr("Cancel/Resume/Download button to stop/restart/start the download") background: Rectangle { border.color: downloadButton.down ? theme.backgroundLightest : theme.buttonBorder border.width: 2 radius: 10 color: downloadButton.hovered ? theme.backgroundDark : theme.backgroundDarkest } onClicked: { if (!isDownloading) { Download.downloadModel(filename); } else { Download.cancelDownload(filename); } } } MyButton { id: removeButton text: qsTr("Remove") Layout.topMargin: 20 Layout.leftMargin: 20 Layout.minimumWidth: openaiKey.width Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter visible: installed || downloadError !== "" Accessible.description: qsTr("Remove button to remove model from filesystem") background: Rectangle { border.color: removeButton.down ? theme.backgroundLightest : theme.buttonBorder border.width: 2 radius: 10 color: removeButton.hovered ? theme.backgroundDark : theme.backgroundDarkest } onClicked: { Download.removeModel(filename); } } MyButton { id: installButton visible: !installed && isChatGPT Layout.topMargin: 20 Layout.leftMargin: 20 Layout.minimumWidth: openaiKey.width Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter text: qsTr("Install") background: Rectangle { border.color: installButton.down ? theme.backgroundLightest : theme.buttonBorder border.width: 2 radius: 10 color: installButton.hovered ? theme.backgroundDark : theme.backgroundDarkest } onClicked: { if (openaiKey.text === "") openaiKey.showError(); else Download.installModel(filename, openaiKey.text); } Accessible.role: Accessible.Button Accessible.name: qsTr("Install button") Accessible.description: qsTr("Install button to install chatgpt model") } ColumnLayout { spacing: 0 Label { Layout.topMargin: 20 Layout.leftMargin: 20 textFormat: Text.StyledText text: "" + (qsTr("Status: ") + (installed ? qsTr("Installed") : (downloadError !== "" ? qsTr("Error") : (isDownloading ? qsTr("Downloading") : qsTr("Available"))))) + "" color: theme.textColor linkColor: theme.textErrorColor Accessible.role: Accessible.Paragraph Accessible.name: text Accessible.description: qsTr("Whether the file is already installed on your system") onLinkActivated: { downloadingErrorPopup.text = downloadError; downloadingErrorPopup.open(); } } Label { Layout.leftMargin: 20 textFormat: Text.StyledText text: "" + (qsTr("Download size: ") + filesize) + "
" + (qsTr("RAM required: ") + (ramrequired > 0 ? ramrequired + " GB" : qsTr("minimal"))) + "
" + (qsTr("Parameters: ") + parameters) + "
" + (qsTr("Quantization: ") + quant) + "
" + (qsTr("Type: ") + type) + "
" color: theme.textColor Accessible.role: Accessible.Paragraph Accessible.name: text Accessible.description: qsTr("Metadata about the model") onLinkActivated: { downloadingErrorPopup.text = downloadError; downloadingErrorPopup.open(); } } Label { visible: LLM.systemTotalRAMInGB() < ramrequired Layout.topMargin: 20 Layout.leftMargin: 20 Layout.maximumWidth: openaiKey.width textFormat: Text.StyledText text: qsTr("WARNING: Not recommended for your hardware.") + qsTr(" Model requires more memory (") + ramrequired + qsTr(" GB) than your system has available (") + LLM.systemTotalRAMInGBString() + ")." color: theme.textErrorColor wrapMode: Text.WordWrap Accessible.role: Accessible.Paragraph Accessible.name: text Accessible.description: qsTr("Error for incompatible hardware") onLinkActivated: { downloadingErrorPopup.text = downloadError; downloadingErrorPopup.open(); } } } ColumnLayout { visible: isDownloading && !calcHash Layout.topMargin: 20 Layout.leftMargin: 20 Layout.minimumWidth: openaiKey.width Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter spacing: 20 ProgressBar { id: itemProgressBar Layout.fillWidth: true width: openaiKey.width value: bytesReceived / bytesTotal background: Rectangle { implicitHeight: 45 color: theme.backgroundDarkest radius: 3 } contentItem: Item { implicitHeight: 40 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") } Label { id: speedLabel color: theme.textColor Layout.alignment: Qt.AlignRight text: speed Accessible.role: Accessible.Paragraph Accessible.name: qsTr("Download speed") Accessible.description: qsTr("Download speed in bytes/kilobytes/megabytes per second") } } RowLayout { visible: calcHash Layout.topMargin: 20 Layout.leftMargin: 20 Layout.minimumWidth: openaiKey.width Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter Label { id: 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") } MyBusyIndicator { id: busyCalcHash running: calcHash Accessible.role: Accessible.Animation Accessible.name: qsTr("Busy indicator") Accessible.description: qsTr("Displayed when the file hash is being calculated") } } MyTextField { id: openaiKey visible: !installed && isChatGPT Layout.topMargin: 20 Layout.leftMargin: 20 Layout.minimumWidth: 150 Layout.maximumWidth: textMetrics.width + 25 Layout.alignment: Qt.AlignTop | Qt.AlignHCenter color: theme.textColor background: Rectangle { color: theme.backgroundLighter radius: 10 } wrapMode: Text.WrapAnywhere function showError() { openaiKey.placeholderTextColor = theme.textErrorColor } onTextChanged: { openaiKey.placeholderTextColor = theme.backgroundLightest } placeholderText: qsTr("enter $OPENAI_API_KEY") placeholderTextColor: theme.backgroundLightest Accessible.role: Accessible.EditableText Accessible.name: placeholderText Accessible.description: qsTr("Whether the file hash is being calculated") TextMetrics { id: textMetrics font: openaiKey.font text: openaiKey.placeholderText } } } } Text { id: descriptionText text: description Layout.row: 1 Layout.column: 0 Layout.leftMargin: 20 Layout.bottomMargin: 20 Layout.maximumWidth: modelListView.width - actionBox.width - 60 wrapMode: Text.WordWrap textFormat: Text.StyledText color: theme.textColor linkColor: theme.textColor Accessible.role: Accessible.Paragraph Accessible.name: qsTr("Description") Accessible.description: qsTr("The description of the file") onLinkActivated: Qt.openUrlExternally(link) } } } footer: Component { Rectangle { width: modelListView.width height: expandButton.height + 80 color: ModelList.downloadableModels.count % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter MyButton { id: expandButton anchors.centerIn: parent padding: 40 text: ModelList.downloadableModels.expanded ? qsTr("Show fewer models") : qsTr("Show more models") background: Rectangle { border.color: expandButton.down ? theme.backgroundLightest : theme.buttonBorder border.width: 2 radius: 10 color: expandButton.hovered ? theme.backgroundDark : theme.backgroundDarkest } onClicked: { ModelList.downloadableModels.expanded = !ModelList.downloadableModels.expanded; } } } } } } RowLayout { Layout.alignment: Qt.AlignCenter Layout.fillWidth: true spacing: 20 FolderDialog { id: modelPathDialog title: "Please choose a directory" currentFolder: "file://" + MySettings.modelPath onAccepted: { MySettings.modelPath = selectedFolder } } Label { id: modelPathLabel text: qsTr("Download path:") color: theme.textColor Layout.row: 1 Layout.column: 0 } MyDirectoryField { id: modelPathDisplayField text: MySettings.modelPath 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) { MySettings.modelPath = modelPathDisplayField.text } else { text = MySettings.modelPath } } } MyButton { text: qsTr("Browse") Accessible.description: qsTr("Opens a folder picker dialog to choose where to save model files") onClicked: modelPathDialog.open() } } } }