File indexing completed on 2025-01-05 05:18:57
0001 // SPDX-FileCopyrightText: 2023 Loren Burkholder <computersemiexpert@outlook.com> 0002 // SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0003 // 0004 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 0006 #include "KLLMInterface.h" 0007 0008 #include <KLocalizedString> 0009 0010 #include <QBuffer> 0011 #include <QJsonDocument> 0012 #include <QJsonObject> 0013 #include <QNetworkAccessManager> 0014 #include <QNetworkReply> 0015 0016 using namespace Qt::StringLiterals; 0017 using namespace KLLMCore; 0018 0019 KLLMInterface::KLLMInterface(QObject *parent) 0020 : KLLMInterface{QString{}, parent} 0021 { 0022 } 0023 0024 KLLMInterface::KLLMInterface(const QString &ollamaUrl, QObject *parent) 0025 : QObject{parent} 0026 , m_manager{new QNetworkAccessManager{this}} 0027 , m_ollamaUrl{ollamaUrl} 0028 { 0029 if (!m_ollamaUrl.isEmpty()) 0030 reload(); 0031 } 0032 0033 KLLMInterface::KLLMInterface(const QUrl &ollamaUrl, QObject *parent) 0034 : KLLMInterface{ollamaUrl.toString(), parent} 0035 { 0036 } 0037 0038 bool KLLMInterface::ready() const 0039 { 0040 return m_ready && !m_hasError; 0041 } 0042 0043 bool KLLMInterface::hasError() const 0044 { 0045 return m_hasError; 0046 } 0047 0048 QStringList KLLMInterface::models() const 0049 { 0050 return m_models; 0051 } 0052 0053 #if 0 0054 void KLLMInterface::deleteModel(const QString &modelName) 0055 { 0056 Q_ASSERT(ready()); 0057 0058 QNetworkRequest req{QUrl::fromUserInput(m_ollamaUrl + QStringLiteral("/api/delete"))}; 0059 req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); 0060 QJsonObject data; 0061 data["name"_L1] = modelName; 0062 0063 // Delete resource doesn't take argument. Need to look at how to do it. 0064 auto buf = new QBuffer{this}; 0065 buf->setData(QJsonDocument(data).toJson(QJsonDocument::Compact)); 0066 0067 auto reply = new KLLMReply{m_manager->deleteResource(req, buf), this}; 0068 connect(reply, &KLLMReply::finished, this, [this, reply, buf] { 0069 Q_EMIT finished(reply->readResponse()); 0070 buf->deleteLater(); 0071 }); 0072 } 0073 #endif 0074 0075 KLLMReply *KLLMInterface::getCompletion(const KLLMRequest &request) 0076 { 0077 Q_ASSERT(ready()); 0078 0079 QNetworkRequest req{QUrl::fromUserInput(m_ollamaUrl + QStringLiteral("/api/generate"))}; 0080 req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); 0081 0082 QJsonObject data; 0083 data["model"_L1] = request.model().isEmpty() ? m_models.constFirst() : request.model(); 0084 data["prompt"_L1] = request.message(); 0085 0086 const auto context = request.context().toJson(); 0087 if (!context.isNull()) { 0088 data["context"_L1] = context; 0089 } 0090 0091 if (!m_systemPrompt.isEmpty()) { 0092 data["system"_L1] = m_systemPrompt; 0093 } 0094 0095 auto buf = new QBuffer{this}; 0096 buf->setData(QJsonDocument(data).toJson(QJsonDocument::Compact)); 0097 0098 auto reply = new KLLMReply{m_manager->post(req, buf), this}; 0099 connect(reply, &KLLMReply::finished, this, [this, reply, buf] { 0100 Q_EMIT finished(reply->readResponse()); 0101 buf->deleteLater(); 0102 }); 0103 return reply; 0104 } 0105 0106 void KLLMInterface::reload() 0107 { 0108 if (m_ollamaCheck) 0109 disconnect(m_ollamaCheck); 0110 0111 QNetworkRequest req{QUrl::fromUserInput(m_ollamaUrl + QStringLiteral("/api/tags"))}; 0112 req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); 0113 auto rep = m_manager->get(req); 0114 m_ollamaCheck = connect(rep, &QNetworkReply::finished, this, [this, rep] { 0115 if (rep->error() != QNetworkReply::NoError) { 0116 Q_EMIT errorOccurred(i18n("Failed to connect to interface at %1: %2", m_ollamaUrl, rep->errorString())); 0117 m_hasError = true; 0118 Q_EMIT readyChanged(); 0119 Q_EMIT hasErrorChanged(); 0120 return; 0121 } 0122 0123 const auto json = QJsonDocument::fromJson(rep->readAll()); 0124 const auto models = json["models"_L1].toArray(); 0125 for (const QJsonValue &model : models) { 0126 m_models.push_back(model["name"_L1].toString()); 0127 } 0128 Q_EMIT modelsChanged(); 0129 0130 m_ready = !m_models.isEmpty(); 0131 m_hasError = false; 0132 Q_EMIT readyChanged(); 0133 Q_EMIT hasErrorChanged(); 0134 }); 0135 } 0136 0137 QString KLLMInterface::ollamaUrl() const 0138 { 0139 return m_ollamaUrl; 0140 } 0141 0142 void KLLMInterface::setOllamaUrl(const QString &ollamaUrl) 0143 { 0144 if (m_ollamaUrl == ollamaUrl) 0145 return; 0146 m_ollamaUrl = ollamaUrl; 0147 Q_EMIT ollamaUrlChanged(); 0148 reload(); 0149 } 0150 0151 void KLLMInterface::setOllamaUrl(const QUrl &ollamaUrl) 0152 { 0153 setOllamaUrl(ollamaUrl.toString()); 0154 } 0155 0156 QString KLLMInterface::systemPrompt() const 0157 { 0158 return m_systemPrompt; 0159 } 0160 0161 void KLLMInterface::setSystemPrompt(const QString &systemPrompt) 0162 { 0163 if (m_systemPrompt == systemPrompt) 0164 return; 0165 m_systemPrompt = systemPrompt; 0166 Q_EMIT systemPromptChanged(); 0167 } 0168 0169 #include "moc_KLLMInterface.cpp"