mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-08-16 15:07:18 +00:00
Provide a non-priviledged place for model downloads when exe is installed to root.
This commit is contained in:
parent
09c27f6ec4
commit
29685b3eab
35
download.cpp
35
download.cpp
@ -8,6 +8,7 @@
|
|||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
class MyDownload: public Download { };
|
class MyDownload: public Download { };
|
||||||
Q_GLOBAL_STATIC(MyDownload, downloadInstance)
|
Q_GLOBAL_STATIC(MyDownload, downloadInstance)
|
||||||
@ -38,6 +39,26 @@ QList<ModelInfo> Download::modelList() const
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Download::downloadLocalModelsPath() const
|
||||||
|
{
|
||||||
|
QString exePath = QCoreApplication::applicationDirPath() + QDir::separator();
|
||||||
|
QFileInfo infoExe(exePath);
|
||||||
|
if (infoExe.isWritable())
|
||||||
|
return exePath;
|
||||||
|
|
||||||
|
QString localPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
|
||||||
|
QDir localDir(localPath);
|
||||||
|
if (!localDir.exists())
|
||||||
|
localDir.mkpath(localPath);
|
||||||
|
QString localDownloadPath = localPath
|
||||||
|
+ QDir::separator();
|
||||||
|
QFileInfo infoLocal(localDownloadPath);
|
||||||
|
if (infoLocal.isWritable())
|
||||||
|
return localDownloadPath;
|
||||||
|
qWarning() << "ERROR: Local download path appears not writeable:" << localDownloadPath;
|
||||||
|
return localDownloadPath;
|
||||||
|
}
|
||||||
|
|
||||||
void Download::updateModelList()
|
void Download::updateModelList()
|
||||||
{
|
{
|
||||||
QUrl jsonUrl("http://gpt4all.io/models/models.json");
|
QUrl jsonUrl("http://gpt4all.io/models/models.json");
|
||||||
@ -143,7 +164,7 @@ void Download::parseJsonFile(const QByteArray &jsonData)
|
|||||||
modelFilesize = QString("%1 GB").arg(qreal(sz) / (1024 * 1024 * 1024), 0, 'g', 3);
|
modelFilesize = QString("%1 GB").arg(qreal(sz) / (1024 * 1024 * 1024), 0, 'g', 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString filePath = QCoreApplication::applicationDirPath() + QDir::separator() + modelFilename;
|
QString filePath = downloadLocalModelsPath() + modelFilename;
|
||||||
QFileInfo info(filePath);
|
QFileInfo info(filePath);
|
||||||
ModelInfo modelInfo;
|
ModelInfo modelInfo;
|
||||||
modelInfo.filename = modelFilename;
|
modelInfo.filename = modelFilename;
|
||||||
@ -164,7 +185,6 @@ void Download::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
QString modelFilename = modelReply->url().fileName();
|
QString modelFilename = modelReply->url().fileName();
|
||||||
// qDebug() << "handleDownloadProgress" << bytesReceived << bytesTotal << modelFilename;
|
|
||||||
emit downloadProgress(bytesReceived, bytesTotal, modelFilename);
|
emit downloadProgress(bytesReceived, bytesTotal, modelFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +199,6 @@ void Download::handleModelDownloadFinished()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
QString modelFilename = modelReply->url().fileName();
|
QString modelFilename = modelReply->url().fileName();
|
||||||
// qDebug() << "handleModelDownloadFinished" << modelFilename;
|
|
||||||
m_activeDownloads.removeAll(modelReply);
|
m_activeDownloads.removeAll(modelReply);
|
||||||
|
|
||||||
if (modelReply->error()) {
|
if (modelReply->error()) {
|
||||||
@ -210,10 +229,18 @@ void Download::handleModelDownloadFinished()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save the model file to disk
|
// Save the model file to disk
|
||||||
QFile file(QCoreApplication::applicationDirPath() + QDir::separator() + modelFilename);
|
QFile file(downloadLocalModelsPath() + modelFilename);
|
||||||
if (file.open(QIODevice::WriteOnly)) {
|
if (file.open(QIODevice::WriteOnly)) {
|
||||||
file.write(modelData);
|
file.write(modelData);
|
||||||
file.close();
|
file.close();
|
||||||
|
} else {
|
||||||
|
QFile::FileError error = file.error();
|
||||||
|
qWarning() << "ERROR: Could not save model to location:"
|
||||||
|
<< downloadLocalModelsPath() + modelFilename
|
||||||
|
<< "failed with code" << error;
|
||||||
|
modelReply->deleteLater();
|
||||||
|
emit downloadFinished(modelFilename);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
modelReply->deleteLater();
|
modelReply->deleteLater();
|
||||||
|
@ -36,6 +36,7 @@ public:
|
|||||||
Q_INVOKABLE void updateModelList();
|
Q_INVOKABLE void updateModelList();
|
||||||
Q_INVOKABLE void downloadModel(const QString &modelFile);
|
Q_INVOKABLE void downloadModel(const QString &modelFile);
|
||||||
Q_INVOKABLE void cancelDownload(const QString &modelFile);
|
Q_INVOKABLE void cancelDownload(const QString &modelFile);
|
||||||
|
Q_INVOKABLE QString downloadLocalModelsPath() const;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void handleJsonDownloadFinished();
|
void handleJsonDownloadFinished();
|
||||||
|
72
llm.cpp
72
llm.cpp
@ -17,6 +17,23 @@ LLM *LLM::globalInstance()
|
|||||||
|
|
||||||
static LLModel::PromptContext s_ctx;
|
static LLModel::PromptContext s_ctx;
|
||||||
|
|
||||||
|
static QString modelFilePath(const QString &modelName)
|
||||||
|
{
|
||||||
|
QString appPath = QCoreApplication::applicationDirPath()
|
||||||
|
+ QDir::separator() + "ggml-" + modelName + ".bin";
|
||||||
|
QFileInfo infoAppPath(appPath);
|
||||||
|
if (infoAppPath.exists())
|
||||||
|
return appPath;
|
||||||
|
|
||||||
|
QString downloadPath = Download::globalInstance()->downloadLocalModelsPath()
|
||||||
|
+ QDir::separator() + "ggml-" + modelName + ".bin";
|
||||||
|
|
||||||
|
QFileInfo infoLocalPath(downloadPath);
|
||||||
|
if (infoLocalPath.exists())
|
||||||
|
return downloadPath;
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
LLMObject::LLMObject()
|
LLMObject::LLMObject()
|
||||||
: QObject{nullptr}
|
: QObject{nullptr}
|
||||||
, m_llmodel(nullptr)
|
, m_llmodel(nullptr)
|
||||||
@ -31,14 +48,15 @@ LLMObject::LLMObject()
|
|||||||
|
|
||||||
bool LLMObject::loadModel()
|
bool LLMObject::loadModel()
|
||||||
{
|
{
|
||||||
if (modelList().isEmpty()) {
|
const QList<QString> models = modelList();
|
||||||
|
if (models.isEmpty()) {
|
||||||
// try again when we get a list of models
|
// try again when we get a list of models
|
||||||
connect(Download::globalInstance(), &Download::modelListChanged, this,
|
connect(Download::globalInstance(), &Download::modelListChanged, this,
|
||||||
&LLMObject::loadModel, Qt::SingleShotConnection);
|
&LLMObject::loadModel, Qt::SingleShotConnection);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return loadModelPrivate(modelList().first());
|
return loadModelPrivate(models.first());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLMObject::loadModelPrivate(const QString &modelName)
|
bool LLMObject::loadModelPrivate(const QString &modelName)
|
||||||
@ -54,8 +72,7 @@ bool LLMObject::loadModelPrivate(const QString &modelName)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isGPTJ = false;
|
bool isGPTJ = false;
|
||||||
QString filePath = QCoreApplication::applicationDirPath() + QDir::separator() +
|
QString filePath = modelFilePath(modelName);
|
||||||
"ggml-" + modelName + ".bin";
|
|
||||||
QFileInfo info(filePath);
|
QFileInfo info(filePath);
|
||||||
if (info.exists()) {
|
if (info.exists()) {
|
||||||
|
|
||||||
@ -169,18 +186,18 @@ void LLMObject::modelNameChangeRequested(const QString &modelName)
|
|||||||
|
|
||||||
QList<QString> LLMObject::modelList() const
|
QList<QString> LLMObject::modelList() const
|
||||||
{
|
{
|
||||||
QDir dir(QCoreApplication::applicationDirPath());
|
// Build a model list from exepath and from the localpath
|
||||||
|
QList<QString> list;
|
||||||
|
|
||||||
|
QString exePath = QCoreApplication::applicationDirPath() + QDir::separator();
|
||||||
|
QString localPath = Download::globalInstance()->downloadLocalModelsPath();
|
||||||
|
|
||||||
|
{
|
||||||
|
QDir dir(exePath);
|
||||||
dir.setNameFilters(QStringList() << "ggml-*.bin");
|
dir.setNameFilters(QStringList() << "ggml-*.bin");
|
||||||
QStringList fileNames = dir.entryList();
|
QStringList fileNames = dir.entryList();
|
||||||
if (fileNames.isEmpty()) {
|
|
||||||
qWarning() << "ERROR: Could not find any applicable models in directory"
|
|
||||||
<< QCoreApplication::applicationDirPath();
|
|
||||||
return QList<QString>();
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QString> list;
|
|
||||||
for (QString f : fileNames) {
|
for (QString f : fileNames) {
|
||||||
QString filePath = QCoreApplication::applicationDirPath() + QDir::separator() + f;
|
QString filePath = exePath + f;
|
||||||
QFileInfo info(filePath);
|
QFileInfo info(filePath);
|
||||||
QString name = info.completeBaseName().remove(0, 5);
|
QString name = info.completeBaseName().remove(0, 5);
|
||||||
if (info.exists()) {
|
if (info.exists()) {
|
||||||
@ -190,6 +207,35 @@ QList<QString> LLMObject::modelList() const
|
|||||||
list.append(name);
|
list.append(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localPath != exePath) {
|
||||||
|
QDir dir(localPath);
|
||||||
|
dir.setNameFilters(QStringList() << "ggml-*.bin");
|
||||||
|
QStringList fileNames = dir.entryList();
|
||||||
|
for (QString f : fileNames) {
|
||||||
|
QString filePath = localPath + f;
|
||||||
|
QFileInfo info(filePath);
|
||||||
|
QString name = info.completeBaseName().remove(0, 5);
|
||||||
|
if (info.exists() && !list.contains(name)) { // don't allow duplicates
|
||||||
|
if (name == m_modelName)
|
||||||
|
list.prepend(name);
|
||||||
|
else
|
||||||
|
list.append(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
if (exePath != localPath) {
|
||||||
|
qWarning() << "ERROR: Could not find any applicable models in"
|
||||||
|
<< exePath << "nor" << localPath;
|
||||||
|
} else {
|
||||||
|
qWarning() << "ERROR: Could not find any applicable models in"
|
||||||
|
<< exePath;
|
||||||
|
}
|
||||||
|
return QList<QString>();
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import llm
|
|||||||
Dialog {
|
Dialog {
|
||||||
id: modelDownloaderDialog
|
id: modelDownloaderDialog
|
||||||
width: 1024
|
width: 1024
|
||||||
height: 400
|
height: 435
|
||||||
modal: true
|
modal: true
|
||||||
opacity: 0.9
|
opacity: 0.9
|
||||||
closePolicy: LLM.modelList.length === 0 ? Popup.NoAutoClose : (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
|
closePolicy: LLM.modelList.length === 0 ? Popup.NoAutoClose : (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
|
||||||
@ -28,7 +28,7 @@ Dialog {
|
|||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 20
|
anchors.margins: 20
|
||||||
spacing: 10
|
spacing: 30
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: listLabel
|
id: listLabel
|
||||||
@ -38,12 +38,16 @@ Dialog {
|
|||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ScrollView {
|
||||||
id: modelList
|
id: scrollView
|
||||||
|
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
model: Download.modelList
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: modelList
|
||||||
|
model: Download.modelList
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
@ -233,4 +237,17 @@ Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("NOTE: models will be downloaded to\n") + Download.downloadLocalModelsPath()
|
||||||
|
wrapMode: Text.WrapAnywhere
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: theme.textColor
|
||||||
|
Accessible.role: Accessible.Paragraph
|
||||||
|
Accessible.name: qsTr("Model download path")
|
||||||
|
Accessible.description: qsTr("The path where downloaded models will be saved.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user