Huge change that completely revamps the settings dialog and implements

per model settings as well as the ability to clone a model into a "character."
This also implements system prompts as well as quite a few bugfixes for
instance this fixes chatgpt.
This commit is contained in:
Adam Treat 2023-07-01 11:34:21 -04:00 committed by AT
parent 2a6c673c25
commit 6d9cdf228c
30 changed files with 2041 additions and 881 deletions

View File

@ -99,7 +99,6 @@ qt_add_qml_module(chat
qml/AboutDialog.qml
qml/Theme.qml
qml/ModelSettings.qml
qml/GenerationSettings.qml
qml/ApplicationSettings.qml
qml/LocalDocsSettings.qml
qml/MySettingsTab.qml
@ -119,6 +118,7 @@ qt_add_qml_module(chat
icons/db.svg
icons/settings.svg
icons/edit.svg
icons/image.svg
icons/trash.svg
icons/network.svg
icons/thumbs_up.svg

View File

@ -67,6 +67,7 @@ void Chat::connectLLM()
connect(this, &Chat::regenerateResponseRequested, m_llmodel, &ChatLLM::regenerateResponse, Qt::QueuedConnection);
connect(this, &Chat::resetResponseRequested, m_llmodel, &ChatLLM::resetResponse, Qt::QueuedConnection);
connect(this, &Chat::resetContextRequested, m_llmodel, &ChatLLM::resetContext, Qt::QueuedConnection);
connect(this, &Chat::processSystemPromptRequested, m_llmodel, &ChatLLM::processSystemPrompt, Qt::QueuedConnection);
connect(ModelList::globalInstance()->installedModels(), &InstalledModels::countChanged,
this, &Chat::handleModelInstalled, Qt::QueuedConnection);
@ -93,6 +94,11 @@ void Chat::reset()
m_chatModel->clear();
}
void Chat::processSystemPrompt()
{
emit processSystemPromptRequested();
}
bool Chat::isModelLoaded() const
{
return m_isModelLoaded;
@ -111,27 +117,10 @@ void Chat::resetResponseState()
emit responseStateChanged();
}
void Chat::prompt(const QString &prompt, const QString &prompt_template, int32_t n_predict,
int32_t top_k, float top_p, float temp, int32_t n_batch, float repeat_penalty,
int32_t repeat_penalty_tokens)
void Chat::prompt(const QString &prompt)
{
resetResponseState();
int threadCount = MySettings::globalInstance()->threadCount();
if (threadCount <= 0)
threadCount = std::min(4, (int32_t) std::thread::hardware_concurrency());
emit promptRequested(
m_collections,
prompt,
prompt_template,
n_predict,
top_k,
top_p,
temp,
n_batch,
repeat_penalty,
repeat_penalty_tokens,
threadCount);
emit promptRequested( m_collections, prompt);
}
void Chat::regenerateResponse()
@ -386,7 +375,10 @@ bool Chat::serialize(QDataStream &stream, int version) const
stream << m_id;
stream << m_name;
stream << m_userName;
stream << m_modelInfo.filename;
if (version > 4)
stream << m_modelInfo.id();
else
stream << m_modelInfo.filename();
if (version > 2)
stream << m_collections;
if (!m_llmodel->serialize(stream, version))
@ -405,16 +397,22 @@ bool Chat::deserialize(QDataStream &stream, int version)
stream >> m_userName;
emit nameChanged();
QString filename;
stream >> filename;
if (!ModelList::globalInstance()->contains(filename))
return false;
m_modelInfo = ModelList::globalInstance()->modelInfo(filename);
QString modelId;
stream >> modelId;
if (version > 4) {
if (!ModelList::globalInstance()->contains(modelId))
return false;
m_modelInfo = ModelList::globalInstance()->modelInfo(modelId);
} else {
if (!ModelList::globalInstance()->containsByFilename(modelId))
return false;
m_modelInfo = ModelList::globalInstance()->modelInfoByFilename(modelId);
}
emit modelInfoChanged();
// Prior to version 2 gptj models had a bug that fixed the kv_cache to F32 instead of F16 so
// unfortunately, we cannot deserialize these
if (version < 2 && m_modelInfo.filename.contains("gpt4all-j"))
if (version < 2 && m_modelInfo.filename().contains("gpt4all-j"))
return false;
if (version > 2) {
stream >> m_collections;

View File

@ -53,9 +53,9 @@ public:
ChatModel *chatModel() { return m_chatModel; }
Q_INVOKABLE void reset();
Q_INVOKABLE void processSystemPrompt();
Q_INVOKABLE bool isModelLoaded() const;
Q_INVOKABLE void prompt(const QString &prompt, const QString &prompt_template, int32_t n_predict,
int32_t top_k, float top_p, float temp, int32_t n_batch, float repeat_penalty, int32_t repeat_penalty_tokens);
Q_INVOKABLE void prompt(const QString &prompt);
Q_INVOKABLE void regenerateResponse();
Q_INVOKABLE void stopGenerating();
Q_INVOKABLE void newPromptResponsePair(const QString &prompt);
@ -102,12 +102,11 @@ Q_SIGNALS:
void responseChanged();
void responseInProgressChanged();
void responseStateChanged();
void promptRequested(const QList<QString> &collectionList, const QString &prompt, const QString &prompt_template,
int32_t n_predict, int32_t top_k, float top_p, float temp, int32_t n_batch, float repeat_penalty,
int32_t repeat_penalty_tokens, int32_t n_threads);
void promptRequested(const QList<QString> &collectionList, const QString &prompt);
void regenerateResponseRequested();
void resetResponseRequested();
void resetContextRequested();
void processSystemPromptRequested();
void modelChangeRequested(const ModelInfo &modelInfo);
void modelInfoChanged();
void recalcChanged();

View File

@ -5,7 +5,7 @@
#include <QDataStream>
#define CHAT_FORMAT_MAGIC 0xF5D553CC
#define CHAT_FORMAT_VERSION 4
#define CHAT_FORMAT_VERSION 5
class MyChatListModel: public ChatListModel { };
Q_GLOBAL_STATIC(MyChatListModel, chatListModelInstance)

View File

@ -69,6 +69,7 @@ ChatLLM::ChatLLM(Chat *parent, bool isServer)
, m_isServer(isServer)
, m_forceMetal(MySettings::globalInstance()->forceMetal())
, m_reloadingToChangeVariant(false)
, m_processedSystemPrompt(false)
{
moveToThread(&m_llmThread);
connect(this, &ChatLLM::sendStartup, Network::globalInstance(), &Network::sendStartup);
@ -123,7 +124,7 @@ void ChatLLM::handleForceMetalChanged(bool forceMetal)
bool ChatLLM::loadDefaultModel()
{
ModelInfo defaultModel = ModelList::globalInstance()->defaultModelInfo();
if (defaultModel.filename.isEmpty()) {
if (defaultModel.filename().isEmpty()) {
emit modelLoadingError(QString("Could not find any model to load"));
return false;
}
@ -145,7 +146,7 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo)
return true;
bool isChatGPT = modelInfo.isChatGPT;
QString filePath = modelInfo.dirpath + modelInfo.filename;
QString filePath = modelInfo.dirpath + modelInfo.filename();
QFileInfo fileInfo(filePath);
// We have a live model, but it isn't the one we want
@ -186,6 +187,11 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo)
#endif
restoreState();
emit isModelLoadedChanged(true);
Q_ASSERT(!m_modelInfo.filename().isEmpty());
if (m_modelInfo.filename().isEmpty())
emit modelLoadingError(QString("Modelinfo is left null for %1").arg(modelInfo.filename()));
else
processSystemPrompt();
return true;
} else {
// Release the memory since we have to switch to a different model.
@ -237,7 +243,7 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo)
if (!m_isServer)
LLModelStore::globalInstance()->releaseModel(m_llModelInfo); // release back into the store
m_llModelInfo = LLModelInfo();
emit modelLoadingError(QString("Could not load model due to invalid model file for %1").arg(modelInfo.filename));
emit modelLoadingError(QString("Could not load model due to invalid model file for %1").arg(modelInfo.filename()));
} else {
switch (m_llModelInfo.model->implementation().modelType[0]) {
case 'L': m_llModelType = LLModelType::LLAMA_; break;
@ -251,7 +257,7 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo)
if (!m_isServer)
LLModelStore::globalInstance()->releaseModel(m_llModelInfo); // release back into the store
m_llModelInfo = LLModelInfo();
emit modelLoadingError(QString("Could not determine model type for %1").arg(modelInfo.filename));
emit modelLoadingError(QString("Could not determine model type for %1").arg(modelInfo.filename()));
}
}
}
@ -259,7 +265,7 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo)
if (!m_isServer)
LLModelStore::globalInstance()->releaseModel(m_llModelInfo); // release back into the store
m_llModelInfo = LLModelInfo();
emit modelLoadingError(QString("Could not load model due to invalid format for %1").arg(modelInfo.filename));
emit modelLoadingError(QString("Could not load model due to invalid format for %1").arg(modelInfo.filename()));
}
}
#if defined(DEBUG_MODEL_LOADING)
@ -282,12 +288,13 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo)
if (!m_isServer)
LLModelStore::globalInstance()->releaseModel(m_llModelInfo); // release back into the store
m_llModelInfo = LLModelInfo();
emit modelLoadingError(QString("Could not find file for model %1").arg(modelInfo.filename));
emit modelLoadingError(QString("Could not find file for model %1").arg(modelInfo.filename()));
}
if (m_llModelInfo.model)
setModelInfo(modelInfo);
processSystemPrompt();
return m_llModelInfo.model;
}
@ -323,6 +330,7 @@ void ChatLLM::resetResponse()
void ChatLLM::resetContext()
{
regenerateResponse();
m_processedSystemPrompt = false;
m_ctx = LLModel::PromptContext();
}
@ -412,9 +420,25 @@ bool ChatLLM::handleRecalculate(bool isRecalc)
}
return !m_stopGenerating;
}
bool ChatLLM::prompt(const QList<QString> &collectionList, const QString &prompt)
{
if (!m_processedSystemPrompt)
processSystemPrompt();
const QString promptTemplate = MySettings::globalInstance()->modelPromptTemplate(m_modelInfo);
const int32_t n_predict = MySettings::globalInstance()->modelMaxLength(m_modelInfo);
const int32_t top_k = MySettings::globalInstance()->modelTopK(m_modelInfo);
const float top_p = MySettings::globalInstance()->modelTopP(m_modelInfo);
const float temp = MySettings::globalInstance()->modelTemperature(m_modelInfo);
const int32_t n_batch = MySettings::globalInstance()->modelPromptBatchSize(m_modelInfo);
const float repeat_penalty = MySettings::globalInstance()->modelRepeatPenalty(m_modelInfo);
const int32_t repeat_penalty_tokens = MySettings::globalInstance()->modelRepeatPenaltyTokens(m_modelInfo);
return promptInternal(collectionList, prompt, promptTemplate, n_predict, top_k, top_p, temp, n_batch,
repeat_penalty, repeat_penalty_tokens);
}
bool ChatLLM::prompt(const QList<QString> &collectionList, const QString &prompt, const QString &prompt_template, int32_t n_predict, int32_t top_k,
float top_p, float temp, int32_t n_batch, float repeat_penalty, int32_t repeat_penalty_tokens, int n_threads)
bool ChatLLM::promptInternal(const QList<QString> &collectionList, const QString &prompt, const QString &promptTemplate,
int32_t n_predict, int32_t top_k, float top_p, float temp, int32_t n_batch, float repeat_penalty,
int32_t repeat_penalty_tokens)
{
if (!isModelLoaded())
return false;
@ -430,10 +454,14 @@ bool ChatLLM::prompt(const QList<QString> &collectionList, const QString &prompt
augmentedTemplate.append("### Context:");
for (const ResultInfo &info : databaseResults)
augmentedTemplate.append(info.text);
augmentedTemplate.append(prompt_template);
augmentedTemplate.append(promptTemplate);
QString instructPrompt = augmentedTemplate.join("\n").arg(prompt);
int n_threads = MySettings::globalInstance()->threadCount();
if (n_threads <= 0)
n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
m_stopGenerating = false;
auto promptFunc = std::bind(&ChatLLM::handlePrompt, this, std::placeholders::_1);
auto responseFunc = std::bind(&ChatLLM::handleResponse, this, std::placeholders::_1,
@ -515,7 +543,7 @@ void ChatLLM::reloadModel()
qDebug() << "reloadModel" << m_llmThread.objectName() << m_llModelInfo.model;
#endif
const ModelInfo m = modelInfo();
if (m.name.isEmpty())
if (m.name().isEmpty())
loadDefaultModel();
else
loadModel(m);
@ -581,6 +609,27 @@ bool ChatLLM::handleNameRecalculate(bool isRecalc)
return true;
}
bool ChatLLM::handleSystemPrompt(int32_t token)
{
Q_UNUSED(token);
qt_noop();
return true;
}
bool ChatLLM::handleSystemResponse(int32_t token, const std::string &response)
{
Q_UNUSED(token);
Q_UNUSED(response);
return false;
}
bool ChatLLM::handleSystemRecalculate(bool isRecalc)
{
Q_UNUSED(isRecalc);
Q_UNREACHABLE();
return true;
}
bool ChatLLM::serialize(QDataStream &stream, int version)
{
if (version > 1) {
@ -701,3 +750,45 @@ void ChatLLM::restoreState()
m_state.clear();
m_state.resize(0);
}
void ChatLLM::processSystemPrompt()
{
Q_ASSERT(isModelLoaded());
if (!isModelLoaded() || m_processedSystemPrompt || m_isServer)
return;
auto promptFunc = std::bind(&ChatLLM::handleSystemPrompt, this, std::placeholders::_1);
auto responseFunc = std::bind(&ChatLLM::handleSystemResponse, this, std::placeholders::_1,
std::placeholders::_2);
auto recalcFunc = std::bind(&ChatLLM::handleSystemRecalculate, this, std::placeholders::_1);
const std::string systemPrompt = MySettings::globalInstance()->modelSystemPrompt(m_modelInfo).toStdString();
const int32_t n_predict = MySettings::globalInstance()->modelMaxLength(m_modelInfo);
const int32_t top_k = MySettings::globalInstance()->modelTopK(m_modelInfo);
const float top_p = MySettings::globalInstance()->modelTopP(m_modelInfo);
const float temp = MySettings::globalInstance()->modelTemperature(m_modelInfo);
const int32_t n_batch = MySettings::globalInstance()->modelPromptBatchSize(m_modelInfo);
const float repeat_penalty = MySettings::globalInstance()->modelRepeatPenalty(m_modelInfo);
const int32_t repeat_penalty_tokens = MySettings::globalInstance()->modelRepeatPenaltyTokens(m_modelInfo);
int n_threads = MySettings::globalInstance()->threadCount();
if (n_threads <= 0)
n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
m_ctx.n_predict = n_predict;
m_ctx.top_k = top_k;
m_ctx.top_p = top_p;
m_ctx.temp = temp;
m_ctx.n_batch = n_batch;
m_ctx.repeat_penalty = repeat_penalty;
m_ctx.repeat_last_n = repeat_penalty_tokens;
m_llModelInfo.model->setThreadCount(n_threads);
#if defined(DEBUG)
printf("%s", qPrintable(QString::fromStdString(systemPrompt)));
fflush(stdout);
#endif
m_llModelInfo.model->prompt(systemPrompt, promptFunc, responseFunc, recalcFunc, m_ctx);
#if defined(DEBUG)
printf("\n");
fflush(stdout);
#endif
m_processedSystemPrompt = true;
}

View File

@ -97,9 +97,7 @@ public:
bool deserialize(QDataStream &stream, int version);
public Q_SLOTS:
bool prompt(const QList<QString> &collectionList, const QString &prompt, const QString &prompt_template,
int32_t n_predict, int32_t top_k, float top_p, float temp, int32_t n_batch, float repeat_penalty,
int32_t repeat_penalty_tokens, int32_t n_threads);
bool prompt(const QList<QString> &collectionList, const QString &prompt);
bool loadDefaultModel();
bool loadModel(const ModelInfo &modelInfo);
void modelChangeRequested(const ModelInfo &modelInfo);
@ -111,6 +109,7 @@ public Q_SLOTS:
void handleShouldBeLoadedChanged();
void handleThreadStarted();
void handleForceMetalChanged(bool forceMetal);
void processSystemPrompt();
Q_SIGNALS:
void recalcChanged();
@ -131,12 +130,18 @@ Q_SIGNALS:
void modelInfoChanged(const ModelInfo &modelInfo);
protected:
bool promptInternal(const QList<QString> &collectionList, const QString &prompt, const QString &promptTemplate,
int32_t n_predict, int32_t top_k, float top_p, float temp, int32_t n_batch, float repeat_penalty,
int32_t repeat_penalty_tokens);
bool handlePrompt(int32_t token);
bool handleResponse(int32_t token, const std::string &response);
bool handleRecalculate(bool isRecalc);
bool handleNamePrompt(int32_t token);
bool handleNameResponse(int32_t token, const std::string &response);
bool handleNameRecalculate(bool isRecalc);
bool handleSystemPrompt(int32_t token);
bool handleSystemResponse(int32_t token, const std::string &response);
bool handleSystemRecalculate(bool isRecalc);
void saveState();
void restoreState();
@ -160,6 +165,7 @@ private:
bool m_isServer;
bool m_forceMetal;
bool m_reloadingToChangeVariant;
bool m_processedSystemPrompt;
};
#endif // CHATLLM_H

View File

@ -175,7 +175,7 @@ void Download::installModel(const QString &modelFile, const QString &apiKey)
return;
Network::globalInstance()->sendInstallModel(modelFile);
QString filePath = MySettings::globalInstance()->modelPath() + modelFile + ".txt";
QString filePath = MySettings::globalInstance()->modelPath() + modelFile;
QFile file(filePath);
if (file.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text)) {
QTextStream stream(&file);

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#7d7d8e" viewBox="0 0 512 512"><path d="M464 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h416c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM112 120c-30.928 0-56 25.072-56 56s25.072 56 56 56 56-25.072 56-56-25.072-56-56-56zM64 384h384V272l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L208 320l-55.515-55.515c-4.686-4.686-12.284-4.686-16.971 0L64 336v48z"/></svg>
<!--
Font Awesome Free 5.2.0 by @fontawesome - https://fontawesome.com
License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
-->

After

Width:  |  Height:  |  Size: 606 B

View File

@ -204,12 +204,12 @@ Window {
anchors.horizontalCenterOffset: window.width >= 950 ? 0 : Math.max(-((950 - window.width) / 2), -99.5)
enabled: !currentChat.isServer
model: ModelList.installedModels
valueRole: "filename"
valueRole: "id"
textRole: "name"
property string currentModelName: ""
function updateCurrentModelName() {
var info = ModelList.modelInfo(currentChat.modelInfo.filename);
comboBox.currentModelName = info.name !== "" ? info.name : info.filename;
var info = ModelList.modelInfo(currentChat.modelInfo.id);
comboBox.currentModelName = info.name;
}
Connections {
target: currentChat
@ -217,6 +217,16 @@ Window {
comboBox.updateCurrentModelName();
}
}
Connections {
target: window
function onCurrentChatChanged() {
comboBox.updateCurrentModelName();
}
}
background: Rectangle {
color: theme.backgroundDark
radius: 10
}
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
leftPadding: 10
@ -233,7 +243,7 @@ Window {
delegate: ItemDelegate {
width: comboBox.width
contentItem: Text {
text: name !== "" ? name : filename
text: name
color: theme.textColor
font: comboBox.font
elide: Text.ElideRight
@ -557,6 +567,7 @@ Window {
onClicked: {
Network.sendResetContext(chatModel.count)
currentChat.reset();
currentChat.processSystemPrompt();
}
}
@ -870,6 +881,7 @@ Window {
}
MyButton {
id: myButton
visible: chatModel.count && !currentChat.isServer
Image {
anchors.verticalCenter: parent.verticalCenter
@ -894,19 +906,17 @@ Window {
chatModel.updateThumbsUpState(index, false);
chatModel.updateThumbsDownState(index, false);
chatModel.updateNewResponse(index, "");
currentChat.prompt(listElement.prompt,
MySettings.promptTemplate,
MySettings.maxLength,
MySettings.topK,
MySettings.topP,
MySettings.temperature,
MySettings.promptBatchSize,
MySettings.repeatPenalty,
MySettings.repeatPenaltyTokens)
currentChat.prompt(listElement.prompt)
}
}
}
}
background: Rectangle {
border.color: myButton.down ? theme.backgroundLightest : theme.buttonBorder
border.width: 2
radius: 10
color: myButton.hovered ? theme.backgroundLighter : theme.backgroundLight
}
anchors.bottom: textInputView.top
anchors.horizontalCenter: textInputView.horizontalCenter
anchors.bottomMargin: 20

View File

@ -64,7 +64,9 @@
"parameters": "7 billion",
"quant": "q4_0",
"type": "MPT",
"description": "<strong>Best overall smaller model</strong><br><ul><li>Fast responses<li>Chat based<li>Trained by Mosaic ML<li>Cannot be used commercially</ul>"
"description": "<strong>Best overall smaller model</strong><br><ul><li>Fast responses<li>Chat based<li>Trained by Mosaic ML<li>Cannot be used commercially</ul>",
"promptTemplate": "<|im_start|>user\n%1<|im_end|><|im_start|>assistant\n",
"systemPrompt": "<|im_start|>system\n- You are a helpful assistant chatbot trained by MosaicML.\n- You answer questions.\n- You are excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user.\n- You are more than just an information source, you are also able to write poetry, short stories, and make jokes.<|im_end|>"
},
{
"order": "g",

View File

@ -1,10 +1,142 @@
#include "modellist.h"
#include "mysettings.h"
#include "network.h"
#include <algorithm>
//#define USE_LOCAL_MODELSJSON
QString ModelInfo::id() const
{
return m_id;
}
void ModelInfo::setId(const QString &id)
{
m_id = id;
}
QString ModelInfo::name() const
{
return MySettings::globalInstance()->modelName(*this);
}
void ModelInfo::setName(const QString &name)
{
if (isClone) MySettings::globalInstance()->setModelName(*this, name, isClone /*force*/);
m_name = name;
}
QString ModelInfo::filename() const
{
return MySettings::globalInstance()->modelFilename(*this);
}
void ModelInfo::setFilename(const QString &filename)
{
if (isClone) MySettings::globalInstance()->setModelFilename(*this, filename, isClone /*force*/);
m_filename = filename;
}
double ModelInfo::temperature() const
{
return MySettings::globalInstance()->modelTemperature(*this);
}
void ModelInfo::setTemperature(double t)
{
if (isClone) MySettings::globalInstance()->setModelTemperature(*this, t, isClone /*force*/);
m_temperature = t;
}
double ModelInfo::topP() const
{
return MySettings::globalInstance()->modelTopP(*this);
}
void ModelInfo::setTopP(double p)
{
if (isClone) MySettings::globalInstance()->setModelTopP(*this, p, isClone /*force*/);
m_topP = p;
}
int ModelInfo::topK() const
{
return MySettings::globalInstance()->modelTopK(*this);
}
void ModelInfo::setTopK(int k)
{
if (isClone) MySettings::globalInstance()->setModelTopK(*this, k, isClone /*force*/);
m_topK = k;
}
int ModelInfo::maxLength() const
{
return MySettings::globalInstance()->modelMaxLength(*this);
}
void ModelInfo::setMaxLength(int l)
{
if (isClone) MySettings::globalInstance()->setModelMaxLength(*this, l, isClone /*force*/);
m_maxLength = l;
}
int ModelInfo::promptBatchSize() const
{
return MySettings::globalInstance()->modelPromptBatchSize(*this);
}
void ModelInfo::setPromptBatchSize(int s)
{
if (isClone) MySettings::globalInstance()->setModelPromptBatchSize(*this, s, isClone /*force*/);
m_promptBatchSize = s;
}
double ModelInfo::repeatPenalty() const
{
return MySettings::globalInstance()->modelRepeatPenalty(*this);
}
void ModelInfo::setRepeatPenalty(double p)
{
if (isClone) MySettings::globalInstance()->setModelRepeatPenalty(*this, p, isClone /*force*/);
m_repeatPenalty = p;
}
int ModelInfo::repeatPenaltyTokens() const
{
return MySettings::globalInstance()->modelRepeatPenaltyTokens(*this);
}
void ModelInfo::setRepeatPenaltyTokens(int t)
{
if (isClone) MySettings::globalInstance()->setModelRepeatPenaltyTokens(*this, t, isClone /*force*/);
m_repeatPenaltyTokens = t;
}
QString ModelInfo::promptTemplate() const
{
return MySettings::globalInstance()->modelPromptTemplate(*this);
}
void ModelInfo::setPromptTemplate(const QString &t)
{
if (isClone) MySettings::globalInstance()->setModelPromptTemplate(*this, t, isClone /*force*/);
m_promptTemplate = t;
}
QString ModelInfo::systemPrompt() const
{
return MySettings::globalInstance()->modelSystemPrompt(*this);
}
void ModelInfo::setSystemPrompt(const QString &p)
{
if (isClone) MySettings::globalInstance()->setModelSystemPrompt(*this, p, isClone /*force*/);
m_systemPrompt = p;
}
InstalledModels::InstalledModels(QObject *parent)
: QSortFilterProxyModel(parent)
{
@ -27,11 +159,11 @@ int InstalledModels::count() const
return rowCount();
}
QString InstalledModels::firstFilename() const
QString InstalledModels::firstId() const
{
if (rowCount() > 0) {
QModelIndex firstIndex = index(0, 0);
return sourceModel()->data(firstIndex, ModelList::FilenameRole).toString();
return sourceModel()->data(firstIndex, ModelList::IdRole).toString();
} else {
return QString();
}
@ -96,9 +228,23 @@ ModelList::ModelList()
m_watcher->addPath(exePath);
m_watcher->addPath(MySettings::globalInstance()->modelPath());
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &ModelList::updateModelsFromDirectory);
connect(MySettings::globalInstance(), &MySettings::modelPathChanged, this, &ModelList::updateModelList);
connect(MySettings::globalInstance(), &MySettings::modelPathChanged, this, &ModelList::updateModelsFromDirectory);
connect(MySettings::globalInstance(), &MySettings::modelPathChanged, this, &ModelList::updateModelsFromJson);
connect(MySettings::globalInstance(), &MySettings::modelPathChanged, this, &ModelList::updateModelsFromSettings);
connect(MySettings::globalInstance(), &MySettings::nameChanged, this, &ModelList::updateDataForSettings);
connect(MySettings::globalInstance(), &MySettings::temperatureChanged, this, &ModelList::updateDataForSettings);
connect(MySettings::globalInstance(), &MySettings::topPChanged, this, &ModelList::updateDataForSettings);
connect(MySettings::globalInstance(), &MySettings::topKChanged, this, &ModelList::updateDataForSettings);
connect(MySettings::globalInstance(), &MySettings::maxLengthChanged, this, &ModelList::updateDataForSettings);
connect(MySettings::globalInstance(), &MySettings::promptBatchSizeChanged, this, &ModelList::updateDataForSettings);
connect(MySettings::globalInstance(), &MySettings::repeatPenaltyChanged, this, &ModelList::updateDataForSettings);
connect(MySettings::globalInstance(), &MySettings::repeatPenaltyTokensChanged, this, &ModelList::updateDataForSettings);;
connect(MySettings::globalInstance(), &MySettings::promptTemplateChanged, this, &ModelList::updateDataForSettings);
connect(MySettings::globalInstance(), &MySettings::systemPromptChanged, this, &ModelList::updateDataForSettings);
updateModelsFromJson();
updateModelsFromSettings();
updateModelsFromDirectory();
updateModelList();
}
QString ModelList::incompleteDownloadPath(const QString &modelFile)
@ -124,19 +270,19 @@ const QList<QString> ModelList::userDefaultModelList() const
QList<QString> models;
bool foundUserDefault = false;
for (ModelInfo *info : m_models) {
if (info->installed && (info->name == userDefaultModelName || info->filename == userDefaultModelName)) {
if (info->installed && info->id() == userDefaultModelName) {
foundUserDefault = true;
models.prepend(info->name.isEmpty() ? info->filename : info->name);
models.prepend(info->name());
} else if (info->installed) {
models.append(info->name.isEmpty() ? info->filename : info->name);
models.append(info->name());
}
}
const QString defaultFileName = "Application default";
const QString defaultId = "Application default";
if (foundUserDefault)
models.append(defaultFileName);
models.append(defaultId);
else
models.prepend(defaultFileName);
models.prepend(defaultId);
return models;
}
@ -166,11 +312,11 @@ ModelInfo ModelList::defaultModelInfo() const
break;
// If we don't have a user specified default, but *do* have a default setting and match, then use it
if (!hasUserDefaultName && hasDefaultName && (defaultModel->name == defaultModelName || defaultModel->filename == defaultModelName))
if (!hasUserDefaultName && hasDefaultName && (defaultModel->id() == defaultModelName))
break;
// If we have a user specified default and match, then use it
if (hasUserDefaultName && (defaultModel->name == userDefaultModelName || defaultModel->filename == userDefaultModelName))
if (hasUserDefaultName && (defaultModel->id() == userDefaultModelName))
break;
}
if (defaultModel)
@ -178,14 +324,28 @@ ModelInfo ModelList::defaultModelInfo() const
return ModelInfo();
}
bool ModelList::contains(const QString &filename) const
bool ModelList::contains(const QString &id) const
{
QMutexLocker locker(&m_mutex);
return m_modelMap.contains(filename);
return m_modelMap.contains(id);
}
bool ModelList::containsByFilename(const QString &filename) const
{
QMutexLocker locker(&m_mutex);
for (ModelInfo *info : m_models)
if (info->filename() == filename)
return true;
return false;
}
bool ModelList::lessThan(const ModelInfo* a, const ModelInfo* b)
{
// Rule 0: Non-clone before clone
if (a->isClone != b->isClone) {
return !a->isClone;
}
// Rule 1: Non-empty 'order' before empty
if (a->order.isEmpty() != b->order.isEmpty()) {
return !a->order.isEmpty();
@ -196,27 +356,32 @@ bool ModelList::lessThan(const ModelInfo* a, const ModelInfo* b)
return a->order < b->order;
}
// Rule 3: Both 'order' are empty, sort by filename
return a->filename < b->filename;
// Rule 3: Both 'order' are empty, sort by id
return a->id() < b->id();
}
void ModelList::addModel(const QString &filename)
void ModelList::addModel(const QString &id)
{
const bool hasModel = contains(filename);
const bool hasModel = contains(id);
Q_ASSERT(!hasModel);
if (hasModel) {
qWarning() << "ERROR: model list already contains" << filename;
qWarning() << "ERROR: model list already contains" << id;
return;
}
beginInsertRows(QModelIndex(), m_models.size(), m_models.size());
int modelSizeBefore = 0;
int modelSizeAfter = 0;
{
QMutexLocker locker(&m_mutex);
modelSizeBefore = m_models.size();
}
beginInsertRows(QModelIndex(), modelSizeBefore, modelSizeBefore);
{
QMutexLocker locker(&m_mutex);
ModelInfo *info = new ModelInfo;
info->filename = filename;
info->setId(id);
m_models.append(info);
m_modelMap.insert(filename, info);
m_modelMap.insert(id, info);
std::stable_sort(m_models.begin(), m_models.end(), ModelList::lessThan);
modelSizeAfter = m_models.size();
}
@ -235,10 +400,12 @@ int ModelList::rowCount(const QModelIndex &parent) const
QVariant ModelList::dataInternal(const ModelInfo *info, int role) const
{
switch (role) {
case IdRole:
return info->id();
case NameRole:
return info->name;
return info->name();
case FilenameRole:
return info->filename;
return info->filename();
case DirpathRole:
return info->dirpath;
case FilesizeRole:
@ -287,15 +454,35 @@ QVariant ModelList::dataInternal(const ModelInfo *info, int role) const
return info->quant;
case TypeRole:
return info->type;
case IsCloneRole:
return info->isClone;
case TemperatureRole:
return info->temperature();
case TopPRole:
return info->topP();
case TopKRole:
return info->topK();
case MaxLengthRole:
return info->maxLength();
case PromptBatchSizeRole:
return info->promptBatchSize();
case RepeatPenaltyRole:
return info->repeatPenalty();
case RepeatPenaltyTokensRole:
return info->repeatPenaltyTokens();
case PromptTemplateRole:
return info->promptTemplate();
case SystemPromptRole:
return info->systemPrompt();
}
return QVariant();
}
QVariant ModelList::data(const QString &filename, int role) const
QVariant ModelList::data(const QString &id, int role) const
{
QMutexLocker locker(&m_mutex);
ModelInfo *info = m_modelMap.value(filename);
ModelInfo *info = m_modelMap.value(id);
return dataInternal(info, role);
}
@ -308,7 +495,7 @@ QVariant ModelList::data(const QModelIndex &index, int role) const
return dataInternal(info, role);
}
void ModelList::updateData(const QString &filename, int role, const QVariant &value)
void ModelList::updateData(const QString &id, int role, const QVariant &value)
{
int modelSize;
bool updateInstalled;
@ -316,23 +503,25 @@ void ModelList::updateData(const QString &filename, int role, const QVariant &va
int index;
{
QMutexLocker locker(&m_mutex);
if (!m_modelMap.contains(filename)) {
qWarning() << "ERROR: cannot update as model map does not contain" << filename;
if (!m_modelMap.contains(id)) {
qWarning() << "ERROR: cannot update as model map does not contain" << id;
return;
}
ModelInfo *info = m_modelMap.value(filename);
ModelInfo *info = m_modelMap.value(id);
index = m_models.indexOf(info);
if (index == -1) {
qWarning() << "ERROR: cannot update as model list does not contain" << filename;
qWarning() << "ERROR: cannot update as model list does not contain" << id;
return;
}
switch (role) {
case IdRole:
info->setId(value.toString()); break;
case NameRole:
info->name = value.toString(); break;
info->setName(value.toString()); break;
case FilenameRole:
info->filename = value.toString(); break;
info->setFilename(value.toString()); break;
case DirpathRole:
info->dirpath = value.toString(); break;
case FilesizeRole:
@ -381,15 +570,35 @@ void ModelList::updateData(const QString &filename, int role, const QVariant &va
info->quant = value.toString(); break;
case TypeRole:
info->type = value.toString(); break;
case IsCloneRole:
info->isClone = value.toBool(); break;
case TemperatureRole:
info->setTemperature(value.toDouble()); break;
case TopPRole:
info->setTopP(value.toDouble()); break;
case TopKRole:
info->setTopK(value.toInt()); break;
case MaxLengthRole:
info->setMaxLength(value.toInt()); break;
case PromptBatchSizeRole:
info->setPromptBatchSize(value.toInt()); break;
case RepeatPenaltyRole:
info->setRepeatPenalty(value.toDouble()); break;
case RepeatPenaltyTokensRole:
info->setRepeatPenaltyTokens(value.toInt()); break;
case PromptTemplateRole:
info->setPromptTemplate(value.toString()); break;
case SystemPromptRole:
info->setSystemPrompt(value.toString()); break;
}
// Extra guarantee that these always remains in sync with filesystem
QFileInfo fileInfo(info->dirpath + info->filename);
QFileInfo fileInfo(info->dirpath + info->filename());
if (info->installed != fileInfo.exists()) {
info->installed = fileInfo.exists();
updateInstalled = true;
}
QFileInfo incompleteInfo(incompleteDownloadPath(info->filename));
QFileInfo incompleteInfo(incompleteDownloadPath(info->filename()));
if (info->isIncomplete != incompleteInfo.exists()) {
info->isIncomplete = incompleteInfo.exists();
updateIncomplete = true;
@ -402,12 +611,120 @@ void ModelList::updateData(const QString &filename, int role, const QVariant &va
emit userDefaultModelListChanged();
}
ModelInfo ModelList::modelInfo(const QString &filename) const
ModelInfo ModelList::modelInfo(const QString &id) const
{
QMutexLocker locker(&m_mutex);
if (!m_modelMap.contains(filename))
if (!m_modelMap.contains(id))
return ModelInfo();
return *m_modelMap.value(filename);
return *m_modelMap.value(id);
}
ModelInfo ModelList::modelInfoByFilename(const QString &filename) const
{
QMutexLocker locker(&m_mutex);
for (ModelInfo *info : m_models)
if (info->filename() == filename)
return *info;
return ModelInfo();
}
bool ModelList::isUniqueName(const QString &name) const
{
QMutexLocker locker(&m_mutex);
for (const ModelInfo *info : m_models) {
if(info->name() == name)
return false;
}
return true;
}
QString ModelList::clone(const ModelInfo &model)
{
const QString id = Network::globalInstance()->generateUniqueId();
addModel(id);
updateData(id, ModelList::IsCloneRole, true);
updateData(id, ModelList::NameRole, uniqueModelName(model));
updateData(id, ModelList::FilenameRole, model.filename());
updateData(id, ModelList::DirpathRole, model.dirpath);
updateData(id, ModelList::InstalledRole, model.installed);
updateData(id, ModelList::ChatGPTRole, model.isChatGPT);
updateData(id, ModelList::TemperatureRole, model.temperature());
updateData(id, ModelList::TopPRole, model.topP());
updateData(id, ModelList::TopKRole, model.topK());
updateData(id, ModelList::MaxLengthRole, model.maxLength());
updateData(id, ModelList::PromptBatchSizeRole, model.promptBatchSize());
updateData(id, ModelList::RepeatPenaltyRole, model.repeatPenalty());
updateData(id, ModelList::RepeatPenaltyTokensRole, model.repeatPenaltyTokens());
updateData(id, ModelList::PromptTemplateRole, model.promptTemplate());
updateData(id, ModelList::SystemPromptRole, model.systemPrompt());
return id;
}
void ModelList::remove(const ModelInfo &model)
{
Q_ASSERT(model.isClone);
if (!model.isClone)
return;
const bool hasModel = contains(model.id());
Q_ASSERT(hasModel);
if (!hasModel) {
qWarning() << "ERROR: model list does not contain" << model.id();
return;
}
int indexOfModel = 0;
int modelSizeAfter = 0;
{
QMutexLocker locker(&m_mutex);
ModelInfo *info = m_modelMap.value(model.id());
indexOfModel = m_models.indexOf(info);
}
beginRemoveRows(QModelIndex(), indexOfModel, indexOfModel);
{
QMutexLocker locker(&m_mutex);
ModelInfo *info = m_models.takeAt(indexOfModel);
m_modelMap.remove(info->id());
delete info;
modelSizeAfter = m_models.size();
}
endRemoveRows();
emit dataChanged(index(0, 0), index(modelSizeAfter - 1, 0));
emit userDefaultModelListChanged();
MySettings::globalInstance()->eraseModel(model);
}
QString ModelList::uniqueModelName(const ModelInfo &model) const
{
QMutexLocker locker(&m_mutex);
QRegularExpression re("^(.*)~(\\d+)$");
QRegularExpressionMatch match = re.match(model.name());
QString baseName;
if (match.hasMatch())
baseName = match.captured(1);
else
baseName = model.name();
int maxSuffixNumber = 0;
bool baseNameExists = false;
for (const ModelInfo *info : m_models) {
if(info->name() == baseName)
baseNameExists = true;
QRegularExpressionMatch match = re.match(info->name());
if (match.hasMatch()) {
QString currentBaseName = match.captured(1);
int currentSuffixNumber = match.captured(2).toInt();
if (currentBaseName == baseName && currentSuffixNumber > maxSuffixNumber)
maxSuffixNumber = currentSuffixNumber;
}
}
if (baseNameExists)
return baseName + "~" + QString::number(maxSuffixNumber + 1);
return baseName;
}
QString ModelList::modelDirPath(const QString &modelName, bool isChatGPT)
@ -456,12 +773,25 @@ void ModelList::updateModelsFromDirectory()
if (!info.exists())
continue;
if (!contains(filename))
addModel(filename);
QVector<QString> modelsById;
{
QMutexLocker locker(&m_mutex);
for (ModelInfo *info : m_models)
if (info->filename() == filename)
modelsById.append(info->id());
}
updateData(filename, ChatGPTRole, filename.startsWith("chatgpt-"));
updateData(filename, DirpathRole, path);
updateData(filename, FilesizeRole, toFileSize(info.size()));
if (modelsById.isEmpty()) {
addModel(filename);
modelsById.append(filename);
}
for (const QString &id : modelsById) {
updateData(id, FilenameRole, filename);
updateData(id, ChatGPTRole, filename.startsWith("chatgpt-"));
updateData(id, DirpathRole, path);
updateData(id, FilesizeRole, toFileSize(info.size()));
}
}
}
}
@ -470,9 +800,17 @@ void ModelList::updateModelsFromDirectory()
processDirectory(exePath);
if (localPath != exePath)
processDirectory(localPath);
if (installedModels()->count()) {
const QString firstModel =
installedModels()->firstId();
QSettings settings;
settings.setValue("defaultModel", firstModel);
settings.sync();
}
}
void ModelList::updateModelList()
void ModelList::updateModelsFromJson()
{
#if defined(USE_LOCAL_MODELSJSON)
QUrl jsonUrl("file://" + QDir::homePath() + "/dev/large_language_models/gpt4all/gpt4all-chat/metadata/models.json");
@ -498,8 +836,9 @@ void ModelList::updateModelList()
delete jsonReply;
}
static bool operator==(const ModelInfo& lhs, const ModelInfo& rhs) {
return lhs.filename == rhs.filename && lhs.md5sum == rhs.md5sum;
void ModelList::updateDataForSettings()
{
emit dataChanged(index(0, 0), index(m_models.size() - 1, 0));
}
static bool compareVersions(const QString &a, const QString &b) {
@ -566,24 +905,45 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData)
modelFilesize = ModelList::toFileSize(modelFilesize.toULongLong());
if (!contains(modelFilename))
addModel(modelFilename);
const QString id = modelName;
Q_ASSERT(!id.isEmpty());
if (!modelName.isEmpty())
updateData(modelFilename, ModelList::NameRole, modelName);
updateData(modelFilename, ModelList::FilesizeRole, modelFilesize);
updateData(modelFilename, ModelList::Md5sumRole, modelMd5sum);
updateData(modelFilename, ModelList::DefaultRole, isDefault);
updateData(modelFilename, ModelList::DescriptionRole, description);
updateData(modelFilename, ModelList::RequiresVersionRole, requiresVersion);
updateData(modelFilename, ModelList::DeprecatedVersionRole, deprecatedVersion);
updateData(modelFilename, ModelList::UrlRole, url);
updateData(modelFilename, ModelList::DisableGUIRole, disableGUI);
updateData(modelFilename, ModelList::OrderRole, order);
updateData(modelFilename, ModelList::RamrequiredRole, ramrequired);
updateData(modelFilename, ModelList::ParametersRole, parameters);
updateData(modelFilename, ModelList::QuantRole, quant);
updateData(modelFilename, ModelList::TypeRole, type);
if (!contains(id))
addModel(id);
updateData(id, ModelList::NameRole, modelName);
updateData(id, ModelList::FilenameRole, modelFilename);
updateData(id, ModelList::FilesizeRole, modelFilesize);
updateData(id, ModelList::Md5sumRole, modelMd5sum);
updateData(id, ModelList::DefaultRole, isDefault);
updateData(id, ModelList::DescriptionRole, description);
updateData(id, ModelList::RequiresVersionRole, requiresVersion);
updateData(id, ModelList::DeprecatedVersionRole, deprecatedVersion);
updateData(id, ModelList::UrlRole, url);
updateData(id, ModelList::DisableGUIRole, disableGUI);
updateData(id, ModelList::OrderRole, order);
updateData(id, ModelList::RamrequiredRole, ramrequired);
updateData(id, ModelList::ParametersRole, parameters);
updateData(id, ModelList::QuantRole, quant);
updateData(id, ModelList::TypeRole, type);
if (obj.contains("temperature"))
updateData(id, ModelList::TemperatureRole, obj["temperature"].toDouble());
if (obj.contains("topP"))
updateData(id, ModelList::TopPRole, obj["topP"].toDouble());
if (obj.contains("topK"))
updateData(id, ModelList::TopKRole, obj["topK"].toInt());
if (obj.contains("maxLength"))
updateData(id, ModelList::MaxLengthRole, obj["maxLength"].toInt());
if (obj.contains("promptBatchSize"))
updateData(id, ModelList::PromptBatchSizeRole, obj["promptBatchSize"].toInt());
if (obj.contains("repeatPenalty"))
updateData(id, ModelList::RepeatPenaltyRole, obj["repeatPenalty"].toDouble());
if (obj.contains("repeatPenaltyTokens"))
updateData(id, ModelList::RepeatPenaltyTokensRole, obj["repeatPenaltyTokens"].toInt());
if (obj.contains("promptTemplate"))
updateData(id, ModelList::PromptTemplateRole, obj["promptTemplate"].toString());
if (obj.contains("systemPrompt"))
updateData(id, ModelList::SystemPromptRole, obj["systemPrompt"].toString());
}
const QString chatGPTDesc = tr("<ul><li>Requires personal OpenAI API key.</li><li>WARNING: Will send"
@ -592,44 +952,105 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData)
" <a href=\"https://platform.openai.com/account/api-keys\">here.</a></li>");
{
const QString modelName = "ChatGPT-3.5 Turbo";
const QString id = modelName;
const QString modelFilename = "chatgpt-gpt-3.5-turbo.txt";
if (!contains(modelFilename))
addModel(modelFilename);
updateData(modelFilename, ModelList::NameRole, "ChatGPT-3.5 Turbo");
updateData(modelFilename, ModelList::FilesizeRole, "minimal");
updateData(modelFilename, ModelList::ChatGPTRole, true);
updateData(modelFilename, ModelList::DescriptionRole,
if (!contains(id))
addModel(id);
updateData(id, ModelList::NameRole, modelName);
updateData(id, ModelList::FilenameRole, modelFilename);
updateData(id, ModelList::FilesizeRole, "minimal");
updateData(id, ModelList::ChatGPTRole, true);
updateData(id, ModelList::DescriptionRole,
tr("<strong>OpenAI's ChatGPT model GPT-3.5 Turbo</strong><br>") + chatGPTDesc);
updateData(modelFilename, ModelList::RequiresVersionRole, "2.4.2");
updateData(modelFilename, ModelList::OrderRole, "ca");
updateData(modelFilename, ModelList::RamrequiredRole, 0);
updateData(modelFilename, ModelList::ParametersRole, "?");
updateData(modelFilename, ModelList::QuantRole, "NA");
updateData(modelFilename, ModelList::TypeRole, "GPT");
updateData(id, ModelList::RequiresVersionRole, "2.4.2");
updateData(id, ModelList::OrderRole, "ca");
updateData(id, ModelList::RamrequiredRole, 0);
updateData(id, ModelList::ParametersRole, "?");
updateData(id, ModelList::QuantRole, "NA");
updateData(id, ModelList::TypeRole, "GPT");
}
{
const QString modelName = "ChatGPT-4";
const QString id = modelName;
const QString modelFilename = "chatgpt-gpt-4.txt";
if (!contains(modelFilename))
addModel(modelFilename);
updateData(modelFilename, ModelList::NameRole, "ChatGPT-4");
updateData(modelFilename, ModelList::FilesizeRole, "minimal");
updateData(modelFilename, ModelList::ChatGPTRole, true);
updateData(modelFilename, ModelList::DescriptionRole,
if (!contains(id))
addModel(id);
updateData(id, ModelList::NameRole, modelName);
updateData(id, ModelList::FilenameRole, modelFilename);
updateData(id, ModelList::FilesizeRole, "minimal");
updateData(id, ModelList::ChatGPTRole, true);
updateData(id, ModelList::DescriptionRole,
tr("<strong>OpenAI's ChatGPT model GPT-4</strong><br>") + chatGPTDesc);
updateData(modelFilename, ModelList::RequiresVersionRole, "2.4.2");
updateData(modelFilename, ModelList::OrderRole, "cb");
updateData(modelFilename, ModelList::RamrequiredRole, 0);
updateData(modelFilename, ModelList::ParametersRole, "?");
updateData(modelFilename, ModelList::QuantRole, "NA");
updateData(modelFilename, ModelList::TypeRole, "GPT");
updateData(id, ModelList::RequiresVersionRole, "2.4.2");
updateData(id, ModelList::OrderRole, "cb");
updateData(id, ModelList::RamrequiredRole, 0);
updateData(id, ModelList::ParametersRole, "?");
updateData(id, ModelList::QuantRole, "NA");
updateData(id, ModelList::TypeRole, "GPT");
}
if (installedModels()->count()) {
const QString firstModel =
installedModels()->firstFilename();
installedModels()->firstId();
QSettings settings;
settings.setValue("defaultModel", firstModel);
settings.sync();
}
}
void ModelList::updateModelsFromSettings()
{
QSettings settings;
settings.sync();
QStringList groups = settings.childGroups();
for (const QString g : groups) {
if (!g.startsWith("model-"))
continue;
const QString id = g.sliced(6);
if (contains(id))
continue;
if (!settings.contains(g+ "/isClone"))
continue;
Q_ASSERT(settings.contains(g + "/name"));
const QString name = settings.value(g + "/name").toString();
Q_ASSERT(settings.contains(g + "/filename"));
const QString filename = settings.value(g + "/filename").toString();
Q_ASSERT(settings.contains(g + "/temperature"));
const double temperature = settings.value(g + "/temperature").toDouble();
Q_ASSERT(settings.contains(g + "/topP"));
const double topP = settings.value(g + "/topP").toDouble();
Q_ASSERT(settings.contains(g + "/topK"));
const int topK = settings.value(g + "/topK").toInt();
Q_ASSERT(settings.contains(g + "/maxLength"));
const int maxLength = settings.value(g + "/maxLength").toInt();
Q_ASSERT(settings.contains(g + "/promptBatchSize"));
const int promptBatchSize = settings.value(g + "/promptBatchSize").toInt();
Q_ASSERT(settings.contains(g + "/repeatPenalty"));
const double repeatPenalty = settings.value(g + "/repeatPenalty").toDouble();
Q_ASSERT(settings.contains(g + "/repeatPenaltyTokens"));
const int repeatPenaltyTokens = settings.value(g + "/repeatPenaltyTokens").toInt();
Q_ASSERT(settings.contains(g + "/promptTemplate"));
const QString promptTemplate = settings.value(g + "/promptTemplate").toString();
Q_ASSERT(settings.contains(g + "/systemPrompt"));
const QString systemPrompt = settings.value(g + "/systemPrompt").toString();
addModel(id);
updateData(id, ModelList::IsCloneRole, true);
updateData(id, ModelList::NameRole, name);
updateData(id, ModelList::FilenameRole, filename);
updateData(id, ModelList::TemperatureRole, temperature);
updateData(id, ModelList::TopPRole, topP);
updateData(id, ModelList::TopKRole, topK);
updateData(id, ModelList::MaxLengthRole, maxLength);
updateData(id, ModelList::PromptBatchSizeRole, promptBatchSize);
updateData(id, ModelList::RepeatPenaltyRole, repeatPenalty);
updateData(id, ModelList::RepeatPenaltyTokensRole, repeatPenaltyTokens);
updateData(id, ModelList::PromptTemplateRole, promptTemplate);
updateData(id, ModelList::SystemPromptRole, systemPrompt);
}
}

View File

@ -6,8 +6,9 @@
struct ModelInfo {
Q_GADGET
Q_PROPERTY(QString name MEMBER name)
Q_PROPERTY(QString filename MEMBER filename)
Q_PROPERTY(QString id READ id WRITE setId)
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(QString filename READ filename WRITE setFilename)
Q_PROPERTY(QString dirpath MEMBER dirpath)
Q_PROPERTY(QString filesize MEMBER filesize)
Q_PROPERTY(QByteArray md5sum MEMBER md5sum)
@ -32,10 +33,27 @@ struct ModelInfo {
Q_PROPERTY(QString parameters MEMBER parameters)
Q_PROPERTY(QString quant MEMBER quant)
Q_PROPERTY(QString type MEMBER type)
Q_PROPERTY(bool isClone MEMBER isClone)
Q_PROPERTY(double temperature READ temperature WRITE setTemperature)
Q_PROPERTY(double topP READ topP WRITE setTopP)
Q_PROPERTY(int topK READ topK WRITE setTopK)
Q_PROPERTY(int maxLength READ maxLength WRITE setMaxLength)
Q_PROPERTY(int promptBatchSize READ promptBatchSize WRITE setPromptBatchSize)
Q_PROPERTY(double repeatPenalty READ repeatPenalty WRITE setRepeatPenalty)
Q_PROPERTY(int repeatPenaltyTokens READ repeatPenaltyTokens WRITE setRepeatPenaltyTokens)
Q_PROPERTY(QString promptTemplate READ promptTemplate WRITE setPromptTemplate)
Q_PROPERTY(QString systemPrompt READ systemPrompt WRITE setSystemPrompt)
public:
QString name;
QString filename;
QString id() const;
void setId(const QString &id);
QString name() const;
void setName(const QString &name);
QString filename() const;
void setFilename(const QString &name);
QString dirpath;
QString filesize;
QByteArray md5sum;
@ -60,9 +78,45 @@ public:
QString parameters;
QString quant;
QString type;
bool isClone = false;
bool operator==(const ModelInfo &other) const {
return filename == other.filename;
return m_id == other.m_id;
}
double temperature() const;
void setTemperature(double t);
double topP() const;
void setTopP(double p);
int topK() const;
void setTopK(int k);
int maxLength() const;
void setMaxLength(int l);
int promptBatchSize() const;
void setPromptBatchSize(int s);
double repeatPenalty() const;
void setRepeatPenalty(double p);
int repeatPenaltyTokens() const;
void setRepeatPenaltyTokens(int t);
QString promptTemplate() const;
void setPromptTemplate(const QString &t);
QString systemPrompt() const;
void setSystemPrompt(const QString &p);
private:
QString m_id;
QString m_name;
QString m_filename;
double m_temperature = 0.7;
double m_topP = 0.1;
int m_topK = 40;
int m_maxLength = 4096;
int m_promptBatchSize = 128;
double m_repeatPenalty = 1.18;
int m_repeatPenaltyTokens = 64;
QString m_promptTemplate = "### Human:\n%1\n### Assistant:\n";
QString m_systemPrompt = "### System:\nYou are an AI assistant who gives quality response to whatever humans ask of you.\n";
friend class MySettings;
};
Q_DECLARE_METATYPE(ModelInfo)
@ -73,7 +127,7 @@ class InstalledModels : public QSortFilterProxyModel
public:
explicit InstalledModels(QObject *parent);
int count() const;
QString firstFilename() const;
QString firstId() const;
Q_SIGNALS:
void countChanged();
@ -120,7 +174,8 @@ public:
static ModelList *globalInstance();
enum Roles {
NameRole = Qt::UserRole + 1,
IdRole = Qt::UserRole + 1,
NameRole,
FilenameRole,
DirpathRole,
FilesizeRole,
@ -145,12 +200,23 @@ public:
RamrequiredRole,
ParametersRole,
QuantRole,
TypeRole
TypeRole,
IsCloneRole,
TemperatureRole,
TopPRole,
TopKRole,
MaxLengthRole,
PromptBatchSizeRole,
RepeatPenaltyRole,
RepeatPenaltyTokensRole,
PromptTemplateRole,
SystemPromptRole,
};
QHash<int, QByteArray> roleNames() const override
{
QHash<int, QByteArray> roles;
roles[IdRole] = "id";
roles[NameRole] = "name";
roles[FilenameRole] = "filename";
roles[DirpathRole] = "dirpath";
@ -177,21 +243,36 @@ public:
roles[ParametersRole] = "parameters";
roles[QuantRole] = "quant";
roles[TypeRole] = "type";
roles[IsCloneRole] = "isClone";
roles[TemperatureRole] = "temperature";
roles[TopPRole] = "topP";
roles[TopKRole] = "topK";
roles[MaxLengthRole] = "maxLength";
roles[PromptBatchSizeRole] = "promptBatchSize";
roles[RepeatPenaltyRole] = "repeatPenalty";
roles[RepeatPenaltyTokensRole] = "repeatPenaltyTokens";
roles[PromptTemplateRole] = "promptTemplate";
roles[SystemPromptRole] = "systemPrompt";
return roles;
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant data(const QString &filename, int role) const;
void updateData(const QString &filename, int role, const QVariant &value);
QVariant data(const QString &id, int role) const;
void updateData(const QString &id, int role, const QVariant &value);
int count() const { return m_models.size(); }
bool contains(const QString &filename) const;
Q_INVOKABLE ModelInfo modelInfo(const QString &filename) const;
bool contains(const QString &id) const;
bool containsByFilename(const QString &filename) const;
Q_INVOKABLE ModelInfo modelInfo(const QString &id) const;
Q_INVOKABLE ModelInfo modelInfoByFilename(const QString &filename) const;
Q_INVOKABLE bool isUniqueName(const QString &name) const;
Q_INVOKABLE QString clone(const ModelInfo &model);
Q_INVOKABLE void remove(const ModelInfo &model);
ModelInfo defaultModelInfo() const;
void addModel(const QString &filename);
void addModel(const QString &id);
const QList<ModelInfo> exportModelList() const;
const QList<QString> userDefaultModelList() const;
@ -220,8 +301,10 @@ Q_SIGNALS:
void userDefaultModelListChanged();
private Q_SLOTS:
void updateModelsFromJson();
void updateModelsFromSettings();
void updateModelsFromDirectory();
void updateModelList();
void updateDataForSettings();
private:
QString modelDirPath(const QString &modelName, bool isChatGPT);
@ -229,6 +312,7 @@ private:
QVariant dataInternal(const ModelInfo *info, int role) const;
static bool lessThan(const ModelInfo* a, const ModelInfo* b);
void parseModelsJsonFile(const QByteArray &jsonData);
QString uniqueModelName(const ModelInfo &model) const;
private:
mutable QMutex m_mutex;

View File

@ -1,4 +1,5 @@
#include "mysettings.h"
#include "modellist.h"
#include <QDir>
#include <QFile>
@ -7,14 +8,6 @@
#include <QStandardPaths>
#include <QUrl>
static double default_temperature = 0.7;
static double default_topP = 0.1;
static int default_topK = 40;
static int default_maxLength = 4096;
static int default_promptBatchSize = 128;
static double default_repeatPenalty = 1.18;
static int default_repeatPenaltyTokens = 64;
static QString default_promptTemplate = "### Human:\n%1\n### Assistant:\n";
static int default_threadCount = 0;
static bool default_saveChats = false;
static bool default_saveChatGPTChats = true;
@ -68,16 +61,17 @@ MySettings::MySettings()
QSettings::setDefaultFormat(QSettings::IniFormat);
}
void MySettings::restoreGenerationDefaults()
void MySettings::restoreModelDefaults(const ModelInfo &model)
{
setTemperature(default_temperature);
setTopP(default_topP);
setTopK(default_topK);
setMaxLength(default_maxLength);
setPromptBatchSize(default_promptBatchSize);
setRepeatPenalty(default_repeatPenalty);
setRepeatPenaltyTokens(default_repeatPenaltyTokens);
setPromptTemplate(default_promptTemplate);
setModelTemperature(model, model.m_temperature);
setModelTopP(model, model.m_topP);
setModelTopK(model, model.m_topK);;
setModelMaxLength(model, model.m_maxLength);
setModelPromptBatchSize(model, model.m_promptBatchSize);
setModelRepeatPenalty(model, model.m_repeatPenalty);
setModelRepeatPenaltyTokens(model, model.m_repeatPenaltyTokens);
setModelPromptTemplate(model, model.m_promptTemplate);
setModelSystemPrompt(model, model.m_systemPrompt);
}
void MySettings::restoreApplicationDefaults()
@ -97,148 +91,256 @@ void MySettings::restoreLocalDocsDefaults()
setLocalDocsRetrievalSize(default_localDocsRetrievalSize);
}
double MySettings::temperature() const
void MySettings::eraseModel(const ModelInfo &m)
{
QSettings settings;
settings.remove(QString("model-%1").arg(m.id()));
settings.sync();
}
QString MySettings::modelName(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value("temperature", default_temperature).toDouble();
return setting.value(QString("model-%1").arg(m.id()) + "/name",
!m.m_name.isEmpty() ? m.m_name : m.m_filename).toString();
}
void MySettings::setTemperature(double t)
void MySettings::setModelName(const ModelInfo &m, const QString &name, bool force)
{
if (temperature() == t)
if ((modelName(m) == name || m.id().isEmpty()) && !force)
return;
QSettings setting;
setting.setValue("temperature", t);
if ((m.m_name == name || m.m_filename == name) && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/name");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/name", name);
if (m.isClone)
setting.setValue(QString("model-%1").arg(m.id()) + "/isClone", "true");
setting.sync();
emit temperatureChanged();
if (!force)
emit nameChanged(m);
}
double MySettings::topP() const
QString MySettings::modelFilename(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value("topP", default_topP).toDouble();
return setting.value(QString("model-%1").arg(m.id()) + "/filename", m.m_filename).toString();
}
void MySettings::setTopP(double p)
void MySettings::setModelFilename(const ModelInfo &m, const QString &filename, bool force)
{
if (topP() == p)
if ((modelFilename(m) == filename || m.id().isEmpty()) && !force)
return;
QSettings setting;
setting.setValue("topP", p);
if (m.m_filename == filename && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/filename");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/filename", filename);
setting.sync();
emit topPChanged();
if (!force)
emit filenameChanged(m);
}
int MySettings::topK() const
double MySettings::modelTemperature(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value("topK", default_topK).toInt();
return setting.value(QString("model-%1").arg(m.id()) + "/temperature", m.m_temperature).toDouble();
}
void MySettings::setTopK(int k)
void MySettings::setModelTemperature(const ModelInfo &m, double t, bool force)
{
if (topK() == k)
if (modelTemperature(m) == t && !force)
return;
QSettings setting;
setting.setValue("topK", k);
if (m.m_temperature == t && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/temperature");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/temperature", t);
setting.sync();
emit topKChanged();
if (!force)
emit temperatureChanged(m);
}
int MySettings::maxLength() const
double MySettings::modelTopP(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value("maxLength", default_maxLength).toInt();
return setting.value(QString("model-%1").arg(m.id()) + "/topP", m.m_topP).toDouble();
}
void MySettings::setMaxLength(int l)
void MySettings::setModelTopP(const ModelInfo &m, double p, bool force)
{
if (maxLength() == l)
if (modelTopP(m) == p && !force)
return;
QSettings setting;
setting.setValue("maxLength", l);
if (m.m_topP == p && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/topP");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/topP", p);
setting.sync();
emit maxLengthChanged();
if (!force)
emit topPChanged(m);
}
int MySettings::promptBatchSize() const
int MySettings::modelTopK(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value("promptBatchSize", default_promptBatchSize).toInt();
return setting.value(QString("model-%1").arg(m.id()) + "/topK", m.m_topK).toInt();
}
void MySettings::setPromptBatchSize(int s)
void MySettings::setModelTopK(const ModelInfo &m, int k, bool force)
{
if (promptBatchSize() == s)
if (modelTopK(m) == k && !force)
return;
QSettings setting;
setting.setValue("promptBatchSize", s);
if (m.m_topK == k && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/topK");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/topK", k);
setting.sync();
emit promptBatchSizeChanged();
if (!force)
emit topKChanged(m);
}
double MySettings::repeatPenalty() const
int MySettings::modelMaxLength(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value("repeatPenalty", default_repeatPenalty).toDouble();
return setting.value(QString("model-%1").arg(m.id()) + "/maxLength", m.m_maxLength).toInt();
}
void MySettings::setRepeatPenalty(double p)
void MySettings::setModelMaxLength(const ModelInfo &m, int l, bool force)
{
if (repeatPenalty() == p)
if (modelMaxLength(m) == l && !force)
return;
QSettings setting;
setting.setValue("repeatPenalty", p);
if (m.m_maxLength == l && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/maxLength");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/maxLength", l);
setting.sync();
emit repeatPenaltyChanged();
if (!force)
emit maxLengthChanged(m);
}
int MySettings::repeatPenaltyTokens() const
int MySettings::modelPromptBatchSize(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value("repeatPenaltyTokens", default_repeatPenaltyTokens).toInt();
return setting.value(QString("model-%1").arg(m.id()) + "/promptBatchSize", m.m_promptBatchSize).toInt();
}
void MySettings::setRepeatPenaltyTokens(int t)
void MySettings::setModelPromptBatchSize(const ModelInfo &m, int s, bool force)
{
if (repeatPenaltyTokens() == t)
if (modelPromptBatchSize(m) == s && !force)
return;
QSettings setting;
setting.setValue("repeatPenaltyTokens", t);
if (m.m_promptBatchSize == s && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/promptBatchSize");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/promptBatchSize", s);
setting.sync();
emit repeatPenaltyTokensChanged();
if (!force)
emit promptBatchSizeChanged(m);
}
QString MySettings::promptTemplate() const
double MySettings::modelRepeatPenalty(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value("promptTemplate", default_promptTemplate).toString();
return setting.value(QString("model-%1").arg(m.id()) + "/repeatPenalty", m.m_repeatPenalty).toDouble();
}
void MySettings::setPromptTemplate(const QString &t)
void MySettings::setModelRepeatPenalty(const ModelInfo &m, double p, bool force)
{
if (promptTemplate() == t)
if (modelRepeatPenalty(m) == p && !force)
return;
QSettings setting;
setting.setValue("promptTemplate", t);
if (m.m_repeatPenalty == p && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/repeatPenalty");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/repeatPenalty", p);
setting.sync();
emit promptTemplateChanged();
if (!force)
emit repeatPenaltyChanged(m);
}
int MySettings::modelRepeatPenaltyTokens(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/repeatPenaltyTokens", m.m_repeatPenaltyTokens).toInt();
}
void MySettings::setModelRepeatPenaltyTokens(const ModelInfo &m, int t, bool force)
{
if (modelRepeatPenaltyTokens(m) == t && !force)
return;
QSettings setting;
if (m.m_repeatPenaltyTokens == t && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/repeatPenaltyTokens");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/repeatPenaltyTokens", t);
setting.sync();
if (!force)
emit repeatPenaltyTokensChanged(m);
}
QString MySettings::modelPromptTemplate(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/promptTemplate", m.m_promptTemplate).toString();
}
void MySettings::setModelPromptTemplate(const ModelInfo &m, const QString &t, bool force)
{
if (modelPromptTemplate(m) == t && !force)
return;
QSettings setting;
if (m.m_promptTemplate == t && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/promptTemplate");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/promptTemplate", t);
setting.sync();
if (!force)
emit promptTemplateChanged(m);
}
QString MySettings::modelSystemPrompt(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/systemPrompt", m.m_systemPrompt).toString();
}
void MySettings::setModelSystemPrompt(const ModelInfo &m, const QString &p, bool force)
{
if (modelSystemPrompt(m) == p && !force)
return;
QSettings setting;
if (m.m_systemPrompt == p && !m.isClone)
setting.remove(QString("model-%1").arg(m.id()) + "/systemPrompt");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/systemPrompt", p);
setting.sync();
if (!force)
emit systemPromptChanged(m);
}
int MySettings::threadCount() const

View File

@ -4,17 +4,10 @@
#include <QObject>
#include <QMutex>
class ModelInfo;
class MySettings : public QObject
{
Q_OBJECT
Q_PROPERTY(double temperature READ temperature WRITE setTemperature NOTIFY temperatureChanged)
Q_PROPERTY(double topP READ topP WRITE setTopP NOTIFY topPChanged)
Q_PROPERTY(int topK READ topK WRITE setTopK NOTIFY topKChanged)
Q_PROPERTY(int maxLength READ maxLength WRITE setMaxLength NOTIFY maxLengthChanged)
Q_PROPERTY(int promptBatchSize READ promptBatchSize WRITE setPromptBatchSize NOTIFY promptBatchSizeChanged)
Q_PROPERTY(double repeatPenalty READ repeatPenalty WRITE setRepeatPenalty NOTIFY repeatPenaltyChanged)
Q_PROPERTY(int repeatPenaltyTokens READ repeatPenaltyTokens WRITE setRepeatPenaltyTokens NOTIFY repeatPenaltyTokensChanged)
Q_PROPERTY(QString promptTemplate READ promptTemplate WRITE setPromptTemplate NOTIFY promptTemplateChanged)
Q_PROPERTY(int threadCount READ threadCount WRITE setThreadCount NOTIFY threadCountChanged)
Q_PROPERTY(bool saveChats READ saveChats WRITE setSaveChats NOTIFY saveChatsChanged)
Q_PROPERTY(bool saveChatGPTChats READ saveChatGPTChats WRITE setSaveChatGPTChats NOTIFY saveChatGPTChatsChanged)
@ -33,27 +26,34 @@ public:
static MySettings *globalInstance();
// Restore methods
Q_INVOKABLE void restoreGenerationDefaults();
Q_INVOKABLE void restoreModelDefaults(const ModelInfo &model);
Q_INVOKABLE void restoreApplicationDefaults();
Q_INVOKABLE void restoreLocalDocsDefaults();
// Generation settings
double temperature() const;
void setTemperature(double t);
double topP() const;
void setTopP(double p);
int topK() const;
void setTopK(int k);
int maxLength() const;
void setMaxLength(int l);
int promptBatchSize() const;
void setPromptBatchSize(int s);
double repeatPenalty() const;
void setRepeatPenalty(double p);
int repeatPenaltyTokens() const;
void setRepeatPenaltyTokens(int t);
QString promptTemplate() const;
void setPromptTemplate(const QString &t);
// Model/Character settings
void eraseModel(const ModelInfo &m);
QString modelName(const ModelInfo &m) const;
Q_INVOKABLE void setModelName(const ModelInfo &m, const QString &name, bool force = false);
QString modelFilename(const ModelInfo &m) const;
Q_INVOKABLE void setModelFilename(const ModelInfo &m, const QString &filename, bool force = false);
double modelTemperature(const ModelInfo &m) const;
Q_INVOKABLE void setModelTemperature(const ModelInfo &m, double t, bool force = false);
double modelTopP(const ModelInfo &m) const;
Q_INVOKABLE void setModelTopP(const ModelInfo &m, double p, bool force = false);
int modelTopK(const ModelInfo &m) const;
Q_INVOKABLE void setModelTopK(const ModelInfo &m, int k, bool force = false);
int modelMaxLength(const ModelInfo &m) const;
Q_INVOKABLE void setModelMaxLength(const ModelInfo &m, int l, bool force = false);
int modelPromptBatchSize(const ModelInfo &m) const;
Q_INVOKABLE void setModelPromptBatchSize(const ModelInfo &m, int s, bool force = false);
double modelRepeatPenalty(const ModelInfo &m) const;
Q_INVOKABLE void setModelRepeatPenalty(const ModelInfo &m, double p, bool force = false);
int modelRepeatPenaltyTokens(const ModelInfo &m) const;
Q_INVOKABLE void setModelRepeatPenaltyTokens(const ModelInfo &m, int t, bool force = false);
QString modelPromptTemplate(const ModelInfo &m) const;
Q_INVOKABLE void setModelPromptTemplate(const ModelInfo &m, const QString &t, bool force = false);
QString modelSystemPrompt(const ModelInfo &m) const;
Q_INVOKABLE void setModelSystemPrompt(const ModelInfo &m, const QString &p, bool force = false);
// Application settings
int threadCount() const;
@ -90,14 +90,17 @@ public:
void setNetworkUsageStatsActive(bool b);
Q_SIGNALS:
void temperatureChanged();
void topPChanged();
void topKChanged();
void maxLengthChanged();
void promptBatchSizeChanged();
void repeatPenaltyChanged();
void repeatPenaltyTokensChanged();
void promptTemplateChanged();
void nameChanged(const ModelInfo &model);
void filenameChanged(const ModelInfo &model);
void temperatureChanged(const ModelInfo &model);
void topPChanged(const ModelInfo &model);
void topKChanged(const ModelInfo &model);
void maxLengthChanged(const ModelInfo &model);
void promptBatchSizeChanged(const ModelInfo &model);
void repeatPenaltyChanged(const ModelInfo &model);
void repeatPenaltyTokensChanged(const ModelInfo &model);
void promptTemplateChanged(const ModelInfo &model);
void systemPromptChanged(const ModelInfo &model);
void threadCountChanged();
void saveChatsChanged();
void saveChatGPTChatsChanged();

View File

@ -88,7 +88,7 @@ bool Network::packageAndSendJson(const QString &ingestId, const QString &json)
Q_ASSERT(ChatListModel::globalInstance()->currentChat());
QJsonObject object = doc.object();
object.insert("source", "gpt4all-chat");
object.insert("agent_id", ChatListModel::globalInstance()->currentChat()->modelInfo().filename);
object.insert("agent_id", ChatListModel::globalInstance()->currentChat()->modelInfo().filename());
object.insert("submitter_id", m_uniqueId);
object.insert("ingest_id", ingestId);
@ -96,7 +96,7 @@ bool Network::packageAndSendJson(const QString &ingestId, const QString &json)
if (!attribution.isEmpty())
object.insert("network/attribution", attribution);
QString promptTemplate = MySettings::globalInstance()->promptTemplate();
QString promptTemplate = ChatListModel::globalInstance()->currentChat()->modelInfo().promptTemplate();
object.insert("prompt_template", promptTemplate);
QJsonDocument newDoc;
@ -391,7 +391,7 @@ void Network::sendMixpanelEvent(const QString &ev, const QVector<KeyValue> &valu
properties.insert("ip", m_ipify);
properties.insert("name", QCoreApplication::applicationName() + " v"
+ QCoreApplication::applicationVersion());
properties.insert("model", ChatListModel::globalInstance()->currentChat()->modelInfo().filename);
properties.insert("model", ChatListModel::globalInstance()->currentChat()->modelInfo().filename());
// Some additional startup information
if (ev == "startup") {

View File

@ -6,8 +6,12 @@ import QtQuick.Layouts
import QtQuick.Dialogs
import modellist
import mysettings
import network
MySettingsTab {
onRestoreDefaultsClicked: {
MySettings.restoreApplicationDefaults();
}
title: qsTr("Application")
contentItem: GridLayout {
id: applicationSettingsTabInner
@ -26,7 +30,9 @@ MySettingsTab {
id: comboBox
Layout.row: 1
Layout.column: 1
Layout.columnSpan: 2
Layout.minimumWidth: 350
Layout.fillWidth: true
model: ModelList.userDefaultModelList
Accessible.role: Accessible.ComboBox
Accessible.name: qsTr("ComboBox for displaying/picking the default model")
@ -178,25 +184,30 @@ MySettingsTab {
Layout.columnSpan: 3
Layout.fillWidth: true
height: 1
color: theme.dialogBorder
color: theme.tabBorder
}
}
advancedSettings: GridLayout {
columns: 3
rowSpacing: 10
columnSpacing: 10
Rectangle {
Layout.row: 9
Layout.row: 2
Layout.column: 0
Layout.fillWidth: true
Layout.columnSpan: 3
height: 1
color: theme.dialogBorder
color: theme.tabBorder
}
Label {
id: gpuOverrideLabel
text: qsTr("Force Metal (macOS+arm):")
color: theme.textColor
Layout.row: 8
Layout.row: 1
Layout.column: 0
}
RowLayout {
Layout.row: 8
Layout.row: 1
Layout.column: 1
Layout.columnSpan: 2
MyCheckBox {
@ -206,24 +217,18 @@ MySettingsTab {
MySettings.forceMetal = !MySettings.forceMetal
}
}
Label {
id: warningLabel
Layout.maximumWidth: 730
Item {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
color: theme.textErrorColor
wrapMode: Text.WordWrap
text: qsTr("WARNING: On macOS with arm (M1+) this setting forces usage of the GPU. Can cause crashes if the model requires more RAM than the system supports. Because of crash possibility the setting will not persist across restarts of the application. This has no effect on non-macs or intel.")
}
}
MyButton {
Layout.row: 10
Layout.column: 1
Layout.columnSpan: 2
Layout.fillWidth: true
text: qsTr("Restore Defaults")
Accessible.description: qsTr("Restores the settings dialog to a default state")
onClicked: {
MySettings.restoreApplicationDefaults();
Layout.minimumHeight: warningLabel.height
Label {
id: warningLabel
width: parent.width
color: theme.textErrorColor
wrapMode: Text.WordWrap
text: qsTr("WARNING: On macOS with arm (M1+) this setting forces usage of the GPU. Can cause crashes if the model requires more RAM than the system supports. Because of crash possibility the setting will not persist across restarts of the application. This has no effect on non-macs or intel.")
}
}
}
}

View File

@ -1,302 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import localdocs
import mysettings
MySettingsTab {
title: qsTr("Presets")
contentItem: GridLayout {
id: generationSettingsTabInner
columns: 2
rowSpacing: 10
columnSpacing: 10
Label {
id: tempLabel
text: qsTr("Temperature:")
color: theme.textColor
Layout.row: 0
Layout.column: 0
}
MyTextField {
text: MySettings.temperature
color: theme.textColor
ToolTip.text: qsTr("Temperature increases the chances of choosing less likely tokens.\nNOTE: Higher temperature gives more creative but less predictable outputs.")
ToolTip.visible: hovered
Layout.row: 0
Layout.column: 1
validator: DoubleValidator {
locale: "C"
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.temperature = val
focus = false
} else {
text = MySettings.temperature
}
}
Accessible.role: Accessible.EditableText
Accessible.name: tempLabel.text
Accessible.description: ToolTip.text
}
Label {
id: topPLabel
text: qsTr("Top P:")
color: theme.textColor
Layout.row: 1
Layout.column: 0
}
MyTextField {
text: MySettings.topP
color: theme.textColor
ToolTip.text: qsTr("Only the most likely tokens up to a total probability of top_p can be chosen.\nNOTE: Prevents choosing highly unlikely tokens, aka Nucleus Sampling")
ToolTip.visible: hovered
Layout.row: 1
Layout.column: 1
validator: DoubleValidator {
locale: "C"
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.topP = val
focus = false
} else {
text = MySettings.topP
}
}
Accessible.role: Accessible.EditableText
Accessible.name: topPLabel.text
Accessible.description: ToolTip.text
}
Label {
id: topKLabel
text: qsTr("Top K:")
color: theme.textColor
Layout.row: 2
Layout.column: 0
}
MyTextField {
text: MySettings.topK
color: theme.textColor
ToolTip.text: qsTr("Only the top K most likely tokens will be chosen from")
ToolTip.visible: hovered
Layout.row: 2
Layout.column: 1
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.topK = val
focus = false
} else {
text = MySettings.topK
}
}
Accessible.role: Accessible.EditableText
Accessible.name: topKLabel.text
Accessible.description: ToolTip.text
}
Label {
id: maxLengthLabel
text: qsTr("Max Length:")
color: theme.textColor
Layout.row: 3
Layout.column: 0
}
MyTextField {
text: MySettings.maxLength
color: theme.textColor
ToolTip.text: qsTr("Maximum length of response in tokens")
ToolTip.visible: hovered
Layout.row: 3
Layout.column: 1
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.maxLength = val
focus = false
} else {
text = MySettings.maxLength
}
}
Accessible.role: Accessible.EditableText
Accessible.name: maxLengthLabel.text
Accessible.description: ToolTip.text
}
Label {
id: batchSizeLabel
text: qsTr("Prompt Batch Size:")
color: theme.textColor
Layout.row: 4
Layout.column: 0
}
MyTextField {
text: MySettings.promptBatchSize
color: theme.textColor
ToolTip.text: qsTr("Amount of prompt tokens to process at once.\nNOTE: Higher values can speed up reading prompts but will use more RAM")
ToolTip.visible: hovered
Layout.row: 4
Layout.column: 1
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.promptBatchSize = val
focus = false
} else {
text = MySettings.promptBatchSize
}
}
Accessible.role: Accessible.EditableText
Accessible.name: batchSizeLabel.text
Accessible.description: ToolTip.text
}
Label {
id: repeatPenaltyLabel
text: qsTr("Repeat Penalty:")
color: theme.textColor
Layout.row: 5
Layout.column: 0
}
MyTextField {
text: MySettings.repeatPenalty
color: theme.textColor
ToolTip.text: qsTr("Amount to penalize repetitiveness of the output")
ToolTip.visible: hovered
Layout.row: 5
Layout.column: 1
validator: DoubleValidator {
locale: "C"
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.repeatPenalty = val
focus = false
} else {
text = MySettings.repeatPenalty
}
}
Accessible.role: Accessible.EditableText
Accessible.name: repeatPenaltyLabel.text
Accessible.description: ToolTip.text
}
Label {
id: repeatPenaltyTokensLabel
text: qsTr("Repeat Penalty Tokens:")
color: theme.textColor
Layout.row: 6
Layout.column: 0
}
MyTextField {
text: MySettings.repeatPenaltyTokens
color: theme.textColor
ToolTip.text: qsTr("How far back in output to apply repeat penalty")
ToolTip.visible: hovered
Layout.row: 6
Layout.column: 1
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.repeatPenaltyTokens = val
focus = false
} else {
text = MySettings.repeatPenaltyTokens
}
}
Accessible.role: Accessible.EditableText
Accessible.name: repeatPenaltyTokensLabel.text
Accessible.description: ToolTip.text
}
ColumnLayout {
Layout.row: 7
Layout.column: 0
Layout.topMargin: 10
Layout.alignment: Qt.AlignTop
spacing: 20
Label {
id: promptTemplateLabel
text: qsTr("Prompt Template:")
color: theme.textColor
}
Label {
id: promptTemplateLabelHelp
Layout.maximumWidth: promptTemplateLabel.width
visible: templateTextArea.text.indexOf(
"%1") === -1
color: theme.textErrorColor
text: qsTr("Must contain the string \"%1\" to be replaced with the user's input.")
wrapMode: TextArea.Wrap
Accessible.role: Accessible.EditableText
Accessible.name: text
}
}
Rectangle {
Layout.row: 7
Layout.column: 1
Layout.fillWidth: true
height: 200
color: "transparent"
clip: true
ScrollView {
id: templateScrollView
anchors.fill: parent
TextArea {
id: templateTextArea
text: MySettings.promptTemplate
color: theme.textColor
background: Rectangle {
implicitWidth: 150
color: theme.backgroundLighter
radius: 10
}
padding: 10
wrapMode: TextArea.Wrap
onTextChanged: {
if (templateTextArea.text.indexOf("%1") !== -1) {
MySettings.promptTemplate = text
}
}
bottomPadding: 10
Accessible.role: Accessible.EditableText
Accessible.name: promptTemplateLabel.text
Accessible.description: promptTemplateLabelHelp.text
ToolTip.text: qsTr("The prompt template partially determines how models will respond to prompts.\nNOTE: A longer, detailed template can lead to higher quality answers, but can also slow down generation.")
ToolTip.visible: hovered
}
}
}
MyButton {
Layout.row: 8
Layout.column: 1
Layout.fillWidth: true
text: qsTr("Restore Defaults")
Accessible.description: qsTr("Restores the settings dialog to a default state")
onClicked: {
MySettings.restoreGenerationDefaults();
templateTextArea.text = MySettings.promptTemplate
}
}
}
}

View File

@ -6,8 +6,13 @@ import QtQuick.Layouts
import QtQuick.Dialogs
import localdocs
import mysettings
import network
MySettingsTab {
onRestoreDefaultsClicked: {
MySettings.restoreLocalDocsDefaults();
}
title: qsTr("LocalDocs Plugin (BETA)")
contentItem: ColumnLayout {
id: root
@ -25,101 +30,95 @@ MySettingsTab {
}
}
RowLayout {
Item {
Layout.fillWidth: true
height: collection.height + 20
spacing: 10
MyTextField {
id: collection
width: 225
horizontalAlignment: Text.AlignJustify
color: theme.textColor
placeholderText: qsTr("Collection name...")
placeholderTextColor: theme.mutedTextColor
ToolTip.text: qsTr("Name of the collection to add (Required)")
ToolTip.visible: hovered
Accessible.role: Accessible.EditableText
Accessible.name: collection.text
Accessible.description: ToolTip.text
function showError() {
collection.placeholderTextColor = theme.textErrorColor
}
onTextChanged: {
collection.placeholderTextColor = theme.mutedTextColor
}
}
MyDirectoryField {
id: folderEdit
Layout.fillWidth: true
text: root.folder_path
placeholderText: qsTr("Folder path...")
placeholderTextColor: theme.mutedTextColor
ToolTip.text: qsTr("Folder path to documents (Required)")
ToolTip.visible: hovered
function showError() {
folderEdit.placeholderTextColor = theme.textErrorColor
}
onTextChanged: {
folderEdit.placeholderTextColor = theme.mutedTextColor
}
}
MyButton {
id: browseButton
text: qsTr("Browse")
onClicked: {
folderDialog.open();
}
}
MyButton {
id: addButton
text: qsTr("Add")
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.description: qsTr("Add button")
onClicked: {
var isError = false;
if (root.collection === "") {
isError = true;
collection.showError();
height: row.height
RowLayout {
id: row
anchors.left: parent.left
anchors.right: parent.right
height: collection.height
spacing: 10
MyTextField {
id: collection
width: 225
horizontalAlignment: Text.AlignJustify
color: theme.textColor
placeholderText: qsTr("Collection name...")
placeholderTextColor: theme.mutedTextColor
ToolTip.text: qsTr("Name of the collection to add (Required)")
ToolTip.visible: hovered
Accessible.role: Accessible.EditableText
Accessible.name: collection.text
Accessible.description: ToolTip.text
function showError() {
collection.placeholderTextColor = theme.textErrorColor
}
if (root.folder_path === "" || !folderEdit.isValid) {
isError = true;
folderEdit.showError();
onTextChanged: {
collection.placeholderTextColor = theme.mutedTextColor
}
}
MyDirectoryField {
id: folderEdit
Layout.fillWidth: true
text: root.folder_path
placeholderText: qsTr("Folder path...")
placeholderTextColor: theme.mutedTextColor
ToolTip.text: qsTr("Folder path to documents (Required)")
ToolTip.visible: hovered
function showError() {
folderEdit.placeholderTextColor = theme.textErrorColor
}
onTextChanged: {
folderEdit.placeholderTextColor = theme.mutedTextColor
}
}
MyButton {
id: browseButton
text: qsTr("Browse")
onClicked: {
folderDialog.open();
}
}
MyButton {
id: addButton
text: qsTr("Add")
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.description: qsTr("Add button")
onClicked: {
var isError = false;
if (root.collection === "") {
isError = true;
collection.showError();
}
if (root.folder_path === "" || !folderEdit.isValid) {
isError = true;
folderEdit.showError();
}
if (isError)
return;
LocalDocs.addFolder(root.collection, root.folder_path)
root.collection = ""
root.folder_path = ""
collection.clear()
}
if (isError)
return;
LocalDocs.addFolder(root.collection, root.folder_path)
root.collection = ""
root.folder_path = ""
collection.clear()
}
}
}
ScrollView {
id: scrollView
Layout.fillWidth: true
Layout.bottomMargin: 10
clip: true
contentHeight: 300
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
background: Rectangle {
color: theme.backgroundLighter
}
ListView {
id: listView
ColumnLayout {
spacing: 0
Repeater {
model: LocalDocs.localDocsModel
boundsBehavior: Flickable.StopAtBounds
delegate: Rectangle {
id: item
width: listView.width
Layout.fillWidth: true
height: buttons.height + 20
color: index % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter
color: index % 2 === 0 ? theme.backgroundDark : theme.backgroundDarker
property bool removing: false
Text {
@ -136,6 +135,7 @@ MySettingsTab {
Text {
id: folderId
anchors.left: collectionId.right
anchors.right: buttons.left
anchors.margins: 20
anchors.verticalCenter: parent.verticalCenter
text: folder_path
@ -170,124 +170,99 @@ MySettingsTab {
}
}
GridLayout {
id: gridLayout
Rectangle {
Layout.fillWidth: true
columns: 3
rowSpacing: 10
columnSpacing: 10
height: 1
color: theme.tabBorder
}
}
advancedSettings: GridLayout {
id: gridLayout
columns: 3
rowSpacing: 10
columnSpacing: 10
Rectangle {
Layout.row: 0
Layout.column: 0
Layout.fillWidth: true
Layout.columnSpan: 3
height: 1
color: theme.dialogBorder
Rectangle {
Layout.row: 3
Layout.column: 0
Layout.fillWidth: true
Layout.columnSpan: 3
height: 1
color: theme.tabBorder
}
Label {
id: chunkLabel
Layout.row: 1
Layout.column: 0
color: theme.textColor
text: qsTr("Document snippet size (characters):")
}
MyTextField {
id: chunkSizeTextField
Layout.row: 1
Layout.column: 1
ToolTip.text: qsTr("Number of characters per document snippet.\nNOTE: larger numbers increase likelihood of factual responses, but also result in slower generation.")
ToolTip.visible: hovered
text: MySettings.localDocsChunkSize
validator: IntValidator {
bottom: 1
}
Rectangle {
Layout.row: 3
Layout.column: 0
Layout.fillWidth: true
Layout.columnSpan: 3
height: 1
color: theme.dialogBorder
}
// This is here just to stretch out the third column
Rectangle {
Layout.row: 3
Layout.column: 2
Layout.fillWidth: true
height: 1
color: theme.dialogBorder
}
Label {
id: chunkLabel
Layout.row: 1
Layout.column: 0
color: theme.textColor
text: qsTr("Document snippet size (characters):")
}
MyTextField {
id: chunkSizeTextField
Layout.row: 1
Layout.column: 1
ToolTip.text: qsTr("Number of characters per document snippet.\nNOTE: larger numbers increase likelihood of factual responses, but also result in slower generation.")
ToolTip.visible: hovered
text: MySettings.localDocsChunkSize
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.localDocsChunkSize = val
focus = false
} else {
text = MySettings.localDocsChunkSize
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.localDocsChunkSize = val
focus = false
} else {
text = MySettings.localDocsChunkSize
}
}
}
Label {
id: contextItemsPerPrompt
Layout.row: 2
Layout.column: 0
color: theme.textColor
text: qsTr("Document snippets per prompt:")
Label {
id: contextItemsPerPrompt
Layout.row: 2
Layout.column: 0
color: theme.textColor
text: qsTr("Document snippets per prompt:")
}
MyTextField {
Layout.row: 2
Layout.column: 1
ToolTip.text: qsTr("Best N matches of retrieved document snippets to add to the context for prompt.\nNOTE: larger numbers increase likelihood of factual responses, but also result in slower generation.")
ToolTip.visible: hovered
text: MySettings.localDocsRetrievalSize
validator: IntValidator {
bottom: 1
}
MyTextField {
Layout.row: 2
Layout.column: 1
ToolTip.text: qsTr("Best N matches of retrieved document snippets to add to the context for prompt.\nNOTE: larger numbers increase likelihood of factual responses, but also result in slower generation.")
ToolTip.visible: hovered
text: MySettings.localDocsRetrievalSize
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.localDocsRetrievalSize = val
focus = false
} else {
text = MySettings.localDocsRetrievalSize
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.localDocsRetrievalSize = val
focus = false
} else {
text = MySettings.localDocsRetrievalSize
}
}
}
Item {
Layout.row: 1
Layout.column: 2
Layout.rowSpan: 2
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
Layout.minimumHeight: warningLabel.height
Label {
id: warningLabel
Layout.row: 1
Layout.column: 2
Layout.rowSpan: 2
Layout.maximumWidth: 520
Layout.alignment: Qt.AlignTop
width: parent.width
color: theme.textErrorColor
wrapMode: Text.WordWrap
text: qsTr("Warning: Advanced usage only. Values too large may cause localdocs failure, extremely slow responses or failure to respond at all. Roughly speaking, the {N chars x N snippets} are added to the model's context window. More info <a href=\"https://docs.gpt4all.io/gpt4all_chat.html#localdocs-beta-plugin-chat-with-your-data\">here.</a>")
onLinkActivated: function(link) { Qt.openUrlExternally(link) }
}
MyButton {
id: restoreDefaultsButton
Layout.row: 4
Layout.column: 1
Layout.columnSpan: 2
Layout.fillWidth: true
text: qsTr("Restore Defaults")
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.description: qsTr("Restores the settings dialog to a default state")
onClicked: {
MySettings.restoreLocalDocsDefaults();
}
}
}
}
}

View File

@ -83,7 +83,7 @@ Dialog {
Text {
textFormat: Text.StyledText
text: "<h2>" + (name !== "" ? name : filename) + "</h2>"
text: "<h2>" + name + "</h2>"
Layout.row: 0
Layout.column: 0
Layout.topMargin: 20
@ -329,12 +329,14 @@ Dialog {
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
}
@ -346,6 +348,11 @@ Dialog {
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
}
}
}
}

View File

@ -3,27 +3,665 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import localdocs
import modellist
import mysettings
MySettingsTab {
title: qsTr("Models")
onRestoreDefaultsClicked: {
MySettings.restoreModelDefaults(root.currentModelInfo);
}
title: qsTr("Model/Character Settings")
contentItem: GridLayout {
id: generationSettingsTabInner
columns: 2
id: root
columns: 3
rowSpacing: 10
columnSpacing: 10
MyButton {
Layout.row: 8
Layout.column: 1
property var currentModelName: comboBox.currentText
property var currentModelId: comboBox.currentValue
property var currentModelInfo: ModelList.modelInfo(root.currentModelId)
Label {
id: label
Layout.row: 0
Layout.column: 0
text: qsTr("Model/Character:")
color: theme.textColor
}
RowLayout {
Layout.fillWidth: true
text: qsTr("Restore Defaults")
Accessible.description: qsTr("Restores the settings dialog to a default state")
onClicked: {
MySettings.restoreGenerationDefaults();
templateTextArea.text = MySettings.promptTemplate
Layout.row: 1
Layout.column: 0
Layout.columnSpan: 2
height: label.height + 20
spacing: 10
MyComboBox {
id: comboBox
Layout.fillWidth: true
model: ModelList.installedModels
valueRole: "id"
textRole: "name"
currentIndex: 0
contentItem: Text {
leftPadding: 10
rightPadding: 20
text: comboBox.currentText
font: comboBox.font
color: theme.textColor
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
delegate: ItemDelegate {
width: comboBox.width
contentItem: Text {
text: name
color: theme.textColor
font: comboBox.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: highlighted ? theme.backgroundLight : theme.backgroundDark
}
highlighted: comboBox.highlightedIndex === index
}
}
MyButton {
id: cloneButton
text: qsTr("Clone")
onClicked: {
var id = ModelList.clone(root.currentModelInfo);
comboBox.currentIndex = comboBox.indexOfValue(id);
}
}
MyButton {
id: removeButton
enabled: root.currentModelInfo.isClone
text: qsTr("Remove")
onClicked: {
ModelList.remove(root.currentModelInfo);
comboBox.currentIndex = 0;
}
}
}
RowLayout {
Layout.row: 2
Layout.column: 0
Layout.topMargin: 15
spacing: 10
Label {
id: uniqueNameLabel
text: qsTr("Unique Name:")
color: theme.textColor
}
Label {
id: uniqueNameLabelHelp
visible: false
text: qsTr("Must contain a non-empty unique name that does not match any existing model/character.")
color: theme.textErrorColor
wrapMode: TextArea.Wrap
}
}
MyTextField {
id: uniqueNameField
text: root.currentModelName
enabled: root.currentModelInfo.isClone || root.currentModelInfo.description === ""
color: enabled ? theme.textColor : theme.mutedTextColor
Layout.row: 3
Layout.column: 0
Layout.columnSpan: 2
Layout.fillWidth: true
Connections {
target: MySettings
function onNameChanged() {
uniqueNameField.text = root.currentModelInfo.name;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
uniqueNameField.text = root.currentModelInfo.name;
}
}
onTextChanged: {
if (text !== "" && ModelList.isUniqueName(text)) {
MySettings.setModelName(root.currentModelInfo, text);
}
uniqueNameLabelHelp.visible = root.currentModelInfo.name !== "" &&
(text === "" || (text !== root.currentModelInfo.name && !ModelList.isUniqueName(text)));
}
}
Label {
text: qsTr("Model File:")
color: theme.textColor
Layout.row: 4
Layout.column: 0
Layout.topMargin: 15
}
MyTextField {
text: root.currentModelInfo.filename
enabled: false
color: enabled ? theme.textColor : theme.mutedTextColor
Layout.row: 5
Layout.column: 0
Layout.columnSpan: 2
Layout.fillWidth: true
}
Label {
visible: !root.currentModelInfo.isChatGPT
text: qsTr("System Prompt:")
color: theme.textColor
Layout.row: 6
Layout.column: 0
Layout.topMargin: 15
}
Rectangle {
id: systemPrompt
visible: !root.currentModelInfo.isChatGPT
Layout.row: 7
Layout.column: 0
Layout.columnSpan: 2
Layout.fillWidth: true
color: "transparent"
Layout.minimumHeight: Math.max(150, systemPromptArea.contentHeight + 20)
TextArea {
id: systemPromptArea
anchors.fill: parent
text: root.currentModelInfo.systemPrompt
color: theme.textColor
background: Rectangle {
implicitWidth: 150
color: theme.backgroundDark
radius: 10
}
padding: 10
wrapMode: TextArea.Wrap
Connections {
target: MySettings
function onSystemPromptChanged() {
systemPromptArea.text = root.currentModelInfo.systemPrompt;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
systemPromptArea.text = root.currentModelInfo.systemPrompt;
}
}
onTextChanged: {
MySettings.setModelSystemPrompt(root.currentModelInfo, text)
}
bottomPadding: 10
Accessible.role: Accessible.EditableText
ToolTip.text: qsTr("The systemPrompt allows instructions to the model at the beginning of a chat.\nNOTE: A longer, detailed system prompt can lead to higher quality answers, but can also slow down generation.")
ToolTip.visible: hovered
}
}
RowLayout {
Layout.row: 8
Layout.column: 0
Layout.columnSpan: 2
Layout.topMargin: 15
spacing: 10
Label {
id: promptTemplateLabel
text: qsTr("Prompt Template:")
color: theme.textColor
}
Label {
id: promptTemplateLabelHelp
text: qsTr("Must contain the string \"%1\" to be replaced with the user's input.")
color: theme.textErrorColor
visible: templateTextArea.text.indexOf("%1") === -1
wrapMode: TextArea.Wrap
}
}
Rectangle {
id: promptTemplate
Layout.row: 9
Layout.column: 0
Layout.columnSpan: 2
Layout.fillWidth: true
Layout.minimumHeight: Math.max(150, templateTextArea.contentHeight + 20)
color: "transparent"
clip: true
TextArea {
id: templateTextArea
anchors.fill: parent
text: root.currentModelInfo.promptTemplate
color: theme.textColor
background: Rectangle {
implicitWidth: 150
color: theme.backgroundDark
radius: 10
}
padding: 10
wrapMode: TextArea.Wrap
Connections {
target: MySettings
function onPromptTemplateChanged() {
templateTextArea.text = root.currentModelInfo.promptTemplate;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
templateTextArea.text = root.currentModelInfo.promptTemplate;
}
}
onTextChanged: {
if (templateTextArea.text.indexOf("%1") !== -1) {
MySettings.setModelPromptTemplate(root.currentModelInfo, text)
}
}
bottomPadding: 10
Accessible.role: Accessible.EditableText
Accessible.name: promptTemplateLabel.text
Accessible.description: promptTemplateLabelHelp.text
ToolTip.text: qsTr("The prompt template partially determines how models will respond to prompts.\nNOTE: A longer, detailed template can lead to higher quality answers, but can also slow down generation.")
ToolTip.visible: hovered
}
}
Rectangle {
id: optionalImageRect
visible: false // FIXME: for later
Layout.row: 2
Layout.column: 1
Layout.rowSpan: 5
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
Layout.maximumWidth: height
Layout.topMargin: 35
Layout.bottomMargin: 35
Layout.leftMargin: 35
width: 3000
border.width: 1
border.color: theme.tabBorder
radius: 10
color: "transparent"
Item {
anchors.centerIn: parent
height: childrenRect.height
Image {
id: img
anchors.horizontalCenter: parent.horizontalCenter
width: 100
height: 100
source: "qrc:/gpt4all/icons/image.svg"
}
Text {
text: qsTr("Add\noptional image")
anchors.top: img.bottom
anchors.horizontalCenter: parent.horizontalCenter
wrapMode: TextArea.Wrap
horizontalAlignment: Qt.AlignHCenter
color: theme.mutedTextColor
}
}
}
Label {
text: qsTr("Generation Settings")
color: theme.textColor
Layout.row: 10
Layout.column: 0
Layout.columnSpan: 2
Layout.topMargin: 15
Layout.alignment: Qt.AlignHCenter
Layout.minimumWidth: promptTemplate.width
horizontalAlignment: Qt.AlignHCenter
font.pixelSize: theme.fontSizeLarger
font.bold: true
}
GridLayout {
Layout.row: 11
Layout.column: 0
Layout.columnSpan: 2
Layout.topMargin: 15
Layout.fillWidth: true
Layout.minimumWidth: promptTemplate.width
columns: 4
rowSpacing: 10
columnSpacing: 10
Label {
id: tempLabel
text: qsTr("Temperature:")
color: theme.textColor
Layout.row: 0
Layout.column: 0
}
MyTextField {
id: temperatureField
text: root.currentModelInfo.temperature
color: theme.textColor
ToolTip.text: qsTr("Temperature increases the chances of choosing less likely tokens.\nNOTE: Higher temperature gives more creative but less predictable outputs.")
ToolTip.visible: hovered
Layout.row: 0
Layout.column: 1
validator: DoubleValidator {
locale: "C"
}
Connections {
target: MySettings
function onTemperatureChanged() {
temperatureField.text = root.currentModelInfo.temperature;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
temperatureField.text = root.currentModelInfo.temperature;
}
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.setModelTemperature(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.temperature
}
}
Accessible.role: Accessible.EditableText
Accessible.name: tempLabel.text
Accessible.description: ToolTip.text
}
Label {
id: topPLabel
text: qsTr("Top P:")
color: theme.textColor
Layout.row: 0
Layout.column: 2
}
MyTextField {
id: topPField
text: root.currentModelInfo.topP
color: theme.textColor
ToolTip.text: qsTr("Only the most likely tokens up to a total probability of top_p can be chosen.\nNOTE: Prevents choosing highly unlikely tokens, aka Nucleus Sampling")
ToolTip.visible: hovered
Layout.row: 0
Layout.column: 3
validator: DoubleValidator {
locale: "C"
}
Connections {
target: MySettings
function onTopPChanged() {
topPField.text = root.currentModelInfo.topP;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
topPField.text = root.currentModelInfo.topP;
}
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.setModelTopP(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.topP
}
}
Accessible.role: Accessible.EditableText
Accessible.name: topPLabel.text
Accessible.description: ToolTip.text
}
Label {
id: topKLabel
visible: !root.currentModelInfo.isChatGPT
text: qsTr("Top K:")
color: theme.textColor
Layout.row: 1
Layout.column: 0
}
MyTextField {
id: topKField
visible: !root.currentModelInfo.isChatGPT
text: root.currentModelInfo.topK
color: theme.textColor
ToolTip.text: qsTr("Only the top K most likely tokens will be chosen from")
ToolTip.visible: hovered
Layout.row: 1
Layout.column: 1
validator: IntValidator {
bottom: 1
}
Connections {
target: MySettings
function onTopKChanged() {
topKField.text = root.currentModelInfo.topK;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
topKField.text = root.currentModelInfo.topK;
}
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.setModelTopK(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.topK
}
}
Accessible.role: Accessible.EditableText
Accessible.name: topKLabel.text
Accessible.description: ToolTip.text
}
Label {
id: maxLengthLabel
visible: !root.currentModelInfo.isChatGPT
text: qsTr("Max Length:")
color: theme.textColor
Layout.row: 1
Layout.column: 2
}
MyTextField {
id: maxLengthField
visible: !root.currentModelInfo.isChatGPT
text: root.currentModelInfo.maxLength
color: theme.textColor
ToolTip.text: qsTr("Maximum length of response in tokens")
ToolTip.visible: hovered
Layout.row: 1
Layout.column: 3
validator: IntValidator {
bottom: 1
}
Connections {
target: MySettings
function onMaxLengthChanged() {
maxLengthField.text = root.currentModelInfo.maxLength;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
maxLengthField.text = root.currentModelInfo.maxLength;
}
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.setModelMaxLength(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.maxLength
}
}
Accessible.role: Accessible.EditableText
Accessible.name: maxLengthLabel.text
Accessible.description: ToolTip.text
}
Label {
id: batchSizeLabel
visible: !root.currentModelInfo.isChatGPT
text: qsTr("Prompt Batch Size:")
color: theme.textColor
Layout.row: 2
Layout.column: 0
}
MyTextField {
id: batchSizeField
visible: !root.currentModelInfo.isChatGPT
text: root.currentModelInfo.promptBatchSize
color: theme.textColor
ToolTip.text: qsTr("Amount of prompt tokens to process at once.\nNOTE: Higher values can speed up reading prompts but will use more RAM")
ToolTip.visible: hovered
Layout.row: 2
Layout.column: 1
validator: IntValidator {
bottom: 1
}
Connections {
target: MySettings
function onPromptBatchSizeChanged() {
batchSizeField.text = root.currentModelInfo.promptBatchSize;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
batchSizeField.text = root.currentModelInfo.promptBatchSize;
}
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.setModelPromptBatchSize(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.promptBatchSize
}
}
Accessible.role: Accessible.EditableText
Accessible.name: batchSizeLabel.text
Accessible.description: ToolTip.text
}
Label {
id: repeatPenaltyLabel
visible: !root.currentModelInfo.isChatGPT
text: qsTr("Repeat Penalty:")
color: theme.textColor
Layout.row: 2
Layout.column: 2
}
MyTextField {
id: repeatPenaltyField
visible: !root.currentModelInfo.isChatGPT
text: root.currentModelInfo.repeatPenalty
color: theme.textColor
ToolTip.text: qsTr("Amount to penalize repetitiveness of the output")
ToolTip.visible: hovered
Layout.row: 2
Layout.column: 3
validator: DoubleValidator {
locale: "C"
}
Connections {
target: MySettings
function onRepeatPenaltyChanged() {
repeatPenaltyField.text = root.currentModelInfo.repeatPenalty;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
repeatPenaltyField.text = root.currentModelInfo.repeatPenalty;
}
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.setModelRepeatPenalty(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.repeatPenalty
}
}
Accessible.role: Accessible.EditableText
Accessible.name: repeatPenaltyLabel.text
Accessible.description: ToolTip.text
}
Label {
id: repeatPenaltyTokensLabel
visible: !root.currentModelInfo.isChatGPT
text: qsTr("Repeat Penalty Tokens:")
color: theme.textColor
Layout.row: 3
Layout.column: 0
}
MyTextField {
id: repeatPenaltyTokenField
visible: !root.currentModelInfo.isChatGPT
text: root.currentModelInfo.repeatPenaltyTokens
color: theme.textColor
ToolTip.text: qsTr("How far back in output to apply repeat penalty")
ToolTip.visible: hovered
Layout.row: 3
Layout.column: 1
validator: IntValidator {
bottom: 1
}
Connections {
target: MySettings
function onRepeatPenaltyTokensChanged() {
repeatPenaltyTokenField.text = root.currentModelInfo.repeatPenaltyTokens;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
repeatPenaltyTokenField.text = root.currentModelInfo.repeatPenaltyTokens;
}
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.setModelRepeatPenaltyTokens(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.repeatPenaltyTokens
}
}
Accessible.role: Accessible.EditableText
Accessible.name: repeatPenaltyTokensLabel.text
Accessible.description: ToolTip.text
}
}
Rectangle {
Layout.row: 12
Layout.column: 0
Layout.columnSpan: 2
Layout.topMargin: 15
Layout.fillWidth: true
Layout.minimumWidth: promptTemplate.width
height: 1
color: theme.tabBorder
}
}
}

View File

@ -17,7 +17,7 @@ Button {
border.color: myButton.down ? theme.backgroundLightest : theme.buttonBorder
border.width: 2
radius: 10
color: myButton.hovered ? theme.backgroundLighter : theme.backgroundLight
color: myButton.hovered ? theme.backgroundDark : theme.backgroundDarkest
}
Accessible.role: Accessible.Button
Accessible.name: text

View File

@ -9,14 +9,13 @@ ComboBox {
padding: 10
Accessible.role: Accessible.ComboBox
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
id: text
leftPadding: 10
rightPadding: 20
text: comboBox.displayText
font: comboBox.font
color: theme.textColor
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
delegate: ItemDelegate {

View File

@ -11,7 +11,7 @@ TextField {
color: text === "" || isValid ? theme.textColor : theme.textErrorColor
background: Rectangle {
implicitWidth: 150
color: theme.backgroundLighter
color: theme.backgroundDark
radius: 10
}
}

View File

@ -12,71 +12,56 @@ Item {
id: theme
}
property alias title: titleLabel.text
property ListModel tabTitlesModel: ListModel { }
property list<Component> tabs: [ ]
Canvas {
id: canvas
anchors.fill: parent
contextType: "2d"
onPaint: {
var context = getContext("2d");
context.reset();
context.lineWidth = 2;
context.moveTo(stackLayout.x, stackLayout.y);
context.lineTo(stackLayout.x, stackLayout.y + stackLayout.height);
context.lineTo(stackLayout.x + stackLayout.width, stackLayout.y + stackLayout.height);
context.lineTo(stackLayout.x + stackLayout.width, stackLayout.y);
context.lineTo(/*settingsTabBar.currentItem.x + */settingsTabBar.currentItem.width, stackLayout.y);
context.strokeStyle = theme.tabBorder;
context.stroke();
}
Label {
id: titleLabel
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
color: theme.textColor
padding: 10
font.bold: true
font.pixelSize: theme.fontSizeLarger
}
Rectangle {
anchors.top: titleLabel.bottom
anchors.leftMargin: 15
anchors.rightMargin: 15
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: theme.tabBorder
}
TabBar {
id: settingsTabBar
width: parent.width / 1.25
anchors.top: titleLabel.bottom
anchors.topMargin: 15
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width / 1.75
z: 200
visible: tabTitlesModel.count > 1
background: Rectangle {
color: "transparent"
}
Repeater {
model: settingsStack.tabTitlesModel
TabButton {
id: tabButton
padding: 10
contentItem: IconLabel {
color: theme.textColor
font.bold: tabButton.checked
text: model.title
}
background: Rectangle {
color: tabButton.checked ? theme.backgroundDarkest : theme.backgroundLight
// Rectangle {
// anchors.top: parent.top
// anchors.left: parent.left
// anchors.right: parent.right
// height: tabButton.checked
// color: theme.tabBorder
// }
// Rectangle {
// anchors.bottom: parent.bottom
// anchors.left: parent.left
// anchors.right: parent.right
// height: !tabButton.checked
// color: theme.tabBorder
// }
// Rectangle {
// anchors.top: parent.top
// anchors.bottom: parent.bottom
// anchors.left: parent.left
// width: tabButton.checked
// color: theme.tabBorder
// }
// Rectangle {
// anchors.top: parent.top
// anchors.bottom: parent.bottom
// anchors.right: parent.right
// width: tabButton.checked
// color: theme.tabBorder
// }
color: "transparent"
border.width: 1
border.color: tabButton.checked ? theme.tabBorder : "transparent"
radius: 10
}
Accessible.role: Accessible.Button
Accessible.name: model.title
@ -84,9 +69,31 @@ Item {
}
}
Rectangle {
id: dividerTabBar
visible: tabTitlesModel.count > 1
anchors.top: settingsTabBar.bottom
anchors.topMargin: 15
anchors.bottomMargin: 15
anchors.leftMargin: 15
anchors.rightMargin: 15
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: theme.tabBorder
}
Rectangle {
anchors.fill: parent
color: "transparent"
radius: 10
border.width: 1
border.color: theme.tabBorder
}
StackLayout {
id: stackLayout
anchors.top: tabTitlesModel.count > 1 ? settingsTabBar.bottom : parent.top
anchors.top: tabTitlesModel.count > 1 ? dividerTabBar.bottom : titleLabel.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom

View File

@ -5,42 +5,87 @@ import QtQuick.Controls.Basic
import QtQuick.Layouts
Item {
id: root
property string title: ""
property Item contentItem: null
property Item advancedSettings: null
signal restoreDefaultsClicked
onContentItemChanged: function() {
if (contentItem) {
contentItem.parent = tabInner;
contentItem.anchors.left = tabInner.left;
contentItem.anchors.right = tabInner.right;
contentItem.parent = contentInner;
contentItem.anchors.left = contentInner.left;
contentItem.anchors.right = contentInner.right;
}
}
onAdvancedSettingsChanged: function() {
if (advancedSettings) {
advancedSettings.parent = advancedInner;
advancedSettings.anchors.left = advancedInner.left;
advancedSettings.anchors.right = advancedInner.right;
}
}
ScrollView {
id: root
width: parent.width
height: parent.height
padding: 15
rightPadding: 20
contentWidth: availableWidth
contentHeight: tabInner.height
contentHeight: innerColumn.height
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
Theme {
id: theme
}
// background: Rectangle {
// color: 'transparent'
// border.color: theme.tabBorder
// border.width: 1
// radius: 10
// }
Column {
id: tabInner
ColumnLayout {
id: innerColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 15
spacing: 10
Column {
id: contentInner
Layout.fillWidth: true
}
Column {
id: advancedInner
visible: false
Layout.fillWidth: true
}
Item {
Layout.fillWidth: true
height: restoreDefaultsButton.height
MyButton {
id: restoreDefaultsButton
anchors.left: parent.left
width: implicitWidth
text: qsTr("Restore Defaults")
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.description: qsTr("Restores the settings dialog to a default state")
onClicked: {
root.restoreDefaultsClicked();
}
}
MyButton {
id: advancedSettingsButton
anchors.right: parent.right
visible: root.advancedSettings
width: implicitWidth
text: !advancedInner.visible ? qsTr("Advanced Settings") : qsTr("Hide Advanced Settings")
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.description: qsTr("Shows/hides the advanced settings")
onClicked: {
advancedInner.visible = !advancedInner.visible;
}
}
}
}
}
}

View File

@ -8,7 +8,7 @@ TextField {
padding: 10
background: Rectangle {
implicitWidth: 150
color: theme.backgroundLighter
color: theme.backgroundDark
radius: 10
}
}

View File

@ -14,7 +14,6 @@ import mysettings
Dialog {
id: settingsDialog
modal: true
opacity: 0.9
padding: 20
bottomPadding: 30
background: Rectangle {
@ -38,7 +37,7 @@ Dialog {
ListModel {
id: stacksModel
ListElement {
title: "Generation"
title: "Models"
}
ListElement {
title: "Application"
@ -67,7 +66,6 @@ Dialog {
id: item
width: listView.width
height: titleLabel.height + 25
// color: index % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter
color: "transparent"
border.color: theme.backgroundLighter
border.width: index == listView.currentIndex ? 1 : 0
@ -78,6 +76,7 @@ Dialog {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.margins: 20
font.bold: index == listView.currentIndex
text: title
elide: Text.ElideRight
color: theme.textColor
@ -101,19 +100,21 @@ Dialog {
currentIndex: listView.currentIndex
MySettingsStack {
title: qsTr("Model/Character Settings")
tabs: [
Component { ModelSettings { } },
Component { GenerationSettings { } }
Component { ModelSettings { } }
]
}
MySettingsStack {
title: qsTr("Application General Settings")
tabs: [
Component { ApplicationSettings { } }
]
}
MySettingsStack {
title: qsTr("LocalDocs Plugin (BETA) Settings")
tabs: [
Component { LocalDocsSettings { } }
]

View File

@ -7,9 +7,9 @@ QtObject {
property color textAccent: "#8e8ea0"
property color mutedTextColor: backgroundLightest
property color textErrorColor: "red"
property color backgroundDarkest: "#202123"
property color backgroundDarker: "#222326"
property color backgroundDark: "#242528"
property color backgroundDarkest: "#1c1f21"
property color backgroundDarker: "#1e2123"
property color backgroundDark: "#222527"
property color backgroundLight: "#343541"
property color backgroundLighter: "#444654"
property color backgroundLightest: "#7d7d8e"

View File

@ -120,6 +120,74 @@ static QVector<HighlightingRule> pythonHighlightingRules()
return highlightingRules;
}
static QVector<HighlightingRule> csharpHighlightingRules()
{
static QVector<HighlightingRule> highlightingRules;
if (highlightingRules.isEmpty()) {
HighlightingRule rule;
// Function call highlighting
QTextCharFormat functionCallFormat;
functionCallFormat.setForeground(functionCallColor);
rule.pattern = QRegularExpression("\\b(\\w+)\\s*(?=\\()");
rule.format = functionCallFormat;
highlightingRules.append(rule);
// Function definition highlighting
QTextCharFormat functionFormat;
functionFormat.setForeground(functionColor);
rule.pattern = QRegularExpression("\\bvoid|int|double|string|bool\\s+(\\w+)\\s*(?=\\()");
rule.format = functionFormat;
highlightingRules.append(rule);
// Number highlighting
QTextCharFormat numberFormat;
numberFormat.setForeground(numberColor);
rule.pattern = QRegularExpression("\\b[0-9]*\\.?[0-9]+\\b");
rule.format = numberFormat;
highlightingRules.append(rule);
// Keyword highlighting
QTextCharFormat keywordFormat;
keywordFormat.setForeground(keywordColor);
QStringList keywordPatterns = {
"\\bvoid\\b", "\\bint\\b", "\\bdouble\\b", "\\bstring\\b", "\\bbool\\b",
"\\bclass\\b", "\\bif\\b", "\\belse\\b", "\\bwhile\\b", "\\bfor\\b",
"\\breturn\\b", "\\bnew\\b", "\\bthis\\b", "\\bpublic\\b", "\\bprivate\\b",
"\\bprotected\\b", "\\bstatic\\b", "\\btrue\\b", "\\bfalse\\b", "\\bnull\\b",
"\\bnamespace\\b", "\\busing\\b", "\\btry\\b", "\\bcatch\\b", "\\bfinally\\b",
"\\bthrow\\b", "\\bvar\\b"
};
for (const QString &pattern : keywordPatterns) {
rule.pattern = QRegularExpression(pattern);
rule.format = keywordFormat;
highlightingRules.append(rule);
}
// String highlighting
QTextCharFormat stringFormat;
stringFormat.setForeground(stringColor);
rule.pattern = QRegularExpression("\".*?\"");
rule.format = stringFormat;
highlightingRules.append(rule);
// Single-line comment highlighting
QTextCharFormat commentFormat;
commentFormat.setForeground(commentColor);
rule.pattern = QRegularExpression("//[^\n]*");
rule.format = commentFormat;
highlightingRules.append(rule);
// Multi-line comment highlighting
rule.pattern = QRegularExpression("/\\*.*?\\*/");
rule.format = commentFormat;
highlightingRules.append(rule);
}
return highlightingRules;
}
static QVector<HighlightingRule> cppHighlightingRules()
{
static QVector<HighlightingRule> highlightingRules;

View File

@ -14,11 +14,11 @@
static inline QJsonObject modelToJson(const ModelInfo &info)
{
QJsonObject model;
model.insert("id", info.name);
model.insert("id", info.name());
model.insert("object", "model");
model.insert("created", "who can keep track?");
model.insert("owned_by", "humanity");
model.insert("root", info.name);
model.insert("root", info.name());
model.insert("parent", QJsonValue::Null);
QJsonArray permissions;
@ -106,7 +106,7 @@ void Server::start()
if (!info.installed)
continue;
if (model == info.name) {
if (model == info.name()) {
object = modelToJson(info);
break;
}
@ -173,7 +173,7 @@ QHttpServerResponse Server::handleCompletionRequest(const QHttpServerRequest &re
for (const ModelInfo &info : modelList) {
if (!info.installed)
continue;
if (modelRequested == info.name || modelRequested == info.filename) {
if (modelRequested == info.name() || modelRequested == info.filename()) {
modelInfo = info;
break;
}
@ -284,32 +284,28 @@ QHttpServerResponse Server::handleCompletionRequest(const QHttpServerRequest &re
// load the new model if necessary
setShouldBeLoaded(true);
if (modelInfo.filename.isEmpty()) {
if (modelInfo.filename().isEmpty()) {
std::cerr << "ERROR: couldn't load default model " << modelRequested.toStdString() << std::endl;
return QHttpServerResponse(QHttpServerResponder::StatusCode::BadRequest);
} else if (!loadModel(modelInfo)) {
std::cerr << "ERROR: couldn't load model " << modelInfo.name.toStdString() << std::endl;
std::cerr << "ERROR: couldn't load model " << modelInfo.name().toStdString() << std::endl;
return QHttpServerResponse(QHttpServerResponder::StatusCode::InternalServerError);
}
// don't remember any context
resetContext();
const QString promptTemplate = MySettings::globalInstance()->promptTemplate();
const float top_k = MySettings::globalInstance()->topK();
const int n_batch = MySettings::globalInstance()->promptBatchSize();
const float repeat_penalty = MySettings::globalInstance()->repeatPenalty();
const int repeat_last_n = MySettings::globalInstance()->repeatPenaltyTokens();
int threadCount = MySettings::globalInstance()->threadCount();
if (threadCount <= 0)
threadCount = std::min(4, (int32_t) std::thread::hardware_concurrency());
const QString promptTemplate = modelInfo.promptTemplate();
const float top_k = modelInfo.topK();
const int n_batch = modelInfo.promptBatchSize();
const float repeat_penalty = modelInfo.repeatPenalty();
const int repeat_last_n = modelInfo.repeatPenaltyTokens();
int promptTokens = 0;
int responseTokens = 0;
QList<QPair<QString, QList<ResultInfo>>> responses;
for (int i = 0; i < n; ++i) {
if (!prompt(
if (!promptInternal(
m_collections,
actualPrompt,
promptTemplate,
@ -319,10 +315,9 @@ QHttpServerResponse Server::handleCompletionRequest(const QHttpServerRequest &re
temperature,
n_batch,
repeat_penalty,
repeat_last_n,
threadCount)) {
repeat_last_n)) {
std::cerr << "ERROR: couldn't prompt model " << modelInfo.name.toStdString() << std::endl;
std::cerr << "ERROR: couldn't prompt model " << modelInfo.name().toStdString() << std::endl;
return QHttpServerResponse(QHttpServerResponder::StatusCode::InternalServerError);
}
QString echoedPrompt = actualPrompt;
@ -340,7 +335,7 @@ QHttpServerResponse Server::handleCompletionRequest(const QHttpServerRequest &re
responseObject.insert("id", "foobarbaz");
responseObject.insert("object", "text_completion");
responseObject.insert("created", QDateTime::currentSecsSinceEpoch());
responseObject.insert("model", modelInfo.name);
responseObject.insert("model", modelInfo.name());
QJsonArray choices;