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.
This commit is contained in:
Adam Treat 2023-10-11 10:23:33 -04:00 committed by AT
parent aed2068342
commit 908aec27fe
9 changed files with 74 additions and 156 deletions

View File

@ -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;

View File

@ -54,6 +54,8 @@ public:
}
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;

View File

@ -5,7 +5,7 @@
#include <QDataStream>
#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);
}
@ -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*>({chat});
setCurrentChat(chat);
delete m_dummyChat;
m_dummyChat = nullptr;
endResetModel();
} else {
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();
}

View File

@ -84,7 +84,7 @@ public:
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 @@ 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();
}
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<Chat*> m_chats;

View File

@ -10,8 +10,7 @@
#include <QUrl>
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

View File

@ -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 @@ public:
// 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 @@ Q_SIGNALS:
void promptTemplateChanged(const ModelInfo &model);
void systemPromptChanged(const ModelInfo &model);
void threadCountChanged();
void saveChatsChanged();
void saveChatGPTChatsChanged();
void saveChatsContextChanged();
void serverChatChanged();
void modelPathChanged();
void userDefaultModelChanged();

View File

@ -317,16 +317,6 @@ void Network::sendNetworkToggled(bool isActive)
sendMixpanelEvent("network_toggled", QVector<KeyValue>{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<KeyValue>{kv});
}
void Network::sendNewChat(int count)
{
if (!MySettings::globalInstance()->networkUsageStatsActive())

View File

@ -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();

View File

@ -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