gpt4all

Форк
0
/
modellist.cpp 
1347 строк · 48.1 Кб
1
#include "modellist.h"
2
#include "mysettings.h"
3
#include "network.h"
4
#include "../gpt4all-backend/llmodel.h"
5

6
#include <QFile>
7
#include <QStandardPaths>
8
#include <algorithm>
9

10
//#define USE_LOCAL_MODELSJSON
11

12
#define DEFAULT_EMBEDDING_MODEL "all-MiniLM-L6-v2-f16.gguf"
13
#define NOMIC_EMBEDDING_MODEL "nomic-embed-text-v1.txt"
14

15
QString ModelInfo::id() const
16
{
17
    return m_id;
18
}
19

20
void ModelInfo::setId(const QString &id)
21
{
22
    m_id = id;
23
}
24

25
QString ModelInfo::name() const
26
{
27
    return MySettings::globalInstance()->modelName(*this);
28
}
29

30
void ModelInfo::setName(const QString &name)
31
{
32
    if (isClone) MySettings::globalInstance()->setModelName(*this, name, isClone /*force*/);
33
    m_name = name;
34
}
35

36
QString ModelInfo::filename() const
37
{
38
    return MySettings::globalInstance()->modelFilename(*this);
39
}
40

41
void ModelInfo::setFilename(const QString &filename)
42
{
43
    if (isClone) MySettings::globalInstance()->setModelFilename(*this, filename, isClone /*force*/);
44
    m_filename = filename;
45
}
46

47
double ModelInfo::temperature() const
48
{
49
    return MySettings::globalInstance()->modelTemperature(*this);
50
}
51

52
void ModelInfo::setTemperature(double t)
53
{
54
    if (isClone) MySettings::globalInstance()->setModelTemperature(*this, t, isClone /*force*/);
55
    m_temperature = t;
56
}
57

58
double ModelInfo::topP() const
59
{
60
    return MySettings::globalInstance()->modelTopP(*this);
61
}
62

63
double ModelInfo::minP() const
64
{
65
    return MySettings::globalInstance()->modelMinP(*this);
66
}
67

68
void ModelInfo::setTopP(double p)
69
{
70
    if (isClone) MySettings::globalInstance()->setModelTopP(*this, p, isClone /*force*/);
71
    m_topP = p;
72
}
73

74
void ModelInfo::setMinP(double p)
75
{
76
    if (isClone) MySettings::globalInstance()->setModelMinP(*this, p, isClone /*force*/);
77
    m_minP = p;
78
}
79

80
int ModelInfo::topK() const
81
{
82
    return MySettings::globalInstance()->modelTopK(*this);
83
}
84

85
void ModelInfo::setTopK(int k)
86
{
87
    if (isClone) MySettings::globalInstance()->setModelTopK(*this, k, isClone /*force*/);
88
    m_topK = k;
89
}
90

91
int ModelInfo::maxLength() const
92
{
93
    return MySettings::globalInstance()->modelMaxLength(*this);
94
}
95

96
void ModelInfo::setMaxLength(int l)
97
{
98
    if (isClone) MySettings::globalInstance()->setModelMaxLength(*this, l, isClone /*force*/);
99
    m_maxLength = l;
100
}
101

102
int ModelInfo::promptBatchSize() const
103
{
104
    return MySettings::globalInstance()->modelPromptBatchSize(*this);
105
}
106

107
void ModelInfo::setPromptBatchSize(int s)
108
{
109
    if (isClone) MySettings::globalInstance()->setModelPromptBatchSize(*this, s, isClone /*force*/);
110
    m_promptBatchSize = s;
111
}
112

113
int ModelInfo::contextLength() const
114
{
115
    return MySettings::globalInstance()->modelContextLength(*this);
116
}
117

118
void ModelInfo::setContextLength(int l)
119
{
120
    if (isClone) MySettings::globalInstance()->setModelContextLength(*this, l, isClone /*force*/);
121
    m_contextLength = l;
122
}
123

124
int ModelInfo::maxContextLength() const
125
{
126
    if (m_maxContextLength != -1) return m_maxContextLength;
127
    auto path = (dirpath + filename()).toStdString();
128
    int layers = LLModel::Implementation::maxContextLength(path);
129
    if (layers < 0) {
130
        layers = 4096; // fallback value
131
    }
132
    m_maxContextLength = layers;
133
    return m_maxContextLength;
134
}
135

136
int ModelInfo::gpuLayers() const
137
{
138
    return MySettings::globalInstance()->modelGpuLayers(*this);
139
}
140

141
void ModelInfo::setGpuLayers(int l)
142
{
143
    if (isClone) MySettings::globalInstance()->setModelGpuLayers(*this, l, isClone /*force*/);
144
    m_gpuLayers = l;
145
}
146

147
int ModelInfo::maxGpuLayers() const
148
{
149
    if (!installed || isOnline) return -1;
150
    if (m_maxGpuLayers != -1) return m_maxGpuLayers;
151
    auto path = (dirpath + filename()).toStdString();
152
    int layers = LLModel::Implementation::layerCount(path);
153
    if (layers < 0) {
154
        layers = 100; // fallback value
155
    }
156
    m_maxGpuLayers = layers;
157
    return m_maxGpuLayers;
158
}
159

160
double ModelInfo::repeatPenalty() const
161
{
162
    return MySettings::globalInstance()->modelRepeatPenalty(*this);
163
}
164

165
void ModelInfo::setRepeatPenalty(double p)
166
{
167
    if (isClone) MySettings::globalInstance()->setModelRepeatPenalty(*this, p, isClone /*force*/);
168
    m_repeatPenalty = p;
169
}
170

171
int ModelInfo::repeatPenaltyTokens() const
172
{
173
    return MySettings::globalInstance()->modelRepeatPenaltyTokens(*this);
174
}
175

176
void ModelInfo::setRepeatPenaltyTokens(int t)
177
{
178
    if (isClone) MySettings::globalInstance()->setModelRepeatPenaltyTokens(*this, t, isClone /*force*/);
179
    m_repeatPenaltyTokens = t;
180
}
181

182
QString ModelInfo::promptTemplate() const
183
{
184
    return MySettings::globalInstance()->modelPromptTemplate(*this);
185
}
186

187
void ModelInfo::setPromptTemplate(const QString &t)
188
{
189
    if (isClone) MySettings::globalInstance()->setModelPromptTemplate(*this, t, isClone /*force*/);
190
    m_promptTemplate = t;
191
}
192

193
QString ModelInfo::systemPrompt() const
194
{
195
    return MySettings::globalInstance()->modelSystemPrompt(*this);
196
}
197

198
void ModelInfo::setSystemPrompt(const QString &p)
199
{
200
    if (isClone) MySettings::globalInstance()->setModelSystemPrompt(*this, p, isClone /*force*/);
201
    m_systemPrompt = p;
202
}
203

204
EmbeddingModels::EmbeddingModels(QObject *parent)
205
    : QSortFilterProxyModel(parent)
206
{
207
    connect(this, &EmbeddingModels::rowsInserted, this, &EmbeddingModels::countChanged);
208
    connect(this, &EmbeddingModels::rowsRemoved, this, &EmbeddingModels::countChanged);
209
    connect(this, &EmbeddingModels::modelReset, this, &EmbeddingModels::countChanged);
210
    connect(this, &EmbeddingModels::layoutChanged, this, &EmbeddingModels::countChanged);
211
}
212

213
bool EmbeddingModels::filterAcceptsRow(int sourceRow,
214
                                       const QModelIndex &sourceParent) const
215
{
216
    QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
217
    bool isInstalled = sourceModel()->data(index, ModelList::InstalledRole).toBool();
218
    bool isEmbedding = sourceModel()->data(index, ModelList::FilenameRole).toString() == DEFAULT_EMBEDDING_MODEL ||
219
        sourceModel()->data(index, ModelList::FilenameRole).toString() == NOMIC_EMBEDDING_MODEL;
220
    return isInstalled && isEmbedding;
221
}
222

223
int EmbeddingModels::count() const
224
{
225
    return rowCount();
226
}
227

228
ModelInfo EmbeddingModels::defaultModelInfo() const
229
{
230
    if (!sourceModel())
231
        return ModelInfo();
232

233
    const ModelList *sourceListModel = qobject_cast<const ModelList*>(sourceModel());
234
    if (!sourceListModel)
235
        return ModelInfo();
236

237
    const int rows = sourceListModel->rowCount();
238
    for (int i = 0; i < rows; ++i) {
239
        QModelIndex sourceIndex = sourceListModel->index(i, 0);
240
        if (filterAcceptsRow(i, sourceIndex.parent())) {
241
            const QString id = sourceListModel->data(sourceIndex, ModelList::IdRole).toString();
242
            return sourceListModel->modelInfo(id);
243
        }
244
    }
245

246
    return ModelInfo();
247
}
248

249
InstalledModels::InstalledModels(QObject *parent)
250
    : QSortFilterProxyModel(parent)
251
{
252
    connect(this, &InstalledModels::rowsInserted, this, &InstalledModels::countChanged);
253
    connect(this, &InstalledModels::rowsRemoved, this, &InstalledModels::countChanged);
254
    connect(this, &InstalledModels::modelReset, this, &InstalledModels::countChanged);
255
    connect(this, &InstalledModels::layoutChanged, this, &InstalledModels::countChanged);
256
}
257

258
bool InstalledModels::filterAcceptsRow(int sourceRow,
259
                                       const QModelIndex &sourceParent) const
260
{
261
    QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
262
    bool isInstalled = sourceModel()->data(index, ModelList::InstalledRole).toBool();
263
    bool showInGUI = !sourceModel()->data(index, ModelList::DisableGUIRole).toBool();
264
    return isInstalled && showInGUI;
265
}
266

267
int InstalledModels::count() const
268
{
269
    return rowCount();
270
}
271

272
DownloadableModels::DownloadableModels(QObject *parent)
273
    : QSortFilterProxyModel(parent)
274
    , m_expanded(false)
275
    , m_limit(5)
276
{
277
    connect(this, &DownloadableModels::rowsInserted, this, &DownloadableModels::countChanged);
278
    connect(this, &DownloadableModels::rowsRemoved, this, &DownloadableModels::countChanged);
279
    connect(this, &DownloadableModels::modelReset, this, &DownloadableModels::countChanged);
280
    connect(this, &DownloadableModels::layoutChanged, this, &DownloadableModels::countChanged);
281
}
282

283
bool DownloadableModels::filterAcceptsRow(int sourceRow,
284
                                          const QModelIndex &sourceParent) const
285
{
286
    bool withinLimit = sourceRow < (m_expanded ? sourceModel()->rowCount() : m_limit);
287
    QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
288
    bool isDownloadable = !sourceModel()->data(index, ModelList::DescriptionRole).toString().isEmpty();
289
    return withinLimit && isDownloadable;
290
}
291

292
int DownloadableModels::count() const
293
{
294
    return rowCount();
295
}
296

297
bool DownloadableModels::isExpanded() const
298
{
299
    return m_expanded;
300
}
301

302
void DownloadableModels::setExpanded(bool expanded)
303
{
304
    if (m_expanded != expanded) {
305
        m_expanded = expanded;
306
        invalidateFilter();
307
        emit expandedChanged(m_expanded);
308
    }
309
}
310

311
class MyModelList: public ModelList { };
312
Q_GLOBAL_STATIC(MyModelList, modelListInstance)
313
ModelList *ModelList::globalInstance()
314
{
315
    return modelListInstance();
316
}
317

318
ModelList::ModelList()
319
    : QAbstractListModel(nullptr)
320
    , m_embeddingModels(new EmbeddingModels(this))
321
    , m_installedModels(new InstalledModels(this))
322
    , m_downloadableModels(new DownloadableModels(this))
323
    , m_asyncModelRequestOngoing(false)
324
{
325
    m_embeddingModels->setSourceModel(this);
326
    m_installedModels->setSourceModel(this);
327
    m_downloadableModels->setSourceModel(this);
328

329
    connect(MySettings::globalInstance(), &MySettings::modelPathChanged, this, &ModelList::updateModelsFromDirectory);
330
    connect(MySettings::globalInstance(), &MySettings::modelPathChanged, this, &ModelList::updateModelsFromJson);
331
    connect(MySettings::globalInstance(), &MySettings::modelPathChanged, this, &ModelList::updateModelsFromSettings);
332
    connect(MySettings::globalInstance(), &MySettings::nameChanged, this, &ModelList::updateDataForSettings);
333
    connect(MySettings::globalInstance(), &MySettings::temperatureChanged, this, &ModelList::updateDataForSettings);
334
    connect(MySettings::globalInstance(), &MySettings::topPChanged, this, &ModelList::updateDataForSettings);
335
    connect(MySettings::globalInstance(), &MySettings::minPChanged, this, &ModelList::updateDataForSettings);
336
    connect(MySettings::globalInstance(), &MySettings::topKChanged, this, &ModelList::updateDataForSettings);
337
    connect(MySettings::globalInstance(), &MySettings::maxLengthChanged, this, &ModelList::updateDataForSettings);
338
    connect(MySettings::globalInstance(), &MySettings::promptBatchSizeChanged, this, &ModelList::updateDataForSettings);
339
    connect(MySettings::globalInstance(), &MySettings::contextLengthChanged, this, &ModelList::updateDataForSettings);
340
    connect(MySettings::globalInstance(), &MySettings::gpuLayersChanged, this, &ModelList::updateDataForSettings);
341
    connect(MySettings::globalInstance(), &MySettings::repeatPenaltyChanged, this, &ModelList::updateDataForSettings);
342
    connect(MySettings::globalInstance(), &MySettings::repeatPenaltyTokensChanged, this, &ModelList::updateDataForSettings);;
343
    connect(MySettings::globalInstance(), &MySettings::promptTemplateChanged, this, &ModelList::updateDataForSettings);
344
    connect(MySettings::globalInstance(), &MySettings::systemPromptChanged, this, &ModelList::updateDataForSettings);
345
    connect(&m_networkManager, &QNetworkAccessManager::sslErrors, this, &ModelList::handleSslErrors);
346

347
    updateModelsFromJson();
348
    updateModelsFromSettings();
349
    updateModelsFromDirectory();
350
}
351

352
QString ModelList::incompleteDownloadPath(const QString &modelFile)
353
{
354
    return MySettings::globalInstance()->modelPath() + "incomplete-" + modelFile;
355
}
356

357
const QList<ModelInfo> ModelList::exportModelList() const
358
{
359
    QMutexLocker locker(&m_mutex);
360
    QList<ModelInfo> infos;
361
    for (ModelInfo *info : m_models)
362
        if (info->installed)
363
            infos.append(*info);
364
    return infos;
365
}
366

367
const QList<QString> ModelList::userDefaultModelList() const
368
{
369
    QMutexLocker locker(&m_mutex);
370

371
    const QString userDefaultModelName = MySettings::globalInstance()->userDefaultModel();
372
    QList<QString> models;
373
    bool foundUserDefault = false;
374
    for (ModelInfo *info : m_models) {
375
        if (info->installed && info->id() == userDefaultModelName) {
376
            foundUserDefault = true;
377
            models.prepend(info->name());
378
        } else if (info->installed) {
379
            models.append(info->name());
380
        }
381
    }
382

383
    const QString defaultId = "Application default";
384
    if (foundUserDefault)
385
        models.append(defaultId);
386
    else
387
        models.prepend(defaultId);
388
    return models;
389
}
390

391
int ModelList::defaultEmbeddingModelIndex() const
392
{
393
    QMutexLocker locker(&m_mutex);
394
    for (int i = 0; i < m_models.size(); ++i) {
395
        const ModelInfo *info = m_models.at(i);
396
        const bool isEmbedding = info->filename() == DEFAULT_EMBEDDING_MODEL;
397
        if (isEmbedding) return i;
398
    }
399
    return -1;
400
}
401

402
ModelInfo ModelList::defaultModelInfo() const
403
{
404
    QMutexLocker locker(&m_mutex);
405

406
    QSettings settings;
407
    settings.sync();
408

409
    // The user default model can be set by the user in the settings dialog. The "default" user
410
    // default model is "Application default" which signals we should use the logic here.
411
    const QString userDefaultModelName = MySettings::globalInstance()->userDefaultModel();
412
    const bool hasUserDefaultName = !userDefaultModelName.isEmpty() && userDefaultModelName != "Application default";
413

414
    ModelInfo *defaultModel = nullptr;
415
    for (ModelInfo *info : m_models) {
416
        if (!info->installed)
417
            continue;
418
        defaultModel = info;
419

420
        const size_t ramrequired = defaultModel->ramrequired;
421

422
        // If we don't have either setting, then just use the first model that requires less than 16GB that is installed
423
        if (!hasUserDefaultName && !info->isOnline && ramrequired > 0 && ramrequired < 16)
424
            break;
425

426
        // If we have a user specified default and match, then use it
427
        if (hasUserDefaultName && (defaultModel->id() == userDefaultModelName))
428
            break;
429
    }
430
    if (defaultModel)
431
        return *defaultModel;
432
    return ModelInfo();
433
}
434

435
bool ModelList::contains(const QString &id) const
436
{
437
    QMutexLocker locker(&m_mutex);
438
    return m_modelMap.contains(id);
439
}
440

441
bool ModelList::containsByFilename(const QString &filename) const
442
{
443
    QMutexLocker locker(&m_mutex);
444
    for (ModelInfo *info : m_models)
445
        if (info->filename() == filename)
446
            return true;
447
    return false;
448
}
449

450
bool ModelList::lessThan(const ModelInfo* a, const ModelInfo* b)
451
{
452
    // Rule 0: Non-clone before clone
453
    if (a->isClone != b->isClone) {
454
        return !a->isClone;
455
    }
456

457
    // Rule 1: Non-empty 'order' before empty
458
    if (a->order.isEmpty() != b->order.isEmpty()) {
459
        return !a->order.isEmpty();
460
    }
461

462
    // Rule 2: Both 'order' are non-empty, sort alphanumerically
463
    if (!a->order.isEmpty() && !b->order.isEmpty()) {
464
        return a->order < b->order;
465
    }
466

467
    // Rule 3: Both 'order' are empty, sort by id
468
    return a->id() < b->id();
469
}
470

471
void ModelList::addModel(const QString &id)
472
{
473
    const bool hasModel = contains(id);
474
    Q_ASSERT(!hasModel);
475
    if (hasModel) {
476
        qWarning() << "ERROR: model list already contains" << id;
477
        return;
478
    }
479

480
    int modelSizeBefore = 0;
481
    int modelSizeAfter = 0;
482
    {
483
        QMutexLocker locker(&m_mutex);
484
        modelSizeBefore = m_models.size();
485
    }
486
    beginInsertRows(QModelIndex(), modelSizeBefore, modelSizeBefore);
487
    {
488
        QMutexLocker locker(&m_mutex);
489
        ModelInfo *info = new ModelInfo;
490
        info->setId(id);
491
        m_models.append(info);
492
        m_modelMap.insert(id, info);
493
        std::stable_sort(m_models.begin(), m_models.end(), ModelList::lessThan);
494
        modelSizeAfter = m_models.size();
495
    }
496
    endInsertRows();
497
    emit dataChanged(index(0, 0), index(modelSizeAfter - 1, 0));
498
    emit userDefaultModelListChanged();
499
}
500

501
void ModelList::changeId(const QString &oldId, const QString &newId)
502
{
503
    const bool hasModel = contains(oldId);
504
    Q_ASSERT(hasModel);
505
    if (!hasModel) {
506
        qWarning() << "ERROR: model list does not contain" << oldId;
507
        return;
508
    }
509

510
    QMutexLocker locker(&m_mutex);
511
    ModelInfo *info = m_modelMap.take(oldId);
512
    info->setId(newId);
513
    m_modelMap.insert(newId, info);
514
}
515

516
int ModelList::rowCount(const QModelIndex &parent) const
517
{
518
    Q_UNUSED(parent)
519
    QMutexLocker locker(&m_mutex);
520
    return m_models.size();
521
}
522

523
QVariant ModelList::dataInternal(const ModelInfo *info, int role) const
524
{
525
    switch (role) {
526
        case IdRole:
527
            return info->id();
528
        case NameRole:
529
            return info->name();
530
        case FilenameRole:
531
            return info->filename();
532
        case DirpathRole:
533
            return info->dirpath;
534
        case FilesizeRole:
535
            return info->filesize;
536
        case Md5sumRole:
537
            return info->md5sum;
538
        case CalcHashRole:
539
            return info->calcHash;
540
        case InstalledRole:
541
            return info->installed;
542
        case DefaultRole:
543
            return info->isDefault;
544
        case OnlineRole:
545
            return info->isOnline;
546
        case DisableGUIRole:
547
            return info->disableGUI;
548
        case DescriptionRole:
549
            return info->description;
550
        case RequiresVersionRole:
551
            return info->requiresVersion;
552
        case DeprecatedVersionRole:
553
            return info->deprecatedVersion;
554
        case UrlRole:
555
            return info->url;
556
        case BytesReceivedRole:
557
            return info->bytesReceived;
558
        case BytesTotalRole:
559
            return info->bytesTotal;
560
        case TimestampRole:
561
            return info->timestamp;
562
        case SpeedRole:
563
            return info->speed;
564
        case DownloadingRole:
565
            return info->isDownloading;
566
        case IncompleteRole:
567
            return info->isIncomplete;
568
        case DownloadErrorRole:
569
            return info->downloadError;
570
        case OrderRole:
571
            return info->order;
572
        case RamrequiredRole:
573
            return info->ramrequired;
574
        case ParametersRole:
575
            return info->parameters;
576
        case QuantRole:
577
            return info->quant;
578
        case TypeRole:
579
            return info->type;
580
        case IsCloneRole:
581
            return info->isClone;
582
        case TemperatureRole:
583
            return info->temperature();
584
        case TopPRole:
585
            return info->topP();
586
        case MinPRole:
587
            return info->minP();
588
        case TopKRole:
589
            return info->topK();
590
        case MaxLengthRole:
591
            return info->maxLength();
592
        case PromptBatchSizeRole:
593
            return info->promptBatchSize();
594
        case ContextLengthRole:
595
            return info->contextLength();
596
        case GpuLayersRole:
597
            return info->gpuLayers();
598
        case RepeatPenaltyRole:
599
            return info->repeatPenalty();
600
        case RepeatPenaltyTokensRole:
601
            return info->repeatPenaltyTokens();
602
        case PromptTemplateRole:
603
            return info->promptTemplate();
604
        case SystemPromptRole:
605
            return info->systemPrompt();
606
    }
607

608
    return QVariant();
609
}
610

611
QVariant ModelList::data(const QString &id, int role) const
612
{
613
    QMutexLocker locker(&m_mutex);
614
    ModelInfo *info = m_modelMap.value(id);
615
    return dataInternal(info, role);
616
}
617

618
QVariant ModelList::dataByFilename(const QString &filename, int role) const
619
{
620
    QMutexLocker locker(&m_mutex);
621
    for (ModelInfo *info : m_models)
622
        if (info->filename() == filename)
623
            return dataInternal(info, role);
624
    return QVariant();
625
}
626

627
QVariant ModelList::data(const QModelIndex &index, int role) const
628
{
629
    QMutexLocker locker(&m_mutex);
630
    if (!index.isValid() || index.row() < 0 || index.row() >= m_models.size())
631
        return QVariant();
632
    const ModelInfo *info = m_models.at(index.row());
633
    return dataInternal(info, role);
634
}
635

636
void ModelList::updateData(const QString &id, int role, const QVariant &value)
637
{
638
    int modelSize;
639
    bool updateInstalled;
640
    bool updateIncomplete;
641
    int index;
642
    {
643
        QMutexLocker locker(&m_mutex);
644
        if (!m_modelMap.contains(id)) {
645
            qWarning() << "ERROR: cannot update as model map does not contain" << id;
646
            return;
647
        }
648

649
        ModelInfo *info = m_modelMap.value(id);
650
        index = m_models.indexOf(info);
651
        if (index == -1) {
652
            qWarning() << "ERROR: cannot update as model list does not contain" << id;
653
            return;
654
        }
655

656
        switch (role) {
657
        case IdRole:
658
            info->setId(value.toString()); break;
659
        case NameRole:
660
            info->setName(value.toString()); break;
661
        case FilenameRole:
662
            info->setFilename(value.toString()); break;
663
        case DirpathRole:
664
            info->dirpath = value.toString(); break;
665
        case FilesizeRole:
666
            info->filesize = value.toString(); break;
667
        case Md5sumRole:
668
            info->md5sum = value.toByteArray(); break;
669
        case CalcHashRole:
670
            info->calcHash = value.toBool(); break;
671
        case InstalledRole:
672
            info->installed = value.toBool(); break;
673
        case DefaultRole:
674
            info->isDefault = value.toBool(); break;
675
        case OnlineRole:
676
            info->isOnline = value.toBool(); break;
677
        case DisableGUIRole:
678
            info->disableGUI = value.toBool(); break;
679
        case DescriptionRole:
680
            info->description = value.toString(); break;
681
        case RequiresVersionRole:
682
            info->requiresVersion = value.toString(); break;
683
        case DeprecatedVersionRole:
684
            info->deprecatedVersion = value.toString(); break;
685
        case UrlRole:
686
            info->url = value.toString(); break;
687
        case BytesReceivedRole:
688
            info->bytesReceived = value.toLongLong(); break;
689
        case BytesTotalRole:
690
            info->bytesTotal = value.toLongLong(); break;
691
        case TimestampRole:
692
            info->timestamp = value.toLongLong(); break;
693
        case SpeedRole:
694
            info->speed = value.toString(); break;
695
        case DownloadingRole:
696
            info->isDownloading = value.toBool(); break;
697
        case IncompleteRole:
698
            info->isIncomplete = value.toBool(); break;
699
        case DownloadErrorRole:
700
            info->downloadError = value.toString(); break;
701
        case OrderRole:
702
            info->order = value.toString(); break;
703
        case RamrequiredRole:
704
            info->ramrequired = value.toInt(); break;
705
        case ParametersRole:
706
            info->parameters = value.toString(); break;
707
        case QuantRole:
708
            info->quant = value.toString(); break;
709
        case TypeRole:
710
            info->type = value.toString(); break;
711
        case IsCloneRole:
712
            info->isClone = value.toBool(); break;
713
        case TemperatureRole:
714
            info->setTemperature(value.toDouble()); break;
715
        case TopPRole:
716
            info->setTopP(value.toDouble()); break;
717
        case MinPRole:
718
            info->setMinP(value.toDouble()); break;
719
        case TopKRole:
720
            info->setTopK(value.toInt()); break;
721
        case MaxLengthRole:
722
            info->setMaxLength(value.toInt()); break;
723
        case PromptBatchSizeRole:
724
            info->setPromptBatchSize(value.toInt()); break;
725
        case ContextLengthRole:
726
            info->setContextLength(value.toInt()); break;
727
        case GpuLayersRole:
728
            info->setGpuLayers(value.toInt()); break;
729
        case RepeatPenaltyRole:
730
            info->setRepeatPenalty(value.toDouble()); break;
731
        case RepeatPenaltyTokensRole:
732
            info->setRepeatPenaltyTokens(value.toInt()); break;
733
        case PromptTemplateRole:
734
            info->setPromptTemplate(value.toString()); break;
735
        case SystemPromptRole:
736
            info->setSystemPrompt(value.toString()); break;
737
        }
738

739
        // Extra guarantee that these always remains in sync with filesystem
740
        QFileInfo fileInfo(info->dirpath + info->filename());
741
        if (info->installed != fileInfo.exists()) {
742
            info->installed = fileInfo.exists();
743
            updateInstalled = true;
744
        }
745
        QFileInfo incompleteInfo(incompleteDownloadPath(info->filename()));
746
        if (info->isIncomplete != incompleteInfo.exists()) {
747
            info->isIncomplete = incompleteInfo.exists();
748
            updateIncomplete = true;
749
        }
750

751
        std::stable_sort(m_models.begin(), m_models.end(), ModelList::lessThan);
752
        modelSize = m_models.size();
753
    }
754
    emit dataChanged(createIndex(0, 0), createIndex(modelSize - 1, 0));
755
    emit userDefaultModelListChanged();
756
}
757

758
void ModelList::updateDataByFilename(const QString &filename, int role, const QVariant &value)
759
{
760
    QVector<QString> modelsById;
761
    {
762
        QMutexLocker locker(&m_mutex);
763
        for (ModelInfo *info : m_models)
764
            if (info->filename() == filename)
765
                modelsById.append(info->id());
766
    }
767

768
    if (modelsById.isEmpty()) {
769
        qWarning() << "ERROR: cannot update model as list does not contain file" << filename;
770
        return;
771
    }
772

773
    for (const QString &id : modelsById)
774
        updateData(id, role, value);;
775
}
776

777
ModelInfo ModelList::modelInfo(const QString &id) const
778
{
779
    QMutexLocker locker(&m_mutex);
780
    if (!m_modelMap.contains(id))
781
        return ModelInfo();
782
    return *m_modelMap.value(id);
783
}
784

785
ModelInfo ModelList::modelInfoByFilename(const QString &filename) const
786
{
787
    QMutexLocker locker(&m_mutex);
788
    for (ModelInfo *info : m_models)
789
        if (info->filename() == filename)
790
            return *info;
791
    return ModelInfo();
792
}
793

794
bool ModelList::isUniqueName(const QString &name) const
795
{
796
    QMutexLocker locker(&m_mutex);
797
    for (const ModelInfo *info : m_models) {
798
        if(info->name() == name)
799
            return false;
800
    }
801
    return true;
802
}
803

804
QString ModelList::clone(const ModelInfo &model)
805
{
806
    const QString id = Network::globalInstance()->generateUniqueId();
807
    addModel(id);
808
    updateData(id, ModelList::IsCloneRole, true);
809
    updateData(id, ModelList::NameRole, uniqueModelName(model));
810
    updateData(id, ModelList::FilenameRole, model.filename());
811
    updateData(id, ModelList::DirpathRole, model.dirpath);
812
    updateData(id, ModelList::InstalledRole, model.installed);
813
    updateData(id, ModelList::OnlineRole, model.isOnline);
814
    updateData(id, ModelList::TemperatureRole, model.temperature());
815
    updateData(id, ModelList::TopPRole, model.topP());
816
    updateData(id, ModelList::MinPRole, model.minP());
817
    updateData(id, ModelList::TopKRole, model.topK());
818
    updateData(id, ModelList::MaxLengthRole, model.maxLength());
819
    updateData(id, ModelList::PromptBatchSizeRole, model.promptBatchSize());
820
    updateData(id, ModelList::ContextLengthRole, model.contextLength());
821
    updateData(id, ModelList::GpuLayersRole, model.gpuLayers());
822
    updateData(id, ModelList::RepeatPenaltyRole, model.repeatPenalty());
823
    updateData(id, ModelList::RepeatPenaltyTokensRole, model.repeatPenaltyTokens());
824
    updateData(id, ModelList::PromptTemplateRole, model.promptTemplate());
825
    updateData(id, ModelList::SystemPromptRole, model.systemPrompt());
826
    return id;
827
}
828

829
void ModelList::remove(const ModelInfo &model)
830
{
831
    Q_ASSERT(model.isClone);
832
    if (!model.isClone)
833
        return;
834

835
    const bool hasModel = contains(model.id());
836
    Q_ASSERT(hasModel);
837
    if (!hasModel) {
838
        qWarning() << "ERROR: model list does not contain" << model.id();
839
        return;
840
    }
841

842
    int indexOfModel = 0;
843
    int modelSizeAfter = 0;
844
    {
845
        QMutexLocker locker(&m_mutex);
846
        ModelInfo *info = m_modelMap.value(model.id());
847
        indexOfModel = m_models.indexOf(info);
848
    }
849
    beginRemoveRows(QModelIndex(), indexOfModel, indexOfModel);
850
    {
851
        QMutexLocker locker(&m_mutex);
852
        ModelInfo *info = m_models.takeAt(indexOfModel);
853
        m_modelMap.remove(info->id());
854
        delete info;
855
        modelSizeAfter = m_models.size();
856
    }
857
    endRemoveRows();
858
    emit dataChanged(index(0, 0), index(modelSizeAfter - 1, 0));
859
    emit userDefaultModelListChanged();
860
    MySettings::globalInstance()->eraseModel(model);
861
}
862

863
QString ModelList::uniqueModelName(const ModelInfo &model) const
864
{
865
    QMutexLocker locker(&m_mutex);
866
    QRegularExpression re("^(.*)~(\\d+)$");
867
    QRegularExpressionMatch match = re.match(model.name());
868
    QString baseName;
869
    if (match.hasMatch())
870
        baseName = match.captured(1);
871
    else
872
        baseName = model.name();
873

874
    int maxSuffixNumber = 0;
875
    bool baseNameExists = false;
876

877
    for (const ModelInfo *info : m_models) {
878
        if(info->name() == baseName)
879
            baseNameExists = true;
880

881
        QRegularExpressionMatch match = re.match(info->name());
882
        if (match.hasMatch()) {
883
            QString currentBaseName = match.captured(1);
884
            int currentSuffixNumber = match.captured(2).toInt();
885
            if (currentBaseName == baseName && currentSuffixNumber > maxSuffixNumber)
886
                maxSuffixNumber = currentSuffixNumber;
887
        }
888
    }
889

890
    if (baseNameExists)
891
        return baseName + "~" + QString::number(maxSuffixNumber + 1);
892

893
    return baseName;
894
}
895

896
QString ModelList::modelDirPath(const QString &modelName, bool isOnline)
897
{
898
    QVector<QString> possibleFilePaths;
899
    if (isOnline)
900
        possibleFilePaths << "/" + modelName + ".txt";
901
    else {
902
        possibleFilePaths << "/ggml-" + modelName + ".bin";
903
        possibleFilePaths << "/" + modelName + ".bin";
904
    }
905
    for (const QString &modelFilename : possibleFilePaths) {
906
        QString appPath = QCoreApplication::applicationDirPath() + modelFilename;
907
        QFileInfo infoAppPath(appPath);
908
        if (infoAppPath.exists())
909
            return QCoreApplication::applicationDirPath();
910

911
        QString downloadPath = MySettings::globalInstance()->modelPath() + modelFilename;
912
        QFileInfo infoLocalPath(downloadPath);
913
        if (infoLocalPath.exists())
914
            return MySettings::globalInstance()->modelPath();
915
    }
916
    return QString();
917
}
918

919
void ModelList::updateModelsFromDirectory()
920
{
921
    const QString exePath = QCoreApplication::applicationDirPath() + QDir::separator();
922
    const QString localPath = MySettings::globalInstance()->modelPath();
923

924
    auto processDirectory = [&](const QString& path) {
925
        QDirIterator it(path, QDirIterator::Subdirectories);
926
        while (it.hasNext()) {
927
            it.next();
928

929
            if (!it.fileInfo().isDir()) {
930
                QString filename = it.fileName();
931

932
                // All files that end with .bin and have 'ggml' somewhere in the name
933
                if (((filename.endsWith(".bin") || filename.endsWith(".gguf")) && (/*filename.contains("ggml") ||*/ filename.contains("gguf")) && !filename.startsWith("incomplete"))
934
                    || (filename.endsWith(".txt") && (filename.startsWith("chatgpt-") || filename.startsWith("nomic-")))) {
935

936
                    QString filePath = it.filePath();
937
                    QFileInfo info(filePath);
938

939
                    if (!info.exists())
940
                        continue;
941

942
                    QVector<QString> modelsById;
943
                    {
944
                        QMutexLocker locker(&m_mutex);
945
                        for (ModelInfo *info : m_models)
946
                            if (info->filename() == filename)
947
                                modelsById.append(info->id());
948
                    }
949

950
                    if (modelsById.isEmpty()) {
951
                        addModel(filename);
952
                        modelsById.append(filename);
953
                    }
954

955
                    for (const QString &id : modelsById) {
956
                        updateData(id, FilenameRole, filename);
957
                        // FIXME: WE should change this to use a consistent filename for online models
958
                        updateData(id, OnlineRole, filename.startsWith("chatgpt-") || filename.startsWith("nomic-"));
959
                        updateData(id, DirpathRole, info.dir().absolutePath() + "/");
960
                        updateData(id, FilesizeRole, toFileSize(info.size()));
961
                    }
962
                }
963
            }
964
        }
965
    };
966

967
    processDirectory(exePath);
968
    if (localPath != exePath)
969
        processDirectory(localPath);
970
}
971

972
#define MODELS_VERSION 3
973

974
void ModelList::updateModelsFromJson()
975
{
976
#if defined(USE_LOCAL_MODELSJSON)
977
    QUrl jsonUrl("file://" + QDir::homePath() + QString("/dev/large_language_models/gpt4all/gpt4all-chat/metadata/models%1.json").arg(MODELS_VERSION));
978
#else
979
    QUrl jsonUrl(QString("http://gpt4all.io/models/models%1.json").arg(MODELS_VERSION));
980
#endif
981
    QNetworkRequest request(jsonUrl);
982
    QSslConfiguration conf = request.sslConfiguration();
983
    conf.setPeerVerifyMode(QSslSocket::VerifyNone);
984
    request.setSslConfiguration(conf);
985
    QNetworkReply *jsonReply = m_networkManager.get(request);
986
    connect(qApp, &QCoreApplication::aboutToQuit, jsonReply, &QNetworkReply::abort);
987
    QEventLoop loop;
988
    connect(jsonReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
989
    QTimer::singleShot(1500, &loop, &QEventLoop::quit);
990
    loop.exec();
991
    if (jsonReply->error() == QNetworkReply::NoError && jsonReply->isFinished()) {
992
        QByteArray jsonData = jsonReply->readAll();
993
        jsonReply->deleteLater();
994
        parseModelsJsonFile(jsonData, true);
995
    } else {
996
        qWarning() << "WARNING: Could not download models.json synchronously";
997
        updateModelsFromJsonAsync();
998

999
        QSettings settings;
1000
        QFileInfo info(settings.fileName());
1001
        QString dirPath = info.canonicalPath();
1002
        const QString modelsConfig = dirPath + "/models.json";
1003
        QFile file(modelsConfig);
1004
        if (!file.open(QIODeviceBase::ReadOnly)) {
1005
            qWarning() << "ERROR: Couldn't read models config file: " << modelsConfig;
1006
        } else {
1007
            QByteArray jsonData = file.readAll();
1008
            file.close();
1009
            parseModelsJsonFile(jsonData, false);
1010
        }
1011
    }
1012
    delete jsonReply;
1013
}
1014

1015
void ModelList::updateModelsFromJsonAsync()
1016
{
1017
    m_asyncModelRequestOngoing = true;
1018
    emit asyncModelRequestOngoingChanged();
1019

1020
#if defined(USE_LOCAL_MODELSJSON)
1021
    QUrl jsonUrl("file://" + QDir::homePath() + QString("/dev/large_language_models/gpt4all/gpt4all-chat/metadata/models%1.json").arg(MODELS_VERSION));
1022
#else
1023
    QUrl jsonUrl(QString("http://gpt4all.io/models/models%1.json").arg(MODELS_VERSION));
1024
#endif
1025
    QNetworkRequest request(jsonUrl);
1026
    QSslConfiguration conf = request.sslConfiguration();
1027
    conf.setPeerVerifyMode(QSslSocket::VerifyNone);
1028
    request.setSslConfiguration(conf);
1029
    QNetworkReply *jsonReply = m_networkManager.get(request);
1030
    connect(qApp, &QCoreApplication::aboutToQuit, jsonReply, &QNetworkReply::abort);
1031
    connect(jsonReply, &QNetworkReply::finished, this, &ModelList::handleModelsJsonDownloadFinished);
1032
    connect(jsonReply, &QNetworkReply::errorOccurred, this, &ModelList::handleModelsJsonDownloadErrorOccurred);
1033
}
1034

1035
void ModelList::handleModelsJsonDownloadFinished()
1036
{
1037
    QNetworkReply *jsonReply = qobject_cast<QNetworkReply *>(sender());
1038
    if (!jsonReply) {
1039
        m_asyncModelRequestOngoing = false;
1040
        emit asyncModelRequestOngoingChanged();
1041
        return;
1042
    }
1043

1044
    QByteArray jsonData = jsonReply->readAll();
1045
    jsonReply->deleteLater();
1046
    parseModelsJsonFile(jsonData, true);
1047
    m_asyncModelRequestOngoing = false;
1048
    emit asyncModelRequestOngoingChanged();
1049
}
1050

1051
void ModelList::handleModelsJsonDownloadErrorOccurred(QNetworkReply::NetworkError code)
1052
{
1053
    // TODO: Show what error occurred in the GUI
1054
    m_asyncModelRequestOngoing = false;
1055
    emit asyncModelRequestOngoingChanged();
1056

1057
    QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
1058
    if (!reply)
1059
        return;
1060

1061
    qWarning() << QString("ERROR: Modellist download failed with error code \"%1-%2\"")
1062
                      .arg(code).arg(reply->errorString()).toStdString();
1063
}
1064

1065
void ModelList::handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
1066
{
1067
    QUrl url = reply->request().url();
1068
    for (const auto &e : errors)
1069
        qWarning() << "ERROR: Received ssl error:" << e.errorString() << "for" << url;
1070
}
1071

1072
void ModelList::updateDataForSettings()
1073
{
1074
    emit dataChanged(index(0, 0), index(m_models.size() - 1, 0));
1075
}
1076

1077
static bool compareVersions(const QString &a, const QString &b) {
1078
    QStringList aParts = a.split('.');
1079
    QStringList bParts = b.split('.');
1080

1081
    for (int i = 0; i < std::min(aParts.size(), bParts.size()); ++i) {
1082
        int aInt = aParts[i].toInt();
1083
        int bInt = bParts[i].toInt();
1084

1085
        if (aInt > bInt) {
1086
            return true;
1087
        } else if (aInt < bInt) {
1088
            return false;
1089
        }
1090
    }
1091

1092
    return aParts.size() > bParts.size();
1093
}
1094

1095
void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save)
1096
{
1097
    QJsonParseError err;
1098
    QJsonDocument document = QJsonDocument::fromJson(jsonData, &err);
1099
    if (err.error != QJsonParseError::NoError) {
1100
        qWarning() << "ERROR: Couldn't parse: " << jsonData << err.errorString();
1101
        return;
1102
    }
1103

1104
    if (save) {
1105
        QSettings settings;
1106
        QFileInfo info(settings.fileName());
1107
        QString dirPath = info.canonicalPath();
1108
        const QString modelsConfig = dirPath + "/models.json";
1109
        QFile file(modelsConfig);
1110
        if (!file.open(QIODeviceBase::WriteOnly)) {
1111
            qWarning() << "ERROR: Couldn't write models config file: " << modelsConfig;
1112
        } else {
1113
            file.write(jsonData.constData());
1114
            file.close();
1115
        }
1116
    }
1117

1118
    QJsonArray jsonArray = document.array();
1119
    const QString currentVersion = QCoreApplication::applicationVersion();
1120

1121
    for (const QJsonValue &value : jsonArray) {
1122
        QJsonObject obj = value.toObject();
1123

1124
        QString modelName = obj["name"].toString();
1125
        QString modelFilename = obj["filename"].toString();
1126
        QString modelFilesize = obj["filesize"].toString();
1127
        QString requiresVersion = obj["requires"].toString();
1128
        QString deprecatedVersion = obj["deprecated"].toString();
1129
        QString url = obj["url"].toString();
1130
        QByteArray modelMd5sum = obj["md5sum"].toString().toLatin1().constData();
1131
        bool isDefault = obj.contains("isDefault") && obj["isDefault"] == QString("true");
1132
        bool disableGUI = obj.contains("disableGUI") && obj["disableGUI"] == QString("true");
1133
        QString description = obj["description"].toString();
1134
        QString order = obj["order"].toString();
1135
        int ramrequired = obj["ramrequired"].toString().toInt();
1136
        QString parameters = obj["parameters"].toString();
1137
        QString quant = obj["quant"].toString();
1138
        QString type = obj["type"].toString();
1139

1140
        // If the currentVersion version is strictly less than required version, then continue
1141
        if (!requiresVersion.isEmpty()
1142
            && requiresVersion != currentVersion
1143
            && compareVersions(requiresVersion, currentVersion)) {
1144
            continue;
1145
        }
1146

1147
        // If the current version is strictly greater than the deprecated version, then continue
1148
        if (!deprecatedVersion.isEmpty()
1149
            && compareVersions(currentVersion, deprecatedVersion)) {
1150
            continue;
1151
        }
1152

1153
        modelFilesize = ModelList::toFileSize(modelFilesize.toULongLong());
1154

1155
        const QString id = modelName;
1156
        Q_ASSERT(!id.isEmpty());
1157

1158
        if (contains(modelFilename))
1159
            changeId(modelFilename, id);
1160

1161
        if (!contains(id))
1162
            addModel(id);
1163

1164
        updateData(id, ModelList::NameRole, modelName);
1165
        updateData(id, ModelList::FilenameRole, modelFilename);
1166
        updateData(id, ModelList::FilesizeRole, modelFilesize);
1167
        updateData(id, ModelList::Md5sumRole, modelMd5sum);
1168
        updateData(id, ModelList::DefaultRole, isDefault);
1169
        updateData(id, ModelList::DescriptionRole, description);
1170
        updateData(id, ModelList::RequiresVersionRole, requiresVersion);
1171
        updateData(id, ModelList::DeprecatedVersionRole, deprecatedVersion);
1172
        updateData(id, ModelList::UrlRole, url);
1173
        updateData(id, ModelList::DisableGUIRole, disableGUI);
1174
        updateData(id, ModelList::OrderRole, order);
1175
        updateData(id, ModelList::RamrequiredRole, ramrequired);
1176
        updateData(id, ModelList::ParametersRole, parameters);
1177
        updateData(id, ModelList::QuantRole, quant);
1178
        updateData(id, ModelList::TypeRole, type);
1179
        if (obj.contains("temperature"))
1180
            updateData(id, ModelList::TemperatureRole, obj["temperature"].toDouble());
1181
        if (obj.contains("topP"))
1182
            updateData(id, ModelList::TopPRole, obj["topP"].toDouble());
1183
        if (obj.contains("minP"))
1184
            updateData(id, ModelList::MinPRole, obj["minP"].toDouble());
1185
        if (obj.contains("topK"))
1186
            updateData(id, ModelList::TopKRole, obj["topK"].toInt());
1187
        if (obj.contains("maxLength"))
1188
            updateData(id, ModelList::MaxLengthRole, obj["maxLength"].toInt());
1189
        if (obj.contains("promptBatchSize"))
1190
            updateData(id, ModelList::PromptBatchSizeRole, obj["promptBatchSize"].toInt());
1191
        if (obj.contains("contextLength"))
1192
            updateData(id, ModelList::ContextLengthRole, obj["contextLength"].toInt());
1193
        if (obj.contains("gpuLayers"))
1194
            updateData(id, ModelList::GpuLayersRole, obj["gpuLayers"].toInt());
1195
        if (obj.contains("repeatPenalty"))
1196
            updateData(id, ModelList::RepeatPenaltyRole, obj["repeatPenalty"].toDouble());
1197
        if (obj.contains("repeatPenaltyTokens"))
1198
            updateData(id, ModelList::RepeatPenaltyTokensRole, obj["repeatPenaltyTokens"].toInt());
1199
        if (obj.contains("promptTemplate"))
1200
            updateData(id, ModelList::PromptTemplateRole, obj["promptTemplate"].toString());
1201
        if (obj.contains("systemPrompt"))
1202
            updateData(id, ModelList::SystemPromptRole, obj["systemPrompt"].toString());
1203
    }
1204

1205
    const QString chatGPTDesc = tr("<ul><li>Requires personal OpenAI API key.</li><li>WARNING: Will send"
1206
        " your chats to OpenAI!</li><li>Your API key will be stored on disk</li><li>Will only be used"
1207
        " to communicate with OpenAI</li><li>You can apply for an API key"
1208
        " <a href=\"https://platform.openai.com/account/api-keys\">here.</a></li>");
1209

1210
    {
1211
        const QString modelName = "ChatGPT-3.5 Turbo";
1212
        const QString id = modelName;
1213
        const QString modelFilename = "chatgpt-gpt-3.5-turbo.txt";
1214
        if (contains(modelFilename))
1215
            changeId(modelFilename, id);
1216
        if (!contains(id))
1217
            addModel(id);
1218
        updateData(id, ModelList::NameRole, modelName);
1219
        updateData(id, ModelList::FilenameRole, modelFilename);
1220
        updateData(id, ModelList::FilesizeRole, "minimal");
1221
        updateData(id, ModelList::OnlineRole, true);
1222
        updateData(id, ModelList::DescriptionRole,
1223
            tr("<strong>OpenAI's ChatGPT model GPT-3.5 Turbo</strong><br>") + chatGPTDesc);
1224
        updateData(id, ModelList::RequiresVersionRole, "2.4.2");
1225
        updateData(id, ModelList::OrderRole, "ca");
1226
        updateData(id, ModelList::RamrequiredRole, 0);
1227
        updateData(id, ModelList::ParametersRole, "?");
1228
        updateData(id, ModelList::QuantRole, "NA");
1229
        updateData(id, ModelList::TypeRole, "GPT");
1230
    }
1231

1232
    {
1233
        const QString chatGPT4Warn = tr("<br><br><i>* Even if you pay OpenAI for ChatGPT-4 this does not guarantee API key access. Contact OpenAI for more info.");
1234

1235
        const QString modelName = "ChatGPT-4";
1236
        const QString id = modelName;
1237
        const QString modelFilename = "chatgpt-gpt-4.txt";
1238
        if (contains(modelFilename))
1239
            changeId(modelFilename, id);
1240
        if (!contains(id))
1241
            addModel(id);
1242
        updateData(id, ModelList::NameRole, modelName);
1243
        updateData(id, ModelList::FilenameRole, modelFilename);
1244
        updateData(id, ModelList::FilesizeRole, "minimal");
1245
        updateData(id, ModelList::OnlineRole, true);
1246
        updateData(id, ModelList::DescriptionRole,
1247
            tr("<strong>OpenAI's ChatGPT model GPT-4</strong><br>") + chatGPTDesc + chatGPT4Warn);
1248
        updateData(id, ModelList::RequiresVersionRole, "2.4.2");
1249
        updateData(id, ModelList::OrderRole, "cb");
1250
        updateData(id, ModelList::RamrequiredRole, 0);
1251
        updateData(id, ModelList::ParametersRole, "?");
1252
        updateData(id, ModelList::QuantRole, "NA");
1253
        updateData(id, ModelList::TypeRole, "GPT");
1254
    }
1255

1256
    {
1257
        const QString nomicEmbedDesc = tr("<ul><li>For use with LocalDocs feature</li>"
1258
            "<li>Used for retrieval augmented generation (RAG)</li>"
1259
            "<li>Requires personal Nomic API key.</li>"
1260
            "<li>WARNING: Will send your localdocs to Nomic Atlas!</li>"
1261
            "<li>You can apply for an API key <a href=\"https://atlas.nomic.ai/\">with Nomic Atlas.</a></li>");
1262
        const QString modelName = "Nomic Embed";
1263
        const QString id = modelName;
1264
        const QString modelFilename = "nomic-embed-text-v1.txt";
1265
        if (contains(modelFilename))
1266
            changeId(modelFilename, id);
1267
        if (!contains(id))
1268
            addModel(id);
1269
        updateData(id, ModelList::NameRole, modelName);
1270
        updateData(id, ModelList::FilenameRole, modelFilename);
1271
        updateData(id, ModelList::FilesizeRole, "minimal");
1272
        updateData(id, ModelList::OnlineRole, true);
1273
        updateData(id, ModelList::DisableGUIRole, true);
1274
        updateData(id, ModelList::DescriptionRole,
1275
            tr("<strong>LocalDocs Nomic Atlas Embed</strong><br>") + nomicEmbedDesc);
1276
        updateData(id, ModelList::RequiresVersionRole, "2.6.3");
1277
        updateData(id, ModelList::OrderRole, "na");
1278
        updateData(id, ModelList::RamrequiredRole, 0);
1279
        updateData(id, ModelList::ParametersRole, "?");
1280
        updateData(id, ModelList::QuantRole, "NA");
1281
        updateData(id, ModelList::TypeRole, "Bert");
1282
    }
1283
}
1284

1285
void ModelList::updateModelsFromSettings()
1286
{
1287
    QSettings settings;
1288
    settings.sync();
1289
    QStringList groups = settings.childGroups();
1290
    for (const QString g : groups) {
1291
        if (!g.startsWith("model-"))
1292
            continue;
1293

1294
        const QString id = g.sliced(6);
1295
        if (contains(id))
1296
            continue;
1297

1298
        if (!settings.contains(g+ "/isClone"))
1299
            continue;
1300

1301
        Q_ASSERT(settings.contains(g + "/name"));
1302
        const QString name = settings.value(g + "/name").toString();
1303
        Q_ASSERT(settings.contains(g + "/filename"));
1304
        const QString filename = settings.value(g + "/filename").toString();
1305
        Q_ASSERT(settings.contains(g + "/temperature"));
1306
        const double temperature = settings.value(g + "/temperature").toDouble();
1307
        Q_ASSERT(settings.contains(g + "/topP"));
1308
        const double topP = settings.value(g + "/topP").toDouble();
1309
        Q_ASSERT(settings.contains(g + "/minP"));
1310
        const double minP = settings.value(g + "/minP").toDouble();
1311
        Q_ASSERT(settings.contains(g + "/topK"));
1312
        const int topK = settings.value(g + "/topK").toInt();
1313
        Q_ASSERT(settings.contains(g + "/maxLength"));
1314
        const int maxLength = settings.value(g + "/maxLength").toInt();
1315
        Q_ASSERT(settings.contains(g + "/promptBatchSize"));
1316
        const int promptBatchSize = settings.value(g + "/promptBatchSize").toInt();
1317
        Q_ASSERT(settings.contains(g + "/contextLength"));
1318
        const int contextLength = settings.value(g + "/contextLength").toInt();
1319
        Q_ASSERT(settings.contains(g + "/gpuLayers"));
1320
        const int gpuLayers = settings.value(g + "/gpuLayers").toInt();
1321
        Q_ASSERT(settings.contains(g + "/repeatPenalty"));
1322
        const double repeatPenalty = settings.value(g + "/repeatPenalty").toDouble();
1323
        Q_ASSERT(settings.contains(g + "/repeatPenaltyTokens"));
1324
        const int repeatPenaltyTokens = settings.value(g + "/repeatPenaltyTokens").toInt();
1325
        Q_ASSERT(settings.contains(g + "/promptTemplate"));
1326
        const QString promptTemplate = settings.value(g + "/promptTemplate").toString();
1327
        Q_ASSERT(settings.contains(g + "/systemPrompt"));
1328
        const QString systemPrompt = settings.value(g + "/systemPrompt").toString();
1329

1330
        addModel(id);
1331
        updateData(id, ModelList::IsCloneRole, true);
1332
        updateData(id, ModelList::NameRole, name);
1333
        updateData(id, ModelList::FilenameRole, filename);
1334
        updateData(id, ModelList::TemperatureRole, temperature);
1335
        updateData(id, ModelList::TopPRole, topP);
1336
        updateData(id, ModelList::MinPRole, minP);
1337
        updateData(id, ModelList::TopKRole, topK);
1338
        updateData(id, ModelList::MaxLengthRole, maxLength);
1339
        updateData(id, ModelList::PromptBatchSizeRole, promptBatchSize);
1340
        updateData(id, ModelList::ContextLengthRole, contextLength);
1341
        updateData(id, ModelList::GpuLayersRole, gpuLayers);
1342
        updateData(id, ModelList::RepeatPenaltyRole, repeatPenalty);
1343
        updateData(id, ModelList::RepeatPenaltyTokensRole, repeatPenaltyTokens);
1344
        updateData(id, ModelList::PromptTemplateRole, promptTemplate);
1345
        updateData(id, ModelList::SystemPromptRole, systemPrompt);
1346
    }
1347
}
1348

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.