From 09c5ea219fd87cd3edd5fb9258f8d1b4440f5a2e Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Wed, 11 Oct 2023 10:23:33 -0400 Subject: [PATCH] Always save chats to disk, but save them as text by default. This also changes the UI behavior to always open a 'New Chat' and setting it as current instead of setting a restored chat as current. This improves usability by not requiring the user to wait if they want to immediately start chatting. --- gpt4all-chat/chat.cpp | 12 ++- gpt4all-chat/chat.h | 2 + gpt4all-chat/chatlistmodel.cpp | 108 +++++++++-------------- gpt4all-chat/chatlistmodel.h | 15 +--- gpt4all-chat/mysettings.cpp | 36 ++------ gpt4all-chat/mysettings.h | 12 +-- gpt4all-chat/network.cpp | 10 --- gpt4all-chat/network.h | 1 - gpt4all-chat/qml/ApplicationSettings.qml | 34 ++----- 9 files changed, 74 insertions(+), 156 deletions(-) diff --git a/gpt4all-chat/chat.cpp b/gpt4all-chat/chat.cpp index e504f1657a21..91560c50a99e 100644 --- a/gpt4all-chat/chat.cpp +++ b/gpt4all-chat/chat.cpp @@ -385,7 +385,11 @@ bool Chat::serialize(QDataStream &stream, int version) const stream << m_modelInfo.filename(); if (version > 2) stream << m_collections; - if (!m_llmodel->serialize(stream, version, true /*serializeKV*/)) + + const bool serializeKV = MySettings::globalInstance()->saveChatsContext(); + if (version > 5) + stream << serializeKV; + if (!m_llmodel->serialize(stream, version, serializeKV)) return false; if (!m_chatModel->serialize(stream, version)) return false; @@ -413,7 +417,6 @@ bool Chat::deserialize(QDataStream &stream, int version) if (!m_modelInfo.id().isEmpty()) emit modelInfoChanged(); - bool deserializeKV = true; // make this a setting bool discardKV = m_modelInfo.id().isEmpty(); // Prior to version 2 gptj models had a bug that fixed the kv_cache to F32 instead of F16 so @@ -425,6 +428,11 @@ bool Chat::deserialize(QDataStream &stream, int version) stream >> m_collections; emit collectionListChanged(m_collections); } + + bool deserializeKV = true; + if (version > 5) + stream >> deserializeKV; + m_llmodel->setModelInfo(m_modelInfo); if (!m_llmodel->deserialize(stream, version, deserializeKV, discardKV)) return false; diff --git a/gpt4all-chat/chat.h b/gpt4all-chat/chat.h index fb27fcf46de6..45aa1816c241 100644 --- a/gpt4all-chat/chat.h +++ b/gpt4all-chat/chat.h @@ -54,6 +54,8 @@ class Chat : public QObject } ChatModel *chatModel() { return m_chatModel; } + bool isNewChat() const { return m_name == tr("New Chat") && !m_chatModel->count(); } + Q_INVOKABLE void reset(); Q_INVOKABLE void processSystemPrompt(); Q_INVOKABLE bool isModelLoaded() const; diff --git a/gpt4all-chat/chatlistmodel.cpp b/gpt4all-chat/chatlistmodel.cpp index 26c8b564eeae..e12edd87aa1d 100644 --- a/gpt4all-chat/chatlistmodel.cpp +++ b/gpt4all-chat/chatlistmodel.cpp @@ -5,7 +5,7 @@ #include #define CHAT_FORMAT_MAGIC 0xF5D553CC -#define CHAT_FORMAT_VERSION 5 +#define CHAT_FORMAT_VERSION 6 class MyChatListModel: public ChatListModel { }; Q_GLOBAL_STATIC(MyChatListModel, chatListModelInstance) @@ -17,11 +17,10 @@ ChatListModel *ChatListModel::globalInstance() ChatListModel::ChatListModel() : QAbstractListModel(nullptr) , m_newChat(nullptr) - , m_dummyChat(nullptr) , m_serverChat(nullptr) , m_currentChat(nullptr) { - addDummyChat(); + addChat(); ChatsRestoreThread *thread = new ChatsRestoreThread; connect(thread, &ChatsRestoreThread::chatRestored, this, &ChatListModel::restoreChat); @@ -59,10 +58,7 @@ void ChatListModel::saveChats() for (Chat *chat : m_chats) { if (chat == m_serverChat) continue; - const bool isChatGPT = chat->modelInfo().isChatGPT; - if (!isChatGPT && !MySettings::globalInstance()->saveChats()) - continue; - if (isChatGPT && !MySettings::globalInstance()->saveChatGPTChats()) + if (chat->isNewChat()) continue; toSave.append(chat); } @@ -197,47 +193,47 @@ void ChatsRestoreThread::run() }); for (FileInfo &f : files) { - QFile file(f.file); - bool success = file.open(QIODevice::ReadOnly); - if (!success) { - qWarning() << "ERROR: Couldn't restore chat from file:" << file.fileName(); + QFile file(f.file); + bool success = file.open(QIODevice::ReadOnly); + if (!success) { + qWarning() << "ERROR: Couldn't restore chat from file:" << file.fileName(); + continue; + } + QDataStream in(&file); + + qint32 version = 0; + if (!f.oldFile) { + // Read and check the header + quint32 magic; + in >> magic; + if (magic != CHAT_FORMAT_MAGIC) { + qWarning() << "ERROR: Chat file has bad magic:" << file.fileName(); continue; } - QDataStream in(&file); - qint32 version = 0; - if (!f.oldFile) { - // Read and check the header - quint32 magic; - in >> magic; - if (magic != CHAT_FORMAT_MAGIC) { - qWarning() << "ERROR: Chat file has bad magic:" << file.fileName(); - continue; - } - - // Read the version - in >> version; - if (version < 1) { - qWarning() << "ERROR: Chat file has non supported version:" << file.fileName(); - continue; - } - - if (version <= 1) - in.setVersion(QDataStream::Qt_6_2); + // Read the version + in >> version; + if (version < 1) { + qWarning() << "ERROR: Chat file has non supported version:" << file.fileName(); + continue; } - qDebug() << "deserializing chat" << f.file; + if (version <= 1) + in.setVersion(QDataStream::Qt_6_2); + } - Chat *chat = new Chat; - chat->moveToThread(qApp->thread()); - if (!chat->deserialize(in, version)) { - qWarning() << "ERROR: Couldn't deserialize chat from file:" << file.fileName(); - } else { - emit chatRestored(chat); - } - if (f.oldFile) - file.remove(); // No longer storing in this directory - file.close(); + qDebug() << "deserializing chat" << f.file; + + Chat *chat = new Chat; + chat->moveToThread(qApp->thread()); + if (!chat->deserialize(in, version)) { + qWarning() << "ERROR: Couldn't deserialize chat from file:" << file.fileName(); + } else { + emit chatRestored(chat); + } + if (f.oldFile) + file.remove(); // No longer storing in this directory + file.close(); } qint64 elapsedTime = timer.elapsed(); @@ -249,35 +245,13 @@ void ChatListModel::restoreChat(Chat *chat) chat->setParent(this); connect(chat, &Chat::nameChanged, this, &ChatListModel::nameChanged); - if (m_dummyChat) { - beginResetModel(); - m_chats = QList({chat}); - setCurrentChat(chat); - delete m_dummyChat; - m_dummyChat = nullptr; - endResetModel(); - } else { - beginInsertRows(QModelIndex(), m_chats.size(), m_chats.size()); - m_chats.append(chat); - endInsertRows(); - } + beginInsertRows(QModelIndex(), m_chats.size(), m_chats.size()); + m_chats.append(chat); + endInsertRows(); } void ChatListModel::chatsRestoredFinished() { - if (m_dummyChat) { - beginResetModel(); - Chat *dummy = m_dummyChat; - m_dummyChat = nullptr; - m_chats.clear(); - addChat(); - delete dummy; - endResetModel(); - } - - if (m_chats.isEmpty()) - addChat(); - addServerChat(); } diff --git a/gpt4all-chat/chatlistmodel.h b/gpt4all-chat/chatlistmodel.h index d43d33d84c9f..8f4b1c2e8931 100644 --- a/gpt4all-chat/chatlistmodel.h +++ b/gpt4all-chat/chatlistmodel.h @@ -84,7 +84,7 @@ class ChatListModel : public QAbstractListModel Q_INVOKABLE void addChat() { // Don't add a new chat if we already have one - if (m_newChat || m_dummyChat) + if (m_newChat) return; // Create a new chat pointer and connect it to determine when it is populated @@ -101,18 +101,6 @@ class ChatListModel : public QAbstractListModel setCurrentChat(m_newChat); } - Q_INVOKABLE void addDummyChat() - { - // Create a new dummy chat pointer and don't connect it - m_dummyChat = new Chat(this); - beginInsertRows(QModelIndex(), 0, 0); - m_chats.prepend(m_dummyChat); - endInsertRows(); - emit countChanged(); - m_currentChat = m_dummyChat; - emit currentChatChanged(); - } - Q_INVOKABLE void addServerChat() { // Create a new dummy chat pointer and don't connect it @@ -252,7 +240,6 @@ private Q_SLOTS: private: Chat* m_newChat; - Chat* m_dummyChat; Chat* m_serverChat; Chat* m_currentChat; QList m_chats; diff --git a/gpt4all-chat/mysettings.cpp b/gpt4all-chat/mysettings.cpp index b1613786db02..60cc3765a0e9 100644 --- a/gpt4all-chat/mysettings.cpp +++ b/gpt4all-chat/mysettings.cpp @@ -10,8 +10,7 @@ #include static int default_threadCount = std::min(4, (int32_t) std::thread::hardware_concurrency()); -static bool default_saveChats = false; -static bool default_saveChatGPTChats = true; +static bool default_saveChatsContext = false; static bool default_serverChat = false; static QString default_userDefaultModel = "Application default"; static bool default_forceMetal = false; @@ -103,8 +102,7 @@ void MySettings::restoreApplicationDefaults() setFontSize(default_fontSize); setDevice(default_device); setThreadCount(default_threadCount); - setSaveChats(default_saveChats); - setSaveChatGPTChats(default_saveChatGPTChats); + setSaveChatsContext(default_saveChatsContext); setServerChat(default_serverChat); setModelPath(defaultLocalModelsPath()); setUserDefaultModel(default_userDefaultModel); @@ -397,40 +395,22 @@ void MySettings::setThreadCount(int c) emit threadCountChanged(); } -bool MySettings::saveChats() const +bool MySettings::saveChatsContext() const { QSettings setting; setting.sync(); - return setting.value("saveChats", default_saveChats).toBool(); + return setting.value("saveChatsContext", default_saveChatsContext).toBool(); } -void MySettings::setSaveChats(bool b) +void MySettings::setSaveChatsContext(bool b) { - if (saveChats() == b) + if (saveChatsContext() == b) return; QSettings setting; - setting.setValue("saveChats", b); + setting.setValue("saveChatsContext", b); setting.sync(); - emit saveChatsChanged(); -} - -bool MySettings::saveChatGPTChats() const -{ - QSettings setting; - setting.sync(); - return setting.value("saveChatGPTChats", default_saveChatGPTChats).toBool(); -} - -void MySettings::setSaveChatGPTChats(bool b) -{ - if (saveChatGPTChats() == b) - return; - - QSettings setting; - setting.setValue("saveChatGPTChats", b); - setting.sync(); - emit saveChatGPTChatsChanged(); + emit saveChatsContextChanged(); } bool MySettings::serverChat() const diff --git a/gpt4all-chat/mysettings.h b/gpt4all-chat/mysettings.h index 2f460864dcbc..54b8b6e62294 100644 --- a/gpt4all-chat/mysettings.h +++ b/gpt4all-chat/mysettings.h @@ -10,8 +10,7 @@ class MySettings : public QObject { Q_OBJECT Q_PROPERTY(int threadCount READ threadCount WRITE setThreadCount NOTIFY threadCountChanged) - Q_PROPERTY(bool saveChats READ saveChats WRITE setSaveChats NOTIFY saveChatsChanged) - Q_PROPERTY(bool saveChatGPTChats READ saveChatGPTChats WRITE setSaveChatGPTChats NOTIFY saveChatGPTChatsChanged) + Q_PROPERTY(bool saveChatsContext READ saveChatsContext WRITE setSaveChatsContext NOTIFY saveChatsContextChanged) Q_PROPERTY(bool serverChat READ serverChat WRITE setServerChat NOTIFY serverChatChanged) Q_PROPERTY(QString modelPath READ modelPath WRITE setModelPath NOTIFY modelPathChanged) Q_PROPERTY(QString userDefaultModel READ userDefaultModel WRITE setUserDefaultModel NOTIFY userDefaultModelChanged) @@ -64,10 +63,8 @@ class MySettings : public QObject // Application settings int threadCount() const; void setThreadCount(int c); - bool saveChats() const; - void setSaveChats(bool b); - bool saveChatGPTChats() const; - void setSaveChatGPTChats(bool b); + bool saveChatsContext() const; + void setSaveChatsContext(bool b); bool serverChat() const; void setServerChat(bool b); QString modelPath() const; @@ -122,8 +119,7 @@ class MySettings : public QObject void promptTemplateChanged(const ModelInfo &model); void systemPromptChanged(const ModelInfo &model); void threadCountChanged(); - void saveChatsChanged(); - void saveChatGPTChatsChanged(); + void saveChatsContextChanged(); void serverChatChanged(); void modelPathChanged(); void userDefaultModelChanged(); diff --git a/gpt4all-chat/network.cpp b/gpt4all-chat/network.cpp index fe54655f4fe8..6ca2a643cd49 100644 --- a/gpt4all-chat/network.cpp +++ b/gpt4all-chat/network.cpp @@ -317,16 +317,6 @@ void Network::sendNetworkToggled(bool isActive) sendMixpanelEvent("network_toggled", QVector{kv}); } -void Network::sendSaveChatsToggled(bool isActive) -{ - if (!MySettings::globalInstance()->networkUsageStatsActive()) - return; - KeyValue kv; - kv.key = QString("isActive"); - kv.value = QJsonValue(isActive); - sendMixpanelEvent("savechats_toggled", QVector{kv}); -} - void Network::sendNewChat(int count) { if (!MySettings::globalInstance()->networkUsageStatsActive()) diff --git a/gpt4all-chat/network.h b/gpt4all-chat/network.h index 2db751f3932e..98977967979c 100644 --- a/gpt4all-chat/network.h +++ b/gpt4all-chat/network.h @@ -38,7 +38,6 @@ public Q_SLOTS: void sendDownloadFinished(const QString &model, bool success); Q_INVOKABLE void sendSettingsDialog(); Q_INVOKABLE void sendNetworkToggled(bool active); - Q_INVOKABLE void sendSaveChatsToggled(bool active); Q_INVOKABLE void sendNewChat(int count); Q_INVOKABLE void sendRemoveChat(); Q_INVOKABLE void sendRenameChat(); diff --git a/gpt4all-chat/qml/ApplicationSettings.qml b/gpt4all-chat/qml/ApplicationSettings.qml index dbfdddcd09bc..fb6db0abe068 100644 --- a/gpt4all-chat/qml/ApplicationSettings.qml +++ b/gpt4all-chat/qml/ApplicationSettings.qml @@ -234,53 +234,35 @@ MySettingsTab { Accessible.description: ToolTip.text } Label { - id: saveChatsLabel - text: qsTr("Save chats to disk:") + id: saveChatsContextLabel + text: qsTr("Save chats context to disk:") color: theme.textColor font.pixelSize: theme.fontSizeLarge Layout.row: 7 Layout.column: 0 } MyCheckBox { - id: saveChatsBox + id: saveChatsContextBox Layout.row: 7 Layout.column: 1 - checked: MySettings.saveChats + checked: MySettings.saveChatsContext onClicked: { - Network.sendSaveChatsToggled(saveChatsBox.checked); - MySettings.saveChats = !MySettings.saveChats + MySettings.saveChatsContext = !MySettings.saveChatsContext } ToolTip.text: qsTr("WARNING: Saving chats to disk can be ~2GB per chat") ToolTip.visible: hovered } - Label { - id: saveChatGPTChatsLabel - text: qsTr("Save ChatGPT chats to disk:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge - Layout.row: 8 - Layout.column: 0 - } - MyCheckBox { - id: saveChatGPTChatsBox - Layout.row: 8 - Layout.column: 1 - checked: MySettings.saveChatGPTChats - onClicked: { - MySettings.saveChatGPTChats = !MySettings.saveChatGPTChats - } - } Label { id: serverChatLabel text: qsTr("Enable API server:") color: theme.textColor font.pixelSize: theme.fontSizeLarge - Layout.row: 9 + Layout.row: 8 Layout.column: 0 } MyCheckBox { id: serverChatBox - Layout.row: 9 + Layout.row: 8 Layout.column: 1 checked: MySettings.serverChat onClicked: { @@ -290,7 +272,7 @@ MySettingsTab { ToolTip.visible: hovered } Rectangle { - Layout.row: 10 + Layout.row: 9 Layout.column: 0 Layout.columnSpan: 3 Layout.fillWidth: true