Provide a non-priviledged place for model downloads when exe is installed to root.

pull/520/head
Adam Treat 1 year ago
parent 73df702abe
commit c086a45173

@ -8,6 +8,7 @@
#include <QJsonArray>
#include <QUrl>
#include <QDir>
#include <QStandardPaths>
class MyDownload: public Download { };
Q_GLOBAL_STATIC(MyDownload, downloadInstance)
@ -38,6 +39,26 @@ QList<ModelInfo> Download::modelList() const
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()
{
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);
}
QString filePath = QCoreApplication::applicationDirPath() + QDir::separator() + modelFilename;
QString filePath = downloadLocalModelsPath() + modelFilename;
QFileInfo info(filePath);
ModelInfo modelInfo;
modelInfo.filename = modelFilename;
@ -164,7 +185,6 @@ void Download::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
return;
QString modelFilename = modelReply->url().fileName();
// qDebug() << "handleDownloadProgress" << bytesReceived << bytesTotal << modelFilename;
emit downloadProgress(bytesReceived, bytesTotal, modelFilename);
}
@ -179,7 +199,6 @@ void Download::handleModelDownloadFinished()
return;
QString modelFilename = modelReply->url().fileName();
// qDebug() << "handleModelDownloadFinished" << modelFilename;
m_activeDownloads.removeAll(modelReply);
if (modelReply->error()) {
@ -210,10 +229,18 @@ void Download::handleModelDownloadFinished()
}
// Save the model file to disk
QFile file(QCoreApplication::applicationDirPath() + QDir::separator() + modelFilename);
QFile file(downloadLocalModelsPath() + modelFilename);
if (file.open(QIODevice::WriteOnly)) {
file.write(modelData);
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();

@ -36,6 +36,7 @@ public:
Q_INVOKABLE void updateModelList();
Q_INVOKABLE void downloadModel(const QString &modelFile);
Q_INVOKABLE void cancelDownload(const QString &modelFile);
Q_INVOKABLE QString downloadLocalModelsPath() const;
private Q_SLOTS:
void handleJsonDownloadFinished();

@ -17,6 +17,23 @@ LLM *LLM::globalInstance()
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()
: QObject{nullptr}
, m_llmodel(nullptr)
@ -31,14 +48,15 @@ LLMObject::LLMObject()
bool LLMObject::loadModel()
{
if (modelList().isEmpty()) {
const QList<QString> models = modelList();
if (models.isEmpty()) {
// try again when we get a list of models
connect(Download::globalInstance(), &Download::modelListChanged, this,
&LLMObject::loadModel, Qt::SingleShotConnection);
return false;
}
return loadModelPrivate(modelList().first());
return loadModelPrivate(models.first());
}
bool LLMObject::loadModelPrivate(const QString &modelName)
@ -54,8 +72,7 @@ bool LLMObject::loadModelPrivate(const QString &modelName)
}
bool isGPTJ = false;
QString filePath = QCoreApplication::applicationDirPath() + QDir::separator() +
"ggml-" + modelName + ".bin";
QString filePath = modelFilePath(modelName);
QFileInfo info(filePath);
if (info.exists()) {
@ -169,27 +186,56 @@ void LLMObject::modelNameChangeRequested(const QString &modelName)
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");
QStringList fileNames = dir.entryList();
if (fileNames.isEmpty()) {
qWarning() << "ERROR: Could not find any applicable models in directory"
<< QCoreApplication::applicationDirPath();
return QList<QString>();
for (QString f : fileNames) {
QString filePath = exePath + f;
QFileInfo info(filePath);
QString name = info.completeBaseName().remove(0, 5);
if (info.exists()) {
if (name == m_modelName)
list.prepend(name);
else
list.append(name);
}
}
}
QList<QString> list;
if (localPath != exePath) {
QDir dir(localPath);
dir.setNameFilters(QStringList() << "ggml-*.bin");
QStringList fileNames = dir.entryList();
for (QString f : fileNames) {
QString filePath = QCoreApplication::applicationDirPath() + QDir::separator() + f;
QString filePath = localPath + f;
QFileInfo info(filePath);
QString name = info.completeBaseName().remove(0, 5);
if (info.exists()) {
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;
}

@ -7,7 +7,7 @@ import llm
Dialog {
id: modelDownloaderDialog
width: 1024
height: 400
height: 435
modal: true
opacity: 0.9
closePolicy: LLM.modelList.length === 0 ? Popup.NoAutoClose : (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
@ -28,7 +28,7 @@ Dialog {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 10
spacing: 30
Label {
id: listLabel
@ -38,12 +38,16 @@ Dialog {
color: theme.textColor
}
ListView {
id: modelList
ScrollView {
id: scrollView
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
Layout.fillWidth: true
Layout.fillHeight: true
model: Download.modelList
clip: true
ListView {
id: modelList
model: Download.modelList
boundsBehavior: Flickable.StopAtBounds
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…
Cancel
Save