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 #pragma once
0007 
0008 #include "kllmcore_export.h"
0009 #include <QJsonDocument>
0010 
0011 #include "KLLMContext.h"
0012 
0013 class QNetworkReply;
0014 namespace KLLMCore
0015 {
0016 /**
0017  * @brief The KLLMReply class represents a reply from an LLM.
0018  *
0019  * Requesting a completion from a KLLMInterface will return a KLLMReply. You can use this to track the progress of the LLM's
0020  * reply.
0021  *
0022  * If you want to stream a reply as it is written in real time, connect to contentAdded() and use readResponse() to retrieve
0023  * the new content. If you prefer to wait for the entire reply before displaying anything, connect to finished(), which will
0024  * only be emitted once the reply is complete.
0025  */
0026 class KLLMCORE_EXPORT KLLMReply : public QObject
0027 {
0028     Q_OBJECT
0029 
0030 public:
0031     /**
0032      * @brief Get the current response content.
0033      *
0034      * This function returns what it has recieved of the response so far. Therefore, until finished() is emitted, this
0035      * function may return different values. However, once finished() is emitted, the content is guaranteed to remain
0036      * constant.
0037      *
0038      * @return The content that has been returned so far.
0039      */
0040     [[nodiscard]] QString readResponse() const;
0041 
0042     /**
0043      * @brief Get the context token for this response.
0044      *
0045      * Messages sent by most LLMs have a context identifier that allows you to chain messages into a conversation. To create
0046      * such a conversation, you need to take this context object and set it on the next KLLMRequest in the conversation.
0047      * KLLMInterface::getCompletion() will use that context object to continue the message thread.
0048      *
0049      * @return A context object that refers to this response.
0050      */
0051     const KLLMContext &context() const;
0052 
0053     /**
0054      * @brief Check whether the reply has finished.
0055      *
0056      * If you need to know if the response has finished changing or if the context has been received yet, call this function.
0057      *
0058      * @return Whether the reply has finished.
0059      */
0060     [[nodiscard]] bool isFinished() const;
0061 
0062 protected:
0063     explicit KLLMReply(QNetworkReply *netReply, QObject *parent = nullptr);
0064 
0065     friend class KLLMInterface;
0066 
0067 Q_SIGNALS:
0068     /**
0069      * @brief Emits when new content has been added to the response.
0070      *
0071      * If you are not streaming the response live, this signal is not of importance to you. However, if you are streaming
0072      * content, when this signal is emitted, you should call readResponse() to update the response that your application
0073      * shows.
0074      */
0075     void contentAdded();
0076 
0077     /**
0078      * @brief Emits when the LLM has finished returning its response.
0079      *
0080      * After this signal has emitted, the content is guaranteed to not change. At this point, you should call readResponse()
0081      * to get the content and then either take ownership of the KLLMReply or delete it, as automatic reply deletion is not
0082      * implemented yet.
0083      */
0084     void finished();
0085 
0086 private:
0087     QNetworkReply *const m_reply;
0088     QByteArray m_incompleteTokens;
0089 
0090     QList<QJsonDocument> m_tokens;
0091 
0092     KLLMContext m_context;
0093 
0094     int m_receivedSize = 0;
0095     bool m_finished = false;
0096 };
0097 }