diff --git a/chatlistmodel.cpp b/chatlistmodel.cpp index a621f3b5..6916f4d9 100644 --- a/chatlistmodel.cpp +++ b/chatlistmodel.cpp @@ -7,6 +7,21 @@ #define CHAT_FORMAT_MAGIC 0xF5D553CC #define CHAT_FORMAT_VERSION 100 +ChatListModel::ChatListModel(QObject *parent) + : QAbstractListModel(parent) + , m_newChat(nullptr) + , m_dummyChat(nullptr) + , m_currentChat(nullptr) + , m_shouldSaveChats(false) +{ + addDummyChat(); + + ChatsRestoreThread *thread = new ChatsRestoreThread; + connect(thread, &ChatsRestoreThread::chatsRestored, this, &ChatListModel::restoreChats); + connect(thread, &ChatsRestoreThread::finished, thread, &QObject::deleteLater); + thread->start(); +} + bool ChatListModel::shouldSaveChats() const { return m_shouldSaveChats; @@ -36,6 +51,8 @@ void ChatListModel::saveChats() const if (!m_shouldSaveChats) return; + QElapsedTimer timer; + timer.start(); const QString savePath = Download::globalInstance()->downloadLocalModelsPath(); for (Chat *chat : m_chats) { QString fileName = "gpt4all-" + chat->id() + ".chat"; @@ -58,11 +75,15 @@ void ChatListModel::saveChats() const } file.close(); } + qint64 elapsedTime = timer.elapsed(); + qDebug() << "serializing chats took:" << elapsedTime << "ms"; } -void ChatListModel::restoreChats() +void ChatsRestoreThread::run() { - beginResetModel(); + QElapsedTimer timer; + timer.start(); + QList chats; { // Look for any files in the original spot which was the settings config directory QSettings settings; @@ -80,13 +101,13 @@ void ChatListModel::restoreChats() continue; } QDataStream in(&file); - Chat *chat = new Chat(this); + Chat *chat = new Chat; + chat->moveToThread(qApp->thread()); if (!chat->deserialize(in)) { qWarning() << "ERROR: Couldn't deserialize chat from file:" << file.fileName(); file.remove(); } else { - connect(chat, &Chat::nameChanged, this, &ChatListModel::nameChanged); - m_chats.append(chat); + chats.append(chat); } qDebug() << "deserializing chat" << f; file.remove(); // No longer storing in this directory @@ -126,20 +147,51 @@ void ChatListModel::restoreChats() if (version <= 100) in.setVersion(QDataStream::Qt_6_5); - Chat *chat = new Chat(this); + Chat *chat = new Chat; + chat->moveToThread(qApp->thread()); if (!chat->deserialize(in)) { qWarning() << "ERROR: Couldn't deserialize chat from file:" << file.fileName(); file.remove(); } else { - connect(chat, &Chat::nameChanged, this, &ChatListModel::nameChanged); - m_chats.append(chat); + chats.append(chat); } qDebug() << "deserializing chat" << f; file.close(); } } - std::sort(m_chats.begin(), m_chats.end(), [](const Chat* a, const Chat* b) { + std::sort(chats.begin(), chats.end(), [](const Chat* a, const Chat* b) { return a->creationDate() > b->creationDate(); }); + qint64 elapsedTime = timer.elapsed(); + qDebug() << "deserializing chats took:" << elapsedTime << "ms"; + + emit chatsRestored(chats); +} + +void ChatListModel::restoreChats(const QList &chats) +{ + for (Chat* chat : chats) { + chat->setParent(this); + connect(chat, &Chat::nameChanged, this, &ChatListModel::nameChanged); + } + + beginResetModel(); + + // Setup the new chats + m_chats = chats; + + if (!m_chats.isEmpty()) { + Chat *firstChat = m_chats.first(); + if (firstChat->chatModel()->count() < 2) + setNewChat(firstChat); + else + setCurrentChat(firstChat); + } else + addChat(); + + // Clean up the dummy + delete m_dummyChat; + m_dummyChat = nullptr; + endResetModel(); } diff --git a/chatlistmodel.h b/chatlistmodel.h index ee28baf1..f2e89135 100644 --- a/chatlistmodel.h +++ b/chatlistmodel.h @@ -4,6 +4,16 @@ #include #include "chat.h" +class ChatsRestoreThread : public QThread +{ + Q_OBJECT +public: + void run() override; + +Q_SIGNALS: + void chatsRestored(QList chats); +}; + class ChatListModel : public QAbstractListModel { Q_OBJECT @@ -12,13 +22,7 @@ class ChatListModel : public QAbstractListModel Q_PROPERTY(bool shouldSaveChats READ shouldSaveChats WRITE setShouldSaveChats NOTIFY shouldSaveChatsChanged) public: - explicit ChatListModel(QObject *parent = nullptr) - : QAbstractListModel(parent) - , m_currentChat(nullptr) - , m_newChat(nullptr) - , m_shouldSaveChats(false) - { - } + explicit ChatListModel(QObject *parent = nullptr); enum Roles { IdRole = Qt::UserRole + 1, @@ -61,7 +65,7 @@ public: Q_INVOKABLE void addChat() { // Don't add a new chat if we already have one - if (m_newChat) + if (m_newChat || m_dummyChat) return; // Create a new chat pointer and connect it to determine when it is populated @@ -78,6 +82,18 @@ public: 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(); + } + void setNewChat(Chat* chat) { // Don't add a new chat if we already have one @@ -101,7 +117,6 @@ public: removeChatFile(chat); - emit disconnectChat(chat); if (chat == m_newChat) { m_newChat->disconnect(this); m_newChat = nullptr; @@ -140,13 +155,9 @@ public: return; } - if (m_currentChat) { - if (m_currentChat->isModelLoaded()) - m_currentChat->unloadModel(); - emit disconnect(m_currentChat); - } + if (m_currentChat && m_currentChat->isModelLoaded()) + m_currentChat->unloadModel(); - emit connectChat(chat); m_currentChat = chat; if (!m_currentChat->isModelLoaded()) m_currentChat->reloadModel(); @@ -163,12 +174,10 @@ public: void removeChatFile(Chat *chat) const; void saveChats() const; - void restoreChats(); + void restoreChats(const QList &chats); Q_SIGNALS: void countChanged(); - void connectChat(Chat*); - void disconnectChat(Chat*); void currentChatChanged(); void shouldSaveChatsChanged(); @@ -206,6 +215,7 @@ private Q_SLOTS: private: bool m_shouldSaveChats; Chat* m_newChat; + Chat* m_dummyChat; Chat* m_currentChat; QList m_chats; }; diff --git a/llm.cpp b/llm.cpp index 2689e47f..fda940de 100644 --- a/llm.cpp +++ b/llm.cpp @@ -24,16 +24,6 @@ LLM::LLM() { connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &LLM::aboutToQuit); - - m_chatListModel->restoreChats(); - if (m_chatListModel->count()) { - Chat *firstChat = m_chatListModel->get(0); - if (firstChat->chatModel()->count() < 2) - m_chatListModel->setNewChat(firstChat); - else - m_chatListModel->setCurrentChat(firstChat); - } else - m_chatListModel->addChat(); } bool LLM::checkForUpdates() const