mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-09-24 04:53:02 +00:00
Add new remote model provider view. (#3506)
Signed-off-by: Adam Treat <treat.adam@gmail.com> Signed-off-by: AT <manyoso@users.noreply.github.com> Signed-off-by: Jared Van Bortel <jared@nomic.ai> Co-authored-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
@@ -204,7 +204,7 @@ ColumnLayout {
|
||||
Layout.minimumWidth: 200
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
visible: !isOnline && !installed && !calcHash && downloadError === ""
|
||||
visible: !installed && !calcHash && downloadError === ""
|
||||
Accessible.description: qsTr("Stop/restart/start the download")
|
||||
onClicked: {
|
||||
if (!isDownloading) {
|
||||
@@ -230,52 +230,6 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
MySettingsButton {
|
||||
id: installButton
|
||||
visible: !installed && isOnline
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 20
|
||||
Layout.minimumWidth: 200
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
text: qsTr("Install")
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
onClicked: {
|
||||
var apiKeyText = apiKey.text.trim(),
|
||||
baseUrlText = baseUrl.text.trim(),
|
||||
modelNameText = modelName.text.trim();
|
||||
|
||||
var apiKeyOk = apiKeyText !== "",
|
||||
baseUrlOk = !isCompatibleApi || baseUrlText !== "",
|
||||
modelNameOk = !isCompatibleApi || modelNameText !== "";
|
||||
|
||||
if (!apiKeyOk)
|
||||
apiKey.showError();
|
||||
if (!baseUrlOk)
|
||||
baseUrl.showError();
|
||||
if (!modelNameOk)
|
||||
modelName.showError();
|
||||
|
||||
if (!apiKeyOk || !baseUrlOk || !modelNameOk)
|
||||
return;
|
||||
|
||||
if (!isCompatibleApi)
|
||||
Download.installModel(
|
||||
filename,
|
||||
apiKeyText,
|
||||
);
|
||||
else
|
||||
Download.installCompatibleModel(
|
||||
modelNameText,
|
||||
apiKeyText,
|
||||
baseUrlText,
|
||||
);
|
||||
}
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Install")
|
||||
Accessible.description: qsTr("Install online model")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
Label {
|
||||
@@ -390,69 +344,6 @@ ColumnLayout {
|
||||
Accessible.description: qsTr("Displayed when the file hash is being calculated")
|
||||
}
|
||||
}
|
||||
|
||||
MyTextField {
|
||||
id: apiKey
|
||||
visible: !installed && isOnline
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 20
|
||||
Layout.minimumWidth: 200
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAnywhere
|
||||
function showError() {
|
||||
messageToast.show(qsTr("ERROR: $API_KEY is empty."));
|
||||
apiKey.placeholderTextColor = theme.textErrorColor;
|
||||
}
|
||||
onTextChanged: {
|
||||
apiKey.placeholderTextColor = theme.mutedTextColor;
|
||||
}
|
||||
placeholderText: qsTr("enter $API_KEY")
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: placeholderText
|
||||
Accessible.description: qsTr("Whether the file hash is being calculated")
|
||||
}
|
||||
|
||||
MyTextField {
|
||||
id: baseUrl
|
||||
visible: !installed && isOnline && isCompatibleApi
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 20
|
||||
Layout.minimumWidth: 200
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAnywhere
|
||||
function showError() {
|
||||
messageToast.show(qsTr("ERROR: $BASE_URL is empty."));
|
||||
baseUrl.placeholderTextColor = theme.textErrorColor;
|
||||
}
|
||||
onTextChanged: {
|
||||
baseUrl.placeholderTextColor = theme.mutedTextColor;
|
||||
}
|
||||
placeholderText: qsTr("enter $BASE_URL")
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: placeholderText
|
||||
Accessible.description: qsTr("Whether the file hash is being calculated")
|
||||
}
|
||||
|
||||
MyTextField {
|
||||
id: modelName
|
||||
visible: !installed && isOnline && isCompatibleApi
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 20
|
||||
Layout.minimumWidth: 200
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAnywhere
|
||||
function showError() {
|
||||
messageToast.show(qsTr("ERROR: $MODEL_NAME is empty."))
|
||||
modelName.placeholderTextColor = theme.textErrorColor;
|
||||
}
|
||||
onTextChanged: {
|
||||
modelName.placeholderTextColor = theme.mutedTextColor;
|
||||
}
|
||||
placeholderText: qsTr("enter $MODEL_NAME")
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: placeholderText
|
||||
Accessible.description: qsTr("Whether the file hash is being calculated")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -89,6 +89,13 @@ Rectangle {
|
||||
gpt4AllModelView.show();
|
||||
}
|
||||
}
|
||||
MyTabButton {
|
||||
text: qsTr("Remote Providers")
|
||||
isSelected: remoteModelView.isShown()
|
||||
onPressed: {
|
||||
remoteModelView.show();
|
||||
}
|
||||
}
|
||||
MyTabButton {
|
||||
text: qsTr("HuggingFace")
|
||||
isSelected: huggingfaceModelView.isShown()
|
||||
@@ -112,7 +119,20 @@ Rectangle {
|
||||
stackLayout.currentIndex = 0;
|
||||
}
|
||||
function isShown() {
|
||||
return stackLayout.currentIndex === 0
|
||||
return stackLayout.currentIndex === 0;
|
||||
}
|
||||
}
|
||||
|
||||
AddRemoteModelView {
|
||||
id: remoteModelView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
function show() {
|
||||
stackLayout.currentIndex = 1;
|
||||
}
|
||||
function isShown() {
|
||||
return stackLayout.currentIndex === 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,10 +146,10 @@ Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
function show() {
|
||||
stackLayout.currentIndex = 1;
|
||||
stackLayout.currentIndex = 2;
|
||||
}
|
||||
function isShown() {
|
||||
return stackLayout.currentIndex === 1
|
||||
return stackLayout.currentIndex === 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
147
gpt4all-chat/qml/AddRemoteModelView.qml
Normal file
147
gpt4all-chat/qml/AddRemoteModelView.qml
Normal file
@@ -0,0 +1,147 @@
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.Basic
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
import Qt.labs.folderlistmodel
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import llm
|
||||
import chatlistmodel
|
||||
import download
|
||||
import modellist
|
||||
import network
|
||||
import gpt4all
|
||||
import mysettings
|
||||
import localdocs
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
spacing: 5
|
||||
|
||||
Label {
|
||||
Layout.topMargin: 0
|
||||
Layout.bottomMargin: 25
|
||||
Layout.rightMargin: 150 * theme.fontScale
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
verticalAlignment: Text.AlignTop
|
||||
text: qsTr("Various remote model providers that use network resources for inference.")
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
color: theme.textColor
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
contentWidth: availableWidth
|
||||
clip: true
|
||||
Flow {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 20
|
||||
bottomPadding: 20
|
||||
property int childWidth: 330 * theme.fontScale
|
||||
property int childHeight: 400 + 166 * theme.fontScale
|
||||
RemoteModelCard {
|
||||
width: parent.childWidth
|
||||
height: parent.childHeight
|
||||
providerBaseUrl: "https://api.groq.com/openai/v1/"
|
||||
providerName: qsTr("Groq")
|
||||
providerImage: "qrc:/gpt4all/icons/groq.svg"
|
||||
providerDesc: qsTr('Groq offers a high-performance AI inference engine designed for low-latency and efficient processing. Optimized for real-time applications, Groq’s technology is ideal for users who need fast responses from open large language models and other AI workloads.<br><br>Get your API key: <a href="https://console.groq.com/keys">https://groq.com/</a>')
|
||||
modelWhitelist: [
|
||||
// last updated 2025-02-24
|
||||
"deepseek-r1-distill-llama-70b",
|
||||
"deepseek-r1-distill-qwen-32b",
|
||||
"gemma2-9b-it",
|
||||
"llama-3.1-8b-instant",
|
||||
"llama-3.2-1b-preview",
|
||||
"llama-3.2-3b-preview",
|
||||
"llama-3.3-70b-specdec",
|
||||
"llama-3.3-70b-versatile",
|
||||
"llama3-70b-8192",
|
||||
"llama3-8b-8192",
|
||||
"mixtral-8x7b-32768",
|
||||
"qwen-2.5-32b",
|
||||
"qwen-2.5-coder-32b",
|
||||
]
|
||||
}
|
||||
RemoteModelCard {
|
||||
width: parent.childWidth
|
||||
height: parent.childHeight
|
||||
providerBaseUrl: "https://api.openai.com/v1/"
|
||||
providerName: qsTr("OpenAI")
|
||||
providerImage: "qrc:/gpt4all/icons/openai.svg"
|
||||
providerDesc: qsTr('OpenAI provides access to advanced AI models, including GPT-4 supporting a wide range of applications, from conversational AI to content generation and code completion.<br><br>Get your API key: <a href="https://platform.openai.com/signup">https://openai.com/</a>')
|
||||
modelWhitelist: [
|
||||
// last updated 2025-02-24
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-3.5-turbo-16k",
|
||||
"gpt-4",
|
||||
"gpt-4-32k",
|
||||
"gpt-4-turbo",
|
||||
"gpt-4o",
|
||||
]
|
||||
}
|
||||
RemoteModelCard {
|
||||
width: parent.childWidth
|
||||
height: parent.childHeight
|
||||
providerBaseUrl: "https://api.mistral.ai/v1/"
|
||||
providerName: qsTr("Mistral")
|
||||
providerImage: "qrc:/gpt4all/icons/mistral.svg"
|
||||
providerDesc: qsTr('Mistral AI specializes in efficient, open-weight language models optimized for various natural language processing tasks. Their models are designed for flexibility and performance, making them a solid option for applications requiring scalable AI solutions.<br><br>Get your API key: <a href="https://mistral.ai/">https://mistral.ai/</a>')
|
||||
modelWhitelist: [
|
||||
// last updated 2025-02-24
|
||||
"codestral-2405",
|
||||
"codestral-2411-rc5",
|
||||
"codestral-2412",
|
||||
"codestral-2501",
|
||||
"codestral-latest",
|
||||
"codestral-mamba-2407",
|
||||
"codestral-mamba-latest",
|
||||
"ministral-3b-2410",
|
||||
"ministral-3b-latest",
|
||||
"ministral-8b-2410",
|
||||
"ministral-8b-latest",
|
||||
"mistral-large-2402",
|
||||
"mistral-large-2407",
|
||||
"mistral-large-2411",
|
||||
"mistral-large-latest",
|
||||
"mistral-medium-2312",
|
||||
"mistral-medium-latest",
|
||||
"mistral-saba-2502",
|
||||
"mistral-saba-latest",
|
||||
"mistral-small-2312",
|
||||
"mistral-small-2402",
|
||||
"mistral-small-2409",
|
||||
"mistral-small-2501",
|
||||
"mistral-small-latest",
|
||||
"mistral-tiny-2312",
|
||||
"mistral-tiny-2407",
|
||||
"mistral-tiny-latest",
|
||||
"open-codestral-mamba",
|
||||
"open-mistral-7b",
|
||||
"open-mistral-nemo",
|
||||
"open-mistral-nemo-2407",
|
||||
"open-mixtral-8x22b",
|
||||
"open-mixtral-8x22b-2404",
|
||||
"open-mixtral-8x7b",
|
||||
]
|
||||
}
|
||||
RemoteModelCard {
|
||||
width: parent.childWidth
|
||||
height: parent.childHeight
|
||||
providerIsCustom: true
|
||||
providerName: qsTr("Custom")
|
||||
providerImage: "qrc:/gpt4all/icons/antenna_3.svg"
|
||||
providerDesc: qsTr("The custom provider option allows users to connect their own OpenAI-compatible AI models or third-party inference services. This is useful for organizations with proprietary models or those leveraging niche AI providers not listed here.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -60,27 +60,28 @@ ComboBox {
|
||||
highlighted: comboBox.highlightedIndex === index
|
||||
}
|
||||
popup: Popup {
|
||||
// FIXME This should be made much nicer to take into account lists that are very long so
|
||||
// that it is scrollable and also sized optimally taking into account the x,y and the content
|
||||
// width and height as well as the window width and height
|
||||
y: comboBox.height - 1
|
||||
width: comboBox.width
|
||||
implicitHeight: contentItem.implicitHeight + 20
|
||||
implicitHeight: Math.min(window.height - y, contentItem.implicitHeight + 20)
|
||||
padding: 0
|
||||
|
||||
contentItem: Rectangle {
|
||||
implicitWidth: myListView.contentWidth
|
||||
implicitWidth: comboBox.width
|
||||
implicitHeight: myListView.contentHeight
|
||||
color: "transparent"
|
||||
ListView {
|
||||
id: myListView
|
||||
radius: 10
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: comboBox.popup.visible ? comboBox.delegateModel : null
|
||||
currentIndex: comboBox.highlightedIndex
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ListView {
|
||||
id: myListView
|
||||
implicitHeight: contentHeight
|
||||
model: comboBox.popup.visible ? comboBox.delegateModel : null
|
||||
currentIndex: comboBox.highlightedIndex
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
221
gpt4all-chat/qml/RemoteModelCard.qml
Normal file
221
gpt4all-chat/qml/RemoteModelCard.qml
Normal file
@@ -0,0 +1,221 @@
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.Basic
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
import Qt.labs.folderlistmodel
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import llm
|
||||
import chatlistmodel
|
||||
import download
|
||||
import modellist
|
||||
import network
|
||||
import gpt4all
|
||||
import mysettings
|
||||
import localdocs
|
||||
|
||||
|
||||
Rectangle {
|
||||
property alias providerName: providerNameLabel.text
|
||||
property alias providerImage: myimage.source
|
||||
property alias providerDesc: providerDescLabel.text
|
||||
property string providerBaseUrl: ""
|
||||
property bool providerIsCustom: false
|
||||
property var modelWhitelist: null
|
||||
|
||||
color: theme.conversationBackground
|
||||
radius: 10
|
||||
border.width: 1
|
||||
border.color: theme.controlBorder
|
||||
implicitHeight: topColumn.height + bottomColumn.height + 33 * theme.fontScale
|
||||
|
||||
ColumnLayout {
|
||||
id: topColumn
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: 20
|
||||
spacing: 15 * theme.fontScale
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
spacing: 10
|
||||
Item {
|
||||
Layout.preferredWidth: 27 * theme.fontScale
|
||||
Layout.preferredHeight: 27 * theme.fontScale
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
Image {
|
||||
id: myimage
|
||||
anchors.centerIn: parent
|
||||
sourceSize.width: parent.width
|
||||
sourceSize.height: parent.height
|
||||
mipmap: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: providerNameLabel
|
||||
color: theme.textColor
|
||||
font.pixelSize: theme.fontSizeBanner
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: providerDescLabel
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
color: theme.settingsTitleTextColor
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
onLinkActivated: function(link) { Qt.openUrlExternally(link); }
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton // pass clicks to parent
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: bottomColumn
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 20
|
||||
spacing: 30
|
||||
|
||||
ColumnLayout {
|
||||
MySettingsLabel {
|
||||
text: qsTr("API Key")
|
||||
font.bold: true
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
color: theme.settingsTitleTextColor
|
||||
}
|
||||
|
||||
MyTextField {
|
||||
id: apiKeyField
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
wrapMode: Text.WrapAnywhere
|
||||
function showError() {
|
||||
messageToast.show(qsTr("ERROR: $API_KEY is empty."));
|
||||
apiKeyField.placeholderTextColor = theme.textErrorColor;
|
||||
}
|
||||
onTextChanged: {
|
||||
apiKeyField.placeholderTextColor = theme.mutedTextColor;
|
||||
if (!providerIsCustom) {
|
||||
let models = ModelList.remoteModelList(apiKeyField.text, providerBaseUrl);
|
||||
if (modelWhitelist !== null)
|
||||
models = models.filter(m => modelWhitelist.includes(m));
|
||||
myModelList.model = models;
|
||||
myModelList.currentIndex = -1;
|
||||
}
|
||||
}
|
||||
placeholderText: qsTr("enter $API_KEY")
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: placeholderText
|
||||
Accessible.description: qsTr("Whether the file hash is being calculated")
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: providerIsCustom
|
||||
MySettingsLabel {
|
||||
text: qsTr("Base Url")
|
||||
font.bold: true
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
color: theme.settingsTitleTextColor
|
||||
}
|
||||
MyTextField {
|
||||
id: baseUrlField
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
wrapMode: Text.WrapAnywhere
|
||||
function showError() {
|
||||
messageToast.show(qsTr("ERROR: $BASE_URL is empty."));
|
||||
baseUrlField.placeholderTextColor = theme.textErrorColor;
|
||||
}
|
||||
onTextChanged: {
|
||||
baseUrlField.placeholderTextColor = theme.mutedTextColor;
|
||||
}
|
||||
placeholderText: qsTr("enter $BASE_URL")
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: placeholderText
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
visible: providerIsCustom
|
||||
MySettingsLabel {
|
||||
text: qsTr("Model Name")
|
||||
font.bold: true
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
color: theme.settingsTitleTextColor
|
||||
}
|
||||
MyTextField {
|
||||
id: modelNameField
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
wrapMode: Text.WrapAnywhere
|
||||
function showError() {
|
||||
messageToast.show(qsTr("ERROR: $MODEL_NAME is empty."))
|
||||
modelNameField.placeholderTextColor = theme.textErrorColor;
|
||||
}
|
||||
onTextChanged: {
|
||||
modelNameField.placeholderTextColor = theme.mutedTextColor;
|
||||
}
|
||||
placeholderText: qsTr("enter $MODEL_NAME")
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: placeholderText
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: myModelList.count > 0 && !providerIsCustom
|
||||
|
||||
MySettingsLabel {
|
||||
text: qsTr("Models")
|
||||
font.bold: true
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
color: theme.settingsTitleTextColor
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
|
||||
MyComboBox {
|
||||
Layout.fillWidth: true
|
||||
id: myModelList
|
||||
currentIndex: -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MySettingsButton {
|
||||
id: installButton
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("Install")
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
|
||||
property string apiKeyText: apiKeyField.text.trim()
|
||||
property string baseUrlText: providerIsCustom ? baseUrlField.text.trim() : providerBaseUrl.trim()
|
||||
property string modelNameText: providerIsCustom ? modelNameField.text.trim() : myModelList.currentText.trim()
|
||||
|
||||
enabled: apiKeyText !== "" && baseUrlText !== "" && modelNameText !== ""
|
||||
|
||||
onClicked: {
|
||||
Download.installCompatibleModel(
|
||||
modelNameText,
|
||||
apiKeyText,
|
||||
baseUrlText,
|
||||
);
|
||||
myModelList.currentIndex = -1;
|
||||
}
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Install")
|
||||
Accessible.description: qsTr("Install remote model")
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user