modellist: reduce updateData indent

Signed-off-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
Jared Van Bortel 2024-10-04 18:42:47 -04:00
parent 9ec0f92f67
commit 62186a007e
2 changed files with 190 additions and 175 deletions

View File

@ -820,198 +820,207 @@ QVariant ModelList::data(const QModelIndex &index, int role) const
} }
void ModelList::updateData(const QString &id, const QVector<QPair<int, QVariant>> &data) void ModelList::updateData(const QString &id, const QVector<QPair<int, QVariant>> &data)
{
QMutexLocker lock(&m_mutex);
updateDataInternal(id, data, lock, /*relock*/ false);
}
void ModelList::updateDataInternal(const QString &id, const QVector<QPair<int, QVariant>> &data,
QMutexLocker<QMutex> &lock, bool relock)
{ {
Q_ASSERT(QThread::currentThread() == thread()); Q_ASSERT(QThread::currentThread() == thread());
int index; Q_ASSERT(lock.isLocked());
{
QMutexLocker locker(&m_mutex);
if (!m_modelMap.contains(id)) {
qWarning() << "ERROR: cannot update as model map does not contain" << id;
return;
}
ModelInfo *info = m_modelMap.value(id); if (!m_modelMap.contains(id)) {
index = m_models.indexOf(info); qWarning() << "ERROR: cannot update as model map does not contain" << id;
if (index == -1) { return;
qWarning() << "ERROR: cannot update as model list does not contain" << id; }
return;
}
// We only sort when one of the fields used by the sorting algorithm actually changes that ModelInfo *info = m_modelMap.value(id);
// is implicated or used by the sorting algorithm int index = m_models.indexOf(info);
bool shouldSort = false; if (index == -1) {
qWarning() << "ERROR: cannot update as model list does not contain" << id;
return;
}
for (const auto &d : data) { // We only sort when one of the fields used by the sorting algorithm actually changes that
const int role = d.first; // is implicated or used by the sorting algorithm
const QVariant value = d.second; bool shouldSort = false;
switch (role) {
case IdRole: for (const auto &d : data) {
{ const int role = d.first;
if (info->id() != value.toString()) { const QVariant value = d.second;
info->setId(value.toString()); switch (role) {
shouldSort = true; case IdRole:
} {
break; if (info->id() != value.toString()) {
info->setId(value.toString());
shouldSort = true;
} }
case NameRole: break;
info->setName(value.toString()); break; }
case FilenameRole: case NameRole:
info->setFilename(value.toString()); break; info->setName(value.toString()); break;
case DirpathRole: case FilenameRole:
info->dirpath = value.toString(); break; info->setFilename(value.toString()); break;
case FilesizeRole: case DirpathRole:
info->filesize = value.toString(); break; info->dirpath = value.toString(); break;
case HashRole: case FilesizeRole:
info->hash = value.toByteArray(); break; info->filesize = value.toString(); break;
case HashAlgorithmRole: case HashRole:
info->hashAlgorithm = static_cast<ModelInfo::HashAlgorithm>(value.toInt()); break; info->hash = value.toByteArray(); break;
case CalcHashRole: case HashAlgorithmRole:
info->calcHash = value.toBool(); break; info->hashAlgorithm = static_cast<ModelInfo::HashAlgorithm>(value.toInt()); break;
case InstalledRole: case CalcHashRole:
info->installed = value.toBool(); break; info->calcHash = value.toBool(); break;
case DefaultRole: case InstalledRole:
info->isDefault = value.toBool(); break; info->installed = value.toBool(); break;
case OnlineRole: case DefaultRole:
info->isOnline = value.toBool(); break; info->isDefault = value.toBool(); break;
case CompatibleApiRole: case OnlineRole:
info->isCompatibleApi = value.toBool(); break; info->isOnline = value.toBool(); break;
case DescriptionRole: case CompatibleApiRole:
info->setDescription(value.toString()); break; info->isCompatibleApi = value.toBool(); break;
case RequiresVersionRole: case DescriptionRole:
info->requiresVersion = value.toString(); break; info->setDescription(value.toString()); break;
case VersionRemovedRole: case RequiresVersionRole:
info->versionRemoved = value.toString(); break; info->requiresVersion = value.toString(); break;
case UrlRole: case VersionRemovedRole:
info->setUrl(value.toString()); break; info->versionRemoved = value.toString(); break;
case BytesReceivedRole: case UrlRole:
info->bytesReceived = value.toLongLong(); break; info->setUrl(value.toString()); break;
case BytesTotalRole: case BytesReceivedRole:
info->bytesTotal = value.toLongLong(); break; info->bytesReceived = value.toLongLong(); break;
case TimestampRole: case BytesTotalRole:
info->timestamp = value.toLongLong(); break; info->bytesTotal = value.toLongLong(); break;
case SpeedRole: case TimestampRole:
info->speed = value.toString(); break; info->timestamp = value.toLongLong(); break;
case DownloadingRole: case SpeedRole:
info->isDownloading = value.toBool(); break; info->speed = value.toString(); break;
case IncompleteRole: case DownloadingRole:
info->isIncomplete = value.toBool(); break; info->isDownloading = value.toBool(); break;
case DownloadErrorRole: case IncompleteRole:
info->downloadError = value.toString(); break; info->isIncomplete = value.toBool(); break;
case OrderRole: case DownloadErrorRole:
{ info->downloadError = value.toString(); break;
if (info->order != value.toString()) { case OrderRole:
info->order = value.toString(); {
shouldSort = true; if (info->order != value.toString()) {
} info->order = value.toString();
break; shouldSort = true;
} }
case RamrequiredRole: break;
info->ramrequired = value.toInt(); break; }
case ParametersRole: case RamrequiredRole:
info->parameters = value.toString(); break; info->ramrequired = value.toInt(); break;
case QuantRole: case ParametersRole:
info->setQuant(value.toString()); break; info->parameters = value.toString(); break;
case TypeRole: case QuantRole:
info->setType(value.toString()); break; info->setQuant(value.toString()); break;
case IsCloneRole: case TypeRole:
{ info->setType(value.toString()); break;
if (info->isClone() != value.toBool()) { case IsCloneRole:
info->setIsClone(value.toBool()); {
shouldSort = true; if (info->isClone() != value.toBool()) {
} info->setIsClone(value.toBool());
break; shouldSort = true;
} }
case IsDiscoveredRole: break;
{ }
if (info->isDiscovered() != value.toBool()) { case IsDiscoveredRole:
info->setIsDiscovered(value.toBool()); {
shouldSort = true; if (info->isDiscovered() != value.toBool()) {
} info->setIsDiscovered(value.toBool());
break; shouldSort = true;
} }
case IsEmbeddingModelRole: break;
info->isEmbeddingModel = value.toBool(); break; }
case TemperatureRole: case IsEmbeddingModelRole:
info->setTemperature(value.toDouble()); break; info->isEmbeddingModel = value.toBool(); break;
case TopPRole: case TemperatureRole:
info->setTopP(value.toDouble()); break; info->setTemperature(value.toDouble()); break;
case MinPRole: case TopPRole:
info->setMinP(value.toDouble()); break; info->setTopP(value.toDouble()); break;
case TopKRole: case MinPRole:
info->setTopK(value.toInt()); break; info->setMinP(value.toDouble()); break;
case MaxLengthRole: case TopKRole:
info->setMaxLength(value.toInt()); break; info->setTopK(value.toInt()); break;
case PromptBatchSizeRole: case MaxLengthRole:
info->setPromptBatchSize(value.toInt()); break; info->setMaxLength(value.toInt()); break;
case ContextLengthRole: case PromptBatchSizeRole:
info->setContextLength(value.toInt()); break; info->setPromptBatchSize(value.toInt()); break;
case GpuLayersRole: case ContextLengthRole:
info->setGpuLayers(value.toInt()); break; info->setContextLength(value.toInt()); break;
case RepeatPenaltyRole: case GpuLayersRole:
info->setRepeatPenalty(value.toDouble()); break; info->setGpuLayers(value.toInt()); break;
case RepeatPenaltyTokensRole: case RepeatPenaltyRole:
info->setRepeatPenaltyTokens(value.toInt()); break; info->setRepeatPenalty(value.toDouble()); break;
case PromptTemplateRole: case RepeatPenaltyTokensRole:
info->setPromptTemplate(value.toString()); break; info->setRepeatPenaltyTokens(value.toInt()); break;
case SystemPromptRole: case PromptTemplateRole:
info->setSystemPrompt(value.toString()); break; info->setPromptTemplate(value.toString()); break;
case ChatNamePromptRole: case SystemPromptRole:
info->setChatNamePrompt(value.toString()); break; info->setSystemPrompt(value.toString()); break;
case SuggestedFollowUpPromptRole: case ChatNamePromptRole:
info->setSuggestedFollowUpPrompt(value.toString()); break; info->setChatNamePrompt(value.toString()); break;
case LikesRole: case SuggestedFollowUpPromptRole:
{ info->setSuggestedFollowUpPrompt(value.toString()); break;
if (info->likes() != value.toInt()) { case LikesRole:
info->setLikes(value.toInt()); {
shouldSort = true; if (info->likes() != value.toInt()) {
} info->setLikes(value.toInt());
break; shouldSort = true;
} }
case DownloadsRole: break;
{ }
if (info->downloads() != value.toInt()) { case DownloadsRole:
info->setDownloads(value.toInt()); {
shouldSort = true; if (info->downloads() != value.toInt()) {
} info->setDownloads(value.toInt());
break; shouldSort = true;
} }
case RecencyRole: break;
{ }
if (info->recency() != value.toDateTime()) { case RecencyRole:
info->setRecency(value.toDateTime()); {
shouldSort = true; if (info->recency() != value.toDateTime()) {
} info->setRecency(value.toDateTime());
break; shouldSort = true;
} }
break;
} }
} }
// Extra guarantee that these always remains in sync with filesystem
QString modelPath = info->dirpath + info->filename();
const QFileInfo fileInfo(modelPath);
info->installed = fileInfo.exists();
const QFileInfo incompleteInfo(incompleteDownloadPath(info->filename()));
info->isIncomplete = incompleteInfo.exists();
// check installed, discovered/sideloaded models only (including clones)
if (!info->checkedEmbeddingModel && !info->isEmbeddingModel && info->installed
&& (info->isDiscovered() || info->description().isEmpty()))
{
// read GGUF and decide based on model architecture
info->isEmbeddingModel = LLModel::Implementation::isEmbeddingModel(modelPath.toStdString());
info->checkedEmbeddingModel = true;
}
if (shouldSort) {
auto s = m_discoverSort;
auto d = m_discoverSortDirection;
std::stable_sort(m_models.begin(), m_models.end(), [s, d](const ModelInfo* lhs, const ModelInfo* rhs) {
return ModelList::lessThan(lhs, rhs, s, d);
});
}
} }
// Extra guarantee that these always remains in sync with filesystem
QString modelPath = info->dirpath + info->filename();
const QFileInfo fileInfo(modelPath);
info->installed = fileInfo.exists();
const QFileInfo incompleteInfo(incompleteDownloadPath(info->filename()));
info->isIncomplete = incompleteInfo.exists();
// check installed, discovered/sideloaded models only (including clones)
if (!info->checkedEmbeddingModel && !info->isEmbeddingModel && info->installed
&& (info->isDiscovered() || info->description().isEmpty()))
{
// read GGUF and decide based on model architecture
info->isEmbeddingModel = LLModel::Implementation::isEmbeddingModel(modelPath.toStdString());
info->checkedEmbeddingModel = true;
}
if (shouldSort) {
auto s = m_discoverSort;
auto d = m_discoverSortDirection;
std::stable_sort(m_models.begin(), m_models.end(), [s, d](const ModelInfo* lhs, const ModelInfo* rhs) {
return ModelList::lessThan(lhs, rhs, s, d);
});
}
lock.unlock();
emit dataChanged(createIndex(index, 0), createIndex(index, 0)); emit dataChanged(createIndex(index, 0), createIndex(index, 0));
emit selectableModelListChanged(); emit selectableModelListChanged();
if (relock)
lock.relock();
} }
void ModelList::resortModel() void ModelList::resortModel()

View File

@ -23,6 +23,8 @@
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
template <typename> class QMutexLocker;
struct ModelInfo { struct ModelInfo {
Q_GADGET Q_GADGET
@ -495,6 +497,10 @@ private Q_SLOTS:
void handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors); void handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
private: private:
// call with lock held
void updateDataInternal(const QString &id, const QVector<QPair<int, QVariant>> &data, QMutexLocker<QMutex> &lock,
bool relock = true);
void removeInternal(const ModelInfo &model); void removeInternal(const ModelInfo &model);
void clearDiscoveredModels(); void clearDiscoveredModels();
bool modelExists(const QString &fileName) const; bool modelExists(const QString &fileName) const;