Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save chats on quit, even if window isn't closed first #3387

Merged
merged 2 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion gpt4all-chat/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

## Fixed
### Fixed
- Fix the timeout error in code interpreter ([#3369](https://github.com/nomic-ai/gpt4all/pull/3369))
- Fix code interpreter console.log not accepting multiple arguments ([#3371](https://github.com/nomic-ai/gpt4all/pull/3371))
- Remove 'X is defined' checks from templates as they work incorrectly with Jinja2Cpp ([#3372](https://github.com/nomic-ai/gpt4all/pull/3372))
- Jinja2Cpp: Add 'if' requirement for 'else' parsing to fix crash ([#3373](https://github.com/nomic-ai/gpt4all/pull/3373))
- Save chats on quit, even if the window isn't closed first ([#3387](https://github.com/nomic-ai/gpt4all/pull/3387))

## [3.6.1] - 2024-12-20

Expand Down
6 changes: 3 additions & 3 deletions gpt4all-chat/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Window {
systemTrayIcon.shouldClose = true;
window.shouldClose = true;
savingPopup.open();
ChatListModel.saveChats();
ChatListModel.saveChatsForQuit();
}
}
}
Expand Down Expand Up @@ -231,8 +231,8 @@ Window {

window.shouldClose = true;
savingPopup.open();
ChatListModel.saveChats();
close.accepted = false
ChatListModel.saveChatsForQuit();
close.accepted = false;
}

Connections {
Expand Down
48 changes: 37 additions & 11 deletions gpt4all-chat/src/chatlistmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ void ChatListModel::loadChats()
connect(thread, &ChatsRestoreThread::finished, thread, &QObject::deleteLater);
thread->start();

ChatSaver *saver = new ChatSaver;
connect(this, &ChatListModel::requestSaveChats, saver, &ChatSaver::saveChats, Qt::QueuedConnection);
connect(saver, &ChatSaver::saveChatsFinished, this, &ChatListModel::saveChatsFinished, Qt::QueuedConnection);
m_chatSaver = std::make_unique<ChatSaver>();
connect(this, &ChatListModel::requestSaveChats, m_chatSaver.get(), &ChatSaver::saveChats, Qt::QueuedConnection);
connect(m_chatSaver.get(), &ChatSaver::saveChatsFinished, this, &ChatListModel::saveChatsFinished, Qt::QueuedConnection);
// save chats on application quit
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &ChatListModel::saveChatsSync);

connect(MySettings::globalInstance(), &MySettings::serverChatChanged, this, &ChatListModel::handleServerEnabledChanged);
}
Expand All @@ -78,16 +80,24 @@ ChatSaver::ChatSaver()
m_thread.start();
}

ChatSaver::~ChatSaver()
{
m_thread.quit();
m_thread.wait();
}

QVector<Chat *> ChatListModel::getChatsToSave() const
{
QVector<Chat *> toSave;
for (auto *chat : m_chats)
if (chat != m_serverChat && !chat->isNewChat())
toSave << chat;
return toSave;
}

void ChatListModel::saveChats()
{
QVector<Chat*> toSave;
for (Chat *chat : m_chats) {
if (chat == m_serverChat)
continue;
if (chat->isNewChat())
continue;
toSave.append(chat);
}
auto toSave = getChatsToSave();
if (toSave.isEmpty()) {
emit saveChatsFinished();
return;
Expand All @@ -96,8 +106,24 @@ void ChatListModel::saveChats()
emit requestSaveChats(toSave);
}

void ChatListModel::saveChatsForQuit()
{
saveChats();
m_startedFinalSave = true;
}

void ChatListModel::saveChatsSync()
{
auto toSave = getChatsToSave();
if (!m_startedFinalSave && !toSave.isEmpty())
m_chatSaver->saveChats(toSave);
}

void ChatSaver::saveChats(const QVector<Chat *> &chats)
{
// we can be called from the main thread instead of a worker thread at quit time, so take a lock
QMutexLocker locker(&m_mutex);

QElapsedTimer timer;
timer.start();
const QString savePath = MySettings::globalInstance()->modelPath();
Expand Down
15 changes: 15 additions & 0 deletions gpt4all-chat/src/chatlistmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <QDebug>
#include <QHash>
#include <QList>
#include <QMutex>
#include <QObject>
#include <QThread>
#include <QVariant>
Expand All @@ -18,6 +19,9 @@
#include <QtGlobal>
#include <QtLogging>

#include <memory>


class ChatsRestoreThread : public QThread
{
Q_OBJECT
Expand All @@ -33,6 +37,7 @@ class ChatSaver : public QObject
Q_OBJECT
public:
explicit ChatSaver();
~ChatSaver() override;

Q_SIGNALS:
void saveChatsFinished();
Expand All @@ -42,6 +47,7 @@ public Q_SLOTS:

private:
QThread m_thread;
QMutex m_mutex;
};

class ChatListModel : public QAbstractListModel
Expand Down Expand Up @@ -228,6 +234,7 @@ class ChatListModel : public QAbstractListModel

void removeChatFile(Chat *chat) const;
Q_INVOKABLE void saveChats();
Q_INVOKABLE void saveChatsForQuit();
void restoreChat(Chat *chat);
void chatsRestoredFinished();

Expand All @@ -244,6 +251,9 @@ public Q_SLOTS:
bool eventFilter(QObject *obj, QEvent *ev) override;

private Q_SLOTS:
// Used with QCoreApplication::aboutToQuit. Does not require an event loop.
void saveChatsSync();

void newChatCountChanged()
{
Q_ASSERT(m_newChat && m_newChat->chatModel()->count());
Expand Down Expand Up @@ -274,11 +284,16 @@ private Q_SLOTS:
}
}

private:
QVector<Chat *> getChatsToSave() const;

private:
Chat* m_newChat = nullptr;
Chat* m_serverChat = nullptr;
Chat* m_currentChat = nullptr;
QList<Chat*> m_chats;
std::unique_ptr<ChatSaver> m_chatSaver;
bool m_startedFinalSave = false;

private:
explicit ChatListModel();
Expand Down
Loading