mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-06-20 04:34:37 +00:00
feat: Add support for Mistral API models (#2053)
Signed-off-by: Jared Van Bortel <jared@nomic.ai> Signed-off-by: Cédric Sazos <cedric.sazos@tutanota.com> Co-authored-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
parent
406e88b59a
commit
2c0a660e6e
@ -73,7 +73,7 @@ qt_add_executable(chat
|
||||
chat.h chat.cpp
|
||||
chatllm.h chatllm.cpp
|
||||
chatmodel.h chatlistmodel.h chatlistmodel.cpp
|
||||
chatgpt.h chatgpt.cpp
|
||||
chatapi.h chatapi.cpp
|
||||
database.h database.cpp
|
||||
embeddings.h embeddings.cpp
|
||||
download.h download.cpp
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "chatgpt.h"
|
||||
#include "chatapi.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -13,14 +13,15 @@
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
ChatGPT::ChatGPT()
|
||||
ChatAPI::ChatAPI()
|
||||
: QObject(nullptr)
|
||||
, m_modelName("gpt-3.5-turbo")
|
||||
, m_requestURL("")
|
||||
, m_responseCallback(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
size_t ChatGPT::requiredMem(const std::string &modelPath, int n_ctx, int ngl)
|
||||
size_t ChatAPI::requiredMem(const std::string &modelPath, int n_ctx, int ngl)
|
||||
{
|
||||
Q_UNUSED(modelPath);
|
||||
Q_UNUSED(n_ctx);
|
||||
@ -28,7 +29,7 @@ size_t ChatGPT::requiredMem(const std::string &modelPath, int n_ctx, int ngl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ChatGPT::loadModel(const std::string &modelPath, int n_ctx, int ngl)
|
||||
bool ChatAPI::loadModel(const std::string &modelPath, int n_ctx, int ngl)
|
||||
{
|
||||
Q_UNUSED(modelPath);
|
||||
Q_UNUSED(n_ctx);
|
||||
@ -36,59 +37,59 @@ bool ChatGPT::loadModel(const std::string &modelPath, int n_ctx, int ngl)
|
||||
return true;
|
||||
}
|
||||
|
||||
void ChatGPT::setThreadCount(int32_t n_threads)
|
||||
void ChatAPI::setThreadCount(int32_t n_threads)
|
||||
{
|
||||
Q_UNUSED(n_threads);
|
||||
qt_noop();
|
||||
}
|
||||
|
||||
int32_t ChatGPT::threadCount() const
|
||||
int32_t ChatAPI::threadCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
ChatGPT::~ChatGPT()
|
||||
ChatAPI::~ChatAPI()
|
||||
{
|
||||
}
|
||||
|
||||
bool ChatGPT::isModelLoaded() const
|
||||
bool ChatAPI::isModelLoaded() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// All three of the state virtual functions are handled custom inside of chatllm save/restore
|
||||
size_t ChatGPT::stateSize() const
|
||||
size_t ChatAPI::stateSize() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t ChatGPT::saveState(uint8_t *dest) const
|
||||
size_t ChatAPI::saveState(uint8_t *dest) const
|
||||
{
|
||||
Q_UNUSED(dest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t ChatGPT::restoreState(const uint8_t *src)
|
||||
size_t ChatAPI::restoreState(const uint8_t *src)
|
||||
{
|
||||
Q_UNUSED(src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ChatGPT::prompt(const std::string &prompt,
|
||||
const std::string &promptTemplate,
|
||||
std::function<bool(int32_t)> promptCallback,
|
||||
std::function<bool(int32_t, const std::string&)> responseCallback,
|
||||
std::function<bool(bool)> recalculateCallback,
|
||||
PromptContext &promptCtx,
|
||||
bool special,
|
||||
std::string *fakeReply) {
|
||||
void ChatAPI::prompt(const std::string &prompt,
|
||||
const std::string &promptTemplate,
|
||||
std::function<bool(int32_t)> promptCallback,
|
||||
std::function<bool(int32_t, const std::string&)> responseCallback,
|
||||
std::function<bool(bool)> recalculateCallback,
|
||||
PromptContext &promptCtx,
|
||||
bool special,
|
||||
std::string *fakeReply) {
|
||||
|
||||
Q_UNUSED(promptCallback);
|
||||
Q_UNUSED(recalculateCallback);
|
||||
Q_UNUSED(special);
|
||||
|
||||
if (!isModelLoaded()) {
|
||||
std::cerr << "ChatGPT ERROR: prompt won't work with an unloaded model!\n";
|
||||
std::cerr << "ChatAPI ERROR: prompt won't work with an unloaded model!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -128,7 +129,7 @@ void ChatGPT::prompt(const std::string &prompt,
|
||||
QJsonArray messages;
|
||||
for (int i = 0; i < m_context.count(); ++i) {
|
||||
QJsonObject message;
|
||||
message.insert("role", i % 2 == 0 ? "assistant" : "user");
|
||||
message.insert("role", i % 2 == 0 ? "user" : "assistant");
|
||||
message.insert("content", m_context.at(i));
|
||||
messages.append(message);
|
||||
}
|
||||
@ -142,7 +143,7 @@ void ChatGPT::prompt(const std::string &prompt,
|
||||
QJsonDocument doc(root);
|
||||
|
||||
#if defined(DEBUG)
|
||||
qDebug().noquote() << "ChatGPT::prompt begin network request" << doc.toJson();
|
||||
qDebug().noquote() << "ChatAPI::prompt begin network request" << doc.toJson();
|
||||
#endif
|
||||
|
||||
m_responseCallback = responseCallback;
|
||||
@ -150,10 +151,10 @@ void ChatGPT::prompt(const std::string &prompt,
|
||||
// The following code sets up a worker thread and object to perform the actual api request to
|
||||
// chatgpt and then blocks until it is finished
|
||||
QThread workerThread;
|
||||
ChatGPTWorker worker(this);
|
||||
ChatAPIWorker worker(this);
|
||||
worker.moveToThread(&workerThread);
|
||||
connect(&worker, &ChatGPTWorker::finished, &workerThread, &QThread::quit, Qt::DirectConnection);
|
||||
connect(this, &ChatGPT::request, &worker, &ChatGPTWorker::request, Qt::QueuedConnection);
|
||||
connect(&worker, &ChatAPIWorker::finished, &workerThread, &QThread::quit, Qt::DirectConnection);
|
||||
connect(this, &ChatAPI::request, &worker, &ChatAPIWorker::request, Qt::QueuedConnection);
|
||||
workerThread.start();
|
||||
emit request(m_apiKey, &promptCtx, doc.toJson(QJsonDocument::Compact));
|
||||
workerThread.wait();
|
||||
@ -164,40 +165,40 @@ void ChatGPT::prompt(const std::string &prompt,
|
||||
m_responseCallback = nullptr;
|
||||
|
||||
#if defined(DEBUG)
|
||||
qDebug() << "ChatGPT::prompt end network request";
|
||||
qDebug() << "ChatAPI::prompt end network request";
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ChatGPT::callResponse(int32_t token, const std::string& string)
|
||||
bool ChatAPI::callResponse(int32_t token, const std::string& string)
|
||||
{
|
||||
Q_ASSERT(m_responseCallback);
|
||||
if (!m_responseCallback) {
|
||||
std::cerr << "ChatGPT ERROR: no response callback!\n";
|
||||
std::cerr << "ChatAPI ERROR: no response callback!\n";
|
||||
return false;
|
||||
}
|
||||
return m_responseCallback(token, string);
|
||||
}
|
||||
|
||||
void ChatGPTWorker::request(const QString &apiKey,
|
||||
LLModel::PromptContext *promptCtx,
|
||||
const QByteArray &array)
|
||||
void ChatAPIWorker::request(const QString &apiKey,
|
||||
LLModel::PromptContext *promptCtx,
|
||||
const QByteArray &array)
|
||||
{
|
||||
m_ctx = promptCtx;
|
||||
|
||||
QUrl openaiUrl("https://api.openai.com/v1/chat/completions");
|
||||
QUrl apiUrl(m_chat->url());
|
||||
const QString authorization = QString("Bearer %1").arg(apiKey).trimmed();
|
||||
QNetworkRequest request(openaiUrl);
|
||||
QNetworkRequest request(apiUrl);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
request.setRawHeader("Authorization", authorization.toUtf8());
|
||||
m_networkManager = new QNetworkAccessManager(this);
|
||||
QNetworkReply *reply = m_networkManager->post(request, array);
|
||||
connect(qApp, &QCoreApplication::aboutToQuit, reply, &QNetworkReply::abort);
|
||||
connect(reply, &QNetworkReply::finished, this, &ChatGPTWorker::handleFinished);
|
||||
connect(reply, &QNetworkReply::readyRead, this, &ChatGPTWorker::handleReadyRead);
|
||||
connect(reply, &QNetworkReply::errorOccurred, this, &ChatGPTWorker::handleErrorOccurred);
|
||||
connect(reply, &QNetworkReply::finished, this, &ChatAPIWorker::handleFinished);
|
||||
connect(reply, &QNetworkReply::readyRead, this, &ChatAPIWorker::handleReadyRead);
|
||||
connect(reply, &QNetworkReply::errorOccurred, this, &ChatAPIWorker::handleErrorOccurred);
|
||||
}
|
||||
|
||||
void ChatGPTWorker::handleFinished()
|
||||
void ChatAPIWorker::handleFinished()
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||
if (!reply) {
|
||||
@ -210,14 +211,14 @@ void ChatGPTWorker::handleFinished()
|
||||
bool ok;
|
||||
int code = response.toInt(&ok);
|
||||
if (!ok || code != 200) {
|
||||
qWarning() << QString("ERROR: ChatGPT responded with error code \"%1-%2\"")
|
||||
.arg(code).arg(reply->errorString());
|
||||
qWarning().noquote() << "ERROR: ChatAPIWorker::handleFinished got HTTP Error" << code << "response:"
|
||||
<< reply->errorString();
|
||||
}
|
||||
reply->deleteLater();
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void ChatGPTWorker::handleReadyRead()
|
||||
void ChatAPIWorker::handleReadyRead()
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||
if (!reply) {
|
||||
@ -230,8 +231,11 @@ void ChatGPTWorker::handleReadyRead()
|
||||
bool ok;
|
||||
int code = response.toInt(&ok);
|
||||
if (!ok || code != 200) {
|
||||
m_chat->callResponse(-1, QString("\nERROR: 2 ChatGPT responded with error code \"%1-%2\" %3\n")
|
||||
.arg(code).arg(reply->errorString()).arg(reply->readAll()).toStdString());
|
||||
m_chat->callResponse(
|
||||
-1,
|
||||
QString("ERROR: ChatAPIWorker::handleReadyRead got HTTP Error %1 %2: %3")
|
||||
.arg(code).arg(reply->errorString()).arg(reply->readAll()).toStdString()
|
||||
);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
@ -251,8 +255,8 @@ void ChatGPTWorker::handleReadyRead()
|
||||
QJsonParseError err;
|
||||
const QJsonDocument document = QJsonDocument::fromJson(jsonData.toUtf8(), &err);
|
||||
if (err.error != QJsonParseError::NoError) {
|
||||
m_chat->callResponse(-1, QString("\nERROR: ChatGPT responded with invalid json \"%1\"\n")
|
||||
.arg(err.errorString()).toStdString());
|
||||
m_chat->callResponse(-1, QString("ERROR: ChatAPI responded with invalid json \"%1\"")
|
||||
.arg(err.errorString()).toStdString());
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -271,7 +275,7 @@ void ChatGPTWorker::handleReadyRead()
|
||||
}
|
||||
}
|
||||
|
||||
void ChatGPTWorker::handleErrorOccurred(QNetworkReply::NetworkError code)
|
||||
void ChatAPIWorker::handleErrorOccurred(QNetworkReply::NetworkError code)
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||
if (!reply || reply->error() == QNetworkReply::OperationCanceledError /*when we call abort on purpose*/) {
|
||||
@ -279,7 +283,7 @@ void ChatGPTWorker::handleErrorOccurred(QNetworkReply::NetworkError code)
|
||||
return;
|
||||
}
|
||||
|
||||
qWarning() << QString("ERROR: ChatGPT responded with error code \"%1-%2\"")
|
||||
.arg(code).arg(reply->errorString());
|
||||
qWarning().noquote() << "ERROR: ChatAPIWorker::handleErrorOccurred got HTTP Error" << code << "response:"
|
||||
<< reply->errorString();
|
||||
emit finished();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#ifndef CHATGPT_H
|
||||
#define CHATGPT_H
|
||||
#ifndef CHATAPI_H
|
||||
#define CHATAPI_H
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
@ -13,22 +13,22 @@
|
||||
|
||||
#include "../gpt4all-backend/llmodel.h"
|
||||
|
||||
class ChatGPT;
|
||||
class ChatGPTWorker : public QObject {
|
||||
class ChatAPI;
|
||||
class ChatAPIWorker : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ChatGPTWorker(ChatGPT *chatGPT)
|
||||
ChatAPIWorker(ChatAPI *chatAPI)
|
||||
: QObject(nullptr)
|
||||
, m_ctx(nullptr)
|
||||
, m_networkManager(nullptr)
|
||||
, m_chat(chatGPT) {}
|
||||
virtual ~ChatGPTWorker() {}
|
||||
, m_chat(chatAPI) {}
|
||||
virtual ~ChatAPIWorker() {}
|
||||
|
||||
QString currentResponse() const { return m_currentResponse; }
|
||||
|
||||
void request(const QString &apiKey,
|
||||
LLModel::PromptContext *promptCtx,
|
||||
const QByteArray &array);
|
||||
LLModel::PromptContext *promptCtx,
|
||||
const QByteArray &array);
|
||||
|
||||
Q_SIGNALS:
|
||||
void finished();
|
||||
@ -39,17 +39,17 @@ private Q_SLOTS:
|
||||
void handleErrorOccurred(QNetworkReply::NetworkError code);
|
||||
|
||||
private:
|
||||
ChatGPT *m_chat;
|
||||
ChatAPI *m_chat;
|
||||
LLModel::PromptContext *m_ctx;
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QString m_currentResponse;
|
||||
};
|
||||
|
||||
class ChatGPT : public QObject, public LLModel {
|
||||
class ChatAPI : public QObject, public LLModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ChatGPT();
|
||||
virtual ~ChatGPT();
|
||||
ChatAPI();
|
||||
virtual ~ChatAPI();
|
||||
|
||||
bool supportsEmbedding() const override { return false; }
|
||||
bool supportsCompletion() const override { return true; }
|
||||
@ -60,19 +60,21 @@ public:
|
||||
size_t saveState(uint8_t *dest) const override;
|
||||
size_t restoreState(const uint8_t *src) override;
|
||||
void prompt(const std::string &prompt,
|
||||
const std::string &promptTemplate,
|
||||
std::function<bool(int32_t)> promptCallback,
|
||||
std::function<bool(int32_t, const std::string&)> responseCallback,
|
||||
std::function<bool(bool)> recalculateCallback,
|
||||
PromptContext &ctx,
|
||||
bool special,
|
||||
std::string *fakeReply) override;
|
||||
const std::string &promptTemplate,
|
||||
std::function<bool(int32_t)> promptCallback,
|
||||
std::function<bool(int32_t, const std::string&)> responseCallback,
|
||||
std::function<bool(bool)> recalculateCallback,
|
||||
PromptContext &ctx,
|
||||
bool special,
|
||||
std::string *fakeReply) override;
|
||||
|
||||
void setThreadCount(int32_t n_threads) override;
|
||||
int32_t threadCount() const override;
|
||||
|
||||
void setModelName(const QString &modelName) { m_modelName = modelName; }
|
||||
void setAPIKey(const QString &apiKey) { m_apiKey = apiKey; }
|
||||
void setRequestURL(const QString &requestURL) { m_requestURL = requestURL; }
|
||||
QString url() const { return m_requestURL; }
|
||||
|
||||
QList<QString> context() const { return m_context; }
|
||||
void setContext(const QList<QString> &context) { m_context = context; }
|
||||
@ -81,8 +83,8 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void request(const QString &apiKey,
|
||||
LLModel::PromptContext *ctx,
|
||||
const QByteArray &array);
|
||||
LLModel::PromptContext *ctx,
|
||||
const QByteArray &array);
|
||||
|
||||
protected:
|
||||
// We have to implement these as they are pure virtual in base class, but we don't actually use
|
||||
@ -128,8 +130,9 @@ private:
|
||||
std::function<bool(int32_t, const std::string&)> m_responseCallback;
|
||||
QString m_modelName;
|
||||
QString m_apiKey;
|
||||
QString m_requestURL;
|
||||
QList<QString> m_context;
|
||||
QStringList m_queuedPrompts;
|
||||
};
|
||||
|
||||
#endif // CHATGPT_H
|
||||
#endif // CHATAPI_H
|
@ -1,6 +1,6 @@
|
||||
#include "chatllm.h"
|
||||
#include "chat.h"
|
||||
#include "chatgpt.h"
|
||||
#include "chatapi.h"
|
||||
#include "localdocs.h"
|
||||
#include "modellist.h"
|
||||
#include "network.h"
|
||||
@ -213,7 +213,6 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo)
|
||||
if (isModelLoaded() && this->modelInfo() == modelInfo)
|
||||
return true;
|
||||
|
||||
bool isChatGPT = modelInfo.isOnline; // right now only chatgpt is offered for online chat models...
|
||||
QString filePath = modelInfo.dirpath + modelInfo.filename();
|
||||
QFileInfo fileInfo(filePath);
|
||||
|
||||
@ -279,19 +278,23 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo)
|
||||
m_llModelInfo.fileInfo = fileInfo;
|
||||
|
||||
if (fileInfo.exists()) {
|
||||
if (isChatGPT) {
|
||||
if (modelInfo.isOnline) {
|
||||
QString apiKey;
|
||||
QString chatGPTModel = fileInfo.completeBaseName().remove(0, 8); // remove the chatgpt- prefix
|
||||
QString modelName;
|
||||
{
|
||||
QFile file(filePath);
|
||||
file.open(QIODeviceBase::ReadOnly | QIODeviceBase::Text);
|
||||
QTextStream stream(&file);
|
||||
apiKey = stream.readAll();
|
||||
file.close();
|
||||
QString text = stream.readAll();
|
||||
QJsonDocument doc = QJsonDocument::fromJson(text.toUtf8());
|
||||
QJsonObject obj = doc.object();
|
||||
apiKey = obj["apiKey"].toString();
|
||||
modelName = obj["modelName"].toString();
|
||||
}
|
||||
m_llModelType = LLModelType::CHATGPT_;
|
||||
ChatGPT *model = new ChatGPT();
|
||||
model->setModelName(chatGPTModel);
|
||||
m_llModelType = LLModelType::API_;
|
||||
ChatAPI *model = new ChatAPI();
|
||||
model->setModelName(modelName);
|
||||
model->setRequestURL(modelInfo.url());
|
||||
model->setAPIKey(apiKey);
|
||||
m_llModelInfo.model = model;
|
||||
} else {
|
||||
@ -468,7 +471,7 @@ void ChatLLM::regenerateResponse()
|
||||
{
|
||||
// ChatGPT uses a different semantic meaning for n_past than local models. For ChatGPT, the meaning
|
||||
// of n_past is of the number of prompt/response pairs, rather than for total tokens.
|
||||
if (m_llModelType == LLModelType::CHATGPT_)
|
||||
if (m_llModelType == LLModelType::API_)
|
||||
m_ctx.n_past -= 1;
|
||||
else
|
||||
m_ctx.n_past -= m_promptResponseTokens;
|
||||
@ -958,12 +961,12 @@ void ChatLLM::saveState()
|
||||
if (!isModelLoaded())
|
||||
return;
|
||||
|
||||
if (m_llModelType == LLModelType::CHATGPT_) {
|
||||
if (m_llModelType == LLModelType::API_) {
|
||||
m_state.clear();
|
||||
QDataStream stream(&m_state, QIODeviceBase::WriteOnly);
|
||||
stream.setVersion(QDataStream::Qt_6_4);
|
||||
ChatGPT *chatGPT = static_cast<ChatGPT*>(m_llModelInfo.model);
|
||||
stream << chatGPT->context();
|
||||
ChatAPI *chatAPI = static_cast<ChatAPI*>(m_llModelInfo.model);
|
||||
stream << chatAPI->context();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -980,13 +983,13 @@ void ChatLLM::restoreState()
|
||||
if (!isModelLoaded())
|
||||
return;
|
||||
|
||||
if (m_llModelType == LLModelType::CHATGPT_) {
|
||||
if (m_llModelType == LLModelType::API_) {
|
||||
QDataStream stream(&m_state, QIODeviceBase::ReadOnly);
|
||||
stream.setVersion(QDataStream::Qt_6_4);
|
||||
ChatGPT *chatGPT = static_cast<ChatGPT*>(m_llModelInfo.model);
|
||||
ChatAPI *chatAPI = static_cast<ChatAPI*>(m_llModelInfo.model);
|
||||
QList<QString> context;
|
||||
stream >> context;
|
||||
chatGPT->setContext(context);
|
||||
chatAPI->setContext(context);
|
||||
m_state.clear();
|
||||
m_state.squeeze();
|
||||
return;
|
||||
|
@ -12,7 +12,7 @@
|
||||
enum LLModelType {
|
||||
GPTJ_,
|
||||
LLAMA_,
|
||||
CHATGPT_,
|
||||
API_,
|
||||
};
|
||||
|
||||
struct LLModelInfo {
|
||||
|
@ -182,8 +182,17 @@ void Download::installModel(const QString &modelFile, const QString &apiKey)
|
||||
QString filePath = MySettings::globalInstance()->modelPath() + modelFile;
|
||||
QFile file(filePath);
|
||||
if (file.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text)) {
|
||||
|
||||
QJsonObject obj;
|
||||
QString modelName(modelFile);
|
||||
modelName.remove(0, 8); // strip "gpt4all-" prefix
|
||||
modelName.chop(7); // strip ".rmodel" extension
|
||||
obj.insert("apiKey", apiKey);
|
||||
obj.insert("modelName", modelName);
|
||||
QJsonDocument doc(obj);
|
||||
|
||||
QTextStream stream(&file);
|
||||
stream << apiKey;
|
||||
stream << doc.toJson();
|
||||
file.close();
|
||||
ModelList::globalInstance()->updateModelsFromDirectory();
|
||||
}
|
||||
|
@ -1172,6 +1172,44 @@ void ModelList::updateModelsFromDirectory()
|
||||
const QString exePath = QCoreApplication::applicationDirPath() + QDir::separator();
|
||||
const QString localPath = MySettings::globalInstance()->modelPath();
|
||||
|
||||
auto updateOldRemoteModels = [&](const QString& path) {
|
||||
QDirIterator it(path, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
if (!it.fileInfo().isDir()) {
|
||||
QString filename = it.fileName();
|
||||
if (filename.endsWith(".txt")) {
|
||||
QString apikey;
|
||||
QString modelname(filename);
|
||||
modelname.chop(4); // strip ".txt" extension
|
||||
if (filename.startsWith("chatgpt-")) {
|
||||
modelname.remove(0, 8); // strip "chatgpt-" prefix
|
||||
}
|
||||
QFile file(path + filename);
|
||||
if (file.open(QIODevice::ReadWrite)) {
|
||||
QTextStream in(&file);
|
||||
apikey = in.readAll();
|
||||
file.close();
|
||||
}
|
||||
|
||||
QJsonObject obj;
|
||||
obj.insert("apiKey", apikey);
|
||||
obj.insert("modelName", modelname);
|
||||
QJsonDocument doc(obj);
|
||||
|
||||
auto newfilename = QString("gpt4all-%1.rmodel").arg(modelname);
|
||||
QFile newfile(path + newfilename);
|
||||
if (newfile.open(QIODevice::ReadWrite)) {
|
||||
QTextStream out(&newfile);
|
||||
out << doc.toJson();
|
||||
newfile.close();
|
||||
}
|
||||
file.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto processDirectory = [&](const QString& path) {
|
||||
QDirIterator it(path, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
@ -1180,8 +1218,7 @@ void ModelList::updateModelsFromDirectory()
|
||||
if (!it.fileInfo().isDir()) {
|
||||
QString filename = it.fileName();
|
||||
|
||||
if ((filename.endsWith(".gguf") && !filename.startsWith("incomplete"))
|
||||
|| (filename.endsWith(".txt") && (filename.startsWith("chatgpt-") || filename.startsWith("nomic-")))) {
|
||||
if ((filename.endsWith(".gguf") && !filename.startsWith("incomplete")) || filename.endsWith(".rmodel")) {
|
||||
|
||||
QString filePath = it.filePath();
|
||||
QFileInfo info(filePath);
|
||||
@ -1207,8 +1244,7 @@ void ModelList::updateModelsFromDirectory()
|
||||
QVector<QPair<int, QVariant>> data {
|
||||
{ InstalledRole, true },
|
||||
{ FilenameRole, filename },
|
||||
// FIXME: WE should change this to use a consistent filename for online models
|
||||
{ OnlineRole, filename.startsWith("chatgpt-") || filename.startsWith("nomic-") },
|
||||
{ OnlineRole, filename.endsWith(".rmodel") },
|
||||
{ DirpathRole, info.dir().absolutePath() + "/" },
|
||||
{ FilesizeRole, toFileSize(info.size()) },
|
||||
};
|
||||
@ -1219,9 +1255,13 @@ void ModelList::updateModelsFromDirectory()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
updateOldRemoteModels(exePath);
|
||||
processDirectory(exePath);
|
||||
if (localPath != exePath)
|
||||
if (localPath != exePath) {
|
||||
updateOldRemoteModels(localPath);
|
||||
processDirectory(localPath);
|
||||
}
|
||||
}
|
||||
|
||||
#define MODELS_VERSION 3
|
||||
@ -1466,7 +1506,7 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save)
|
||||
{
|
||||
const QString modelName = "ChatGPT-3.5 Turbo";
|
||||
const QString id = modelName;
|
||||
const QString modelFilename = "chatgpt-gpt-3.5-turbo.txt";
|
||||
const QString modelFilename = "gpt4all-gpt-3.5-turbo.rmodel";
|
||||
if (contains(modelFilename))
|
||||
changeId(modelFilename, id);
|
||||
if (!contains(id))
|
||||
@ -1478,12 +1518,13 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save)
|
||||
{ ModelList::OnlineRole, true },
|
||||
{ ModelList::DescriptionRole,
|
||||
tr("<strong>OpenAI's ChatGPT model GPT-3.5 Turbo</strong><br>") + chatGPTDesc },
|
||||
{ ModelList::RequiresVersionRole, "2.4.2" },
|
||||
{ ModelList::RequiresVersionRole, "2.7.4" },
|
||||
{ ModelList::OrderRole, "ca" },
|
||||
{ ModelList::RamrequiredRole, 0 },
|
||||
{ ModelList::ParametersRole, "?" },
|
||||
{ ModelList::QuantRole, "NA" },
|
||||
{ ModelList::TypeRole, "GPT" },
|
||||
{ ModelList::UrlRole, "https://api.openai.com/v1/chat/completions"},
|
||||
};
|
||||
updateData(id, data);
|
||||
}
|
||||
@ -1493,7 +1534,7 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save)
|
||||
|
||||
const QString modelName = "ChatGPT-4";
|
||||
const QString id = modelName;
|
||||
const QString modelFilename = "chatgpt-gpt-4.txt";
|
||||
const QString modelFilename = "gpt4all-gpt-4.rmodel";
|
||||
if (contains(modelFilename))
|
||||
changeId(modelFilename, id);
|
||||
if (!contains(id))
|
||||
@ -1505,15 +1546,99 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save)
|
||||
{ ModelList::OnlineRole, true },
|
||||
{ ModelList::DescriptionRole,
|
||||
tr("<strong>OpenAI's ChatGPT model GPT-4</strong><br>") + chatGPTDesc + chatGPT4Warn },
|
||||
{ ModelList::RequiresVersionRole, "2.4.2" },
|
||||
{ ModelList::RequiresVersionRole, "2.7.4" },
|
||||
{ ModelList::OrderRole, "cb" },
|
||||
{ ModelList::RamrequiredRole, 0 },
|
||||
{ ModelList::ParametersRole, "?" },
|
||||
{ ModelList::QuantRole, "NA" },
|
||||
{ ModelList::TypeRole, "GPT" },
|
||||
{ ModelList::UrlRole, "https://api.openai.com/v1/chat/completions"},
|
||||
};
|
||||
updateData(id, data);
|
||||
}
|
||||
|
||||
const QString mistralDesc = tr("<ul><li>Requires personal Mistral API key.</li><li>WARNING: Will send"
|
||||
" your chats to Mistral!</li><li>Your API key will be stored on disk</li><li>Will only be used"
|
||||
" to communicate with Mistral</li><li>You can apply for an API key"
|
||||
" <a href=\"https://console.mistral.ai/user/api-keys\">here</a>.</li>");
|
||||
|
||||
{
|
||||
const QString modelName = "Mistral Tiny API";
|
||||
const QString id = modelName;
|
||||
const QString modelFilename = "gpt4all-mistral-tiny.rmodel";
|
||||
if (contains(modelFilename))
|
||||
changeId(modelFilename, id);
|
||||
if (!contains(id))
|
||||
addModel(id);
|
||||
QVector<QPair<int, QVariant>> data {
|
||||
{ ModelList::NameRole, modelName },
|
||||
{ ModelList::FilenameRole, modelFilename },
|
||||
{ ModelList::FilesizeRole, "minimal" },
|
||||
{ ModelList::OnlineRole, true },
|
||||
{ ModelList::DescriptionRole,
|
||||
tr("<strong>Mistral Tiny model</strong><br>") + mistralDesc },
|
||||
{ ModelList::RequiresVersionRole, "2.7.4" },
|
||||
{ ModelList::OrderRole, "cc" },
|
||||
{ ModelList::RamrequiredRole, 0 },
|
||||
{ ModelList::ParametersRole, "?" },
|
||||
{ ModelList::QuantRole, "NA" },
|
||||
{ ModelList::TypeRole, "Mistral" },
|
||||
{ ModelList::UrlRole, "https://api.mistral.ai/v1/chat/completions"},
|
||||
};
|
||||
updateData(id, data);
|
||||
}
|
||||
{
|
||||
const QString modelName = "Mistral Small API";
|
||||
const QString id = modelName;
|
||||
const QString modelFilename = "gpt4all-mistral-small.rmodel";
|
||||
if (contains(modelFilename))
|
||||
changeId(modelFilename, id);
|
||||
if (!contains(id))
|
||||
addModel(id);
|
||||
QVector<QPair<int, QVariant>> data {
|
||||
{ ModelList::NameRole, modelName },
|
||||
{ ModelList::FilenameRole, modelFilename },
|
||||
{ ModelList::FilesizeRole, "minimal" },
|
||||
{ ModelList::OnlineRole, true },
|
||||
{ ModelList::DescriptionRole,
|
||||
tr("<strong>Mistral Small model</strong><br>") + mistralDesc },
|
||||
{ ModelList::RequiresVersionRole, "2.7.4" },
|
||||
{ ModelList::OrderRole, "cd" },
|
||||
{ ModelList::RamrequiredRole, 0 },
|
||||
{ ModelList::ParametersRole, "?" },
|
||||
{ ModelList::QuantRole, "NA" },
|
||||
{ ModelList::TypeRole, "Mistral" },
|
||||
{ ModelList::UrlRole, "https://api.mistral.ai/v1/chat/completions"},
|
||||
};
|
||||
updateData(id, data);
|
||||
}
|
||||
|
||||
{
|
||||
const QString modelName = "Mistral Medium API";
|
||||
const QString id = modelName;
|
||||
const QString modelFilename = "gpt4all-mistral-medium.rmodel";
|
||||
if (contains(modelFilename))
|
||||
changeId(modelFilename, id);
|
||||
if (!contains(id))
|
||||
addModel(id);
|
||||
QVector<QPair<int, QVariant>> data {
|
||||
{ ModelList::NameRole, modelName },
|
||||
{ ModelList::FilenameRole, modelFilename },
|
||||
{ ModelList::FilesizeRole, "minimal" },
|
||||
{ ModelList::OnlineRole, true },
|
||||
{ ModelList::DescriptionRole,
|
||||
tr("<strong>Mistral Medium model</strong><br>") + mistralDesc },
|
||||
{ ModelList::RequiresVersionRole, "2.7.4" },
|
||||
{ ModelList::OrderRole, "ce" },
|
||||
{ ModelList::RamrequiredRole, 0 },
|
||||
{ ModelList::ParametersRole, "?" },
|
||||
{ ModelList::QuantRole, "NA" },
|
||||
{ ModelList::TypeRole, "Mistral" },
|
||||
{ ModelList::UrlRole, "https://api.mistral.ai/v1/chat/completions"},
|
||||
};
|
||||
updateData(id, data);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
const QString nomicEmbedDesc = tr("<ul><li>For use with LocalDocs feature</li>"
|
||||
|
Loading…
Reference in New Issue
Block a user