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:
不知火 Shiranui
2024-07-25 22:02:52 +08:00
committed by GitHub
parent 6b97d0f8ea
commit f9cd2e321c
14 changed files with 539 additions and 15 deletions

View File

@@ -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);
}
}
}

View File

@@ -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) {

View File

@@ -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);
}
}
}

View 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();
}
}
}
}

View 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}
}