191 lines
5.3 KiB
C++
191 lines
5.3 KiB
C++
// SPDX-FileCopyrightText: 2023 Loren Burkholder <computersemiexpert@outlook.com>
|
|
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
|
//
|
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
|
|
#include "KLLMInterface.h"
|
|
|
|
#include <KLocalizedString>
|
|
|
|
#include <QBuffer>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QNetworkAccessManager>
|
|
#include <QNetworkReply>
|
|
|
|
using namespace Qt::StringLiterals;
|
|
using namespace KLLMCore;
|
|
|
|
KLLMInterface::KLLMInterface(QObject *parent)
|
|
: KLLMInterface{QString{}, parent}
|
|
{
|
|
}
|
|
|
|
KLLMInterface::KLLMInterface(const QString &ollamaUrl, QObject *parent)
|
|
: QObject{parent}
|
|
, m_manager{new QNetworkAccessManager{this}}
|
|
, m_ollamaUrl{ollamaUrl}
|
|
{
|
|
if (!m_ollamaUrl.isEmpty())
|
|
reload();
|
|
}
|
|
|
|
KLLMInterface::KLLMInterface(const QUrl &ollamaUrl, QObject *parent)
|
|
: KLLMInterface{ollamaUrl.toString(), parent}
|
|
{
|
|
}
|
|
|
|
bool KLLMInterface::ready() const
|
|
{
|
|
return m_ready && !m_hasError;
|
|
}
|
|
|
|
bool KLLMInterface::hasError() const
|
|
{
|
|
return m_hasError;
|
|
}
|
|
|
|
QStringList KLLMInterface::models() const
|
|
{
|
|
return m_models;
|
|
}
|
|
|
|
#if 0
|
|
void KLLMInterface::deleteModel(const QString &modelName)
|
|
{
|
|
Q_ASSERT(ready());
|
|
|
|
QNetworkRequest req{QUrl::fromUserInput(m_ollamaUrl + QStringLiteral("/api/delete"))};
|
|
req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
|
|
QJsonObject data;
|
|
data["name"_L1] = modelName;
|
|
|
|
// Delete resource doesn't take argument. Need to look at how to do it.
|
|
auto buf = new QBuffer{this};
|
|
buf->setData(QJsonDocument(data).toJson(QJsonDocument::Compact));
|
|
|
|
auto reply = new KLLMReply{m_manager->deleteResource(req, buf), this};
|
|
connect(reply, &KLLMReply::finished, this, [this, reply, buf] {
|
|
Q_EMIT finished(reply->readResponse());
|
|
buf->deleteLater();
|
|
});
|
|
}
|
|
#endif
|
|
|
|
KLLMReply *KLLMInterface::getCompletion(const KLLMRequest &request)
|
|
{
|
|
Q_ASSERT(ready());
|
|
|
|
QNetworkRequest req{QUrl::fromUserInput(m_ollamaUrl + QStringLiteral("/api/generate"))};
|
|
req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
|
|
|
|
QJsonObject data;
|
|
data["model"_L1] = request.model().isEmpty() ? m_models.constFirst() : request.model();
|
|
data["prompt"_L1] = request.message();
|
|
|
|
const auto context = request.context().toJson();
|
|
if (!context.isNull()) {
|
|
data["context"_L1] = context;
|
|
}
|
|
|
|
if (!m_systemPrompt.isEmpty()) {
|
|
data["system"_L1] = m_systemPrompt;
|
|
}
|
|
|
|
auto buf = new QBuffer{this};
|
|
buf->setData(QJsonDocument(data).toJson(QJsonDocument::Compact));
|
|
|
|
auto reply = new KLLMReply{m_manager->post(req, buf), this};
|
|
connect(reply, &KLLMReply::finished, this, [this, reply, buf] {
|
|
Q_EMIT finished(reply->readResponse());
|
|
buf->deleteLater();
|
|
});
|
|
return reply;
|
|
}
|
|
|
|
KLLMReply *KLLMInterface::getModelInfo(const KLLMRequest &request)
|
|
{
|
|
Q_ASSERT(ready());
|
|
|
|
QNetworkRequest req{QUrl::fromUserInput(m_ollamaUrl + QStringLiteral("/api/show"))};
|
|
req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
|
|
|
|
QJsonObject data;
|
|
data["model"_L1] = request.model().isEmpty() ? m_models.constFirst() : request.model();
|
|
|
|
auto buf = new QBuffer{this};
|
|
buf->setData(QJsonDocument(data).toJson(QJsonDocument::Compact));
|
|
|
|
auto reply = new KLLMReply{m_manager->post(req, buf), this, KLLMReply::RequestTypes::Show};
|
|
connect(reply, &KLLMReply::finished, this, [this, reply, buf] {
|
|
Q_EMIT finished(reply->readResponse());
|
|
buf->deleteLater();
|
|
});
|
|
return reply;
|
|
}
|
|
|
|
void KLLMInterface::reload()
|
|
{
|
|
if (m_ollamaCheck)
|
|
disconnect(m_ollamaCheck);
|
|
|
|
QNetworkRequest req{QUrl::fromUserInput(m_ollamaUrl + QStringLiteral("/api/tags"))};
|
|
req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
|
|
auto rep = m_manager->get(req);
|
|
m_ollamaCheck = connect(rep, &QNetworkReply::finished, this, [this, rep] {
|
|
if (rep->error() != QNetworkReply::NoError) {
|
|
Q_EMIT errorOccurred(i18n("Failed to connect to interface at %1: %2", m_ollamaUrl, rep->errorString()));
|
|
m_hasError = true;
|
|
Q_EMIT readyChanged();
|
|
Q_EMIT hasErrorChanged();
|
|
return;
|
|
}
|
|
|
|
const auto json = QJsonDocument::fromJson(rep->readAll());
|
|
const auto models = json["models"_L1].toArray();
|
|
for (const QJsonValue &model : models) {
|
|
m_models.push_back(model["name"_L1].toString());
|
|
}
|
|
Q_EMIT modelsChanged();
|
|
|
|
m_ready = !m_models.isEmpty();
|
|
m_hasError = false;
|
|
Q_EMIT readyChanged();
|
|
Q_EMIT hasErrorChanged();
|
|
});
|
|
}
|
|
|
|
QString KLLMInterface::ollamaUrl() const
|
|
{
|
|
return m_ollamaUrl;
|
|
}
|
|
|
|
void KLLMInterface::setOllamaUrl(const QString &ollamaUrl)
|
|
{
|
|
if (m_ollamaUrl == ollamaUrl)
|
|
return;
|
|
m_ollamaUrl = ollamaUrl;
|
|
Q_EMIT ollamaUrlChanged();
|
|
reload();
|
|
}
|
|
|
|
void KLLMInterface::setOllamaUrl(const QUrl &ollamaUrl)
|
|
{
|
|
setOllamaUrl(ollamaUrl.toString());
|
|
}
|
|
|
|
QString KLLMInterface::systemPrompt() const
|
|
{
|
|
return m_systemPrompt;
|
|
}
|
|
|
|
void KLLMInterface::setSystemPrompt(const QString &systemPrompt)
|
|
{
|
|
if (m_systemPrompt == systemPrompt)
|
|
return;
|
|
m_systemPrompt = systemPrompt;
|
|
Q_EMIT systemPromptChanged();
|
|
}
|
|
|
|
#include "moc_KLLMInterface.cpp"
|