mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2025-07-17 20:44:32 -04:00
Add basic chat widgets and functionality
This commit is contained in:
@ -32,10 +32,12 @@ add_qtc_plugin(QodeAssist
|
|||||||
LLMClientInterface.hpp LLMClientInterface.cpp
|
LLMClientInterface.hpp LLMClientInterface.cpp
|
||||||
PromptTemplateManager.hpp PromptTemplateManager.cpp
|
PromptTemplateManager.hpp PromptTemplateManager.cpp
|
||||||
templates/PromptTemplate.hpp
|
templates/PromptTemplate.hpp
|
||||||
templates/CodeLLamaTemplate.hpp
|
templates/CodeLlamaFimTemplate.hpp
|
||||||
templates/StarCoder2Template.hpp
|
templates/StarCoder2Template.hpp
|
||||||
templates/DeepSeekCoderV2.hpp
|
templates/DeepSeekCoderV2.hpp
|
||||||
templates/CustomTemplate.hpp
|
templates/CustomTemplate.hpp
|
||||||
|
templates/DeepSeekCoderChatTemplate.hpp
|
||||||
|
templates/CodeLlamaInstruct.hpp
|
||||||
providers/LLMProvider.hpp
|
providers/LLMProvider.hpp
|
||||||
providers/OllamaProvider.hpp providers/OllamaProvider.cpp
|
providers/OllamaProvider.hpp providers/OllamaProvider.cpp
|
||||||
providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp
|
providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp
|
||||||
@ -55,4 +57,9 @@ add_qtc_plugin(QodeAssist
|
|||||||
settings/PresetPromptsSettings.hpp settings/PresetPromptsSettings.cpp
|
settings/PresetPromptsSettings.hpp settings/PresetPromptsSettings.cpp
|
||||||
settings/SettingsUtils.hpp
|
settings/SettingsUtils.hpp
|
||||||
core/ChangesManager.h core/ChangesManager.cpp
|
core/ChangesManager.h core/ChangesManager.cpp
|
||||||
|
core/LLMRequestHandler.hpp core/LLMRequestHandler.cpp
|
||||||
|
core/LLMRequestConfig.hpp
|
||||||
|
chat/ChatWidget.h chat/ChatWidget.cpp
|
||||||
|
chat/ChatOutputPane.h chat/ChatOutputPane.cpp
|
||||||
|
chat/ChatClientInterface.hpp chat/ChatClientInterface.cpp
|
||||||
)
|
)
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
#include <languageserverprotocol/lsptypes.h>
|
#include <languageserverprotocol/lsptypes.h>
|
||||||
|
|
||||||
|
#include "core/ChangesManager.h"
|
||||||
#include "settings/ContextSettings.hpp"
|
#include "settings/ContextSettings.hpp"
|
||||||
|
|
||||||
const QRegularExpression &getYearRegex()
|
const QRegularExpression &getYearRegex()
|
||||||
@ -209,4 +210,56 @@ CopyrightInfo DocumentContextReader::copyrightInfo() const
|
|||||||
return m_copyrightInfo;
|
return m_copyrightInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContextData DocumentContextReader::prepareContext(int lineNumber, int cursorPosition) const
|
||||||
|
{
|
||||||
|
QString contextBefore = getContextBefore(lineNumber, cursorPosition);
|
||||||
|
QString contextAfter = getContextAfter(lineNumber, cursorPosition);
|
||||||
|
QString instructions = getInstructions();
|
||||||
|
|
||||||
|
return {contextBefore, contextAfter, instructions};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DocumentContextReader::getContextBefore(int lineNumber, int cursorPosition) const
|
||||||
|
{
|
||||||
|
if (Settings::contextSettings().readFullFile()) {
|
||||||
|
return readWholeFileBefore(lineNumber, cursorPosition);
|
||||||
|
} else {
|
||||||
|
int effectiveStartLine;
|
||||||
|
int beforeCursor = Settings::contextSettings().readStringsBeforeCursor();
|
||||||
|
if (m_copyrightInfo.found) {
|
||||||
|
effectiveStartLine = qMax(m_copyrightInfo.endLine + 1, lineNumber - beforeCursor);
|
||||||
|
} else {
|
||||||
|
effectiveStartLine = qMax(0, lineNumber - beforeCursor);
|
||||||
|
}
|
||||||
|
return getContextBetween(effectiveStartLine, lineNumber, cursorPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DocumentContextReader::getContextAfter(int lineNumber, int cursorPosition) const
|
||||||
|
{
|
||||||
|
if (Settings::contextSettings().readFullFile()) {
|
||||||
|
return readWholeFileAfter(lineNumber, cursorPosition);
|
||||||
|
} else {
|
||||||
|
int endLine = qMin(m_document->blockCount() - 1,
|
||||||
|
lineNumber + Settings::contextSettings().readStringsAfterCursor());
|
||||||
|
return getContextBetween(lineNumber + 1, endLine, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DocumentContextReader::getInstructions() const
|
||||||
|
{
|
||||||
|
QString instructions;
|
||||||
|
|
||||||
|
if (Settings::contextSettings().useSpecificInstructions())
|
||||||
|
instructions += getSpecificInstructions();
|
||||||
|
|
||||||
|
if (Settings::contextSettings().useFilePathInContext())
|
||||||
|
instructions += getLanguageAndFileInfo();
|
||||||
|
|
||||||
|
if (Settings::contextSettings().useProjectChangesCache())
|
||||||
|
instructions += ChangesManager::instance().getRecentChangesContext(m_textDocument);
|
||||||
|
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <texteditor/textdocument.h>
|
#include <texteditor/textdocument.h>
|
||||||
|
|
||||||
|
#include "QodeAssistData.hpp"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
struct CopyrightInfo
|
struct CopyrightInfo
|
||||||
@ -48,6 +50,13 @@ public:
|
|||||||
|
|
||||||
CopyrightInfo copyrightInfo() const;
|
CopyrightInfo copyrightInfo() const;
|
||||||
|
|
||||||
|
ContextData prepareContext(int lineNumber, int cursorPosition) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString getContextBefore(int lineNumber, int cursorPosition) const;
|
||||||
|
QString getContextAfter(int lineNumber, int cursorPosition) const;
|
||||||
|
QString getInstructions() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TextEditor::TextDocument *m_textDocument;
|
TextEditor::TextDocument *m_textDocument;
|
||||||
QTextDocument *m_document;
|
QTextDocument *m_document;
|
||||||
|
@ -29,16 +29,18 @@
|
|||||||
#include "LLMProvidersManager.hpp"
|
#include "LLMProvidersManager.hpp"
|
||||||
#include "PromptTemplateManager.hpp"
|
#include "PromptTemplateManager.hpp"
|
||||||
#include "QodeAssistUtils.hpp"
|
#include "QodeAssistUtils.hpp"
|
||||||
#include "core/ChangesManager.h"
|
#include "core/LLMRequestConfig.hpp"
|
||||||
#include "settings/ContextSettings.hpp"
|
|
||||||
#include "settings/GeneralSettings.hpp"
|
#include "settings/GeneralSettings.hpp"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
LLMClientInterface::LLMClientInterface()
|
LLMClientInterface::LLMClientInterface()
|
||||||
: m_manager(new QNetworkAccessManager(this))
|
: m_requestHandler(this)
|
||||||
{
|
{
|
||||||
updateProvider();
|
connect(&m_requestHandler,
|
||||||
|
&LLMRequestHandler::completionReceived,
|
||||||
|
this,
|
||||||
|
&LLMClientInterface::sendCompletionToClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::FilePath LLMClientInterface::serverDeviceTemplate() const
|
Utils::FilePath LLMClientInterface::serverDeviceTemplate() const
|
||||||
@ -53,8 +55,6 @@ void LLMClientInterface::startImpl()
|
|||||||
|
|
||||||
void LLMClientInterface::sendData(const QByteArray &data)
|
void LLMClientInterface::sendData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
updateProvider();
|
|
||||||
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||||
if (!doc.isObject())
|
if (!doc.isObject())
|
||||||
return;
|
return;
|
||||||
@ -86,87 +86,13 @@ void LLMClientInterface::sendData(const QByteArray &data)
|
|||||||
void LLMClientInterface::handleCancelRequest(const QJsonObject &request)
|
void LLMClientInterface::handleCancelRequest(const QJsonObject &request)
|
||||||
{
|
{
|
||||||
QString id = request["params"].toObject()["id"].toString();
|
QString id = request["params"].toObject()["id"].toString();
|
||||||
if (m_activeRequests.contains(id)) {
|
if (m_requestHandler.cancelRequest(id)) {
|
||||||
m_activeRequests[id]->abort();
|
|
||||||
m_activeRequests.remove(id);
|
|
||||||
logMessage(QString("Request %1 cancelled successfully").arg(id));
|
logMessage(QString("Request %1 cancelled successfully").arg(id));
|
||||||
} else {
|
} else {
|
||||||
logMessage(QString("Request %1 not found").arg(id));
|
logMessage(QString("Request %1 not found").arg(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLMClientInterface::processSingleLineCompletion(QNetworkReply *reply,
|
|
||||||
const QJsonObject &request,
|
|
||||||
const QString &accumulatedCompletion)
|
|
||||||
{
|
|
||||||
int newlinePos = accumulatedCompletion.indexOf('\n');
|
|
||||||
|
|
||||||
if (newlinePos != -1) {
|
|
||||||
QString singleLineCompletion = accumulatedCompletion.left(newlinePos).trimmed();
|
|
||||||
singleLineCompletion = removeStopWords(singleLineCompletion);
|
|
||||||
|
|
||||||
QJsonObject position = request["params"].toObject()["doc"].toObject()["position"].toObject();
|
|
||||||
|
|
||||||
sendCompletionToClient(singleLineCompletion, request, position, true);
|
|
||||||
m_accumulatedResponses.remove(reply);
|
|
||||||
reply->abort();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LLMClientInterface::сontextBefore(TextEditor::TextEditorWidget *widget,
|
|
||||||
int lineNumber,
|
|
||||||
int cursorPosition)
|
|
||||||
{
|
|
||||||
if (!widget)
|
|
||||||
return QString();
|
|
||||||
|
|
||||||
DocumentContextReader reader(widget->textDocument());
|
|
||||||
const auto ©right = reader.copyrightInfo();
|
|
||||||
|
|
||||||
logMessage(QString{"Line Number: %1"}.arg(lineNumber));
|
|
||||||
logMessage(QString("Copyright found %1 %2").arg(copyright.found).arg(copyright.endLine));
|
|
||||||
if (lineNumber < reader.findCopyright().endLine)
|
|
||||||
return QString();
|
|
||||||
|
|
||||||
QString contextBefore;
|
|
||||||
if (Settings::contextSettings().readFullFile()) {
|
|
||||||
contextBefore = reader.readWholeFileBefore(lineNumber, cursorPosition);
|
|
||||||
} else {
|
|
||||||
contextBefore
|
|
||||||
= reader.getContextBefore(lineNumber,
|
|
||||||
cursorPosition,
|
|
||||||
Settings::contextSettings().readStringsBeforeCursor());
|
|
||||||
}
|
|
||||||
|
|
||||||
return contextBefore;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LLMClientInterface::сontextAfter(TextEditor::TextEditorWidget *widget,
|
|
||||||
int lineNumber,
|
|
||||||
int cursorPosition)
|
|
||||||
{
|
|
||||||
if (!widget)
|
|
||||||
return QString();
|
|
||||||
|
|
||||||
DocumentContextReader reader(widget->textDocument());
|
|
||||||
if (lineNumber < reader.findCopyright().endLine)
|
|
||||||
return QString();
|
|
||||||
|
|
||||||
QString contextAfter;
|
|
||||||
if (Settings::contextSettings().readFullFile()) {
|
|
||||||
contextAfter = reader.readWholeFileAfter(lineNumber, cursorPosition);
|
|
||||||
} else {
|
|
||||||
contextAfter = reader.getContextAfter(lineNumber,
|
|
||||||
cursorPosition,
|
|
||||||
Settings::contextSettings().readStringsAfterCursor());
|
|
||||||
}
|
|
||||||
|
|
||||||
return contextAfter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLMClientInterface::handleInitialize(const QJsonObject &request)
|
void LLMClientInterface::handleInitialize(const QJsonObject &request)
|
||||||
{
|
{
|
||||||
QJsonObject response;
|
QJsonObject response;
|
||||||
@ -217,40 +143,26 @@ void LLMClientInterface::handleExit(const QJsonObject &request)
|
|||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::handleLLMResponse(QNetworkReply *reply, const QJsonObject &request)
|
void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[reply];
|
auto updatedContext = prepareContext(request);
|
||||||
|
|
||||||
auto &templateManager = PromptTemplateManager::instance();
|
LLMConfig config;
|
||||||
const Templates::PromptTemplate *currentTemplate = templateManager.getCurrentTemplate();
|
config.requestType = RequestType::Fim;
|
||||||
|
config.provider = LLMProvidersManager::instance().getCurrentFimProvider();
|
||||||
|
config.promptTemplate = PromptTemplateManager::instance().getCurrentFimTemplate();
|
||||||
|
config.url = QUrl(QString("%1%2").arg(Settings::generalSettings().url(),
|
||||||
|
Settings::generalSettings().endPoint()));
|
||||||
|
|
||||||
auto &providerManager = LLMProvidersManager::instance();
|
config.providerRequest = {{"model", Settings::generalSettings().modelName.value()},
|
||||||
bool isComplete = providerManager.getCurrentProvider()->handleResponse(reply,
|
{"stream", true},
|
||||||
accumulatedResponse);
|
{"stop",
|
||||||
|
QJsonArray::fromStringList(config.promptTemplate->stopWords())}};
|
||||||
|
|
||||||
QJsonObject position = request["params"].toObject()["doc"].toObject()["position"].toObject();
|
config.promptTemplate->prepareRequest(config.providerRequest, updatedContext);
|
||||||
|
config.provider->prepareRequest(config.providerRequest);
|
||||||
|
|
||||||
if (!Settings::generalSettings().multiLineCompletion()
|
m_requestHandler.sendLLMRequest(config, request);
|
||||||
&& processSingleLineCompletion(reply, request, accumulatedResponse)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isComplete || reply->isFinished()) {
|
|
||||||
if (isComplete) {
|
|
||||||
auto cleanedCompletion = removeStopWords(accumulatedResponse);
|
|
||||||
sendCompletionToClient(cleanedCompletion, request, position, true);
|
|
||||||
} else {
|
|
||||||
handleCompletion(request, accumulatedResponse);
|
|
||||||
}
|
|
||||||
m_accumulatedResponses.remove(reply);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLMClientInterface::handleCompletion(const QJsonObject &request,
|
|
||||||
const QStringView &accumulatedCompletion)
|
|
||||||
{
|
|
||||||
auto updatedContext = prepareContext(request, accumulatedCompletion);
|
|
||||||
sendLLMRequest(request, updatedContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextData LLMClientInterface::prepareContext(const QJsonObject &request,
|
ContextData LLMClientInterface::prepareContext(const QJsonObject &request,
|
||||||
@ -273,39 +185,16 @@ ContextData LLMClientInterface::prepareContext(const QJsonObject &request,
|
|||||||
int cursorPosition = position["character"].toInt();
|
int cursorPosition = position["character"].toInt();
|
||||||
int lineNumber = position["line"].toInt();
|
int lineNumber = position["line"].toInt();
|
||||||
|
|
||||||
auto textEditor = TextEditor::BaseTextEditor::currentTextEditor();
|
DocumentContextReader reader(textDocument);
|
||||||
TextEditor::TextEditorWidget *widget = textEditor->editorWidget();
|
return reader.prepareContext(lineNumber, cursorPosition);
|
||||||
|
|
||||||
DocumentContextReader reader(widget->textDocument());
|
|
||||||
|
|
||||||
QString recentChanges = ChangesManager::instance().getRecentChangesContext(textDocument);
|
|
||||||
|
|
||||||
QString contextBefore = сontextBefore(widget, lineNumber, cursorPosition);
|
|
||||||
QString contextAfter = сontextAfter(widget, lineNumber, cursorPosition);
|
|
||||||
QString instructions
|
|
||||||
= QString("%1%2%3").arg(Settings::contextSettings().useSpecificInstructions()
|
|
||||||
? reader.getSpecificInstructions()
|
|
||||||
: QString(),
|
|
||||||
Settings::contextSettings().useFilePathInContext()
|
|
||||||
? reader.getLanguageAndFileInfo()
|
|
||||||
: QString(),
|
|
||||||
Settings::contextSettings().useProjectChangesCache() ? recentChanges
|
|
||||||
: QString());
|
|
||||||
|
|
||||||
return {QString("%1%2").arg(contextBefore, accumulatedCompletion), contextAfter, instructions};
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLMClientInterface::updateProvider()
|
|
||||||
{
|
|
||||||
m_serverUrl = QUrl(QString("%1%2").arg(Settings::generalSettings().url(),
|
|
||||||
Settings::generalSettings().endPoint()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::sendCompletionToClient(const QString &completion,
|
void LLMClientInterface::sendCompletionToClient(const QString &completion,
|
||||||
const QJsonObject &request,
|
const QJsonObject &request,
|
||||||
const QJsonObject &position,
|
|
||||||
bool isComplete)
|
bool isComplete)
|
||||||
{
|
{
|
||||||
|
QJsonObject position = request["params"].toObject()["doc"].toObject()["position"].toObject();
|
||||||
|
|
||||||
QJsonObject response;
|
QJsonObject response;
|
||||||
response["jsonrpc"] = "2.0";
|
response["jsonrpc"] = "2.0";
|
||||||
response[LanguageServerProtocol::idKey] = request["id"];
|
response[LanguageServerProtocol::idKey] = request["id"];
|
||||||
@ -337,69 +226,6 @@ void LLMClientInterface::sendCompletionToClient(const QString &completion,
|
|||||||
emit messageReceived(LanguageServerProtocol::JsonRpcMessage(response));
|
emit messageReceived(LanguageServerProtocol::JsonRpcMessage(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::sendLLMRequest(const QJsonObject &request, const ContextData &prompt)
|
|
||||||
{
|
|
||||||
QJsonObject providerRequest = {{"model", Settings::generalSettings().modelName.value()},
|
|
||||||
{"stream", true}};
|
|
||||||
|
|
||||||
auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
|
||||||
currentTemplate->prepareRequest(providerRequest, prompt);
|
|
||||||
|
|
||||||
auto &providerManager = LLMProvidersManager::instance();
|
|
||||||
providerManager.getCurrentProvider()->prepareRequest(providerRequest);
|
|
||||||
|
|
||||||
logMessage(QString("Sending request to llm: \nurl: %1\nRequest body:\n%2")
|
|
||||||
.arg(m_serverUrl.toString(),
|
|
||||||
QString::fromUtf8(
|
|
||||||
QJsonDocument(providerRequest).toJson(QJsonDocument::Indented))));
|
|
||||||
|
|
||||||
QNetworkRequest networkRequest(m_serverUrl);
|
|
||||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
||||||
|
|
||||||
if (providerRequest.contains("api_key")) {
|
|
||||||
QString apiKey = providerRequest["api_key"].toString();
|
|
||||||
networkRequest.setRawHeader("Authorization", QString("Bearer %1").arg(apiKey).toUtf8());
|
|
||||||
providerRequest.remove("api_key");
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply *reply = m_manager->post(networkRequest, QJsonDocument(providerRequest).toJson());
|
|
||||||
if (!reply) {
|
|
||||||
logMessage("Error: Failed to create network reply");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString requestId = request["id"].toString();
|
|
||||||
m_activeRequests[requestId] = reply;
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::readyRead, this, [this, reply, request]() {
|
|
||||||
handleLLMResponse(reply, request);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId]() {
|
|
||||||
reply->deleteLater();
|
|
||||||
m_activeRequests.remove(requestId);
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
|
||||||
logMessage(QString("Error in QodeAssist request: %1").arg(reply->errorString()));
|
|
||||||
} else {
|
|
||||||
logMessage("Request finished successfully");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LLMClientInterface::removeStopWords(const QStringView &completion)
|
|
||||||
{
|
|
||||||
QString filteredCompletion = completion.toString();
|
|
||||||
|
|
||||||
auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
|
||||||
QStringList stopWords = currentTemplate->stopWords();
|
|
||||||
|
|
||||||
for (const QString &stopWord : stopWords) {
|
|
||||||
filteredCompletion = filteredCompletion.replace(stopWord, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
return filteredCompletion;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLMClientInterface::startTimeMeasurement(const QString &requestId)
|
void LLMClientInterface::startTimeMeasurement(const QString &requestId)
|
||||||
{
|
{
|
||||||
m_requestStartTimes[requestId] = QDateTime::currentMSecsSinceEpoch();
|
m_requestStartTimes[requestId] = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
#include "QodeAssistData.hpp"
|
#include "QodeAssistData.hpp"
|
||||||
|
#include "core/LLMRequestHandler.hpp"
|
||||||
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
@ -36,22 +37,13 @@ class LLMClientInterface : public LanguageClient::BaseClientInterface
|
|||||||
public:
|
public:
|
||||||
LLMClientInterface();
|
LLMClientInterface();
|
||||||
|
|
||||||
public:
|
|
||||||
Utils::FilePath serverDeviceTemplate() const override;
|
Utils::FilePath serverDeviceTemplate() const override;
|
||||||
|
|
||||||
void sendCompletionToClient(const QString &completion,
|
void sendCompletionToClient(const QString &completion,
|
||||||
const QJsonObject &request,
|
const QJsonObject &request,
|
||||||
const QJsonObject &position,
|
|
||||||
bool isComplete);
|
bool isComplete);
|
||||||
|
|
||||||
void handleCompletion(const QJsonObject &request,
|
void handleCompletion(const QJsonObject &request);
|
||||||
const QStringView &accumulatedCompletion = QString());
|
|
||||||
void sendLLMRequest(const QJsonObject &request, const ContextData &prompt);
|
|
||||||
void handleLLMResponse(QNetworkReply *reply, const QJsonObject &request);
|
|
||||||
|
|
||||||
ContextData prepareContext(const QJsonObject &request,
|
|
||||||
const QStringView &accumulatedCompletion = QString{});
|
|
||||||
void updateProvider();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void startImpl() override;
|
void startImpl() override;
|
||||||
@ -65,19 +57,11 @@ private:
|
|||||||
void handleInitialized(const QJsonObject &request);
|
void handleInitialized(const QJsonObject &request);
|
||||||
void handleExit(const QJsonObject &request);
|
void handleExit(const QJsonObject &request);
|
||||||
void handleCancelRequest(const QJsonObject &request);
|
void handleCancelRequest(const QJsonObject &request);
|
||||||
bool processSingleLineCompletion(QNetworkReply *reply,
|
|
||||||
const QJsonObject &request,
|
|
||||||
const QString &accumulatedCompletion);
|
|
||||||
|
|
||||||
QString сontextBefore(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition);
|
ContextData prepareContext(const QJsonObject &request,
|
||||||
QString сontextAfter(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition);
|
const QStringView &accumulatedCompletion = QString{});
|
||||||
QString removeStopWords(const QStringView &completion);
|
|
||||||
|
|
||||||
QUrl m_serverUrl;
|
|
||||||
QNetworkAccessManager *m_manager;
|
|
||||||
QMap<QString, QNetworkReply *> m_activeRequests;
|
|
||||||
QMap<QNetworkReply *, QString> m_accumulatedResponses;
|
|
||||||
|
|
||||||
|
LLMRequestHandler m_requestHandler;
|
||||||
QElapsedTimer m_completionTimer;
|
QElapsedTimer m_completionTimer;
|
||||||
QMap<QString, qint64> m_requestStartTimes;
|
QMap<QString, qint64> m_requestStartTimes;
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#include "LLMProvidersManager.hpp"
|
#include "LLMProvidersManager.hpp"
|
||||||
|
|
||||||
|
#include "QodeAssistUtils.hpp"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
LLMProvidersManager &LLMProvidersManager::instance()
|
LLMProvidersManager &LLMProvidersManager::instance()
|
||||||
@ -27,25 +29,53 @@ LLMProvidersManager &LLMProvidersManager::instance()
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList LLMProvidersManager::getProviderNames() const
|
Providers::LLMProvider *LLMProvidersManager::setCurrentFimProvider(const QString &name)
|
||||||
{
|
{
|
||||||
return m_providers.keys();
|
logMessage("Setting current FIM provider to: " + name);
|
||||||
}
|
if (!m_providers.contains(name)) {
|
||||||
|
logMessage("Can't find provider with name: " + name);
|
||||||
void LLMProvidersManager::setCurrentProvider(const QString &name)
|
|
||||||
{
|
|
||||||
if (m_providers.contains(name)) {
|
|
||||||
m_currentProviderName = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Providers::LLMProvider *LLMProvidersManager::getCurrentProvider()
|
|
||||||
{
|
|
||||||
if (m_currentProviderName.isEmpty()) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_providers[m_currentProviderName];
|
m_currentFimProvider = m_providers[name];
|
||||||
|
return m_currentFimProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
Providers::LLMProvider *LLMProvidersManager::setCurrentChatProvider(const QString &name)
|
||||||
|
{
|
||||||
|
logMessage("Setting current chat provider to: " + name);
|
||||||
|
if (!m_providers.contains(name)) {
|
||||||
|
logMessage("Can't find chat provider with name: " + name);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currentChatProvider = m_providers[name];
|
||||||
|
return m_currentChatProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
Providers::LLMProvider *LLMProvidersManager::getCurrentFimProvider()
|
||||||
|
{
|
||||||
|
if (m_currentFimProvider == nullptr) {
|
||||||
|
logMessage("Current fim provider is null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_currentFimProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
Providers::LLMProvider *LLMProvidersManager::getCurrentChatProvider()
|
||||||
|
{
|
||||||
|
if (m_currentChatProvider == nullptr) {
|
||||||
|
logMessage("Current chat provider is null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_currentChatProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList LLMProvidersManager::providersNames() const
|
||||||
|
{
|
||||||
|
return m_providers.keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
LLMProvidersManager::~LLMProvidersManager()
|
LLMProvidersManager::~LLMProvidersManager()
|
||||||
|
@ -29,6 +29,7 @@ class LLMProvidersManager
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static LLMProvidersManager &instance();
|
static LLMProvidersManager &instance();
|
||||||
|
~LLMProvidersManager();
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void registerProvider()
|
void registerProvider()
|
||||||
@ -40,11 +41,13 @@ public:
|
|||||||
m_providers[name] = provider;
|
m_providers[name] = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList getProviderNames() const;
|
Providers::LLMProvider *setCurrentFimProvider(const QString &name);
|
||||||
void setCurrentProvider(const QString &name);
|
Providers::LLMProvider *setCurrentChatProvider(const QString &name);
|
||||||
Providers::LLMProvider *getCurrentProvider();
|
|
||||||
|
|
||||||
~LLMProvidersManager();
|
Providers::LLMProvider *getCurrentFimProvider();
|
||||||
|
Providers::LLMProvider *getCurrentChatProvider();
|
||||||
|
|
||||||
|
QStringList providersNames() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LLMProvidersManager() = default;
|
LLMProvidersManager() = default;
|
||||||
@ -52,7 +55,8 @@ private:
|
|||||||
LLMProvidersManager &operator=(const LLMProvidersManager &) = delete;
|
LLMProvidersManager &operator=(const LLMProvidersManager &) = delete;
|
||||||
|
|
||||||
QMap<QString, Providers::LLMProvider *> m_providers;
|
QMap<QString, Providers::LLMProvider *> m_providers;
|
||||||
QString m_currentProviderName;
|
Providers::LLMProvider *m_currentFimProvider = nullptr;
|
||||||
|
Providers::LLMProvider *m_currentChatProvider = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#include "PromptTemplateManager.hpp"
|
#include "PromptTemplateManager.hpp"
|
||||||
|
|
||||||
|
#include "QodeAssistUtils.hpp"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
PromptTemplateManager &PromptTemplateManager::instance()
|
PromptTemplateManager &PromptTemplateManager::instance()
|
||||||
@ -27,27 +29,60 @@ PromptTemplateManager &PromptTemplateManager::instance()
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PromptTemplateManager::setCurrentTemplate(const QString &name)
|
void PromptTemplateManager::setCurrentFimTemplate(const QString &name)
|
||||||
{
|
{
|
||||||
if (m_templates.contains(name)) {
|
logMessage("Setting current FIM provider to: " + name);
|
||||||
m_currentTemplateName = name;
|
if (!m_fimTemplates.contains(name) || m_fimTemplates[name] == nullptr) {
|
||||||
}
|
logMessage("Error to set current FIM template" + name);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Templates::PromptTemplate *PromptTemplateManager::getCurrentTemplate() const
|
m_currentFimTemplate = m_fimTemplates[name];
|
||||||
{
|
|
||||||
auto it = m_templates.find(m_currentTemplateName);
|
|
||||||
return it != m_templates.end() ? it.value() : nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList PromptTemplateManager::getTemplateNames() const
|
Templates::PromptTemplate *PromptTemplateManager::getCurrentFimTemplate()
|
||||||
{
|
{
|
||||||
return m_templates.keys();
|
if (m_currentFimTemplate == nullptr) {
|
||||||
|
logMessage("Current fim provider is null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_currentFimTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PromptTemplateManager::setCurrentChatTemplate(const QString &name)
|
||||||
|
{
|
||||||
|
logMessage("Setting current chat provider to: " + name);
|
||||||
|
if (!m_chatTemplates.contains(name) || m_chatTemplates[name] == nullptr) {
|
||||||
|
logMessage("Error to set current chat template" + name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currentChatTemplate = m_chatTemplates[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
Templates::PromptTemplate *PromptTemplateManager::getCurrentChatTemplate()
|
||||||
|
{
|
||||||
|
if (m_currentChatTemplate == nullptr)
|
||||||
|
logMessage("Current chat provider is null");
|
||||||
|
|
||||||
|
return m_currentChatTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList PromptTemplateManager::fimTemplatesNames() const
|
||||||
|
{
|
||||||
|
return m_fimTemplates.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList PromptTemplateManager::chatTemplatesNames() const
|
||||||
|
{
|
||||||
|
return m_chatTemplates.keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
PromptTemplateManager::~PromptTemplateManager()
|
PromptTemplateManager::~PromptTemplateManager()
|
||||||
{
|
{
|
||||||
qDeleteAll(m_templates);
|
qDeleteAll(m_fimTemplates);
|
||||||
|
qDeleteAll(m_chatTemplates);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
@ -30,6 +30,7 @@ class PromptTemplateManager
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static PromptTemplateManager &instance();
|
static PromptTemplateManager &instance();
|
||||||
|
~PromptTemplateManager();
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void registerTemplate()
|
void registerTemplate()
|
||||||
@ -38,22 +39,31 @@ public:
|
|||||||
"T must inherit from PromptTemplate");
|
"T must inherit from PromptTemplate");
|
||||||
T *template_ptr = new T();
|
T *template_ptr = new T();
|
||||||
QString name = template_ptr->name();
|
QString name = template_ptr->name();
|
||||||
m_templates[name] = template_ptr;
|
if (template_ptr->type() == Templates::TemplateType::Fim) {
|
||||||
|
m_fimTemplates[name] = template_ptr;
|
||||||
|
} else if (template_ptr->type() == Templates::TemplateType::Chat) {
|
||||||
|
m_chatTemplates[name] = template_ptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCurrentTemplate(const QString &name);
|
void setCurrentFimTemplate(const QString &name);
|
||||||
const Templates::PromptTemplate *getCurrentTemplate() const;
|
Templates::PromptTemplate *getCurrentFimTemplate();
|
||||||
QStringList getTemplateNames() const;
|
|
||||||
|
|
||||||
~PromptTemplateManager();
|
void setCurrentChatTemplate(const QString &name);
|
||||||
|
Templates::PromptTemplate *getCurrentChatTemplate();
|
||||||
|
|
||||||
|
QStringList fimTemplatesNames() const;
|
||||||
|
QStringList chatTemplatesNames() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PromptTemplateManager() = default;
|
PromptTemplateManager() = default;
|
||||||
PromptTemplateManager(const PromptTemplateManager &) = delete;
|
PromptTemplateManager(const PromptTemplateManager &) = delete;
|
||||||
PromptTemplateManager &operator=(const PromptTemplateManager &) = delete;
|
PromptTemplateManager &operator=(const PromptTemplateManager &) = delete;
|
||||||
|
|
||||||
QMap<QString, Templates::PromptTemplate *> m_templates;
|
QMap<QString, Templates::PromptTemplate *> m_fimTemplates;
|
||||||
QString m_currentTemplateName;
|
QMap<QString, Templates::PromptTemplate *> m_chatTemplates;
|
||||||
|
Templates::PromptTemplate *m_currentFimTemplate;
|
||||||
|
Templates::PromptTemplate *m_currentChatTemplate;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
@ -61,6 +61,12 @@ const char USE_FILE_PATH_IN_CONTEXT[] = "QodeAssist.useFilePathInContext";
|
|||||||
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
|
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
|
||||||
const char USE_PROJECT_CHANGES_CACHE[] = "QodeAssist.useProjectChangesCache";
|
const char USE_PROJECT_CHANGES_CACHE[] = "QodeAssist.useProjectChangesCache";
|
||||||
const char MAX_CHANGES_CACHE_SIZE[] = "QodeAssist.maxChangesCacheSize";
|
const char MAX_CHANGES_CACHE_SIZE[] = "QodeAssist.maxChangesCacheSize";
|
||||||
|
const char CHAT_LLM_PROVIDERS[] = "QodeAssist.chatLlmProviders";
|
||||||
|
const char CHAT_URL[] = "QodeAssist.chatUrl";
|
||||||
|
const char CHAT_END_POINT[] = "QodeAssist.chatEndPoint";
|
||||||
|
const char CHAT_MODEL_NAME[] = "QodeAssist.chatModelName";
|
||||||
|
const char CHAT_SELECT_MODELS[] = "QodeAssist.chatSelectModels";
|
||||||
|
const char CHAT_PROMPTS[] = "QodeAssist.chatPrompts";
|
||||||
|
|
||||||
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
|
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
|
||||||
const char QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID[] = "QodeAssist.1GeneralSettingsPageId";
|
const char QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID[] = "QodeAssist.1GeneralSettingsPageId";
|
||||||
|
@ -47,6 +47,7 @@ inline void logMessage(const QString &message, bool silent = true)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const QString prefixedMessage = QLatin1String("[Qode Assist] ") + message;
|
const QString prefixedMessage = QLatin1String("[Qode Assist] ") + message;
|
||||||
|
qDebug() << prefixedMessage;
|
||||||
if (silent) {
|
if (silent) {
|
||||||
Core::MessageManager::writeSilently(prefixedMessage);
|
Core::MessageManager::writeSilently(prefixedMessage);
|
||||||
} else {
|
} else {
|
||||||
@ -60,6 +61,8 @@ inline void logMessages(const QStringList &messages, bool silent = true)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
QStringList prefixedMessages;
|
QStringList prefixedMessages;
|
||||||
|
qDebug() << prefixedMessages;
|
||||||
|
|
||||||
for (const QString &message : messages) {
|
for (const QString &message : messages) {
|
||||||
prefixedMessages << (QLatin1String("[Qode Assist] ") + message);
|
prefixedMessages << (QLatin1String("[Qode Assist] ") + message);
|
||||||
}
|
}
|
||||||
|
153
chat/ChatClientInterface.cpp
Normal file
153
chat/ChatClientInterface.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ChatClientInterface.hpp"
|
||||||
|
#include "LLMProvidersManager.hpp"
|
||||||
|
#include "PromptTemplateManager.hpp"
|
||||||
|
#include "QodeAssistUtils.hpp"
|
||||||
|
#include "settings/ContextSettings.hpp"
|
||||||
|
#include "settings/GeneralSettings.hpp"
|
||||||
|
#include "settings/PresetPromptsSettings.hpp"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
ChatClientInterface::ChatClientInterface(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_requestHandler(new LLMRequestHandler(this))
|
||||||
|
{
|
||||||
|
connect(m_requestHandler,
|
||||||
|
&LLMRequestHandler::completionReceived,
|
||||||
|
this,
|
||||||
|
[this](const QString &completion, const QJsonObject &, bool isComplete) {
|
||||||
|
handleLLMResponse(completion, isComplete);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_requestHandler,
|
||||||
|
&LLMRequestHandler::requestFinished,
|
||||||
|
this,
|
||||||
|
[this](const QString &, bool success, const QString &errorString) {
|
||||||
|
if (!success) {
|
||||||
|
emit errorOccurred(errorString);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// QJsonObject systemMessage;
|
||||||
|
// systemMessage["role"] = "system";
|
||||||
|
// systemMessage["content"] = "You are a helpful C++ and QML programming assistant.";
|
||||||
|
// m_chatHistory.append(systemMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatClientInterface::~ChatClientInterface()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatClientInterface::sendMessage(const QString &message)
|
||||||
|
{
|
||||||
|
logMessage("Sending message: " + message);
|
||||||
|
logMessage("chatProvider " + Settings::generalSettings().chatLlmProviders.stringValue());
|
||||||
|
logMessage("chatTemplate " + Settings::generalSettings().chatPrompts.stringValue());
|
||||||
|
|
||||||
|
auto chatTemplate = PromptTemplateManager::instance().getCurrentChatTemplate();
|
||||||
|
auto chatProvider = LLMProvidersManager::instance().getCurrentChatProvider();
|
||||||
|
|
||||||
|
ContextData context;
|
||||||
|
context.prefix = message;
|
||||||
|
context.suffix = "";
|
||||||
|
if (Settings::contextSettings().useSpecificInstructions())
|
||||||
|
context.instriuctions = Settings::contextSettings().specificInstractions();
|
||||||
|
|
||||||
|
QJsonObject providerRequest;
|
||||||
|
providerRequest["model"] = Settings::generalSettings().chatModelName();
|
||||||
|
providerRequest["stream"] = true;
|
||||||
|
|
||||||
|
providerRequest["messages"] = m_chatHistory;
|
||||||
|
|
||||||
|
chatTemplate->prepareRequest(providerRequest, context);
|
||||||
|
chatProvider->prepareRequest(providerRequest);
|
||||||
|
|
||||||
|
m_chatHistory = providerRequest["messages"].toArray();
|
||||||
|
|
||||||
|
LLMConfig config;
|
||||||
|
config.requestType = RequestType::Chat;
|
||||||
|
config.provider = chatProvider;
|
||||||
|
config.promptTemplate = chatTemplate;
|
||||||
|
config.url = QString("%1%2").arg(Settings::generalSettings().chatUrl(),
|
||||||
|
Settings::generalSettings().chatEndPoint());
|
||||||
|
config.providerRequest = providerRequest;
|
||||||
|
|
||||||
|
QJsonObject request;
|
||||||
|
request["id"] = QUuid::createUuid().toString();
|
||||||
|
|
||||||
|
m_accumulatedResponse.clear();
|
||||||
|
m_pendingMessage = message;
|
||||||
|
m_requestHandler->sendLLMRequest(config, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatClientInterface::handleLLMResponse(const QString &response, bool isComplete)
|
||||||
|
{
|
||||||
|
m_accumulatedResponse += response;
|
||||||
|
logMessage("Accumulated response: " + m_accumulatedResponse);
|
||||||
|
|
||||||
|
if (isComplete) {
|
||||||
|
logMessage("Message completed. Final response: " + m_accumulatedResponse);
|
||||||
|
emit messageReceived(m_accumulatedResponse.trimmed());
|
||||||
|
|
||||||
|
QJsonObject assistantMessage;
|
||||||
|
assistantMessage["role"] = "assistant";
|
||||||
|
assistantMessage["content"] = m_accumulatedResponse.trimmed();
|
||||||
|
m_chatHistory.append(assistantMessage);
|
||||||
|
|
||||||
|
m_pendingMessage.clear();
|
||||||
|
m_accumulatedResponse.clear();
|
||||||
|
|
||||||
|
trimChatHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatClientInterface::trimChatHistory()
|
||||||
|
{
|
||||||
|
int maxTokens = 4000;
|
||||||
|
int totalTokens = 0;
|
||||||
|
QJsonArray newHistory;
|
||||||
|
|
||||||
|
if (!m_chatHistory.isEmpty()
|
||||||
|
&& m_chatHistory.first().toObject()["role"].toString() == "system") {
|
||||||
|
newHistory.append(m_chatHistory.first());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = m_chatHistory.size() - 1; i >= 0; --i) {
|
||||||
|
QJsonObject message = m_chatHistory[i].toObject();
|
||||||
|
int messageTokens = message["content"].toString().length() / 4;
|
||||||
|
|
||||||
|
if (totalTokens + messageTokens > maxTokens) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
newHistory.prepend(message);
|
||||||
|
totalTokens += messageTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_chatHistory = newHistory;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
53
chat/ChatClientInterface.hpp
Normal file
53
chat/ChatClientInterface.hpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtCore/qjsonarray.h>
|
||||||
|
#include "QodeAssistData.hpp"
|
||||||
|
#include "core/LLMRequestHandler.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
class ChatClientInterface : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ChatClientInterface(QObject *parent = nullptr);
|
||||||
|
~ChatClientInterface();
|
||||||
|
|
||||||
|
void sendMessage(const QString &message);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void messageReceived(const QString &message);
|
||||||
|
void errorOccurred(const QString &error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleLLMResponse(const QString &response, bool isComplete);
|
||||||
|
void trimChatHistory();
|
||||||
|
|
||||||
|
LLMRequestHandler *m_requestHandler;
|
||||||
|
QString m_accumulatedResponse;
|
||||||
|
QString m_pendingMessage;
|
||||||
|
QJsonArray m_chatHistory;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
95
chat/ChatOutputPane.cpp
Normal file
95
chat/ChatOutputPane.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ChatOutputPane.h"
|
||||||
|
|
||||||
|
#include "QodeAssisttr.h"
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
ChatOutputPane::ChatOutputPane(QObject *parent)
|
||||||
|
: Core::IOutputPane(parent)
|
||||||
|
, m_chatWidget(new ChatWidget)
|
||||||
|
{
|
||||||
|
setId("QodeAssistChat");
|
||||||
|
setDisplayName(Tr::tr("QodeAssist Chat"));
|
||||||
|
setPriorityInStatusBar(-40);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatOutputPane::~ChatOutputPane()
|
||||||
|
{
|
||||||
|
delete m_chatWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *ChatOutputPane::outputWidget(QWidget *)
|
||||||
|
{
|
||||||
|
return m_chatWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QWidget *> ChatOutputPane::toolBarWidgets() const
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatOutputPane::clearContents()
|
||||||
|
{
|
||||||
|
m_chatWidget->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatOutputPane::visibilityChanged(bool visible)
|
||||||
|
{
|
||||||
|
if (visible)
|
||||||
|
m_chatWidget->scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatOutputPane::setFocus()
|
||||||
|
{
|
||||||
|
m_chatWidget->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatOutputPane::hasFocus() const
|
||||||
|
{
|
||||||
|
return m_chatWidget->hasFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatOutputPane::canFocus() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatOutputPane::canNavigate() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatOutputPane::canNext() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatOutputPane::canPrevious() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatOutputPane::goToNext() {}
|
||||||
|
|
||||||
|
void ChatOutputPane::goToPrev() {}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
52
chat/ChatOutputPane.h
Normal file
52
chat/ChatOutputPane.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ChatWidget.h"
|
||||||
|
#include <coreplugin/ioutputpane.h>
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
class ChatOutputPane : public Core::IOutputPane
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ChatOutputPane(QObject *parent = nullptr);
|
||||||
|
~ChatOutputPane() override;
|
||||||
|
|
||||||
|
QWidget *outputWidget(QWidget *parent) override;
|
||||||
|
QList<QWidget *> toolBarWidgets() const override;
|
||||||
|
void clearContents() override;
|
||||||
|
void visibilityChanged(bool visible) override;
|
||||||
|
void setFocus() override;
|
||||||
|
bool hasFocus() const override;
|
||||||
|
bool canFocus() const override;
|
||||||
|
bool canNavigate() const override;
|
||||||
|
bool canNext() const override;
|
||||||
|
bool canPrevious() const override;
|
||||||
|
void goToNext() override;
|
||||||
|
void goToPrev() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ChatWidget *m_chatWidget;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
150
chat/ChatWidget.cpp
Normal file
150
chat/ChatWidget.cpp
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ChatWidget.h"
|
||||||
|
#include "QodeAssistUtils.hpp"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QtCore/qtimer.h>
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
ChatWidget::ChatWidget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, m_showTimestamp(false)
|
||||||
|
, m_chatClient(new ChatClientInterface(this))
|
||||||
|
{
|
||||||
|
setupUi();
|
||||||
|
|
||||||
|
connect(m_sendButton, &QPushButton::clicked, this, &ChatWidget::sendMessage);
|
||||||
|
connect(m_messageInput, &QLineEdit::returnPressed, this, &ChatWidget::sendMessage);
|
||||||
|
|
||||||
|
connect(m_chatClient, &ChatClientInterface::messageReceived, this, &ChatWidget::receiveMessage);
|
||||||
|
connect(m_chatClient, &ChatClientInterface::errorOccurred, this, &ChatWidget::handleError);
|
||||||
|
|
||||||
|
logMessage("ChatWidget initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::setupUi()
|
||||||
|
{
|
||||||
|
m_chatDisplay = new QTextEdit(this);
|
||||||
|
m_chatDisplay->setReadOnly(true);
|
||||||
|
|
||||||
|
m_messageInput = new QLineEdit(this);
|
||||||
|
m_sendButton = new QPushButton("Send", this);
|
||||||
|
|
||||||
|
QHBoxLayout *inputLayout = new QHBoxLayout;
|
||||||
|
inputLayout->addWidget(m_messageInput);
|
||||||
|
inputLayout->addWidget(m_sendButton);
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||||
|
mainLayout->addWidget(m_chatDisplay);
|
||||||
|
mainLayout->addLayout(inputLayout);
|
||||||
|
|
||||||
|
setLayout(mainLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::sendMessage()
|
||||||
|
{
|
||||||
|
QString message = m_messageInput->text().trimmed();
|
||||||
|
if (!message.isEmpty()) {
|
||||||
|
logMessage("Sending message: " + message);
|
||||||
|
addMessage(message, true);
|
||||||
|
m_chatClient->sendMessage(message);
|
||||||
|
m_messageInput->clear();
|
||||||
|
addMessage("AI is typing...", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::receiveMessage(const QString &message)
|
||||||
|
{
|
||||||
|
logMessage("Received message: " + message);
|
||||||
|
updateLastAIMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::receivePartialMessage(const QString &partialMessage)
|
||||||
|
{
|
||||||
|
logMessage("Received partial message: " + partialMessage);
|
||||||
|
m_currentAIResponse += partialMessage;
|
||||||
|
updateLastAIMessage(m_currentAIResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::onMessageCompleted()
|
||||||
|
{
|
||||||
|
logMessage("Message completed. Final response: " + m_currentAIResponse);
|
||||||
|
updateLastAIMessage(m_currentAIResponse);
|
||||||
|
m_currentAIResponse.clear();
|
||||||
|
scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::handleError(const QString &error)
|
||||||
|
{
|
||||||
|
logMessage("Error occurred: " + error);
|
||||||
|
addMessage("Error: " + error, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::addMessage(const QString &message, bool fromUser)
|
||||||
|
{
|
||||||
|
auto prefix = fromUser ? "You: " : "AI: ";
|
||||||
|
QString timestamp = m_showTimestamp ? QDateTime::currentDateTime().toString("[hh:mm:ss] ") : "";
|
||||||
|
QString fullMessage = timestamp + prefix + message;
|
||||||
|
logMessage("Adding message to display: " + fullMessage);
|
||||||
|
m_chatDisplay->append(fullMessage);
|
||||||
|
scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::updateLastAIMessage(const QString &message)
|
||||||
|
{
|
||||||
|
logMessage("Updating last AI message: " + message);
|
||||||
|
QTextCursor cursor = m_chatDisplay->textCursor();
|
||||||
|
cursor.movePosition(QTextCursor::End);
|
||||||
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
|
||||||
|
cursor.removeSelectedText();
|
||||||
|
|
||||||
|
QString timestamp = m_showTimestamp ? QDateTime::currentDateTime().toString("[hh:mm:ss] ") : "";
|
||||||
|
cursor.insertText(timestamp + "AI: " + message);
|
||||||
|
|
||||||
|
cursor.movePosition(QTextCursor::End);
|
||||||
|
m_chatDisplay->setTextCursor(cursor);
|
||||||
|
|
||||||
|
scrollToBottom();
|
||||||
|
m_chatDisplay->repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::clear()
|
||||||
|
{
|
||||||
|
m_chatDisplay->clear();
|
||||||
|
m_currentAIResponse.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::scrollToBottom()
|
||||||
|
{
|
||||||
|
QScrollBar *scrollBar = m_chatDisplay->verticalScrollBar();
|
||||||
|
scrollBar->setValue(scrollBar->maximum());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::setShowTimestamp(bool show)
|
||||||
|
{
|
||||||
|
m_showTimestamp = show;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
62
chat/ChatWidget.h
Normal file
62
chat/ChatWidget.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "ChatClientInterface.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
class ChatWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ChatWidget(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
void scrollToBottom();
|
||||||
|
void setShowTimestamp(bool show);
|
||||||
|
|
||||||
|
void receiveMessage(const QString &message);
|
||||||
|
private slots:
|
||||||
|
void sendMessage();
|
||||||
|
void receivePartialMessage(const QString &partialMessage);
|
||||||
|
void onMessageCompleted();
|
||||||
|
void handleError(const QString &error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTextEdit *m_chatDisplay;
|
||||||
|
QLineEdit *m_messageInput;
|
||||||
|
QPushButton *m_sendButton;
|
||||||
|
bool m_showTimestamp;
|
||||||
|
ChatClientInterface *m_chatClient;
|
||||||
|
QString m_currentAIResponse;
|
||||||
|
|
||||||
|
void setupUi();
|
||||||
|
void addMessage(const QString &message, bool fromUser = true);
|
||||||
|
void updateLastAIMessage(const QString &message);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
40
core/LLMRequestConfig.hpp
Normal file
40
core/LLMRequestConfig.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QUrl>
|
||||||
|
#include "providers/LLMProvider.hpp"
|
||||||
|
#include "templates/PromptTemplate.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
enum class RequestType { Fim, Chat };
|
||||||
|
|
||||||
|
struct LLMConfig
|
||||||
|
{
|
||||||
|
QUrl url;
|
||||||
|
Providers::LLMProvider *provider;
|
||||||
|
Templates::PromptTemplate *promptTemplate;
|
||||||
|
QJsonObject providerRequest;
|
||||||
|
RequestType requestType;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist
|
162
core/LLMRequestHandler.cpp
Normal file
162
core/LLMRequestHandler.cpp
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "LLMRequestHandler.hpp"
|
||||||
|
#include "LLMProvidersManager.hpp"
|
||||||
|
#include "QodeAssistUtils.hpp"
|
||||||
|
#include "settings/GeneralSettings.hpp"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
LLMRequestHandler::LLMRequestHandler(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_manager(new QNetworkAccessManager(this))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void LLMRequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObject &request)
|
||||||
|
{
|
||||||
|
logMessage(QString("Sending request to llm: \nurl: %1\nRequest body:\n%2")
|
||||||
|
.arg(config.url.toString(),
|
||||||
|
QString::fromUtf8(
|
||||||
|
QJsonDocument(config.providerRequest).toJson(QJsonDocument::Indented))));
|
||||||
|
|
||||||
|
QNetworkRequest networkRequest(config.url);
|
||||||
|
prepareNetworkRequest(networkRequest, config.providerRequest);
|
||||||
|
|
||||||
|
QNetworkReply *reply = m_manager->post(networkRequest,
|
||||||
|
QJsonDocument(config.providerRequest).toJson());
|
||||||
|
if (!reply) {
|
||||||
|
logMessage("Error: Failed to create network reply");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString requestId = request["id"].toString();
|
||||||
|
m_activeRequests[requestId] = reply;
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::readyRead, this, [this, reply, request, config]() {
|
||||||
|
handleLLMResponse(reply, request, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId]() {
|
||||||
|
reply->deleteLater();
|
||||||
|
m_activeRequests.remove(requestId);
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
logMessage(QString("Error in QodeAssist request: %1").arg(reply->errorString()));
|
||||||
|
emit requestFinished(requestId, false, reply->errorString());
|
||||||
|
} else {
|
||||||
|
logMessage("Request finished successfully");
|
||||||
|
emit requestFinished(requestId, true, QString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLMRequestHandler::handleLLMResponse(QNetworkReply *reply,
|
||||||
|
const QJsonObject &request,
|
||||||
|
const LLMConfig &config)
|
||||||
|
{
|
||||||
|
qDebug() << "Handling LLM response" << request;
|
||||||
|
|
||||||
|
QString &accumulatedResponse = m_accumulatedResponses[reply];
|
||||||
|
|
||||||
|
bool isComplete = config.provider->handleResponse(reply, accumulatedResponse);
|
||||||
|
|
||||||
|
if (config.requestType == RequestType::Fim) {
|
||||||
|
if (!Settings::generalSettings().multiLineCompletion()
|
||||||
|
&& processSingleLineCompletion(reply, request, accumulatedResponse, config)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isComplete || reply->isFinished()) {
|
||||||
|
if (isComplete) {
|
||||||
|
if (config.requestType == RequestType::Fim) {
|
||||||
|
auto cleanedCompletion = removeStopWords(accumulatedResponse,
|
||||||
|
config.promptTemplate->stopWords());
|
||||||
|
emit completionReceived(cleanedCompletion, request, true);
|
||||||
|
} else {
|
||||||
|
emit completionReceived(accumulatedResponse, request, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit completionReceived(accumulatedResponse, request, false);
|
||||||
|
}
|
||||||
|
m_accumulatedResponses.remove(reply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLMRequestHandler::cancelRequest(const QString &id)
|
||||||
|
{
|
||||||
|
if (m_activeRequests.contains(id)) {
|
||||||
|
QNetworkReply *reply = m_activeRequests[id];
|
||||||
|
reply->abort();
|
||||||
|
m_activeRequests.remove(id);
|
||||||
|
m_accumulatedResponses.remove(reply);
|
||||||
|
emit requestCancelled(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLMRequestHandler::prepareNetworkRequest(QNetworkRequest &networkRequest,
|
||||||
|
const QJsonObject &providerRequest)
|
||||||
|
{
|
||||||
|
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
if (providerRequest.contains("api_key")) {
|
||||||
|
QString apiKey = providerRequest["api_key"].toString();
|
||||||
|
networkRequest.setRawHeader("Authorization", QString("Bearer %1").arg(apiKey).toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLMRequestHandler::processSingleLineCompletion(QNetworkReply *reply,
|
||||||
|
const QJsonObject &request,
|
||||||
|
const QString &accumulatedResponse,
|
||||||
|
const LLMConfig &config)
|
||||||
|
{
|
||||||
|
int newlinePos = accumulatedResponse.indexOf('\n');
|
||||||
|
|
||||||
|
if (newlinePos != -1) {
|
||||||
|
QString singleLineCompletion = accumulatedResponse.left(newlinePos).trimmed();
|
||||||
|
singleLineCompletion = removeStopWords(singleLineCompletion,
|
||||||
|
config.promptTemplate->stopWords());
|
||||||
|
|
||||||
|
emit completionReceived(singleLineCompletion, request, true);
|
||||||
|
m_accumulatedResponses.remove(reply);
|
||||||
|
reply->abort();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LLMRequestHandler::removeStopWords(const QStringView &completion,
|
||||||
|
const QStringList &stopWords)
|
||||||
|
{
|
||||||
|
QString filteredCompletion = completion.toString();
|
||||||
|
|
||||||
|
for (const QString &stopWord : stopWords) {
|
||||||
|
filteredCompletion = filteredCompletion.replace(stopWord, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredCompletion;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist
|
64
core/LLMRequestHandler.hpp
Normal file
64
core/LLMRequestHandler.hpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "QodeAssistData.hpp"
|
||||||
|
#include "core/LLMRequestConfig.hpp"
|
||||||
|
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
class LLMRequestHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LLMRequestHandler(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void sendLLMRequest(const LLMConfig &config, const QJsonObject &request);
|
||||||
|
void handleLLMResponse(QNetworkReply *reply,
|
||||||
|
const QJsonObject &request,
|
||||||
|
const LLMConfig &config);
|
||||||
|
bool cancelRequest(const QString &id);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void completionReceived(const QString &completion, const QJsonObject &request, bool isComplete);
|
||||||
|
void requestFinished(const QString &requestId, bool success, const QString &errorString);
|
||||||
|
void requestCancelled(const QString &id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager *m_manager;
|
||||||
|
QMap<QString, QNetworkReply *> m_activeRequests;
|
||||||
|
QMap<QNetworkReply *, QString> m_accumulatedResponses;
|
||||||
|
|
||||||
|
void prepareNetworkRequest(QNetworkRequest &networkRequest, const QJsonObject &providerRequest);
|
||||||
|
bool processSingleLineCompletion(QNetworkReply *reply,
|
||||||
|
const QJsonObject &request,
|
||||||
|
const QString &accumulatedResponse,
|
||||||
|
const LLMConfig &config);
|
||||||
|
QString removeStopWords(const QStringView &completion, const QStringList &stopWords);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist
|
@ -35,6 +35,7 @@ public:
|
|||||||
virtual QString name() const = 0;
|
virtual QString name() const = 0;
|
||||||
virtual QString url() const = 0;
|
virtual QString url() const = 0;
|
||||||
virtual QString completionEndpoint() const = 0;
|
virtual QString completionEndpoint() const = 0;
|
||||||
|
virtual QString chatEndpoint() const = 0;
|
||||||
|
|
||||||
virtual void prepareRequest(QJsonObject &request) = 0;
|
virtual void prepareRequest(QJsonObject &request) = 0;
|
||||||
virtual bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) = 0;
|
virtual bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) = 0;
|
||||||
|
@ -48,12 +48,14 @@ QString LMStudioProvider::completionEndpoint() const
|
|||||||
return "/v1/chat/completions";
|
return "/v1/chat/completions";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString LMStudioProvider::chatEndpoint() const
|
||||||
|
{
|
||||||
|
return "/v1/chat/completions";
|
||||||
|
}
|
||||||
|
|
||||||
void LMStudioProvider::prepareRequest(QJsonObject &request)
|
void LMStudioProvider::prepareRequest(QJsonObject &request)
|
||||||
{
|
{
|
||||||
auto &settings = Settings::presetPromptsSettings();
|
auto &settings = Settings::presetPromptsSettings();
|
||||||
const auto ¤tTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
|
||||||
if (currentTemplate->name() == "Custom Template")
|
|
||||||
return;
|
|
||||||
if (request.contains("prompt")) {
|
if (request.contains("prompt")) {
|
||||||
QJsonArray messages{
|
QJsonArray messages{
|
||||||
{QJsonObject{{"role", "user"}, {"content", request.take("prompt").toString()}}}};
|
{QJsonObject{{"role", "user"}, {"content", request.take("prompt").toString()}}}};
|
||||||
@ -62,7 +64,6 @@ void LMStudioProvider::prepareRequest(QJsonObject &request)
|
|||||||
|
|
||||||
request["max_tokens"] = settings.maxTokens();
|
request["max_tokens"] = settings.maxTokens();
|
||||||
request["temperature"] = settings.temperature();
|
request["temperature"] = settings.temperature();
|
||||||
request["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords());
|
|
||||||
if (settings.useTopP())
|
if (settings.useTopP())
|
||||||
request["top_p"] = settings.topP();
|
request["top_p"] = settings.topP();
|
||||||
if (settings.useTopK())
|
if (settings.useTopK())
|
||||||
|
@ -31,6 +31,7 @@ public:
|
|||||||
QString name() const override;
|
QString name() const override;
|
||||||
QString url() const override;
|
QString url() const override;
|
||||||
QString completionEndpoint() const override;
|
QString completionEndpoint() const override;
|
||||||
|
QString chatEndpoint() const override;
|
||||||
void prepareRequest(QJsonObject &request) override;
|
void prepareRequest(QJsonObject &request) override;
|
||||||
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
||||||
QList<QString> getInstalledModels(const Utils::Environment &env) override;
|
QList<QString> getInstalledModels(const Utils::Environment &env) override;
|
||||||
|
@ -48,18 +48,19 @@ QString OllamaProvider::completionEndpoint() const
|
|||||||
return "/api/generate";
|
return "/api/generate";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString OllamaProvider::chatEndpoint() const
|
||||||
|
{
|
||||||
|
return "/api/chat";
|
||||||
|
}
|
||||||
|
|
||||||
void OllamaProvider::prepareRequest(QJsonObject &request)
|
void OllamaProvider::prepareRequest(QJsonObject &request)
|
||||||
{
|
{
|
||||||
auto &settings = Settings::presetPromptsSettings();
|
auto &settings = Settings::presetPromptsSettings();
|
||||||
auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
|
||||||
if (currentTemplate->name() == "Custom Template")
|
|
||||||
return;
|
|
||||||
|
|
||||||
QJsonObject options;
|
QJsonObject options;
|
||||||
options["num_predict"] = settings.maxTokens();
|
options["num_predict"] = settings.maxTokens();
|
||||||
options["keep_alive"] = settings.ollamaLivetime();
|
options["keep_alive"] = settings.ollamaLivetime();
|
||||||
options["temperature"] = settings.temperature();
|
options["temperature"] = settings.temperature();
|
||||||
options["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords());
|
|
||||||
if (settings.useTopP())
|
if (settings.useTopP())
|
||||||
options["top_p"] = settings.topP();
|
options["top_p"] = settings.topP();
|
||||||
if (settings.useTopK())
|
if (settings.useTopK())
|
||||||
@ -73,28 +74,52 @@ void OllamaProvider::prepareRequest(QJsonObject &request)
|
|||||||
|
|
||||||
bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
|
bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
|
||||||
{
|
{
|
||||||
|
QString endpoint = reply->url().path();
|
||||||
|
|
||||||
bool isComplete = false;
|
bool isComplete = false;
|
||||||
while (reply->canReadLine()) {
|
while (reply->canReadLine()) {
|
||||||
QByteArray line = reply->readLine().trimmed();
|
QByteArray line = reply->readLine().trimmed();
|
||||||
if (line.isEmpty()) {
|
if (line.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(line);
|
|
||||||
if (jsonResponse.isNull()) {
|
QJsonDocument doc = QJsonDocument::fromJson(line);
|
||||||
qWarning() << "Invalid JSON response from Ollama:" << line;
|
if (doc.isNull()) {
|
||||||
|
logMessage("Invalid JSON response from Ollama: " + QString::fromUtf8(line));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QJsonObject responseObj = jsonResponse.object();
|
|
||||||
|
QJsonObject responseObj = doc.object();
|
||||||
|
|
||||||
|
if (responseObj.contains("error")) {
|
||||||
|
QString errorMessage = responseObj["error"].toString();
|
||||||
|
logMessage("Error in Ollama response: " + errorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endpoint == completionEndpoint()) {
|
||||||
if (responseObj.contains("response")) {
|
if (responseObj.contains("response")) {
|
||||||
QString completion = responseObj["response"].toString();
|
QString completion = responseObj["response"].toString();
|
||||||
|
|
||||||
accumulatedResponse += completion;
|
accumulatedResponse += completion;
|
||||||
}
|
}
|
||||||
if (responseObj["done"].toBool()) {
|
} else if (endpoint == chatEndpoint()) {
|
||||||
|
if (responseObj.contains("message")) {
|
||||||
|
QJsonObject message = responseObj["message"].toObject();
|
||||||
|
if (message.contains("content")) {
|
||||||
|
QString content = message["content"].toString();
|
||||||
|
accumulatedResponse += content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logMessage("Unknown endpoint: " + endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseObj.contains("done") && responseObj["done"].toBool()) {
|
||||||
isComplete = true;
|
isComplete = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return isComplete;
|
return isComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ public:
|
|||||||
QString name() const override;
|
QString name() const override;
|
||||||
QString url() const override;
|
QString url() const override;
|
||||||
QString completionEndpoint() const override;
|
QString completionEndpoint() const override;
|
||||||
|
QString chatEndpoint() const override;
|
||||||
void prepareRequest(QJsonObject &request) override;
|
void prepareRequest(QJsonObject &request) override;
|
||||||
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
||||||
QList<QString> getInstalledModels(const Utils::Environment &env) override;
|
QList<QString> getInstalledModels(const Utils::Environment &env) override;
|
||||||
|
@ -46,13 +46,14 @@ QString OpenAICompatProvider::completionEndpoint() const
|
|||||||
return "/v1/chat/completions";
|
return "/v1/chat/completions";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString OpenAICompatProvider::chatEndpoint() const
|
||||||
|
{
|
||||||
|
return "/v1/chat/completions";
|
||||||
|
}
|
||||||
|
|
||||||
void OpenAICompatProvider::prepareRequest(QJsonObject &request)
|
void OpenAICompatProvider::prepareRequest(QJsonObject &request)
|
||||||
{
|
{
|
||||||
auto &settings = Settings::presetPromptsSettings();
|
auto &settings = Settings::presetPromptsSettings();
|
||||||
const auto ¤tTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
|
||||||
if (currentTemplate->name() == "Custom Template")
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (request.contains("prompt")) {
|
if (request.contains("prompt")) {
|
||||||
QJsonArray messages{
|
QJsonArray messages{
|
||||||
{QJsonObject{{"role", "user"}, {"content", request.take("prompt").toString()}}}};
|
{QJsonObject{{"role", "user"}, {"content", request.take("prompt").toString()}}}};
|
||||||
@ -61,7 +62,6 @@ void OpenAICompatProvider::prepareRequest(QJsonObject &request)
|
|||||||
|
|
||||||
request["max_tokens"] = settings.maxTokens();
|
request["max_tokens"] = settings.maxTokens();
|
||||||
request["temperature"] = settings.temperature();
|
request["temperature"] = settings.temperature();
|
||||||
request["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords());
|
|
||||||
if (settings.useTopP())
|
if (settings.useTopP())
|
||||||
request["top_p"] = settings.topP();
|
request["top_p"] = settings.topP();
|
||||||
if (settings.useTopK())
|
if (settings.useTopK())
|
||||||
|
@ -31,6 +31,7 @@ public:
|
|||||||
QString name() const override;
|
QString name() const override;
|
||||||
QString url() const override;
|
QString url() const override;
|
||||||
QString completionEndpoint() const override;
|
QString completionEndpoint() const override;
|
||||||
|
QString chatEndpoint() const override;
|
||||||
void prepareRequest(QJsonObject &request) override;
|
void prepareRequest(QJsonObject &request) override;
|
||||||
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
||||||
QList<QString> getInstalledModels(const Utils::Environment &env) override;
|
QList<QString> getInstalledModels(const Utils::Environment &env) override;
|
||||||
|
@ -26,8 +26,9 @@
|
|||||||
#include <coreplugin/coreconstants.h>
|
#include <coreplugin/coreconstants.h>
|
||||||
#include <coreplugin/icontext.h>
|
#include <coreplugin/icontext.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/messagemanager.h>
|
||||||
|
#include <coreplugin/modemanager.h>
|
||||||
#include <coreplugin/statusbarmanager.h>
|
#include <coreplugin/statusbarmanager.h>
|
||||||
|
|
||||||
#include <extensionsystem/iplugin.h>
|
#include <extensionsystem/iplugin.h>
|
||||||
#include <languageclient/languageclientmanager.h>
|
#include <languageclient/languageclientmanager.h>
|
||||||
|
|
||||||
@ -41,11 +42,16 @@
|
|||||||
#include "LLMProvidersManager.hpp"
|
#include "LLMProvidersManager.hpp"
|
||||||
#include "PromptTemplateManager.hpp"
|
#include "PromptTemplateManager.hpp"
|
||||||
#include "QodeAssistClient.hpp"
|
#include "QodeAssistClient.hpp"
|
||||||
|
#include "chat/ChatOutputPane.h"
|
||||||
#include "providers/LMStudioProvider.hpp"
|
#include "providers/LMStudioProvider.hpp"
|
||||||
#include "providers/OllamaProvider.hpp"
|
#include "providers/OllamaProvider.hpp"
|
||||||
#include "providers/OpenAICompatProvider.hpp"
|
#include "providers/OpenAICompatProvider.hpp"
|
||||||
#include "templates/CodeLLamaTemplate.hpp"
|
|
||||||
|
#include "settings/GeneralSettings.hpp"
|
||||||
|
#include "templates/CodeLlamaFimTemplate.hpp"
|
||||||
|
#include "templates/CodeLlamaInstruct.hpp"
|
||||||
#include "templates/CustomTemplate.hpp"
|
#include "templates/CustomTemplate.hpp"
|
||||||
|
#include "templates/DeepSeekCoderChatTemplate.hpp"
|
||||||
#include "templates/DeepSeekCoderV2.hpp"
|
#include "templates/DeepSeekCoderV2.hpp"
|
||||||
#include "templates/StarCoder2Template.hpp"
|
#include "templates/StarCoder2Template.hpp"
|
||||||
|
|
||||||
@ -78,10 +84,12 @@ public:
|
|||||||
providerManager.registerProvider<Providers::OpenAICompatProvider>();
|
providerManager.registerProvider<Providers::OpenAICompatProvider>();
|
||||||
|
|
||||||
auto &templateManager = PromptTemplateManager::instance();
|
auto &templateManager = PromptTemplateManager::instance();
|
||||||
templateManager.registerTemplate<Templates::CodeLLamaTemplate>();
|
templateManager.registerTemplate<Templates::CodeLlamaFimTemplate>();
|
||||||
templateManager.registerTemplate<Templates::StarCoder2Template>();
|
templateManager.registerTemplate<Templates::StarCoder2Template>();
|
||||||
templateManager.registerTemplate<Templates::DeepSeekCoderV2Template>();
|
templateManager.registerTemplate<Templates::DeepSeekCoderV2Template>();
|
||||||
templateManager.registerTemplate<Templates::CustomTemplate>();
|
templateManager.registerTemplate<Templates::CustomTemplate>();
|
||||||
|
templateManager.registerTemplate<Templates::DeepSeekCoderChatTemplate>();
|
||||||
|
templateManager.registerTemplate<Templates::CodeLlamaInstructTemplate>();
|
||||||
|
|
||||||
Utils::Icon QCODEASSIST_ICON(
|
Utils::Icon QCODEASSIST_ICON(
|
||||||
{{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}});
|
{{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}});
|
||||||
@ -106,6 +114,8 @@ public:
|
|||||||
auto toggleButton = new QToolButton;
|
auto toggleButton = new QToolButton;
|
||||||
toggleButton->setDefaultAction(requestAction.contextAction());
|
toggleButton->setDefaultAction(requestAction.contextAction());
|
||||||
StatusBarManager::addStatusBarWidget(toggleButton, StatusBarManager::RightCorner);
|
StatusBarManager::addStatusBarWidget(toggleButton, StatusBarManager::RightCorner);
|
||||||
|
|
||||||
|
m_chatOutputPane = new Chat::ChatOutputPane(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void extensionsInitialized() final
|
void extensionsInitialized() final
|
||||||
@ -139,6 +149,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<QodeAssistClient> m_qodeAssistClient;
|
QPointer<QodeAssistClient> m_qodeAssistClient;
|
||||||
|
QPointer<Chat::ChatOutputPane> m_chatOutputPane;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Internal
|
} // namespace QodeAssist::Internal
|
||||||
|
@ -84,9 +84,8 @@ GeneralSettings::GeneralSettings()
|
|||||||
autoCompletionTypingInterval.setDefaultValue(2000);
|
autoCompletionTypingInterval.setDefaultValue(2000);
|
||||||
|
|
||||||
llmProviders.setSettingsKey(Constants::LLM_PROVIDERS);
|
llmProviders.setSettingsKey(Constants::LLM_PROVIDERS);
|
||||||
llmProviders.setDisplayName(Tr::tr("FIM Provider:"));
|
llmProviders.setDisplayName(Tr::tr("AI Suggest Provider:"));
|
||||||
llmProviders.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
llmProviders.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
||||||
llmProviders.setDefaultValue(0);
|
|
||||||
|
|
||||||
url.setSettingsKey(Constants::URL);
|
url.setSettingsKey(Constants::URL);
|
||||||
url.setLabelText(Tr::tr("URL:"));
|
url.setLabelText(Tr::tr("URL:"));
|
||||||
@ -97,37 +96,53 @@ GeneralSettings::GeneralSettings()
|
|||||||
endPoint.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
endPoint.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
|
||||||
modelName.setSettingsKey(Constants::MODEL_NAME);
|
modelName.setSettingsKey(Constants::MODEL_NAME);
|
||||||
modelName.setLabelText(Tr::tr("LLM Name:"));
|
modelName.setLabelText(Tr::tr("Model name:"));
|
||||||
modelName.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
modelName.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
|
||||||
selectModels.m_buttonText = Tr::tr("Select Fill-In-the-Middle Model");
|
selectModels.m_buttonText = Tr::tr("Select Fill-In-the-Middle Model");
|
||||||
|
|
||||||
fimPrompts.setDisplayName(Tr::tr("Fill-In-the-Middle Prompt"));
|
fimPrompts.setDisplayName(Tr::tr("Fill-In-the-Middle Prompt"));
|
||||||
fimPrompts.setSettingsKey(Constants::FIM_PROMPTS);
|
fimPrompts.setSettingsKey(Constants::FIM_PROMPTS);
|
||||||
fimPrompts.setDefaultValue(0);
|
|
||||||
fimPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
fimPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
||||||
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
|
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
|
||||||
|
|
||||||
const auto &manager = LLMProvidersManager::instance();
|
chatLlmProviders.setSettingsKey(Constants::CHAT_LLM_PROVIDERS);
|
||||||
if (!manager.getProviderNames().isEmpty()) {
|
chatLlmProviders.setDisplayName(Tr::tr("AI Chat Provider:"));
|
||||||
const auto providerNames = manager.getProviderNames();
|
chatLlmProviders.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
||||||
for (const QString &name : providerNames) {
|
|
||||||
llmProviders.addOption(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &promptManager = PromptTemplateManager::instance();
|
chatUrl.setSettingsKey(Constants::CHAT_URL);
|
||||||
if (!promptManager.getTemplateNames().isEmpty()) {
|
chatUrl.setLabelText(Tr::tr("URL:"));
|
||||||
const auto promptNames = promptManager.getTemplateNames();
|
chatUrl.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
for (const QString &name : promptNames) {
|
|
||||||
fimPrompts.addOption(name);
|
chatEndPoint.setSettingsKey(Constants::CHAT_END_POINT);
|
||||||
}
|
chatEndPoint.setLabelText(Tr::tr("Chat Endpoint:"));
|
||||||
}
|
chatEndPoint.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
|
||||||
|
chatModelName.setSettingsKey(Constants::CHAT_MODEL_NAME);
|
||||||
|
chatModelName.setLabelText(Tr::tr("Model name:"));
|
||||||
|
chatModelName.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
|
||||||
|
chatSelectModels.m_buttonText = Tr::tr("Select Chat Model");
|
||||||
|
|
||||||
|
chatPrompts.setDisplayName(Tr::tr("Chat Prompt"));
|
||||||
|
chatPrompts.setSettingsKey(Constants::CHAT_PROMPTS);
|
||||||
|
chatPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
||||||
|
|
||||||
|
loadProviders();
|
||||||
|
loadPrompts();
|
||||||
|
|
||||||
readSettings();
|
readSettings();
|
||||||
|
|
||||||
LLMProvidersManager::instance().setCurrentProvider(llmProviders.stringValue());
|
auto fimProviderName = llmProviders.displayForIndex(llmProviders.value());
|
||||||
PromptTemplateManager::instance().setCurrentTemplate(fimPrompts.stringValue());
|
setCurrentFimProvider(fimProviderName);
|
||||||
|
auto chatProviderName = chatLlmProviders.displayForIndex(chatLlmProviders.value());
|
||||||
|
setCurrentChatProvider(chatProviderName);
|
||||||
|
|
||||||
|
auto nameFimPromts = fimPrompts.displayForIndex(fimPrompts.value());
|
||||||
|
PromptTemplateManager::instance().setCurrentFimTemplate(nameFimPromts);
|
||||||
|
auto nameChatPromts = chatPrompts.displayForIndex(chatPrompts.value());
|
||||||
|
PromptTemplateManager::instance().setCurrentChatTemplate(nameChatPromts);
|
||||||
|
|
||||||
setLoggingEnabled(enableLogging());
|
setLoggingEnabled(enableLogging());
|
||||||
|
|
||||||
setupConnections();
|
setupConnections();
|
||||||
@ -135,7 +150,8 @@ GeneralSettings::GeneralSettings()
|
|||||||
setLayouter([this]() {
|
setLayouter([this]() {
|
||||||
using namespace Layouting;
|
using namespace Layouting;
|
||||||
|
|
||||||
auto rootLayout = Column{Row{enableQodeAssist, Stretch{1}, resetToDefaults},
|
auto rootLayout
|
||||||
|
= Column{Row{enableQodeAssist, Stretch{1}, resetToDefaults},
|
||||||
enableAutoComplete,
|
enableAutoComplete,
|
||||||
multiLineCompletion,
|
multiLineCompletion,
|
||||||
Row{autoCompletionCharThreshold,
|
Row{autoCompletionCharThreshold,
|
||||||
@ -145,12 +161,17 @@ GeneralSettings::GeneralSettings()
|
|||||||
Space{8},
|
Space{8},
|
||||||
enableLogging,
|
enableLogging,
|
||||||
Space{8},
|
Space{8},
|
||||||
Row{llmProviders, Stretch{1}},
|
Group{title(Tr::tr("AI Suggestions")),
|
||||||
Row{url, endPoint, urlIndicator},
|
Column{Row{llmProviders, Stretch{1}},
|
||||||
Space{8},
|
Row{url, endPoint, fimUrlIndicator},
|
||||||
Row{selectModels, modelName, modelIndicator},
|
Row{selectModels, modelName, fimModelIndicator},
|
||||||
Space{8},
|
Row{fimPrompts, Stretch{1}}}},
|
||||||
fimPrompts,
|
Space{16},
|
||||||
|
Group{title(Tr::tr("AI Chat")),
|
||||||
|
Column{Row{chatLlmProviders, Stretch{1}},
|
||||||
|
Row{chatUrl, chatEndPoint, chatUrlIndicator},
|
||||||
|
Row{chatSelectModels, chatModelName, chatModelIndicator},
|
||||||
|
Row{chatPrompts, Stretch{1}}}},
|
||||||
Stretch{1}};
|
Stretch{1}};
|
||||||
return rootLayout;
|
return rootLayout;
|
||||||
});
|
});
|
||||||
@ -161,17 +182,32 @@ GeneralSettings::GeneralSettings()
|
|||||||
void GeneralSettings::setupConnections()
|
void GeneralSettings::setupConnections()
|
||||||
{
|
{
|
||||||
connect(&llmProviders, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
|
connect(&llmProviders, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
|
||||||
int index = llmProviders.volatileValue();
|
auto providerName = llmProviders.displayForIndex(llmProviders.volatileValue());
|
||||||
logMessage(QString("currentProvider %1").arg(llmProviders.displayForIndex(index)));
|
setCurrentFimProvider(providerName);
|
||||||
LLMProvidersManager::instance().setCurrentProvider(llmProviders.displayForIndex(index));
|
|
||||||
updateProviderSettings();
|
|
||||||
});
|
});
|
||||||
|
connect(&chatLlmProviders, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
|
||||||
|
auto providerName = chatLlmProviders.displayForIndex(chatLlmProviders.volatileValue());
|
||||||
|
setCurrentChatProvider(providerName);
|
||||||
|
});
|
||||||
|
|
||||||
connect(&fimPrompts, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
|
connect(&fimPrompts, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
|
||||||
int index = fimPrompts.volatileValue();
|
int index = fimPrompts.volatileValue();
|
||||||
logMessage(QString("currentPrompt %1").arg(fimPrompts.displayForIndex(index)));
|
PromptTemplateManager::instance().setCurrentFimTemplate(fimPrompts.displayForIndex(index));
|
||||||
PromptTemplateManager::instance().setCurrentTemplate(fimPrompts.displayForIndex(index));
|
|
||||||
});
|
});
|
||||||
connect(&selectModels, &ButtonAspect::clicked, this, [this]() { showModelSelectionDialog(); });
|
connect(&chatPrompts, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
|
||||||
|
int index = chatPrompts.volatileValue();
|
||||||
|
PromptTemplateManager::instance().setCurrentChatTemplate(chatPrompts.displayForIndex(index));
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&selectModels, &ButtonAspect::clicked, this, [this]() {
|
||||||
|
auto *provider = LLMProvidersManager::instance().getCurrentFimProvider();
|
||||||
|
showModelSelectionDialog(&modelName, provider);
|
||||||
|
});
|
||||||
|
connect(&chatSelectModels, &ButtonAspect::clicked, this, [this]() {
|
||||||
|
auto *provider = LLMProvidersManager::instance().getCurrentChatProvider();
|
||||||
|
showModelSelectionDialog(&chatModelName, provider);
|
||||||
|
});
|
||||||
|
|
||||||
connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
||||||
setLoggingEnabled(enableLogging.volatileValue());
|
setLoggingEnabled(enableLogging.volatileValue());
|
||||||
});
|
});
|
||||||
@ -185,22 +221,19 @@ void GeneralSettings::setupConnections()
|
|||||||
&Utils::StringAspect::volatileValueChanged,
|
&Utils::StringAspect::volatileValueChanged,
|
||||||
this,
|
this,
|
||||||
&GeneralSettings::updateStatusIndicators);
|
&GeneralSettings::updateStatusIndicators);
|
||||||
|
connect(&chatUrl,
|
||||||
|
&Utils::StringAspect::volatileValueChanged,
|
||||||
|
this,
|
||||||
|
&GeneralSettings::updateStatusIndicators);
|
||||||
|
connect(&chatModelName,
|
||||||
|
&Utils::StringAspect::volatileValueChanged,
|
||||||
|
this,
|
||||||
|
&GeneralSettings::updateStatusIndicators);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralSettings::updateProviderSettings()
|
void GeneralSettings::showModelSelectionDialog(Utils::StringAspect *modelNameObj,
|
||||||
|
Providers::LLMProvider *provider)
|
||||||
{
|
{
|
||||||
const auto provider = LLMProvidersManager::instance().getCurrentProvider();
|
|
||||||
|
|
||||||
if (provider) {
|
|
||||||
url.setVolatileValue(provider->url());
|
|
||||||
endPoint.setVolatileValue(provider->completionEndpoint());
|
|
||||||
modelName.setVolatileValue("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GeneralSettings::showModelSelectionDialog()
|
|
||||||
{
|
|
||||||
auto *provider = LLMProvidersManager::instance().getCurrentProvider();
|
|
||||||
Utils::Environment env = Utils::Environment::systemEnvironment();
|
Utils::Environment env = Utils::Environment::systemEnvironment();
|
||||||
|
|
||||||
if (provider) {
|
if (provider) {
|
||||||
@ -215,7 +248,7 @@ void GeneralSettings::showModelSelectionDialog()
|
|||||||
&ok);
|
&ok);
|
||||||
|
|
||||||
if (ok && !selectedModel.isEmpty()) {
|
if (ok && !selectedModel.isEmpty()) {
|
||||||
modelName.setVolatileValue(selectedModel);
|
modelNameObj->setVolatileValue(selectedModel);
|
||||||
writeSettings();
|
writeSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,42 +266,58 @@ void GeneralSettings::resetPageToDefaults()
|
|||||||
if (reply == QMessageBox::Yes) {
|
if (reply == QMessageBox::Yes) {
|
||||||
resetAspect(enableQodeAssist);
|
resetAspect(enableQodeAssist);
|
||||||
resetAspect(enableAutoComplete);
|
resetAspect(enableAutoComplete);
|
||||||
resetAspect(llmProviders);
|
|
||||||
resetAspect(url);
|
|
||||||
resetAspect(endPoint);
|
|
||||||
resetAspect(modelName);
|
|
||||||
resetAspect(fimPrompts);
|
|
||||||
resetAspect(enableLogging);
|
resetAspect(enableLogging);
|
||||||
resetAspect(startSuggestionTimer);
|
resetAspect(startSuggestionTimer);
|
||||||
resetAspect(autoCompletionTypingInterval);
|
resetAspect(autoCompletionTypingInterval);
|
||||||
resetAspect(autoCompletionCharThreshold);
|
resetAspect(autoCompletionCharThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
fimPrompts.setStringValue("StarCoder2");
|
int fimIndex = llmProviders.indexForDisplay("Ollama");
|
||||||
llmProviders.setStringValue("Ollama");
|
llmProviders.setVolatileValue(fimIndex);
|
||||||
|
int chatIndex = chatLlmProviders.indexForDisplay("Ollama");
|
||||||
|
chatLlmProviders.setVolatileValue(chatIndex);
|
||||||
|
modelName.setVolatileValue("");
|
||||||
|
chatModelName.setVolatileValue("");
|
||||||
|
|
||||||
updateStatusIndicators();
|
updateStatusIndicators();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralSettings::updateStatusIndicators()
|
void GeneralSettings::updateStatusIndicators()
|
||||||
{
|
{
|
||||||
bool urlValid = !url.volatileValue().isEmpty() && !endPoint.volatileValue().isEmpty();
|
bool fimUrlValid = !url.volatileValue().isEmpty() && !endPoint.volatileValue().isEmpty();
|
||||||
bool modelValid = !modelName.volatileValue().isEmpty();
|
bool fimModelValid = !modelName.volatileValue().isEmpty();
|
||||||
|
bool chatUrlValid = !chatUrl.volatileValue().isEmpty()
|
||||||
|
&& !chatEndPoint.volatileValue().isEmpty();
|
||||||
|
bool chatModelValid = !chatModelName.volatileValue().isEmpty();
|
||||||
|
|
||||||
bool pingSuccessful = false;
|
bool fimPingSuccessful = false;
|
||||||
if (urlValid) {
|
if (fimUrlValid) {
|
||||||
QUrl pingUrl(url.volatileValue());
|
QUrl pingUrl(url.volatileValue());
|
||||||
pingSuccessful = QodeAssist::pingUrl(pingUrl);
|
fimPingSuccessful = QodeAssist::pingUrl(pingUrl);
|
||||||
|
}
|
||||||
|
bool chatPingSuccessful = false;
|
||||||
|
if (chatUrlValid) {
|
||||||
|
QUrl pingUrl(chatUrl.volatileValue());
|
||||||
|
chatPingSuccessful = QodeAssist::pingUrl(pingUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
setIndicatorStatus(modelIndicator,
|
setIndicatorStatus(fimModelIndicator,
|
||||||
modelValid ? tr("Model is properly configured")
|
fimModelValid ? tr("Model is properly configured")
|
||||||
: tr("No model selected or model name is invalid"),
|
: tr("No model selected or model name is invalid"),
|
||||||
modelValid);
|
fimModelValid);
|
||||||
|
setIndicatorStatus(fimUrlIndicator,
|
||||||
setIndicatorStatus(urlIndicator,
|
fimPingSuccessful ? tr("Server is reachable")
|
||||||
pingSuccessful ? tr("Server is reachable")
|
|
||||||
: tr("Server is not reachable or URL is invalid"),
|
: tr("Server is not reachable or URL is invalid"),
|
||||||
pingSuccessful);
|
fimPingSuccessful);
|
||||||
|
|
||||||
|
setIndicatorStatus(chatModelIndicator,
|
||||||
|
chatModelValid ? tr("Model is properly configured")
|
||||||
|
: tr("No model selected or model name is invalid"),
|
||||||
|
chatModelValid);
|
||||||
|
setIndicatorStatus(chatUrlIndicator,
|
||||||
|
chatPingSuccessful ? tr("Server is reachable")
|
||||||
|
: tr("Server is not reachable or URL is invalid"),
|
||||||
|
chatPingSuccessful);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralSettings::setIndicatorStatus(Utils::StringAspect &indicator,
|
void GeneralSettings::setIndicatorStatus(Utils::StringAspect &indicator,
|
||||||
@ -280,6 +329,44 @@ void GeneralSettings::setIndicatorStatus(Utils::StringAspect &indicator,
|
|||||||
indicator.setToolTip(tooltip);
|
indicator.setToolTip(tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::setCurrentFimProvider(const QString &name)
|
||||||
|
{
|
||||||
|
const auto provider = LLMProvidersManager::instance().setCurrentFimProvider(name);
|
||||||
|
if (!provider)
|
||||||
|
return;
|
||||||
|
|
||||||
|
url.setValue(provider->url());
|
||||||
|
endPoint.setValue(provider->completionEndpoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::setCurrentChatProvider(const QString &name)
|
||||||
|
{
|
||||||
|
const auto provider = LLMProvidersManager::instance().setCurrentChatProvider(name);
|
||||||
|
if (!provider)
|
||||||
|
return;
|
||||||
|
|
||||||
|
chatUrl.setValue(provider->url());
|
||||||
|
chatEndPoint.setValue(provider->chatEndpoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::loadProviders()
|
||||||
|
{
|
||||||
|
for (const auto &name : LLMProvidersManager::instance().providersNames()) {
|
||||||
|
llmProviders.addOption(name);
|
||||||
|
chatLlmProviders.addOption(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::loadPrompts()
|
||||||
|
{
|
||||||
|
for (const auto &name : PromptTemplateManager::instance().fimTemplatesNames()) {
|
||||||
|
fimPrompts.addOption(name);
|
||||||
|
}
|
||||||
|
for (const auto &name : PromptTemplateManager::instance().chatTemplatesNames()) {
|
||||||
|
chatPrompts.addOption(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class GeneralSettingsPage : public Core::IOptionsPage
|
class GeneralSettingsPage : public Core::IOptionsPage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <utils/aspects.h>
|
#include <utils/aspects.h>
|
||||||
|
|
||||||
|
#include "providers/LLMProvider.hpp"
|
||||||
#include "settings/SettingsUtils.hpp"
|
#include "settings/SettingsUtils.hpp"
|
||||||
|
|
||||||
namespace QodeAssist::Settings {
|
namespace QodeAssist::Settings {
|
||||||
@ -47,17 +48,33 @@ public:
|
|||||||
Utils::SelectionAspect fimPrompts{this};
|
Utils::SelectionAspect fimPrompts{this};
|
||||||
ButtonAspect resetToDefaults{this};
|
ButtonAspect resetToDefaults{this};
|
||||||
|
|
||||||
Utils::StringAspect modelIndicator{this};
|
Utils::SelectionAspect chatLlmProviders{this};
|
||||||
Utils::StringAspect urlIndicator{this};
|
Utils::StringAspect chatUrl{this};
|
||||||
|
Utils::StringAspect chatEndPoint{this};
|
||||||
|
|
||||||
|
Utils::StringAspect chatModelName{this};
|
||||||
|
ButtonAspect chatSelectModels{this};
|
||||||
|
Utils::SelectionAspect chatPrompts{this};
|
||||||
|
|
||||||
|
Utils::StringAspect fimModelIndicator{this};
|
||||||
|
Utils::StringAspect fimUrlIndicator{this};
|
||||||
|
Utils::StringAspect chatModelIndicator{this};
|
||||||
|
Utils::StringAspect chatUrlIndicator{this};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupConnections();
|
void setupConnections();
|
||||||
void updateProviderSettings();
|
void showModelSelectionDialog(Utils::StringAspect *modelNameObj,
|
||||||
void showModelSelectionDialog();
|
Providers::LLMProvider *provider);
|
||||||
void resetPageToDefaults();
|
void resetPageToDefaults();
|
||||||
|
|
||||||
void updateStatusIndicators();
|
void updateStatusIndicators();
|
||||||
void setIndicatorStatus(Utils::StringAspect &indicator, const QString &tooltip, bool isValid);
|
void setIndicatorStatus(Utils::StringAspect &indicator, const QString &tooltip, bool isValid);
|
||||||
|
|
||||||
|
void setCurrentFimProvider(const QString &name);
|
||||||
|
void setCurrentChatProvider(const QString &name);
|
||||||
|
|
||||||
|
void loadProviders();
|
||||||
|
void loadPrompts();
|
||||||
};
|
};
|
||||||
|
|
||||||
GeneralSettings &generalSettings();
|
GeneralSettings &generalSettings();
|
||||||
|
@ -23,10 +23,11 @@
|
|||||||
|
|
||||||
namespace QodeAssist::Templates {
|
namespace QodeAssist::Templates {
|
||||||
|
|
||||||
class CodeLLamaTemplate : public PromptTemplate
|
class CodeLlamaFimTemplate : public PromptTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QString name() const override { return "CodeLlama"; }
|
TemplateType type() const override { return TemplateType::Fim; }
|
||||||
|
QString name() const override { return "CodeLlama FIM"; }
|
||||||
QString promptTemplate() const override { return "%1<PRE> %2 <SUF>%3 <MID>"; }
|
QString promptTemplate() const override { return "%1<PRE> %2 <SUF>%3 <MID>"; }
|
||||||
QStringList stopWords() const override
|
QStringList stopWords() const override
|
||||||
{
|
{
|
49
templates/CodeLlamaInstruct.hpp
Normal file
49
templates/CodeLlamaInstruct.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtCore/qjsonarray.h>
|
||||||
|
#include "PromptTemplate.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Templates {
|
||||||
|
|
||||||
|
class CodeLlamaInstructTemplate : public PromptTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TemplateType type() const override { return TemplateType::Chat; }
|
||||||
|
QString name() const override { return "CodeLlama Chat"; }
|
||||||
|
QString promptTemplate() const override { return "[INST] %1 [/INST]"; }
|
||||||
|
QStringList stopWords() const override { return QStringList() << "[INST]" << "[/INST]"; }
|
||||||
|
|
||||||
|
void prepareRequest(QJsonObject &request, const ContextData &context) const override
|
||||||
|
{
|
||||||
|
QString formattedPrompt = promptTemplate().arg(context.prefix);
|
||||||
|
QJsonArray messages = request["messages"].toArray();
|
||||||
|
|
||||||
|
QJsonObject newMessage;
|
||||||
|
newMessage["role"] = "user";
|
||||||
|
newMessage["content"] = formattedPrompt;
|
||||||
|
messages.append(newMessage);
|
||||||
|
|
||||||
|
request["messages"] = messages;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Templates
|
@ -32,7 +32,8 @@ namespace QodeAssist::Templates {
|
|||||||
class CustomTemplate : public PromptTemplate
|
class CustomTemplate : public PromptTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QString name() const override { return "Custom Template"; }
|
TemplateType type() const override { return TemplateType::Fim; }
|
||||||
|
QString name() const override { return "Custom FIM Template"; }
|
||||||
QString promptTemplate() const override
|
QString promptTemplate() const override
|
||||||
{
|
{
|
||||||
return Settings::customPromptSettings().customJsonTemplate();
|
return Settings::customPromptSettings().customJsonTemplate();
|
||||||
|
54
templates/DeepSeekCoderChatTemplate.hpp
Normal file
54
templates/DeepSeekCoderChatTemplate.hpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include "PromptTemplate.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Templates {
|
||||||
|
|
||||||
|
class DeepSeekCoderChatTemplate : public PromptTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString name() const override { return "DeepSeek Coder Chat"; }
|
||||||
|
TemplateType type() const override { return TemplateType::Chat; }
|
||||||
|
|
||||||
|
QString promptTemplate() const override { return "### Instruction:\n%1\n### Response:\n"; }
|
||||||
|
|
||||||
|
QStringList stopWords() const override
|
||||||
|
{
|
||||||
|
return QStringList() << "### Instruction:" << "### Response:" << "\n\n### " << "<|EOT|>";
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepareRequest(QJsonObject &request, const ContextData &context) const override
|
||||||
|
{
|
||||||
|
QString formattedPrompt = promptTemplate().arg(context.prefix);
|
||||||
|
QJsonArray messages = request["messages"].toArray();
|
||||||
|
|
||||||
|
QJsonObject newMessage;
|
||||||
|
newMessage["role"] = "user";
|
||||||
|
newMessage["content"] = formattedPrompt;
|
||||||
|
messages.append(newMessage);
|
||||||
|
|
||||||
|
request["messages"] = messages;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Templates
|
@ -26,7 +26,8 @@ namespace QodeAssist::Templates {
|
|||||||
class DeepSeekCoderV2Template : public PromptTemplate
|
class DeepSeekCoderV2Template : public PromptTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QString name() const override { return "DeepSeekCoderV2"; }
|
TemplateType type() const override { return TemplateType::Fim; }
|
||||||
|
QString name() const override { return "DeepSeekCoder FIM"; }
|
||||||
QString promptTemplate() const override
|
QString promptTemplate() const override
|
||||||
{
|
{
|
||||||
return "%1<|fim▁begin|>%2<|fim▁hole|>%3<|fim▁end|>";
|
return "%1<|fim▁begin|>%2<|fim▁hole|>%3<|fim▁end|>";
|
||||||
|
@ -27,10 +27,13 @@
|
|||||||
|
|
||||||
namespace QodeAssist::Templates {
|
namespace QodeAssist::Templates {
|
||||||
|
|
||||||
|
enum class TemplateType { Chat, Fim };
|
||||||
|
|
||||||
class PromptTemplate
|
class PromptTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~PromptTemplate() = default;
|
virtual ~PromptTemplate() = default;
|
||||||
|
virtual TemplateType type() const = 0;
|
||||||
virtual QString name() const = 0;
|
virtual QString name() const = 0;
|
||||||
virtual QString promptTemplate() const = 0;
|
virtual QString promptTemplate() const = 0;
|
||||||
virtual QStringList stopWords() const = 0;
|
virtual QStringList stopWords() const = 0;
|
||||||
|
@ -26,7 +26,8 @@ namespace QodeAssist::Templates {
|
|||||||
class StarCoder2Template : public PromptTemplate
|
class StarCoder2Template : public PromptTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QString name() const override { return "StarCoder2"; }
|
TemplateType type() const override { return TemplateType::Fim; }
|
||||||
|
QString name() const override { return "StarCoder2 FIM"; }
|
||||||
QString promptTemplate() const override { return "%1<fim_prefix>%2<fim_suffix>%3<fim_middle>"; }
|
QString promptTemplate() const override { return "%1<fim_prefix>%2<fim_suffix>%3<fim_middle>"; }
|
||||||
QStringList stopWords() const override
|
QStringList stopWords() const override
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user