Model discovery.

Signed-off-by: Adam Treat <treat.adam@gmail.com>
This commit is contained in:
Adam Treat 2024-03-05 11:31:31 -05:00
parent f2b4809b72
commit 83c76be68a
9 changed files with 1362 additions and 266 deletions

View File

@ -130,7 +130,7 @@ void Download::downloadModel(const QString &modelFile)
ModelList::globalInstance()->updateDataByFilename(modelFile, ModelList::DownloadingRole, true);
ModelInfo info = ModelList::globalInstance()->modelInfoByFilename(modelFile);
QString url = !info.url.isEmpty() ? info.url : "http://gpt4all.io/models/gguf/" + modelFile;
QString url = !info.url().isEmpty() ? info.url() : "http://gpt4all.io/models/gguf/" + modelFile;
Network::globalInstance()->sendDownloadStarted(modelFile);
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::User, modelFile);
@ -201,6 +201,8 @@ void Download::removeModel(const QString &modelFile)
QFile file(filePath);
if (file.exists()) {
const ModelInfo info = ModelList::globalInstance()->modelInfoByFilename(modelFile);
ModelList::globalInstance()->removeInstalled(info);
Network::globalInstance()->sendRemoveModel(modelFile);
file.remove();
}
@ -364,8 +366,8 @@ HashAndSaveFile::HashAndSaveFile()
m_hashAndSaveThread.start();
}
void HashAndSaveFile::hashAndSave(const QString &expectedHash, const QString &saveFilePath,
QFile *tempFile, QNetworkReply *modelReply)
void HashAndSaveFile::hashAndSave(const QString &expectedHash, QCryptographicHash::Algorithm a,
const QString &saveFilePath, QFile *tempFile, QNetworkReply *modelReply)
{
Q_ASSERT(!tempFile->isOpen());
QString modelFilename = modelReply->request().attribute(QNetworkRequest::User).toString();
@ -379,13 +381,16 @@ void HashAndSaveFile::hashAndSave(const QString &expectedHash, const QString &sa
return;
}
QCryptographicHash hash(QCryptographicHash::Md5);
QCryptographicHash hash(a);
while(!tempFile->atEnd())
hash.addData(tempFile->read(16384));
if (hash.result().toHex() != expectedHash) {
if (hash.result().toHex() != expectedHash.toLatin1()) {
tempFile->close();
const QString error
= QString("ERROR: Download error MD5SUM did not match: %1 != %2 for %3").arg(hash.result().toHex()).arg(expectedHash).arg(modelFilename);
= QString("ERROR: Download error hash did not match: %1 != %2 for %3")
.arg(hash.result().toHex())
.arg(expectedHash.toLatin1())
.arg(modelFilename);
qWarning() << error;
tempFile->remove();
emit hashAndSaveFinished(false, error, tempFile, modelReply);
@ -472,9 +477,12 @@ void Download::handleModelDownloadFinished()
// Notify that we are calculating hash
ModelList::globalInstance()->updateDataByFilename(modelFilename, ModelList::CalcHashRole, true);
QByteArray md5sum = ModelList::globalInstance()->modelInfoByFilename(modelFilename).md5sum;
QByteArray hash = ModelList::globalInstance()->modelInfoByFilename(modelFilename).hash;
ModelInfo::HashAlgorithm hashAlgorithm = ModelList::globalInstance()->modelInfoByFilename(modelFilename).hashAlgorithm;
const QString saveFilePath = MySettings::globalInstance()->modelPath() + modelFilename;
emit requestHashAndSave(md5sum, saveFilePath, tempFile, modelReply);
emit requestHashAndSave(hash,
(hashAlgorithm == ModelInfo::Md5 ? QCryptographicHash::Md5 : QCryptographicHash::Sha256),
saveFilePath, tempFile, modelReply);
}
void Download::handleHashAndSaveFinished(bool success, const QString &error,
@ -489,10 +497,14 @@ void Download::handleHashAndSaveFinished(bool success, const QString &error,
tempFile->deleteLater();
ModelList::globalInstance()->updateDataByFilename(modelFilename, ModelList::DownloadingRole, false);
if (!success)
if (!success) {
ModelList::globalInstance()->updateDataByFilename(modelFilename, ModelList::DownloadErrorRole, error);
else
} else {
ModelInfo info = ModelList::globalInstance()->modelInfoByFilename(modelFilename);
if (info.isDiscovered())
ModelList::globalInstance()->updateDiscoveredInstalled(info);
ModelList::globalInstance()->updateDataByFilename(modelFilename, ModelList::DownloadErrorRole, QString());
}
}
void Download::handleReadyRead()

View File

@ -28,7 +28,7 @@ public:
HashAndSaveFile();
public Q_SLOTS:
void hashAndSave(const QString &hash, const QString &saveFilePath,
void hashAndSave(const QString &hash, QCryptographicHash::Algorithm a, const QString &saveFilePath,
QFile *tempFile, QNetworkReply *modelReply);
Q_SIGNALS:
@ -72,7 +72,7 @@ private Q_SLOTS:
Q_SIGNALS:
void releaseInfoChanged();
void hasNewerReleaseChanged();
void requestHashAndSave(const QString &hash, const QString &saveFilePath,
void requestHashAndSave(const QString &hash, QCryptographicHash::Algorithm a, const QString &saveFilePath,
QFile *tempFile, QNetworkReply *modelReply);
private:

File diff suppressed because it is too large Load Diff

View File

@ -11,16 +11,17 @@ struct ModelInfo {
Q_PROPERTY(QString filename READ filename WRITE setFilename)
Q_PROPERTY(QString dirpath MEMBER dirpath)
Q_PROPERTY(QString filesize MEMBER filesize)
Q_PROPERTY(QByteArray md5sum MEMBER md5sum)
Q_PROPERTY(QByteArray hash MEMBER hash)
Q_PROPERTY(HashAlgorithm hashAlgorithm MEMBER hashAlgorithm)
Q_PROPERTY(bool calcHash MEMBER calcHash)
Q_PROPERTY(bool installed MEMBER installed)
Q_PROPERTY(bool isDefault MEMBER isDefault)
Q_PROPERTY(bool disableGUI MEMBER disableGUI)
Q_PROPERTY(bool isOnline MEMBER isOnline)
Q_PROPERTY(QString description MEMBER description)
Q_PROPERTY(QString description READ description WRITE setDescription)
Q_PROPERTY(QString requiresVersion MEMBER requiresVersion)
Q_PROPERTY(QString deprecatedVersion MEMBER deprecatedVersion)
Q_PROPERTY(QString url MEMBER url)
Q_PROPERTY(QString url READ url WRITE setUrl)
Q_PROPERTY(qint64 bytesReceived MEMBER bytesReceived)
Q_PROPERTY(qint64 bytesTotal MEMBER bytesTotal)
Q_PROPERTY(qint64 timestamp MEMBER timestamp)
@ -31,9 +32,10 @@ struct ModelInfo {
Q_PROPERTY(QString order MEMBER order)
Q_PROPERTY(int ramrequired MEMBER ramrequired)
Q_PROPERTY(QString parameters MEMBER parameters)
Q_PROPERTY(QString quant MEMBER quant)
Q_PROPERTY(QString type MEMBER type)
Q_PROPERTY(bool isClone MEMBER isClone)
Q_PROPERTY(QString quant READ quant WRITE setQuant)
Q_PROPERTY(QString type READ type WRITE setType)
Q_PROPERTY(bool isClone READ isClone WRITE setIsClone)
Q_PROPERTY(bool isDiscovered READ isDiscovered WRITE setIsDiscovered)
Q_PROPERTY(double temperature READ temperature WRITE setTemperature)
Q_PROPERTY(double topP READ topP WRITE setTopP)
Q_PROPERTY(double minP READ minP WRITE setMinP)
@ -48,8 +50,16 @@ struct ModelInfo {
Q_PROPERTY(int repeatPenaltyTokens READ repeatPenaltyTokens WRITE setRepeatPenaltyTokens)
Q_PROPERTY(QString promptTemplate READ promptTemplate WRITE setPromptTemplate)
Q_PROPERTY(QString systemPrompt READ systemPrompt WRITE setSystemPrompt)
Q_PROPERTY(int likes READ likes WRITE setLikes)
Q_PROPERTY(int downloads READ downloads WRITE setDownloads)
Q_PROPERTY(QDateTime recency READ recency WRITE setRecency)
public:
enum HashAlgorithm {
Md5,
Sha256
};
QString id() const;
void setId(const QString &id);
@ -59,18 +69,44 @@ public:
QString filename() const;
void setFilename(const QString &name);
QString description() const;
void setDescription(const QString &d);
QString url() const;
void setUrl(const QString &u);
QString quant() const;
void setQuant(const QString &q);
QString type() const;
void setType(const QString &t);
bool isClone() const;
void setIsClone(bool b);
bool isDiscovered() const;
void setIsDiscovered(bool b);
int likes() const;
void setLikes(int l);
int downloads() const;
void setDownloads(int d);
QDateTime recency() const;
void setRecency(const QDateTime &r);
QString dirpath;
QString filesize;
QByteArray md5sum;
QByteArray hash;
HashAlgorithm hashAlgorithm;
bool calcHash = false;
bool installed = false;
bool isDefault = false;
bool isOnline = false;
bool disableGUI = false;
QString description;
QString requiresVersion;
QString deprecatedVersion;
QString url;
qint64 bytesReceived = 0;
qint64 bytesTotal = 0;
qint64 timestamp = 0;
@ -79,11 +115,8 @@ public:
bool isIncomplete = false;
QString downloadError;
QString order;
int ramrequired = 0;
int ramrequired = -1;
QString parameters;
QString quant;
QString type;
bool isClone = false;
bool operator==(const ModelInfo &other) const {
return m_id == other.m_id;
@ -116,10 +149,21 @@ public:
QString systemPrompt() const;
void setSystemPrompt(const QString &p);
bool shouldSaveMetadata() const;
private:
QString m_id;
QString m_name;
QString m_filename;
QString m_description;
QString m_url;
QString m_quant;
QString m_type;
bool m_isClone = false;
bool m_isDiscovered = false;
int m_likes = -1;
int m_downloads = -1;
QDateTime m_recency;
double m_temperature = 0.7;
double m_topP = 0.4;
double m_minP = 0.0;
@ -183,6 +227,8 @@ public:
bool isExpanded() const;
void setExpanded(bool expanded);
Q_INVOKABLE void discoverAndFilter(const QString &discover);
Q_SIGNALS:
void countChanged();
@ -195,6 +241,7 @@ Q_SIGNALS:
private:
bool m_expanded;
int m_limit;
QString m_discoverFilter;
};
class ModelList : public QAbstractListModel
@ -207,17 +254,30 @@ class ModelList : public QAbstractListModel
Q_PROPERTY(DownloadableModels* downloadableModels READ downloadableModels NOTIFY downloadableModelsChanged)
Q_PROPERTY(QList<QString> userDefaultModelList READ userDefaultModelList NOTIFY userDefaultModelListChanged)
Q_PROPERTY(bool asyncModelRequestOngoing READ asyncModelRequestOngoing NOTIFY asyncModelRequestOngoingChanged)
Q_PROPERTY(int discoverLimit READ discoverLimit WRITE setDiscoverLimit NOTIFY discoverLimitChanged)
Q_PROPERTY(int discoverSortDirection READ discoverSortDirection WRITE setDiscoverSortDirection NOTIFY discoverSortDirectionChanged)
Q_PROPERTY(DiscoverSort discoverSort READ discoverSort WRITE setDiscoverSort NOTIFY discoverSortChanged)
Q_PROPERTY(float discoverProgress READ discoverProgress NOTIFY discoverProgressChanged)
Q_PROPERTY(bool discoverInProgress READ discoverInProgress NOTIFY discoverInProgressChanged)
public:
static ModelList *globalInstance();
enum DiscoverSort {
Default,
Likes,
Downloads,
Recent
};
enum Roles {
IdRole = Qt::UserRole + 1,
NameRole,
FilenameRole,
DirpathRole,
FilesizeRole,
Md5sumRole,
HashRole,
HashAlgorithmRole,
CalcHashRole,
InstalledRole,
DefaultRole,
@ -240,6 +300,7 @@ public:
QuantRole,
TypeRole,
IsCloneRole,
IsDiscoveredRole,
TemperatureRole,
TopPRole,
TopKRole,
@ -252,6 +313,9 @@ public:
PromptTemplateRole,
SystemPromptRole,
MinPRole,
LikesRole,
DownloadsRole,
RecencyRole
};
QHash<int, QByteArray> roleNames() const override
@ -262,7 +326,8 @@ public:
roles[FilenameRole] = "filename";
roles[DirpathRole] = "dirpath";
roles[FilesizeRole] = "filesize";
roles[Md5sumRole] = "md5sum";
roles[HashRole] = "hash";
roles[HashAlgorithmRole] = "hashAlgorithm";
roles[CalcHashRole] = "calcHash";
roles[InstalledRole] = "installed";
roles[DefaultRole] = "isDefault";
@ -285,6 +350,7 @@ public:
roles[QuantRole] = "quant";
roles[TypeRole] = "type";
roles[IsCloneRole] = "isClone";
roles[IsDiscoveredRole] = "isDiscovered";
roles[TemperatureRole] = "temperature";
roles[TopPRole] = "topP";
roles[MinPRole] = "minP";
@ -297,6 +363,9 @@ public:
roles[RepeatPenaltyTokensRole] = "repeatPenaltyTokens";
roles[PromptTemplateRole] = "promptTemplate";
roles[SystemPromptRole] = "systemPrompt";
roles[LikesRole] = "likes";
roles[DownloadsRole] = "downloads";
roles[RecencyRole] = "recency";
return roles;
}
@ -306,6 +375,7 @@ public:
QVariant dataByFilename(const QString &filename, int role) const;
void updateData(const QString &id, int role, const QVariant &value);
void updateDataByFilename(const QString &filename, int role, const QVariant &value);
void updateData(const QString &id, const QVector<QPair<int, QVariant>> &data);
int count() const { return m_models.size(); }
@ -315,7 +385,8 @@ public:
Q_INVOKABLE ModelInfo modelInfoByFilename(const QString &filename) const;
Q_INVOKABLE bool isUniqueName(const QString &name) const;
Q_INVOKABLE QString clone(const ModelInfo &model);
Q_INVOKABLE void remove(const ModelInfo &model);
Q_INVOKABLE void removeClone(const ModelInfo &model);
Q_INVOKABLE void removeInstalled(const ModelInfo &model);
ModelInfo defaultModelInfo() const;
int defaultEmbeddingModelIndex() const;
@ -345,6 +416,21 @@ public:
bool asyncModelRequestOngoing() const { return m_asyncModelRequestOngoing; }
void updateModelsFromDirectory();
void updateDiscoveredInstalled(const ModelInfo &info);
int discoverLimit() const;
void setDiscoverLimit(int limit);
int discoverSortDirection() const;
void setDiscoverSortDirection(int direction); // -1 or 1
DiscoverSort discoverSort() const;
void setDiscoverSort(DiscoverSort sort);
float discoverProgress() const;
bool discoverInProgress() const;
Q_INVOKABLE void discoverSearch(const QString &discover);
Q_SIGNALS:
void countChanged();
@ -354,22 +440,35 @@ Q_SIGNALS:
void userDefaultModelListChanged();
void asyncModelRequestOngoingChanged();
void defaultEmbeddingModelIndexChanged();
void discoverLimitChanged();
void discoverSortDirectionChanged();
void discoverSortChanged();
void discoverProgressChanged();
void discoverInProgressChanged();
private Q_SLOTS:
void resortModel();
void updateModelsFromJson();
void updateModelsFromJsonAsync();
void updateModelsFromSettings();
void updateDataForSettings();
void handleModelsJsonDownloadFinished();
void handleModelsJsonDownloadErrorOccurred(QNetworkReply::NetworkError code);
void handleDiscoveryFinished();
void handleDiscoveryErrorOccurred(QNetworkReply::NetworkError code);
void handleDiscoveryItemFinished();
void handleDiscoveryItemErrorOccurred(QNetworkReply::NetworkError code);
void handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
private:
void removeInternal(const ModelInfo &model);
void clearDiscoveredModels();
QString modelDirPath(const QString &modelName, bool isOnline);
int indexForModel(ModelInfo *model);
QVariant dataInternal(const ModelInfo *info, int role) const;
static bool lessThan(const ModelInfo* a, const ModelInfo* b);
static bool lessThan(const ModelInfo* a, const ModelInfo* b, DiscoverSort s, int d);
void parseModelsJsonFile(const QByteArray &jsonData, bool save);
void parseDiscoveryJsonFile(const QByteArray &jsonData);
QString uniqueModelName(const ModelInfo &model) const;
private:
@ -381,8 +480,14 @@ private:
QList<ModelInfo*> m_models;
QHash<QString, ModelInfo*> m_modelMap;
bool m_asyncModelRequestOngoing;
int m_discoverLimit;
int m_discoverSortDirection;
DiscoverSort m_discoverSort;
int m_discoverNumberOfResults;
int m_discoverResultsCompleted;
bool m_discoverInProgress;
private:
protected:
explicit ModelList();
~ModelList() {}
friend class MyModelList;

View File

@ -140,12 +140,10 @@ void MySettings::setModelName(const ModelInfo &m, const QString &name, bool forc
return;
QSettings setting;
if ((m.m_name == name || m.m_filename == name) && !m.isClone)
if ((m.m_name == name || m.m_filename == name) && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/name");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/name", name);
if (m.isClone)
setting.setValue(QString("model-%1").arg(m.id()) + "/isClone", "true");
setting.sync();
if (!force)
emit nameChanged(m);
@ -164,7 +162,7 @@ void MySettings::setModelFilename(const ModelInfo &m, const QString &filename, b
return;
QSettings setting;
if (m.m_filename == filename && !m.isClone)
if (m.m_filename == filename && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/filename");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/filename", filename);
@ -173,6 +171,186 @@ void MySettings::setModelFilename(const ModelInfo &m, const QString &filename, b
emit filenameChanged(m);
}
QString MySettings::modelDescription(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/description", m.m_description).toString();
}
void MySettings::setModelDescription(const ModelInfo &m, const QString &d, bool force)
{
if ((modelDescription(m) == d || m.id().isEmpty()) && !force)
return;
QSettings setting;
if (m.m_description == d && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/description");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/description", d);
setting.sync();
}
QString MySettings::modelUrl(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/url", m.m_url).toString();
}
void MySettings::setModelUrl(const ModelInfo &m, const QString &u, bool force)
{
if ((modelUrl(m) == u || m.id().isEmpty()) && !force)
return;
QSettings setting;
if (m.m_url == u && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/url");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/url", u);
setting.sync();
}
QString MySettings::modelQuant(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/quant", m.m_quant).toString();
}
void MySettings::setModelQuant(const ModelInfo &m, const QString &q, bool force)
{
if ((modelUrl(m) == q || m.id().isEmpty()) && !force)
return;
QSettings setting;
if (m.m_quant == q && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/quant");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/quant", q);
setting.sync();
}
QString MySettings::modelType(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/type", m.m_type).toString();
}
void MySettings::setModelType(const ModelInfo &m, const QString &t, bool force)
{
if ((modelType(m) == t || m.id().isEmpty()) && !force)
return;
QSettings setting;
if (m.m_type == t && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/type");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/type", t);
setting.sync();
}
bool MySettings::modelIsClone(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/isClone", m.m_isClone).toBool();
}
void MySettings::setModelIsClone(const ModelInfo &m, bool b, bool force)
{
if ((modelIsClone(m) == b || m.id().isEmpty()) && !force)
return;
QSettings setting;
if (m.m_isClone == b && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/isClone");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/isClone", b);
setting.sync();
}
bool MySettings::modelIsDiscovered(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/isDiscovered", m.m_isDiscovered).toBool();
}
void MySettings::setModelIsDiscovered(const ModelInfo &m, bool b, bool force)
{
if ((modelIsDiscovered(m) == b || m.id().isEmpty()) && !force)
return;
QSettings setting;
if (m.m_isDiscovered == b && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/isDiscovered");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/isDiscovered", b);
setting.sync();
}
int MySettings::modelLikes(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/likes", m.m_likes).toInt();
}
void MySettings::setModelLikes(const ModelInfo &m, int l, bool force)
{
if ((modelLikes(m) == l || m.id().isEmpty()) && !force)
return;
QSettings setting;
if (m.m_likes == l && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/likes");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/likes", l);
setting.sync();
}
int MySettings::modelDownloads(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/downloads", m.m_downloads).toInt();
}
void MySettings::setModelDownloads(const ModelInfo &m, int d, bool force)
{
if ((modelDownloads(m) == d || m.id().isEmpty()) && !force)
return;
QSettings setting;
if (m.m_downloads == d && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/downloads");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/downloads", d);
setting.sync();
}
QDateTime MySettings::modelRecency(const ModelInfo &m) const
{
QSettings setting;
setting.sync();
return setting.value(QString("model-%1").arg(m.id()) + "/recency", m.m_recency).toDateTime();
}
void MySettings::setModelRecency(const ModelInfo &m, const QDateTime &r, bool force)
{
if ((modelRecency(m) == r || m.id().isEmpty()) && !force)
return;
QSettings setting;
if (m.m_recency == r && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/recency");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/recency", r);
setting.sync();
}
double MySettings::modelTemperature(const ModelInfo &m) const
{
QSettings setting;
@ -186,7 +364,7 @@ void MySettings::setModelTemperature(const ModelInfo &m, double t, bool force)
return;
QSettings setting;
if (m.m_temperature == t && !m.isClone)
if (m.m_temperature == t && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/temperature");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/temperature", t);
@ -215,7 +393,7 @@ void MySettings::setModelTopP(const ModelInfo &m, double p, bool force)
return;
QSettings setting;
if (m.m_topP == p && !m.isClone)
if (m.m_topP == p && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/topP");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/topP", p);
@ -230,7 +408,7 @@ void MySettings::setModelMinP(const ModelInfo &m, double p, bool force)
return;
QSettings setting;
if (m.m_minP == p && !m.isClone)
if (m.m_minP == p && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/minP");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/minP", p);
@ -252,7 +430,7 @@ void MySettings::setModelTopK(const ModelInfo &m, int k, bool force)
return;
QSettings setting;
if (m.m_topK == k && !m.isClone)
if (m.m_topK == k && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/topK");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/topK", k);
@ -274,7 +452,7 @@ void MySettings::setModelMaxLength(const ModelInfo &m, int l, bool force)
return;
QSettings setting;
if (m.m_maxLength == l && !m.isClone)
if (m.m_maxLength == l && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/maxLength");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/maxLength", l);
@ -296,7 +474,7 @@ void MySettings::setModelPromptBatchSize(const ModelInfo &m, int s, bool force)
return;
QSettings setting;
if (m.m_promptBatchSize == s && !m.isClone)
if (m.m_promptBatchSize == s && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/promptBatchSize");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/promptBatchSize", s);
@ -318,7 +496,7 @@ void MySettings::setModelContextLength(const ModelInfo &m, int l, bool force)
return;
QSettings setting;
if (m.m_contextLength == l && !m.isClone)
if (m.m_contextLength == l && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/contextLength");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/contextLength", l);
@ -340,7 +518,7 @@ void MySettings::setModelGpuLayers(const ModelInfo &m, int l, bool force)
return;
QSettings setting;
if (m.m_gpuLayers == l && !m.isClone)
if (m.m_gpuLayers == l && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/gpuLayers");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/gpuLayers", l);
@ -362,7 +540,7 @@ void MySettings::setModelRepeatPenalty(const ModelInfo &m, double p, bool force)
return;
QSettings setting;
if (m.m_repeatPenalty == p && !m.isClone)
if (m.m_repeatPenalty == p && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/repeatPenalty");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/repeatPenalty", p);
@ -384,7 +562,7 @@ void MySettings::setModelRepeatPenaltyTokens(const ModelInfo &m, int t, bool for
return;
QSettings setting;
if (m.m_repeatPenaltyTokens == t && !m.isClone)
if (m.m_repeatPenaltyTokens == t && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/repeatPenaltyTokens");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/repeatPenaltyTokens", t);
@ -406,7 +584,7 @@ void MySettings::setModelPromptTemplate(const ModelInfo &m, const QString &t, bo
return;
QSettings setting;
if (m.m_promptTemplate == t && !m.isClone)
if (m.m_promptTemplate == t && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/promptTemplate");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/promptTemplate", t);
@ -428,7 +606,7 @@ void MySettings::setModelSystemPrompt(const ModelInfo &m, const QString &p, bool
return;
QSettings setting;
if (m.m_systemPrompt == p && !m.isClone)
if (m.m_systemPrompt == p && !m.shouldSaveMetadata())
setting.remove(QString("model-%1").arg(m.id()) + "/systemPrompt");
else
setting.setValue(QString("model-%1").arg(m.id()) + "/systemPrompt", p);

View File

@ -43,6 +43,26 @@ public:
Q_INVOKABLE void setModelName(const ModelInfo &m, const QString &name, bool force = false);
QString modelFilename(const ModelInfo &m) const;
Q_INVOKABLE void setModelFilename(const ModelInfo &m, const QString &filename, bool force = false);
QString modelDescription(const ModelInfo &m) const;
void setModelDescription(const ModelInfo &m, const QString &d, bool force = false);
QString modelUrl(const ModelInfo &m) const;
void setModelUrl(const ModelInfo &m, const QString &u, bool force = false);
QString modelQuant(const ModelInfo &m) const;
void setModelQuant(const ModelInfo &m, const QString &q, bool force = false);
QString modelType(const ModelInfo &m) const;
void setModelType(const ModelInfo &m, const QString &t, bool force = false);
bool modelIsClone(const ModelInfo &m) const;
void setModelIsClone(const ModelInfo &m, bool b, bool force = false);
bool modelIsDiscovered(const ModelInfo &m) const;
void setModelIsDiscovered(const ModelInfo &m, bool b, bool force = false);
int modelLikes(const ModelInfo &m) const;
void setModelLikes(const ModelInfo &m, int l, bool force = false);
int modelDownloads(const ModelInfo &m) const;
void setModelDownloads(const ModelInfo &m, int d, bool force = false);
QDateTime modelRecency(const ModelInfo &m) const;
void setModelRecency(const ModelInfo &m, const QDateTime &r, bool force = false);
double modelTemperature(const ModelInfo &m) const;
Q_INVOKABLE void setModelTemperature(const ModelInfo &m, double t, bool force = false);
double modelTopP(const ModelInfo &m) const;
@ -112,7 +132,6 @@ public:
bool networkUsageStatsActive() const;
void setNetworkUsageStatsActive(bool b);
QVector<QString> deviceList() const;
void setDeviceList(const QVector<QString> &deviceList);

View File

@ -41,17 +41,222 @@ MyDialog {
Label {
id: listLabel
text: qsTr("Available Models")
visible: false
Layout.alignment: Qt.AlignLeft
text: qsTr("Discover and Download Models")
visible: true
Layout.fillWidth: true
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
color: theme.titleTextColor
font.pixelSize: theme.fontSizeLarge
font.pixelSize: theme.fontSizeLargest
font.bold: true
}
Item {
height: 0 // for visible space between close button and rest of dialog
RowLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
Layout.margins: 0
spacing: 10
MyTextField {
id: discoverField
property string textBeingSearched: ""
readOnly: ModelList.discoverInProgress
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: 720
Layout.preferredHeight: 90
font.pixelSize: theme.fontSizeLarger
placeholderText: qsTr("Discover and download models by keyword search...")
Accessible.role: Accessible.EditableText
Accessible.name: placeholderText
Accessible.description: qsTr("Text field for discovering and filtering downloadable models")
Connections {
target: ModelList
function onDiscoverInProgressChanged() {
if (ModelList.discoverInProgress) {
discoverField.textBeingSearched = discoverField.text;
discoverField.text = qsTr("Searching \u00B7 ") + discoverField.textBeingSearched;
} else {
discoverField.text = discoverField.textBeingSearched;
discoverField.textBeingSearched = "";
}
}
}
background: ProgressBar {
id: discoverProgressBar
indeterminate: ModelList.discoverInProgress && ModelList.discoverProgress === 0.0
value: ModelList.discoverProgress
background: Rectangle {
color: theme.controlBackground
radius: 10
}
contentItem: Item {
Rectangle {
visible: ModelList.discoverInProgress
anchors.bottom: parent.bottom
width: discoverProgressBar.visualPosition * parent.width
height: 10
radius: 2
color: theme.progressForeground
}
}
}
Keys.onReturnPressed: (event)=> {
if (event.modifiers & Qt.ControlModifier || event.modifiers & Qt.ShiftModifier)
event.accepted = false;
else {
editingFinished();
sendDiscovery()
}
}
function sendDiscovery() {
ModelList.downloadableModels.discoverAndFilter(discoverField.text);
}
RowLayout {
spacing: 0
anchors.right: discoverField.right
anchors.verticalCenter: discoverField.verticalCenter
anchors.rightMargin: 15
visible: !ModelList.discoverInProgress
MyMiniButton {
id: clearDiscoverButton
backgroundColor: theme.textColor
backgroundColorHovered: theme.iconBackgroundDark
visible: discoverField.text !== ""
contentItem: Text {
color: clearDiscoverButton.hovered ? theme.iconBackgroundDark : theme.textColor
text: "\u2715"
font.pixelSize: theme.fontSizeLarge
}
onClicked: {
discoverField.text = ""
discoverField.sendDiscovery() // should clear results
}
}
MyMiniButton {
backgroundColor: theme.textColor
backgroundColorHovered: theme.iconBackgroundDark
source: "qrc:/gpt4all/icons/settings.svg"
onClicked: {
discoveryTools.visible = !discoveryTools.visible
}
}
MyMiniButton {
id: sendButton
enabled: !ModelList.discoverInProgress
backgroundColor: theme.textColor
backgroundColorHovered: theme.iconBackgroundDark
source: "qrc:/gpt4all/icons/send_message.svg"
Accessible.name: qsTr("Initiate model discovery and filtering")
Accessible.description: qsTr("Triggers discovery and filtering of models")
onClicked: {
discoverField.sendDiscovery()
}
}
}
}
}
RowLayout {
id: discoveryTools
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
Layout.margins: 0
spacing: 20
visible: false
MyComboBox {
id: comboSort
model: [qsTr("Default"), qsTr("Likes"), qsTr("Downloads"), qsTr("Recent")]
currentIndex: ModelList.discoverSort
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
rightPadding: 30
color: theme.textColor
text: {
return qsTr("Sort by: ") + comboSort.displayText
}
font.pixelSize: theme.fontSizeLarger
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
onActivated: function (index) {
ModelList.discoverSort = index;
}
}
MyComboBox {
id: comboSortDirection
model: [qsTr("Asc"), qsTr("Desc")]
currentIndex: {
if (ModelList.discoverSortDirection === 1)
return 0
else
return 1;
}
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
rightPadding: 30
color: theme.textColor
text: {
return qsTr("Sort dir: ") + comboSortDirection.displayText
}
font.pixelSize: theme.fontSizeLarger
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
onActivated: function (index) {
if (index === 0)
ModelList.discoverSortDirection = 1;
else
ModelList.discoverSortDirection = -1;
}
}
MyComboBox {
id: comboLimit
model: ["5", "10", "20", "50", "100", qsTr("None")]
currentIndex: {
if (ModelList.discoverLimit === 5)
return 0;
else if (ModelList.discoverLimit === 10)
return 1;
else if (ModelList.discoverLimit === 20)
return 2;
else if (ModelList.discoverLimit === 50)
return 3;
else if (ModelList.discoverLimit === 100)
return 4;
else if (ModelList.discoverLimit === -1)
return 5;
}
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
rightPadding: 30
color: theme.textColor
text: {
return qsTr("Limit: ") + comboLimit.displayText
}
font.pixelSize: theme.fontSizeLarger
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
onActivated: function (index) {
switch (index) {
case 0:
ModelList.discoverLimit = 5; break;
case 1:
ModelList.discoverLimit = 10; break;
case 2:
ModelList.discoverLimit = 20; break;
case 3:
ModelList.discoverLimit = 50; break;
case 4:
ModelList.discoverLimit = 100; break;
case 5:
ModelList.discoverLimit = -1; break;
}
}
}
}
Label {
@ -213,15 +418,11 @@ MyDialog {
Layout.leftMargin: 20
textFormat: Text.StyledText
text: "<strong><font size=\"1\">"
+ (qsTr("Download size: ") + filesize)
+ "<br>"
+ (qsTr("RAM required: ") + (ramrequired > 0 ? ramrequired + " GB" : qsTr("minimal")))
+ "<br>"
+ (qsTr("Parameters: ") + parameters)
+ "<br>"
+ (qsTr("Quantization: ") + quant)
+ "<br>"
+ (qsTr("Type: ") + type)
+ (qsTr("File size: ") + filesize)
+ (ramrequired < 0 ? "" : "<br>" + (qsTr("RAM required: ") + (ramrequired > 0 ? ramrequired + " GB" : qsTr("minimal"))))
+ (parameters === "" ? "" : "<br>" + qsTr("Parameters: ") + parameters)
+ (quant === "" ? "" : "<br>" + (qsTr("Quantization: ") + quant))
+ (type === "" ? "" : "<br>" + (qsTr("Type: ") + type))
+ "</strong></font>"
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
@ -350,11 +551,6 @@ MyDialog {
Accessible.role: Accessible.EditableText
Accessible.name: placeholderText
Accessible.description: qsTr("Whether the file hash is being calculated")
TextMetrics {
id: textMetrics
font: apiKey.font
text: apiKey.placeholderText
}
}
}
}

View File

@ -82,7 +82,7 @@ MySettingsTab {
enabled: root.currentModelInfo.isClone
text: qsTr("Remove")
onClicked: {
ModelList.remove(root.currentModelInfo);
ModelList.removeClone(root.currentModelInfo);
comboBox.currentIndex = 0;
}
}
@ -453,7 +453,7 @@ MySettingsTab {
Accessible.description: ToolTip.text
}
MySettingsLabel {
id: minPLabel
id: minPLabel
text: qsTr("Min P")
Layout.row: 3
Layout.column: 0

View File

@ -11,8 +11,8 @@ Button {
property color backgroundColorHovered: theme.iconBackgroundHovered
property alias source: image.source
property alias fillMode: image.fillMode
width: 30
height: 30
implicitWidth: 30
implicitHeight: 30
contentItem: Text {
text: myButton.text
horizontalAlignment: Text.AlignHCenter