gpt4all/gpt4all-chat/chatlistmodel.h

289 lines
7.5 KiB
C
Raw Normal View History

2023-05-02 00:13:20 +03:00
#ifndef CHATLISTMODEL_H
#define CHATLISTMODEL_H
#include "chat.h"
#include "chatllm.h"
#include "chatmodel.h"
#include <QAbstractListModel>
#include <QByteArray>
#include <QDebug>
#include <QHash>
#include <QList>
#include <QObject>
#include <QThread>
#include <QVariant>
#include <QVector>
#include <Qt>
#include <QtGlobal>
#include <QtLogging>
2023-05-02 00:13:20 +03:00
class ChatsRestoreThread : public QThread
{
Q_OBJECT
public:
void run() override;
Q_SIGNALS:
void chatRestored(Chat *chat);
};
class ChatSaver : public QObject
{
Q_OBJECT
public:
explicit ChatSaver();
void stop();
Q_SIGNALS:
void saveChatsFinished();
public Q_SLOTS:
void saveChats(const QVector<Chat*> &chats);
private:
QThread m_thread;
};
2023-05-02 00:13:20 +03:00
class ChatListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(Chat *currentChat READ currentChat WRITE setCurrentChat NOTIFY currentChatChanged)
public:
2023-06-22 22:44:49 +03:00
static ChatListModel *globalInstance();
2023-05-02 00:13:20 +03:00
enum Roles {
IdRole = Qt::UserRole + 1,
NameRole,
SectionRole
2023-05-02 00:13:20 +03:00
};
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
Q_UNUSED(parent)
return m_chats.size();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if (!index.isValid() || index.row() < 0 || index.row() >= m_chats.size())
return QVariant();
const Chat *item = m_chats.at(index.row());
switch (role) {
case IdRole:
return item->id();
case NameRole:
return item->name();
case SectionRole: {
if (item == m_serverChat)
return QString();
const QDate date = QDate::currentDate();
const QDate itemDate = item->creationDate().date();
if (date == itemDate)
return tr("TODAY");
else if (itemDate >= date.addDays(-7))
return tr("THIS WEEK");
else if (itemDate >= date.addMonths(-1))
return tr("THIS MONTH");
else if (itemDate >= date.addMonths(-6))
return tr("LAST SIX MONTHS");
else if (itemDate.year() == date.year())
return tr("THIS YEAR");
else if (itemDate.year() == date.year() - 1)
return tr("LAST YEAR");
else
return QString::number(itemDate.year());
}
2023-05-02 00:13:20 +03:00
}
return QVariant();
}
QHash<int, QByteArray> roleNames() const override
{
QHash<int, QByteArray> roles;
roles[IdRole] = "id";
roles[NameRole] = "name";
roles[SectionRole] = "section";
2023-05-02 00:13:20 +03:00
return roles;
}
bool shouldSaveChats() const;
void setShouldSaveChats(bool b);
bool shouldSaveChatGPTChats() const;
void setShouldSaveChatGPTChats(bool b);
Q_INVOKABLE void loadChats();
2023-05-02 14:48:40 +03:00
Q_INVOKABLE void addChat()
2023-05-02 00:13:20 +03:00
{
// Select the existing new chat if we already have one
if (m_newChat) {
setCurrentChat(m_newChat);
2023-05-02 14:48:40 +03:00
return;
}
2023-05-02 14:48:40 +03:00
// Create a new chat pointer and connect it to determine when it is populated
m_newChat = new Chat(this);
connect(m_newChat->chatModel(), &ChatModel::countChanged,
this, &ChatListModel::newChatCountChanged);
2023-05-02 18:19:17 +03:00
connect(m_newChat, &Chat::nameChanged,
this, &ChatListModel::nameChanged);
2023-05-02 14:48:40 +03:00
beginInsertRows(QModelIndex(), 0, 0);
2023-05-02 14:48:40 +03:00
m_chats.prepend(m_newChat);
2023-05-02 00:13:20 +03:00
endInsertRows();
emit countChanged();
2023-05-02 14:48:40 +03:00
setCurrentChat(m_newChat);
2023-05-02 00:13:20 +03:00
}
2023-05-11 23:46:25 +03:00
Q_INVOKABLE void addServerChat()
{
// Create a new dummy chat pointer and don't connect it
if (m_serverChat)
return;
m_serverChat = new Chat(true /*isServer*/, this);
beginInsertRows(QModelIndex(), m_chats.size(), m_chats.size());
m_chats.append(m_serverChat);
endInsertRows();
emit countChanged();
}
2023-05-02 00:13:20 +03:00
Q_INVOKABLE void removeChat(Chat* chat)
{
Q_ASSERT(chat != m_serverChat);
2023-05-02 00:13:20 +03:00
if (!m_chats.contains(chat)) {
qWarning() << "WARNING: Removing chat failed with id" << chat->id();
2023-05-02 00:13:20 +03:00
return;
}
removeChatFile(chat);
2023-05-02 16:07:28 +03:00
if (chat == m_newChat) {
m_newChat->disconnect(this);
m_newChat = nullptr;
}
chat->markForDeletion();
2023-05-02 00:13:20 +03:00
const int index = m_chats.indexOf(chat);
if (m_chats.count() < 3 /*m_serverChat included*/) {
2023-05-02 03:56:53 +03:00
addChat();
} else {
int nextIndex;
if (index == m_chats.count() - 2 /*m_serverChat is last*/)
2023-05-02 03:56:53 +03:00
nextIndex = index - 1;
else
nextIndex = index + 1;
Chat *nextChat = get(nextIndex);
Q_ASSERT(nextChat);
setCurrentChat(nextChat);
}
2023-05-02 18:26:21 +03:00
const int newIndex = m_chats.indexOf(chat);
beginRemoveRows(QModelIndex(), newIndex, newIndex);
2023-05-02 00:13:20 +03:00
m_chats.removeAll(chat);
endRemoveRows();
chat->unloadAndDeleteLater();
2023-05-02 00:13:20 +03:00
}
Chat *currentChat() const
{
return m_currentChat;
}
void setCurrentChat(Chat *chat)
{
if (!m_chats.contains(chat)) {
qWarning() << "ERROR: Setting current chat failed with id" << chat->id();
2023-05-02 00:13:20 +03:00
return;
}
if (m_currentChat && m_currentChat != m_serverChat)
m_currentChat->unloadModel();
2023-05-02 00:13:20 +03:00
m_currentChat = chat;
emit currentChatChanged();
if (!m_currentChat->isModelLoaded() && m_currentChat != m_serverChat)
m_currentChat->trySwitchContextOfLoadedModel();
2023-05-02 00:13:20 +03:00
}
Q_INVOKABLE Chat* get(int index)
{
if (index < 0 || index >= m_chats.size()) return nullptr;
return m_chats.at(index);
}
int count() const { return m_chats.size(); }
// stop ChatLLM threads for clean shutdown
void destroyChats()
{
for (auto *chat: m_chats) { chat->destroy(); }
ChatLLM::destroyStore();
}
void removeChatFile(Chat *chat) const;
Q_INVOKABLE void saveChats();
void restoreChat(Chat *chat);
void chatsRestoredFinished();
2023-05-11 23:46:25 +03:00
public Q_SLOTS:
void handleServerEnabledChanged();
2023-05-02 00:13:20 +03:00
Q_SIGNALS:
void countChanged();
void currentChatChanged();
void chatsSavedFinished();
void requestSaveChats(const QVector<Chat*> &);
void saveChatsFinished();
2023-05-02 00:13:20 +03:00
2023-05-02 14:48:40 +03:00
private Q_SLOTS:
void newChatCountChanged()
{
Q_ASSERT(m_newChat && m_newChat->chatModel()->count());
2023-05-02 18:19:17 +03:00
m_newChat->chatModel()->disconnect(this);
2023-05-02 14:48:40 +03:00
m_newChat = nullptr;
}
2023-05-02 18:19:17 +03:00
void nameChanged()
{
Chat *chat = qobject_cast<Chat *>(sender());
if (!chat)
return;
int row = m_chats.indexOf(chat);
if (row < 0 || row >= m_chats.size())
return;
QModelIndex index = createIndex(row, 0);
emit dataChanged(index, index, {NameRole});
}
2023-05-02 18:26:21 +03:00
void printChats()
{
for (auto c : m_chats) {
qDebug() << c->name()
<< (c == m_currentChat ? "currentChat: true" : "currentChat: false")
<< (c == m_newChat ? "newChat: true" : "newChat: false");
}
}
2023-05-02 00:13:20 +03:00
private:
2023-12-09 20:55:46 +03:00
Chat* m_newChat = nullptr;
Chat* m_serverChat = nullptr;
Chat* m_currentChat = nullptr;
2023-05-02 00:13:20 +03:00
QList<Chat*> m_chats;
2023-06-22 22:44:49 +03:00
private:
explicit ChatListModel();
~ChatListModel() {}
friend class MyChatListModel;
2023-05-02 00:13:20 +03:00
};
#endif // CHATITEMMODEL_H