mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-06-21 13:10:35 +00:00
Misc fixes for undefined behavior, crashes, and build failure (#3465)
Signed-off-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
parent
051a63f031
commit
22ebd42c32
@ -140,9 +140,14 @@ const std::vector<LLModel::Implementation> &LLModel::Implementation::implementat
|
|||||||
std::string path;
|
std::string path;
|
||||||
// Split the paths string by the delimiter and process each path.
|
// Split the paths string by the delimiter and process each path.
|
||||||
while (std::getline(ss, path, ';')) {
|
while (std::getline(ss, path, ';')) {
|
||||||
std::u8string u8_path(path.begin(), path.end());
|
fs::directory_iterator iter;
|
||||||
|
try {
|
||||||
|
iter = fs::directory_iterator(std::u8string(path.begin(), path.end()));
|
||||||
|
} catch (const fs::filesystem_error &) {
|
||||||
|
continue; // skip nonexistent path
|
||||||
|
}
|
||||||
// Iterate over all libraries
|
// Iterate over all libraries
|
||||||
for (const auto &f : fs::directory_iterator(u8_path)) {
|
for (const auto &f : iter) {
|
||||||
const fs::path &p = f.path();
|
const fs::path &p = f.path();
|
||||||
|
|
||||||
if (p.extension() != LIB_FILE_EXT) continue;
|
if (p.extension() != LIB_FILE_EXT) continue;
|
||||||
|
@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix several potential crashes ([#3465](https://github.com/nomic-ai/gpt4all/pull/3465))
|
||||||
|
|
||||||
## [3.9.0] - 2025-02-04
|
## [3.9.0] - 2025-02-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@ -295,6 +300,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
- Fix several Vulkan resource management issues ([#2694](https://github.com/nomic-ai/gpt4all/pull/2694))
|
- Fix several Vulkan resource management issues ([#2694](https://github.com/nomic-ai/gpt4all/pull/2694))
|
||||||
- Fix crash/hang when some models stop generating, by showing special tokens ([#2701](https://github.com/nomic-ai/gpt4all/pull/2701))
|
- Fix crash/hang when some models stop generating, by showing special tokens ([#2701](https://github.com/nomic-ai/gpt4all/pull/2701))
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/nomic-ai/gpt4all/compare/v3.9.0...HEAD
|
||||||
[3.9.0]: https://github.com/nomic-ai/gpt4all/compare/v3.8.0...v3.9.0
|
[3.9.0]: https://github.com/nomic-ai/gpt4all/compare/v3.8.0...v3.9.0
|
||||||
[3.8.0]: https://github.com/nomic-ai/gpt4all/compare/v3.7.0...v3.8.0
|
[3.8.0]: https://github.com/nomic-ai/gpt4all/compare/v3.7.0...v3.8.0
|
||||||
[3.7.0]: https://github.com/nomic-ai/gpt4all/compare/v3.6.1...v3.7.0
|
[3.7.0]: https://github.com/nomic-ai/gpt4all/compare/v3.6.1...v3.7.0
|
||||||
|
@ -204,7 +204,8 @@ public:
|
|||||||
: QObject(nullptr)
|
: QObject(nullptr)
|
||||||
{
|
{
|
||||||
moveToThread(parent->thread());
|
moveToThread(parent->thread());
|
||||||
setParent(parent);
|
// setParent must be called from the thread the object lives in
|
||||||
|
QMetaObject::invokeMethod(this, [this, parent]() { this->setParent(parent); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: System messages are currently never serialized and only *stored* by the local server.
|
// NOTE: System messages are currently never serialized and only *stored* by the local server.
|
||||||
|
@ -1111,9 +1111,9 @@ class DocumentReader {
|
|||||||
public:
|
public:
|
||||||
struct Metadata { QString title, author, subject, keywords; };
|
struct Metadata { QString title, author, subject, keywords; };
|
||||||
|
|
||||||
static std::unique_ptr<DocumentReader> fromDocument(const DocumentInfo &info);
|
static std::unique_ptr<DocumentReader> fromDocument(DocumentInfo info);
|
||||||
|
|
||||||
const DocumentInfo &doc () const { return *m_info; }
|
const DocumentInfo &doc () const { return m_info; }
|
||||||
const Metadata &metadata() const { return m_metadata; }
|
const Metadata &metadata() const { return m_metadata; }
|
||||||
const std::optional<QString> &word () const { return m_word; }
|
const std::optional<QString> &word () const { return m_word; }
|
||||||
const std::optional<QString> &nextWord() { m_word = advance(); return m_word; }
|
const std::optional<QString> &nextWord() { m_word = advance(); return m_word; }
|
||||||
@ -1123,8 +1123,8 @@ public:
|
|||||||
virtual ~DocumentReader() = default;
|
virtual ~DocumentReader() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit DocumentReader(const DocumentInfo &info)
|
explicit DocumentReader(DocumentInfo info)
|
||||||
: m_info(&info) {}
|
: m_info(std::move(info)) {}
|
||||||
|
|
||||||
void postInit(Metadata &&metadata = {})
|
void postInit(Metadata &&metadata = {})
|
||||||
{
|
{
|
||||||
@ -1134,7 +1134,7 @@ protected:
|
|||||||
|
|
||||||
virtual std::optional<QString> advance() = 0;
|
virtual std::optional<QString> advance() = 0;
|
||||||
|
|
||||||
const DocumentInfo *m_info;
|
DocumentInfo m_info;
|
||||||
Metadata m_metadata;
|
Metadata m_metadata;
|
||||||
std::optional<QString> m_word;
|
std::optional<QString> m_word;
|
||||||
};
|
};
|
||||||
@ -1144,8 +1144,8 @@ namespace {
|
|||||||
#ifdef GPT4ALL_USE_QTPDF
|
#ifdef GPT4ALL_USE_QTPDF
|
||||||
class PdfDocumentReader final : public DocumentReader {
|
class PdfDocumentReader final : public DocumentReader {
|
||||||
public:
|
public:
|
||||||
explicit PdfDocumentReader(const DocumentInfo &info)
|
explicit PdfDocumentReader(DocumentInfo info)
|
||||||
: DocumentReader(info)
|
: DocumentReader(std::move(info))
|
||||||
{
|
{
|
||||||
QString path = info.file.canonicalFilePath();
|
QString path = info.file.canonicalFilePath();
|
||||||
if (m_doc.load(path) != QPdfDocument::Error::None)
|
if (m_doc.load(path) != QPdfDocument::Error::None)
|
||||||
@ -1185,8 +1185,8 @@ private:
|
|||||||
#else
|
#else
|
||||||
class PdfDocumentReader final : public DocumentReader {
|
class PdfDocumentReader final : public DocumentReader {
|
||||||
public:
|
public:
|
||||||
explicit PdfDocumentReader(const DocumentInfo &info)
|
explicit PdfDocumentReader(DocumentInfo info)
|
||||||
: DocumentReader(info)
|
: DocumentReader(std::move(info))
|
||||||
{
|
{
|
||||||
QString path = info.file.canonicalFilePath();
|
QString path = info.file.canonicalFilePath();
|
||||||
m_doc = FPDF_LoadDocument(path.toUtf8().constData(), nullptr);
|
m_doc = FPDF_LoadDocument(path.toUtf8().constData(), nullptr);
|
||||||
@ -1277,8 +1277,8 @@ private:
|
|||||||
|
|
||||||
class WordDocumentReader final : public DocumentReader {
|
class WordDocumentReader final : public DocumentReader {
|
||||||
public:
|
public:
|
||||||
explicit WordDocumentReader(const DocumentInfo &info)
|
explicit WordDocumentReader(DocumentInfo info)
|
||||||
: DocumentReader(info)
|
: DocumentReader(std::move(info))
|
||||||
, m_doc(info.file.canonicalFilePath().toStdString())
|
, m_doc(info.file.canonicalFilePath().toStdString())
|
||||||
{
|
{
|
||||||
m_doc.open();
|
m_doc.open();
|
||||||
@ -1370,8 +1370,8 @@ protected:
|
|||||||
|
|
||||||
class TxtDocumentReader final : public DocumentReader {
|
class TxtDocumentReader final : public DocumentReader {
|
||||||
public:
|
public:
|
||||||
explicit TxtDocumentReader(const DocumentInfo &info)
|
explicit TxtDocumentReader(DocumentInfo info)
|
||||||
: DocumentReader(info)
|
: DocumentReader(std::move(info))
|
||||||
, m_file(info.file.canonicalFilePath())
|
, m_file(info.file.canonicalFilePath())
|
||||||
{
|
{
|
||||||
if (!m_file.open(QIODevice::ReadOnly))
|
if (!m_file.open(QIODevice::ReadOnly))
|
||||||
@ -1412,13 +1412,13 @@ protected:
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::unique_ptr<DocumentReader> DocumentReader::fromDocument(const DocumentInfo &doc)
|
std::unique_ptr<DocumentReader> DocumentReader::fromDocument(DocumentInfo doc)
|
||||||
{
|
{
|
||||||
if (doc.isPdf())
|
if (doc.isPdf())
|
||||||
return std::make_unique<PdfDocumentReader>(doc);
|
return std::make_unique<PdfDocumentReader>(std::move(doc));
|
||||||
if (doc.isDocx())
|
if (doc.isDocx())
|
||||||
return std::make_unique<WordDocumentReader>(doc);
|
return std::make_unique<WordDocumentReader>(std::move(doc));
|
||||||
return std::make_unique<TxtDocumentReader>(doc);
|
return std::make_unique<TxtDocumentReader>(std::move(doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
ChunkStreamer::ChunkStreamer(Database *database)
|
ChunkStreamer::ChunkStreamer(Database *database)
|
||||||
@ -1426,12 +1426,12 @@ ChunkStreamer::ChunkStreamer(Database *database)
|
|||||||
|
|
||||||
ChunkStreamer::~ChunkStreamer() = default;
|
ChunkStreamer::~ChunkStreamer() = default;
|
||||||
|
|
||||||
void ChunkStreamer::setDocument(const DocumentInfo &doc, int documentId, const QString &embeddingModel)
|
void ChunkStreamer::setDocument(DocumentInfo doc, int documentId, const QString &embeddingModel)
|
||||||
{
|
{
|
||||||
auto docKey = doc.key();
|
auto docKey = doc.key();
|
||||||
if (!m_docKey || *m_docKey != docKey) {
|
if (!m_docKey || *m_docKey != docKey) {
|
||||||
m_docKey = docKey;
|
m_docKey = docKey;
|
||||||
m_reader = DocumentReader::fromDocument(doc);
|
m_reader = DocumentReader::fromDocument(std::move(doc));
|
||||||
m_documentId = documentId;
|
m_documentId = documentId;
|
||||||
m_embeddingModel = embeddingModel;
|
m_embeddingModel = embeddingModel;
|
||||||
m_chunk.clear();
|
m_chunk.clear();
|
||||||
@ -1441,7 +1441,8 @@ void ChunkStreamer::setDocument(const DocumentInfo &doc, int documentId, const Q
|
|||||||
if (m_database->m_documentIdCache.contains(documentId)) {
|
if (m_database->m_documentIdCache.contains(documentId)) {
|
||||||
QSqlQuery q(m_database->m_db);
|
QSqlQuery q(m_database->m_db);
|
||||||
if (!m_database->removeChunksByDocumentId(q, documentId))
|
if (!m_database->removeChunksByDocumentId(q, documentId))
|
||||||
handleDocumentError("ERROR: Cannot remove chunks of document", documentId, doc.file.canonicalPath(), q.lastError());
|
handleDocumentError("ERROR: Cannot remove chunks of document",
|
||||||
|
documentId, m_reader->doc().file.canonicalPath(), q.lastError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ public:
|
|||||||
explicit ChunkStreamer(Database *database);
|
explicit ChunkStreamer(Database *database);
|
||||||
~ChunkStreamer();
|
~ChunkStreamer();
|
||||||
|
|
||||||
void setDocument(const DocumentInfo &doc, int documentId, const QString &embeddingModel);
|
void setDocument(DocumentInfo doc, int documentId, const QString &embeddingModel);
|
||||||
std::optional<DocumentInfo::key_type> currentDocKey() const;
|
std::optional<DocumentInfo::key_type> currentDocKey() const;
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
@ -359,8 +359,11 @@ void EmbeddingLLMWorker::handleFinished()
|
|||||||
if (retrievedData.isValid() && retrievedData.canConvert<QVector<EmbeddingChunk>>())
|
if (retrievedData.isValid() && retrievedData.canConvert<QVector<EmbeddingChunk>>())
|
||||||
chunks = retrievedData.value<QVector<EmbeddingChunk>>();
|
chunks = retrievedData.value<QVector<EmbeddingChunk>>();
|
||||||
|
|
||||||
QVariant response = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
QVariant response;
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
response = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
Q_ASSERT(response.isValid());
|
Q_ASSERT(response.isValid());
|
||||||
|
}
|
||||||
bool ok;
|
bool ok;
|
||||||
int code = response.toInt(&ok);
|
int code = response.toInt(&ok);
|
||||||
if (!ok || code != 200) {
|
if (!ok || code != 200) {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QGlobalStatic>
|
#include <QGlobalStatic>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
|
#include <QMutexLocker>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@ -62,8 +63,11 @@ void Logger::messageHandler(QtMsgType type, const QMessageLogContext &, const QS
|
|||||||
}
|
}
|
||||||
// Get time and date
|
// Get time and date
|
||||||
auto timestamp = QDateTime::currentDateTime().toString();
|
auto timestamp = QDateTime::currentDateTime().toString();
|
||||||
// Write message
|
|
||||||
const std::string out = u"[%1] (%2): %3\n"_s.arg(typeString, timestamp, msg).toStdString();
|
const std::string out = u"[%1] (%2): %3\n"_s.arg(typeString, timestamp, msg).toStdString();
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
QMutexLocker locker(&logger->m_mutex);
|
||||||
logger->m_file.write(out.c_str());
|
logger->m_file.write(out.c_str());
|
||||||
logger->m_file.flush();
|
logger->m_file.flush();
|
||||||
std::cerr << out;
|
std::cerr << out;
|
||||||
|
@ -2,19 +2,23 @@
|
|||||||
#define LOGGER_H
|
#define LOGGER_H
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QMutex>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QtLogging>
|
#include <QtLogging>
|
||||||
|
|
||||||
class Logger
|
class Logger {
|
||||||
{
|
|
||||||
QFile m_file;
|
|
||||||
|
|
||||||
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
explicit Logger();
|
||||||
|
|
||||||
static Logger *globalInstance();
|
static Logger *globalInstance();
|
||||||
|
|
||||||
explicit Logger();
|
private:
|
||||||
|
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFile m_file;
|
||||||
|
QMutex m_mutex;
|
||||||
|
|
||||||
friend class MyLogger;
|
friend class MyLogger;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -242,6 +242,12 @@ void Network::handleJsonUploadFinished()
|
|||||||
|
|
||||||
m_activeUploads.removeAll(jsonReply);
|
m_activeUploads.removeAll(jsonReply);
|
||||||
|
|
||||||
|
if (jsonReply->error() != QNetworkReply::NoError) {
|
||||||
|
qWarning() << "Request to" << jsonReply->url().toString() << "failed:" << jsonReply->errorString();
|
||||||
|
jsonReply->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QVariant response = jsonReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
QVariant response = jsonReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
Q_ASSERT(response.isValid());
|
Q_ASSERT(response.isValid());
|
||||||
bool ok;
|
bool ok;
|
||||||
@ -449,6 +455,11 @@ void Network::handleIpifyFinished()
|
|||||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||||
if (!reply)
|
if (!reply)
|
||||||
return;
|
return;
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
qWarning() << "Request to" << reply->url().toString() << "failed:" << reply->errorString();
|
||||||
|
reply->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QVariant response = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
QVariant response = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
Q_ASSERT(response.isValid());
|
Q_ASSERT(response.isValid());
|
||||||
@ -473,6 +484,11 @@ void Network::handleMixpanelFinished()
|
|||||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||||
if (!reply)
|
if (!reply)
|
||||||
return;
|
return;
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
qWarning() << "Request to" << reply->url().toString() << "failed:" << reply->errorString();
|
||||||
|
reply->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QVariant response = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
QVariant response = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
Q_ASSERT(response.isValid());
|
Q_ASSERT(response.isValid());
|
||||||
@ -511,6 +527,11 @@ void Network::handleHealthFinished()
|
|||||||
QNetworkReply *healthReply = qobject_cast<QNetworkReply *>(sender());
|
QNetworkReply *healthReply = qobject_cast<QNetworkReply *>(sender());
|
||||||
if (!healthReply)
|
if (!healthReply)
|
||||||
return;
|
return;
|
||||||
|
if (healthReply->error() != QNetworkReply::NoError) {
|
||||||
|
qWarning() << "Request to" << healthReply->url().toString() << "failed:" << healthReply->errorString();
|
||||||
|
healthReply->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QVariant response = healthReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
QVariant response = healthReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
Q_ASSERT(response.isValid());
|
Q_ASSERT(response.isValid());
|
||||||
|
@ -498,11 +498,6 @@
|
|||||||
<source>enter $BASE_URL</source>
|
<source>enter $BASE_URL</source>
|
||||||
<translation>introdu $BASE_URL</translation>
|
<translation>introdu $BASE_URL</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../qml/AddHFModelView.qml" line="600"/>
|
|
||||||
<source>ERROR: $API_KEY is empty.</source>
|
|
||||||
<translation>EROARE: $API_KEY absentă</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/AddHFModelView.qml" line="606"/>
|
<location filename="../qml/AddHFModelView.qml" line="606"/>
|
||||||
<source>enter $MODEL_NAME</source>
|
<source>enter $MODEL_NAME</source>
|
||||||
|
Loading…
Reference in New Issue
Block a user