mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2024-10-01 01:06:10 -04:00
Modellist temp
This commit is contained in:
parent
c1794597a7
commit
7f01b153b3
@ -71,9 +71,10 @@ qt_add_executable(chat
|
|||||||
chatgpt.h chatgpt.cpp
|
chatgpt.h chatgpt.cpp
|
||||||
database.h database.cpp
|
database.h database.cpp
|
||||||
download.h download.cpp
|
download.h download.cpp
|
||||||
network.h network.cpp
|
|
||||||
localdocs.h localdocs.cpp localdocsmodel.h localdocsmodel.cpp
|
localdocs.h localdocs.cpp localdocsmodel.h localdocsmodel.cpp
|
||||||
llm.h llm.cpp
|
llm.h llm.cpp
|
||||||
|
modellist.h modellist.cpp
|
||||||
|
network.h network.cpp
|
||||||
server.h server.cpp
|
server.h server.cpp
|
||||||
logger.h logger.cpp
|
logger.h logger.cpp
|
||||||
responsetext.h responsetext.cpp
|
responsetext.h responsetext.cpp
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "chat.h"
|
#include "chat.h"
|
||||||
|
#include "chatlistmodel.h"
|
||||||
#include "llm.h"
|
#include "llm.h"
|
||||||
#include "localdocs.h"
|
#include "modellist.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "download.h"
|
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
Chat::Chat(QObject *parent)
|
Chat::Chat(QObject *parent)
|
||||||
@ -45,17 +45,6 @@ Chat::~Chat()
|
|||||||
|
|
||||||
void Chat::connectLLM()
|
void Chat::connectLLM()
|
||||||
{
|
{
|
||||||
const QString exePath = QCoreApplication::applicationDirPath() + QDir::separator();
|
|
||||||
const QString localPath = Download::globalInstance()->downloadLocalModelsPath();
|
|
||||||
m_watcher = new QFileSystemWatcher(this);
|
|
||||||
m_watcher->addPath(exePath);
|
|
||||||
m_watcher->addPath(localPath);
|
|
||||||
|
|
||||||
// Should be in same thread
|
|
||||||
connect(Download::globalInstance(), &Download::modelListChanged, this, &Chat::handleModelListChanged, Qt::DirectConnection);
|
|
||||||
connect(this, &Chat::modelNameChanged, this, &Chat::handleModelListChanged, Qt::DirectConnection);
|
|
||||||
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &Chat::handleModelListChanged);
|
|
||||||
|
|
||||||
// Should be in different threads
|
// Should be in different threads
|
||||||
connect(m_llmodel, &ChatLLM::isModelLoadedChanged, this, &Chat::handleModelLoadedChanged, Qt::QueuedConnection);
|
connect(m_llmodel, &ChatLLM::isModelLoadedChanged, this, &Chat::handleModelLoadedChanged, Qt::QueuedConnection);
|
||||||
connect(m_llmodel, &ChatLLM::responseChanged, this, &Chat::handleResponseChanged, Qt::QueuedConnection);
|
connect(m_llmodel, &ChatLLM::responseChanged, this, &Chat::handleResponseChanged, Qt::QueuedConnection);
|
||||||
@ -66,24 +55,23 @@ void Chat::connectLLM()
|
|||||||
connect(m_llmodel, &ChatLLM::generatedNameChanged, this, &Chat::generatedNameChanged, Qt::QueuedConnection);
|
connect(m_llmodel, &ChatLLM::generatedNameChanged, this, &Chat::generatedNameChanged, Qt::QueuedConnection);
|
||||||
connect(m_llmodel, &ChatLLM::reportSpeed, this, &Chat::handleTokenSpeedChanged, Qt::QueuedConnection);
|
connect(m_llmodel, &ChatLLM::reportSpeed, this, &Chat::handleTokenSpeedChanged, Qt::QueuedConnection);
|
||||||
connect(m_llmodel, &ChatLLM::databaseResultsChanged, this, &Chat::handleDatabaseResultsChanged, Qt::QueuedConnection);
|
connect(m_llmodel, &ChatLLM::databaseResultsChanged, this, &Chat::handleDatabaseResultsChanged, Qt::QueuedConnection);
|
||||||
|
connect(m_llmodel, &ChatLLM::modelInfoChanged, this, &Chat::handleModelInfoChanged, Qt::QueuedConnection);
|
||||||
|
|
||||||
connect(this, &Chat::promptRequested, m_llmodel, &ChatLLM::prompt, Qt::QueuedConnection);
|
connect(this, &Chat::promptRequested, m_llmodel, &ChatLLM::prompt, Qt::QueuedConnection);
|
||||||
connect(this, &Chat::modelNameChangeRequested, m_llmodel, &ChatLLM::modelNameChangeRequested, Qt::QueuedConnection);
|
connect(this, &Chat::modelChangeRequested, m_llmodel, &ChatLLM::modelChangeRequested, Qt::QueuedConnection);
|
||||||
connect(this, &Chat::loadDefaultModelRequested, m_llmodel, &ChatLLM::loadDefaultModel, Qt::QueuedConnection);
|
connect(this, &Chat::loadDefaultModelRequested, m_llmodel, &ChatLLM::loadDefaultModel, Qt::QueuedConnection);
|
||||||
connect(this, &Chat::loadModelRequested, m_llmodel, &ChatLLM::loadModel, Qt::QueuedConnection);
|
connect(this, &Chat::loadModelRequested, m_llmodel, &ChatLLM::loadModel, Qt::QueuedConnection);
|
||||||
connect(this, &Chat::generateNameRequested, m_llmodel, &ChatLLM::generateName, Qt::QueuedConnection);
|
connect(this, &Chat::generateNameRequested, m_llmodel, &ChatLLM::generateName, Qt::QueuedConnection);
|
||||||
connect(this, &Chat::regenerateResponseRequested, m_llmodel, &ChatLLM::regenerateResponse, Qt::QueuedConnection);
|
connect(this, &Chat::regenerateResponseRequested, m_llmodel, &ChatLLM::regenerateResponse, Qt::QueuedConnection);
|
||||||
connect(this, &Chat::resetResponseRequested, m_llmodel, &ChatLLM::resetResponse, 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::resetContextRequested, m_llmodel, &ChatLLM::resetContext, Qt::QueuedConnection);
|
||||||
|
|
||||||
emit defaultModelChanged(modelList().first());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::reset()
|
void Chat::reset()
|
||||||
{
|
{
|
||||||
stopGenerating();
|
stopGenerating();
|
||||||
// Erase our current on disk representation as we're completely resetting the chat along with id
|
// Erase our current on disk representation as we're completely resetting the chat along with id
|
||||||
LLM::globalInstance()->chatListModel()->removeChatFile(this);
|
ChatListModel::globalInstance()->removeChatFile(this);
|
||||||
emit resetContextRequested();
|
emit resetContextRequested();
|
||||||
m_id = Network::globalInstance()->generateUniqueId();
|
m_id = Network::globalInstance()->generateUniqueId();
|
||||||
emit idChanged(m_id);
|
emit idChanged(m_id);
|
||||||
@ -251,21 +239,21 @@ void Chat::responseStopped()
|
|||||||
Network::globalInstance()->sendChatStarted();
|
Network::globalInstance()->sendChatStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Chat::modelName() const
|
ModelInfo Chat::modelInfo() const
|
||||||
{
|
{
|
||||||
return m_modelName;
|
return m_modelInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::setModelName(const QString &modelName)
|
void Chat::setModelInfo(const ModelInfo &modelInfo)
|
||||||
{
|
{
|
||||||
if (m_modelName == modelName)
|
if (m_modelInfo == modelInfo)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_modelLoadingError = QString();
|
m_modelLoadingError = QString();
|
||||||
emit modelLoadingErrorChanged();
|
emit modelLoadingErrorChanged();
|
||||||
m_modelName = modelName;
|
m_modelInfo = modelInfo;
|
||||||
emit modelNameChanged();
|
emit modelInfoChanged();
|
||||||
emit modelNameChangeRequested(modelName);
|
emit modelChangeRequested(modelInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::newPromptResponsePair(const QString &prompt)
|
void Chat::newPromptResponsePair(const QString &prompt)
|
||||||
@ -292,17 +280,16 @@ bool Chat::isRecalc() const
|
|||||||
|
|
||||||
void Chat::loadDefaultModel()
|
void Chat::loadDefaultModel()
|
||||||
{
|
{
|
||||||
emit defaultModelChanged(modelList().first());
|
|
||||||
m_modelLoadingError = QString();
|
m_modelLoadingError = QString();
|
||||||
emit modelLoadingErrorChanged();
|
emit modelLoadingErrorChanged();
|
||||||
emit loadDefaultModelRequested();
|
emit loadDefaultModelRequested();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::loadModel(const QString &modelName)
|
void Chat::loadModel(const ModelInfo &modelInfo)
|
||||||
{
|
{
|
||||||
m_modelLoadingError = QString();
|
m_modelLoadingError = QString();
|
||||||
emit modelLoadingErrorChanged();
|
emit modelLoadingErrorChanged();
|
||||||
emit loadModelRequested(modelName);
|
emit loadModelRequested(modelInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::unloadAndDeleteLater()
|
void Chat::unloadAndDeleteLater()
|
||||||
@ -361,13 +348,22 @@ void Chat::handleDatabaseResultsChanged(const QList<ResultInfo> &results)
|
|||||||
m_databaseResults = results;
|
m_databaseResults = results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Chat::handleModelInfoChanged(const ModelInfo &modelInfo)
|
||||||
|
{
|
||||||
|
if (m_modelInfo == modelInfo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_modelInfo = modelInfo;
|
||||||
|
emit modelInfoChanged();
|
||||||
|
}
|
||||||
|
|
||||||
bool Chat::serialize(QDataStream &stream, int version) const
|
bool Chat::serialize(QDataStream &stream, int version) const
|
||||||
{
|
{
|
||||||
stream << m_creationDate;
|
stream << m_creationDate;
|
||||||
stream << m_id;
|
stream << m_id;
|
||||||
stream << m_name;
|
stream << m_name;
|
||||||
stream << m_userName;
|
stream << m_userName;
|
||||||
stream << m_modelName;
|
stream << m_modelInfo.filename;
|
||||||
if (version > 2)
|
if (version > 2)
|
||||||
stream << m_collections;
|
stream << m_collections;
|
||||||
if (!m_llmodel->serialize(stream, version))
|
if (!m_llmodel->serialize(stream, version))
|
||||||
@ -385,17 +381,23 @@ bool Chat::deserialize(QDataStream &stream, int version)
|
|||||||
stream >> m_name;
|
stream >> m_name;
|
||||||
stream >> m_userName;
|
stream >> m_userName;
|
||||||
emit nameChanged();
|
emit nameChanged();
|
||||||
stream >> m_modelName;
|
|
||||||
emit modelNameChanged();
|
QString filename;
|
||||||
|
stream >> filename;
|
||||||
|
if (!ModelList::globalInstance()->contains(filename))
|
||||||
|
return false;
|
||||||
|
m_modelInfo = ModelList::globalInstance()->modelInfo(filename);
|
||||||
|
emit modelInfoChanged();
|
||||||
|
|
||||||
// Prior to version 2 gptj models had a bug that fixed the kv_cache to F32 instead of F16 so
|
// 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
|
// unfortunately, we cannot deserialize these
|
||||||
if (version < 2 && m_modelName.contains("gpt4all-j"))
|
if (version < 2 && m_modelInfo.filename.contains("gpt4all-j"))
|
||||||
return false;
|
return false;
|
||||||
if (version > 2) {
|
if (version > 2) {
|
||||||
stream >> m_collections;
|
stream >> m_collections;
|
||||||
emit collectionListChanged(m_collections);
|
emit collectionListChanged(m_collections);
|
||||||
}
|
}
|
||||||
m_llmodel->setModelName(m_modelName);
|
m_llmodel->setModelInfo(m_modelInfo);
|
||||||
if (!m_llmodel->deserialize(stream, version))
|
if (!m_llmodel->deserialize(stream, version))
|
||||||
return false;
|
return false;
|
||||||
if (!m_chatModel->deserialize(stream, version))
|
if (!m_chatModel->deserialize(stream, version))
|
||||||
@ -404,103 +406,6 @@ bool Chat::deserialize(QDataStream &stream, int version)
|
|||||||
return stream.status() == QDataStream::Ok;
|
return stream.status() == QDataStream::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QString> Chat::modelList() const
|
|
||||||
{
|
|
||||||
// Build a model list from exepath and from the localpath
|
|
||||||
QList<QString> list;
|
|
||||||
|
|
||||||
QString exePath = QCoreApplication::applicationDirPath() + QDir::separator();
|
|
||||||
QString localPath = Download::globalInstance()->downloadLocalModelsPath();
|
|
||||||
|
|
||||||
QSettings settings;
|
|
||||||
settings.sync();
|
|
||||||
// The user default model can be set by the user in the settings dialog. The "default" user
|
|
||||||
// default model is "Application default" which signals we should use the default model that was
|
|
||||||
// specified by the models.json file.
|
|
||||||
QString defaultModel = settings.value("userDefaultModel").toString();
|
|
||||||
if (defaultModel.isEmpty() || defaultModel == "Application default")
|
|
||||||
defaultModel = settings.value("defaultModel").toString();
|
|
||||||
|
|
||||||
QString currentModelName = modelName().isEmpty() ? defaultModel : modelName();
|
|
||||||
|
|
||||||
{
|
|
||||||
QDir dir(exePath);
|
|
||||||
QStringList allFiles = dir.entryList(QDir::Files);
|
|
||||||
|
|
||||||
// All files that end with .bin and have 'ggml' somewhere in the name
|
|
||||||
QStringList fileNames;
|
|
||||||
for(const QString& filename : allFiles) {
|
|
||||||
if (filename.endsWith(".bin") && filename.contains("ggml")) {
|
|
||||||
fileNames.append(filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const QString& f : fileNames) {
|
|
||||||
QString filePath = exePath + f;
|
|
||||||
QFileInfo info(filePath);
|
|
||||||
QString basename = info.completeBaseName();
|
|
||||||
QString name = basename.startsWith("ggml-") ? basename.remove(0, 5) : basename;
|
|
||||||
if (info.exists()) {
|
|
||||||
if (name == currentModelName)
|
|
||||||
list.prepend(name);
|
|
||||||
else
|
|
||||||
list.append(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localPath != exePath) {
|
|
||||||
QDir dir(localPath);
|
|
||||||
QStringList allFiles = dir.entryList(QDir::Files);
|
|
||||||
QStringList fileNames;
|
|
||||||
for(const QString& filename : allFiles) {
|
|
||||||
if ((filename.endsWith(".bin") && filename.contains("ggml"))
|
|
||||||
|| (filename.endsWith(".txt") && filename.startsWith("chatgpt-"))) {
|
|
||||||
fileNames.append(filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const QString &f : fileNames) {
|
|
||||||
QString filePath = localPath + f;
|
|
||||||
QFileInfo info(filePath);
|
|
||||||
QString basename = info.completeBaseName();
|
|
||||||
QString name = basename.startsWith("ggml-") ? basename.remove(0, 5) : basename;
|
|
||||||
if (info.exists() && !list.contains(name)) { // don't allow duplicates
|
|
||||||
if (name == currentModelName)
|
|
||||||
list.prepend(name);
|
|
||||||
else
|
|
||||||
list.append(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
if (exePath != localPath) {
|
|
||||||
qWarning() << "ERROR: Could not find any applicable models in"
|
|
||||||
<< exePath << "nor" << localPath;
|
|
||||||
} else {
|
|
||||||
qWarning() << "ERROR: Could not find any applicable models in"
|
|
||||||
<< exePath;
|
|
||||||
}
|
|
||||||
return QList<QString>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Chat::handleModelListChanged()
|
|
||||||
{
|
|
||||||
emit modelListChanged();
|
|
||||||
emit defaultModelChanged(modelList().first());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Chat::handleDownloadLocalModelsPathChanged()
|
|
||||||
{
|
|
||||||
emit modelListChanged();
|
|
||||||
emit defaultModelChanged(modelList().first());
|
|
||||||
m_watcher->addPath(Download::globalInstance()->downloadLocalModelsPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QString> Chat::collectionList() const
|
QList<QString> Chat::collectionList() const
|
||||||
{
|
{
|
||||||
return m_collections;
|
return m_collections;
|
||||||
|
@ -17,10 +17,9 @@ class Chat : public QObject
|
|||||||
Q_PROPERTY(ChatModel *chatModel READ chatModel NOTIFY chatModelChanged)
|
Q_PROPERTY(ChatModel *chatModel READ chatModel NOTIFY chatModelChanged)
|
||||||
Q_PROPERTY(bool isModelLoaded READ isModelLoaded NOTIFY isModelLoadedChanged)
|
Q_PROPERTY(bool isModelLoaded READ isModelLoaded NOTIFY isModelLoadedChanged)
|
||||||
Q_PROPERTY(QString response READ response NOTIFY responseChanged)
|
Q_PROPERTY(QString response READ response NOTIFY responseChanged)
|
||||||
Q_PROPERTY(QString modelName READ modelName WRITE setModelName NOTIFY modelNameChanged)
|
Q_PROPERTY(ModelInfo modelInfo READ modelInfo WRITE setModelInfo NOTIFY modelInfoChanged)
|
||||||
Q_PROPERTY(bool responseInProgress READ responseInProgress NOTIFY responseInProgressChanged)
|
Q_PROPERTY(bool responseInProgress READ responseInProgress NOTIFY responseInProgressChanged)
|
||||||
Q_PROPERTY(bool isRecalc READ isRecalc NOTIFY recalcChanged)
|
Q_PROPERTY(bool isRecalc READ isRecalc NOTIFY recalcChanged)
|
||||||
Q_PROPERTY(QList<QString> modelList READ modelList NOTIFY modelListChanged)
|
|
||||||
Q_PROPERTY(bool isServer READ isServer NOTIFY isServerChanged)
|
Q_PROPERTY(bool isServer READ isServer NOTIFY isServerChanged)
|
||||||
Q_PROPERTY(QString responseState READ responseState NOTIFY responseStateChanged)
|
Q_PROPERTY(QString responseState READ responseState NOTIFY responseStateChanged)
|
||||||
Q_PROPERTY(QList<QString> collectionList READ collectionList NOTIFY collectionListChanged)
|
Q_PROPERTY(QList<QString> collectionList READ collectionList NOTIFY collectionListChanged)
|
||||||
@ -66,12 +65,12 @@ public:
|
|||||||
QString response() const;
|
QString response() const;
|
||||||
bool responseInProgress() const { return m_responseInProgress; }
|
bool responseInProgress() const { return m_responseInProgress; }
|
||||||
QString responseState() const;
|
QString responseState() const;
|
||||||
QString modelName() const;
|
ModelInfo modelInfo() const;
|
||||||
void setModelName(const QString &modelName);
|
void setModelInfo(const ModelInfo &modelInfo);
|
||||||
bool isRecalc() const;
|
bool isRecalc() const;
|
||||||
|
|
||||||
void loadDefaultModel();
|
void loadDefaultModel();
|
||||||
void loadModel(const QString &modelName);
|
void loadModel(const ModelInfo &modelInfo);
|
||||||
void unloadModel();
|
void unloadModel();
|
||||||
void reloadModel();
|
void reloadModel();
|
||||||
void unloadAndDeleteLater();
|
void unloadAndDeleteLater();
|
||||||
@ -79,8 +78,6 @@ public:
|
|||||||
qint64 creationDate() const { return m_creationDate; }
|
qint64 creationDate() const { return m_creationDate; }
|
||||||
bool serialize(QDataStream &stream, int version) const;
|
bool serialize(QDataStream &stream, int version) const;
|
||||||
bool deserialize(QDataStream &stream, int version);
|
bool deserialize(QDataStream &stream, int version);
|
||||||
|
|
||||||
QList<QString> modelList() const;
|
|
||||||
bool isServer() const { return m_isServer; }
|
bool isServer() const { return m_isServer; }
|
||||||
|
|
||||||
QList<QString> collectionList() const;
|
QList<QString> collectionList() const;
|
||||||
@ -111,18 +108,16 @@ Q_SIGNALS:
|
|||||||
void regenerateResponseRequested();
|
void regenerateResponseRequested();
|
||||||
void resetResponseRequested();
|
void resetResponseRequested();
|
||||||
void resetContextRequested();
|
void resetContextRequested();
|
||||||
void modelNameChangeRequested(const QString &modelName);
|
void modelChangeRequested(const ModelInfo &modelInfo);
|
||||||
void modelNameChanged();
|
void modelInfoChanged();
|
||||||
void recalcChanged();
|
void recalcChanged();
|
||||||
void loadDefaultModelRequested();
|
void loadDefaultModelRequested();
|
||||||
void loadModelRequested(const QString &modelName);
|
void loadModelRequested(const ModelInfo &modelInfo);
|
||||||
void generateNameRequested();
|
void generateNameRequested();
|
||||||
void modelListChanged();
|
|
||||||
void modelLoadingErrorChanged();
|
void modelLoadingErrorChanged();
|
||||||
void isServerChanged();
|
void isServerChanged();
|
||||||
void collectionListChanged(const QList<QString> &collectionList);
|
void collectionListChanged(const QList<QString> &collectionList);
|
||||||
void tokenSpeedChanged();
|
void tokenSpeedChanged();
|
||||||
void defaultModelChanged(const QString &defaultModel);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void handleResponseChanged(const QString &response);
|
void handleResponseChanged(const QString &response);
|
||||||
@ -134,15 +129,14 @@ private Q_SLOTS:
|
|||||||
void handleModelLoadingError(const QString &error);
|
void handleModelLoadingError(const QString &error);
|
||||||
void handleTokenSpeedChanged(const QString &tokenSpeed);
|
void handleTokenSpeedChanged(const QString &tokenSpeed);
|
||||||
void handleDatabaseResultsChanged(const QList<ResultInfo> &results);
|
void handleDatabaseResultsChanged(const QList<ResultInfo> &results);
|
||||||
void handleModelListChanged();
|
void handleModelInfoChanged(const ModelInfo &modelInfo);
|
||||||
void handleDownloadLocalModelsPathChanged();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_id;
|
QString m_id;
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QString m_generatedName;
|
QString m_generatedName;
|
||||||
QString m_userName;
|
QString m_userName;
|
||||||
QString m_modelName;
|
ModelInfo m_modelInfo;
|
||||||
QString m_modelLoadingError;
|
QString m_modelLoadingError;
|
||||||
QString m_tokenSpeed;
|
QString m_tokenSpeed;
|
||||||
QString m_response;
|
QString m_response;
|
||||||
@ -156,7 +150,6 @@ private:
|
|||||||
bool m_isServer;
|
bool m_isServer;
|
||||||
bool m_shouldDeleteLater;
|
bool m_shouldDeleteLater;
|
||||||
bool m_isModelLoaded;
|
bool m_isModelLoaded;
|
||||||
QFileSystemWatcher *m_watcher;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CHAT_H
|
#endif // CHAT_H
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "chatlistmodel.h"
|
#include "chatlistmodel.h"
|
||||||
#include "download.h"
|
|
||||||
#include "llm.h"
|
#include "llm.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@ -8,8 +7,15 @@
|
|||||||
#define CHAT_FORMAT_MAGIC 0xF5D553CC
|
#define CHAT_FORMAT_MAGIC 0xF5D553CC
|
||||||
#define CHAT_FORMAT_VERSION 4
|
#define CHAT_FORMAT_VERSION 4
|
||||||
|
|
||||||
ChatListModel::ChatListModel(QObject *parent)
|
class MyChatListModel: public ChatListModel { };
|
||||||
: QAbstractListModel(parent)
|
Q_GLOBAL_STATIC(MyChatListModel, chatListModelInstance)
|
||||||
|
ChatListModel *ChatListModel::globalInstance()
|
||||||
|
{
|
||||||
|
return chatListModelInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatListModel::ChatListModel()
|
||||||
|
: QAbstractListModel(nullptr)
|
||||||
, m_newChat(nullptr)
|
, m_newChat(nullptr)
|
||||||
, m_dummyChat(nullptr)
|
, m_dummyChat(nullptr)
|
||||||
, m_serverChat(nullptr)
|
, m_serverChat(nullptr)
|
||||||
@ -54,7 +60,7 @@ void ChatListModel::setShouldSaveChatGPTChats(bool b)
|
|||||||
void ChatListModel::removeChatFile(Chat *chat) const
|
void ChatListModel::removeChatFile(Chat *chat) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(chat != m_serverChat);
|
Q_ASSERT(chat != m_serverChat);
|
||||||
const QString savePath = Download::globalInstance()->downloadLocalModelsPath();
|
const QString savePath = ModelList::globalInstance()->localModelsPath();
|
||||||
QFile file(savePath + "/gpt4all-" + chat->id() + ".chat");
|
QFile file(savePath + "/gpt4all-" + chat->id() + ".chat");
|
||||||
if (!file.exists())
|
if (!file.exists())
|
||||||
return;
|
return;
|
||||||
@ -72,12 +78,12 @@ ChatSaver::ChatSaver()
|
|||||||
|
|
||||||
void ChatListModel::saveChats()
|
void ChatListModel::saveChats()
|
||||||
{
|
{
|
||||||
const QString savePath = Download::globalInstance()->downloadLocalModelsPath();
|
const QString savePath = ModelList::globalInstance()->localModelsPath();
|
||||||
QVector<Chat*> toSave;
|
QVector<Chat*> toSave;
|
||||||
for (Chat *chat : m_chats) {
|
for (Chat *chat : m_chats) {
|
||||||
if (chat == m_serverChat)
|
if (chat == m_serverChat)
|
||||||
continue;
|
continue;
|
||||||
const bool isChatGPT = chat->modelName().startsWith("chatgpt-");
|
const bool isChatGPT = chat->modelInfo().isChatGPT;
|
||||||
if (!isChatGPT && !m_shouldSaveChats)
|
if (!isChatGPT && !m_shouldSaveChats)
|
||||||
continue;
|
continue;
|
||||||
if (isChatGPT && !m_shouldSaveChatGPTChats)
|
if (isChatGPT && !m_shouldSaveChatGPTChats)
|
||||||
@ -99,7 +105,7 @@ void ChatSaver::saveChats(const QVector<Chat *> &chats)
|
|||||||
{
|
{
|
||||||
QElapsedTimer timer;
|
QElapsedTimer timer;
|
||||||
timer.start();
|
timer.start();
|
||||||
const QString savePath = Download::globalInstance()->downloadLocalModelsPath();
|
const QString savePath = ModelList::globalInstance()->localModelsPath();
|
||||||
for (Chat *chat : chats) {
|
for (Chat *chat : chats) {
|
||||||
QString fileName = "gpt4all-" + chat->id() + ".chat";
|
QString fileName = "gpt4all-" + chat->id() + ".chat";
|
||||||
QFile file(savePath + "/" + fileName);
|
QFile file(savePath + "/" + fileName);
|
||||||
@ -162,7 +168,7 @@ void ChatsRestoreThread::run()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const QString savePath = Download::globalInstance()->downloadLocalModelsPath();
|
const QString savePath = ModelList::globalInstance()->localModelsPath();
|
||||||
QDir dir(savePath);
|
QDir dir(savePath);
|
||||||
dir.setNameFilters(QStringList() << "gpt4all-*.chat");
|
dir.setNameFilters(QStringList() << "gpt4all-*.chat");
|
||||||
QStringList fileNames = dir.entryList();
|
QStringList fileNames = dir.entryList();
|
||||||
|
@ -40,7 +40,7 @@ class ChatListModel : public QAbstractListModel
|
|||||||
Q_PROPERTY(bool shouldSaveChatGPTChats READ shouldSaveChatGPTChats WRITE setShouldSaveChatGPTChats NOTIFY shouldSaveChatGPTChatsChanged)
|
Q_PROPERTY(bool shouldSaveChatGPTChats READ shouldSaveChatGPTChats WRITE setShouldSaveChatGPTChats NOTIFY shouldSaveChatGPTChatsChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ChatListModel(QObject *parent = nullptr);
|
static ChatListModel *globalInstance();
|
||||||
|
|
||||||
enum Roles {
|
enum Roles {
|
||||||
IdRole = Qt::UserRole + 1,
|
IdRole = Qt::UserRole + 1,
|
||||||
@ -262,6 +262,11 @@ private:
|
|||||||
Chat* m_serverChat;
|
Chat* m_serverChat;
|
||||||
Chat* m_currentChat;
|
Chat* m_currentChat;
|
||||||
QList<Chat*> m_chats;
|
QList<Chat*> m_chats;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit ChatListModel();
|
||||||
|
~ChatListModel() {}
|
||||||
|
friend class MyChatListModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CHATITEMMODEL_H
|
#endif // CHATITEMMODEL_H
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#include "chatllm.h"
|
#include "chatllm.h"
|
||||||
#include "chat.h"
|
#include "chat.h"
|
||||||
#include "download.h"
|
#include "chatgpt.h"
|
||||||
|
#include "modellist.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "../gpt4all-backend/llmodel.h"
|
#include "../gpt4all-backend/llmodel.h"
|
||||||
#include "chatgpt.h"
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@ -20,29 +20,6 @@
|
|||||||
#define REPLIT_INTERNAL_STATE_VERSION 0
|
#define REPLIT_INTERNAL_STATE_VERSION 0
|
||||||
#define LLAMA_INTERNAL_STATE_VERSION 0
|
#define LLAMA_INTERNAL_STATE_VERSION 0
|
||||||
|
|
||||||
static QString modelFilePath(const QString &modelName, bool isChatGPT)
|
|
||||||
{
|
|
||||||
QVector<QString> possibleFilePaths;
|
|
||||||
if (isChatGPT)
|
|
||||||
possibleFilePaths << "/" + modelName + ".txt";
|
|
||||||
else {
|
|
||||||
possibleFilePaths << "/ggml-" + modelName + ".bin";
|
|
||||||
possibleFilePaths << "/" + modelName + ".bin";
|
|
||||||
}
|
|
||||||
for (const QString &modelFilename : possibleFilePaths) {
|
|
||||||
QString appPath = QCoreApplication::applicationDirPath() + modelFilename;
|
|
||||||
QFileInfo infoAppPath(appPath);
|
|
||||||
if (infoAppPath.exists())
|
|
||||||
return appPath;
|
|
||||||
|
|
||||||
QString downloadPath = Download::globalInstance()->downloadLocalModelsPath() + modelFilename;
|
|
||||||
QFileInfo infoLocalPath(downloadPath);
|
|
||||||
if (infoLocalPath.exists())
|
|
||||||
return downloadPath;
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
class LLModelStore {
|
class LLModelStore {
|
||||||
public:
|
public:
|
||||||
static LLModelStore *globalInstance();
|
static LLModelStore *globalInstance();
|
||||||
@ -102,7 +79,6 @@ ChatLLM::ChatLLM(Chat *parent, bool isServer)
|
|||||||
connect(this, &ChatLLM::shouldBeLoadedChanged, this, &ChatLLM::handleShouldBeLoadedChanged,
|
connect(this, &ChatLLM::shouldBeLoadedChanged, this, &ChatLLM::handleShouldBeLoadedChanged,
|
||||||
Qt::QueuedConnection); // explicitly queued
|
Qt::QueuedConnection); // explicitly queued
|
||||||
connect(parent, &Chat::idChanged, this, &ChatLLM::handleChatIdChanged);
|
connect(parent, &Chat::idChanged, this, &ChatLLM::handleChatIdChanged);
|
||||||
connect(parent, &Chat::defaultModelChanged, this, &ChatLLM::handleDefaultModelChanged);
|
|
||||||
connect(&m_llmThread, &QThread::started, this, &ChatLLM::handleThreadStarted);
|
connect(&m_llmThread, &QThread::started, this, &ChatLLM::handleThreadStarted);
|
||||||
|
|
||||||
// The following are blocking operations and will block the llm thread
|
// The following are blocking operations and will block the llm thread
|
||||||
@ -121,8 +97,8 @@ ChatLLM::~ChatLLM()
|
|||||||
// The only time we should have a model loaded here is on shutdown
|
// The only time we should have a model loaded here is on shutdown
|
||||||
// as we explicitly unload the model in all other circumstances
|
// as we explicitly unload the model in all other circumstances
|
||||||
if (isModelLoaded()) {
|
if (isModelLoaded()) {
|
||||||
delete m_modelInfo.model;
|
delete m_llModelInfo.model;
|
||||||
m_modelInfo.model = nullptr;
|
m_llModelInfo.model = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,14 +111,15 @@ void ChatLLM::handleThreadStarted()
|
|||||||
|
|
||||||
bool ChatLLM::loadDefaultModel()
|
bool ChatLLM::loadDefaultModel()
|
||||||
{
|
{
|
||||||
if (m_defaultModel.isEmpty()) {
|
ModelInfo defaultModel = ModelList::globalInstance()->defaultModelInfo();
|
||||||
emit modelLoadingError(QString("Could not find default model to load"));
|
if (defaultModel.filename.isEmpty()) {
|
||||||
|
emit modelLoadingError(QString("Could not find any model to load"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return loadModel(m_defaultModel);
|
return loadModel(defaultModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatLLM::loadModel(const QString &modelName)
|
bool ChatLLM::loadModel(const ModelInfo &modelInfo)
|
||||||
{
|
{
|
||||||
// This is a complicated method because N different possible threads are interested in the outcome
|
// This is a complicated method because N different possible threads are interested in the outcome
|
||||||
// of this method. Why? Because we have a main/gui thread trying to monitor the state of N different
|
// of this method. Why? Because we have a main/gui thread trying to monitor the state of N different
|
||||||
@ -153,11 +130,11 @@ bool ChatLLM::loadModel(const QString &modelName)
|
|||||||
// to provide an overview of what we're doing here.
|
// to provide an overview of what we're doing here.
|
||||||
|
|
||||||
// We're already loaded with this model
|
// We're already loaded with this model
|
||||||
if (isModelLoaded() && this->modelName() == modelName)
|
if (isModelLoaded() && this->modelInfo() == modelInfo)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
bool isChatGPT = modelName.startsWith("chatgpt-");
|
bool isChatGPT = modelInfo.isChatGPT;
|
||||||
QString filePath = modelFilePath(modelName, isChatGPT);
|
QString filePath = modelInfo.dirpath + modelInfo.filename;
|
||||||
QFileInfo fileInfo(filePath);
|
QFileInfo fileInfo(filePath);
|
||||||
|
|
||||||
// We have a live model, but it isn't the one we want
|
// We have a live model, but it isn't the one we want
|
||||||
@ -165,36 +142,36 @@ bool ChatLLM::loadModel(const QString &modelName)
|
|||||||
if (alreadyAcquired) {
|
if (alreadyAcquired) {
|
||||||
resetContext();
|
resetContext();
|
||||||
#if defined(DEBUG_MODEL_LOADING)
|
#if defined(DEBUG_MODEL_LOADING)
|
||||||
qDebug() << "already acquired model deleted" << m_llmThread.objectName() << m_modelInfo.model;
|
qDebug() << "already acquired model deleted" << m_llmThread.objectName() << m_llModelInfo.model;
|
||||||
#endif
|
#endif
|
||||||
delete m_modelInfo.model;
|
delete m_llModelInfo.model;
|
||||||
m_modelInfo.model = nullptr;
|
m_llModelInfo.model = nullptr;
|
||||||
emit isModelLoadedChanged(false);
|
emit isModelLoadedChanged(false);
|
||||||
} else if (!m_isServer) {
|
} else if (!m_isServer) {
|
||||||
// This is a blocking call that tries to retrieve the model we need from the model store.
|
// This is a blocking call that tries to retrieve the model we need from the model store.
|
||||||
// If it succeeds, then we just have to restore state. If the store has never had a model
|
// If it succeeds, then we just have to restore state. If the store has never had a model
|
||||||
// returned to it, then the modelInfo.model pointer should be null which will happen on startup
|
// returned to it, then the modelInfo.model pointer should be null which will happen on startup
|
||||||
m_modelInfo = LLModelStore::globalInstance()->acquireModel();
|
m_llModelInfo = LLModelStore::globalInstance()->acquireModel();
|
||||||
#if defined(DEBUG_MODEL_LOADING)
|
#if defined(DEBUG_MODEL_LOADING)
|
||||||
qDebug() << "acquired model from store" << m_llmThread.objectName() << m_modelInfo.model;
|
qDebug() << "acquired model from store" << m_llmThread.objectName() << m_llModelInfo3.model;
|
||||||
#endif
|
#endif
|
||||||
// At this point it is possible that while we were blocked waiting to acquire the model from the
|
// At this point it is possible that while we were blocked waiting to acquire the model from the
|
||||||
// store, that our state was changed to not be loaded. If this is the case, release the model
|
// store, that our state was changed to not be loaded. If this is the case, release the model
|
||||||
// back into the store and quit loading
|
// back into the store and quit loading
|
||||||
if (!m_shouldBeLoaded) {
|
if (!m_shouldBeLoaded) {
|
||||||
#if defined(DEBUG_MODEL_LOADING)
|
#if defined(DEBUG_MODEL_LOADING)
|
||||||
qDebug() << "no longer need model" << m_llmThread.objectName() << m_modelInfo.model;
|
qDebug() << "no longer need model" << m_llmThread.objectName() << m_llModelInfo.model;
|
||||||
#endif
|
#endif
|
||||||
LLModelStore::globalInstance()->releaseModel(m_modelInfo);
|
LLModelStore::globalInstance()->releaseModel(m_llModelInfo);
|
||||||
m_modelInfo = LLModelInfo();
|
m_llModelInfo = LLModelInfo();
|
||||||
emit isModelLoadedChanged(false);
|
emit isModelLoadedChanged(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the store just gave us exactly the model we were looking for
|
// Check if the store just gave us exactly the model we were looking for
|
||||||
if (m_modelInfo.model && m_modelInfo.fileInfo == fileInfo) {
|
if (m_llModelInfo.model && m_llModelInfo.fileInfo == fileInfo) {
|
||||||
#if defined(DEBUG_MODEL_LOADING)
|
#if defined(DEBUG_MODEL_LOADING)
|
||||||
qDebug() << "store had our model" << m_llmThread.objectName() << m_modelInfo.model;
|
qDebug() << "store had our model" << m_llmThread.objectName() << m_llModelInfo.model;
|
||||||
#endif
|
#endif
|
||||||
restoreState();
|
restoreState();
|
||||||
emit isModelLoadedChanged(true);
|
emit isModelLoadedChanged(true);
|
||||||
@ -202,18 +179,18 @@ bool ChatLLM::loadModel(const QString &modelName)
|
|||||||
} else {
|
} else {
|
||||||
// Release the memory since we have to switch to a different model.
|
// Release the memory since we have to switch to a different model.
|
||||||
#if defined(DEBUG_MODEL_LOADING)
|
#if defined(DEBUG_MODEL_LOADING)
|
||||||
qDebug() << "deleting model" << m_llmThread.objectName() << m_modelInfo.model;
|
qDebug() << "deleting model" << m_llmThread.objectName() << m_llModelInfo.model;
|
||||||
#endif
|
#endif
|
||||||
delete m_modelInfo.model;
|
delete m_llModelInfo.model;
|
||||||
m_modelInfo.model = nullptr;
|
m_llModelInfo.model = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guarantee we've released the previous models memory
|
// Guarantee we've released the previous models memory
|
||||||
Q_ASSERT(!m_modelInfo.model);
|
Q_ASSERT(!m_llModelInfo.model);
|
||||||
|
|
||||||
// Store the file info in the modelInfo in case we have an error loading
|
// Store the file info in the modelInfo in case we have an error loading
|
||||||
m_modelInfo.fileInfo = fileInfo;
|
m_llModelInfo.fileInfo = fileInfo;
|
||||||
|
|
||||||
if (fileInfo.exists()) {
|
if (fileInfo.exists()) {
|
||||||
if (isChatGPT) {
|
if (isChatGPT) {
|
||||||
@ -226,46 +203,46 @@ bool ChatLLM::loadModel(const QString &modelName)
|
|||||||
apiKey = stream.readAll();
|
apiKey = stream.readAll();
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
m_modelType = LLModelType::CHATGPT_;
|
m_llModelType = LLModelType::CHATGPT_;
|
||||||
ChatGPT *model = new ChatGPT();
|
ChatGPT *model = new ChatGPT();
|
||||||
model->setModelName(chatGPTModel);
|
model->setModelName(chatGPTModel);
|
||||||
model->setAPIKey(apiKey);
|
model->setAPIKey(apiKey);
|
||||||
m_modelInfo.model = model;
|
m_llModelInfo.model = model;
|
||||||
} else {
|
} else {
|
||||||
m_modelInfo.model = LLModel::construct(filePath.toStdString());
|
m_llModelInfo.model = LLModel::construct(filePath.toStdString());
|
||||||
if (m_modelInfo.model) {
|
if (m_llModelInfo.model) {
|
||||||
bool success = m_modelInfo.model->loadModel(filePath.toStdString());
|
bool success = m_llModelInfo.model->loadModel(filePath.toStdString());
|
||||||
if (!success) {
|
if (!success) {
|
||||||
delete std::exchange(m_modelInfo.model, nullptr);
|
delete std::exchange(m_llModelInfo.model, nullptr);
|
||||||
if (!m_isServer)
|
if (!m_isServer)
|
||||||
LLModelStore::globalInstance()->releaseModel(m_modelInfo); // release back into the store
|
LLModelStore::globalInstance()->releaseModel(m_llModelInfo); // release back into the store
|
||||||
m_modelInfo = LLModelInfo();
|
m_llModelInfo = LLModelInfo();
|
||||||
emit modelLoadingError(QString("Could not load model due to invalid model file for %1").arg(modelName));
|
emit modelLoadingError(QString("Could not load model due to invalid model file for %1").arg(modelInfo.filename));
|
||||||
} else {
|
} else {
|
||||||
switch (m_modelInfo.model->implementation().modelType[0]) {
|
switch (m_llModelInfo.model->implementation().modelType[0]) {
|
||||||
case 'L': m_modelType = LLModelType::LLAMA_; break;
|
case 'L': m_llModelType = LLModelType::LLAMA_; break;
|
||||||
case 'G': m_modelType = LLModelType::GPTJ_; break;
|
case 'G': m_llModelType = LLModelType::GPTJ_; break;
|
||||||
case 'M': m_modelType = LLModelType::MPT_; break;
|
case 'M': m_llModelType = LLModelType::MPT_; break;
|
||||||
case 'R': m_modelType = LLModelType::REPLIT_; break;
|
case 'R': m_llModelType = LLModelType::REPLIT_; break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
delete std::exchange(m_modelInfo.model, nullptr);
|
delete std::exchange(m_llModelInfo.model, nullptr);
|
||||||
if (!m_isServer)
|
if (!m_isServer)
|
||||||
LLModelStore::globalInstance()->releaseModel(m_modelInfo); // release back into the store
|
LLModelStore::globalInstance()->releaseModel(m_llModelInfo); // release back into the store
|
||||||
m_modelInfo = LLModelInfo();
|
m_llModelInfo = LLModelInfo();
|
||||||
emit modelLoadingError(QString("Could not determine model type for %1").arg(modelName));
|
emit modelLoadingError(QString("Could not determine model type for %1").arg(modelInfo.filename));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!m_isServer)
|
if (!m_isServer)
|
||||||
LLModelStore::globalInstance()->releaseModel(m_modelInfo); // release back into the store
|
LLModelStore::globalInstance()->releaseModel(m_llModelInfo); // release back into the store
|
||||||
m_modelInfo = LLModelInfo();
|
m_llModelInfo = LLModelInfo();
|
||||||
emit modelLoadingError(QString("Could not load model due to invalid format for %1").arg(modelName));
|
emit modelLoadingError(QString("Could not load model due to invalid format for %1").arg(modelInfo.filename));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if defined(DEBUG_MODEL_LOADING)
|
#if defined(DEBUG_MODEL_LOADING)
|
||||||
qDebug() << "new model" << m_llmThread.objectName() << m_modelInfo.model;
|
qDebug() << "new model" << m_llmThread.objectName() << m_llModelInfo.model;
|
||||||
#endif
|
#endif
|
||||||
restoreState();
|
restoreState();
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
@ -282,31 +259,27 @@ bool ChatLLM::loadModel(const QString &modelName)
|
|||||||
emit sendModelLoaded();
|
emit sendModelLoaded();
|
||||||
} else {
|
} else {
|
||||||
if (!m_isServer)
|
if (!m_isServer)
|
||||||
LLModelStore::globalInstance()->releaseModel(m_modelInfo); // release back into the store
|
LLModelStore::globalInstance()->releaseModel(m_llModelInfo); // release back into the store
|
||||||
m_modelInfo = LLModelInfo();
|
m_llModelInfo = LLModelInfo();
|
||||||
emit modelLoadingError(QString("Could not find file for model %1").arg(modelName));
|
emit modelLoadingError(QString("Could not find file for model %1").arg(modelInfo.filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_modelInfo.model) {
|
if (m_llModelInfo.model)
|
||||||
QString basename = fileInfo.completeBaseName();
|
setModelInfo(modelInfo);
|
||||||
if (basename.startsWith("ggml-")) // remove the ggml- prefix
|
|
||||||
basename.remove(0, 5);
|
|
||||||
setModelName(basename);
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_modelInfo.model;
|
return m_llModelInfo.model;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatLLM::isModelLoaded() const
|
bool ChatLLM::isModelLoaded() const
|
||||||
{
|
{
|
||||||
return m_modelInfo.model && m_modelInfo.model->isModelLoaded();
|
return m_llModelInfo.model && m_llModelInfo.model->isModelLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatLLM::regenerateResponse()
|
void ChatLLM::regenerateResponse()
|
||||||
{
|
{
|
||||||
// ChatGPT uses a different semantic meaning for n_past than local models. For ChatGPT, the meaning
|
// ChatGPT uses a different semantic meaning for n_past than local models. For ChatGPT, the meaning
|
||||||
// of n_past is of the number of prompt/response pairs, rather than for total tokens.
|
// of n_past is of the number of prompt/response pairs, rather than for total tokens.
|
||||||
if (m_modelType == LLModelType::CHATGPT_)
|
if (m_llModelType == LLModelType::CHATGPT_)
|
||||||
m_ctx.n_past -= 1;
|
m_ctx.n_past -= 1;
|
||||||
else
|
else
|
||||||
m_ctx.n_past -= m_promptResponseTokens;
|
m_ctx.n_past -= m_promptResponseTokens;
|
||||||
@ -357,20 +330,20 @@ QString ChatLLM::response() const
|
|||||||
return QString::fromStdString(remove_leading_whitespace(m_response));
|
return QString::fromStdString(remove_leading_whitespace(m_response));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ChatLLM::modelName() const
|
ModelInfo ChatLLM::modelInfo() const
|
||||||
{
|
{
|
||||||
return m_modelName;
|
return m_modelInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatLLM::setModelName(const QString &modelName)
|
void ChatLLM::setModelInfo(const ModelInfo &modelInfo)
|
||||||
{
|
{
|
||||||
m_modelName = modelName;
|
m_modelInfo = modelInfo;
|
||||||
emit modelNameChanged();
|
emit modelInfoChanged(modelInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatLLM::modelNameChangeRequested(const QString &modelName)
|
void ChatLLM::modelChangeRequested(const ModelInfo &modelInfo)
|
||||||
{
|
{
|
||||||
loadModel(modelName);
|
loadModel(modelInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatLLM::handlePrompt(int32_t token)
|
bool ChatLLM::handlePrompt(int32_t token)
|
||||||
@ -454,13 +427,13 @@ bool ChatLLM::prompt(const QList<QString> &collectionList, const QString &prompt
|
|||||||
m_ctx.n_batch = n_batch;
|
m_ctx.n_batch = n_batch;
|
||||||
m_ctx.repeat_penalty = repeat_penalty;
|
m_ctx.repeat_penalty = repeat_penalty;
|
||||||
m_ctx.repeat_last_n = repeat_penalty_tokens;
|
m_ctx.repeat_last_n = repeat_penalty_tokens;
|
||||||
m_modelInfo.model->setThreadCount(n_threads);
|
m_llModelInfo.model->setThreadCount(n_threads);
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
printf("%s", qPrintable(instructPrompt));
|
printf("%s", qPrintable(instructPrompt));
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
m_timer->start();
|
m_timer->start();
|
||||||
m_modelInfo.model->prompt(instructPrompt.toStdString(), promptFunc, responseFunc, recalcFunc, m_ctx);
|
m_llModelInfo.model->prompt(instructPrompt.toStdString(), promptFunc, responseFunc, recalcFunc, m_ctx);
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@ -478,7 +451,7 @@ bool ChatLLM::prompt(const QList<QString> &collectionList, const QString &prompt
|
|||||||
void ChatLLM::setShouldBeLoaded(bool b)
|
void ChatLLM::setShouldBeLoaded(bool b)
|
||||||
{
|
{
|
||||||
#if defined(DEBUG_MODEL_LOADING)
|
#if defined(DEBUG_MODEL_LOADING)
|
||||||
qDebug() << "setShouldBeLoaded" << m_llmThread.objectName() << b << m_modelInfo.model;
|
qDebug() << "setShouldBeLoaded" << m_llmThread.objectName() << b << m_llModelInfo.model;
|
||||||
#endif
|
#endif
|
||||||
m_shouldBeLoaded = b; // atomic
|
m_shouldBeLoaded = b; // atomic
|
||||||
emit shouldBeLoadedChanged();
|
emit shouldBeLoadedChanged();
|
||||||
@ -505,10 +478,10 @@ void ChatLLM::unloadModel()
|
|||||||
|
|
||||||
saveState();
|
saveState();
|
||||||
#if defined(DEBUG_MODEL_LOADING)
|
#if defined(DEBUG_MODEL_LOADING)
|
||||||
qDebug() << "unloadModel" << m_llmThread.objectName() << m_modelInfo.model;
|
qDebug() << "unloadModel" << m_llmThread.objectName() << m_llModelInfo.model;
|
||||||
#endif
|
#endif
|
||||||
LLModelStore::globalInstance()->releaseModel(m_modelInfo);
|
LLModelStore::globalInstance()->releaseModel(m_llModelInfo);
|
||||||
m_modelInfo = LLModelInfo();
|
m_llModelInfo = LLModelInfo();
|
||||||
emit isModelLoadedChanged(false);
|
emit isModelLoadedChanged(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,10 +491,10 @@ void ChatLLM::reloadModel()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
#if defined(DEBUG_MODEL_LOADING)
|
#if defined(DEBUG_MODEL_LOADING)
|
||||||
qDebug() << "reloadModel" << m_llmThread.objectName() << m_modelInfo.model;
|
qDebug() << "reloadModel" << m_llmThread.objectName() << m_llModelInfo.model;
|
||||||
#endif
|
#endif
|
||||||
const QString m = modelName();
|
const ModelInfo m = modelInfo();
|
||||||
if (m.isEmpty())
|
if (m.name.isEmpty())
|
||||||
loadDefaultModel();
|
loadDefaultModel();
|
||||||
else
|
else
|
||||||
loadModel(m);
|
loadModel(m);
|
||||||
@ -545,7 +518,7 @@ void ChatLLM::generateName()
|
|||||||
printf("%s", qPrintable(instructPrompt));
|
printf("%s", qPrintable(instructPrompt));
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
m_modelInfo.model->prompt(instructPrompt.toStdString(), promptFunc, responseFunc, recalcFunc, ctx);
|
m_llModelInfo.model->prompt(instructPrompt.toStdString(), promptFunc, responseFunc, recalcFunc, ctx);
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@ -562,11 +535,6 @@ void ChatLLM::handleChatIdChanged(const QString &id)
|
|||||||
m_llmThread.setObjectName(id);
|
m_llmThread.setObjectName(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatLLM::handleDefaultModelChanged(const QString &defaultModel)
|
|
||||||
{
|
|
||||||
m_defaultModel = defaultModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ChatLLM::handleNamePrompt(int32_t token)
|
bool ChatLLM::handleNamePrompt(int32_t token)
|
||||||
{
|
{
|
||||||
Q_UNUSED(token);
|
Q_UNUSED(token);
|
||||||
@ -595,8 +563,8 @@ bool ChatLLM::handleNameRecalculate(bool isRecalc)
|
|||||||
bool ChatLLM::serialize(QDataStream &stream, int version)
|
bool ChatLLM::serialize(QDataStream &stream, int version)
|
||||||
{
|
{
|
||||||
if (version > 1) {
|
if (version > 1) {
|
||||||
stream << m_modelType;
|
stream << m_llModelType;
|
||||||
switch (m_modelType) {
|
switch (m_llModelType) {
|
||||||
case REPLIT_: stream << REPLIT_INTERNAL_STATE_VERSION; break;
|
case REPLIT_: stream << REPLIT_INTERNAL_STATE_VERSION; break;
|
||||||
case MPT_: stream << MPT_INTERNAL_STATE_VERSION; break;
|
case MPT_: stream << MPT_INTERNAL_STATE_VERSION; break;
|
||||||
case GPTJ_: stream << GPTJ_INTERNAL_STATE_VERSION; break;
|
case GPTJ_: stream << GPTJ_INTERNAL_STATE_VERSION; break;
|
||||||
@ -629,7 +597,7 @@ bool ChatLLM::deserialize(QDataStream &stream, int version)
|
|||||||
{
|
{
|
||||||
if (version > 1) {
|
if (version > 1) {
|
||||||
int internalStateVersion;
|
int internalStateVersion;
|
||||||
stream >> m_modelType;
|
stream >> m_llModelType;
|
||||||
stream >> internalStateVersion; // for future use
|
stream >> internalStateVersion; // for future use
|
||||||
}
|
}
|
||||||
QString response;
|
QString response;
|
||||||
@ -670,21 +638,21 @@ void ChatLLM::saveState()
|
|||||||
if (!isModelLoaded())
|
if (!isModelLoaded())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_modelType == LLModelType::CHATGPT_) {
|
if (m_llModelType == LLModelType::CHATGPT_) {
|
||||||
m_state.clear();
|
m_state.clear();
|
||||||
QDataStream stream(&m_state, QIODeviceBase::WriteOnly);
|
QDataStream stream(&m_state, QIODeviceBase::WriteOnly);
|
||||||
stream.setVersion(QDataStream::Qt_6_5);
|
stream.setVersion(QDataStream::Qt_6_5);
|
||||||
ChatGPT *chatGPT = static_cast<ChatGPT*>(m_modelInfo.model);
|
ChatGPT *chatGPT = static_cast<ChatGPT*>(m_llModelInfo.model);
|
||||||
stream << chatGPT->context();
|
stream << chatGPT->context();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t stateSize = m_modelInfo.model->stateSize();
|
const size_t stateSize = m_llModelInfo.model->stateSize();
|
||||||
m_state.resize(stateSize);
|
m_state.resize(stateSize);
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
qDebug() << "saveState" << m_llmThread.objectName() << "size:" << m_state.size();
|
qDebug() << "saveState" << m_llmThread.objectName() << "size:" << m_state.size();
|
||||||
#endif
|
#endif
|
||||||
m_modelInfo.model->saveState(static_cast<uint8_t*>(reinterpret_cast<void*>(m_state.data())));
|
m_llModelInfo.model->saveState(static_cast<uint8_t*>(reinterpret_cast<void*>(m_state.data())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatLLM::restoreState()
|
void ChatLLM::restoreState()
|
||||||
@ -692,10 +660,10 @@ void ChatLLM::restoreState()
|
|||||||
if (!isModelLoaded() || m_state.isEmpty())
|
if (!isModelLoaded() || m_state.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_modelType == LLModelType::CHATGPT_) {
|
if (m_llModelType == LLModelType::CHATGPT_) {
|
||||||
QDataStream stream(&m_state, QIODeviceBase::ReadOnly);
|
QDataStream stream(&m_state, QIODeviceBase::ReadOnly);
|
||||||
stream.setVersion(QDataStream::Qt_6_5);
|
stream.setVersion(QDataStream::Qt_6_5);
|
||||||
ChatGPT *chatGPT = static_cast<ChatGPT*>(m_modelInfo.model);
|
ChatGPT *chatGPT = static_cast<ChatGPT*>(m_llModelInfo.model);
|
||||||
QList<QString> context;
|
QList<QString> context;
|
||||||
stream >> context;
|
stream >> context;
|
||||||
chatGPT->setContext(context);
|
chatGPT->setContext(context);
|
||||||
@ -707,7 +675,7 @@ void ChatLLM::restoreState()
|
|||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
qDebug() << "restoreState" << m_llmThread.objectName() << "size:" << m_state.size();
|
qDebug() << "restoreState" << m_llmThread.objectName() << "size:" << m_state.size();
|
||||||
#endif
|
#endif
|
||||||
m_modelInfo.model->restoreState(static_cast<const uint8_t*>(reinterpret_cast<void*>(m_state.data())));
|
m_llModelInfo.model->restoreState(static_cast<const uint8_t*>(reinterpret_cast<void*>(m_state.data())));
|
||||||
m_state.clear();
|
m_state.clear();
|
||||||
m_state.resize(0);
|
m_state.resize(0);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
|
||||||
#include "localdocs.h"
|
#include "localdocs.h"
|
||||||
|
#include "modellist.h"
|
||||||
#include "../gpt4all-backend/llmodel.h"
|
#include "../gpt4all-backend/llmodel.h"
|
||||||
|
|
||||||
enum LLModelType {
|
enum LLModelType {
|
||||||
@ -67,12 +68,7 @@ class Chat;
|
|||||||
class ChatLLM : public QObject
|
class ChatLLM : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool isModelLoaded READ isModelLoaded NOTIFY isModelLoadedChanged)
|
|
||||||
Q_PROPERTY(QString response READ response NOTIFY responseChanged)
|
|
||||||
Q_PROPERTY(QString modelName READ modelName WRITE setModelName NOTIFY modelNameChanged)
|
|
||||||
Q_PROPERTY(bool isRecalc READ isRecalc NOTIFY recalcChanged)
|
Q_PROPERTY(bool isRecalc READ isRecalc NOTIFY recalcChanged)
|
||||||
Q_PROPERTY(QString generatedName READ generatedName NOTIFY generatedNameChanged)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ChatLLM(Chat *parent, bool isServer = false);
|
ChatLLM(Chat *parent, bool isServer = false);
|
||||||
virtual ~ChatLLM();
|
virtual ~ChatLLM();
|
||||||
@ -88,9 +84,9 @@ public:
|
|||||||
void setShouldBeLoaded(bool b);
|
void setShouldBeLoaded(bool b);
|
||||||
|
|
||||||
QString response() const;
|
QString response() const;
|
||||||
QString modelName() const;
|
|
||||||
|
|
||||||
void setModelName(const QString &modelName);
|
ModelInfo modelInfo() const;
|
||||||
|
void setModelInfo(const ModelInfo &info);
|
||||||
|
|
||||||
bool isRecalc() const { return m_isRecalc; }
|
bool isRecalc() const { return m_isRecalc; }
|
||||||
|
|
||||||
@ -104,25 +100,23 @@ public Q_SLOTS:
|
|||||||
int32_t n_predict, int32_t top_k, float top_p, float temp, int32_t n_batch, float repeat_penalty,
|
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);
|
int32_t repeat_penalty_tokens, int32_t n_threads);
|
||||||
bool loadDefaultModel();
|
bool loadDefaultModel();
|
||||||
bool loadModel(const QString &modelName);
|
bool loadModel(const ModelInfo &modelInfo);
|
||||||
void modelNameChangeRequested(const QString &modelName);
|
void modelChangeRequested(const ModelInfo &modelInfo);
|
||||||
void forceUnloadModel();
|
void forceUnloadModel();
|
||||||
void unloadModel();
|
void unloadModel();
|
||||||
void reloadModel();
|
void reloadModel();
|
||||||
void generateName();
|
void generateName();
|
||||||
void handleChatIdChanged(const QString &id);
|
void handleChatIdChanged(const QString &id);
|
||||||
void handleDefaultModelChanged(const QString &defaultModel);
|
|
||||||
void handleShouldBeLoadedChanged();
|
void handleShouldBeLoadedChanged();
|
||||||
void handleThreadStarted();
|
void handleThreadStarted();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
void recalcChanged();
|
||||||
void isModelLoadedChanged(bool);
|
void isModelLoadedChanged(bool);
|
||||||
void modelLoadingError(const QString &error);
|
void modelLoadingError(const QString &error);
|
||||||
void responseChanged(const QString &response);
|
void responseChanged(const QString &response);
|
||||||
void promptProcessing();
|
void promptProcessing();
|
||||||
void responseStopped();
|
void responseStopped();
|
||||||
void modelNameChanged();
|
|
||||||
void recalcChanged();
|
|
||||||
void sendStartup();
|
void sendStartup();
|
||||||
void sendModelLoaded();
|
void sendModelLoaded();
|
||||||
void generatedNameChanged(const QString &name);
|
void generatedNameChanged(const QString &name);
|
||||||
@ -132,6 +126,7 @@ Q_SIGNALS:
|
|||||||
void requestRetrieveFromDB(const QList<QString> &collections, const QString &text, int retrievalSize, QList<ResultInfo> *results);
|
void requestRetrieveFromDB(const QList<QString> &collections, const QString &text, int retrievalSize, QList<ResultInfo> *results);
|
||||||
void reportSpeed(const QString &speed);
|
void reportSpeed(const QString &speed);
|
||||||
void databaseResultsChanged(const QList<ResultInfo>&);
|
void databaseResultsChanged(const QList<ResultInfo>&);
|
||||||
|
void modelInfoChanged(const ModelInfo &modelInfo);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool handlePrompt(int32_t token);
|
bool handlePrompt(int32_t token);
|
||||||
@ -151,10 +146,9 @@ protected:
|
|||||||
private:
|
private:
|
||||||
std::string m_response;
|
std::string m_response;
|
||||||
std::string m_nameResponse;
|
std::string m_nameResponse;
|
||||||
LLModelInfo m_modelInfo;
|
LLModelInfo m_llModelInfo;
|
||||||
LLModelType m_modelType;
|
LLModelType m_llModelType;
|
||||||
QString m_modelName;
|
ModelInfo m_modelInfo;
|
||||||
QString m_defaultModel;
|
|
||||||
TokenTimer *m_timer;
|
TokenTimer *m_timer;
|
||||||
QByteArray m_state;
|
QByteArray m_state;
|
||||||
QThread m_llmThread;
|
QThread m_llmThread;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "database.h"
|
#include "database.h"
|
||||||
#include "download.h"
|
#include "modellist.h"
|
||||||
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QPdfDocument>
|
#include <QPdfDocument>
|
||||||
@ -415,7 +415,7 @@ bool selectDocuments(QSqlQuery &q, int folder_id, QList<int> *documentIds) {
|
|||||||
|
|
||||||
QSqlError initDb()
|
QSqlError initDb()
|
||||||
{
|
{
|
||||||
QString dbPath = Download::globalInstance()->downloadLocalModelsPath()
|
QString dbPath = ModelList::globalInstance()->localModelsPath()
|
||||||
+ QString("localdocs_v%1.db").arg(LOCALDOCS_VERSION);
|
+ QString("localdocs_v%1.db").arg(LOCALDOCS_VERSION);
|
||||||
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
|
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
|
||||||
db.setDatabaseName(dbPath);
|
db.setDatabaseName(dbPath);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "download.h"
|
#include "download.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
|
#include "modellist.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
@ -29,13 +30,9 @@ Download::Download()
|
|||||||
&Download::handleHashAndSaveFinished, Qt::QueuedConnection);
|
&Download::handleHashAndSaveFinished, Qt::QueuedConnection);
|
||||||
connect(&m_networkManager, &QNetworkAccessManager::sslErrors, this,
|
connect(&m_networkManager, &QNetworkAccessManager::sslErrors, this,
|
||||||
&Download::handleSslErrors);
|
&Download::handleSslErrors);
|
||||||
connect(this, &Download::downloadLocalModelsPathChanged, this, &Download::updateModelList);
|
connect(ModelList::globalInstance(), &ModelList::localModelsPathChanged, this, &Download::updateModelList);
|
||||||
updateModelList();
|
updateModelList();
|
||||||
updateReleaseNotes();
|
updateReleaseNotes();
|
||||||
QSettings settings;
|
|
||||||
settings.sync();
|
|
||||||
m_downloadLocalModelsPath = settings.value("modelPath",
|
|
||||||
defaultLocalModelsPath()).toString();
|
|
||||||
m_startTime = QDateTime::currentDateTime();
|
m_startTime = QDateTime::currentDateTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,47 +62,6 @@ bool compareVersions(const QString &a, const QString &b) {
|
|||||||
return aParts.size() > bParts.size();
|
return aParts.size() > bParts.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ModelInfo> Download::modelList() const
|
|
||||||
{
|
|
||||||
// We make sure the default model is listed first
|
|
||||||
QList<ModelInfo> values = m_modelMap.values();
|
|
||||||
ModelInfo defaultInfo;
|
|
||||||
ModelInfo bestGPTJInfo;
|
|
||||||
ModelInfo bestLlamaInfo;
|
|
||||||
ModelInfo bestMPTInfo;
|
|
||||||
QList<ModelInfo> filtered;
|
|
||||||
for (const ModelInfo &v : values) {
|
|
||||||
if (v.isDefault)
|
|
||||||
defaultInfo = v;
|
|
||||||
if (v.bestGPTJ)
|
|
||||||
bestGPTJInfo = v;
|
|
||||||
if (v.bestLlama)
|
|
||||||
bestLlamaInfo = v;
|
|
||||||
if (v.bestMPT)
|
|
||||||
bestMPTInfo = v;
|
|
||||||
filtered.append(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_ASSERT(defaultInfo == bestGPTJInfo || defaultInfo == bestLlamaInfo || defaultInfo == bestMPTInfo);
|
|
||||||
|
|
||||||
if (bestLlamaInfo.bestLlama) {
|
|
||||||
filtered.removeAll(bestLlamaInfo);
|
|
||||||
filtered.prepend(bestLlamaInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestGPTJInfo.bestGPTJ) {
|
|
||||||
filtered.removeAll(bestGPTJInfo);
|
|
||||||
filtered.prepend(bestGPTJInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestMPTInfo.bestMPT) {
|
|
||||||
filtered.removeAll(bestMPTInfo);
|
|
||||||
filtered.prepend(bestMPTInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseInfo Download::releaseInfo() const
|
ReleaseInfo Download::releaseInfo() const
|
||||||
{
|
{
|
||||||
const QString currentVersion = QCoreApplication::applicationVersion();
|
const QString currentVersion = QCoreApplication::applicationVersion();
|
||||||
@ -124,20 +80,6 @@ bool Download::hasNewerRelease() const
|
|||||||
return compareVersions(versions.first(), currentVersion);
|
return compareVersions(versions.first(), currentVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Download::downloadLocalModelsPath() const {
|
|
||||||
return m_downloadLocalModelsPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Download::setDownloadLocalModelsPath(const QString &modelPath) {
|
|
||||||
QString filePath = (modelPath.startsWith("file://") ?
|
|
||||||
QUrl(modelPath).toLocalFile() : modelPath);
|
|
||||||
QString canonical = QFileInfo(filePath).canonicalFilePath() + "/";
|
|
||||||
if (m_downloadLocalModelsPath != canonical) {
|
|
||||||
m_downloadLocalModelsPath = canonical;
|
|
||||||
emit downloadLocalModelsPathChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Download::isFirstStart() const
|
bool Download::isFirstStart() const
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
@ -149,42 +91,10 @@ bool Download::isFirstStart() const
|
|||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Download::incompleteDownloadPath(const QString &modelFile) {
|
|
||||||
QString downloadPath = downloadLocalModelsPath() + "incomplete-" +
|
|
||||||
modelFile;
|
|
||||||
return downloadPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Download::defaultLocalModelsPath() const
|
|
||||||
{
|
|
||||||
QString localPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)
|
|
||||||
+ "/";
|
|
||||||
QString testWritePath = localPath + QString("test_write.txt");
|
|
||||||
QString canonicalLocalPath = QFileInfo(localPath).canonicalFilePath() + "/";
|
|
||||||
QDir localDir(localPath);
|
|
||||||
if (!localDir.exists()) {
|
|
||||||
if (!localDir.mkpath(localPath)) {
|
|
||||||
qWarning() << "ERROR: Local download directory can't be created:" << canonicalLocalPath;
|
|
||||||
return canonicalLocalPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (QFileInfo::exists(testWritePath))
|
|
||||||
return canonicalLocalPath;
|
|
||||||
|
|
||||||
QFile testWriteFile(testWritePath);
|
|
||||||
if (testWriteFile.open(QIODeviceBase::ReadWrite)) {
|
|
||||||
testWriteFile.close();
|
|
||||||
return canonicalLocalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
qWarning() << "ERROR: Local download path appears not writeable:" << canonicalLocalPath;
|
|
||||||
return canonicalLocalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Download::updateModelList()
|
void Download::updateModelList()
|
||||||
{
|
{
|
||||||
QUrl jsonUrl("http://gpt4all.io/models/models.json");
|
// QUrl jsonUrl("http://gpt4all.io/models/models.json");
|
||||||
|
QUrl jsonUrl("file:///home/atreat/dev/large_language_models/gpt4all/gpt4all-chat/metadata/models.json");
|
||||||
QNetworkRequest request(jsonUrl);
|
QNetworkRequest request(jsonUrl);
|
||||||
QSslConfiguration conf = request.sslConfiguration();
|
QSslConfiguration conf = request.sslConfiguration();
|
||||||
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
|
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||||
@ -206,34 +116,40 @@ void Download::updateReleaseNotes()
|
|||||||
|
|
||||||
void Download::downloadModel(const QString &modelFile)
|
void Download::downloadModel(const QString &modelFile)
|
||||||
{
|
{
|
||||||
QFile *tempFile = new QFile(incompleteDownloadPath(modelFile));
|
QFile *tempFile = new QFile(ModelList::globalInstance()->incompleteDownloadPath(modelFile));
|
||||||
QDateTime modTime = tempFile->fileTime(QFile::FileModificationTime);
|
QDateTime modTime = tempFile->fileTime(QFile::FileModificationTime);
|
||||||
bool success = tempFile->open(QIODevice::WriteOnly | QIODevice::Append);
|
bool success = tempFile->open(QIODevice::WriteOnly | QIODevice::Append);
|
||||||
qWarning() << "Opening temp file for writing:" << tempFile->fileName();
|
qWarning() << "Opening temp file for writing:" << tempFile->fileName();
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qWarning() << "ERROR: Could not open temp file:"
|
const QString error
|
||||||
<< tempFile->fileName() << modelFile;
|
= QString("ERROR: Could not open temp file: %1 %2").arg(tempFile->fileName()).arg(modelFile);
|
||||||
|
qWarning() << error;
|
||||||
|
ModelList::globalInstance()->updateData(modelFile, ModelList::DownloadErrorRole, error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
tempFile->flush();
|
||||||
size_t incomplete_size = tempFile->size();
|
size_t incomplete_size = tempFile->size();
|
||||||
if (incomplete_size > 0) {
|
if (incomplete_size > 0) {
|
||||||
if (modTime < m_startTime) {
|
bool success = tempFile->seek(incomplete_size);
|
||||||
qWarning() << "File last modified before app started, rewinding by 1MB";
|
if (!success) {
|
||||||
if (incomplete_size >= 1024 * 1024) {
|
|
||||||
incomplete_size -= 1024 * 1024;
|
|
||||||
} else {
|
|
||||||
incomplete_size = 0;
|
incomplete_size = 0;
|
||||||
|
success = tempFile->seek(incomplete_size);
|
||||||
|
Q_ASSERT(success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tempFile->seek(incomplete_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelInfo info = m_modelMap.value(modelFile);
|
if (!ModelList::globalInstance()->contains(modelFile)) {
|
||||||
|
qWarning() << "ERROR: Could not find file:" << modelFile;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelList::globalInstance()->updateData(modelFile, ModelList::DownloadingRole, true);
|
||||||
|
ModelInfo info = ModelList::globalInstance()->modelInfo(modelFile);
|
||||||
QString url = !info.url.isEmpty() ? info.url : "http://gpt4all.io/models/" + modelFile;
|
QString url = !info.url.isEmpty() ? info.url : "http://gpt4all.io/models/" + modelFile;
|
||||||
Network::globalInstance()->sendDownloadStarted(modelFile);
|
Network::globalInstance()->sendDownloadStarted(modelFile);
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
request.setAttribute(QNetworkRequest::User, modelFile);
|
request.setAttribute(QNetworkRequest::User, modelFile);
|
||||||
request.setRawHeader("range", QString("bytes=%1-").arg(incomplete_size).toUtf8());
|
request.setRawHeader("range", QString("bytes=%1-").arg(tempFile->pos()).toUtf8());
|
||||||
QSslConfiguration conf = request.sslConfiguration();
|
QSslConfiguration conf = request.sslConfiguration();
|
||||||
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
|
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||||
request.setSslConfiguration(conf);
|
request.setSslConfiguration(conf);
|
||||||
@ -263,8 +179,7 @@ void Download::cancelDownload(const QString &modelFile)
|
|||||||
tempFile->deleteLater();
|
tempFile->deleteLater();
|
||||||
m_activeDownloads.remove(modelReply);
|
m_activeDownloads.remove(modelReply);
|
||||||
|
|
||||||
// Emit downloadFinished signal for cleanup
|
ModelList::globalInstance()->updateData(modelFile, ModelList::DownloadingRole, false);
|
||||||
emit downloadFinished(modelFile);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,37 +192,34 @@ void Download::installModel(const QString &modelFile, const QString &apiKey)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Network::globalInstance()->sendInstallModel(modelFile);
|
Network::globalInstance()->sendInstallModel(modelFile);
|
||||||
QString filePath = downloadLocalModelsPath() + modelFile + ".txt";
|
QString filePath = ModelList::globalInstance()->localModelsPath() + modelFile + ".txt";
|
||||||
QFile file(filePath);
|
QFile file(filePath);
|
||||||
if (file.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text)) {
|
if (file.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text)) {
|
||||||
QTextStream stream(&file);
|
QTextStream stream(&file);
|
||||||
stream << apiKey;
|
stream << apiKey;
|
||||||
file.close();
|
file.close();
|
||||||
ModelInfo info = m_modelMap.value(modelFile);
|
|
||||||
info.installed = true;
|
|
||||||
m_modelMap.insert(modelFile, info);
|
|
||||||
emit modelListChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Download::removeModel(const QString &modelFile)
|
void Download::removeModel(const QString &modelFile)
|
||||||
{
|
{
|
||||||
const bool isChatGPT = modelFile.startsWith("chatgpt-");
|
const QString filePath = ModelList::globalInstance()->localModelsPath() + modelFile;
|
||||||
const QString filePath = downloadLocalModelsPath()
|
QFile incompleteFile(ModelList::globalInstance()->incompleteDownloadPath(modelFile));
|
||||||
+ modelFile
|
if (incompleteFile.exists()) {
|
||||||
+ (isChatGPT ? ".txt" : QString());
|
incompleteFile.remove();
|
||||||
QFile file(filePath);
|
|
||||||
if (!file.exists()) {
|
|
||||||
qWarning() << "ERROR: Cannot remove file that does not exist" << filePath;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QFile file(filePath);
|
||||||
|
if (file.exists()) {
|
||||||
Network::globalInstance()->sendRemoveModel(modelFile);
|
Network::globalInstance()->sendRemoveModel(modelFile);
|
||||||
ModelInfo info = m_modelMap.value(modelFile);
|
|
||||||
info.installed = false;
|
|
||||||
m_modelMap.insert(modelFile, info);
|
|
||||||
file.remove();
|
file.remove();
|
||||||
emit modelListChanged();
|
}
|
||||||
|
|
||||||
|
ModelList::globalInstance()->updateData(modelFile, ModelList::BytesReceivedRole, 0);
|
||||||
|
ModelList::globalInstance()->updateData(modelFile, ModelList::BytesTotalRole, 0);
|
||||||
|
ModelList::globalInstance()->updateData(modelFile, ModelList::TimestampRole, 0);
|
||||||
|
ModelList::globalInstance()->updateData(modelFile, ModelList::SpeedRole, QString());
|
||||||
|
ModelList::globalInstance()->updateData(modelFile, ModelList::DownloadErrorRole, QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Download::handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
void Download::handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
||||||
@ -319,37 +231,12 @@ void Download::handleSslErrors(QNetworkReply *reply, const QList<QSslError> &err
|
|||||||
|
|
||||||
void Download::handleModelsJsonDownloadFinished()
|
void Download::handleModelsJsonDownloadFinished()
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
QByteArray jsonData = QString(""
|
|
||||||
"["
|
|
||||||
" {"
|
|
||||||
" \"md5sum\": \"61d48a82cb188cceb14ebb8082bfec37\","
|
|
||||||
" \"filename\": \"ggml-gpt4all-j-v1.1-breezy.bin\","
|
|
||||||
" \"filesize\": \"3785248281\""
|
|
||||||
" },"
|
|
||||||
" {"
|
|
||||||
" \"md5sum\": \"879344aaa9d62fdccbda0be7a09e7976\","
|
|
||||||
" \"filename\": \"ggml-gpt4all-j-v1.2-jazzy.bin\","
|
|
||||||
" \"filesize\": \"3785248281\","
|
|
||||||
" \"isDefault\": \"true\""
|
|
||||||
" },"
|
|
||||||
" {"
|
|
||||||
" \"md5sum\": \"5b5a3f9b858d33b29b52b89692415595\","
|
|
||||||
" \"filesize\": \"3785248281\","
|
|
||||||
" \"filename\": \"ggml-gpt4all-j.bin\""
|
|
||||||
" }"
|
|
||||||
"]"
|
|
||||||
).toUtf8();
|
|
||||||
printf("%s\n", jsonData.toStdString().c_str());
|
|
||||||
fflush(stdout);
|
|
||||||
#else
|
|
||||||
QNetworkReply *jsonReply = qobject_cast<QNetworkReply *>(sender());
|
QNetworkReply *jsonReply = qobject_cast<QNetworkReply *>(sender());
|
||||||
if (!jsonReply)
|
if (!jsonReply)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QByteArray jsonData = jsonReply->readAll();
|
QByteArray jsonData = jsonReply->readAll();
|
||||||
jsonReply->deleteLater();
|
jsonReply->deleteLater();
|
||||||
#endif
|
|
||||||
parseModelsJsonFile(jsonData);
|
parseModelsJsonFile(jsonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,18 +245,17 @@ void Download::parseModelsJsonFile(const QByteArray &jsonData)
|
|||||||
QJsonParseError err;
|
QJsonParseError err;
|
||||||
QJsonDocument document = QJsonDocument::fromJson(jsonData, &err);
|
QJsonDocument document = QJsonDocument::fromJson(jsonData, &err);
|
||||||
if (err.error != QJsonParseError::NoError) {
|
if (err.error != QJsonParseError::NoError) {
|
||||||
qDebug() << "ERROR: Couldn't parse: " << jsonData << err.errorString();
|
qWarning() << "ERROR: Couldn't parse: " << jsonData << err.errorString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString defaultModel;
|
|
||||||
QJsonArray jsonArray = document.array();
|
QJsonArray jsonArray = document.array();
|
||||||
const QString currentVersion = QCoreApplication::applicationVersion();
|
const QString currentVersion = QCoreApplication::applicationVersion();
|
||||||
|
|
||||||
m_modelMap.clear();
|
|
||||||
for (const QJsonValue &value : jsonArray) {
|
for (const QJsonValue &value : jsonArray) {
|
||||||
QJsonObject obj = value.toObject();
|
QJsonObject obj = value.toObject();
|
||||||
|
|
||||||
|
QString modelName = obj["name"].toString();
|
||||||
QString modelFilename = obj["filename"].toString();
|
QString modelFilename = obj["filename"].toString();
|
||||||
QString modelFilesize = obj["filesize"].toString();
|
QString modelFilesize = obj["filesize"].toString();
|
||||||
QString requiresVersion = obj["requires"].toString();
|
QString requiresVersion = obj["requires"].toString();
|
||||||
@ -377,10 +263,13 @@ void Download::parseModelsJsonFile(const QByteArray &jsonData)
|
|||||||
QString url = obj["url"].toString();
|
QString url = obj["url"].toString();
|
||||||
QByteArray modelMd5sum = obj["md5sum"].toString().toLatin1().constData();
|
QByteArray modelMd5sum = obj["md5sum"].toString().toLatin1().constData();
|
||||||
bool isDefault = obj.contains("isDefault") && obj["isDefault"] == QString("true");
|
bool isDefault = obj.contains("isDefault") && obj["isDefault"] == QString("true");
|
||||||
bool bestGPTJ = obj.contains("bestGPTJ") && obj["bestGPTJ"] == QString("true");
|
bool disableGUI = obj.contains("disableGUI") && obj["disableGUI"] == QString("true");
|
||||||
bool bestLlama = obj.contains("bestLlama") && obj["bestLlama"] == QString("true");
|
|
||||||
bool bestMPT = obj.contains("bestMPT") && obj["bestMPT"] == QString("true");
|
|
||||||
QString description = obj["description"].toString();
|
QString description = obj["description"].toString();
|
||||||
|
QString order = obj["order"].toString();
|
||||||
|
int ramrequired = obj["ramrequired"].toString().toInt();
|
||||||
|
QString parameters = obj["parameters"].toString();
|
||||||
|
QString quant = obj["quant"].toString();
|
||||||
|
QString type = obj["type"].toString();
|
||||||
|
|
||||||
// If the currentVersion version is strictly less than required version, then continue
|
// If the currentVersion version is strictly less than required version, then continue
|
||||||
if (!requiresVersion.isEmpty()
|
if (!requiresVersion.isEmpty()
|
||||||
@ -395,77 +284,66 @@ void Download::parseModelsJsonFile(const QByteArray &jsonData)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDefault)
|
modelFilesize = ModelList::toFileSize(modelFilesize.toULongLong());
|
||||||
defaultModel = modelFilename;
|
|
||||||
quint64 sz = modelFilesize.toULongLong();
|
if (!ModelList::globalInstance()->contains(modelFilename))
|
||||||
if (sz < 1024) {
|
ModelList::globalInstance()->addModel(modelFilename);
|
||||||
modelFilesize = QString("%1 bytes").arg(sz);
|
|
||||||
} else if (sz < 1024 * 1024) {
|
if (!modelName.isEmpty())
|
||||||
modelFilesize = QString("%1 KB").arg(qreal(sz) / 1024, 0, 'g', 3);
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::NameRole, modelName);
|
||||||
} else if (sz < 1024 * 1024 * 1024) {
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::FilesizeRole, modelFilesize);
|
||||||
modelFilesize = QString("%1 MB").arg(qreal(sz) / (1024 * 1024), 0, 'g', 3);
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::Md5sumRole, modelMd5sum);
|
||||||
} else {
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DefaultRole, isDefault);
|
||||||
modelFilesize = QString("%1 GB").arg(qreal(sz) / (1024 * 1024 * 1024), 0, 'g', 3);
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DescriptionRole, description);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::RequiresVersionRole, requiresVersion);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DeprecatedVersionRole, deprecatedVersion);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::UrlRole, url);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DisableGUIRole, disableGUI);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::OrderRole, order);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::RamrequiredRole, ramrequired);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::ParametersRole, parameters);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::QuantRole, quant);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::TypeRole, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString filePath = downloadLocalModelsPath() + modelFilename;
|
const QString chatGPTDesc = tr("<ul><li>Requires personal OpenAI API key.</li><li>WARNING: Will send"
|
||||||
QFileInfo info(filePath);
|
" your chats to OpenAI!</li><li>Your API key will be stored on disk</li><li>Will only be used"
|
||||||
ModelInfo modelInfo;
|
" to communicate with OpenAI</li><li>You can apply for an API key"
|
||||||
modelInfo.filename = modelFilename;
|
" <a href=\"https://platform.openai.com/account/api-keys\">here.</a></li>");
|
||||||
modelInfo.filesize = modelFilesize;
|
|
||||||
modelInfo.md5sum = modelMd5sum;
|
|
||||||
modelInfo.installed = info.exists();
|
|
||||||
modelInfo.isDefault = isDefault;
|
|
||||||
modelInfo.bestGPTJ = bestGPTJ;
|
|
||||||
modelInfo.bestLlama = bestLlama;
|
|
||||||
modelInfo.bestMPT = bestMPT;
|
|
||||||
modelInfo.description = description;
|
|
||||||
modelInfo.requiresVersion = requiresVersion;
|
|
||||||
modelInfo.deprecatedVersion = deprecatedVersion;
|
|
||||||
modelInfo.url = url;
|
|
||||||
m_modelMap.insert(modelInfo.filename, modelInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString chatGPTDesc = tr("WARNING: requires personal OpenAI API key and usage of this "
|
|
||||||
"model will send your chats over the network to OpenAI. Your API key will be stored on disk "
|
|
||||||
"and only used to interact with OpenAI models. If you don't have one, you can apply for "
|
|
||||||
"an API key <a href=\"https://platform.openai.com/account/api-keys\">here.</a>");
|
|
||||||
|
|
||||||
{
|
{
|
||||||
ModelInfo modelInfo;
|
const QString modelFilename = "chatgpt-gpt-3.5-turbo.txt";
|
||||||
modelInfo.isChatGPT = true;
|
if (!ModelList::globalInstance()->contains(modelFilename))
|
||||||
modelInfo.filename = "chatgpt-gpt-3.5-turbo";
|
ModelList::globalInstance()->addModel(modelFilename);
|
||||||
modelInfo.description = tr("OpenAI's ChatGPT model gpt-3.5-turbo. ") + chatGPTDesc;
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::NameRole, "ChatGPT-3.5 Turbo");
|
||||||
modelInfo.requiresVersion = "2.4.2";
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::FilesizeRole, "minimal");
|
||||||
QString filePath = downloadLocalModelsPath() + modelInfo.filename + ".txt";
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::ChatGPTRole, true);
|
||||||
QFileInfo info(filePath);
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DescriptionRole,
|
||||||
modelInfo.installed = info.exists();
|
tr("<strong>OpenAI's ChatGPT model GPT-3.5 Turbo</strong><br>") + chatGPTDesc);
|
||||||
m_modelMap.insert(modelInfo.filename, modelInfo);
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::RequiresVersionRole, "2.4.2");
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::OrderRole, "ca");
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::RamrequiredRole, 0);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::ParametersRole, "?");
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::QuantRole, "NA");
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::TypeRole, "GPT");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ModelInfo modelInfo;
|
const QString modelFilename = "chatgpt-gpt-4.txt";
|
||||||
modelInfo.isChatGPT = true;
|
if (!ModelList::globalInstance()->contains(modelFilename))
|
||||||
modelInfo.filename = "chatgpt-gpt-4";
|
ModelList::globalInstance()->addModel(modelFilename);
|
||||||
modelInfo.description = tr("OpenAI's ChatGPT model gpt-4. ") + chatGPTDesc;
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::NameRole, "ChatGPT-4");
|
||||||
modelInfo.requiresVersion = "2.4.2";
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::FilesizeRole, "minimal");
|
||||||
QString filePath = downloadLocalModelsPath() + modelInfo.filename + ".txt";
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::ChatGPTRole, true);
|
||||||
QFileInfo info(filePath);
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DescriptionRole,
|
||||||
modelInfo.installed = info.exists();
|
tr("<strong>OpenAI's ChatGPT model GPT-4</strong><br>") + chatGPTDesc);
|
||||||
m_modelMap.insert(modelInfo.filename, modelInfo);
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::RequiresVersionRole, "2.4.2");
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::OrderRole, "cb");
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::RamrequiredRole, 0);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::ParametersRole, "?");
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::QuantRole, "NA");
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::TypeRole, "GPT");
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove ggml- prefix and .bin suffix
|
|
||||||
if (defaultModel.startsWith("ggml-"))
|
|
||||||
defaultModel = defaultModel.remove(0, 5);
|
|
||||||
if (defaultModel.endsWith(".bin"))
|
|
||||||
defaultModel.chop(4);
|
|
||||||
|
|
||||||
QSettings settings;
|
|
||||||
settings.sync();
|
|
||||||
settings.setValue("defaultModel", defaultModel);
|
|
||||||
settings.sync();
|
|
||||||
emit modelListChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Download::handleReleaseJsonDownloadFinished()
|
void Download::handleReleaseJsonDownloadFinished()
|
||||||
@ -484,7 +362,7 @@ void Download::parseReleaseJsonFile(const QByteArray &jsonData)
|
|||||||
QJsonParseError err;
|
QJsonParseError err;
|
||||||
QJsonDocument document = QJsonDocument::fromJson(jsonData, &err);
|
QJsonDocument document = QJsonDocument::fromJson(jsonData, &err);
|
||||||
if (err.error != QJsonParseError::NoError) {
|
if (err.error != QJsonParseError::NoError) {
|
||||||
qDebug() << "ERROR: Couldn't parse: " << jsonData << err.errorString();
|
qWarning() << "ERROR: Couldn't parse: " << jsonData << err.errorString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,10 +393,13 @@ void Download::handleErrorOccurred(QNetworkReply::NetworkError code)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
QString modelFilename = modelReply->request().attribute(QNetworkRequest::User).toString();
|
QString modelFilename = modelReply->request().attribute(QNetworkRequest::User).toString();
|
||||||
qWarning() << "ERROR: Network error occurred attempting to download"
|
const QString error
|
||||||
<< modelFilename
|
= QString("ERROR: Network error occurred attempting to download %1 code: %2 errorString %3")
|
||||||
<< "code:" << code
|
.arg(modelFilename)
|
||||||
<< "errorString" << modelReply->errorString();
|
.arg(code)
|
||||||
|
.arg(modelReply->errorString());
|
||||||
|
qWarning() << error;
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DownloadErrorRole, error);
|
||||||
Network::globalInstance()->sendDownloadError(modelFilename, (int)code, modelReply->errorString());
|
Network::globalInstance()->sendDownloadError(modelFilename, (int)code, modelReply->errorString());
|
||||||
cancelDownload(modelFilename);
|
cancelDownload(modelFilename);
|
||||||
}
|
}
|
||||||
@ -537,8 +418,30 @@ void Download::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
|||||||
bytesTotal = contentTotalSize.toLongLong();
|
bytesTotal = contentTotalSize.toLongLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString modelFilename = modelReply->request().attribute(QNetworkRequest::User).toString();
|
const QString modelFilename = modelReply->request().attribute(QNetworkRequest::User).toString();
|
||||||
emit downloadProgress(tempFile->pos(), bytesTotal, modelFilename);
|
const qint64 lastUpdate = ModelList::globalInstance()->data(modelFilename, ModelList::TimestampRole).toLongLong();
|
||||||
|
const qint64 currentUpdate = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
if (currentUpdate - lastUpdate < 1000)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const qint64 lastBytesReceived = ModelList::globalInstance()->data(modelFilename, ModelList::BytesReceivedRole).toLongLong();
|
||||||
|
const qint64 currentBytesReceived = tempFile->pos();
|
||||||
|
|
||||||
|
qint64 timeDifference = currentUpdate - lastUpdate;
|
||||||
|
qint64 bytesDifference = currentBytesReceived - lastBytesReceived;
|
||||||
|
qint64 speed = (bytesDifference / timeDifference) * 1000; // bytes per second
|
||||||
|
QString speedText;
|
||||||
|
if (speed < 1024)
|
||||||
|
speedText = QString::number(static_cast<double>(speed), 'f', 2) + " B/s";
|
||||||
|
else if (speed < 1024 * 1024)
|
||||||
|
speedText = QString::number(static_cast<double>(speed / 1024.0), 'f', 2) + " KB/s";
|
||||||
|
else
|
||||||
|
speedText = QString::number(static_cast<double>(speed / (1024.0 * 1024.0)), 'f', 2) + " MB/s";
|
||||||
|
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::BytesReceivedRole, currentBytesReceived);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::BytesTotalRole, bytesTotal);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::SpeedRole, speedText);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::TimestampRole, currentUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
HashAndSaveFile::HashAndSaveFile()
|
HashAndSaveFile::HashAndSaveFile()
|
||||||
@ -557,9 +460,10 @@ void HashAndSaveFile::hashAndSave(const QString &expectedHash, const QString &sa
|
|||||||
|
|
||||||
// Reopen the tempFile for hashing
|
// Reopen the tempFile for hashing
|
||||||
if (!tempFile->open(QIODevice::ReadOnly)) {
|
if (!tempFile->open(QIODevice::ReadOnly)) {
|
||||||
qWarning() << "ERROR: Could not open temp file for hashing:"
|
const QString error
|
||||||
<< tempFile->fileName() << modelFilename;
|
= QString("ERROR: Could not open temp file for hashing: %1 %2").arg(tempFile->fileName()).arg(modelFilename);
|
||||||
emit hashAndSaveFinished(false, tempFile, modelReply);
|
qWarning() << error;
|
||||||
|
emit hashAndSaveFinished(false, error, tempFile, modelReply);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,11 +472,11 @@ void HashAndSaveFile::hashAndSave(const QString &expectedHash, const QString &sa
|
|||||||
hash.addData(tempFile->read(16384));
|
hash.addData(tempFile->read(16384));
|
||||||
if (hash.result().toHex() != expectedHash) {
|
if (hash.result().toHex() != expectedHash) {
|
||||||
tempFile->close();
|
tempFile->close();
|
||||||
qWarning() << "ERROR: Download error MD5SUM did not match:"
|
const QString error
|
||||||
<< hash.result().toHex()
|
= QString("ERROR: Download error MD5SUM did not match: %1 != %2 for %3").arg(hash.result().toHex()).arg(expectedHash).arg(modelFilename);
|
||||||
<< "!=" << expectedHash << "for" << modelFilename;
|
qWarning() << error;
|
||||||
tempFile->remove();
|
tempFile->remove();
|
||||||
emit hashAndSaveFinished(false, tempFile, modelReply);
|
emit hashAndSaveFinished(false, error, tempFile, modelReply);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,15 +486,16 @@ void HashAndSaveFile::hashAndSave(const QString &expectedHash, const QString &sa
|
|||||||
// Attempt to *move* the verified tempfile into place - this should be atomic
|
// Attempt to *move* the verified tempfile into place - this should be atomic
|
||||||
// but will only work if the destination is on the same filesystem
|
// but will only work if the destination is on the same filesystem
|
||||||
if (tempFile->rename(saveFilePath)) {
|
if (tempFile->rename(saveFilePath)) {
|
||||||
emit hashAndSaveFinished(true, tempFile, modelReply);
|
emit hashAndSaveFinished(true, QString(), tempFile, modelReply);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reopen the tempFile for copying
|
// Reopen the tempFile for copying
|
||||||
if (!tempFile->open(QIODevice::ReadOnly)) {
|
if (!tempFile->open(QIODevice::ReadOnly)) {
|
||||||
qWarning() << "ERROR: Could not open temp file at finish:"
|
const QString error
|
||||||
<< tempFile->fileName() << modelFilename;
|
= QString("ERROR: Could not open temp file at finish: %1 %2").arg(tempFile->fileName()).arg(modelFilename);
|
||||||
emit hashAndSaveFinished(false, tempFile, modelReply);
|
qWarning() << error;
|
||||||
|
emit hashAndSaveFinished(false, error, tempFile, modelReply);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -604,14 +509,14 @@ void HashAndSaveFile::hashAndSave(const QString &expectedHash, const QString &sa
|
|||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
tempFile->close();
|
tempFile->close();
|
||||||
emit hashAndSaveFinished(true, tempFile, modelReply);
|
emit hashAndSaveFinished(true, QString(), tempFile, modelReply);
|
||||||
} else {
|
} else {
|
||||||
QFile::FileError error = file.error();
|
QFile::FileError error = file.error();
|
||||||
qWarning() << "ERROR: Could not save model to location:"
|
const QString errorString
|
||||||
<< saveFilePath
|
= QString("ERROR: Could not save model to location: %1 failed with code %2").arg(saveFilePath).arg(error);
|
||||||
<< "failed with code" << error;
|
qWarning() << errorString;
|
||||||
tempFile->close();
|
tempFile->close();
|
||||||
emit hashAndSaveFinished(false, tempFile, modelReply);
|
emit hashAndSaveFinished(false, errorString, tempFile, modelReply);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,40 +535,44 @@ void Download::handleModelDownloadFinished()
|
|||||||
qWarning() << "ERROR: downloading:" << modelReply->errorString();
|
qWarning() << "ERROR: downloading:" << modelReply->errorString();
|
||||||
modelReply->deleteLater();
|
modelReply->deleteLater();
|
||||||
tempFile->deleteLater();
|
tempFile->deleteLater();
|
||||||
emit downloadFinished(modelFilename);
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DownloadingRole, false);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DownloadErrorRole, modelReply->errorString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The hash and save needs the tempFile closed
|
// The hash and save needs the tempFile closed
|
||||||
tempFile->close();
|
tempFile->close();
|
||||||
|
|
||||||
// Notify that we are calculating hash
|
if (!ModelList::globalInstance()->contains(modelFilename)) {
|
||||||
ModelInfo info = m_modelMap.value(modelFilename);
|
qWarning() << "ERROR: downloading no such file:" << modelFilename;
|
||||||
info.calcHash = true;
|
modelReply->deleteLater();
|
||||||
m_modelMap.insert(modelFilename, info);
|
tempFile->deleteLater();
|
||||||
emit modelListChanged();
|
return;
|
||||||
|
|
||||||
const QString saveFilePath = downloadLocalModelsPath() + modelFilename;
|
|
||||||
emit requestHashAndSave(info.md5sum, saveFilePath, tempFile, modelReply);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Download::handleHashAndSaveFinished(bool success,
|
// Notify that we are calculating hash
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::CalcHashRole, true);
|
||||||
|
QByteArray md5sum = ModelList::globalInstance()->modelInfo(modelFilename).md5sum;
|
||||||
|
const QString saveFilePath = ModelList::globalInstance()->localModelsPath() + modelFilename;
|
||||||
|
emit requestHashAndSave(md5sum, saveFilePath, tempFile, modelReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Download::handleHashAndSaveFinished(bool success, const QString &error,
|
||||||
QFile *tempFile, QNetworkReply *modelReply)
|
QFile *tempFile, QNetworkReply *modelReply)
|
||||||
{
|
{
|
||||||
// The hash and save should send back with tempfile closed
|
// The hash and save should send back with tempfile closed
|
||||||
Q_ASSERT(!tempFile->isOpen());
|
Q_ASSERT(!tempFile->isOpen());
|
||||||
QString modelFilename = modelReply->request().attribute(QNetworkRequest::User).toString();
|
QString modelFilename = modelReply->request().attribute(QNetworkRequest::User).toString();
|
||||||
Network::globalInstance()->sendDownloadFinished(modelFilename, success);
|
Network::globalInstance()->sendDownloadFinished(modelFilename, success);
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::CalcHashRole, false);
|
||||||
ModelInfo info = m_modelMap.value(modelFilename);
|
|
||||||
info.calcHash = false;
|
|
||||||
info.installed = success;
|
|
||||||
m_modelMap.insert(modelFilename, info);
|
|
||||||
emit modelListChanged();
|
|
||||||
|
|
||||||
modelReply->deleteLater();
|
modelReply->deleteLater();
|
||||||
tempFile->deleteLater();
|
tempFile->deleteLater();
|
||||||
emit downloadFinished(modelFilename);
|
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DownloadingRole, false);
|
||||||
|
if (!success)
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DownloadErrorRole, error);
|
||||||
|
else
|
||||||
|
ModelList::globalInstance()->updateData(modelFilename, ModelList::DownloadErrorRole, QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Download::handleReadyRead()
|
void Download::handleReadyRead()
|
||||||
|
@ -9,41 +9,6 @@
|
|||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
struct ModelInfo {
|
|
||||||
Q_GADGET
|
|
||||||
Q_PROPERTY(QString filename MEMBER filename)
|
|
||||||
Q_PROPERTY(QString filesize MEMBER filesize)
|
|
||||||
Q_PROPERTY(QByteArray md5sum MEMBER md5sum)
|
|
||||||
Q_PROPERTY(bool calcHash MEMBER calcHash)
|
|
||||||
Q_PROPERTY(bool installed MEMBER installed)
|
|
||||||
Q_PROPERTY(bool isDefault MEMBER isDefault)
|
|
||||||
Q_PROPERTY(bool bestGPTJ MEMBER bestGPTJ)
|
|
||||||
Q_PROPERTY(bool bestLlama MEMBER bestLlama)
|
|
||||||
Q_PROPERTY(bool bestMPT MEMBER bestMPT)
|
|
||||||
Q_PROPERTY(bool isChatGPT MEMBER isChatGPT)
|
|
||||||
Q_PROPERTY(QString description MEMBER description)
|
|
||||||
Q_PROPERTY(QString requiresVersion MEMBER requiresVersion)
|
|
||||||
Q_PROPERTY(QString deprecatedVersion MEMBER deprecatedVersion)
|
|
||||||
Q_PROPERTY(QString url MEMBER url)
|
|
||||||
|
|
||||||
public:
|
|
||||||
QString filename;
|
|
||||||
QString filesize;
|
|
||||||
QByteArray md5sum;
|
|
||||||
bool calcHash = false;
|
|
||||||
bool installed = false;
|
|
||||||
bool isDefault = false;
|
|
||||||
bool bestGPTJ = false;
|
|
||||||
bool bestLlama = false;
|
|
||||||
bool bestMPT = false;
|
|
||||||
bool isChatGPT = false;
|
|
||||||
QString description;
|
|
||||||
QString requiresVersion;
|
|
||||||
QString deprecatedVersion;
|
|
||||||
QString url;
|
|
||||||
};
|
|
||||||
Q_DECLARE_METATYPE(ModelInfo)
|
|
||||||
|
|
||||||
struct ReleaseInfo {
|
struct ReleaseInfo {
|
||||||
Q_GADGET
|
Q_GADGET
|
||||||
Q_PROPERTY(QString version MEMBER version)
|
Q_PROPERTY(QString version MEMBER version)
|
||||||
@ -67,7 +32,7 @@ public Q_SLOTS:
|
|||||||
QFile *tempFile, QNetworkReply *modelReply);
|
QFile *tempFile, QNetworkReply *modelReply);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void hashAndSaveFinished(bool success,
|
void hashAndSaveFinished(bool success, const QString &error,
|
||||||
QFile *tempFile, QNetworkReply *modelReply);
|
QFile *tempFile, QNetworkReply *modelReply);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -77,30 +42,24 @@ private:
|
|||||||
class Download : public QObject
|
class Download : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QList<ModelInfo> modelList READ modelList NOTIFY modelListChanged)
|
|
||||||
Q_PROPERTY(bool hasNewerRelease READ hasNewerRelease NOTIFY hasNewerReleaseChanged)
|
Q_PROPERTY(bool hasNewerRelease READ hasNewerRelease NOTIFY hasNewerReleaseChanged)
|
||||||
Q_PROPERTY(ReleaseInfo releaseInfo READ releaseInfo NOTIFY releaseInfoChanged)
|
Q_PROPERTY(ReleaseInfo releaseInfo READ releaseInfo NOTIFY releaseInfoChanged)
|
||||||
Q_PROPERTY(QString downloadLocalModelsPath READ downloadLocalModelsPath
|
|
||||||
WRITE setDownloadLocalModelsPath
|
|
||||||
NOTIFY downloadLocalModelsPathChanged)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Download *globalInstance();
|
static Download *globalInstance();
|
||||||
|
|
||||||
QList<ModelInfo> modelList() const;
|
|
||||||
ReleaseInfo releaseInfo() const;
|
ReleaseInfo releaseInfo() const;
|
||||||
bool hasNewerRelease() const;
|
bool hasNewerRelease() const;
|
||||||
Q_INVOKABLE void updateModelList();
|
|
||||||
Q_INVOKABLE void updateReleaseNotes();
|
|
||||||
Q_INVOKABLE void downloadModel(const QString &modelFile);
|
Q_INVOKABLE void downloadModel(const QString &modelFile);
|
||||||
Q_INVOKABLE void cancelDownload(const QString &modelFile);
|
Q_INVOKABLE void cancelDownload(const QString &modelFile);
|
||||||
Q_INVOKABLE void installModel(const QString &modelFile, const QString &apiKey);
|
Q_INVOKABLE void installModel(const QString &modelFile, const QString &apiKey);
|
||||||
Q_INVOKABLE void removeModel(const QString &modelFile);
|
Q_INVOKABLE void removeModel(const QString &modelFile);
|
||||||
Q_INVOKABLE QString defaultLocalModelsPath() const;
|
|
||||||
Q_INVOKABLE QString downloadLocalModelsPath() const;
|
|
||||||
Q_INVOKABLE void setDownloadLocalModelsPath(const QString &modelPath);
|
|
||||||
Q_INVOKABLE bool isFirstStart() const;
|
Q_INVOKABLE bool isFirstStart() const;
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void updateModelList();
|
||||||
|
void updateReleaseNotes();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
void handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
||||||
void handleModelsJsonDownloadFinished();
|
void handleModelsJsonDownloadFinished();
|
||||||
@ -108,17 +67,13 @@ private Q_SLOTS:
|
|||||||
void handleErrorOccurred(QNetworkReply::NetworkError code);
|
void handleErrorOccurred(QNetworkReply::NetworkError code);
|
||||||
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
void handleModelDownloadFinished();
|
void handleModelDownloadFinished();
|
||||||
void handleHashAndSaveFinished(bool success,
|
void handleHashAndSaveFinished(bool success, const QString &error,
|
||||||
QFile *tempFile, QNetworkReply *modelReply);
|
QFile *tempFile, QNetworkReply *modelReply);
|
||||||
void handleReadyRead();
|
void handleReadyRead();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal, const QString &modelFile);
|
|
||||||
void downloadFinished(const QString &modelFile);
|
|
||||||
void modelListChanged();
|
|
||||||
void releaseInfoChanged();
|
void releaseInfoChanged();
|
||||||
void hasNewerReleaseChanged();
|
void hasNewerReleaseChanged();
|
||||||
void downloadLocalModelsPathChanged();
|
|
||||||
void requestHashAndSave(const QString &hash, const QString &saveFilePath,
|
void requestHashAndSave(const QString &hash, const QString &saveFilePath,
|
||||||
QFile *tempFile, QNetworkReply *modelReply);
|
QFile *tempFile, QNetworkReply *modelReply);
|
||||||
|
|
||||||
@ -128,11 +83,9 @@ private:
|
|||||||
QString incompleteDownloadPath(const QString &modelFile);
|
QString incompleteDownloadPath(const QString &modelFile);
|
||||||
|
|
||||||
HashAndSaveFile *m_hashAndSave;
|
HashAndSaveFile *m_hashAndSave;
|
||||||
QMap<QString, ModelInfo> m_modelMap;
|
|
||||||
QMap<QString, ReleaseInfo> m_releaseMap;
|
QMap<QString, ReleaseInfo> m_releaseMap;
|
||||||
QNetworkAccessManager m_networkManager;
|
QNetworkAccessManager m_networkManager;
|
||||||
QMap<QNetworkReply*, QFile*> m_activeDownloads;
|
QMap<QNetworkReply*, QFile*> m_activeDownloads;
|
||||||
QString m_downloadLocalModelsPath;
|
|
||||||
QDateTime m_startTime;
|
QDateTime m_startTime;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "llm.h"
|
#include "llm.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "download.h"
|
#include "sysinfo.h"
|
||||||
|
#include "chatlistmodel.h"
|
||||||
|
#include "../gpt4all-backend/llmodel.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
@ -20,7 +22,6 @@ LLM *LLM::globalInstance()
|
|||||||
|
|
||||||
LLM::LLM()
|
LLM::LLM()
|
||||||
: QObject{nullptr}
|
: QObject{nullptr}
|
||||||
, m_chatListModel(new ChatListModel(this))
|
|
||||||
, m_threadCount(std::min(4, (int32_t) std::thread::hardware_concurrency()))
|
, m_threadCount(std::min(4, (int32_t) std::thread::hardware_concurrency()))
|
||||||
, m_serverEnabled(false)
|
, m_serverEnabled(false)
|
||||||
, m_compatHardware(true)
|
, m_compatHardware(true)
|
||||||
@ -39,7 +40,7 @@ LLM::LLM()
|
|||||||
#endif
|
#endif
|
||||||
LLModel::setImplementationsSearchPath(llmodelSearchPaths.toStdString());
|
LLModel::setImplementationsSearchPath(llmodelSearchPaths.toStdString());
|
||||||
connect(this, &LLM::serverEnabledChanged,
|
connect(this, &LLM::serverEnabledChanged,
|
||||||
m_chatListModel, &ChatListModel::handleServerEnabledChanged);
|
ChatListModel::globalInstance(), &ChatListModel::handleServerEnabledChanged);
|
||||||
|
|
||||||
#if defined(__x86_64__)
|
#if defined(__x86_64__)
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
@ -95,6 +96,16 @@ bool LLM::fileExists(const QString &path) const
|
|||||||
return info.exists() && info.isFile();
|
return info.exists() && info.isFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qint64 LLM::systemTotalRAMInGB() const
|
||||||
|
{
|
||||||
|
return getSystemTotalRAMInGB();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LLM::systemTotalRAMInGBString() const
|
||||||
|
{
|
||||||
|
return QString::fromStdString(getSystemTotalRAMInGBString());
|
||||||
|
}
|
||||||
|
|
||||||
int32_t LLM::threadCount() const
|
int32_t LLM::threadCount() const
|
||||||
{
|
{
|
||||||
return m_threadCount;
|
return m_threadCount;
|
||||||
|
@ -3,12 +3,9 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "chatlistmodel.h"
|
|
||||||
|
|
||||||
class LLM : public QObject
|
class LLM : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(ChatListModel *chatListModel READ chatListModel NOTIFY chatListModelChanged)
|
|
||||||
Q_PROPERTY(int32_t threadCount READ threadCount WRITE setThreadCount NOTIFY threadCountChanged)
|
Q_PROPERTY(int32_t threadCount READ threadCount WRITE setThreadCount NOTIFY threadCountChanged)
|
||||||
Q_PROPERTY(bool serverEnabled READ serverEnabled WRITE setServerEnabled NOTIFY serverEnabledChanged)
|
Q_PROPERTY(bool serverEnabled READ serverEnabled WRITE setServerEnabled NOTIFY serverEnabledChanged)
|
||||||
Q_PROPERTY(bool compatHardware READ compatHardware NOTIFY compatHardwareChanged)
|
Q_PROPERTY(bool compatHardware READ compatHardware NOTIFY compatHardwareChanged)
|
||||||
@ -16,7 +13,6 @@ class LLM : public QObject
|
|||||||
public:
|
public:
|
||||||
static LLM *globalInstance();
|
static LLM *globalInstance();
|
||||||
|
|
||||||
ChatListModel *chatListModel() const { return m_chatListModel; }
|
|
||||||
int32_t threadCount() const;
|
int32_t threadCount() const;
|
||||||
void setThreadCount(int32_t n_threads);
|
void setThreadCount(int32_t n_threads);
|
||||||
bool serverEnabled() const;
|
bool serverEnabled() const;
|
||||||
@ -27,15 +23,17 @@ public:
|
|||||||
Q_INVOKABLE bool checkForUpdates() const;
|
Q_INVOKABLE bool checkForUpdates() const;
|
||||||
Q_INVOKABLE bool directoryExists(const QString &path) const;
|
Q_INVOKABLE bool directoryExists(const QString &path) const;
|
||||||
Q_INVOKABLE bool fileExists(const QString &path) const;
|
Q_INVOKABLE bool fileExists(const QString &path) const;
|
||||||
|
Q_INVOKABLE qint64 systemTotalRAMInGB() const;
|
||||||
|
Q_INVOKABLE QString systemTotalRAMInGBString() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void chatListModelChanged();
|
void chatListModelChanged();
|
||||||
|
void modelListChanged();
|
||||||
void threadCountChanged();
|
void threadCountChanged();
|
||||||
void serverEnabledChanged();
|
void serverEnabledChanged();
|
||||||
void compatHardwareChanged();
|
void compatHardwareChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ChatListModel *m_chatListModel;
|
|
||||||
int32_t m_threadCount;
|
int32_t m_threadCount;
|
||||||
bool m_serverEnabled;
|
bool m_serverEnabled;
|
||||||
bool m_compatHardware;
|
bool m_compatHardware;
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
#include "llm.h"
|
#include "llm.h"
|
||||||
|
#include "modellist.h"
|
||||||
|
#include "chatlistmodel.h"
|
||||||
#include "localdocs.h"
|
#include "localdocs.h"
|
||||||
#include "download.h"
|
#include "download.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
@ -24,6 +26,8 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
|
qmlRegisterSingletonInstance("modellist", 1, 0, "ModelList", ModelList::globalInstance());
|
||||||
|
qmlRegisterSingletonInstance("chatlistmodel", 1, 0, "ChatListModel", ChatListModel::globalInstance());
|
||||||
qmlRegisterSingletonInstance("llm", 1, 0, "LLM", LLM::globalInstance());
|
qmlRegisterSingletonInstance("llm", 1, 0, "LLM", LLM::globalInstance());
|
||||||
qmlRegisterSingletonInstance("download", 1, 0, "Download", Download::globalInstance());
|
qmlRegisterSingletonInstance("download", 1, 0, "Download", Download::globalInstance());
|
||||||
qmlRegisterSingletonInstance("network", 1, 0, "Network", Network::globalInstance());
|
qmlRegisterSingletonInstance("network", 1, 0, "Network", Network::globalInstance());
|
||||||
|
@ -5,7 +5,9 @@ import QtQuick.Controls.Basic
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
import llm
|
import llm
|
||||||
|
import chatlistmodel
|
||||||
import download
|
import download
|
||||||
|
import modellist
|
||||||
import network
|
import network
|
||||||
import gpt4all
|
import gpt4all
|
||||||
|
|
||||||
@ -22,7 +24,7 @@ Window {
|
|||||||
id: theme
|
id: theme
|
||||||
}
|
}
|
||||||
|
|
||||||
property var currentChat: LLM.chatListModel.currentChat
|
property var currentChat: ChatListModel.currentChat
|
||||||
property var chatModel: currentChat.chatModel
|
property var chatModel: currentChat.chatModel
|
||||||
property bool hasSaved: false
|
property bool hasSaved: false
|
||||||
|
|
||||||
@ -31,12 +33,12 @@ Window {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
savingPopup.open();
|
savingPopup.open();
|
||||||
LLM.chatListModel.saveChats();
|
ChatListModel.saveChats();
|
||||||
close.accepted = false
|
close.accepted = false
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: LLM.chatListModel
|
target: ChatListModel
|
||||||
function onSaveChatsFinished() {
|
function onSaveChatsFinished() {
|
||||||
window.hasSaved = true;
|
window.hasSaved = true;
|
||||||
savingPopup.close();
|
savingPopup.close();
|
||||||
@ -96,7 +98,7 @@ Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check for any current models and if not, open download dialog
|
// check for any current models and if not, open download dialog
|
||||||
if (currentChat.modelList.length === 0 && !firstStartDialog.opened) {
|
if (ModelList.count === 0 && !firstStartDialog.opened) {
|
||||||
downloadNewModels.open();
|
downloadNewModels.open();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -144,7 +146,17 @@ Window {
|
|||||||
id: modelLoadingErrorPopup
|
id: modelLoadingErrorPopup
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
shouldTimeOut: false
|
shouldTimeOut: false
|
||||||
text: currentChat.modelLoadingError
|
text: qsTr("<h3>Encountered an error loading model:</h3><br>")
|
||||||
|
+ "<i>\"" + currentChat.modelLoadingError + "\"</i>"
|
||||||
|
+ qsTr("<br><br>Model loading failures can happen for a variety of reasons, but the most common "
|
||||||
|
+ "causes include a bad file format, an incomplete or corrupted download, the wrong file "
|
||||||
|
+ "type or an incompatible model type. Here are some suggestions for resolving the problem:"
|
||||||
|
+ "<br><ul>"
|
||||||
|
+ "<li>Ensure the model file has a compatible ggml format and type"
|
||||||
|
+ "<li>Check the model file is complete in the download folder"
|
||||||
|
+ "<li>You can find the download folder in the settings dialog"
|
||||||
|
+ "<li>If you've sideloaded the model ensure the file is not corrupt by checking md5sum"
|
||||||
|
+ "<li>Check out our <a href=\"https://discord.gg/4M2QFmTt2k\">discord channel</a> for help")
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@ -180,12 +192,22 @@ Window {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.horizontalCenterOffset: window.width >= 950 ? 0 : Math.max(-((950 - window.width) / 2), -99.5)
|
anchors.horizontalCenterOffset: window.width >= 950 ? 0 : Math.max(-((950 - window.width) / 2), -99.5)
|
||||||
enabled: !currentChat.isServer
|
enabled: !currentChat.isServer
|
||||||
model: currentChat.modelList
|
model: ModelList.installedModels
|
||||||
|
valueRole: "filename"
|
||||||
|
textRole: "name"
|
||||||
|
Connections {
|
||||||
|
target: currentChat
|
||||||
|
function onModelInfoChanged() {
|
||||||
|
comboBox.currentIndex = comboBox.indexOfValue(currentChat.modelInfo.filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
contentItem: Text {
|
contentItem: Text {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
leftPadding: 10
|
leftPadding: 10
|
||||||
rightPadding: 20
|
rightPadding: 20
|
||||||
text: currentChat.modelLoadingError !== "" ? "Model loading error..." : comboBox.displayText
|
text: currentChat.modelLoadingError !== "" ? qsTr("Model loading error...")
|
||||||
|
: (comboBox.textAt(comboBox.currentIndex) !== "" ? comboBox.textAt(comboBox.currentIndex)
|
||||||
|
: comboBox.valueAt(comboBox.currentIndex))
|
||||||
font: comboBox.font
|
font: comboBox.font
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
@ -195,7 +217,7 @@ Window {
|
|||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
width: comboBox.width
|
width: comboBox.width
|
||||||
contentItem: Text {
|
contentItem: Text {
|
||||||
text: modelData
|
text: name !== "" ? name : filename
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
font: comboBox.font
|
font: comboBox.font
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
@ -212,7 +234,7 @@ Window {
|
|||||||
onActivated: {
|
onActivated: {
|
||||||
currentChat.stopGenerating()
|
currentChat.stopGenerating()
|
||||||
currentChat.reset();
|
currentChat.reset();
|
||||||
currentChat.modelName = comboBox.currentText
|
currentChat.modelInfo = ModelList.modelInfo(comboBox.valueAt(comboBox.currentIndex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -552,7 +574,7 @@ Window {
|
|||||||
id: downloadNewModels
|
id: downloadNewModels
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: Math.min(1024, window.width - (window.width * .2))
|
width: Math.min(1024, window.width - (window.width * .2))
|
||||||
height: Math.min(600, window.height - (window.height * .2))
|
height: window.height - (window.height * .2)
|
||||||
Item {
|
Item {
|
||||||
Accessible.role: Accessible.Dialog
|
Accessible.role: Accessible.Dialog
|
||||||
Accessible.name: qsTr("Download new models dialog")
|
Accessible.name: qsTr("Download new models dialog")
|
||||||
@ -817,7 +839,7 @@ Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
visible: currentChat.isServer || currentChat.modelName.startsWith("chatgpt-")
|
visible: currentChat.isServer || currentChat.modelInfo.isChatGPT
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
sourceSize.width: 1024
|
sourceSize.width: 1024
|
||||||
sourceSize.height: 1024
|
sourceSize.height: 1024
|
||||||
|
@ -1,97 +1,262 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"md5sum": "81a09a0ddf89690372fc296ff7f625af",
|
"order": "a",
|
||||||
"filename": "ggml-gpt4all-j-v1.3-groovy.bin",
|
|
||||||
"filesize": "3785248281",
|
|
||||||
"isDefault": "true",
|
|
||||||
"bestGPTJ": "true",
|
|
||||||
"description": "GPT-J 6B finetuned by Nomic AI on the latest GPT4All dataset.<br>- Licensed for commercial use.<br>- Fast responses."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"md5sum": "11d9f060ca24575a2c303bdc39952486",
|
|
||||||
"filename": "GPT4All-13B-snoozy.ggmlv3.q4_0.bin",
|
|
||||||
"filesize": "8136770688",
|
|
||||||
"requires": "2.4.7",
|
|
||||||
"isDefault": "true",
|
|
||||||
"bestLlama": "true",
|
|
||||||
"description": "LLaMA 13B finetuned by Nomic AI on the latest GPT4All dataset.<br>- Cannot be used commercially.<br>- Slower responses but higher quality.",
|
|
||||||
"url": "https://huggingface.co/TheBloke/GPT4All-13B-snoozy-GGML/resolve/main/GPT4All-13B-snoozy.ggmlv3.q4_0.bin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"md5sum": "756249d3d6abe23bde3b1ae272628640",
|
|
||||||
"filename": "ggml-mpt-7b-chat.bin",
|
|
||||||
"filesize": "4854401050",
|
|
||||||
"isDefault": "true",
|
|
||||||
"bestMPT": "true",
|
|
||||||
"requires": "2.4.1",
|
|
||||||
"description": "MPT 7B chat model trained by Mosaic ML.<br>- Cannot be used commercially.<br>- Fast responses."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"md5sum": "4acc146dd43eb02845c233c29289c7c5",
|
"md5sum": "4acc146dd43eb02845c233c29289c7c5",
|
||||||
|
"name": "Hermes",
|
||||||
"filename": "nous-hermes-13b.ggmlv3.q4_0.bin",
|
"filename": "nous-hermes-13b.ggmlv3.q4_0.bin",
|
||||||
"filesize": "8136777088",
|
"filesize": "8136777088",
|
||||||
"requires": "2.4.7",
|
"requires": "2.4.7",
|
||||||
"description": "LLaMa 13B finetuned on over 300,000 curated and uncensored instructions.<br>- Cannot be used commercially.<br>- Best finetuned LLaMA model.<br>- This model was fine-tuned by Nous Research, with Teknium and Karan4D leading the fine tuning process and dataset curation, Redmond AI sponsoring the compute, and several other contributors. The result is an enhanced Llama 13b model that rivals GPT-3.5-turbo in performance across a variety of tasks. This model stands out for its long responses, low hallucination rate, and absence of OpenAI censorship mechanisms.",
|
"ramrequired": "16",
|
||||||
|
"parameters": "13 billion",
|
||||||
|
"quant": "q4_0",
|
||||||
|
"type": "LLaMA",
|
||||||
|
"description": "
|
||||||
|
<strong>Best overall model</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Instruction based
|
||||||
|
<li>Gives long reponses
|
||||||
|
<li>Curated with 300,000 uncensored instructions
|
||||||
|
<li>Trained by Nous Research
|
||||||
|
<li>Cannot be used commercially
|
||||||
|
</ul>",
|
||||||
"url": "https://huggingface.co/TheBloke/Nous-Hermes-13B-GGML/resolve/main/nous-hermes-13b.ggmlv3.q4_0.bin"
|
"url": "https://huggingface.co/TheBloke/Nous-Hermes-13B-GGML/resolve/main/nous-hermes-13b.ggmlv3.q4_0.bin"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"order": "b",
|
||||||
|
"md5sum": "756249d3d6abe23bde3b1ae272628640",
|
||||||
|
"name": "MPT Chat",
|
||||||
|
"filename": "ggml-mpt-7b-chat.bin",
|
||||||
|
"filesize": "4854401050",
|
||||||
|
"requires": "2.4.1",
|
||||||
|
"ramrequired": "8",
|
||||||
|
"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>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"order": "c",
|
||||||
|
"md5sum": "81a09a0ddf89690372fc296ff7f625af",
|
||||||
|
"name": "Groovy",
|
||||||
|
"filename": "ggml-gpt4all-j-v1.3-groovy.bin",
|
||||||
|
"filesize": "3785248281",
|
||||||
|
"ramrequired": "8",
|
||||||
|
"parameters": "7 billion",
|
||||||
|
"quant": "q4_0",
|
||||||
|
"type": "GPT-J",
|
||||||
|
"description": "
|
||||||
|
<strong>Best overall for commercial usage</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Fast responses
|
||||||
|
<li>Creative responses</li>
|
||||||
|
<li>Instruction based</li>
|
||||||
|
<li>Trained by Nomic ML
|
||||||
|
<li>Licensed for commercial use
|
||||||
|
</ul>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"order": "d",
|
||||||
|
"md5sum": "11d9f060ca24575a2c303bdc39952486",
|
||||||
|
"name": "Snoozy",
|
||||||
|
"filename": "GPT4All-13B-snoozy.ggmlv3.q4_0.bin",
|
||||||
|
"filesize": "8136770688",
|
||||||
|
"requires": "2.4.7",
|
||||||
|
"ramrequired": "16",
|
||||||
|
"parameters": "13 billion",
|
||||||
|
"quant": "q4_0",
|
||||||
|
"type": "LLaMA",
|
||||||
|
"description": "
|
||||||
|
<strong>Very good overall model</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Instruction based
|
||||||
|
<li>Based on the same dataset as Groovy
|
||||||
|
<li>Slower than Groovy, with higher quality responses
|
||||||
|
<li>Trained by Nomic AI
|
||||||
|
<li>Cannot be used commercially
|
||||||
|
</ul>",
|
||||||
|
"url": "https://huggingface.co/TheBloke/GPT4All-13B-snoozy-GGML/resolve/main/GPT4All-13B-snoozy.ggmlv3.q4_0.bin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"order": "e",
|
||||||
"md5sum": "29119f8fa11712704c6b22ac5ab792ea",
|
"md5sum": "29119f8fa11712704c6b22ac5ab792ea",
|
||||||
|
"name": "Vicuna",
|
||||||
"filename": "ggml-vicuna-7b-1.1-q4_2.bin",
|
"filename": "ggml-vicuna-7b-1.1-q4_2.bin",
|
||||||
"filesize": "4212859520",
|
"filesize": "4212859520",
|
||||||
"description": "LLaMA 7B finetuned by teams from UC Berkeley, CMU, Stanford, MBZUAI, and UC San Diego.<br>- Cannot be used commercially."
|
"ramrequired": "8",
|
||||||
|
"parameters": "7 billion",
|
||||||
|
"quant": "q4_2",
|
||||||
|
"type": "LLaMA",
|
||||||
|
"description": "
|
||||||
|
<strong>Good small model - trained by teams from UC Berkeley, CMU, Stanford, MBZUAI, and UC San Diego</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Instruction based
|
||||||
|
<li>Cannot be used commercially
|
||||||
|
</ul>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"order": "f",
|
||||||
"md5sum": "95999b7b0699e2070af63bf5d34101a8",
|
"md5sum": "95999b7b0699e2070af63bf5d34101a8",
|
||||||
|
"name": "Vicuna (large)",
|
||||||
"filename": "ggml-vicuna-13b-1.1-q4_2.bin",
|
"filename": "ggml-vicuna-13b-1.1-q4_2.bin",
|
||||||
"filesize": "8136770688",
|
"filesize": "8136770688",
|
||||||
"description": "LLaMA 13B trained by teams from UC Berkeley, CMU, Stanford, MBZUAI, and UC San Diego.<br>- Cannot be used commercially."
|
"ramrequired": "16",
|
||||||
|
"parameters": "13 billion",
|
||||||
|
"quant": "q4_2",
|
||||||
|
"type": "LLaMA",
|
||||||
|
"description": "
|
||||||
|
<strong>Good larger model - trained by teams from UC Berkeley, CMU, Stanford, MBZUAI, and UC San Diego</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Instruction based
|
||||||
|
<li>Cannot be used commercially
|
||||||
|
</ul>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"order": "g",
|
||||||
"md5sum": "99e6d129745a3f1fb1121abed747b05a",
|
"md5sum": "99e6d129745a3f1fb1121abed747b05a",
|
||||||
|
"name": "Wizard",
|
||||||
"filename": "ggml-wizardLM-7B.q4_2.bin",
|
"filename": "ggml-wizardLM-7B.q4_2.bin",
|
||||||
"filesize": "4212864640",
|
"filesize": "4212864640",
|
||||||
"description": "LLaMA 7B finetuned by Microsoft and Peking University.<br>- Cannot be used commercially."
|
"ramrequired": "8",
|
||||||
|
"parameters": "7 billion",
|
||||||
|
"quant": "q4_2",
|
||||||
|
"type": "LLaMA",
|
||||||
|
"description": "
|
||||||
|
<strong>Good small model - trained by by Microsoft and Peking University</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Instruction based
|
||||||
|
<li>Cannot be used commercially
|
||||||
|
</ul>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"order": "h",
|
||||||
"md5sum": "6cb4ee297537c9133bddab9692879de0",
|
"md5sum": "6cb4ee297537c9133bddab9692879de0",
|
||||||
|
"name": "Stable Vicuna",
|
||||||
"filename": "ggml-stable-vicuna-13B.q4_2.bin",
|
"filename": "ggml-stable-vicuna-13B.q4_2.bin",
|
||||||
"filesize": "8136777088",
|
"filesize": "8136777088",
|
||||||
"description": "LLaMa 13B finetuned with RLHF by Stability AI.<br>- Cannot be used commercially."
|
"ramrequired": "16",
|
||||||
},
|
"parameters": "13 billion",
|
||||||
{
|
"quant": "q4_2",
|
||||||
"md5sum": "120c32a51d020066288df045ef5d52b9",
|
"type": "LLaMA",
|
||||||
"filename": "ggml-mpt-7b-base.bin",
|
"description": "
|
||||||
"filesize": "4854401028",
|
<strong>Trained with RHLF by Stability AI</strong>
|
||||||
"requires": "2.4.1",
|
<br>
|
||||||
"description": "MPT 7B pre-trained by Mosaic ML. Trained for text completion with no assistant finetuning.<br>- Licensed for commercial use."
|
<ul>
|
||||||
},
|
<li>Instruction based
|
||||||
{
|
<li>Cannot be used commercially
|
||||||
"md5sum": "d5eafd5b0bd0d615cfd5fd763f642dfe",
|
</ul>"
|
||||||
"filename": "ggml-nous-gpt4-vicuna-13b.bin",
|
|
||||||
"filesize": "8136777088",
|
|
||||||
"description": "LLaMa 13B fine-tuned on ~180,000 instructions by Nous Research.<br>- Cannot be used commercially."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"order": "i",
|
||||||
"md5sum": "1cfa4958f489f0a0d1ffdf6b37322809",
|
"md5sum": "1cfa4958f489f0a0d1ffdf6b37322809",
|
||||||
|
"name": "MPT Instruct",
|
||||||
"filename": "ggml-mpt-7b-instruct.bin",
|
"filename": "ggml-mpt-7b-instruct.bin",
|
||||||
"filesize": "4854401028",
|
"filesize": "4854401028",
|
||||||
"requires": "2.4.1",
|
"requires": "2.4.1",
|
||||||
"description": "MPT 7B instruction finetuned by Mosaic ML.<br>- Licensed for commercial use."
|
"ramrequired": "8",
|
||||||
|
"parameters": "7 billion",
|
||||||
|
"quant": "q4_0",
|
||||||
|
"type": "MPT",
|
||||||
|
"description": "
|
||||||
|
<strong>Mosaic's instruction model</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Instruction based
|
||||||
|
<li>Trained by Mosaic ML
|
||||||
|
<li>Licensed for commercial use
|
||||||
|
</ul>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"order": "j",
|
||||||
|
"md5sum": "120c32a51d020066288df045ef5d52b9",
|
||||||
|
"name": "MPT Base",
|
||||||
|
"filename": "ggml-mpt-7b-base.bin",
|
||||||
|
"filesize": "4854401028",
|
||||||
|
"requires": "2.4.1",
|
||||||
|
"ramrequired": "8",
|
||||||
|
"parameters": "7 billion",
|
||||||
|
"quant": "q4_0",
|
||||||
|
"type": "MPT",
|
||||||
|
"description": "
|
||||||
|
<strong>Trained for text completion with no assistant finetuning</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Completion based
|
||||||
|
<li>Trained by Mosaic ML
|
||||||
|
<li>Licensed for commercial use
|
||||||
|
</ul>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"order": "k",
|
||||||
|
"md5sum": "d5eafd5b0bd0d615cfd5fd763f642dfe",
|
||||||
|
"name": "Nous Vicuna",
|
||||||
|
"filename": "ggml-nous-gpt4-vicuna-13b.bin",
|
||||||
|
"filesize": "8136777088",
|
||||||
|
"ramrequired": "16",
|
||||||
|
"parameters": "13 billion",
|
||||||
|
"quant": "q4_0",
|
||||||
|
"type": "LLaMA",
|
||||||
|
"description": "
|
||||||
|
<strong>Trained on ~180,000 instructions</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Instruction based
|
||||||
|
<li>Trained by Nous Research
|
||||||
|
<li>Cannot be used commercially
|
||||||
|
</ul>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"order": "l",
|
||||||
"md5sum": "489d21fd48840dcb31e5f92f453f3a20",
|
"md5sum": "489d21fd48840dcb31e5f92f453f3a20",
|
||||||
|
"name": "Wizard Uncensored",
|
||||||
"filename": "wizardLM-13B-Uncensored.ggmlv3.q4_0.bin",
|
"filename": "wizardLM-13B-Uncensored.ggmlv3.q4_0.bin",
|
||||||
"filesize": "8136777088",
|
"filesize": "8136777088",
|
||||||
"requires": "2.4.7",
|
"requires": "2.4.7",
|
||||||
"description": "LLaMa 13B finetuned on the uncensored assistant and instruction data.<br>- Cannot be used commercially.",
|
"ramrequired": "16",
|
||||||
|
"parameters": "13 billion",
|
||||||
|
"quant": "q4_0",
|
||||||
|
"type": "LLaMA",
|
||||||
|
"description": "
|
||||||
|
<strong>Trained on uncensored assistant data and instruction data</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Instruction based
|
||||||
|
<li>Cannot be used commercially
|
||||||
|
</ul>",
|
||||||
"url": "https://huggingface.co/TheBloke/WizardLM-13B-Uncensored-GGML/resolve/main/wizardLM-13B-Uncensored.ggmlv3.q4_0.bin"
|
"url": "https://huggingface.co/TheBloke/WizardLM-13B-Uncensored-GGML/resolve/main/wizardLM-13B-Uncensored.ggmlv3.q4_0.bin"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"order": "m",
|
||||||
"md5sum": "615890cb571fcaa0f70b2f8d15ef809e",
|
"md5sum": "615890cb571fcaa0f70b2f8d15ef809e",
|
||||||
|
"disableGUI": "true",
|
||||||
|
"name": "Replit",
|
||||||
"filename": "ggml-replit-code-v1-3b.bin",
|
"filename": "ggml-replit-code-v1-3b.bin",
|
||||||
"filesize": "5202046853",
|
"filesize": "5202046853",
|
||||||
"requires": "2.4.7",
|
"requires": "2.4.7",
|
||||||
"description": "Replit 3B code model trained on subset of the Stack.<br>- Licensed for commercial use.",
|
"ramrequired": "4",
|
||||||
|
"parameters": "3 billion",
|
||||||
|
"quant": "f16",
|
||||||
|
"type": "Replit",
|
||||||
|
"description": "
|
||||||
|
<strong>Trained on subset of the Stack</strong>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>Code completion based
|
||||||
|
<li>Licensed for commercial use
|
||||||
|
</ul>",
|
||||||
"url": "https://huggingface.co/nomic-ai/ggml-replit-code-v1-3b/resolve/main/ggml-replit-code-v1-3b.bin"
|
"url": "https://huggingface.co/nomic-ai/ggml-replit-code-v1-3b/resolve/main/ggml-replit-code-v1-3b.bin"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
484
gpt4all-chat/modellist.cpp
Normal file
484
gpt4all-chat/modellist.cpp
Normal file
@ -0,0 +1,484 @@
|
|||||||
|
#include "modellist.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
bool InstalledModels::filterAcceptsRow(int sourceRow,
|
||||||
|
const QModelIndex &sourceParent) const
|
||||||
|
{
|
||||||
|
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||||
|
bool isInstalled = sourceModel()->data(index, ModelList::InstalledRole).toBool();
|
||||||
|
return isInstalled;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadableModels::DownloadableModels(QObject *parent)
|
||||||
|
: QSortFilterProxyModel(parent)
|
||||||
|
, m_expanded(false)
|
||||||
|
, m_limit(5)
|
||||||
|
{
|
||||||
|
connect(this, &DownloadableModels::rowsInserted, this, &DownloadableModels::countChanged);
|
||||||
|
connect(this, &DownloadableModels::rowsRemoved, this, &DownloadableModels::countChanged);
|
||||||
|
connect(this, &DownloadableModels::modelReset, this, &DownloadableModels::countChanged);
|
||||||
|
connect(this, &DownloadableModels::layoutChanged, this, &DownloadableModels::countChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DownloadableModels::filterAcceptsRow(int sourceRow,
|
||||||
|
const QModelIndex &sourceParent) const
|
||||||
|
{
|
||||||
|
bool withinLimit = sourceRow < (m_expanded ? sourceModel()->rowCount() : m_limit);
|
||||||
|
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||||
|
bool isDownloadable = !sourceModel()->data(index, ModelList::DescriptionRole).toString().isEmpty();
|
||||||
|
bool showInGUI = !sourceModel()->data(index, ModelList::DisableGUIRole).toBool();
|
||||||
|
return withinLimit && isDownloadable && showInGUI;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DownloadableModels::count() const
|
||||||
|
{
|
||||||
|
return rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DownloadableModels::isExpanded() const
|
||||||
|
{
|
||||||
|
return m_expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadableModels::setExpanded(bool expanded)
|
||||||
|
{
|
||||||
|
if (m_expanded != expanded) {
|
||||||
|
m_expanded = expanded;
|
||||||
|
invalidateFilter();
|
||||||
|
emit expandedChanged(m_expanded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyModelList: public ModelList { };
|
||||||
|
Q_GLOBAL_STATIC(MyModelList, modelListInstance)
|
||||||
|
ModelList *ModelList::globalInstance()
|
||||||
|
{
|
||||||
|
return modelListInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelList::ModelList()
|
||||||
|
: QAbstractListModel(nullptr)
|
||||||
|
, m_installedModels(new InstalledModels(this))
|
||||||
|
, m_downloadableModels(new DownloadableModels(this))
|
||||||
|
{
|
||||||
|
m_installedModels->setSourceModel(this);
|
||||||
|
m_downloadableModels->setSourceModel(this);
|
||||||
|
m_watcher = new QFileSystemWatcher(this);
|
||||||
|
QSettings settings;
|
||||||
|
settings.sync();
|
||||||
|
m_localModelsPath = settings.value("modelPath", defaultLocalModelsPath()).toString();
|
||||||
|
const QString exePath = QCoreApplication::applicationDirPath() + QDir::separator();
|
||||||
|
m_watcher->addPath(exePath);
|
||||||
|
m_watcher->addPath(m_localModelsPath);
|
||||||
|
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &ModelList::updateModelsFromDirectory);
|
||||||
|
updateModelsFromDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ModelList::incompleteDownloadPath(const QString &modelFile)
|
||||||
|
{
|
||||||
|
return localModelsPath() + "incomplete-" + modelFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<ModelInfo> ModelList::exportModelList() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
QList<ModelInfo> infos;
|
||||||
|
for (ModelInfo *info : m_models)
|
||||||
|
if (info->installed)
|
||||||
|
infos.append(*info);
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<QString> ModelList::userDefaultModelList() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
settings.sync();
|
||||||
|
|
||||||
|
const QString userDefaultModelName = settings.value("userDefaultModel").toString();
|
||||||
|
QList<QString> models;
|
||||||
|
bool foundUserDefault = false;
|
||||||
|
for (ModelInfo *info : m_models) {
|
||||||
|
if (info->installed && (info->name == userDefaultModelName || info->filename == userDefaultModelName)) {
|
||||||
|
foundUserDefault = true;
|
||||||
|
models.prepend(info->name.isEmpty() ? info->filename : info->name);
|
||||||
|
} else if (info->installed) {
|
||||||
|
models.append(info->name.isEmpty() ? info->filename : info->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString defaultFileName = "Application default";
|
||||||
|
if (foundUserDefault)
|
||||||
|
models.append(defaultFileName);
|
||||||
|
else
|
||||||
|
models.prepend(defaultFileName);
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelInfo ModelList::defaultModelInfo() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
settings.sync();
|
||||||
|
|
||||||
|
// The user default model can be set by the user in the settings dialog. The "default" user
|
||||||
|
// default model is "Application default" which signals we should use the default model that was
|
||||||
|
// specified by the models.json file.
|
||||||
|
const QString defaultModelName = settings.value("userDefaultModel").toString();
|
||||||
|
const bool hasDefaultName = !defaultModelName.isEmpty() && defaultModelName != "Application default";
|
||||||
|
ModelInfo *defaultModel = nullptr;
|
||||||
|
for (ModelInfo *info : m_models) {
|
||||||
|
if (!info->installed)
|
||||||
|
continue;
|
||||||
|
defaultModel = info;
|
||||||
|
if (!hasDefaultName && defaultModel->isDefault) break;
|
||||||
|
if (hasDefaultName && (defaultModel->name == defaultModelName || defaultModel->filename == defaultModelName)) break;
|
||||||
|
}
|
||||||
|
if (defaultModel)
|
||||||
|
return *defaultModel;
|
||||||
|
return ModelInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModelList::contains(const QString &filename) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
return m_modelMap.contains(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModelList::lessThan(const ModelInfo* a, const ModelInfo* b)
|
||||||
|
{
|
||||||
|
// Rule 1: Non-empty 'order' before empty
|
||||||
|
if (a->order.isEmpty() != b->order.isEmpty()) {
|
||||||
|
return !a->order.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule 2: Both 'order' are non-empty, sort alphanumerically
|
||||||
|
if (!a->order.isEmpty() && !b->order.isEmpty()) {
|
||||||
|
return a->order < b->order;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule 3: Both 'order' are empty, sort by filename
|
||||||
|
return a->filename < b->filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelList::addModel(const QString &filename)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
Q_ASSERT(!m_modelMap.contains(filename));
|
||||||
|
if (m_modelMap.contains(filename)) {
|
||||||
|
qWarning() << "ERROR: model list already contains" << filename;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginInsertRows(QModelIndex(), m_models.size(), m_models.size());
|
||||||
|
ModelInfo *info = new ModelInfo;
|
||||||
|
info->filename = filename;
|
||||||
|
m_models.append(info);
|
||||||
|
m_modelMap.insert(filename, info);
|
||||||
|
endInsertRows();
|
||||||
|
|
||||||
|
std::stable_sort(m_models.begin(), m_models.end(), ModelList::lessThan);
|
||||||
|
emit dataChanged(index(0, 0), index(m_models.size() - 1, 0));
|
||||||
|
|
||||||
|
emit userDefaultModelListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ModelList::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent)
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
return m_models.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ModelList::dataInternal(const ModelInfo *info, int role) const
|
||||||
|
{
|
||||||
|
switch (role) {
|
||||||
|
case NameRole:
|
||||||
|
return info->name;
|
||||||
|
case FilenameRole:
|
||||||
|
return info->filename;
|
||||||
|
case DirpathRole:
|
||||||
|
return info->dirpath;
|
||||||
|
case FilesizeRole:
|
||||||
|
return info->filesize;
|
||||||
|
case Md5sumRole:
|
||||||
|
return info->md5sum;
|
||||||
|
case CalcHashRole:
|
||||||
|
return info->calcHash;
|
||||||
|
case InstalledRole:
|
||||||
|
return info->installed;
|
||||||
|
case DefaultRole:
|
||||||
|
return info->isDefault;
|
||||||
|
case ChatGPTRole:
|
||||||
|
return info->isChatGPT;
|
||||||
|
case DisableGUIRole:
|
||||||
|
return info->disableGUI;
|
||||||
|
case DescriptionRole:
|
||||||
|
return info->description;
|
||||||
|
case RequiresVersionRole:
|
||||||
|
return info->requiresVersion;
|
||||||
|
case DeprecatedVersionRole:
|
||||||
|
return info->deprecatedVersion;
|
||||||
|
case UrlRole:
|
||||||
|
return info->url;
|
||||||
|
case BytesReceivedRole:
|
||||||
|
return info->bytesReceived;
|
||||||
|
case BytesTotalRole:
|
||||||
|
return info->bytesTotal;
|
||||||
|
case TimestampRole:
|
||||||
|
return info->timestamp;
|
||||||
|
case SpeedRole:
|
||||||
|
return info->speed;
|
||||||
|
case DownloadingRole:
|
||||||
|
return info->isDownloading;
|
||||||
|
case IncompleteRole:
|
||||||
|
return info->isIncomplete;
|
||||||
|
case DownloadErrorRole:
|
||||||
|
return info->downloadError;
|
||||||
|
case OrderRole:
|
||||||
|
return info->order;
|
||||||
|
case RamrequiredRole:
|
||||||
|
return info->ramrequired;
|
||||||
|
case ParametersRole:
|
||||||
|
return info->parameters;
|
||||||
|
case QuantRole:
|
||||||
|
return info->quant;
|
||||||
|
case TypeRole:
|
||||||
|
return info->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ModelList::data(const QString &filename, int role) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
ModelInfo *info = m_modelMap.value(filename);
|
||||||
|
return dataInternal(info, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ModelList::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
if (!index.isValid() || index.row() < 0 || index.row() >= m_models.size())
|
||||||
|
return QVariant();
|
||||||
|
const ModelInfo *info = m_models.at(index.row());
|
||||||
|
return dataInternal(info, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelList::updateData(const QString &filename, int role, const QVariant &value)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
if (!m_modelMap.contains(filename)) {
|
||||||
|
qWarning() << "ERROR: cannot update as model map does not contain" << filename;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelInfo *info = m_modelMap.value(filename);
|
||||||
|
const int index = m_models.indexOf(info);
|
||||||
|
if (index == -1) {
|
||||||
|
qWarning() << "ERROR: cannot update as model list does not contain" << filename;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case NameRole:
|
||||||
|
info->name = value.toString(); break;
|
||||||
|
case FilenameRole:
|
||||||
|
info->filename = value.toString(); break;
|
||||||
|
case DirpathRole:
|
||||||
|
info->dirpath = value.toString(); break;
|
||||||
|
case FilesizeRole:
|
||||||
|
info->filesize = value.toString(); break;
|
||||||
|
case Md5sumRole:
|
||||||
|
info->md5sum = value.toByteArray(); break;
|
||||||
|
case CalcHashRole:
|
||||||
|
info->calcHash = value.toBool(); break;
|
||||||
|
case InstalledRole:
|
||||||
|
info->installed = value.toBool(); break;
|
||||||
|
case DefaultRole:
|
||||||
|
info->isDefault = value.toBool(); break;
|
||||||
|
case ChatGPTRole:
|
||||||
|
info->isChatGPT = value.toBool(); break;
|
||||||
|
case DisableGUIRole:
|
||||||
|
info->disableGUI = value.toBool(); break;
|
||||||
|
case DescriptionRole:
|
||||||
|
info->description = value.toString(); break;
|
||||||
|
case RequiresVersionRole:
|
||||||
|
info->requiresVersion = value.toString(); break;
|
||||||
|
case DeprecatedVersionRole:
|
||||||
|
info->deprecatedVersion = value.toString(); break;
|
||||||
|
case UrlRole:
|
||||||
|
info->url = value.toString(); break;
|
||||||
|
case BytesReceivedRole:
|
||||||
|
info->bytesReceived = value.toLongLong(); break;
|
||||||
|
case BytesTotalRole:
|
||||||
|
info->bytesTotal = value.toLongLong(); break;
|
||||||
|
case TimestampRole:
|
||||||
|
info->timestamp = value.toLongLong(); break;
|
||||||
|
case SpeedRole:
|
||||||
|
info->speed = value.toString(); break;
|
||||||
|
case DownloadingRole:
|
||||||
|
info->isDownloading = value.toBool(); break;
|
||||||
|
case IncompleteRole:
|
||||||
|
info->isIncomplete = value.toBool(); break;
|
||||||
|
case DownloadErrorRole:
|
||||||
|
info->downloadError = value.toString(); break;
|
||||||
|
case OrderRole:
|
||||||
|
info->order = value.toString(); break;
|
||||||
|
case RamrequiredRole:
|
||||||
|
info->ramrequired = value.toInt(); break;
|
||||||
|
case ParametersRole:
|
||||||
|
info->parameters = value.toString(); break;
|
||||||
|
case QuantRole:
|
||||||
|
info->quant = value.toString(); break;
|
||||||
|
case TypeRole:
|
||||||
|
info->type = value.toString(); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra guarantee that these always remains in sync with filesystem
|
||||||
|
QFileInfo fileInfo(info->dirpath + info->filename);
|
||||||
|
if (info->installed != fileInfo.exists()) {
|
||||||
|
info->installed = fileInfo.exists();
|
||||||
|
emit dataChanged(createIndex(index, 0), createIndex(index, 0), {InstalledRole});
|
||||||
|
}
|
||||||
|
QFileInfo incompleteInfo(incompleteDownloadPath(info->filename));
|
||||||
|
if (info->isIncomplete != incompleteInfo.exists()) {
|
||||||
|
info->isIncomplete = incompleteInfo.exists();
|
||||||
|
emit dataChanged(createIndex(index, 0), createIndex(index, 0), {IncompleteRole});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stable_sort(m_models.begin(), m_models.end(), ModelList::lessThan);
|
||||||
|
emit dataChanged(createIndex(0, 0), createIndex(m_models.size() - 1, 0));
|
||||||
|
emit userDefaultModelListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelInfo ModelList::modelInfo(const QString &filename) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
if (!m_modelMap.contains(filename))
|
||||||
|
return ModelInfo();
|
||||||
|
return *m_modelMap.value(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ModelList::defaultLocalModelsPath() const
|
||||||
|
{
|
||||||
|
QString localPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)
|
||||||
|
+ "/";
|
||||||
|
QString testWritePath = localPath + QString("test_write.txt");
|
||||||
|
QString canonicalLocalPath = QFileInfo(localPath).canonicalFilePath() + "/";
|
||||||
|
QDir localDir(localPath);
|
||||||
|
if (!localDir.exists()) {
|
||||||
|
if (!localDir.mkpath(localPath)) {
|
||||||
|
qWarning() << "ERROR: Local download directory can't be created:" << canonicalLocalPath;
|
||||||
|
return canonicalLocalPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QFileInfo::exists(testWritePath))
|
||||||
|
return canonicalLocalPath;
|
||||||
|
|
||||||
|
QFile testWriteFile(testWritePath);
|
||||||
|
if (testWriteFile.open(QIODeviceBase::ReadWrite)) {
|
||||||
|
testWriteFile.close();
|
||||||
|
return canonicalLocalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "ERROR: Local download path appears not writeable:" << canonicalLocalPath;
|
||||||
|
return canonicalLocalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ModelList::localModelsPath() const
|
||||||
|
{
|
||||||
|
return m_localModelsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelList::setLocalModelsPath(const QString &modelPath)
|
||||||
|
{
|
||||||
|
QString filePath = (modelPath.startsWith("file://") ?
|
||||||
|
QUrl(modelPath).toLocalFile() : modelPath);
|
||||||
|
QString canonical = QFileInfo(filePath).canonicalFilePath() + "/";
|
||||||
|
if (m_localModelsPath != canonical) {
|
||||||
|
m_localModelsPath = canonical;
|
||||||
|
emit localModelsPathChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ModelList::modelDirPath(const QString &modelName, bool isChatGPT)
|
||||||
|
{
|
||||||
|
QVector<QString> possibleFilePaths;
|
||||||
|
if (isChatGPT)
|
||||||
|
possibleFilePaths << "/" + modelName + ".txt";
|
||||||
|
else {
|
||||||
|
possibleFilePaths << "/ggml-" + modelName + ".bin";
|
||||||
|
possibleFilePaths << "/" + modelName + ".bin";
|
||||||
|
}
|
||||||
|
for (const QString &modelFilename : possibleFilePaths) {
|
||||||
|
QString appPath = QCoreApplication::applicationDirPath() + modelFilename;
|
||||||
|
QFileInfo infoAppPath(appPath);
|
||||||
|
if (infoAppPath.exists())
|
||||||
|
return QCoreApplication::applicationDirPath();
|
||||||
|
|
||||||
|
QString downloadPath = localModelsPath() + modelFilename;
|
||||||
|
QFileInfo infoLocalPath(downloadPath);
|
||||||
|
if (infoLocalPath.exists())
|
||||||
|
return localModelsPath();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelList::updateModelsFromDirectory()
|
||||||
|
{
|
||||||
|
const QString exePath = QCoreApplication::applicationDirPath() + QDir::separator();
|
||||||
|
const QString localPath = localModelsPath();
|
||||||
|
{
|
||||||
|
QDir dir(exePath);
|
||||||
|
QStringList allFiles = dir.entryList(QDir::Files);
|
||||||
|
|
||||||
|
// All files that end with .bin and have 'ggml' somewhere in the name
|
||||||
|
QStringList fileNames;
|
||||||
|
for(const QString& filename : allFiles) {
|
||||||
|
if (filename.endsWith(".bin") && filename.contains("ggml")) {
|
||||||
|
fileNames.append(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString& f : fileNames) {
|
||||||
|
QString filePath = exePath + f;
|
||||||
|
QFileInfo info(filePath);
|
||||||
|
if (!info.exists())
|
||||||
|
continue;
|
||||||
|
if (!contains(f))
|
||||||
|
addModel(f);
|
||||||
|
updateData(f, DirpathRole, exePath);
|
||||||
|
updateData(f, FilesizeRole, toFileSize(info.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localPath != exePath) {
|
||||||
|
QDir dir(localPath);
|
||||||
|
QStringList allFiles = dir.entryList(QDir::Files);
|
||||||
|
QStringList fileNames;
|
||||||
|
for(const QString& filename : allFiles) {
|
||||||
|
if ((filename.endsWith(".bin") && filename.contains("ggml"))
|
||||||
|
|| (filename.endsWith(".txt") && filename.startsWith("chatgpt-"))) {
|
||||||
|
fileNames.append(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString& f : fileNames) {
|
||||||
|
QString filePath = localPath + f;
|
||||||
|
QFileInfo info(filePath);
|
||||||
|
if (!info.exists())
|
||||||
|
continue;
|
||||||
|
if (!contains(f))
|
||||||
|
addModel(f);
|
||||||
|
updateData(f, ChatGPTRole, f.startsWith("chatgpt-"));
|
||||||
|
updateData(f, DirpathRole, localPath);
|
||||||
|
updateData(f, FilesizeRole, toFileSize(info.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
246
gpt4all-chat/modellist.h
Normal file
246
gpt4all-chat/modellist.h
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
#ifndef MODELLIST_H
|
||||||
|
#define MODELLIST_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QtQml>
|
||||||
|
|
||||||
|
struct ModelInfo {
|
||||||
|
Q_GADGET
|
||||||
|
Q_PROPERTY(QString name MEMBER name)
|
||||||
|
Q_PROPERTY(QString filename MEMBER filename)
|
||||||
|
Q_PROPERTY(QString dirpath MEMBER dirpath)
|
||||||
|
Q_PROPERTY(QString filesize MEMBER filesize)
|
||||||
|
Q_PROPERTY(QByteArray md5sum MEMBER md5sum)
|
||||||
|
Q_PROPERTY(bool calcHash MEMBER calcHash)
|
||||||
|
Q_PROPERTY(bool installed MEMBER installed)
|
||||||
|
Q_PROPERTY(bool isDefault MEMBER isDefault)
|
||||||
|
Q_PROPERTY(bool disableGUI MEMBER disableGUI)
|
||||||
|
Q_PROPERTY(bool isChatGPT MEMBER isChatGPT)
|
||||||
|
Q_PROPERTY(QString description MEMBER description)
|
||||||
|
Q_PROPERTY(QString requiresVersion MEMBER requiresVersion)
|
||||||
|
Q_PROPERTY(QString deprecatedVersion MEMBER deprecatedVersion)
|
||||||
|
Q_PROPERTY(QString url MEMBER url)
|
||||||
|
Q_PROPERTY(qint64 bytesReceived MEMBER bytesReceived)
|
||||||
|
Q_PROPERTY(qint64 bytesTotal MEMBER bytesTotal)
|
||||||
|
Q_PROPERTY(qint64 timestamp MEMBER timestamp)
|
||||||
|
Q_PROPERTY(QString speed MEMBER speed)
|
||||||
|
Q_PROPERTY(bool isDownloading MEMBER isDownloading)
|
||||||
|
Q_PROPERTY(bool isIncomplete MEMBER isIncomplete)
|
||||||
|
Q_PROPERTY(QString downloadError MEMBER downloadError)
|
||||||
|
Q_PROPERTY(QString order MEMBER order)
|
||||||
|
Q_PROPERTY(int ramrequired MEMBER ramrequired)
|
||||||
|
Q_PROPERTY(QString parameters MEMBER parameters)
|
||||||
|
Q_PROPERTY(QString quant MEMBER quant)
|
||||||
|
Q_PROPERTY(QString type MEMBER type)
|
||||||
|
|
||||||
|
public:
|
||||||
|
QString name;
|
||||||
|
QString filename;
|
||||||
|
QString dirpath;
|
||||||
|
QString filesize;
|
||||||
|
QByteArray md5sum;
|
||||||
|
bool calcHash = false;
|
||||||
|
bool installed = false;
|
||||||
|
bool isDefault = false;
|
||||||
|
bool isChatGPT = false;
|
||||||
|
bool disableGUI = false;
|
||||||
|
QString description;
|
||||||
|
QString requiresVersion;
|
||||||
|
QString deprecatedVersion;
|
||||||
|
QString url;
|
||||||
|
qint64 bytesReceived = 0;
|
||||||
|
qint64 bytesTotal = 0;
|
||||||
|
qint64 timestamp = 0;
|
||||||
|
QString speed;
|
||||||
|
bool isDownloading = false;
|
||||||
|
bool isIncomplete = false;
|
||||||
|
QString downloadError;
|
||||||
|
QString order;
|
||||||
|
int ramrequired = 0;
|
||||||
|
QString parameters;
|
||||||
|
QString quant;
|
||||||
|
QString type;
|
||||||
|
bool operator==(const ModelInfo &other) const {
|
||||||
|
return filename == other.filename;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Q_DECLARE_METATYPE(ModelInfo)
|
||||||
|
|
||||||
|
class InstalledModels : public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit InstalledModels(QObject *parent) : QSortFilterProxyModel(parent) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DownloadableModels : public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||||
|
Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged)
|
||||||
|
public:
|
||||||
|
explicit DownloadableModels(QObject *parent);
|
||||||
|
int count() const;
|
||||||
|
|
||||||
|
bool isExpanded() const;
|
||||||
|
void setExpanded(bool expanded);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void countChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void expandedChanged(bool expanded);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_expanded;
|
||||||
|
int m_limit;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModelList : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||||
|
Q_PROPERTY(QString localModelsPath READ localModelsPath WRITE setLocalModelsPath NOTIFY localModelsPathChanged)
|
||||||
|
Q_PROPERTY(InstalledModels* installedModels READ installedModels NOTIFY installedModelsChanged)
|
||||||
|
Q_PROPERTY(DownloadableModels* downloadableModels READ downloadableModels NOTIFY downloadableModelsChanged)
|
||||||
|
Q_PROPERTY(QList<QString> userDefaultModelList READ userDefaultModelList NOTIFY userDefaultModelListChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ModelList *globalInstance();
|
||||||
|
|
||||||
|
enum Roles {
|
||||||
|
NameRole = Qt::UserRole + 1,
|
||||||
|
FilenameRole,
|
||||||
|
DirpathRole,
|
||||||
|
FilesizeRole,
|
||||||
|
Md5sumRole,
|
||||||
|
CalcHashRole,
|
||||||
|
InstalledRole,
|
||||||
|
DefaultRole,
|
||||||
|
ChatGPTRole,
|
||||||
|
DisableGUIRole,
|
||||||
|
DescriptionRole,
|
||||||
|
RequiresVersionRole,
|
||||||
|
DeprecatedVersionRole,
|
||||||
|
UrlRole,
|
||||||
|
BytesReceivedRole,
|
||||||
|
BytesTotalRole,
|
||||||
|
TimestampRole,
|
||||||
|
SpeedRole,
|
||||||
|
DownloadingRole,
|
||||||
|
IncompleteRole,
|
||||||
|
DownloadErrorRole,
|
||||||
|
OrderRole,
|
||||||
|
RamrequiredRole,
|
||||||
|
ParametersRole,
|
||||||
|
QuantRole,
|
||||||
|
TypeRole
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<int, QByteArray> roleNames() const override
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[NameRole] = "name";
|
||||||
|
roles[FilenameRole] = "filename";
|
||||||
|
roles[DirpathRole] = "dirpath";
|
||||||
|
roles[FilesizeRole] = "filesize";
|
||||||
|
roles[Md5sumRole] = "md5sum";
|
||||||
|
roles[CalcHashRole] = "calcHash";
|
||||||
|
roles[InstalledRole] = "installed";
|
||||||
|
roles[DefaultRole] = "isDefault";
|
||||||
|
roles[ChatGPTRole] = "isChatGPT";
|
||||||
|
roles[DisableGUIRole] = "disableGUI";
|
||||||
|
roles[DescriptionRole] = "description";
|
||||||
|
roles[RequiresVersionRole] = "requiresVersion";
|
||||||
|
roles[DeprecatedVersionRole] = "deprecatedVersion";
|
||||||
|
roles[UrlRole] = "url";
|
||||||
|
roles[BytesReceivedRole] = "bytesReceived";
|
||||||
|
roles[BytesTotalRole] = "bytesTotal";
|
||||||
|
roles[TimestampRole] = "timestamp";
|
||||||
|
roles[SpeedRole] = "speed";
|
||||||
|
roles[DownloadingRole] = "isDownloading";
|
||||||
|
roles[IncompleteRole] = "isIncomplete";
|
||||||
|
roles[DownloadErrorRole] = "downloadError";
|
||||||
|
roles[OrderRole] = "order";
|
||||||
|
roles[RamrequiredRole] = "ramrequired";
|
||||||
|
roles[ParametersRole] = "parameters";
|
||||||
|
roles[QuantRole] = "quant";
|
||||||
|
roles[TypeRole] = "type";
|
||||||
|
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);
|
||||||
|
|
||||||
|
int count() const { return m_models.size(); }
|
||||||
|
|
||||||
|
bool contains(const QString &filename) const;
|
||||||
|
Q_INVOKABLE ModelInfo modelInfo(const QString &filename) const;
|
||||||
|
ModelInfo defaultModelInfo() const;
|
||||||
|
|
||||||
|
void addModel(const QString &filename);
|
||||||
|
|
||||||
|
Q_INVOKABLE QString defaultLocalModelsPath() const;
|
||||||
|
Q_INVOKABLE QString localModelsPath() const;
|
||||||
|
Q_INVOKABLE void setLocalModelsPath(const QString &modelPath);
|
||||||
|
|
||||||
|
const QList<ModelInfo> exportModelList() const;
|
||||||
|
const QList<QString> userDefaultModelList() const;
|
||||||
|
|
||||||
|
InstalledModels *installedModels() const { return m_installedModels; }
|
||||||
|
DownloadableModels *downloadableModels() const { return m_downloadableModels; }
|
||||||
|
|
||||||
|
static inline QString toFileSize(quint64 sz) {
|
||||||
|
if (sz < 1024) {
|
||||||
|
return QString("%1 bytes").arg(sz);
|
||||||
|
} else if (sz < 1024 * 1024) {
|
||||||
|
return QString("%1 KB").arg(qreal(sz) / 1024, 0, 'g', 3);
|
||||||
|
} else if (sz < 1024 * 1024 * 1024) {
|
||||||
|
return QString("%1 MB").arg(qreal(sz) / (1024 * 1024), 0, 'g', 3);
|
||||||
|
} else {
|
||||||
|
return QString("%1 GB").arg(qreal(sz) / (1024 * 1024 * 1024), 0, 'g', 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString incompleteDownloadPath(const QString &modelFile);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void countChanged();
|
||||||
|
void localModelsPathChanged();
|
||||||
|
void installedModelsChanged();
|
||||||
|
void downloadableModelsChanged();
|
||||||
|
void userDefaultModelListChanged();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void updateModelsFromDirectory();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString modelDirPath(const QString &modelName, bool isChatGPT);
|
||||||
|
int indexForModel(ModelInfo *model);
|
||||||
|
QVariant dataInternal(const ModelInfo *info, int role) const;
|
||||||
|
static bool lessThan(const ModelInfo* a, const ModelInfo* b);
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable QRecursiveMutex m_mutex;
|
||||||
|
InstalledModels *m_installedModels;
|
||||||
|
DownloadableModels *m_downloadableModels;
|
||||||
|
QList<ModelInfo*> m_models;
|
||||||
|
QHash<QString, ModelInfo*> m_modelMap;
|
||||||
|
QString m_localModelsPath;
|
||||||
|
QFileSystemWatcher *m_watcher;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit ModelList();
|
||||||
|
~ModelList() {}
|
||||||
|
friend class MyModelList;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MODELLIST_H
|
@ -1,5 +1,5 @@
|
|||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "llm.h"
|
#include "chatlistmodel.h"
|
||||||
#include "sysinfo.h"
|
#include "sysinfo.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
@ -97,10 +97,10 @@ bool Network::packageAndSendJson(const QString &ingestId, const QString &json)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT(doc.isObject());
|
Q_ASSERT(doc.isObject());
|
||||||
Q_ASSERT(LLM::globalInstance()->chatListModel()->currentChat());
|
Q_ASSERT(ChatListModel::globalInstance()->currentChat());
|
||||||
QJsonObject object = doc.object();
|
QJsonObject object = doc.object();
|
||||||
object.insert("source", "gpt4all-chat");
|
object.insert("source", "gpt4all-chat");
|
||||||
object.insert("agent_id", LLM::globalInstance()->chatListModel()->currentChat()->modelName());
|
object.insert("agent_id", ChatListModel::globalInstance()->currentChat()->modelInfo().filename);
|
||||||
object.insert("submitter_id", m_uniqueId);
|
object.insert("submitter_id", m_uniqueId);
|
||||||
object.insert("ingest_id", ingestId);
|
object.insert("ingest_id", ingestId);
|
||||||
|
|
||||||
@ -394,7 +394,7 @@ void Network::sendMixpanelEvent(const QString &ev, const QVector<KeyValue> &valu
|
|||||||
if (!m_usageStatsActive)
|
if (!m_usageStatsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Q_ASSERT(LLM::globalInstance()->chatListModel()->currentChat());
|
Q_ASSERT(ChatListModel::globalInstance()->currentChat());
|
||||||
QJsonObject properties;
|
QJsonObject properties;
|
||||||
properties.insert("token", "ce362e568ddaee16ed243eaffb5860a2");
|
properties.insert("token", "ce362e568ddaee16ed243eaffb5860a2");
|
||||||
properties.insert("time", QDateTime::currentSecsSinceEpoch());
|
properties.insert("time", QDateTime::currentSecsSinceEpoch());
|
||||||
@ -405,13 +405,13 @@ void Network::sendMixpanelEvent(const QString &ev, const QVector<KeyValue> &valu
|
|||||||
properties.insert("ip", m_ipify);
|
properties.insert("ip", m_ipify);
|
||||||
properties.insert("name", QCoreApplication::applicationName() + " v"
|
properties.insert("name", QCoreApplication::applicationName() + " v"
|
||||||
+ QCoreApplication::applicationVersion());
|
+ QCoreApplication::applicationVersion());
|
||||||
properties.insert("model", LLM::globalInstance()->chatListModel()->currentChat()->modelName());
|
properties.insert("model", ChatListModel::globalInstance()->currentChat()->modelInfo().filename);
|
||||||
|
|
||||||
// Some additional startup information
|
// Some additional startup information
|
||||||
if (ev == "startup") {
|
if (ev == "startup") {
|
||||||
const QSize display = QGuiApplication::primaryScreen()->size();
|
const QSize display = QGuiApplication::primaryScreen()->size();
|
||||||
properties.insert("display", QString("%1x%2").arg(display.width()).arg(display.height()));
|
properties.insert("display", QString("%1x%2").arg(display.width()).arg(display.height()));
|
||||||
properties.insert("ram", getSystemTotalRAM());
|
properties.insert("ram", getSystemTotalRAMInGB());
|
||||||
#if defined(Q_OS_MAC)
|
#if defined(Q_OS_MAC)
|
||||||
properties.insert("cpu", QString::fromStdString(getCPUModel()));
|
properties.insert("cpu", QString::fromStdString(getCPUModel()));
|
||||||
#endif
|
#endif
|
||||||
|
@ -3,6 +3,7 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.Basic
|
import QtQuick.Controls.Basic
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import chatlistmodel
|
||||||
import llm
|
import llm
|
||||||
import download
|
import download
|
||||||
import network
|
import network
|
||||||
@ -48,8 +49,8 @@ Drawer {
|
|||||||
color: newChat.hovered ? theme.backgroundDark : theme.backgroundDarkest
|
color: newChat.hovered ? theme.backgroundDark : theme.backgroundDarkest
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
LLM.chatListModel.addChat();
|
ChatListModel.addChat();
|
||||||
Network.sendNewChat(LLM.chatListModel.count)
|
Network.sendNewChat(ChatListModel.count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,15 +70,15 @@ Drawer {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: 10
|
anchors.rightMargin: 10
|
||||||
|
|
||||||
model: LLM.chatListModel
|
model: ChatListModel
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
id: chatRectangle
|
id: chatRectangle
|
||||||
width: conversationList.width
|
width: conversationList.width
|
||||||
height: chatName.height
|
height: chatName.height
|
||||||
opacity: 0.9
|
opacity: 0.9
|
||||||
property bool isCurrent: LLM.chatListModel.currentChat === LLM.chatListModel.get(index)
|
property bool isCurrent: ChatListModel.currentChat === ChatListModel.get(index)
|
||||||
property bool isServer: LLM.chatListModel.get(index) && LLM.chatListModel.get(index).isServer
|
property bool isServer: ChatListModel.get(index) && ChatListModel.get(index).isServer
|
||||||
property bool trashQuestionDisplayed: false
|
property bool trashQuestionDisplayed: false
|
||||||
visible: !isServer || LLM.serverEnabled
|
visible: !isServer || LLM.serverEnabled
|
||||||
z: isCurrent ? 199 : 1
|
z: isCurrent ? 199 : 1
|
||||||
@ -119,7 +120,7 @@ Drawer {
|
|||||||
Network.sendRenameChat()
|
Network.sendRenameChat()
|
||||||
}
|
}
|
||||||
function changeName() {
|
function changeName() {
|
||||||
LLM.chatListModel.get(index).name = chatName.text
|
ChatListModel.get(index).name = chatName.text
|
||||||
chatName.focus = false
|
chatName.focus = false
|
||||||
chatName.readOnly = true
|
chatName.readOnly = true
|
||||||
chatName.selectByMouse = false
|
chatName.selectByMouse = false
|
||||||
@ -128,7 +129,7 @@ Drawer {
|
|||||||
onTapped: {
|
onTapped: {
|
||||||
if (isCurrent)
|
if (isCurrent)
|
||||||
return;
|
return;
|
||||||
LLM.chatListModel.currentChat = LLM.chatListModel.get(index);
|
ChatListModel.currentChat = ChatListModel.get(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Accessible.role: Accessible.Button
|
Accessible.role: Accessible.Button
|
||||||
@ -201,7 +202,7 @@ Drawer {
|
|||||||
color: "transparent"
|
color: "transparent"
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
LLM.chatListModel.removeChat(LLM.chatListModel.get(index))
|
ChatListModel.removeChat(ChatListModel.get(index))
|
||||||
Network.sendRemoveChat()
|
Network.sendRemoveChat()
|
||||||
}
|
}
|
||||||
Accessible.role: Accessible.Button
|
Accessible.role: Accessible.Button
|
||||||
|
@ -4,6 +4,7 @@ import QtQuick.Controls
|
|||||||
import QtQuick.Controls.Basic
|
import QtQuick.Controls.Basic
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Dialogs
|
import QtQuick.Dialogs
|
||||||
|
import chatlistmodel
|
||||||
import localdocs
|
import localdocs
|
||||||
import llm
|
import llm
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ Dialog {
|
|||||||
width: 480
|
width: 480
|
||||||
height: 640
|
height: 640
|
||||||
|
|
||||||
property var currentChat: LLM.chatListModel.currentChat
|
property var currentChat: ChatListModel.currentChat
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -4,15 +4,17 @@ import QtQuick.Controls
|
|||||||
import QtQuick.Controls.Basic
|
import QtQuick.Controls.Basic
|
||||||
import QtQuick.Dialogs
|
import QtQuick.Dialogs
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import chatlistmodel
|
||||||
import download
|
import download
|
||||||
import llm
|
import llm
|
||||||
|
import modellist
|
||||||
import network
|
import network
|
||||||
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: modelDownloaderDialog
|
id: modelDownloaderDialog
|
||||||
modal: true
|
modal: true
|
||||||
opacity: 0.9
|
opacity: 0.9
|
||||||
closePolicy: LLM.chatListModel.currentChat.modelList.length === 0 ? Popup.NoAutoClose : (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
|
closePolicy: ModelList.installedModels.count === 0 ? Popup.NoAutoClose : (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
|
||||||
padding: 20
|
padding: 20
|
||||||
bottomPadding: 30
|
bottomPadding: 30
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
@ -27,7 +29,7 @@ Dialog {
|
|||||||
Network.sendModelDownloaderDialog();
|
Network.sendModelDownloaderDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
property string defaultModelPath: Download.defaultLocalModelsPath()
|
property string defaultModelPath: ModelList.defaultLocalModelsPath()
|
||||||
property alias modelPath: settings.modelPath
|
property alias modelPath: settings.modelPath
|
||||||
Settings {
|
Settings {
|
||||||
id: settings
|
id: settings
|
||||||
@ -35,13 +37,19 @@ Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Download.downloadLocalModelsPath = settings.modelPath
|
ModelList.localModelsPath = settings.modelPath
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
settings.sync()
|
settings.sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PopupDialog {
|
||||||
|
id: downloadingErrorPopup
|
||||||
|
anchors.centerIn: parent
|
||||||
|
shouldTimeOut: false
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 20
|
anchors.margins: 20
|
||||||
@ -56,7 +64,7 @@ Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
visible: !Download.modelList.length
|
visible: !ModelList.downloadableModels.count
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
@ -73,112 +81,212 @@ Dialog {
|
|||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: modelList
|
id: modelListView
|
||||||
model: Download.modelList
|
model: ModelList.downloadableModels
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Rectangle {
|
||||||
id: delegateItem
|
id: delegateItem
|
||||||
width: modelList.width
|
width: modelListView.width
|
||||||
height: modelName.height + modelName.padding
|
height: childrenRect.height
|
||||||
+ description.height + description.padding
|
|
||||||
objectName: "delegateItem"
|
|
||||||
property bool downloading: false
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: index % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter
|
color: index % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter
|
||||||
}
|
|
||||||
|
GridLayout {
|
||||||
|
columns: 2
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: modelName
|
textFormat: Text.StyledText
|
||||||
objectName: "modelName"
|
text: "<h2>" + (name !== "" ? name : filename) + "</h2>"
|
||||||
property string filename: modelData.filename
|
Layout.row: 0
|
||||||
text: !modelData.isChatGPT ? (filename.startsWith("ggml-") ? filename.slice(5, filename.length - 4) : filename.slice(0, filename.length - 4)) : filename
|
Layout.column: 0
|
||||||
padding: 20
|
Layout.topMargin: 20
|
||||||
anchors.top: parent.top
|
Layout.leftMargin: 20
|
||||||
anchors.left: parent.left
|
Layout.columnSpan: 2
|
||||||
font.bold: modelData.isDefault || modelData.bestGPTJ || modelData.bestLlama || modelData.bestMPT
|
|
||||||
color: theme.assistantColor
|
color: theme.assistantColor
|
||||||
Accessible.role: Accessible.Paragraph
|
Accessible.role: Accessible.Paragraph
|
||||||
Accessible.name: qsTr("Model file")
|
Accessible.name: qsTr("Model file")
|
||||||
Accessible.description: qsTr("Model file to be downloaded")
|
Accessible.description: qsTr("Model file to be downloaded")
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Rectangle {
|
||||||
id: description
|
id: actionBox
|
||||||
text: " - " + modelData.description
|
width: childrenRect.width + 20
|
||||||
leftPadding: 20
|
color: theme.backgroundDark
|
||||||
rightPadding: 20
|
border.width: 1
|
||||||
anchors.top: modelName.bottom
|
radius: 10
|
||||||
anchors.left: modelName.left
|
Layout.row: 1
|
||||||
anchors.right: parent.right
|
Layout.column: 1
|
||||||
wrapMode: Text.WordWrap
|
Layout.rightMargin: 20
|
||||||
|
Layout.bottomMargin: 20
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.minimumHeight: childrenRect.height + 20
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
|
Layout.rowSpan: 2
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
MyButton {
|
||||||
|
id: downloadButton
|
||||||
|
text: isDownloading ? qsTr("Cancel") : isIncomplete ? qsTr("Resume") : qsTr("Download")
|
||||||
|
Layout.topMargin: 20
|
||||||
|
Layout.leftMargin: 20
|
||||||
|
Layout.minimumWidth: openaiKey.width
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
|
visible: !isChatGPT && !installed && !calcHash && downloadError === ""
|
||||||
|
Accessible.description: qsTr("Cancel/Resume/Download button to stop/restart/start the download")
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: downloadButton.down ? theme.backgroundLightest : theme.buttonBorder
|
||||||
|
border.width: 2
|
||||||
|
radius: 10
|
||||||
|
color: downloadButton.hovered ? theme.backgroundDark : theme.backgroundDarkest
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
if (!isDownloading) {
|
||||||
|
Download.downloadModel(filename);
|
||||||
|
} else {
|
||||||
|
Download.cancelDownload(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MyButton {
|
||||||
|
id: removeButton
|
||||||
|
text: qsTr("Remove")
|
||||||
|
Layout.topMargin: 20
|
||||||
|
Layout.leftMargin: 20
|
||||||
|
Layout.minimumWidth: openaiKey.width
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
|
visible: installed || downloadError !== ""
|
||||||
|
Accessible.description: qsTr("Remove button to remove model from filesystem")
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: removeButton.down ? theme.backgroundLightest : theme.buttonBorder
|
||||||
|
border.width: 2
|
||||||
|
radius: 10
|
||||||
|
color: removeButton.hovered ? theme.backgroundDark : theme.backgroundDarkest
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
Download.removeModel(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MyButton {
|
||||||
|
id: installButton
|
||||||
|
visible: !installed && isChatGPT
|
||||||
|
Layout.topMargin: 20
|
||||||
|
Layout.leftMargin: 20
|
||||||
|
Layout.minimumWidth: openaiKey.width
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
|
text: qsTr("Install")
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: installButton.down ? theme.backgroundLightest : theme.buttonBorder
|
||||||
|
border.width: 2
|
||||||
|
radius: 10
|
||||||
|
color: installButton.hovered ? theme.backgroundDark : theme.backgroundDarkest
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
if (openaiKey.text === "")
|
||||||
|
openaiKey.showError();
|
||||||
|
else
|
||||||
|
Download.installModel(filename, openaiKey.text);
|
||||||
|
}
|
||||||
|
Accessible.role: Accessible.Button
|
||||||
|
Accessible.name: qsTr("Install button")
|
||||||
|
Accessible.description: qsTr("Install button to install chatgpt model")
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
Label {
|
||||||
|
Layout.topMargin: 20
|
||||||
|
Layout.leftMargin: 20
|
||||||
textFormat: Text.StyledText
|
textFormat: Text.StyledText
|
||||||
|
text: "<strong><font size=\"1\">"
|
||||||
|
+ (qsTr("Status: ")
|
||||||
|
+ (installed ? qsTr("Installed")
|
||||||
|
: (downloadError !== "" ? qsTr("<a href=\"#error\">Error</a>")
|
||||||
|
: (isDownloading ? qsTr("Downloading") : qsTr("Available")))))
|
||||||
|
+ "</strong></font>"
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
linkColor: theme.textColor
|
|
||||||
Accessible.role: Accessible.Paragraph
|
Accessible.role: Accessible.Paragraph
|
||||||
Accessible.name: qsTr("Description")
|
Accessible.name: text
|
||||||
Accessible.description: qsTr("The description of the file")
|
Accessible.description: qsTr("Whether the file is already installed on your system")
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
onLinkActivated: {
|
||||||
|
downloadingErrorPopup.text = downloadError;
|
||||||
|
downloadingErrorPopup.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
|
||||||
id: isDefault
|
|
||||||
text: qsTr("(default)")
|
|
||||||
visible: modelData.isDefault
|
|
||||||
anchors.top: modelName.top
|
|
||||||
anchors.left: modelName.right
|
|
||||||
padding: 20
|
|
||||||
color: theme.textColor
|
|
||||||
Accessible.role: Accessible.Paragraph
|
|
||||||
Accessible.name: qsTr("Default file")
|
|
||||||
Accessible.description: qsTr("Whether the file is the default model")
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: modelData.filesize
|
|
||||||
anchors.top: modelName.top
|
|
||||||
anchors.left: isDefault.visible ? isDefault.right : modelName.right
|
|
||||||
padding: 20
|
|
||||||
color: theme.textColor
|
|
||||||
Accessible.role: Accessible.Paragraph
|
|
||||||
Accessible.name: qsTr("File size")
|
|
||||||
Accessible.description: qsTr("The size of the file")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: speedLabel
|
Layout.leftMargin: 20
|
||||||
anchors.top: modelName.top
|
textFormat: Text.StyledText
|
||||||
anchors.right: itemProgressBar.left
|
text: "<strong><font size=\"1\">"
|
||||||
padding: 20
|
+ (qsTr("Download size: ") + filesize)
|
||||||
objectName: "speedLabel"
|
+ "<br>"
|
||||||
|
+ (qsTr("RAM required: ") + (ramrequired > 0 ? ramrequired + " GB" : qsTr("minimal")))
|
||||||
|
+ "<br>"
|
||||||
|
+ (qsTr("Parameters: ") + parameters)
|
||||||
|
+ "<br>"
|
||||||
|
+ (qsTr("Quantization: ") + quant)
|
||||||
|
+ "<br>"
|
||||||
|
+ (qsTr("Type: ") + type)
|
||||||
|
+ "</strong></font>"
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
text: ""
|
|
||||||
visible: downloading
|
|
||||||
Accessible.role: Accessible.Paragraph
|
Accessible.role: Accessible.Paragraph
|
||||||
Accessible.name: qsTr("Download speed")
|
Accessible.name: text
|
||||||
Accessible.description: qsTr("Download speed in bytes/kilobytes/megabytes per second")
|
Accessible.description: qsTr("Metadata about the model")
|
||||||
|
onLinkActivated: {
|
||||||
|
downloadingErrorPopup.text = downloadError;
|
||||||
|
downloadingErrorPopup.open();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
visible: LLM.systemTotalRAMInGB() < ramrequired
|
||||||
|
Layout.topMargin: 20
|
||||||
|
Layout.leftMargin: 20
|
||||||
|
Layout.maximumWidth: openaiKey.width
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
text: qsTr("<strong><font size=\"2\">WARNING: Not recommended for your hardware.")
|
||||||
|
+ qsTr(" Model requires more memory (") + ramrequired
|
||||||
|
+ qsTr(" GB) than your system has available (")
|
||||||
|
+ LLM.systemTotalRAMInGBString() + ").</strong></font>"
|
||||||
|
color: theme.textErrorColor
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
Accessible.role: Accessible.Paragraph
|
||||||
|
Accessible.name: text
|
||||||
|
Accessible.description: qsTr("Error for incompatible hardware")
|
||||||
|
onLinkActivated: {
|
||||||
|
downloadingErrorPopup.text = downloadError;
|
||||||
|
downloadingErrorPopup.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
visible: isDownloading && !calcHash
|
||||||
|
Layout.topMargin: 20
|
||||||
|
Layout.leftMargin: 20
|
||||||
|
Layout.minimumWidth: openaiKey.width
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
|
spacing: 20
|
||||||
|
|
||||||
ProgressBar {
|
ProgressBar {
|
||||||
id: itemProgressBar
|
id: itemProgressBar
|
||||||
objectName: "itemProgressBar"
|
Layout.fillWidth: true
|
||||||
anchors.top: modelName.top
|
width: openaiKey.width
|
||||||
anchors.right: downloadButton.left
|
value: bytesReceived / bytesTotal
|
||||||
anchors.topMargin: 20
|
|
||||||
anchors.rightMargin: 20
|
|
||||||
width: 100
|
|
||||||
visible: downloading
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
implicitWidth: 200
|
implicitHeight: 45
|
||||||
implicitHeight: 30
|
|
||||||
color: theme.backgroundDarkest
|
color: theme.backgroundDarkest
|
||||||
radius: 3
|
radius: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
implicitWidth: 200
|
implicitHeight: 40
|
||||||
implicitHeight: 25
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: itemProgressBar.visualPosition * parent.width
|
width: itemProgressBar.visualPosition * parent.width
|
||||||
@ -192,16 +300,27 @@ Dialog {
|
|||||||
Accessible.description: qsTr("Shows the progress made in the download")
|
Accessible.description: qsTr("Shows the progress made in the download")
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Label {
|
||||||
visible: modelData.calcHash
|
id: speedLabel
|
||||||
anchors.top: modelName.top
|
color: theme.textColor
|
||||||
anchors.right: parent.right
|
Layout.alignment: Qt.AlignRight
|
||||||
|
text: speed
|
||||||
|
Accessible.role: Accessible.Paragraph
|
||||||
|
Accessible.name: qsTr("Download speed")
|
||||||
|
Accessible.description: qsTr("Download speed in bytes/kilobytes/megabytes per second")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
visible: calcHash
|
||||||
|
Layout.topMargin: 20
|
||||||
|
Layout.leftMargin: 20
|
||||||
|
Layout.minimumWidth: openaiKey.width
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: calcHashLabel
|
id: calcHashLabel
|
||||||
anchors.right: busyCalcHash.left
|
|
||||||
padding: 20
|
|
||||||
objectName: "calcHashLabel"
|
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
text: qsTr("Calculating MD5...")
|
text: qsTr("Calculating MD5...")
|
||||||
Accessible.role: Accessible.Paragraph
|
Accessible.role: Accessible.Paragraph
|
||||||
@ -211,185 +330,78 @@ Dialog {
|
|||||||
|
|
||||||
MyBusyIndicator {
|
MyBusyIndicator {
|
||||||
id: busyCalcHash
|
id: busyCalcHash
|
||||||
anchors.right: parent.right
|
running: calcHash
|
||||||
padding: 20
|
|
||||||
running: modelData.calcHash
|
|
||||||
Accessible.role: Accessible.Animation
|
Accessible.role: Accessible.Animation
|
||||||
Accessible.name: qsTr("Busy indicator")
|
Accessible.name: qsTr("Busy indicator")
|
||||||
Accessible.description: qsTr("Displayed when the file hash is being calculated")
|
Accessible.description: qsTr("Displayed when the file hash is being calculated")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
MyTextField {
|
||||||
anchors.top: modelName.top
|
|
||||||
anchors.topMargin: 15
|
|
||||||
anchors.right: parent.right
|
|
||||||
visible: modelData.installed
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: installedLabel
|
|
||||||
anchors.verticalCenter: removeButton.verticalCenter
|
|
||||||
anchors.right: removeButton.left
|
|
||||||
anchors.rightMargin: 15
|
|
||||||
objectName: "installedLabel"
|
|
||||||
color: theme.textColor
|
|
||||||
text: qsTr("Already installed")
|
|
||||||
Accessible.role: Accessible.Paragraph
|
|
||||||
Accessible.name: text
|
|
||||||
Accessible.description: qsTr("Whether the file is already installed on your system")
|
|
||||||
}
|
|
||||||
|
|
||||||
MyButton {
|
|
||||||
id: removeButton
|
|
||||||
text: "Remove"
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 20
|
|
||||||
Accessible.description: qsTr("Remove button to remove model from filesystem")
|
|
||||||
onClicked: {
|
|
||||||
Download.removeModel(modelData.filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
visible: modelData.isChatGPT && !modelData.installed
|
|
||||||
anchors.top: modelName.top
|
|
||||||
anchors.topMargin: 15
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
id: openaiKey
|
id: openaiKey
|
||||||
anchors.right: installButton.left
|
visible: !installed && isChatGPT
|
||||||
anchors.rightMargin: 15
|
Layout.topMargin: 20
|
||||||
|
Layout.leftMargin: 20
|
||||||
|
Layout.minimumWidth: 150
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: theme.backgroundLighter
|
color: theme.backgroundLighter
|
||||||
radius: 10
|
radius: 10
|
||||||
}
|
}
|
||||||
|
function showError() {
|
||||||
|
openaiKey.placeholderTextColor = theme.textErrorColor
|
||||||
|
}
|
||||||
|
onTextChanged: {
|
||||||
|
openaiKey.placeholderTextColor = theme.backgroundLightest
|
||||||
|
}
|
||||||
placeholderText: qsTr("enter $OPENAI_API_KEY")
|
placeholderText: qsTr("enter $OPENAI_API_KEY")
|
||||||
placeholderTextColor: theme.backgroundLightest
|
placeholderTextColor: theme.backgroundLightest
|
||||||
Accessible.role: Accessible.EditableText
|
Accessible.role: Accessible.EditableText
|
||||||
Accessible.name: placeholderText
|
Accessible.name: placeholderText
|
||||||
Accessible.description: qsTr("Whether the file hash is being calculated")
|
Accessible.description: qsTr("Whether the file hash is being calculated")
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
|
||||||
id: installButton
|
|
||||||
contentItem: Text {
|
|
||||||
color: openaiKey.text === "" ? theme.backgroundLightest : theme.textColor
|
|
||||||
text: "Install"
|
|
||||||
}
|
|
||||||
enabled: openaiKey.text !== ""
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 20
|
|
||||||
background: Rectangle {
|
|
||||||
opacity: .5
|
|
||||||
border.color: theme.backgroundLightest
|
|
||||||
border.width: 1
|
|
||||||
radius: 10
|
|
||||||
color: theme.backgroundLight
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
Download.installModel(modelData.filename, openaiKey.text);
|
|
||||||
}
|
|
||||||
Accessible.role: Accessible.Button
|
|
||||||
Accessible.name: qsTr("Install button")
|
|
||||||
Accessible.description: qsTr("Install button to install chatgpt model")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: descriptionText
|
||||||
|
text: description
|
||||||
|
Layout.row: 1
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.leftMargin: 20
|
||||||
|
Layout.bottomMargin: 20
|
||||||
|
Layout.maximumWidth: modelListView.width - actionBox.width - 60
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
color: theme.textColor
|
||||||
|
linkColor: theme.textColor
|
||||||
|
Accessible.role: Accessible.Paragraph
|
||||||
|
Accessible.name: qsTr("Description")
|
||||||
|
Accessible.description: qsTr("The description of the file")
|
||||||
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: Component {
|
||||||
|
Rectangle {
|
||||||
|
width: modelListView.width
|
||||||
|
height: expandButton.height + 80
|
||||||
|
color: ModelList.downloadableModels.count % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter
|
||||||
MyButton {
|
MyButton {
|
||||||
id: downloadButton
|
id: expandButton
|
||||||
text: downloading ? qsTr("Cancel") : qsTr("Download")
|
anchors.centerIn: parent
|
||||||
anchors.top: modelName.top
|
padding: 40
|
||||||
anchors.right: parent.right
|
text: ModelList.downloadableModels.expanded ? qsTr("Show fewer models") : qsTr("Show more models")
|
||||||
anchors.topMargin: 15
|
background: Rectangle {
|
||||||
anchors.rightMargin: 20
|
border.color: expandButton.down ? theme.backgroundLightest : theme.buttonBorder
|
||||||
visible: !modelData.isChatGPT && !modelData.installed && !modelData.calcHash
|
border.width: 2
|
||||||
Accessible.description: qsTr("Cancel/Download button to stop/start the download")
|
radius: 10
|
||||||
|
color: expandButton.hovered ? theme.backgroundDark : theme.backgroundDarkest
|
||||||
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!downloading) {
|
ModelList.downloadableModels.expanded = !ModelList.downloadableModels.expanded;
|
||||||
downloading = true;
|
|
||||||
Download.downloadModel(modelData.filename);
|
|
||||||
} else {
|
|
||||||
downloading = false;
|
|
||||||
Download.cancelDownload(modelData.filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
Download.downloadProgress.connect(updateProgress);
|
|
||||||
Download.downloadFinished.connect(resetProgress);
|
|
||||||
}
|
|
||||||
|
|
||||||
property var lastUpdate: ({})
|
|
||||||
|
|
||||||
function updateProgress(bytesReceived, bytesTotal, modelName) {
|
|
||||||
let currentTime = new Date().getTime();
|
|
||||||
|
|
||||||
for (let i = 0; i < modelList.contentItem.children.length; i++) {
|
|
||||||
let delegateItem = modelList.contentItem.children[i];
|
|
||||||
if (delegateItem.objectName === "delegateItem") {
|
|
||||||
let modelNameText = delegateItem.children.find(child => child.objectName === "modelName").filename;
|
|
||||||
if (modelNameText === modelName) {
|
|
||||||
let progressBar = delegateItem.children.find(child => child.objectName === "itemProgressBar");
|
|
||||||
progressBar.value = bytesReceived / bytesTotal;
|
|
||||||
|
|
||||||
let updated = false;
|
|
||||||
|
|
||||||
// Calculate the download speed
|
|
||||||
if (lastUpdate[modelName] && lastUpdate[modelName].timestamp) {
|
|
||||||
let timeDifference = currentTime - lastUpdate[modelName].timestamp;
|
|
||||||
if (timeDifference >= 1500) {
|
|
||||||
let bytesDifference = bytesReceived - lastUpdate[modelName].bytesReceived;
|
|
||||||
let speed = (bytesDifference / timeDifference) * 1000; // bytes per second
|
|
||||||
delegateItem.downloading = true
|
|
||||||
|
|
||||||
// Update the speed label
|
|
||||||
let speedLabel = delegateItem.children.find(child => child.objectName === "speedLabel");
|
|
||||||
if (speed < 1024) {
|
|
||||||
speedLabel.text = speed.toFixed(2) + " B/s";
|
|
||||||
} else if (speed < 1024 * 1024) {
|
|
||||||
speedLabel.text = (speed / 1024).toFixed(2) + " KB/s";
|
|
||||||
} else {
|
|
||||||
speedLabel.text = (speed / (1024 * 1024)).toFixed(2) + " MB/s";
|
|
||||||
}
|
|
||||||
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updated = true; // To get an initial entry in lastUpdate
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the lastUpdate object for the current model
|
|
||||||
if (updated) {
|
|
||||||
lastUpdate[modelName] = {"timestamp": currentTime, "bytesReceived": bytesReceived};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetProgress(modelName) {
|
|
||||||
for (let i = 0; i < modelList.contentItem.children.length; i++) {
|
|
||||||
let delegateItem = modelList.contentItem.children[i];
|
|
||||||
if (delegateItem.objectName === "delegateItem") {
|
|
||||||
let modelNameText = delegateItem.children.find(child => child.objectName === "modelName").filename;
|
|
||||||
if (modelNameText === modelName) {
|
|
||||||
let progressBar = delegateItem.children.find(child => child.objectName === "itemProgressBar");
|
|
||||||
progressBar.value = 0;
|
|
||||||
delegateItem.downloading = false;
|
|
||||||
|
|
||||||
// Remove speed label text
|
|
||||||
let speedLabel = delegateItem.children.find(child => child.objectName === "speedLabel");
|
|
||||||
speedLabel.text = "";
|
|
||||||
|
|
||||||
// Remove the lastUpdate object for the canceled model
|
|
||||||
delete lastUpdate[modelName];
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -404,11 +416,11 @@ Dialog {
|
|||||||
FolderDialog {
|
FolderDialog {
|
||||||
id: modelPathDialog
|
id: modelPathDialog
|
||||||
title: "Please choose a directory"
|
title: "Please choose a directory"
|
||||||
currentFolder: "file://" + Download.downloadLocalModelsPath
|
currentFolder: "file://" + ModelList.localModelsPath
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
modelPathDisplayField.text = selectedFolder
|
modelPathDisplayField.text = selectedFolder
|
||||||
Download.downloadLocalModelsPath = modelPathDisplayField.text
|
ModelList.localModelsPath = modelPathDisplayField.text
|
||||||
settings.modelPath = Download.downloadLocalModelsPath
|
settings.modelPath = ModelList.localModelsPath
|
||||||
settings.sync()
|
settings.sync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,7 +433,7 @@ Dialog {
|
|||||||
}
|
}
|
||||||
MyDirectoryField {
|
MyDirectoryField {
|
||||||
id: modelPathDisplayField
|
id: modelPathDisplayField
|
||||||
text: Download.downloadLocalModelsPath
|
text: ModelList.localModelsPath
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
ToolTip.text: qsTr("Path where model files will be downloaded to")
|
ToolTip.text: qsTr("Path where model files will be downloaded to")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
@ -430,11 +442,11 @@ Dialog {
|
|||||||
Accessible.description: ToolTip.text
|
Accessible.description: ToolTip.text
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
Download.downloadLocalModelsPath = modelPathDisplayField.text
|
ModelList.localModelsPath = modelPathDisplayField.text
|
||||||
settings.modelPath = Download.downloadLocalModelsPath
|
settings.modelPath = ModelList.localModelsPath
|
||||||
settings.sync()
|
settings.sync()
|
||||||
} else {
|
} else {
|
||||||
text = Download.downloadLocalModelsPath
|
text = ModelList.localModelsPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,18 +23,21 @@ Dialog {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 20
|
spacing: 20
|
||||||
|
|
||||||
Text {
|
Label {
|
||||||
id: textField
|
id: textField
|
||||||
width: Math.min(1024, implicitWidth)
|
width: Math.min(1024, implicitWidth)
|
||||||
height: Math.min(600, implicitHeight)
|
height: Math.min(600, implicitHeight)
|
||||||
anchors.verticalCenter: shouldShowBusy ? busyIndicator.verticalCenter : parent.verticalCenter
|
anchors.verticalCenter: shouldShowBusy ? busyIndicator.verticalCenter : parent.verticalCenter
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
textFormat: Text.StyledText
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
|
linkColor: theme.linkColor
|
||||||
Accessible.role: Accessible.HelpBalloon
|
Accessible.role: Accessible.HelpBalloon
|
||||||
Accessible.name: text
|
Accessible.name: text
|
||||||
Accessible.description: qsTr("Reveals a shortlived help balloon")
|
Accessible.description: qsTr("Reveals a shortlived help balloon")
|
||||||
|
onLinkActivated: { Qt.openUrlExternally("https://discord.gg/4M2QFmTt2k") }
|
||||||
}
|
}
|
||||||
|
|
||||||
MyBusyIndicator {
|
MyBusyIndicator {
|
||||||
|
@ -5,7 +5,9 @@ import QtQuick.Controls.Basic
|
|||||||
import QtQuick.Dialogs
|
import QtQuick.Dialogs
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt.labs.folderlistmodel
|
import Qt.labs.folderlistmodel
|
||||||
|
import chatlistmodel
|
||||||
import download
|
import download
|
||||||
|
import modellist
|
||||||
import network
|
import network
|
||||||
import llm
|
import llm
|
||||||
|
|
||||||
@ -27,7 +29,7 @@ Dialog {
|
|||||||
Network.sendSettingsDialog();
|
Network.sendSettingsDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
property var currentChat: LLM.chatListModel.currentChat
|
property var currentChat: ChatListModel.currentChat
|
||||||
|
|
||||||
Theme {
|
Theme {
|
||||||
id: theme
|
id: theme
|
||||||
@ -47,7 +49,7 @@ Dialog {
|
|||||||
property string defaultPromptTemplate: "### Human:
|
property string defaultPromptTemplate: "### Human:
|
||||||
%1
|
%1
|
||||||
### Assistant:\n"
|
### Assistant:\n"
|
||||||
property string defaultModelPath: Download.defaultLocalModelsPath()
|
property string defaultModelPath: ModelList.defaultLocalModelsPath()
|
||||||
property string defaultUserDefaultModel: "Application default"
|
property string defaultUserDefaultModel: "Application default"
|
||||||
|
|
||||||
property alias temperature: settings.temperature
|
property alias temperature: settings.temperature
|
||||||
@ -102,20 +104,20 @@ Dialog {
|
|||||||
settings.saveChatGPTChats = defaultSaveChatGPTChats
|
settings.saveChatGPTChats = defaultSaveChatGPTChats
|
||||||
settings.serverChat = defaultServerChat
|
settings.serverChat = defaultServerChat
|
||||||
settings.userDefaultModel = defaultUserDefaultModel
|
settings.userDefaultModel = defaultUserDefaultModel
|
||||||
Download.downloadLocalModelsPath = settings.modelPath
|
ModelList.localModelsPath = settings.modelPath
|
||||||
LLM.threadCount = settings.threadCount
|
LLM.threadCount = settings.threadCount
|
||||||
LLM.serverEnabled = settings.serverChat
|
LLM.serverEnabled = settings.serverChat
|
||||||
LLM.chatListModel.shouldSaveChats = settings.saveChats
|
ChatListModel.shouldSaveChats = settings.saveChats
|
||||||
LLM.chatListModel.shouldSaveChatGPTChats = settings.saveChatGPTChats
|
chatListModel.shouldSaveChatGPTChats = settings.saveChatGPTChats
|
||||||
settings.sync()
|
settings.sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
LLM.threadCount = settings.threadCount
|
LLM.threadCount = settings.threadCount
|
||||||
LLM.serverEnabled = settings.serverChat
|
LLM.serverEnabled = settings.serverChat
|
||||||
LLM.chatListModel.shouldSaveChats = settings.saveChats
|
ChatListModel.shouldSaveChats = settings.saveChats
|
||||||
LLM.chatListModel.shouldSaveChatGPTChats = settings.saveChatGPTChats
|
ChatListModel.shouldSaveChatGPTChats = settings.saveChatGPTChats
|
||||||
Download.downloadLocalModelsPath = settings.modelPath
|
ModelList.localModelsPath = settings.modelPath
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@ -515,13 +517,32 @@ Dialog {
|
|||||||
Accessible.description: ToolTip.text
|
Accessible.description: ToolTip.text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.row: 7
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.topMargin: 10
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
spacing: 20
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: promptTemplateLabel
|
id: promptTemplateLabel
|
||||||
text: qsTr("Prompt Template:")
|
text: qsTr("Prompt Template:")
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
Layout.row: 7
|
|
||||||
Layout.column: 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: promptTemplateLabelHelp
|
||||||
|
Layout.maximumWidth: promptTemplateLabel.width
|
||||||
|
visible: settings.promptTemplate.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 {
|
Rectangle {
|
||||||
Layout.row: 7
|
Layout.row: 7
|
||||||
Layout.column: 1
|
Layout.column: 1
|
||||||
@ -529,20 +550,6 @@ Dialog {
|
|||||||
height: 200
|
height: 200
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
clip: true
|
clip: true
|
||||||
Label {
|
|
||||||
id: promptTemplateLabelHelp
|
|
||||||
visible: settings.promptTemplate.indexOf(
|
|
||||||
"%1") === -1
|
|
||||||
font.bold: true
|
|
||||||
color: theme.textErrorColor
|
|
||||||
text: qsTr("Prompt template must contain %1 to be replaced with the user's input.")
|
|
||||||
anchors.fill: templateScrollView
|
|
||||||
z: 200
|
|
||||||
padding: 10
|
|
||||||
wrapMode: TextArea.Wrap
|
|
||||||
Accessible.role: Accessible.EditableText
|
|
||||||
Accessible.name: text
|
|
||||||
}
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: templateScrollView
|
id: templateScrollView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -615,31 +622,21 @@ Dialog {
|
|||||||
Layout.row: 1
|
Layout.row: 1
|
||||||
Layout.column: 1
|
Layout.column: 1
|
||||||
Layout.minimumWidth: 350
|
Layout.minimumWidth: 350
|
||||||
model: modelList
|
model: ModelList.userDefaultModelList
|
||||||
Accessible.role: Accessible.ComboBox
|
Accessible.role: Accessible.ComboBox
|
||||||
Accessible.name: qsTr("ComboBox for displaying/picking the default model")
|
Accessible.name: qsTr("ComboBox for displaying/picking the default model")
|
||||||
Accessible.description: qsTr("Use this for picking the default model to use; the first item is the current default model")
|
Accessible.description: qsTr("Use this for picking the default model to use; the first item is the current default model")
|
||||||
function updateModel(newModelList) {
|
function updateModel() {
|
||||||
var newArray = Array.from(newModelList);
|
|
||||||
newArray.unshift('Application default');
|
|
||||||
comboBox.model = newArray;
|
|
||||||
settings.sync();
|
settings.sync();
|
||||||
comboBox.currentIndex = comboBox.indexOfValue(settingsDialog.userDefaultModel);
|
comboBox.currentIndex = comboBox.indexOfValue(settingsDialog.userDefaultModel);
|
||||||
|
|
||||||
}
|
}
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
comboBox.updateModel(currentChat.modelList)
|
comboBox.updateModel()
|
||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: settings
|
target: settings
|
||||||
function onUserDefaultModelChanged() {
|
function onUserDefaultModelChanged() {
|
||||||
comboBox.updateModel(currentChat.modelList)
|
comboBox.updateModel()
|
||||||
}
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: currentChat
|
|
||||||
function onModelListChanged() {
|
|
||||||
comboBox.updateModel(currentChat.modelList)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onActivated: {
|
onActivated: {
|
||||||
@ -650,11 +647,11 @@ Dialog {
|
|||||||
FolderDialog {
|
FolderDialog {
|
||||||
id: modelPathDialog
|
id: modelPathDialog
|
||||||
title: "Please choose a directory"
|
title: "Please choose a directory"
|
||||||
currentFolder: "file://" + Download.downloadLocalModelsPath
|
currentFolder: "file://" + ModelList.localModelsPath
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
modelPathDisplayField.text = selectedFolder
|
modelPathDisplayField.text = selectedFolder
|
||||||
Download.downloadLocalModelsPath = modelPathDisplayField.text
|
ModelList.localModelsPath = modelPathDisplayField.text
|
||||||
settings.modelPath = Download.downloadLocalModelsPath
|
settings.modelPath = ModelList.localModelsPath
|
||||||
settings.sync()
|
settings.sync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -667,7 +664,7 @@ Dialog {
|
|||||||
}
|
}
|
||||||
MyDirectoryField {
|
MyDirectoryField {
|
||||||
id: modelPathDisplayField
|
id: modelPathDisplayField
|
||||||
text: Download.downloadLocalModelsPath
|
text: ModelList.localModelsPath
|
||||||
implicitWidth: 300
|
implicitWidth: 300
|
||||||
Layout.row: 2
|
Layout.row: 2
|
||||||
Layout.column: 1
|
Layout.column: 1
|
||||||
@ -679,11 +676,11 @@ Dialog {
|
|||||||
Accessible.description: ToolTip.text
|
Accessible.description: ToolTip.text
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
Download.downloadLocalModelsPath = modelPathDisplayField.text
|
ModelList.localModelsPath = modelPathDisplayField.text
|
||||||
settings.modelPath = Download.downloadLocalModelsPath
|
settings.modelPath = ModelList.localModelsPath
|
||||||
settings.sync()
|
settings.sync()
|
||||||
} else {
|
} else {
|
||||||
text = Download.downloadLocalModelsPath
|
text = ModelList.localModelsPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -741,7 +738,7 @@ Dialog {
|
|||||||
onClicked: {
|
onClicked: {
|
||||||
Network.sendSaveChatsToggled(saveChatsBox.checked);
|
Network.sendSaveChatsToggled(saveChatsBox.checked);
|
||||||
settingsDialog.saveChats = saveChatsBox.checked
|
settingsDialog.saveChats = saveChatsBox.checked
|
||||||
LLM.chatListModel.shouldSaveChats = saveChatsBox.checked
|
ChatListModel.shouldSaveChats = saveChatsBox.checked
|
||||||
settings.sync()
|
settings.sync()
|
||||||
}
|
}
|
||||||
ToolTip.text: qsTr("WARNING: Saving chats to disk can be ~2GB per chat")
|
ToolTip.text: qsTr("WARNING: Saving chats to disk can be ~2GB per chat")
|
||||||
@ -761,7 +758,7 @@ Dialog {
|
|||||||
checked: settingsDialog.saveChatGPTChats
|
checked: settingsDialog.saveChatGPTChats
|
||||||
onClicked: {
|
onClicked: {
|
||||||
settingsDialog.saveChatGPTChats = saveChatGPTChatsBox.checked
|
settingsDialog.saveChatGPTChats = saveChatGPTChatsBox.checked
|
||||||
LLM.chatListModel.shouldSaveChatGPTChats = saveChatGPTChatsBox.checked
|
ChatListModel.shouldSaveChatGPTChats = saveChatGPTChatsBox.checked
|
||||||
settings.sync()
|
settings.sync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
#include "chat.h"
|
||||||
#include "llm.h"
|
#include "llm.h"
|
||||||
#include "download.h"
|
#include "modellist.h"
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
@ -10,26 +11,14 @@
|
|||||||
|
|
||||||
//#define DEBUG
|
//#define DEBUG
|
||||||
|
|
||||||
static inline QString modelToName(const ModelInfo &info)
|
|
||||||
{
|
|
||||||
QString modelName = info.filename;
|
|
||||||
if (modelName.startsWith("ggml-"))
|
|
||||||
modelName = modelName.remove(0, 5);
|
|
||||||
if (modelName.endsWith(".bin"))
|
|
||||||
modelName.chop(4);
|
|
||||||
return modelName;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QJsonObject modelToJson(const ModelInfo &info)
|
static inline QJsonObject modelToJson(const ModelInfo &info)
|
||||||
{
|
{
|
||||||
QString modelName = modelToName(info);
|
|
||||||
|
|
||||||
QJsonObject model;
|
QJsonObject model;
|
||||||
model.insert("id", modelName);
|
model.insert("id", info.name);
|
||||||
model.insert("object", "model");
|
model.insert("object", "model");
|
||||||
model.insert("created", "who can keep track?");
|
model.insert("created", "who can keep track?");
|
||||||
model.insert("owned_by", "humanity");
|
model.insert("owned_by", "humanity");
|
||||||
model.insert("root", modelName);
|
model.insert("root", info.name);
|
||||||
model.insert("parent", QJsonValue::Null);
|
model.insert("parent", QJsonValue::Null);
|
||||||
|
|
||||||
QJsonArray permissions;
|
QJsonArray permissions;
|
||||||
@ -92,7 +81,7 @@ void Server::start()
|
|||||||
if (!LLM::globalInstance()->serverEnabled())
|
if (!LLM::globalInstance()->serverEnabled())
|
||||||
return QHttpServerResponse(QHttpServerResponder::StatusCode::Unauthorized);
|
return QHttpServerResponse(QHttpServerResponder::StatusCode::Unauthorized);
|
||||||
|
|
||||||
const QList<ModelInfo> modelList = Download::globalInstance()->modelList();
|
const QList<ModelInfo> modelList = ModelList::globalInstance()->exportModelList();
|
||||||
QJsonObject root;
|
QJsonObject root;
|
||||||
root.insert("object", "list");
|
root.insert("object", "list");
|
||||||
QJsonArray data;
|
QJsonArray data;
|
||||||
@ -111,14 +100,13 @@ void Server::start()
|
|||||||
if (!LLM::globalInstance()->serverEnabled())
|
if (!LLM::globalInstance()->serverEnabled())
|
||||||
return QHttpServerResponse(QHttpServerResponder::StatusCode::Unauthorized);
|
return QHttpServerResponse(QHttpServerResponder::StatusCode::Unauthorized);
|
||||||
|
|
||||||
const QList<ModelInfo> modelList = Download::globalInstance()->modelList();
|
const QList<ModelInfo> modelList = ModelList::globalInstance()->exportModelList();
|
||||||
QJsonObject object;
|
QJsonObject object;
|
||||||
for (const ModelInfo &info : modelList) {
|
for (const ModelInfo &info : modelList) {
|
||||||
if (!info.installed)
|
if (!info.installed)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QString modelName = modelToName(info);
|
if (model == info.name) {
|
||||||
if (model == modelName) {
|
|
||||||
object = modelToJson(info);
|
object = modelToJson(info);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -179,14 +167,14 @@ QHttpServerResponse Server::handleCompletionRequest(const QHttpServerRequest &re
|
|||||||
messages = body["messages"].toArray();
|
messages = body["messages"].toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString model = body["model"].toString();
|
const QString modelRequested = body["model"].toString();
|
||||||
bool foundModel = false;
|
ModelInfo modelInfo = ModelList::globalInstance()->defaultModelInfo();
|
||||||
const QList<ModelInfo> modelList = Download::globalInstance()->modelList();
|
const QList<ModelInfo> modelList = ModelList::globalInstance()->exportModelList();
|
||||||
for (const ModelInfo &info : modelList) {
|
for (const ModelInfo &info : modelList) {
|
||||||
if (!info.installed)
|
if (!info.installed)
|
||||||
continue;
|
continue;
|
||||||
if (model == modelToName(info)) {
|
if (modelRequested == info.name) {
|
||||||
foundModel = true;
|
modelInfo = info;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,13 +284,11 @@ QHttpServerResponse Server::handleCompletionRequest(const QHttpServerRequest &re
|
|||||||
// load the new model if necessary
|
// load the new model if necessary
|
||||||
setShouldBeLoaded(true);
|
setShouldBeLoaded(true);
|
||||||
|
|
||||||
if (!foundModel) {
|
if (!modelInfo.name.isEmpty()) {
|
||||||
if (!loadDefaultModel()) {
|
std::cerr << "ERROR: couldn't load default model " << modelRequested.toStdString() << std::endl;
|
||||||
std::cerr << "ERROR: couldn't load default model " << model.toStdString() << std::endl;
|
|
||||||
return QHttpServerResponse(QHttpServerResponder::StatusCode::BadRequest);
|
return QHttpServerResponse(QHttpServerResponder::StatusCode::BadRequest);
|
||||||
}
|
} else if (!loadModel(modelInfo)) {
|
||||||
} else if (!loadModel(model)) {
|
std::cerr << "ERROR: couldn't load model " << modelInfo.name.toStdString() << std::endl;
|
||||||
std::cerr << "ERROR: couldn't load model " << model.toStdString() << std::endl;
|
|
||||||
return QHttpServerResponse(QHttpServerResponder::StatusCode::InternalServerError);
|
return QHttpServerResponse(QHttpServerResponder::StatusCode::InternalServerError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +320,7 @@ QHttpServerResponse Server::handleCompletionRequest(const QHttpServerRequest &re
|
|||||||
repeat_last_n,
|
repeat_last_n,
|
||||||
LLM::globalInstance()->threadCount())) {
|
LLM::globalInstance()->threadCount())) {
|
||||||
|
|
||||||
std::cerr << "ERROR: couldn't prompt model " << model.toStdString() << std::endl;
|
std::cerr << "ERROR: couldn't prompt model " << modelInfo.name.toStdString() << std::endl;
|
||||||
return QHttpServerResponse(QHttpServerResponder::StatusCode::InternalServerError);
|
return QHttpServerResponse(QHttpServerResponder::StatusCode::InternalServerError);
|
||||||
}
|
}
|
||||||
QString echoedPrompt = actualPrompt;
|
QString echoedPrompt = actualPrompt;
|
||||||
@ -352,7 +338,7 @@ QHttpServerResponse Server::handleCompletionRequest(const QHttpServerRequest &re
|
|||||||
responseObject.insert("id", "foobarbaz");
|
responseObject.insert("id", "foobarbaz");
|
||||||
responseObject.insert("object", "text_completion");
|
responseObject.insert("object", "text_completion");
|
||||||
responseObject.insert("created", QDateTime::currentSecsSinceEpoch());
|
responseObject.insert("created", QDateTime::currentSecsSinceEpoch());
|
||||||
responseObject.insert("model", modelName());
|
responseObject.insert("model", modelInfo.name);
|
||||||
|
|
||||||
QJsonArray choices;
|
QJsonArray choices;
|
||||||
|
|
||||||
|
@ -1,49 +1,61 @@
|
|||||||
#include <QtCore/QCoreApplication>
|
#ifndef SYSINFO_H
|
||||||
#include <QDebug>
|
#define SYSINFO_H
|
||||||
#include <QFile>
|
|
||||||
#include <QTextStream>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
|
|
||||||
#if defined(Q_OS_MAC)
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <unistd.h>
|
||||||
|
#elif defined(__APPLE__)
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
static long long getSystemTotalRAMInBytes()
|
||||||
#include <Windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QString getSystemTotalRAM()
|
|
||||||
{
|
{
|
||||||
qint64 totalRAM = 0;
|
long long totalRAM = 0;
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX)
|
#if defined(__linux__)
|
||||||
QFile file("/proc/meminfo");
|
std::ifstream file("/proc/meminfo");
|
||||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
std::string line;
|
||||||
QTextStream in(&file);
|
while (std::getline(file, line)) {
|
||||||
QString line = in.readLine();
|
if (line.find("MemTotal") != std::string::npos) {
|
||||||
while (!line.isNull()) {
|
std::string memTotalStr = line.substr(line.find(":") + 1);
|
||||||
if (line.startsWith("MemTotal")) {
|
memTotalStr.erase(0, memTotalStr.find_first_not_of(" "));
|
||||||
static QRegularExpression spaces("\\s+");
|
memTotalStr = memTotalStr.substr(0, memTotalStr.find(" "));
|
||||||
QStringList parts = line.split(spaces);
|
totalRAM = std::stoll(memTotalStr) * 1024; // Convert from KB to bytes
|
||||||
totalRAM = parts[1].toLongLong() * 1024; // Convert from KB to bytes
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
line = in.readLine();
|
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
}
|
#elif defined(__APPLE__)
|
||||||
#elif defined(Q_OS_MAC)
|
|
||||||
int mib[2] = {CTL_HW, HW_MEMSIZE};
|
int mib[2] = {CTL_HW, HW_MEMSIZE};
|
||||||
size_t length = sizeof(totalRAM);
|
size_t length = sizeof(totalRAM);
|
||||||
sysctl(mib, 2, &totalRAM, &length, NULL, 0);
|
sysctl(mib, 2, &totalRAM, &length, NULL, 0);
|
||||||
#elif defined(Q_OS_WIN)
|
#elif defined(_WIN32)
|
||||||
MEMORYSTATUSEX memoryStatus;
|
MEMORYSTATUSEX memoryStatus;
|
||||||
memoryStatus.dwLength = sizeof(memoryStatus);
|
memoryStatus.dwLength = sizeof(memoryStatus);
|
||||||
GlobalMemoryStatusEx(&memoryStatus);
|
GlobalMemoryStatusEx(&memoryStatus);
|
||||||
totalRAM = memoryStatus.ullTotalPhys;
|
totalRAM = memoryStatus.ullTotalPhys;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
double totalRAM_GB = static_cast<double>(totalRAM) / (1024 * 1024 * 1024);
|
return totalRAM;
|
||||||
return QString::number(totalRAM_GB, 'f', 2) + " GB";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double getSystemTotalRAMInGB()
|
||||||
|
{
|
||||||
|
return static_cast<double>(getSystemTotalRAMInBytes()) / (1024 * 1024 * 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getSystemTotalRAMInGBString()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::fixed << std::setprecision(2) << getSystemTotalRAMInGB() << " GB";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SYSINFO_H
|
||||||
|
Loading…
Reference in New Issue
Block a user