More extensive usage stats to help diagnose errors and problems in the ui.

This commit is contained in:
Adam Treat 2023-05-02 20:31:17 -04:00
parent f8c3ae6cc7
commit db094c5b92
14 changed files with 253 additions and 18 deletions

View File

@ -64,6 +64,7 @@ qt_add_executable(chat
download.h download.cpp
network.h network.cpp
llm.h llm.cpp
sysinfo.h
)
qt_add_qml_module(chat

View File

@ -18,6 +18,7 @@ Chat::Chat(QObject *parent)
connect(m_llmodel, &ChatLLM::threadCountChanged, this, &Chat::threadCountChanged, Qt::QueuedConnection);
connect(m_llmodel, &ChatLLM::threadCountChanged, this, &Chat::syncThreadCount, Qt::QueuedConnection);
connect(m_llmodel, &ChatLLM::recalcChanged, this, &Chat::recalcChanged, Qt::QueuedConnection);
connect(m_llmodel, &ChatLLM::recalcChanged, this, &Chat::handleRecalculating, Qt::QueuedConnection);
connect(m_llmodel, &ChatLLM::generatedNameChanged, this, &Chat::generatedNameChanged, Qt::QueuedConnection);
connect(this, &Chat::promptRequested, m_llmodel, &ChatLLM::prompt, Qt::QueuedConnection);
@ -37,7 +38,6 @@ Chat::Chat(QObject *parent)
void Chat::reset()
{
stopGenerating();
qDebug() << "reset blocking";
emit resetContextRequested(); // blocking queued connection
m_id = Network::globalInstance()->generateUniqueId();
emit idChanged();
@ -80,8 +80,10 @@ void Chat::responseStopped()
{
m_responseInProgress = false;
emit responseInProgressChanged();
if (m_llmodel->generatedName().isEmpty())
if (m_llmodel->generatedName().isEmpty()) {
Network::globalInstance()->sendChatStarted();
emit generateNameRequested();
}
}
QString Chat::modelName() const
@ -143,3 +145,8 @@ void Chat::generatedNameChanged()
m_name = words.mid(0, wordCount).join(' ');
emit nameChanged();
}
void Chat::handleRecalculating()
{
Network::globalInstance()->sendRecalculatingContext(m_chatModel->count());
}

1
chat.h
View File

@ -79,6 +79,7 @@ private Q_SLOTS:
void responseStarted();
void responseStopped();
void generatedNameChanged();
void handleRecalculating();
private:
ChatLLM *m_llmodel;

View File

@ -43,7 +43,6 @@ ChatLLM::ChatLLM()
connect(&m_llmThread, &QThread::started, this, &ChatLLM::loadModel);
connect(this, &ChatLLM::sendStartup, Network::globalInstance(), &Network::sendStartup);
connect(this, &ChatLLM::sendModelLoaded, Network::globalInstance(), &Network::sendModelLoaded);
connect(this, &ChatLLM::sendResetContext, Network::globalInstance(), &Network::sendResetContext);
m_llmThread.setObjectName("llm thread"); // FIXME: Should identify these with chat name
m_llmThread.start();
}

View File

@ -1,4 +1,5 @@
#include "download.h"
#include "network.h"
#include <QCoreApplication>
#include <QNetworkRequest>
@ -202,6 +203,7 @@ void Download::downloadModel(const QString &modelFile)
return;
}
Network::globalInstance()->sendDownloadStarted(modelFile);
QNetworkRequest request("http://gpt4all.io/models/" + modelFile);
QSslConfiguration conf = request.sslConfiguration();
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
@ -219,6 +221,8 @@ void Download::cancelDownload(const QString &modelFile)
QNetworkReply *modelReply = m_activeDownloads.keys().at(i);
QUrl url = modelReply->request().url();
if (url.toString().endsWith(modelFile)) {
Network::globalInstance()->sendDownloadCanceled(modelFile);
// Disconnect the signals
disconnect(modelReply, &QNetworkReply::downloadProgress, this, &Download::handleDownloadProgress);
disconnect(modelReply, &QNetworkReply::finished, this, &Download::handleModelDownloadFinished);
@ -386,6 +390,20 @@ void Download::parseReleaseJsonFile(const QByteArray &jsonData)
emit releaseInfoChanged();
}
void Download::handleErrorOccurred(QNetworkReply::NetworkError code)
{
QNetworkReply *modelReply = qobject_cast<QNetworkReply *>(sender());
if (!modelReply)
return;
QString modelFilename = modelReply->url().fileName();
qWarning() << "ERROR: Network error occurred attemptint to download"
<< modelFilename
<< "code:" << code
<< "errorString" << modelReply->errorString();
Network::globalInstance()->sendDownloadError(modelFilename, (int)code, modelReply->errorString());
cancelDownload(modelFilename);
}
void Download::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
@ -509,6 +527,7 @@ void Download::handleHashAndSaveFinished(bool success,
// The hash and save should send back with tempfile closed
Q_ASSERT(!tempFile->isOpen());
QString modelFilename = modelReply->url().fileName();
Network::globalInstance()->sendDownloadFinished(modelFilename, success);
ModelInfo info = m_modelMap.value(modelFilename);
info.calcHash = false;

View File

@ -95,6 +95,7 @@ private Q_SLOTS:
void handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
void handleModelsJsonDownloadFinished();
void handleReleaseJsonDownloadFinished();
void handleErrorOccurred(QNetworkReply::NetworkError code);
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void handleModelDownloadFinished();
void handleHashAndSaveFinished(bool success,

View File

@ -290,9 +290,10 @@ Window {
}
onClicked: {
if (Network.isActive)
if (Network.isActive) {
Network.isActive = false
else
Network.sendNetworkToggled(false);
} else
networkDialog.open()
}
}
@ -472,6 +473,7 @@ Window {
}
onClicked: {
Network.sendResetContext(chatModel.count)
currentChat.reset();
}
}

View File

@ -1,5 +1,6 @@
#include "network.h"
#include "llm.h"
#include "sysinfo.h"
#include <QCoreApplication>
#include <QGuiApplication>
@ -13,7 +14,7 @@
//#define DEBUG
#ifdef __APPLE__
#if defined(Q_OS_MAC)
#include <sys/sysctl.h>
std::string getCPUModel() {
char buffer[256];
@ -204,11 +205,15 @@ void Network::sendModelLoaded()
sendMixpanelEvent("model_load");
}
void Network::sendResetContext()
void Network::sendResetContext(int conversationLength)
{
if (!m_usageStatsActive)
return;
sendMixpanelEvent("reset_context");
KeyValue kv;
kv.key = QString("length");
kv.value = QJsonValue(conversationLength);
sendMixpanelEvent("reset_context", QVector<KeyValue>{kv});
}
void Network::sendStartup()
@ -228,7 +233,122 @@ void Network::sendCheckForUpdates()
sendMixpanelEvent("check_for_updates");
}
void Network::sendMixpanelEvent(const QString &ev)
void Network::sendModelDownloaderDialog()
{
if (!m_usageStatsActive)
return;
sendMixpanelEvent("download_dialog");
}
void Network::sendDownloadStarted(const QString &model)
{
if (!m_usageStatsActive)
return;
KeyValue kv;
kv.key = QString("model");
kv.value = QJsonValue(model);
sendMixpanelEvent("download_started", QVector<KeyValue>{kv});
}
void Network::sendDownloadCanceled(const QString &model)
{
if (!m_usageStatsActive)
return;
KeyValue kv;
kv.key = QString("model");
kv.value = QJsonValue(model);
sendMixpanelEvent("download_canceled", QVector<KeyValue>{kv});
}
void Network::sendDownloadError(const QString &model, int code, const QString &errorString)
{
if (!m_usageStatsActive)
return;
KeyValue kv;
kv.key = QString("model");
kv.value = QJsonValue(model);
KeyValue kvCode;
kvCode.key = QString("code");
kvCode.value = QJsonValue(code);
KeyValue kvError;
kvError.key = QString("error");
kvError.value = QJsonValue(errorString);
sendMixpanelEvent("download_error", QVector<KeyValue>{kv, kvCode, kvError});
}
void Network::sendDownloadFinished(const QString &model, bool success)
{
if (!m_usageStatsActive)
return;
KeyValue kv;
kv.key = QString("model");
kv.value = QJsonValue(model);
KeyValue kvSuccess;
kvSuccess.key = QString("success");
kvSuccess.value = QJsonValue(success);
sendMixpanelEvent("download_finished", QVector<KeyValue>{kv, kvSuccess});
}
void Network::sendSettingsDialog()
{
if (!m_usageStatsActive)
return;
sendMixpanelEvent("settings_dialog");
}
void Network::sendNetworkToggled(bool isActive)
{
if (!m_usageStatsActive)
return;
KeyValue kv;
kv.key = QString("isActive");
kv.value = QJsonValue(isActive);
sendMixpanelEvent("network_toggled", QVector<KeyValue>{kv});
}
void Network::sendNewChat(int count)
{
if (!m_usageStatsActive)
return;
KeyValue kv;
kv.key = QString("number_of_chats");
kv.value = QJsonValue(count);
sendMixpanelEvent("new_chat", QVector<KeyValue>{kv});
}
void Network::sendRemoveChat()
{
if (!m_usageStatsActive)
return;
sendMixpanelEvent("remove_chat");
}
void Network::sendRenameChat()
{
if (!m_usageStatsActive)
return;
sendMixpanelEvent("rename_chat");
}
void Network::sendChatStarted()
{
if (!m_usageStatsActive)
return;
sendMixpanelEvent("chat_started");
}
void Network::sendRecalculatingContext(int conversationLength)
{
if (!m_usageStatsActive)
return;
KeyValue kv;
kv.key = QString("length");
kv.value = QJsonValue(conversationLength);
sendMixpanelEvent("recalc_context", QVector<KeyValue>{kv});
}
void Network::sendMixpanelEvent(const QString &ev, const QVector<KeyValue> &values)
{
if (!m_usageStatsActive)
return;
@ -250,16 +370,20 @@ void Network::sendMixpanelEvent(const QString &ev)
if (ev == "startup") {
const QSize display = QGuiApplication::primaryScreen()->size();
properties.insert("display", QString("%1x%2").arg(display.width()).arg(display.height()));
properties.insert("ram", getSystemTotalRAM());
#if defined(__x86_64__) || defined(__i386__)
properties.insert("avx", __builtin_cpu_supports("avx"));
properties.insert("avx2", __builtin_cpu_supports("avx2"));
properties.insert("fma", __builtin_cpu_supports("fma"));
properties.insert("avx", bool(__builtin_cpu_supports("avx")));
properties.insert("avx2", bool(__builtin_cpu_supports("avx2")));
properties.insert("fma", bool(__builtin_cpu_supports("fma")));
#endif
#ifdef __APPLE__
#if defined(Q_OS_MAC)
properties.insert("cpu", QString::fromStdString(getCPUModel()));
#endif
}
for (auto p : values)
properties.insert(p.key, p.value);
QJsonObject event;
event.insert("event", ev);
event.insert("properties", properties);

View File

@ -4,6 +4,12 @@
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonValue>
struct KeyValue {
QString key;
QJsonValue value;
};
class Network : public QObject
{
@ -31,9 +37,21 @@ Q_SIGNALS:
public Q_SLOTS:
void sendOptOut();
void sendModelLoaded();
void sendResetContext();
void sendStartup();
void sendCheckForUpdates();
Q_INVOKABLE void sendModelDownloaderDialog();
Q_INVOKABLE void sendResetContext(int conversationLength);
void sendDownloadStarted(const QString &model);
void sendDownloadCanceled(const QString &model);
void sendDownloadError(const QString &model, int code, const QString &errorString);
void sendDownloadFinished(const QString &model, bool success);
Q_INVOKABLE void sendSettingsDialog();
Q_INVOKABLE void sendNetworkToggled(bool active);
Q_INVOKABLE void sendNewChat(int count);
Q_INVOKABLE void sendRemoveChat();
Q_INVOKABLE void sendRenameChat();
void sendChatStarted();
void sendRecalculatingContext(int conversationLength);
private Q_SLOTS:
void handleIpifyFinished();
@ -45,7 +63,7 @@ private Q_SLOTS:
private:
void sendHealth();
void sendIpify();
void sendMixpanelEvent(const QString &event);
void sendMixpanelEvent(const QString &event, const QVector<KeyValue> &values = QVector<KeyValue>());
void sendMixpanel(const QByteArray &json, bool isOptOut = false);
bool packageAndSendJson(const QString &ingestId, const QString &json);

View File

@ -55,6 +55,7 @@ Drawer {
}
onClicked: {
LLM.chatListModel.addChat();
Network.sendNewChat(LLM.chatListModel.count)
}
}
@ -110,11 +111,9 @@ Drawer {
background: Rectangle {
color: "transparent"
}
Keys.onReturnPressed: (event)=> {
changeName();
}
onEditingFinished: {
changeName();
Network.sendRenameChat()
}
function changeName() {
LLM.chatListModel.get(index).name = chatName.text
@ -209,6 +208,7 @@ Drawer {
}
onClicked: {
LLM.chatListModel.removeChat(LLM.chatListModel.get(index))
Network.sendRemoveChat()
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Confirm delete of the chat")

View File

@ -6,6 +6,7 @@ import QtQuick.Dialogs
import QtQuick.Layouts
import download
import llm
import network
Dialog {
id: modelDownloaderDialog
@ -23,6 +24,10 @@ Dialog {
radius: 10
}
onOpened: {
Network.sendModelDownloaderDialog();
}
property string defaultModelPath: Download.defaultLocalModelsPath()
property alias modelPath: settings.modelPath
Settings {

View File

@ -161,10 +161,16 @@ NOTE: By turning on this feature, you will be sending your data to the GPT4All O
}
onAccepted: {
if (Network.isActive)
return
Network.isActive = true;
Network.sendNetworkToggled(true);
}
onRejected: {
if (!Network.isActive)
return
Network.isActive = false;
Network.sendNetworkToggled(false);
}
}

View File

@ -23,6 +23,10 @@ Dialog {
radius: 10
}
onOpened: {
Network.sendSettingsDialog();
}
Theme {
id: theme
}

48
sysinfo.h Normal file
View File

@ -0,0 +1,48 @@
#include <QtCore/QCoreApplication>
#include <QDebug>
#include <QFile>
#include <QTextStream>
#include <QRegularExpression>
#if defined(Q_OS_MAC)
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#if defined(Q_OS_WIN)
#include <Windows.h>
#endif
QString getSystemTotalRAM()
{
qint64 totalRAM = 0;
#if defined(Q_OS_LINUX)
QFile file("/proc/meminfo");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
QString line = in.readLine();
while (!line.isNull()) {
if (line.startsWith("MemTotal")) {
QStringList parts = line.split(QRegularExpression("\\s+"));
totalRAM = parts[1].toLongLong() * 1024; // Convert from KB to bytes
break;
}
line = in.readLine();
}
file.close();
}
#elif defined(Q_OS_MAC)
int mib[2] = {CTL_HW, HW_MEMSIZE};
size_t length = sizeof(totalRAM);
sysctl(mib, 2, &totalRAM, &length, NULL, 0);
#elif defined(Q_OS_WIN)
MEMORYSTATUSEX memoryStatus;
memoryStatus.dwLength = sizeof(memoryStatus);
GlobalMemoryStatusEx(&memoryStatus);
totalRAM = memoryStatus.ullTotalPhys;
#endif
double totalRAM_GB = static_cast<double>(totalRAM) / (1024 * 1024 * 1024);
return QString::number(totalRAM_GB, 'f', 2) + " GB";
}