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(); stream << m_modelInfo.filename();
if (version > 2) if (version > 2)
stream << m_collections; 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; return false;
if (!m_chatModel->serialize(stream, version)) if (!m_chatModel->serialize(stream, version))
return false; return false;
@ -413,7 +417,6 @@ bool Chat::deserialize(QDataStream &stream, int version)
if (!m_modelInfo.id().isEmpty()) if (!m_modelInfo.id().isEmpty())
emit modelInfoChanged(); emit modelInfoChanged();
bool deserializeKV = true; // make this a setting
bool discardKV = m_modelInfo.id().isEmpty(); 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 // 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; stream >> m_collections;
emit collectionListChanged(m_collections); emit collectionListChanged(m_collections);
} }
bool deserializeKV = true;
if (version > 5)
stream >> deserializeKV;
m_llmodel->setModelInfo(m_modelInfo); m_llmodel->setModelInfo(m_modelInfo);
if (!m_llmodel->deserialize(stream, version, deserializeKV, discardKV)) if (!m_llmodel->deserialize(stream, version, deserializeKV, discardKV))
return false; return false;

View File

@ -54,6 +54,8 @@ public:
} }
ChatModel *chatModel() { return m_chatModel; } ChatModel *chatModel() { return m_chatModel; }
bool isNewChat() const { return m_name == tr("New Chat") && !m_chatModel->count(); }
Q_INVOKABLE void reset(); Q_INVOKABLE void reset();
Q_INVOKABLE void processSystemPrompt(); Q_INVOKABLE void processSystemPrompt();
Q_INVOKABLE bool isModelLoaded() const; Q_INVOKABLE bool isModelLoaded() const;

View File

@ -5,7 +5,7 @@
#include <QDataStream> #include <QDataStream>
#define CHAT_FORMAT_MAGIC 0xF5D553CC #define CHAT_FORMAT_MAGIC 0xF5D553CC
#define CHAT_FORMAT_VERSION 5 #define CHAT_FORMAT_VERSION 6
class MyChatListModel: public ChatListModel { }; class MyChatListModel: public ChatListModel { };
Q_GLOBAL_STATIC(MyChatListModel, chatListModelInstance) Q_GLOBAL_STATIC(MyChatListModel, chatListModelInstance)
@ -17,11 +17,10 @@ ChatListModel *ChatListModel::globalInstance()
ChatListModel::ChatListModel() ChatListModel::ChatListModel()
: QAbstractListModel(nullptr) : QAbstractListModel(nullptr)
, m_newChat(nullptr) , m_newChat(nullptr)
, m_dummyChat(nullptr)
, m_serverChat(nullptr) , m_serverChat(nullptr)
, m_currentChat(nullptr) , m_currentChat(nullptr)
{ {
addDummyChat(); addChat();
ChatsRestoreThread *thread = new ChatsRestoreThread; ChatsRestoreThread *thread = new ChatsRestoreThread;
connect(thread, &ChatsRestoreThread::chatRestored, this, &ChatListModel::restoreChat); connect(thread, &ChatsRestoreThread::chatRestored, this, &ChatListModel::restoreChat);
@ -59,10 +58,7 @@ void ChatListModel::saveChats()
for (Chat *chat : m_chats) { for (Chat *chat : m_chats) {
if (chat == m_serverChat) if (chat == m_serverChat)
continue; continue;
const bool isChatGPT = chat->modelInfo().isChatGPT; if (chat->isNewChat())
if (!isChatGPT && !MySettings::globalInstance()->saveChats())
continue;
if (isChatGPT && !MySettings::globalInstance()->saveChatGPTChats())
continue; continue;
toSave.append(chat); toSave.append(chat);
} }
@ -249,35 +245,13 @@ void ChatListModel::restoreChat(Chat *chat)
chat->setParent(this); chat->setParent(this);
connect(chat, &Chat::nameChanged, this, &ChatListModel::nameChanged); 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()); beginInsertRows(QModelIndex(), m_chats.size(), m_chats.size());
m_chats.append(chat); m_chats.append(chat);
endInsertRows(); endInsertRows();
} }
}
void ChatListModel::chatsRestoredFinished() 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(); addServerChat();
} }

View File

@ -84,7 +84,7 @@ public:
Q_INVOKABLE void addChat() Q_INVOKABLE void addChat()
{ {
// Don't add a new chat if we already have one // Don't add a new chat if we already have one
if (m_newChat || m_dummyChat) if (m_newChat)
return; return;
// Create a new chat pointer and connect it to determine when it is populated // Create a new chat pointer and connect it to determine when it is populated
@ -101,18 +101,6 @@ public:
setCurrentChat(m_newChat); 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() Q_INVOKABLE void addServerChat()
{ {
// Create a new dummy chat pointer and don't connect it // Create a new dummy chat pointer and don't connect it
@ -252,7 +240,6 @@ private Q_SLOTS:
private: private:
Chat* m_newChat; Chat* m_newChat;
Chat* m_dummyChat;
Chat* m_serverChat; Chat* m_serverChat;
Chat* m_currentChat; Chat* m_currentChat;
QList<Chat*> m_chats; QList<Chat*> m_chats;

View File

@ -10,8 +10,7 @@
#include <QUrl> #include <QUrl>
static int default_threadCount = std::min(4, (int32_t) std::thread::hardware_concurrency()); static int default_threadCount = std::min(4, (int32_t) std::thread::hardware_concurrency());
static bool default_saveChats = false; static bool default_saveChatsContext = false;
static bool default_saveChatGPTChats = true;
static bool default_serverChat = false; static bool default_serverChat = false;
static QString default_userDefaultModel = "Application default"; static QString default_userDefaultModel = "Application default";
static bool default_forceMetal = false; static bool default_forceMetal = false;
@ -103,8 +102,7 @@ void MySettings::restoreApplicationDefaults()
setFontSize(default_fontSize); setFontSize(default_fontSize);
setDevice(default_device); setDevice(default_device);
setThreadCount(default_threadCount); setThreadCount(default_threadCount);
setSaveChats(default_saveChats); setSaveChatsContext(default_saveChatsContext);
setSaveChatGPTChats(default_saveChatGPTChats);
setServerChat(default_serverChat); setServerChat(default_serverChat);
setModelPath(defaultLocalModelsPath()); setModelPath(defaultLocalModelsPath());
setUserDefaultModel(default_userDefaultModel); setUserDefaultModel(default_userDefaultModel);
@ -397,40 +395,22 @@ void MySettings::setThreadCount(int c)
emit threadCountChanged(); emit threadCountChanged();
} }
bool MySettings::saveChats() const bool MySettings::saveChatsContext() const
{ {
QSettings setting; QSettings setting;
setting.sync(); 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; return;
QSettings setting; QSettings setting;
setting.setValue("saveChats", b); setting.setValue("saveChatsContext", b);
setting.sync(); setting.sync();
emit saveChatsChanged(); emit saveChatsContextChanged();
}
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();
} }
bool MySettings::serverChat() const bool MySettings::serverChat() const

View File

@ -10,8 +10,7 @@ class MySettings : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int threadCount READ threadCount WRITE setThreadCount NOTIFY threadCountChanged) Q_PROPERTY(int threadCount READ threadCount WRITE setThreadCount NOTIFY threadCountChanged)
Q_PROPERTY(bool saveChats READ saveChats WRITE setSaveChats NOTIFY saveChatsChanged) Q_PROPERTY(bool saveChatsContext READ saveChatsContext WRITE setSaveChatsContext NOTIFY saveChatsContextChanged)
Q_PROPERTY(bool saveChatGPTChats READ saveChatGPTChats WRITE setSaveChatGPTChats NOTIFY saveChatGPTChatsChanged)
Q_PROPERTY(bool serverChat READ serverChat WRITE setServerChat NOTIFY serverChatChanged) Q_PROPERTY(bool serverChat READ serverChat WRITE setServerChat NOTIFY serverChatChanged)
Q_PROPERTY(QString modelPath READ modelPath WRITE setModelPath NOTIFY modelPathChanged) Q_PROPERTY(QString modelPath READ modelPath WRITE setModelPath NOTIFY modelPathChanged)
Q_PROPERTY(QString userDefaultModel READ userDefaultModel WRITE setUserDefaultModel NOTIFY userDefaultModelChanged) Q_PROPERTY(QString userDefaultModel READ userDefaultModel WRITE setUserDefaultModel NOTIFY userDefaultModelChanged)
@ -64,10 +63,8 @@ public:
// Application settings // Application settings
int threadCount() const; int threadCount() const;
void setThreadCount(int c); void setThreadCount(int c);
bool saveChats() const; bool saveChatsContext() const;
void setSaveChats(bool b); void setSaveChatsContext(bool b);
bool saveChatGPTChats() const;
void setSaveChatGPTChats(bool b);
bool serverChat() const; bool serverChat() const;
void setServerChat(bool b); void setServerChat(bool b);
QString modelPath() const; QString modelPath() const;
@ -122,8 +119,7 @@ Q_SIGNALS:
void promptTemplateChanged(const ModelInfo &model); void promptTemplateChanged(const ModelInfo &model);
void systemPromptChanged(const ModelInfo &model); void systemPromptChanged(const ModelInfo &model);
void threadCountChanged(); void threadCountChanged();
void saveChatsChanged(); void saveChatsContextChanged();
void saveChatGPTChatsChanged();
void serverChatChanged(); void serverChatChanged();
void modelPathChanged(); void modelPathChanged();
void userDefaultModelChanged(); void userDefaultModelChanged();

View File

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

View File

@ -38,7 +38,6 @@ public Q_SLOTS:
void sendDownloadFinished(const QString &model, bool success); void sendDownloadFinished(const QString &model, bool success);
Q_INVOKABLE void sendSettingsDialog(); Q_INVOKABLE void sendSettingsDialog();
Q_INVOKABLE void sendNetworkToggled(bool active); Q_INVOKABLE void sendNetworkToggled(bool active);
Q_INVOKABLE void sendSaveChatsToggled(bool active);
Q_INVOKABLE void sendNewChat(int count); Q_INVOKABLE void sendNewChat(int count);
Q_INVOKABLE void sendRemoveChat(); Q_INVOKABLE void sendRemoveChat();
Q_INVOKABLE void sendRenameChat(); Q_INVOKABLE void sendRenameChat();

View File

@ -234,53 +234,35 @@ MySettingsTab {
Accessible.description: ToolTip.text Accessible.description: ToolTip.text
} }
Label { Label {
id: saveChatsLabel id: saveChatsContextLabel
text: qsTr("Save chats to disk:") text: qsTr("Save chats context to disk:")
color: theme.textColor color: theme.textColor
font.pixelSize: theme.fontSizeLarge font.pixelSize: theme.fontSizeLarge
Layout.row: 7 Layout.row: 7
Layout.column: 0 Layout.column: 0
} }
MyCheckBox { MyCheckBox {
id: saveChatsBox id: saveChatsContextBox
Layout.row: 7 Layout.row: 7
Layout.column: 1 Layout.column: 1
checked: MySettings.saveChats checked: MySettings.saveChatsContext
onClicked: { onClicked: {
Network.sendSaveChatsToggled(saveChatsBox.checked); MySettings.saveChatsContext = !MySettings.saveChatsContext
MySettings.saveChats = !MySettings.saveChats
} }
ToolTip.text: qsTr("WARNING: Saving chats to disk can be ~2GB per chat") ToolTip.text: qsTr("WARNING: Saving chats to disk can be ~2GB per chat")
ToolTip.visible: hovered 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 { Label {
id: serverChatLabel id: serverChatLabel
text: qsTr("Enable API server:") text: qsTr("Enable API server:")
color: theme.textColor color: theme.textColor
font.pixelSize: theme.fontSizeLarge font.pixelSize: theme.fontSizeLarge
Layout.row: 9 Layout.row: 8
Layout.column: 0 Layout.column: 0
} }
MyCheckBox { MyCheckBox {
id: serverChatBox id: serverChatBox
Layout.row: 9 Layout.row: 8
Layout.column: 1 Layout.column: 1
checked: MySettings.serverChat checked: MySettings.serverChat
onClicked: { onClicked: {
@ -290,7 +272,7 @@ MySettingsTab {
ToolTip.visible: hovered ToolTip.visible: hovered
} }
Rectangle { Rectangle {
Layout.row: 10 Layout.row: 9
Layout.column: 0 Layout.column: 0
Layout.columnSpan: 3 Layout.columnSpan: 3
Layout.fillWidth: true Layout.fillWidth: true