mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-09-04 01:54:49 +00:00
feat: add openai-compatible api models (#2683)
Signed-off-by: Shiranui <supersonic@livemail.tw> Signed-off-by: Jared Van Bortel <jared@nomic.ai> Co-authored-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
@@ -25,6 +25,10 @@ Rectangle {
|
||||
color: theme.viewBackground
|
||||
signal modelsViewRequested()
|
||||
|
||||
ToastManager {
|
||||
id: messageToast
|
||||
}
|
||||
|
||||
PopupDialog {
|
||||
id: downloadingErrorPopup
|
||||
anchors.centerIn: parent
|
||||
@@ -437,10 +441,35 @@ Rectangle {
|
||||
text: qsTr("Install")
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
onClicked: {
|
||||
if (apiKey.text === "")
|
||||
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.installModel(filename, apiKey.text);
|
||||
Download.installCompatibleModel(
|
||||
modelNameText,
|
||||
apiKeyText,
|
||||
baseUrlText,
|
||||
);
|
||||
}
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Install")
|
||||
@@ -571,16 +600,59 @@ Rectangle {
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAnywhere
|
||||
function showError() {
|
||||
apiKey.placeholderTextColor = theme.textErrorColor
|
||||
messageToast.show(qsTr("ERROR: $API_KEY is empty."));
|
||||
apiKey.placeholderTextColor = theme.textErrorColor;
|
||||
}
|
||||
onTextChanged: {
|
||||
apiKey.placeholderTextColor = theme.mutedTextColor
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -718,4 +790,11 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Download
|
||||
function onToastMessage(message) {
|
||||
messageToast.show(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -696,6 +696,7 @@ Rectangle {
|
||||
rightPadding: 60
|
||||
leftPadding: 60
|
||||
property string defaultModel: ""
|
||||
property string defaultModelName: ""
|
||||
function updateDefaultModel() {
|
||||
var i = comboBox.find(MySettings.userDefaultModel)
|
||||
if (i !== -1) {
|
||||
@@ -703,9 +704,14 @@ Rectangle {
|
||||
} else {
|
||||
defaultModel = comboBox.valueAt(0);
|
||||
}
|
||||
if (defaultModel !== "") {
|
||||
defaultModelName = ModelList.modelInfo(defaultModel).name;
|
||||
} else {
|
||||
defaultModelName = "";
|
||||
}
|
||||
}
|
||||
|
||||
text: qsTr("Load \u00B7 %1 (default) \u2192").arg(defaultModel);
|
||||
text: qsTr("Load \u00B7 %1 (default) \u2192").arg(defaultModelName);
|
||||
onClicked: {
|
||||
var i = comboBox.find(MySettings.userDefaultModel)
|
||||
if (i !== -1) {
|
||||
|
@@ -17,6 +17,10 @@ Rectangle {
|
||||
|
||||
signal addModelViewRequested()
|
||||
|
||||
ToastManager {
|
||||
id: messageToast
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 20
|
||||
@@ -233,10 +237,35 @@ Rectangle {
|
||||
text: qsTr("Install")
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
onClicked: {
|
||||
if (apiKey.text === "")
|
||||
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.installModel(filename, apiKey.text);
|
||||
Download.installCompatibleModel(
|
||||
modelNameText,
|
||||
apiKeyText,
|
||||
baseUrlText,
|
||||
);
|
||||
}
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Install")
|
||||
@@ -367,16 +396,59 @@ Rectangle {
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAnywhere
|
||||
function showError() {
|
||||
apiKey.placeholderTextColor = theme.textErrorColor
|
||||
messageToast.show(qsTr("ERROR: $API_KEY is empty."));
|
||||
apiKey.placeholderTextColor = theme.textErrorColor;
|
||||
}
|
||||
onTextChanged: {
|
||||
apiKey.placeholderTextColor = theme.mutedTextColor
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -514,4 +586,11 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Download
|
||||
function onToastMessage(message) {
|
||||
messageToast.show(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
98
gpt4all-chat/qml/Toast.qml
Normal file
98
gpt4all-chat/qml/Toast.qml
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* Source: https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129
|
||||
* Adapted from StackOverflow: http://stackoverflow.com/questions/26879266/make-toast-in-android-by-qml
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
/**
|
||||
* @brief An Android-like timed message text in a box that self-destroys when finished if desired
|
||||
*/
|
||||
Rectangle {
|
||||
/**
|
||||
* Public
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Shows this Toast
|
||||
*
|
||||
* @param {string} text Text to show
|
||||
* @param {real} duration Duration to show in milliseconds, defaults to 3000
|
||||
*/
|
||||
function show(text, duration=3000) {
|
||||
message.text = text;
|
||||
if (typeof duration !== "undefined") { // checks if parameter was passed
|
||||
time = Math.max(duration, 2 * fadeTime);
|
||||
}
|
||||
else {
|
||||
time = defaultTime;
|
||||
}
|
||||
animation.start();
|
||||
}
|
||||
|
||||
property bool selfDestroying: false // whether this Toast will self-destroy when it is finished
|
||||
|
||||
/**
|
||||
* Private
|
||||
*/
|
||||
|
||||
id: root
|
||||
|
||||
readonly property real defaultTime: 3000
|
||||
property real time: defaultTime
|
||||
readonly property real fadeTime: 300
|
||||
|
||||
property real margin: 10
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: margin
|
||||
}
|
||||
|
||||
height: message.height + margin
|
||||
radius: margin
|
||||
|
||||
opacity: 0
|
||||
color: "#222222"
|
||||
|
||||
Text {
|
||||
id: message
|
||||
color: "white"
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: margin / 2
|
||||
}
|
||||
}
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
id: animation
|
||||
running: false
|
||||
|
||||
|
||||
NumberAnimation {
|
||||
to: .9
|
||||
duration: fadeTime
|
||||
}
|
||||
|
||||
PauseAnimation {
|
||||
duration: time - 2 * fadeTime
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
to: 0
|
||||
duration: fadeTime
|
||||
}
|
||||
|
||||
onRunningChanged: {
|
||||
if (!running && selfDestroying) {
|
||||
root.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
gpt4all-chat/qml/ToastManager.qml
Normal file
60
gpt4all-chat/qml/ToastManager.qml
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* Source: https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129
|
||||
* Adapted from StackOverflow: http://stackoverflow.com/questions/26879266/make-toast-in-android-by-qml
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
/**
|
||||
* @brief Manager that creates Toasts dynamically
|
||||
*/
|
||||
ListView {
|
||||
/**
|
||||
* Public
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Shows a Toast
|
||||
*
|
||||
* @param {string} text Text to show
|
||||
* @param {real} duration Duration to show in milliseconds, defaults to 3000
|
||||
*/
|
||||
function show(text, duration=3000) {
|
||||
model.insert(0, {text: text, duration: duration});
|
||||
}
|
||||
|
||||
/**
|
||||
* Private
|
||||
*/
|
||||
|
||||
id: root
|
||||
|
||||
z: Infinity
|
||||
spacing: 5
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: 10
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
interactive: false
|
||||
|
||||
displaced: Transition {
|
||||
NumberAnimation {
|
||||
properties: "y"
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Toast {
|
||||
Component.onCompleted: {
|
||||
if (typeof duration === "undefined") {
|
||||
show(text);
|
||||
}
|
||||
else {
|
||||
show(text, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model: ListModel {id: model}
|
||||
}
|
Reference in New Issue
Block a user