mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-06-28 00:07:04 +00:00
chat: fix comparison of versions with suffixes (#2772)
Pre-release and post-release suffixes are now interpreted correctly. Also fix comparison of incomplete versions. Signed-off-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
parent
e45685b27a
commit
6b8e0f7ae4
@ -5,6 +5,7 @@
|
|||||||
#include "network.h"
|
#include "network.h"
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
#include <QCollator>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QGlobalStatic>
|
#include <QGlobalStatic>
|
||||||
@ -14,6 +15,7 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonValue>
|
#include <QJsonValue>
|
||||||
|
#include <QLocale>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
@ -28,6 +30,7 @@
|
|||||||
#include <QtLogging>
|
#include <QtLogging>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <compare>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@ -60,30 +63,58 @@ static bool operator==(const ReleaseInfo& lhs, const ReleaseInfo& rhs)
|
|||||||
return lhs.version == rhs.version;
|
return lhs.version == rhs.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool compareVersions(const QString &a, const QString &b)
|
std::strong_ordering Download::compareAppVersions(const QString &a, const QString &b)
|
||||||
{
|
{
|
||||||
QRegularExpression regex("(\\d+)");
|
static QRegularExpression versionRegex(R"(^(\d+(?:\.\d+){0,2})(-.+)?$)");
|
||||||
QStringList aParts = a.split('.');
|
|
||||||
QStringList bParts = b.split('.');
|
|
||||||
Q_ASSERT(aParts.size() == 3);
|
|
||||||
Q_ASSERT(bParts.size() == 3);
|
|
||||||
|
|
||||||
for (int i = 0; i < std::min(aParts.size(), bParts.size()); ++i) {
|
// When comparing versions, make sure a2 < a10.
|
||||||
QRegularExpressionMatch aMatch = regex.match(aParts[i]);
|
QCollator versionCollator(QLocale(QLocale::English, QLocale::UnitedStates));
|
||||||
QRegularExpressionMatch bMatch = regex.match(bParts[i]);
|
versionCollator.setNumericMode(true);
|
||||||
Q_ASSERT(aMatch.hasMatch() && bMatch.hasMatch());
|
|
||||||
if (aMatch.hasMatch() && bMatch.hasMatch()) {
|
QRegularExpressionMatch aMatch = versionRegex.match(a);
|
||||||
int aInt = aMatch.captured(1).toInt();
|
QRegularExpressionMatch bMatch = versionRegex.match(b);
|
||||||
int bInt = bMatch.captured(1).toInt();
|
|
||||||
if (aInt > bInt) {
|
Q_ASSERT(aMatch.hasMatch() && bMatch.hasMatch()); // expect valid versions
|
||||||
return true;
|
|
||||||
} else if (aInt < bInt) {
|
// Check for an invalid version. foo < 3.0.0 -> !hasMatch < hasMatch
|
||||||
return false;
|
if (auto diff = aMatch.hasMatch() <=> bMatch.hasMatch(); diff != 0)
|
||||||
}
|
return diff; // invalid version compares as lower
|
||||||
}
|
|
||||||
|
// Compare invalid versions. fooa < foob
|
||||||
|
if (!aMatch.hasMatch() && !bMatch.hasMatch())
|
||||||
|
return versionCollator.compare(a, b) <=> 0; // lexicographic comparison
|
||||||
|
|
||||||
|
// Compare first three components. 3.0.0 < 3.0.1
|
||||||
|
QStringList aParts = aMatch.captured(1).split('.');
|
||||||
|
QStringList bParts = bMatch.captured(1).split('.');
|
||||||
|
for (int i = 0; i < qMax(aParts.size(), bParts.size()); i++) {
|
||||||
|
bool ok = false;
|
||||||
|
int aInt = aParts.value(i, "0").toInt(&ok);
|
||||||
|
Q_ASSERT(ok);
|
||||||
|
int bInt = bParts.value(i, "0").toInt(&ok);
|
||||||
|
Q_ASSERT(ok);
|
||||||
|
if (auto diff = aInt <=> bInt; diff != 0)
|
||||||
|
return diff; // version with lower component compares as lower
|
||||||
}
|
}
|
||||||
|
|
||||||
return aParts.size() > bParts.size();
|
// Check for a pre/post-release suffix. 3.0.0-dev0 < 3.0.0-rc1 < 3.0.0 < 3.0.0-post1
|
||||||
|
auto getSuffixOrder = [](const QRegularExpressionMatch &match) -> int {
|
||||||
|
QString suffix = match.captured(2);
|
||||||
|
return suffix.startsWith("-dev") ? 0 :
|
||||||
|
suffix.startsWith("-rc") ? 1 :
|
||||||
|
suffix.isEmpty() ? 2 :
|
||||||
|
/* some other suffix */ 3;
|
||||||
|
};
|
||||||
|
if (auto diff = getSuffixOrder(aMatch) <=> getSuffixOrder(bMatch); diff != 0)
|
||||||
|
return diff; // different suffix types
|
||||||
|
|
||||||
|
// Lexicographic comparison of suffix. 3.0.0-rc1 < 3.0.0-rc2
|
||||||
|
if (aMatch.hasCaptured(2) && bMatch.hasCaptured(2)) {
|
||||||
|
if (auto diff = versionCollator.compare(aMatch.captured(2), bMatch.captured(2)); diff != 0)
|
||||||
|
return diff <=> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::strong_ordering::equal;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseInfo Download::releaseInfo() const
|
ReleaseInfo Download::releaseInfo() const
|
||||||
@ -99,11 +130,11 @@ ReleaseInfo Download::releaseInfo() const
|
|||||||
bool Download::hasNewerRelease() const
|
bool Download::hasNewerRelease() const
|
||||||
{
|
{
|
||||||
const QString currentVersion = QCoreApplication::applicationVersion();
|
const QString currentVersion = QCoreApplication::applicationVersion();
|
||||||
QList<QString> versions = m_releaseMap.keys();
|
for (const auto &version : m_releaseMap.keys()) {
|
||||||
std::sort(versions.begin(), versions.end(), compareVersions);
|
if (compareAppVersions(version, currentVersion) > 0)
|
||||||
if (versions.isEmpty())
|
return true;
|
||||||
return false;
|
}
|
||||||
return compareVersions(versions.first(), currentVersion);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Download::isFirstStart(bool writeVersion) const
|
bool Download::isFirstStart(bool writeVersion) const
|
||||||
|
@ -57,6 +57,7 @@ class Download : public QObject
|
|||||||
public:
|
public:
|
||||||
static Download *globalInstance();
|
static Download *globalInstance();
|
||||||
|
|
||||||
|
static std::strong_ordering compareAppVersions(const QString &a, const QString &b);
|
||||||
ReleaseInfo releaseInfo() const;
|
ReleaseInfo releaseInfo() const;
|
||||||
bool hasNewerRelease() const;
|
bool hasNewerRelease() const;
|
||||||
QString latestNews() const { return m_latestNews; }
|
QString latestNews() const { return m_latestNews; }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "modellist.h"
|
#include "modellist.h"
|
||||||
|
|
||||||
|
#include "download.h"
|
||||||
#include "mysettings.h"
|
#include "mysettings.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
|
|
||||||
@ -33,7 +34,6 @@
|
|||||||
#include <QtLogging>
|
#include <QtLogging>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <compare>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -1442,29 +1442,6 @@ void ModelList::updateDataForSettings()
|
|||||||
emit dataChanged(index(0, 0), index(m_models.size() - 1, 0));
|
emit dataChanged(index(0, 0), index(m_models.size() - 1, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::strong_ordering compareVersions(const QString &a, const QString &b)
|
|
||||||
{
|
|
||||||
QRegularExpression regex("(\\d+)");
|
|
||||||
QStringList aParts = a.split('.');
|
|
||||||
QStringList bParts = b.split('.');
|
|
||||||
Q_ASSERT(aParts.size() == 3);
|
|
||||||
Q_ASSERT(bParts.size() == 3);
|
|
||||||
|
|
||||||
for (int i = 0; i < std::min(aParts.size(), bParts.size()); ++i) {
|
|
||||||
QRegularExpressionMatch aMatch = regex.match(aParts[i]);
|
|
||||||
QRegularExpressionMatch bMatch = regex.match(bParts[i]);
|
|
||||||
Q_ASSERT(aMatch.hasMatch() && bMatch.hasMatch());
|
|
||||||
if (aMatch.hasMatch() && bMatch.hasMatch()) {
|
|
||||||
int aInt = aMatch.captured(1).toInt();
|
|
||||||
int bInt = bMatch.captured(1).toInt();
|
|
||||||
if (auto diff = aInt <=> bInt; diff != 0)
|
|
||||||
return diff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return aParts.size() <=> bParts.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save)
|
void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save)
|
||||||
{
|
{
|
||||||
QJsonParseError err;
|
QJsonParseError err;
|
||||||
@ -1516,11 +1493,11 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If the current version is strictly less than required version, then skip
|
// If the current version is strictly less than required version, then skip
|
||||||
if (!requiresVersion.isEmpty() && compareVersions(currentVersion, requiresVersion) < 0)
|
if (!requiresVersion.isEmpty() && Download::compareAppVersions(currentVersion, requiresVersion) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If the version removed is less than or equal to the current version, then skip
|
// If the version removed is less than or equal to the current version, then skip
|
||||||
if (!versionRemoved.isEmpty() && compareVersions(versionRemoved, currentVersion) <= 0)
|
if (!versionRemoved.isEmpty() && Download::compareAppVersions(versionRemoved, currentVersion) <= 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
modelFilesize = ModelList::toFileSize(modelFilesize.toULongLong());
|
modelFilesize = ModelList::toFileSize(modelFilesize.toULongLong());
|
||||||
|
Loading…
Reference in New Issue
Block a user