// SPDX-FileCopyrightText: 2023 Loren Burkholder // SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company // // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL #include "KLLMInterface.h" #include #include #include #include #include #include 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"