Upgrade to version 0.3.0

new QML Chat
Qwen chat model
This commit is contained in:
Petr Mironychev 2024-10-14 00:09:08 +02:00
parent 5c98de7440
commit 0c045e65df
71 changed files with 1920 additions and 1070 deletions

View File

@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
list(APPEND CMAKE_PREFIX_PATH "/Users/palm1r/Qt/Qt Creator.app/Contents/Resources/lib/cmake/QtCreator")
project(QodeAssist) project(QodeAssist)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
@ -12,6 +14,9 @@ set(CMAKE_CXX_EXTENSIONS OFF)
find_package(QtCreator REQUIRED COMPONENTS Core) find_package(QtCreator REQUIRED COMPONENTS Core)
find_package(Qt6 COMPONENTS Core Gui Widgets Network REQUIRED) find_package(Qt6 COMPONENTS Core Gui Widgets Network REQUIRED)
add_subdirectory(llmcore)
add_subdirectory(settings)
add_subdirectory(logger)
add_subdirectory(chatview) add_subdirectory(chatview)
add_qtc_plugin(QodeAssist add_qtc_plugin(QodeAssist
@ -32,39 +37,25 @@ add_qtc_plugin(QodeAssist
QodeAssistConstants.hpp QodeAssistConstants.hpp
QodeAssisttr.h QodeAssisttr.h
LLMClientInterface.hpp LLMClientInterface.cpp LLMClientInterface.hpp LLMClientInterface.cpp
PromptTemplateManager.hpp PromptTemplateManager.cpp templates/CodeLlamaFim.hpp
templates/PromptTemplate.hpp templates/StarCoder2Fim.hpp
templates/CodeLlamaFimTemplate.hpp templates/DeepSeekCoderFim.hpp
templates/StarCoder2Template.hpp templates/CustomFimTemplate.hpp
templates/DeepSeekCoderV2.hpp templates/DeepSeekCoderChat.hpp
templates/CustomTemplate.hpp templates/CodeLlamaChat.hpp
templates/DeepSeekCoderChatTemplate.hpp templates/QwenChat.hpp
templates/CodeLlamaInstruct.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
providers/OpenAICompatProvider.hpp providers/OpenAICompatProvider.cpp providers/OpenAICompatProvider.hpp providers/OpenAICompatProvider.cpp
LLMProvidersManager.hpp LLMProvidersManager.cpp
QodeAssist.qrc QodeAssist.qrc
LSPCompletion.hpp LSPCompletion.hpp
LLMSuggestion.hpp LLMSuggestion.cpp LLMSuggestion.hpp LLMSuggestion.cpp
QodeAssistClient.hpp QodeAssistClient.cpp QodeAssistClient.hpp QodeAssistClient.cpp
QodeAssistUtils.hpp
DocumentContextReader.hpp DocumentContextReader.cpp DocumentContextReader.hpp DocumentContextReader.cpp
QodeAssistData.hpp
utils/CounterTooltip.hpp utils/CounterTooltip.cpp utils/CounterTooltip.hpp utils/CounterTooltip.cpp
settings/GeneralSettings.hpp settings/GeneralSettings.cpp
settings/ContextSettings.hpp settings/ContextSettings.cpp
settings/CustomPromptSettings.hpp settings/CustomPromptSettings.cpp
settings/PresetPromptsSettings.hpp settings/PresetPromptsSettings.cpp
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/ChatOutputPane.h chat/ChatOutputPane.cpp
chat/ChatClientInterface.hpp chat/ChatClientInterface.cpp
chat/NavigationPanel.hpp chat/NavigationPanel.cpp chat/NavigationPanel.hpp chat/NavigationPanel.cpp
) )
target_link_libraries(QodeAssist PUBLIC QodeAssistChatViewplugin) target_link_libraries(QodeAssist PRIVATE QodeAssistChatViewplugin)

View File

@ -135,13 +135,6 @@ QString DocumentContextReader::getLanguageAndFileInfo() const
.arg(language, mimeType, filePath, fileExtension); .arg(language, mimeType, filePath, fileExtension);
} }
QString DocumentContextReader::getSpecificInstructions() const
{
QString specificInstruction = Settings::contextSettings().specificInstractions().arg(
LanguageServerProtocol::TextDocumentItem::mimeTypeToLanguageId(m_textDocument->mimeType()));
return QString("%1").arg(specificInstruction);
}
CopyrightInfo DocumentContextReader::findCopyright() CopyrightInfo DocumentContextReader::findCopyright()
{ {
CopyrightInfo result = {-1, -1, false}; CopyrightInfo result = {-1, -1, false};
@ -210,7 +203,7 @@ CopyrightInfo DocumentContextReader::copyrightInfo() const
return m_copyrightInfo; return m_copyrightInfo;
} }
ContextData DocumentContextReader::prepareContext(int lineNumber, int cursorPosition) const LLMCore::ContextData DocumentContextReader::prepareContext(int lineNumber, int cursorPosition) const
{ {
QString contextBefore = getContextBefore(lineNumber, cursorPosition); QString contextBefore = getContextBefore(lineNumber, cursorPosition);
QString contextAfter = getContextAfter(lineNumber, cursorPosition); QString contextAfter = getContextAfter(lineNumber, cursorPosition);

View File

@ -22,7 +22,8 @@
#include <QTextDocument> #include <QTextDocument>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include "QodeAssistData.hpp" // #include "QodeAssistData.hpp"
#include <llmcore/ContextData.hpp>
namespace QodeAssist { namespace QodeAssist {
@ -44,13 +45,12 @@ public:
QString readWholeFileBefore(int lineNumber, int cursorPosition) const; QString readWholeFileBefore(int lineNumber, int cursorPosition) const;
QString readWholeFileAfter(int lineNumber, int cursorPosition) const; QString readWholeFileAfter(int lineNumber, int cursorPosition) const;
QString getLanguageAndFileInfo() const; QString getLanguageAndFileInfo() const;
QString getSpecificInstructions() const;
CopyrightInfo findCopyright(); CopyrightInfo findCopyright();
QString getContextBetween(int startLine, int endLine, int cursorPosition) const; QString getContextBetween(int startLine, int endLine, int cursorPosition) const;
CopyrightInfo copyrightInfo() const; CopyrightInfo copyrightInfo() const;
ContextData prepareContext(int lineNumber, int cursorPosition) const; LLMCore::ContextData prepareContext(int lineNumber, int cursorPosition) const;
private: private:
QString getContextBefore(int lineNumber, int cursorPosition) const; QString getContextBefore(int lineNumber, int cursorPosition) const;

View File

@ -23,13 +23,13 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <llmcore/RequestConfig.hpp>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include "DocumentContextReader.hpp" #include "DocumentContextReader.hpp"
#include "LLMProvidersManager.hpp" #include "logger/Logger.hpp"
#include "PromptTemplateManager.hpp" #include "llmcore/PromptTemplateManager.hpp"
#include "QodeAssistUtils.hpp" #include "llmcore/ProvidersManager.hpp"
#include "core/LLMRequestConfig.hpp"
#include "settings/ContextSettings.hpp" #include "settings/ContextSettings.hpp"
#include "settings/GeneralSettings.hpp" #include "settings/GeneralSettings.hpp"
@ -39,7 +39,7 @@ LLMClientInterface::LLMClientInterface()
: m_requestHandler(this) : m_requestHandler(this)
{ {
connect(&m_requestHandler, connect(&m_requestHandler,
&LLMRequestHandler::completionReceived, &LLMCore::RequestHandler::completionReceived,
this, this,
&LLMClientInterface::sendCompletionToClient); &LLMClientInterface::sendCompletionToClient);
} }
@ -80,7 +80,7 @@ void LLMClientInterface::sendData(const QByteArray &data)
} else if (method == "exit") { } else if (method == "exit") {
// TODO make exit handler // TODO make exit handler
} else { } else {
logMessage(QString("Unknown method: %1").arg(method)); LOG_MESSAGE(QString("Unknown method: %1").arg(method));
} }
} }
@ -88,9 +88,9 @@ void LLMClientInterface::handleCancelRequest(const QJsonObject &request)
{ {
QString id = request["params"].toObject()["id"].toString(); QString id = request["params"].toObject()["id"].toString();
if (m_requestHandler.cancelRequest(id)) { if (m_requestHandler.cancelRequest(id)) {
logMessage(QString("Request %1 cancelled successfully").arg(id)); LOG_MESSAGE(QString("Request %1 cancelled successfully").arg(id));
} else { } else {
logMessage(QString("Request %1 not found").arg(id)); LOG_MESSAGE(QString("Request %1 not found").arg(id));
} }
} }
@ -148,10 +148,10 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
{ {
auto updatedContext = prepareContext(request); auto updatedContext = prepareContext(request);
LLMConfig config; LLMCore::LLMConfig config;
config.requestType = RequestType::Fim; config.requestType = LLMCore::RequestType::Fim;
config.provider = LLMProvidersManager::instance().getCurrentFimProvider(); config.provider = LLMCore::ProvidersManager::instance().getCurrentFimProvider();
config.promptTemplate = PromptTemplateManager::instance().getCurrentFimTemplate(); config.promptTemplate = LLMCore::PromptTemplateManager::instance().getCurrentFimTemplate();
config.url = QUrl(QString("%1%2").arg(Settings::generalSettings().url(), config.url = QUrl(QString("%1%2").arg(Settings::generalSettings().url(),
Settings::generalSettings().endPoint())); Settings::generalSettings().endPoint()));
@ -159,17 +159,18 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
{"stream", true}, {"stream", true},
{"stop", {"stop",
QJsonArray::fromStringList(config.promptTemplate->stopWords())}}; QJsonArray::fromStringList(config.promptTemplate->stopWords())}};
config.multiLineCompletion = Settings::generalSettings().multiLineCompletion();
if (Settings::contextSettings().useSpecificInstructions()) if (Settings::contextSettings().useSystemPrompt())
config.providerRequest["system"] = Settings::contextSettings().specificInstractions(); config.providerRequest["system"] = Settings::contextSettings().systemPrompt();
config.promptTemplate->prepareRequest(config.providerRequest, updatedContext); config.promptTemplate->prepareRequest(config.providerRequest, updatedContext);
config.provider->prepareRequest(config.providerRequest); config.provider->prepareRequest(config.providerRequest, LLMCore::RequestType::Fim);
m_requestHandler.sendLLMRequest(config, request); m_requestHandler.sendLLMRequest(config, request);
} }
ContextData LLMClientInterface::prepareContext(const QJsonObject &request, LLMCore::ContextData LLMClientInterface::prepareContext(const QJsonObject &request,
const QStringView &accumulatedCompletion) const QStringView &accumulatedCompletion)
{ {
QJsonObject params = request["params"].toObject(); QJsonObject params = request["params"].toObject();
@ -182,8 +183,8 @@ ContextData LLMClientInterface::prepareContext(const QJsonObject &request,
filePath); filePath);
if (!textDocument) { if (!textDocument) {
logMessage("Error: Document is not available for" + filePath.toString()); LOG_MESSAGE("Error: Document is not available for" + filePath.toString());
return ContextData{}; return LLMCore::ContextData{};
} }
int cursorPosition = position["character"].toInt(); int cursorPosition = position["character"].toInt();
@ -218,11 +219,11 @@ void LLMClientInterface::sendCompletionToClient(const QString &completion,
result[LanguageServerProtocol::isIncompleteKey] = !isComplete; result[LanguageServerProtocol::isIncompleteKey] = !isComplete;
response[LanguageServerProtocol::resultKey] = result; response[LanguageServerProtocol::resultKey] = result;
logMessage( LOG_MESSAGE(
QString("Completions: \n%1") QString("Completions: \n%1")
.arg(QString::fromUtf8(QJsonDocument(completions).toJson(QJsonDocument::Indented)))); .arg(QString::fromUtf8(QJsonDocument(completions).toJson(QJsonDocument::Indented))));
logMessage(QString("Full response: \n%1") LOG_MESSAGE(QString("Full response: \n%1")
.arg(QString::fromUtf8(QJsonDocument(response).toJson(QJsonDocument::Indented)))); .arg(QString::fromUtf8(QJsonDocument(response).toJson(QJsonDocument::Indented))));
QString requestId = request["id"].toString(); QString requestId = request["id"].toString();
@ -250,7 +251,7 @@ void LLMClientInterface::logPerformance(const QString &requestId,
const QString &operation, const QString &operation,
qint64 elapsedMs) qint64 elapsedMs)
{ {
logMessage(QString("Performance: %1 %2 took %3 ms").arg(requestId, operation).arg(elapsedMs)); LOG_MESSAGE(QString("Performance: %1 %2 took %3 ms").arg(requestId, operation).arg(elapsedMs));
} }
void LLMClientInterface::parseCurrentMessage() {} void LLMClientInterface::parseCurrentMessage() {}

View File

@ -22,8 +22,10 @@
#include <languageclient/languageclientinterface.h> #include <languageclient/languageclientinterface.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include "QodeAssistData.hpp" // #include "QodeAssistData.hpp"
#include "core/LLMRequestHandler.hpp" // #include "core/LLMRequestHandler.hpp"
#include <llmcore/ContextData.hpp>
#include <llmcore/RequestHandler.hpp>
class QNetworkReply; class QNetworkReply;
class QNetworkAccessManager; class QNetworkAccessManager;
@ -58,10 +60,10 @@ private:
void handleExit(const QJsonObject &request); void handleExit(const QJsonObject &request);
void handleCancelRequest(const QJsonObject &request); void handleCancelRequest(const QJsonObject &request);
ContextData prepareContext(const QJsonObject &request, LLMCore::ContextData prepareContext(const QJsonObject &request,
const QStringView &accumulatedCompletion = QString{}); const QStringView &accumulatedCompletion = QString{});
LLMRequestHandler m_requestHandler; LLMCore::RequestHandler m_requestHandler;
QElapsedTimer m_completionTimer; QElapsedTimer m_completionTimer;
QMap<QString, qint64> m_requestStartTimes; QMap<QString, qint64> m_requestStartTimes;

View File

@ -24,60 +24,6 @@ namespace QodeAssist::Constants {
const char ACTION_ID[] = "QodeAssist.Action"; const char ACTION_ID[] = "QodeAssist.Action";
const char MENU_ID[] = "QodeAssist.Menu"; const char MENU_ID[] = "QodeAssist.Menu";
// settings
const char ENABLE_QODE_ASSIST[] = "QodeAssist.enableQodeAssist";
const char ENABLE_AUTO_COMPLETE[] = "QodeAssist.enableAutoComplete";
const char ENABLE_LOGGING[] = "QodeAssist.enableLogging";
const char LLM_PROVIDERS[] = "QodeAssist.llmProviders";
const char URL[] = "QodeAssist.url";
const char END_POINT[] = "QodeAssist.endPoint";
const char MODEL_NAME[] = "QodeAssist.modelName";
const char SELECT_MODELS[] = "QodeAssist.selectModels";
const char FIM_PROMPTS[] = "QodeAssist.fimPrompts";
const char TEMPERATURE[] = "QodeAssist.temperature";
const char MAX_TOKENS[] = "QodeAssist.maxTokens";
const char READ_FULL_FILE[] = "QodeAssist.readFullFile";
const char READ_STRINGS_BEFORE_CURSOR[] = "QodeAssist.readStringsBeforeCursor";
const char READ_STRINGS_AFTER_CURSOR[] = "QodeAssist.readStringsAfterCursor";
const char USE_TOP_P[] = "QodeAssist.useTopP";
const char TOP_P[] = "QodeAssist.topP";
const char USE_TOP_K[] = "QodeAssist.useTopK";
const char TOP_K[] = "QodeAssist.topK";
const char USE_PRESENCE_PENALTY[] = "QodeAssist.usePresencePenalty";
const char PRESENCE_PENALTY[] = "QodeAssist.presencePenalty";
const char USE_FREQUENCY_PENALTY[] = "QodeAssist.useFrequencyPenalty";
const char FREQUENCY_PENALTY[] = "QodeAssist.frequencyPenalty";
const char PROVIDER_PATHS[] = "QodeAssist.providerPaths";
const char START_SUGGESTION_TIMER[] = "QodeAssist.startSuggestionTimer";
const char AUTO_COMPLETION_CHAR_THRESHOLD[] = "QodeAssist.autoCompletionCharThreshold";
const char AUTO_COMPLETION_TYPING_INTERVAL[] = "QodeAssist.autoCompletionTypingInterval";
const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
const char OLLAMA_LIVETIME[] = "QodeAssist.ollamaLivetime";
const char SYSTEM_PROMPT[] = "QodeAssist.systemPrompt";
const char MULTILINE_COMPLETION[] = "QodeAssist.multilineCompletion";
const char API_KEY[] = "QodeAssist.apiKey";
const char USE_SYSTEM_PROMPT[] = "QodeAssist.useSystemPrompt";
const char USE_FILE_PATH_IN_CONTEXT[] = "QodeAssist.useFilePathInContext";
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
const char USE_PROJECT_CHANGES_CACHE[] = "QodeAssist.useProjectChangesCache";
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_SETTINGS_PAGE_ID[] = "QodeAssist.1GeneralSettingsPageId";
const char QODE_ASSIST_CONTEXT_SETTINGS_PAGE_ID[] = "QodeAssist.2ContextSettingsPageId";
const char QODE_ASSIST_PRESET_PROMPTS_SETTINGS_PAGE_ID[]
= "QodeAssist.3PresetPromptsSettingsPageId";
const char QODE_ASSIST_CUSTOM_PROMPT_SETTINGS_PAGE_ID[] = "QodeAssist.4CustomPromptSettingsPageId";
const char QODE_ASSIST_GENERAL_OPTIONS_CATEGORY[] = "QodeAssist.Category";
const char QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY[] = "Qode Assist";
const char QODE_ASSIST_REQUEST_SUGGESTION[] = "QodeAssist.RequestSuggestion"; const char QODE_ASSIST_REQUEST_SUGGESTION[] = "QodeAssist.RequestSuggestion";
} // namespace QodeAssist::Constants } // namespace QodeAssist::Constants

View File

@ -1,107 +0,0 @@
/*
* 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 <QEventLoop>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QString>
#include <QTimer>
#include <QUrl>
#include <coreplugin/messagemanager.h>
#include <utils/qtcassert.h>
namespace QodeAssist {
inline bool &loggingEnabled()
{
static bool enabled = false;
return enabled;
}
inline void setLoggingEnabled(bool enable)
{
loggingEnabled() = enable;
}
inline void logMessage(const QString &message, bool silent = true)
{
if (!loggingEnabled())
return;
const QString prefixedMessage = QLatin1String("[Qode Assist] ") + message;
if (silent) {
Core::MessageManager::writeSilently(prefixedMessage);
} else {
Core::MessageManager::writeFlashing(prefixedMessage);
}
}
inline void logMessages(const QStringList &messages, bool silent = true)
{
if (!loggingEnabled())
return;
QStringList prefixedMessages;
qDebug() << prefixedMessages;
for (const QString &message : messages) {
prefixedMessages << (QLatin1String("[Qode Assist] ") + message);
}
if (silent) {
Core::MessageManager::writeSilently(prefixedMessages);
} else {
Core::MessageManager::writeFlashing(prefixedMessages);
}
}
inline bool pingUrl(const QUrl &url, int timeout = 5000)
{
if (!url.isValid()) {
return false;
}
QNetworkAccessManager manager;
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, true);
QScopedPointer<QNetworkReply> reply(manager.get(request));
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
QObject::connect(reply.data(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
timer.start(timeout);
loop.exec();
if (timer.isActive()) {
timer.stop();
return (reply->error() == QNetworkReply::NoError);
} else {
QObject::disconnect(reply.data(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
reply->abort();
return false;
}
}
} // namespace QodeAssist

View File

@ -1,192 +0,0 @@
/*
* 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 {
int ChatHistory::estimateTokenCount(const QString &text) const
{
return text.length() / 4;
}
void ChatHistory::addMessage(ChatMessage::Role role, const QString &content)
{
int tokenCount = estimateTokenCount(content);
m_messages.append({role, content, tokenCount});
m_totalTokens += tokenCount;
trim();
}
void ChatHistory::clear()
{
m_messages.clear();
m_totalTokens = 0;
}
QVector<ChatMessage> ChatHistory::getMessages() const
{
return m_messages;
}
QString ChatHistory::getSystemPrompt() const
{
return m_systemPrompt;
}
void ChatHistory::setSystemPrompt(const QString &prompt)
{
m_systemPrompt = prompt;
}
void ChatHistory::trim()
{
while (m_messages.size() > MAX_HISTORY_SIZE || m_totalTokens > MAX_TOKENS) {
if (!m_messages.isEmpty()) {
m_totalTokens -= m_messages.first().tokenCount;
m_messages.removeFirst();
} else {
break;
}
}
}
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);
}
});
m_chatHistory.setSystemPrompt("You are a helpful C++ and QML programming assistant.");
}
ChatClientInterface::~ChatClientInterface() = default;
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"] = prepareMessagesForRequest();
chatTemplate->prepareRequest(providerRequest, context);
chatProvider->prepareRequest(providerRequest);
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_chatHistory.addMessage(ChatMessage::Role::User, message);
m_requestHandler->sendLLMRequest(config, request);
}
void ChatClientInterface::clearMessages()
{
m_chatHistory.clear();
m_accumulatedResponse.clear();
logMessage("Chat history cleared");
}
QVector<ChatMessage> ChatClientInterface::getChatHistory() const
{
return m_chatHistory.getMessages();
}
void ChatClientInterface::handleLLMResponse(const QString &response, bool isComplete)
{
m_accumulatedResponse += response;
if (isComplete) {
logMessage("Message completed. Final response: " + m_accumulatedResponse);
emit messageReceived(m_accumulatedResponse.trimmed());
m_chatHistory.addMessage(ChatMessage::Role::Assistant, m_accumulatedResponse.trimmed());
m_accumulatedResponse.clear();
}
}
QJsonArray ChatClientInterface::prepareMessagesForRequest() const
{
QJsonArray messages;
messages.append(QJsonObject{{"role", "system"}, {"content", m_chatHistory.getSystemPrompt()}});
for (const auto &message : m_chatHistory.getMessages()) {
QString role;
switch (message.role) {
case ChatMessage::Role::User:
role = "user";
break;
case ChatMessage::Role::Assistant:
role = "assistant";
break;
default:
continue;
}
messages.append(QJsonObject{{"role", role}, {"content", message.content}});
}
return messages;
}
} // namespace QodeAssist::Chat

View File

@ -19,7 +19,7 @@
#pragma once #pragma once
#include "ChatWidget.h" #include "chatview/ChatWidget.hpp"
#include <coreplugin/ioutputpane.h> #include <coreplugin/ioutputpane.h>
namespace QodeAssist::Chat { namespace QodeAssist::Chat {

View File

@ -1,147 +0,0 @@
/*
* 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)
{
updateLastAIMessage(message);
}
void ChatWidget::receivePartialMessage(const QString &partialMessage)
{
logMessage("Received partial message: " + partialMessage);
m_currentAIResponse += partialMessage;
updateLastAIMessage(m_currentAIResponse);
}
void ChatWidget::onMessageCompleted()
{
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;
m_chatDisplay->append(fullMessage);
scrollToBottom();
}
void ChatWidget::updateLastAIMessage(const QString &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();
m_chatClient->clearMessages();
}
void ChatWidget::scrollToBottom()
{
QScrollBar *scrollBar = m_chatDisplay->verticalScrollBar();
scrollBar->setValue(scrollBar->maximum());
}
void ChatWidget::setShowTimestamp(bool show)
{
m_showTimestamp = show;
}
} // namespace QodeAssist::Chat

View File

@ -1,63 +0,0 @@
/*
* 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

View File

@ -19,7 +19,7 @@
#include "NavigationPanel.hpp" #include "NavigationPanel.hpp"
#include "chatview/BaseChatWidget.hpp" #include "chatview/ChatWidget.hpp"
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
@ -37,7 +37,7 @@ NavigationPanel::~NavigationPanel()
Core::NavigationView NavigationPanel::createWidget() Core::NavigationView NavigationPanel::createWidget()
{ {
Core::NavigationView view; Core::NavigationView view;
view.widget = new BaseChatWidget; view.widget = new ChatWidget;
return view; return view;
} }

View File

@ -1,24 +1,33 @@
qt_add_library(QodeAssistChatView STATIC) qt_add_library(QodeAssistChatView STATIC)
find_package(Qt6 COMPONENTS Core Widgets Quick QuickWidgets Network REQUIRED)
qt_add_qml_module(QodeAssistChatView qt_add_qml_module(QodeAssistChatView
URI ChatView URI ChatView
VERSION 1.0 VERSION 1.0
QML_FILES QML_FILES
qml/RootItem.qml qml/RootItem.qml
qml/ChatItem.qml
qml/Badge.qml
qml/dialog/CodeBlock.qml
qml/dialog/TextBlock.qml
SOURCES SOURCES
BaseChatWidget.hpp BaseChatWidget.cpp ChatWidget.hpp ChatWidget.cpp
ChatModel.hpp ChatModel.cpp ChatModel.hpp ChatModel.cpp
ChatRootView.hpp ChatRootView.cpp ChatRootView.hpp ChatRootView.cpp
ClientInterface.hpp ClientInterface.cpp
MessagePart.hpp
ChatUtils.h ChatUtils.cpp
) )
target_link_libraries(QodeAssistChatView target_link_libraries(QodeAssistChatView
PRIVATE PUBLIC
Qt::Widgets Qt::Widgets
Qt::Quick Qt::Quick
Qt::QuickWidgets Qt::QuickWidgets
Qt::Network Qt::Network
QtCreator::Core
QtCreator::Utils
LLMCore
QodeAssistSettings
) )
target_include_directories(QodeAssistChatView target_include_directories(QodeAssistChatView

View File

@ -18,12 +18,24 @@
*/ */
#include "ChatModel.hpp" #include "ChatModel.hpp"
#include <QtCore/qjsonobject.h>
#include <QtQml>
#include <utils/aspects.h>
#include "GeneralSettings.hpp"
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
ChatModel::ChatModel(QObject *parent) ChatModel::ChatModel(QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
, m_totalTokens(0)
{ {
auto &settings = Settings::generalSettings();
connect(&settings.chatTokensThreshold,
&Utils::BaseAspect::changed,
this,
&ChatModel::tokensThresholdChanged);
} }
int ChatModel::rowCount(const QModelIndex &parent) const int ChatModel::rowCount(const QModelIndex &parent) const
@ -40,8 +52,9 @@ QVariant ChatModel::data(const QModelIndex &index, int role) const
switch (static_cast<Roles>(role)) { switch (static_cast<Roles>(role)) {
case Roles::RoleType: case Roles::RoleType:
return QVariant::fromValue(message.role); return QVariant::fromValue(message.role);
case Roles::Content: case Roles::Content: {
return message.content; return message.content;
}
default: default:
return QVariant(); return QVariant();
} }
@ -55,18 +68,112 @@ QHash<int, QByteArray> ChatModel::roleNames() const
return roles; return roles;
} }
QVector<ChatModel::Message> ChatModel::getChatHistory() const
{
return m_messages;
}
void ChatModel::trim()
{
while (m_totalTokens > tokensThreshold()) {
if (!m_messages.isEmpty()) {
m_totalTokens -= m_messages.first().tokenCount;
beginRemoveRows(QModelIndex(), 0, 0);
m_messages.removeFirst();
endRemoveRows();
} else {
break;
}
}
}
int ChatModel::estimateTokenCount(const QString &text) const
{
return text.length() / 4;
}
void ChatModel::addMessage(const QString &content, ChatRole role) void ChatModel::addMessage(const QString &content, ChatRole role)
{ {
int tokenCount = estimateTokenCount(content);
beginInsertRows(QModelIndex(), m_messages.size(), m_messages.size()); beginInsertRows(QModelIndex(), m_messages.size(), m_messages.size());
m_messages.append({role, content}); m_messages.append({role, content, tokenCount});
m_totalTokens += tokenCount;
endInsertRows(); endInsertRows();
trim();
emit totalTokensChanged();
} }
void ChatModel::clear() void ChatModel::clear()
{ {
beginResetModel(); beginResetModel();
m_messages.clear(); m_messages.clear();
m_totalTokens = 0;
endResetModel(); endResetModel();
emit totalTokensChanged();
}
QList<MessagePart> ChatModel::processMessageContent(const QString &content) const
{
QList<MessagePart> parts;
QRegularExpression codeBlockRegex("```(\\w*)\\n?([\\s\\S]*?)```");
int lastIndex = 0;
auto blockMatches = codeBlockRegex.globalMatch(content);
while (blockMatches.hasNext()) {
auto match = blockMatches.next();
if (match.capturedStart() > lastIndex) {
QString textBetween = content.mid(lastIndex, match.capturedStart() - lastIndex).trimmed();
if (!textBetween.isEmpty()) {
parts.append({MessagePart::Text, textBetween, ""});
}
}
parts.append({MessagePart::Code, match.captured(2).trimmed(), match.captured(1)});
lastIndex = match.capturedEnd();
}
if (lastIndex < content.length()) {
QString remainingText = content.mid(lastIndex).trimmed();
if (!remainingText.isEmpty()) {
parts.append({MessagePart::Text, remainingText, ""});
}
}
return parts;
}
QJsonArray ChatModel::prepareMessagesForRequest(LLMCore::ContextData context) const
{
QJsonArray messages;
messages.append(QJsonObject{{"role", "system"}, {"content", context.systemPrompt}});
for (const auto &message : m_messages) {
QString role;
switch (message.role) {
case ChatRole::User:
role = "user";
break;
case ChatRole::Assistant:
role = "assistant";
break;
default:
continue;
}
messages.append(QJsonObject{{"role", role}, {"content", message.content}});
}
return messages;
}
int ChatModel::totalTokens() const
{
return m_totalTokens;
}
int ChatModel::tokensThreshold() const
{
auto &settings = Settings::generalSettings();
return settings.chatTokensThreshold();
} }
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat

View File

@ -19,25 +19,35 @@
#pragma once #pragma once
#include "ContextData.hpp"
#include "MessagePart.hpp"
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QJsonArray>
#include <qqmlintegration.h>
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
enum class ChatRole { System, User, Assistant }; class ChatModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int totalTokens READ totalTokens NOTIFY totalTokensChanged FINAL)
Q_PROPERTY(int tokensThreshold READ tokensThreshold NOTIFY tokensThresholdChanged FINAL)
QML_ELEMENT
public:
enum Roles { RoleType = Qt::UserRole, Content };
enum ChatRole { System, User, Assistant };
Q_ENUM(ChatRole)
struct Message struct Message
{ {
ChatRole role; ChatRole role;
QString content; QString content;
int tokenCount;
}; };
class ChatModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles { RoleType = Qt::UserRole, Content };
explicit ChatModel(QObject *parent = nullptr); explicit ChatModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@ -46,11 +56,28 @@ public:
Q_INVOKABLE void addMessage(const QString &content, ChatRole role); Q_INVOKABLE void addMessage(const QString &content, ChatRole role);
Q_INVOKABLE void clear(); Q_INVOKABLE void clear();
Q_INVOKABLE QList<MessagePart> processMessageContent(const QString &content) const;
QVector<Message> getChatHistory() const;
QJsonArray prepareMessagesForRequest(LLMCore::ContextData context) const;
int totalTokens() const;
int tokensThreshold() const;
QString currentModel() const;
signals:
void totalTokensChanged();
void tokensThresholdChanged();
private: private:
void trim();
int estimateTokenCount(const QString &text) const;
QVector<Message> m_messages; QVector<Message> m_messages;
int m_totalTokens = 0;
}; };
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat
Q_DECLARE_METATYPE(QodeAssist::Chat::ChatModel::Message)
Q_DECLARE_METATYPE(QodeAssist::Chat::ChatRole) Q_DECLARE_METATYPE(QodeAssist::Chat::MessagePart)

View File

@ -18,17 +18,110 @@
*/ */
#include "ChatRootView.hpp" #include "ChatRootView.hpp"
#include <QtGui/qclipboard.h>
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
#include "GeneralSettings.hpp"
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
ChatRootView::ChatRootView(QQuickItem *parent) ChatRootView::ChatRootView(QQuickItem *parent)
: QQuickItem(parent) : QQuickItem(parent)
, m_chatModel(new ChatModel(this)) , m_chatModel(new ChatModel(this))
{} , m_clientInterface(new ClientInterface(m_chatModel, this))
{
auto &settings = Settings::generalSettings();
connect(&settings.chatModelName,
&Utils::BaseAspect::changed,
this,
&ChatRootView::currentTemplateChanged);
generateColors();
}
ChatModel *ChatRootView::chatModel() const ChatModel *ChatRootView::chatModel() const
{ {
return m_chatModel; return m_chatModel;
} }
QColor ChatRootView::backgroundColor() const
{
return Utils::creatorColor(Utils::Theme::BackgroundColorNormal);
}
void ChatRootView::sendMessage(const QString &message) const
{
m_clientInterface->sendMessage(message);
}
void ChatRootView::copyToClipboard(const QString &text)
{
QGuiApplication::clipboard()->setText(text);
}
void ChatRootView::generateColors()
{
QColor baseColor = backgroundColor();
bool isDarkTheme = baseColor.lightness() < 128;
if (isDarkTheme) {
m_primaryColor = generateColor(baseColor, 0.1, 1.2, 1.4);
m_secondaryColor = generateColor(baseColor, -0.1, 1.1, 1.2);
m_codeColor = generateColor(baseColor, 0.05, 0.8, 1.1);
} else {
m_primaryColor = generateColor(baseColor, 0.05, 1.05, 1.1);
m_secondaryColor = generateColor(baseColor, -0.05, 1.1, 1.2);
m_codeColor = generateColor(baseColor, 0.02, 0.95, 1.05);
}
}
QColor ChatRootView::generateColor(const QColor &baseColor,
float hueShift,
float saturationMod,
float lightnessMod)
{
float h, s, l, a;
baseColor.getHslF(&h, &s, &l, &a);
bool isDarkTheme = l < 0.5;
h = fmod(h + hueShift + 1.0, 1.0);
s = qBound(0.0f, s * saturationMod, 1.0f);
if (isDarkTheme) {
l = qBound(0.0f, l * lightnessMod, 1.0f);
} else {
l = qBound(0.0f, l / lightnessMod, 1.0f);
}
h = qBound(0.0f, h, 1.0f);
s = qBound(0.0f, s, 1.0f);
l = qBound(0.0f, l, 1.0f);
a = qBound(0.0f, a, 1.0f);
return QColor::fromHslF(h, s, l, a);
}
QString ChatRootView::currentTemplate() const
{
auto &settings = Settings::generalSettings();
return settings.chatModelName();
}
QColor ChatRootView::primaryColor() const
{
return m_primaryColor;
}
QColor ChatRootView::secondaryColor() const
{
return m_secondaryColor;
}
QColor ChatRootView::codeColor() const
{
return m_codeColor;
}
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat

View File

@ -22,25 +22,54 @@
#include <QQuickItem> #include <QQuickItem>
#include "ChatModel.hpp" #include "ChatModel.hpp"
#include "ClientInterface.hpp"
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
class ChatRootView : public QQuickItem class ChatRootView : public QQuickItem
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(ChatModel *chatModel READ chatModel NOTIFY chatModelChanged FINAL)
Q_PROPERTY(QString currentTemplate READ currentTemplate NOTIFY currentTemplateChanged FINAL)
Q_PROPERTY(QColor backgroundColor READ backgroundColor CONSTANT FINAL)
Q_PROPERTY(QColor primaryColor READ primaryColor CONSTANT FINAL)
Q_PROPERTY(QColor secondaryColor READ secondaryColor CONSTANT FINAL)
Q_PROPERTY(QColor codeColor READ codeColor CONSTANT FINAL)
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(ChatModel *chatModel READ chatModel NOTIFY chatModelChanged FINAL)
public: public:
ChatRootView(QQuickItem *parent = nullptr); ChatRootView(QQuickItem *parent = nullptr);
ChatModel *chatModel() const; ChatModel *chatModel() const;
QString currentTemplate() const;
QColor backgroundColor() const;
QColor primaryColor() const;
QColor secondaryColor() const;
QColor codeColor() const;
public slots:
void sendMessage(const QString &message) const;
void copyToClipboard(const QString &text);
signals: signals:
void chatModelChanged(); void chatModelChanged();
void currentTemplateChanged();
private: private:
ChatModel *m_chatModel = nullptr; void generateColors();
QColor generateColor(const QColor &baseColor,
float hueShift,
float saturationMod,
float lightnessMod);
ChatModel *m_chatModel;
ClientInterface *m_clientInterface;
QString m_currentTemplate;
QColor m_primaryColor;
QColor m_secondaryColor;
QColor m_codeColor;
}; };
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat

14
chatview/ChatUtils.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "ChatUtils.h"
#include <QClipboard>
#include <QGuiApplication>
namespace QodeAssist::Chat {
void ChatUtils::copyToClipboard(const QString &text)
{
qDebug() << "call clipboard" << text;
QGuiApplication::clipboard()->setText(text);
}
} // namespace QodeAssist::Chat

21
chatview/ChatUtils.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <qobject.h>
#include <qqmlintegration.h>
namespace QodeAssist::Chat {
// Q_NAMESPACE
class ChatUtils : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(ChatUtils)
public:
explicit ChatUtils(QObject *parent = nullptr)
: QObject(parent) {};
Q_INVOKABLE void copyToClipboard(const QString &text);
};
} // namespace QodeAssist::Chat

View File

@ -17,19 +17,27 @@
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>. * along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "BaseChatWidget.hpp" #include "ChatWidget.hpp"
#include <QQmlEngine>
#include <QQmlContext> #include <QQmlContext>
#include <QQmlEngine>
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
BaseChatWidget::BaseChatWidget(QWidget *parent) : QQuickWidget(parent) ChatWidget::ChatWidget(QWidget *parent)
: QQuickWidget(parent)
{ {
setSource(QUrl("qrc:/ChatView/qml/RootItem.qml")); setSource(QUrl("qrc:/ChatView/qml/RootItem.qml"));
setResizeMode(QQuickWidget::SizeRootObjectToView); setResizeMode(QQuickWidget::SizeRootObjectToView);
engine()->rootContext()->setContextObject(this);
} }
void ChatWidget::clear()
{
QMetaObject::invokeMethod(rootObject(), "clearChat");
}
void ChatWidget::scrollToBottom()
{
QMetaObject::invokeMethod(rootObject(), "scrollToBottom");
}
} }

View File

@ -23,13 +23,19 @@
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
class BaseChatWidget : public QQuickWidget class ChatWidget : public QQuickWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit BaseChatWidget(QWidget *parent = nullptr); explicit ChatWidget(QWidget *parent = nullptr);
~BaseChatWidget() = default; ~ChatWidget() = default;
Q_INVOKABLE void clear();
Q_INVOKABLE void scrollToBottom();
signals:
void clearPressed();
}; };
} }

View File

@ -0,0 +1,117 @@
/*
* 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 "ClientInterface.hpp"
#include "ContextSettings.hpp"
#include "GeneralSettings.hpp"
#include "Logger.hpp"
#include "PromptTemplateManager.hpp"
#include "ProvidersManager.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QUuid>
namespace QodeAssist::Chat {
ClientInterface::ClientInterface(ChatModel *chatModel, QObject *parent)
: QObject(parent)
, m_requestHandler(new LLMCore::RequestHandler(this))
, m_chatModel(chatModel)
{
connect(m_requestHandler,
&LLMCore::RequestHandler::completionReceived,
this,
[this](const QString &completion, const QJsonObject &, bool isComplete) {
handleLLMResponse(completion, isComplete);
});
connect(m_requestHandler,
&LLMCore::RequestHandler::requestFinished,
this,
[this](const QString &, bool success, const QString &errorString) {
if (!success) {
emit errorOccurred(errorString);
}
});
}
ClientInterface::~ClientInterface() = default;
void ClientInterface::sendMessage(const QString &message)
{
LOG_MESSAGE("Sending message: " + message);
LOG_MESSAGE("chatProvider " + Settings::generalSettings().chatLlmProviders.stringValue());
LOG_MESSAGE("chatTemplate " + Settings::generalSettings().chatPrompts.stringValue());
auto chatTemplate = LLMCore::PromptTemplateManager::instance().getCurrentChatTemplate();
auto chatProvider = LLMCore::ProvidersManager::instance().getCurrentChatProvider();
LLMCore::ContextData context;
context.prefix = message;
context.suffix = "";
if (Settings::contextSettings().useChatSystemPrompt())
context.systemPrompt = Settings::contextSettings().chatSystemPrompt();
QJsonObject providerRequest;
providerRequest["model"] = Settings::generalSettings().chatModelName();
providerRequest["stream"] = true;
providerRequest["messages"] = m_chatModel->prepareMessagesForRequest(context);
chatTemplate->prepareRequest(providerRequest, context);
chatProvider->prepareRequest(providerRequest, LLMCore::RequestType::Chat);
LLMCore::LLMConfig config;
config.requestType = LLMCore::RequestType::Chat;
config.provider = chatProvider;
config.promptTemplate = chatTemplate;
config.url = QString("%1%2").arg(Settings::generalSettings().chatUrl(),
Settings::generalSettings().chatEndPoint());
config.providerRequest = providerRequest;
config.multiLineCompletion = Settings::generalSettings().multiLineCompletion();
QJsonObject request;
request["id"] = QUuid::createUuid().toString();
m_accumulatedResponse.clear();
m_chatModel->addMessage(message, ChatModel::ChatRole::User);
m_requestHandler->sendLLMRequest(config, request);
}
void ClientInterface::clearMessages()
{
m_chatModel->clear();
m_accumulatedResponse.clear();
LOG_MESSAGE("Chat history cleared");
}
void ClientInterface::handleLLMResponse(const QString &response, bool isComplete)
{
m_accumulatedResponse += response;
if (isComplete) {
LOG_MESSAGE("Message completed. Final response: " + m_accumulatedResponse);
emit messageReceived(m_accumulatedResponse.trimmed());
m_chatModel->addMessage(m_accumulatedResponse.trimmed(), ChatModel::ChatRole::Assistant);
m_accumulatedResponse.clear();
}
}
} // namespace QodeAssist::Chat

View File

@ -22,50 +22,22 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include "QodeAssistData.hpp"
#include "core/LLMRequestHandler.hpp" #include "ChatModel.hpp"
#include "RequestHandler.hpp"
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
struct ChatMessage class ClientInterface : public QObject
{
enum class Role { System, User, Assistant };
Role role;
QString content;
int tokenCount;
};
class ChatHistory
{
public:
void addMessage(ChatMessage::Role role, const QString &content);
void clear();
QVector<ChatMessage> getMessages() const;
QString getSystemPrompt() const;
void setSystemPrompt(const QString &prompt);
void trim();
private:
QVector<ChatMessage> m_messages;
QString m_systemPrompt;
int m_totalTokens = 0;
static const int MAX_HISTORY_SIZE = 50;
static const int MAX_TOKENS = 4000;
int estimateTokenCount(const QString &text) const;
};
class ChatClientInterface : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ChatClientInterface(QObject *parent = nullptr); explicit ClientInterface(ChatModel *chatModel, QObject *parent = nullptr);
~ChatClientInterface(); ~ClientInterface();
void sendMessage(const QString &message); void sendMessage(const QString &message);
void clearMessages(); void clearMessages();
QVector<ChatMessage> getChatHistory() const;
signals: signals:
void messageReceived(const QString &message); void messageReceived(const QString &message);
@ -73,11 +45,10 @@ signals:
private: private:
void handleLLMResponse(const QString &response, bool isComplete); void handleLLMResponse(const QString &response, bool isComplete);
QJsonArray prepareMessagesForRequest() const;
LLMRequestHandler *m_requestHandler; LLMCore::RequestHandler *m_requestHandler;
QString m_accumulatedResponse; QString m_accumulatedResponse;
ChatHistory m_chatHistory; ChatModel *m_chatModel;
}; };
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat

51
chatview/MessagePart.hpp Normal file
View File

@ -0,0 +1,51 @@
/*
* 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.h>
#include <qqmlintegration.h>
namespace QodeAssist::Chat {
Q_NAMESPACE
class MessagePart
{
Q_GADGET
Q_PROPERTY(PartType type MEMBER type CONSTANT FINAL)
Q_PROPERTY(QString text MEMBER text CONSTANT FINAL)
Q_PROPERTY(QString language MEMBER language CONSTANT FINAL)
QML_VALUE_TYPE(messagePart)
public:
enum PartType { Code, Text };
Q_ENUM(PartType)
PartType type;
QString text;
QString language;
};
class MessagePartType : public MessagePart
{
Q_GADGET
};
QML_NAMED_ELEMENT(MessagePart)
QML_FOREIGN_NAMESPACE(QodeAssist::Chat::MessagePartType)
} // namespace QodeAssist::Chat

40
chatview/qml/Badge.qml Normal file
View 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/>.
*/
import QtQuick
Rectangle {
id: root
property alias text: badgeText.text
property alias fontColor: badgeText.color
width: badgeText.implicitWidth + radius
height: badgeText.implicitHeight + 6
color: "lightgreen"
radius: height / 2
border.width: 1
border.color: "gray"
Text {
id: badgeText
anchors.centerIn: parent
}
}

91
chatview/qml/ChatItem.qml Normal file
View File

@ -0,0 +1,91 @@
/*
* 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/>.
*/
import QtQuick
import QtQuick.Controls
import ChatView
import "./dialog"
Rectangle {
id: root
property alias msgModel: msgCreator.model
property color fontColor
property color codeBgColor
property color selectionColor
height: msgColumn.height
radius: 8
Column {
id: msgColumn
anchors.verticalCenter: parent.verticalCenter
width: parent.width
spacing: 5
Repeater {
id: msgCreator
delegate: Loader {
property var itemData: modelData
width: parent.width
sourceComponent: {
switch(modelData.type) {
case MessagePart.Text: return textComponent;
case MessagePart.Code: return codeBlockComponent;
default: return textComponent;
}
}
}
}
}
Component {
id: textComponent
TextBlock {
height: implicitHeight + 10
verticalAlignment: Text.AlignVCenter
leftPadding: 10
text: itemData.text
color: fontColor
selectionColor: root.selectionColor
}
}
Component {
id: codeBlockComponent
CodeBlock {
anchors {
left: parent.left
leftMargin: 10
right: parent.right
rightMargin: 10
}
code: itemData.text
language: itemData.language
color: root.codeBgColor
selectionColor: root.selectionColor
}
}
}

View File

@ -1,4 +1,26 @@
/*
* 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/>.
*/
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic as QQC
import QtQuick.Layouts
import ChatView import ChatView
ChatRootView { ChatRootView {
@ -6,8 +28,134 @@ ChatRootView {
Rectangle { Rectangle {
id: bg id: bg
anchors.fill: parent anchors.fill: parent
color: "gray" color: root.backgroundColor
}
ColumnLayout {
anchors {
fill: parent
}
spacing: 10
ListView {
id: chatListView
Layout.fillWidth: true
Layout.fillHeight: true
leftMargin: 5
model: root.chatModel
clip: true
spacing: 10
boundsBehavior: Flickable.StopAtBounds
cacheBuffer: 2000
delegate: ChatItem {
width: ListView.view.width - scroll.width
msgModel: root.chatModel.processMessageContent(model.content)
color: model.roleType === ChatModel.User ? root.primaryColor : root.secondaryColor
fontColor: root.primaryColor.hslLightness > 0.5 ? "black" : "white"
codeBgColor: root.codeColor
selectionColor: root.primaryColor.hslLightness > 0.5 ? Qt.darker(root.primaryColor, 1.5)
: Qt.lighter(root.primaryColor, 1.5)
}
header: Item {
width: ListView.view.width - scroll.width
height: 30
}
ScrollBar.vertical: ScrollBar {
id: scroll
}
onCountChanged: {
scrollToBottom()
}
onContentHeightChanged: {
if (atYEnd) {
scrollToBottom()
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: 5
QQC.TextField {
id: messageInput
Layout.fillWidth: true
Layout.minimumWidth: 60
Layout.minimumHeight: 30
rightInset: -(parent.width - sendButton.width - clearButton.width)
placeholderText: qsTr("Type your message here...")
placeholderTextColor: "#888"
color: root.primaryColor.hslLightness > 0.5 ? "black" : "white"
background: Rectangle {
radius: 2
color: root.primaryColor
border.color: root.primaryColor.hslLightness > 0.5 ? Qt.lighter(root.primaryColor, 1.5)
: Qt.darker(root.primaryColor, 1.5)
border.width: 1
}
onAccepted: sendButton.clicked()
}
Button {
id: sendButton
Layout.alignment: Qt.AlignVCenter
Layout.minimumHeight: 30
text: qsTr("Send")
onClicked: {
if (messageInput.text.trim() !== "") {
root.sendMessage(messageInput.text);
messageInput.text = ""
scrollToBottom()
}
}
}
Button {
id: clearButton
Layout.alignment: Qt.AlignVCenter
Layout.minimumHeight: 30
text: qsTr("Clear")
onClicked: clearChat()
}
}
}
Row {
id: bar
layoutDirection: Qt.RightToLeft
anchors {
left: parent.left
leftMargin: 5
right: parent.right
rightMargin: scroll.width
}
spacing: 10
Badge {
text: "%1/%2".arg(root.chatModel.totalTokens).arg(root.chatModel.tokensThreshold)
color: root.codeColor
fontColor: root.primaryColor.hslLightness > 0.5 ? "black" : "white"
}
}
function clearChat() {
root.chatModel.clear()
}
function scrollToBottom() {
Qt.callLater(chatListView.positionViewAtEnd)
} }
} }

View File

@ -0,0 +1,86 @@
/*
* 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/>.
*/
import QtQuick
import QtQuick.Controls
import ChatView
Rectangle {
id: root
property string code: ""
property string language: ""
property color selectionColor
border.color: root.color.hslLightness > 0.5 ? Qt.darker(root.color, 1.3)
: Qt.lighter(root.color, 1.3)
border.width: 2
radius: 4
implicitWidth: parent.width
implicitHeight: codeText.implicitHeight + 20
ChatUtils {
id: utils
}
TextEdit {
id: codeText
anchors.fill: parent
anchors.margins: 10
text: root.code
readOnly: true
selectByMouse: true
font.family: "monospace"
color: parent.color.hslLightness > 0.5 ? "black" : "white"
wrapMode: Text.WordWrap
selectionColor: root.selectionColor
}
TextEdit {
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: 5
readOnly: true
selectByMouse: true
text: root.language
color: root.color.hslLightness > 0.5 ? Qt.darker(root.color, 1.1)
: Qt.lighter(root.color, 1.1)
font.pointSize: 8
}
Button {
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: 5
text: "Copy"
onClicked: {
utils.copyToClipboard(root.code)
text = qsTr("Copied")
copyTimer.start()
}
Timer {
id: copyTimer
interval: 2000
onTriggered: parent.text = qsTr("Copy")
}
}
}

View File

@ -0,0 +1,29 @@
/*
* 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/>.
*/
import QtQuick
TextEdit {
id: root
readOnly: true
selectByMouse: true
wrapMode: Text.WordWrap
textFormat: Text.StyledText
}

View File

@ -18,7 +18,7 @@
*/ */
#include "ChangesManager.h" #include "ChangesManager.h"
#include "QodeAssistUtils.hpp" #include "logger/Logger.hpp"
#include "settings/ContextSettings.hpp" #include "settings/ContextSettings.hpp"
namespace QodeAssist { namespace QodeAssist {

23
llmcore/CMakeLists.txt Normal file
View File

@ -0,0 +1,23 @@
add_library(LLMCore STATIC
RequestType.hpp
Provider.hpp
ProvidersManager.hpp ProvidersManager.cpp
ContextData.hpp
PromptTemplate.hpp
PromptTemplateManager.hpp PromptTemplateManager.cpp
RequestConfig.hpp
RequestHandler.hpp RequestHandler.cpp
)
target_link_libraries(LLMCore
PUBLIC
Qt::Core
Qt::Network
QtCreator::Core
QtCreator::Utils
QtCreator::ExtensionSystem
PRIVATE
QodeAssistLogger
)
target_include_directories(LLMCore PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -21,13 +21,13 @@
#include <QString> #include <QString>
namespace QodeAssist { namespace QodeAssist::LLMCore {
struct ContextData struct ContextData
{ {
QString prefix; QString prefix;
QString suffix; QString suffix;
QString instriuctions; QString systemPrompt;
}; };
} // namespace QodeAssist } // namespace QodeAssist::LLMCore

View File

@ -23,9 +23,9 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
#include "QodeAssistData.hpp" #include "ContextData.hpp"
namespace QodeAssist::Templates { namespace QodeAssist::LLMCore {
enum class TemplateType { Chat, Fim }; enum class TemplateType { Chat, Fim };
@ -39,4 +39,4 @@ public:
virtual QStringList stopWords() const = 0; virtual QStringList stopWords() const = 0;
virtual void prepareRequest(QJsonObject &request, const ContextData &context) const = 0; virtual void prepareRequest(QJsonObject &request, const ContextData &context) const = 0;
}; };
} // namespace QodeAssist::Templates } // namespace QodeAssist::LLMCore

View File

@ -19,9 +19,9 @@
#include "PromptTemplateManager.hpp" #include "PromptTemplateManager.hpp"
#include "QodeAssistUtils.hpp" #include "Logger.hpp"
namespace QodeAssist { namespace QodeAssist::LLMCore {
PromptTemplateManager &PromptTemplateManager::instance() PromptTemplateManager &PromptTemplateManager::instance()
{ {
@ -31,19 +31,19 @@ PromptTemplateManager &PromptTemplateManager::instance()
void PromptTemplateManager::setCurrentFimTemplate(const QString &name) void PromptTemplateManager::setCurrentFimTemplate(const QString &name)
{ {
logMessage("Setting current FIM provider to: " + name); LOG_MESSAGE("Setting current FIM provider to: " + name);
if (!m_fimTemplates.contains(name) || m_fimTemplates[name] == nullptr) { if (!m_fimTemplates.contains(name) || m_fimTemplates[name] == nullptr) {
logMessage("Error to set current FIM template" + name); LOG_MESSAGE("Error to set current FIM template" + name);
return; return;
} }
m_currentFimTemplate = m_fimTemplates[name]; m_currentFimTemplate = m_fimTemplates[name];
} }
Templates::PromptTemplate *PromptTemplateManager::getCurrentFimTemplate() PromptTemplate *PromptTemplateManager::getCurrentFimTemplate()
{ {
if (m_currentFimTemplate == nullptr) { if (m_currentFimTemplate == nullptr) {
logMessage("Current fim provider is null"); LOG_MESSAGE("Current fim provider is null");
return nullptr; return nullptr;
} }
@ -52,19 +52,19 @@ Templates::PromptTemplate *PromptTemplateManager::getCurrentFimTemplate()
void PromptTemplateManager::setCurrentChatTemplate(const QString &name) void PromptTemplateManager::setCurrentChatTemplate(const QString &name)
{ {
logMessage("Setting current chat provider to: " + name); LOG_MESSAGE("Setting current chat provider to: " + name);
if (!m_chatTemplates.contains(name) || m_chatTemplates[name] == nullptr) { if (!m_chatTemplates.contains(name) || m_chatTemplates[name] == nullptr) {
logMessage("Error to set current chat template" + name); LOG_MESSAGE("Error to set current chat template" + name);
return; return;
} }
m_currentChatTemplate = m_chatTemplates[name]; m_currentChatTemplate = m_chatTemplates[name];
} }
Templates::PromptTemplate *PromptTemplateManager::getCurrentChatTemplate() PromptTemplate *PromptTemplateManager::getCurrentChatTemplate()
{ {
if (m_currentChatTemplate == nullptr) if (m_currentChatTemplate == nullptr)
logMessage("Current chat provider is null"); LOG_MESSAGE("Current chat provider is null");
return m_currentChatTemplate; return m_currentChatTemplate;
} }
@ -85,4 +85,4 @@ PromptTemplateManager::~PromptTemplateManager()
qDeleteAll(m_chatTemplates); qDeleteAll(m_chatTemplates);
} }
} // namespace QodeAssist } // namespace QodeAssist::LLMCore

View File

@ -22,9 +22,9 @@
#include <QMap> #include <QMap>
#include <QString> #include <QString>
#include "templates/PromptTemplate.hpp" #include "PromptTemplate.hpp"
namespace QodeAssist { namespace QodeAssist::LLMCore {
class PromptTemplateManager class PromptTemplateManager
{ {
@ -35,22 +35,22 @@ public:
template<typename T> template<typename T>
void registerTemplate() void registerTemplate()
{ {
static_assert(std::is_base_of<Templates::PromptTemplate, T>::value, static_assert(std::is_base_of<PromptTemplate, T>::value,
"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();
if (template_ptr->type() == Templates::TemplateType::Fim) { if (template_ptr->type() == TemplateType::Fim) {
m_fimTemplates[name] = template_ptr; m_fimTemplates[name] = template_ptr;
} else if (template_ptr->type() == Templates::TemplateType::Chat) { } else if (template_ptr->type() == TemplateType::Chat) {
m_chatTemplates[name] = template_ptr; m_chatTemplates[name] = template_ptr;
} }
} }
void setCurrentFimTemplate(const QString &name); void setCurrentFimTemplate(const QString &name);
Templates::PromptTemplate *getCurrentFimTemplate(); PromptTemplate *getCurrentFimTemplate();
void setCurrentChatTemplate(const QString &name); void setCurrentChatTemplate(const QString &name);
Templates::PromptTemplate *getCurrentChatTemplate(); PromptTemplate *getCurrentChatTemplate();
QStringList fimTemplatesNames() const; QStringList fimTemplatesNames() const;
QStringList chatTemplatesNames() const; QStringList chatTemplatesNames() const;
@ -60,10 +60,10 @@ private:
PromptTemplateManager(const PromptTemplateManager &) = delete; PromptTemplateManager(const PromptTemplateManager &) = delete;
PromptTemplateManager &operator=(const PromptTemplateManager &) = delete; PromptTemplateManager &operator=(const PromptTemplateManager &) = delete;
QMap<QString, Templates::PromptTemplate *> m_fimTemplates; QMap<QString, PromptTemplate *> m_fimTemplates;
QMap<QString, Templates::PromptTemplate *> m_chatTemplates; QMap<QString, PromptTemplate *> m_chatTemplates;
Templates::PromptTemplate *m_currentFimTemplate; PromptTemplate *m_currentFimTemplate;
Templates::PromptTemplate *m_currentChatTemplate; PromptTemplate *m_currentChatTemplate;
}; };
} // namespace QodeAssist } // namespace QodeAssist::LLMCore

View File

@ -20,26 +20,27 @@
#pragma once #pragma once
#include <QString> #include <QString>
#include "RequestType.hpp"
#include <utils/environment.h> #include <utils/environment.h>
class QNetworkReply; class QNetworkReply;
class QJsonObject; class QJsonObject;
namespace QodeAssist::Providers { namespace QodeAssist::LLMCore {
class LLMProvider class Provider
{ {
public: public:
virtual ~LLMProvider() = default; virtual ~Provider() = default;
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 QString chatEndpoint() const = 0;
virtual void prepareRequest(QJsonObject &request) = 0; virtual void prepareRequest(QJsonObject &request, RequestType type) = 0;
virtual bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) = 0; virtual bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) = 0;
virtual QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) = 0; virtual QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) = 0;
}; };
} // namespace QodeAssist::Providers } // namespace QodeAssist::LLMCore

View File

@ -17,23 +17,23 @@
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>. * along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "LLMProvidersManager.hpp" #include "ProvidersManager.hpp"
#include "Logger.hpp"
#include <coreplugin/messagemanager.h>
#include "QodeAssistUtils.hpp" namespace QodeAssist::LLMCore {
namespace QodeAssist { ProvidersManager &ProvidersManager::instance()
LLMProvidersManager &LLMProvidersManager::instance()
{ {
static LLMProvidersManager instance; static ProvidersManager instance;
return instance; return instance;
} }
Providers::LLMProvider *LLMProvidersManager::setCurrentFimProvider(const QString &name) Provider *ProvidersManager::setCurrentFimProvider(const QString &name)
{ {
logMessage("Setting current FIM provider to: " + name); LOG_MESSAGE("Setting current FIM provider to: " + name);
if (!m_providers.contains(name)) { if (!m_providers.contains(name)) {
logMessage("Can't find provider with name: " + name); LOG_MESSAGE("Can't find provider with name: " + name);
return nullptr; return nullptr;
} }
@ -41,11 +41,11 @@ Providers::LLMProvider *LLMProvidersManager::setCurrentFimProvider(const QString
return m_currentFimProvider; return m_currentFimProvider;
} }
Providers::LLMProvider *LLMProvidersManager::setCurrentChatProvider(const QString &name) Provider *ProvidersManager::setCurrentChatProvider(const QString &name)
{ {
logMessage("Setting current chat provider to: " + name); LOG_MESSAGE("Setting current chat provider to: " + name);
if (!m_providers.contains(name)) { if (!m_providers.contains(name)) {
logMessage("Can't find chat provider with name: " + name); LOG_MESSAGE("Can't find chat provider with name: " + name);
return nullptr; return nullptr;
} }
@ -53,34 +53,34 @@ Providers::LLMProvider *LLMProvidersManager::setCurrentChatProvider(const QStrin
return m_currentChatProvider; return m_currentChatProvider;
} }
Providers::LLMProvider *LLMProvidersManager::getCurrentFimProvider() Provider *ProvidersManager::getCurrentFimProvider()
{ {
if (m_currentFimProvider == nullptr) { if (m_currentFimProvider == nullptr) {
logMessage("Current fim provider is null"); LOG_MESSAGE("Current fim provider is null");
return nullptr; return nullptr;
} }
return m_currentFimProvider; return m_currentFimProvider;
} }
Providers::LLMProvider *LLMProvidersManager::getCurrentChatProvider() Provider *ProvidersManager::getCurrentChatProvider()
{ {
if (m_currentChatProvider == nullptr) { if (m_currentChatProvider == nullptr) {
logMessage("Current chat provider is null"); LOG_MESSAGE("Current chat provider is null");
return nullptr; return nullptr;
} }
return m_currentChatProvider; return m_currentChatProvider;
} }
QStringList LLMProvidersManager::providersNames() const QStringList ProvidersManager::providersNames() const
{ {
return m_providers.keys(); return m_providers.keys();
} }
LLMProvidersManager::~LLMProvidersManager() ProvidersManager::~ProvidersManager()
{ {
qDeleteAll(m_providers); qDeleteAll(m_providers);
} }
} // namespace QodeAssist } // namespace QodeAssist::LLMCore

View File

@ -21,42 +21,41 @@
#include <QString> #include <QString>
#include "providers/LLMProvider.hpp" #include "Provider.hpp"
namespace QodeAssist { namespace QodeAssist::LLMCore {
class LLMProvidersManager class ProvidersManager
{ {
public: public:
static LLMProvidersManager &instance(); static ProvidersManager &instance();
~LLMProvidersManager(); ~ProvidersManager();
template<typename T> template<typename T>
void registerProvider() void registerProvider()
{ {
static_assert(std::is_base_of<Providers::LLMProvider, T>::value, static_assert(std::is_base_of<Provider, T>::value, "T must inherit from Provider");
"T must inherit from LLMProvider");
T *provider = new T(); T *provider = new T();
QString name = provider->name(); QString name = provider->name();
m_providers[name] = provider; m_providers[name] = provider;
} }
Providers::LLMProvider *setCurrentFimProvider(const QString &name); Provider *setCurrentFimProvider(const QString &name);
Providers::LLMProvider *setCurrentChatProvider(const QString &name); Provider *setCurrentChatProvider(const QString &name);
Providers::LLMProvider *getCurrentFimProvider(); Provider *getCurrentFimProvider();
Providers::LLMProvider *getCurrentChatProvider(); Provider *getCurrentChatProvider();
QStringList providersNames() const; QStringList providersNames() const;
private: private:
LLMProvidersManager() = default; ProvidersManager() = default;
LLMProvidersManager(const LLMProvidersManager &) = delete; ProvidersManager(const ProvidersManager &) = delete;
LLMProvidersManager &operator=(const LLMProvidersManager &) = delete; ProvidersManager &operator=(const ProvidersManager &) = delete;
QMap<QString, Providers::LLMProvider *> m_providers; QMap<QString, Provider *> m_providers;
Providers::LLMProvider *m_currentFimProvider = nullptr; Provider *m_currentFimProvider = nullptr;
Providers::LLMProvider *m_currentChatProvider = nullptr; Provider *m_currentChatProvider = nullptr;
}; };
} // namespace QodeAssist } // namespace QodeAssist::LLMCore

View File

@ -21,20 +21,20 @@
#include <QJsonObject> #include <QJsonObject>
#include <QUrl> #include <QUrl>
#include "providers/LLMProvider.hpp" #include "PromptTemplate.hpp"
#include "templates/PromptTemplate.hpp" #include "Provider.hpp"
#include "RequestType.hpp"
namespace QodeAssist { namespace QodeAssist::LLMCore {
enum class RequestType { Fim, Chat };
struct LLMConfig struct LLMConfig
{ {
QUrl url; QUrl url;
Providers::LLMProvider *provider; Provider *provider;
Templates::PromptTemplate *promptTemplate; PromptTemplate *promptTemplate;
QJsonObject providerRequest; QJsonObject providerRequest;
RequestType requestType; RequestType requestType;
bool multiLineCompletion;
}; };
} // namespace QodeAssist } // namespace QodeAssist::LLMCore

View File

@ -17,24 +17,22 @@
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>. * along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "LLMRequestHandler.hpp" #include "RequestHandler.hpp"
#include "LLMProvidersManager.hpp" #include "Logger.hpp"
#include "QodeAssistUtils.hpp"
#include "settings/GeneralSettings.hpp"
#include <QJsonDocument> #include <QJsonDocument>
#include <QNetworkReply> #include <QNetworkReply>
namespace QodeAssist { namespace QodeAssist::LLMCore {
LLMRequestHandler::LLMRequestHandler(QObject *parent) RequestHandler::RequestHandler(QObject *parent)
: QObject(parent) : QObject(parent)
, m_manager(new QNetworkAccessManager(this)) , m_manager(new QNetworkAccessManager(this))
{} {}
void LLMRequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObject &request) void RequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObject &request)
{ {
logMessage(QString("Sending request to llm: \nurl: %1\nRequest body:\n%2") LOG_MESSAGE(QString("Sending request to llm: \nurl: %1\nRequest body:\n%2")
.arg(config.url.toString(), .arg(config.url.toString(),
QString::fromUtf8( QString::fromUtf8(
QJsonDocument(config.providerRequest).toJson(QJsonDocument::Indented)))); QJsonDocument(config.providerRequest).toJson(QJsonDocument::Indented))));
@ -45,7 +43,7 @@ void LLMRequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObjec
QNetworkReply *reply = m_manager->post(networkRequest, QNetworkReply *reply = m_manager->post(networkRequest,
QJsonDocument(config.providerRequest).toJson()); QJsonDocument(config.providerRequest).toJson());
if (!reply) { if (!reply) {
logMessage("Error: Failed to create network reply"); LOG_MESSAGE("Error: Failed to create network reply");
return; return;
} }
@ -60,16 +58,16 @@ void LLMRequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObjec
reply->deleteLater(); reply->deleteLater();
m_activeRequests.remove(requestId); m_activeRequests.remove(requestId);
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
logMessage(QString("Error in QodeAssist request: %1").arg(reply->errorString())); LOG_MESSAGE(QString("Error in QodeAssist request: %1").arg(reply->errorString()));
emit requestFinished(requestId, false, reply->errorString()); emit requestFinished(requestId, false, reply->errorString());
} else { } else {
logMessage("Request finished successfully"); LOG_MESSAGE("Request finished successfully");
emit requestFinished(requestId, true, QString()); emit requestFinished(requestId, true, QString());
} }
}); });
} }
void LLMRequestHandler::handleLLMResponse(QNetworkReply *reply, void RequestHandler::handleLLMResponse(QNetworkReply *reply,
const QJsonObject &request, const QJsonObject &request,
const LLMConfig &config) const LLMConfig &config)
{ {
@ -78,7 +76,7 @@ void LLMRequestHandler::handleLLMResponse(QNetworkReply *reply,
bool isComplete = config.provider->handleResponse(reply, accumulatedResponse); bool isComplete = config.provider->handleResponse(reply, accumulatedResponse);
if (config.requestType == RequestType::Fim) { if (config.requestType == RequestType::Fim) {
if (!Settings::generalSettings().multiLineCompletion() if (!config.multiLineCompletion
&& processSingleLineCompletion(reply, request, accumulatedResponse, config)) { && processSingleLineCompletion(reply, request, accumulatedResponse, config)) {
return; return;
} }
@ -100,7 +98,7 @@ void LLMRequestHandler::handleLLMResponse(QNetworkReply *reply,
} }
} }
bool LLMRequestHandler::cancelRequest(const QString &id) bool RequestHandler::cancelRequest(const QString &id)
{ {
if (m_activeRequests.contains(id)) { if (m_activeRequests.contains(id)) {
QNetworkReply *reply = m_activeRequests[id]; QNetworkReply *reply = m_activeRequests[id];
@ -113,7 +111,7 @@ bool LLMRequestHandler::cancelRequest(const QString &id)
return false; return false;
} }
void LLMRequestHandler::prepareNetworkRequest(QNetworkRequest &networkRequest, void RequestHandler::prepareNetworkRequest(QNetworkRequest &networkRequest,
const QJsonObject &providerRequest) const QJsonObject &providerRequest)
{ {
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
@ -124,7 +122,7 @@ void LLMRequestHandler::prepareNetworkRequest(QNetworkRequest &networkRequest,
} }
} }
bool LLMRequestHandler::processSingleLineCompletion(QNetworkReply *reply, bool RequestHandler::processSingleLineCompletion(QNetworkReply *reply,
const QJsonObject &request, const QJsonObject &request,
const QString &accumulatedResponse, const QString &accumulatedResponse,
const LLMConfig &config) const LLMConfig &config)
@ -145,8 +143,7 @@ bool LLMRequestHandler::processSingleLineCompletion(QNetworkReply *reply,
return false; return false;
} }
QString LLMRequestHandler::removeStopWords(const QStringView &completion, QString RequestHandler::removeStopWords(const QStringView &completion, const QStringList &stopWords)
const QStringList &stopWords)
{ {
QString filteredCompletion = completion.toString(); QString filteredCompletion = completion.toString();
@ -157,4 +154,4 @@ QString LLMRequestHandler::removeStopWords(const QStringView &completion,
return filteredCompletion; return filteredCompletion;
} }
} // namespace QodeAssist } // namespace QodeAssist::LLMCore

View File

@ -23,19 +23,18 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QObject> #include <QObject>
#include "QodeAssistData.hpp" #include "RequestConfig.hpp"
#include "core/LLMRequestConfig.hpp"
class QNetworkReply; class QNetworkReply;
namespace QodeAssist { namespace QodeAssist::LLMCore {
class LLMRequestHandler : public QObject class RequestHandler : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit LLMRequestHandler(QObject *parent = nullptr); explicit RequestHandler(QObject *parent = nullptr);
void sendLLMRequest(const LLMConfig &config, const QJsonObject &request); void sendLLMRequest(const LLMConfig &config, const QJsonObject &request);
void handleLLMResponse(QNetworkReply *reply, void handleLLMResponse(QNetworkReply *reply,
@ -61,4 +60,4 @@ private:
QString removeStopWords(const QStringView &completion, const QStringList &stopWords); QString removeStopWords(const QStringView &completion, const QStringList &stopWords);
}; };
} // namespace QodeAssist } // namespace QodeAssist::LLMCore

6
llmcore/RequestType.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
namespace QodeAssist::LLMCore {
enum RequestType { Fim, Chat };
}

14
logger/CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
add_library(QodeAssistLogger STATIC
Logger.cpp
Logger.hpp
)
target_link_libraries(QodeAssistLogger
PUBLIC
Qt::Core
QtCreator::Core
)
target_include_directories(QodeAssistLogger
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)

55
logger/Logger.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "Logger.hpp"
#include <coreplugin/messagemanager.h>
namespace QodeAssist {
Logger &Logger::instance()
{
static Logger instance;
return instance;
}
Logger::Logger()
: m_loggingEnabled(false)
{}
void Logger::setLoggingEnabled(bool enable)
{
m_loggingEnabled = enable;
}
bool Logger::isLoggingEnabled() const
{
return m_loggingEnabled;
}
void Logger::log(const QString &message, bool silent)
{
if (!m_loggingEnabled)
return;
const QString prefixedMessage = QLatin1String("[Qode Assist] ") + message;
if (silent) {
Core::MessageManager::writeSilently(prefixedMessage);
} else {
Core::MessageManager::writeFlashing(prefixedMessage);
}
}
void Logger::logMessages(const QStringList &messages, bool silent)
{
if (!m_loggingEnabled)
return;
QStringList prefixedMessages;
for (const QString &message : messages) {
prefixedMessages << (QLatin1String("[Qode Assist] ") + message);
}
if (silent) {
Core::MessageManager::writeSilently(prefixedMessages);
} else {
Core::MessageManager::writeFlashing(prefixedMessages);
}
}
} // namespace QodeAssist

33
logger/Logger.hpp Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include <QObject>
#include <QString>
namespace QodeAssist {
class Logger : public QObject
{
Q_OBJECT
public:
static Logger &instance();
void setLoggingEnabled(bool enable);
bool isLoggingEnabled() const;
void log(const QString &message, bool silent = true);
void logMessages(const QStringList &messages, bool silent = true);
private:
Logger();
~Logger() = default;
Logger(const Logger &) = delete;
Logger &operator=(const Logger &) = delete;
bool m_loggingEnabled;
};
#define LOG_MESSAGE(msg) QodeAssist::Logger::instance().log(msg)
#define LOG_MESSAGES(msgs) QodeAssist::Logger::instance().logMessages(msgs)
} // namespace QodeAssist

View File

@ -25,7 +25,7 @@
#include <QJsonObject> #include <QJsonObject>
#include <QNetworkReply> #include <QNetworkReply>
#include "QodeAssistUtils.hpp" #include "logger/Logger.hpp"
#include "settings/PresetPromptsSettings.hpp" #include "settings/PresetPromptsSettings.hpp"
namespace QodeAssist::Providers { namespace QodeAssist::Providers {
@ -52,9 +52,11 @@ QString LMStudioProvider::chatEndpoint() const
return "/v1/chat/completions"; return "/v1/chat/completions";
} }
void LMStudioProvider::prepareRequest(QJsonObject &request) void LMStudioProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
{ {
auto &settings = Settings::presetPromptsSettings(); auto &promptSettings = Settings::presetPromptsSettings();
auto settings = promptSettings.getSettings(type);
QJsonArray messages; QJsonArray messages;
if (request.contains("system")) { if (request.contains("system")) {
@ -72,16 +74,16 @@ void LMStudioProvider::prepareRequest(QJsonObject &request)
request["messages"] = std::move(messages); request["messages"] = std::move(messages);
} }
request["max_tokens"] = settings.maxTokens(); request["max_tokens"] = settings.maxTokens;
request["temperature"] = settings.temperature(); request["temperature"] = settings.temperature;
if (settings.useTopP()) if (settings.useTopP)
request["top_p"] = settings.topP(); request["top_p"] = settings.topP;
if (settings.useTopK()) if (settings.useTopK)
request["top_k"] = settings.topK(); request["top_k"] = settings.topK;
if (settings.useFrequencyPenalty()) if (settings.useFrequencyPenalty)
request["frequency_penalty"] = settings.frequencyPenalty(); request["frequency_penalty"] = settings.frequencyPenalty;
if (settings.usePresencePenalty()) if (settings.usePresencePenalty)
request["presence_penalty"] = settings.presencePenalty(); request["presence_penalty"] = settings.presencePenalty;
} }
bool LMStudioProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse) bool LMStudioProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
@ -150,7 +152,7 @@ QList<QString> LMStudioProvider::getInstalledModels(const Utils::Environment &en
models.append(modelId); models.append(modelId);
} }
} else { } else {
logMessage(QString("Error fetching models: %1").arg(reply->errorString())); LOG_MESSAGE(QString("Error fetching models: %1").arg(reply->errorString()));
} }
reply->deleteLater(); reply->deleteLater();

View File

@ -19,11 +19,11 @@
#pragma once #pragma once
#include "LLMProvider.hpp" #include "llmcore/Provider.hpp"
namespace QodeAssist::Providers { namespace QodeAssist::Providers {
class LMStudioProvider : public LLMProvider class LMStudioProvider : public LLMCore::Provider
{ {
public: public:
LMStudioProvider(); LMStudioProvider();
@ -32,7 +32,7 @@ public:
QString url() const override; QString url() const override;
QString completionEndpoint() const override; QString completionEndpoint() const override;
QString chatEndpoint() const override; QString chatEndpoint() const override;
void prepareRequest(QJsonObject &request) override; void prepareRequest(QJsonObject &request, LLMCore::RequestType type) override;
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override; bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) override; QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) override;
}; };

View File

@ -25,8 +25,8 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QtCore/qeventloop.h> #include <QtCore/qeventloop.h>
#include "PromptTemplateManager.hpp" #include "llmcore/PromptTemplateManager.hpp"
#include "QodeAssistUtils.hpp" #include "logger/Logger.hpp"
#include "settings/PresetPromptsSettings.hpp" #include "settings/PresetPromptsSettings.hpp"
namespace QodeAssist::Providers { namespace QodeAssist::Providers {
@ -53,23 +53,24 @@ QString OllamaProvider::chatEndpoint() const
return "/api/chat"; return "/api/chat";
} }
void OllamaProvider::prepareRequest(QJsonObject &request) void OllamaProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
{ {
auto &settings = Settings::presetPromptsSettings(); auto &promptSettings = Settings::presetPromptsSettings();
auto settings = promptSettings.getSettings(type);
QJsonObject options; QJsonObject options;
options["num_predict"] = settings.maxTokens(); options["num_predict"] = settings.maxTokens;
options["temperature"] = settings.temperature(); options["temperature"] = settings.temperature;
if (settings.useTopP()) if (settings.useTopP)
options["top_p"] = settings.topP(); options["top_p"] = settings.topP;
if (settings.useTopK()) if (settings.useTopK)
options["top_k"] = settings.topK(); options["top_k"] = settings.topK;
if (settings.useFrequencyPenalty()) if (settings.useFrequencyPenalty)
options["frequency_penalty"] = settings.frequencyPenalty(); options["frequency_penalty"] = settings.frequencyPenalty;
if (settings.usePresencePenalty()) if (settings.usePresencePenalty)
options["presence_penalty"] = settings.presencePenalty(); options["presence_penalty"] = settings.presencePenalty;
request["options"] = options; request["options"] = options;
request["keep_alive"] = settings.ollamaLivetime(); request["keep_alive"] = settings.ollamaLivetime;
} }
bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse) bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
@ -85,7 +86,7 @@ bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedRe
QJsonDocument doc = QJsonDocument::fromJson(line); QJsonDocument doc = QJsonDocument::fromJson(line);
if (doc.isNull()) { if (doc.isNull()) {
logMessage("Invalid JSON response from Ollama: " + QString::fromUtf8(line)); LOG_MESSAGE("Invalid JSON response from Ollama: " + QString::fromUtf8(line));
continue; continue;
} }
@ -93,7 +94,7 @@ bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedRe
if (responseObj.contains("error")) { if (responseObj.contains("error")) {
QString errorMessage = responseObj["error"].toString(); QString errorMessage = responseObj["error"].toString();
logMessage("Error in Ollama response: " + errorMessage); LOG_MESSAGE("Error in Ollama response: " + errorMessage);
return false; return false;
} }
@ -111,7 +112,7 @@ bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedRe
} }
} }
} else { } else {
logMessage("Unknown endpoint: " + endpoint); LOG_MESSAGE("Unknown endpoint: " + endpoint);
} }
if (responseObj.contains("done") && responseObj["done"].toBool()) { if (responseObj.contains("done") && responseObj["done"].toBool()) {
@ -146,7 +147,7 @@ QList<QString> OllamaProvider::getInstalledModels(const Utils::Environment &env,
models.append(modelName); models.append(modelName);
} }
} else { } else {
logMessage(QString("Error fetching models: %1").arg(reply->errorString())); LOG_MESSAGE(QString("Error fetching models: %1").arg(reply->errorString()));
} }
reply->deleteLater(); reply->deleteLater();

View File

@ -19,11 +19,11 @@
#pragma once #pragma once
#include "LLMProvider.hpp" #include "llmcore/Provider.hpp"
namespace QodeAssist::Providers { namespace QodeAssist::Providers {
class OllamaProvider : public LLMProvider class OllamaProvider : public LLMCore::Provider
{ {
public: public:
OllamaProvider(); OllamaProvider();
@ -32,7 +32,7 @@ public:
QString url() const override; QString url() const override;
QString completionEndpoint() const override; QString completionEndpoint() const override;
QString chatEndpoint() const override; QString chatEndpoint() const override;
void prepareRequest(QJsonObject &request) override; void prepareRequest(QJsonObject &request, LLMCore::RequestType type) override;
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override; bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) override; QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) override;
}; };

View File

@ -50,9 +50,10 @@ QString OpenAICompatProvider::chatEndpoint() const
return "/v1/chat/completions"; return "/v1/chat/completions";
} }
void OpenAICompatProvider::prepareRequest(QJsonObject &request) void OpenAICompatProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
{ {
auto &settings = Settings::presetPromptsSettings(); auto &promptSettings = Settings::presetPromptsSettings();
auto settings = promptSettings.getSettings(type);
QJsonArray messages; QJsonArray messages;
if (request.contains("system")) { if (request.contains("system")) {
@ -70,18 +71,18 @@ void OpenAICompatProvider::prepareRequest(QJsonObject &request)
request["messages"] = std::move(messages); request["messages"] = std::move(messages);
} }
request["max_tokens"] = settings.maxTokens(); request["max_tokens"] = settings.maxTokens;
request["temperature"] = settings.temperature(); request["temperature"] = settings.temperature;
if (settings.useTopP()) if (settings.useTopP)
request["top_p"] = settings.topP(); request["top_p"] = settings.topP;
if (settings.useTopK()) if (settings.useTopK)
request["top_k"] = settings.topK(); request["top_k"] = settings.topK;
if (settings.useFrequencyPenalty()) if (settings.useFrequencyPenalty)
request["frequency_penalty"] = settings.frequencyPenalty(); request["frequency_penalty"] = settings.frequencyPenalty;
if (settings.usePresencePenalty()) if (settings.usePresencePenalty)
request["presence_penalty"] = settings.presencePenalty(); request["presence_penalty"] = settings.presencePenalty;
const QString &apiKey = settings.apiKey.value(); const QString &apiKey = settings.apiKey;
if (!apiKey.isEmpty()) { if (!apiKey.isEmpty()) {
request["api_key"] = apiKey; request["api_key"] = apiKey;
} }

View File

@ -19,11 +19,11 @@
#pragma once #pragma once
#include "LLMProvider.hpp" #include "llmcore/Provider.hpp"
namespace QodeAssist::Providers { namespace QodeAssist::Providers {
class OpenAICompatProvider : public LLMProvider class OpenAICompatProvider : public LLMCore::Provider
{ {
public: public:
OpenAICompatProvider(); OpenAICompatProvider();
@ -32,7 +32,7 @@ public:
QString url() const override; QString url() const override;
QString completionEndpoint() const override; QString completionEndpoint() const override;
QString chatEndpoint() const override; QString chatEndpoint() const override;
void prepareRequest(QJsonObject &request) override; void prepareRequest(QJsonObject &request, LLMCore::RequestType type) override;
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override; bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) override; QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) override;
}; };

View File

@ -39,21 +39,22 @@
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <utils/icon.h> #include <utils/icon.h>
#include "LLMProvidersManager.hpp"
#include "PromptTemplateManager.hpp"
#include "QodeAssistClient.hpp" #include "QodeAssistClient.hpp"
#include "chat/ChatOutputPane.h" #include "chat/ChatOutputPane.h"
#include "chat/NavigationPanel.hpp"
#include "llmcore/PromptTemplateManager.hpp"
#include "llmcore/ProvidersManager.hpp"
#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 "settings/GeneralSettings.hpp" #include "templates/CodeLlamaChat.hpp"
#include "templates/CodeLlamaFimTemplate.hpp" #include "templates/CodeLlamaFim.hpp"
#include "templates/CodeLlamaInstruct.hpp" #include "templates/CustomFimTemplate.hpp"
#include "templates/CustomTemplate.hpp" #include "templates/DeepSeekCoderChat.hpp"
#include "templates/DeepSeekCoderChatTemplate.hpp" #include "templates/DeepSeekCoderFim.hpp"
#include "templates/DeepSeekCoderV2.hpp" #include "templates/QwenChat.hpp"
#include "templates/StarCoder2Template.hpp" #include "templates/StarCoder2Fim.hpp"
using namespace Utils; using namespace Utils;
using namespace Core; using namespace Core;
@ -73,23 +74,26 @@ public:
~QodeAssistPlugin() final ~QodeAssistPlugin() final
{ {
delete m_qodeAssistClient;
delete m_chatOutputPane;
delete m_navigationPanel;
} }
void initialize() final void initialize() final
{ {
auto &providerManager = LLMProvidersManager::instance(); auto &providerManager = LLMCore::ProvidersManager::instance();
providerManager.registerProvider<Providers::OllamaProvider>(); providerManager.registerProvider<Providers::OllamaProvider>();
providerManager.registerProvider<Providers::LMStudioProvider>(); providerManager.registerProvider<Providers::LMStudioProvider>();
providerManager.registerProvider<Providers::OpenAICompatProvider>(); providerManager.registerProvider<Providers::OpenAICompatProvider>();
auto &templateManager = PromptTemplateManager::instance(); auto &templateManager = LLMCore::PromptTemplateManager::instance();
templateManager.registerTemplate<Templates::CodeLlamaFimTemplate>(); templateManager.registerTemplate<Templates::CodeLlamaFim>();
templateManager.registerTemplate<Templates::StarCoder2Template>(); templateManager.registerTemplate<Templates::StarCoder2Fim>();
templateManager.registerTemplate<Templates::DeepSeekCoderV2Template>(); templateManager.registerTemplate<Templates::DeepSeekCoderFim>();
templateManager.registerTemplate<Templates::CustomTemplate>(); templateManager.registerTemplate<Templates::CustomTemplate>();
templateManager.registerTemplate<Templates::DeepSeekCoderChatTemplate>(); templateManager.registerTemplate<Templates::DeepSeekCoderChat>();
templateManager.registerTemplate<Templates::CodeLlamaInstructTemplate>(); templateManager.registerTemplate<Templates::CodeLlamaChat>();
templateManager.registerTemplate<Templates::QwenChat>();
Utils::Icon QCODEASSIST_ICON( Utils::Icon QCODEASSIST_ICON(
{{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}}); {{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}});
@ -116,6 +120,7 @@ public:
StatusBarManager::addStatusBarWidget(toggleButton, StatusBarManager::RightCorner); StatusBarManager::addStatusBarWidget(toggleButton, StatusBarManager::RightCorner);
m_chatOutputPane = new Chat::ChatOutputPane(this); m_chatOutputPane = new Chat::ChatOutputPane(this);
m_navigationPanel = new Chat::NavigationPanel();
} }
void extensionsInitialized() final void extensionsInitialized() final
@ -124,9 +129,9 @@ public:
void restartClient() void restartClient()
{ {
LanguageClient::LanguageClientManager::shutdownClient(m_qodeAssistClient.get()); LanguageClient::LanguageClientManager::shutdownClient(m_qodeAssistClient);
m_qodeAssistClient.reset(new QodeAssistClient()); m_qodeAssistClient = new QodeAssistClient();
} }
bool delayedInitialize() final bool delayedInitialize() final
@ -140,7 +145,7 @@ public:
{ {
if (!m_qodeAssistClient) if (!m_qodeAssistClient)
return SynchronousShutdown; return SynchronousShutdown;
connect(m_qodeAssistClient.get(), connect(m_qodeAssistClient,
&QObject::destroyed, &QObject::destroyed,
this, this,
&IPlugin::asynchronousShutdownFinished); &IPlugin::asynchronousShutdownFinished);
@ -148,8 +153,9 @@ public:
} }
private: private:
QScopedPointer<QodeAssistClient> m_qodeAssistClient; QPointer<QodeAssistClient> m_qodeAssistClient;
QPointer<Chat::ChatOutputPane> m_chatOutputPane; QPointer<Chat::ChatOutputPane> m_chatOutputPane;
QPointer<Chat::NavigationPanel> m_navigationPanel;
}; };
} // namespace QodeAssist::Internal } // namespace QodeAssist::Internal

31
settings/Assisttr.h Normal file
View File

@ -0,0 +1,31 @@
/*
* 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 <QCoreApplication>
namespace QodeAssist {
struct Tr
{
Q_DECLARE_TR_FUNCTIONS(QtC::QodeAssist)
};
} // namespace QodeAssist

20
settings/CMakeLists.txt Normal file
View File

@ -0,0 +1,20 @@
add_library(QodeAssistSettings STATIC
GeneralSettings.hpp GeneralSettings.cpp
ContextSettings.hpp ContextSettings.cpp
CustomPromptSettings.hpp CustomPromptSettings.cpp
PresetPromptsSettings.hpp PresetPromptsSettings.cpp
SettingsUtils.hpp
SettingsConstants.hpp
)
target_link_libraries(QodeAssistSettings
PUBLIC
Qt::Core
Qt::Network
QtCreator::Core
QtCreator::Utils
QodeAssistLogger
PRIVATE
LLMCore
)
target_include_directories(QodeAssistSettings PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -24,8 +24,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include "QodeAssistConstants.hpp" #include "SettingsConstants.hpp"
#include "QodeAssisttr.h"
namespace QodeAssist::Settings { namespace QodeAssist::Settings {
ContextSettings &contextSettings() ContextSettings &contextSettings()
@ -58,17 +57,37 @@ ContextSettings::ContextSettings()
useFilePathInContext.setDefaultValue(false); useFilePathInContext.setDefaultValue(false);
useFilePathInContext.setLabelText(Tr::tr("Use File Path in Context")); useFilePathInContext.setLabelText(Tr::tr("Use File Path in Context"));
useSpecificInstructions.setSettingsKey(Constants::USE_SYSTEM_PROMPT); useSystemPrompt.setSettingsKey(Constants::USE_SYSTEM_PROMPT);
useSpecificInstructions.setDefaultValue(true); useSystemPrompt.setDefaultValue(true);
useSpecificInstructions.setLabelText(Tr::tr("Use System Prompt")); useSystemPrompt.setLabelText(Tr::tr("Use System Prompt"));
specificInstractions.setSettingsKey(Constants::SYSTEM_PROMPT); systemPrompt.setSettingsKey(Constants::SYSTEM_PROMPT);
specificInstractions.setDisplayStyle(Utils::StringAspect::TextEditDisplay); systemPrompt.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
specificInstractions.setLabelText( systemPrompt.setDefaultValue(
Tr::tr("Instructions: Please keep %1 for languge name, warning, it shouldn't too big")); "You are an expert C++, Qt, and QML code completion AI. Your task is to provide accurate "
specificInstractions.setDefaultValue( "and "
"You are an expert %1 code completion AI." "contextually appropriate code suggestions. Focus on completing the code in a way that "
"CRITICAL: Please provide minimal the best possible code completion suggestions.\n"); "follows best practices, is efficient, and matches the surrounding code style. Prioritize "
"Qt and QML-specific completions when appropriate. Avoid adding comments or explanations "
"in your completions.");
useChatSystemPrompt.setSettingsKey(Constants::USE_CHAT_SYSTEM_PROMPT);
useChatSystemPrompt.setDefaultValue(true);
useChatSystemPrompt.setLabelText(Tr::tr("Use System Prompt for chat"));
chatSystemPrompt.setSettingsKey(Constants::CHAT_SYSTEM_PROMPT);
chatSystemPrompt.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
chatSystemPrompt.setDefaultValue(
"You are an advanced AI assistant specializing in C++, Qt, and QML development. Your role "
"is "
"to provide helpful, accurate, and detailed responses to questions about coding, "
"debugging, "
"and best practices in these technologies. Offer clear explanations, code examples when "
"appropriate, and guidance on Qt Creator usage. Always prioritize officially recommended "
"Qt "
"and C++ practices. If you're unsure about something, state it clearly and suggest where "
"the "
"user might find more information.");
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults"); resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
@ -85,21 +104,26 @@ ContextSettings::ContextSettings()
readStringsAfterCursor.setEnabled(!readFullFile()); readStringsAfterCursor.setEnabled(!readFullFile());
readStringsBeforeCursor.setEnabled(!readFullFile()); readStringsBeforeCursor.setEnabled(!readFullFile());
specificInstractions.setEnabled(useSpecificInstructions()); systemPrompt.setEnabled(useSystemPrompt());
setupConnection(); setupConnection();
setLayouter([this]() { setLayouter([this]() {
using namespace Layouting; using namespace Layouting;
return Column{Row{readFullFile, Stretch{1}, resetToDefaults}, return Column{Row{Stretch{1}, resetToDefaults},
Group{title(Tr::tr("AI Suggestions Context")),
Column{Row{readFullFile, Stretch{1}},
Row{readStringsBeforeCursor, Stretch{1}}, Row{readStringsBeforeCursor, Stretch{1}},
Row{readStringsAfterCursor, Stretch{1}}, Row{readStringsAfterCursor, Stretch{1}},
useFilePathInContext, useFilePathInContext,
useSpecificInstructions, useSystemPrompt,
specificInstractions, systemPrompt,
useProjectChangesCache, useProjectChangesCache,
Row{maxChangesCacheSize, Stretch{1}}, Row{maxChangesCacheSize, Stretch{1}},
Stretch{1}}; Stretch{1}}},
Space{16},
Group{title(Tr::tr("AI Chat Context")),
Column{useChatSystemPrompt, chatSystemPrompt}}};
}); });
} }
@ -109,8 +133,8 @@ void ContextSettings::setupConnection()
readStringsAfterCursor.setEnabled(!readFullFile.volatileValue()); readStringsAfterCursor.setEnabled(!readFullFile.volatileValue());
readStringsBeforeCursor.setEnabled(!readFullFile.volatileValue()); readStringsBeforeCursor.setEnabled(!readFullFile.volatileValue());
}); });
connect(&useSpecificInstructions, &Utils::BoolAspect::volatileValueChanged, this, [this]() { connect(&useSystemPrompt, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
specificInstractions.setEnabled(useSpecificInstructions.volatileValue()); systemPrompt.setEnabled(useSystemPrompt.volatileValue());
}); });
connect(&resetToDefaults, &ButtonAspect::clicked, this, &ContextSettings::resetPageToDefaults); connect(&resetToDefaults, &ButtonAspect::clicked, this, &ContextSettings::resetPageToDefaults);
} }
@ -129,8 +153,10 @@ void ContextSettings::resetPageToDefaults()
resetAspect(readStringsBeforeCursor); resetAspect(readStringsBeforeCursor);
resetAspect(readStringsAfterCursor); resetAspect(readStringsAfterCursor);
resetAspect(useFilePathInContext); resetAspect(useFilePathInContext);
resetAspect(useSpecificInstructions); resetAspect(useSystemPrompt);
resetAspect(specificInstractions); resetAspect(systemPrompt);
resetAspect(useChatSystemPrompt);
resetAspect(chatSystemPrompt);
} }
} }

View File

@ -34,11 +34,13 @@ public:
Utils::IntegerAspect readStringsBeforeCursor{this}; Utils::IntegerAspect readStringsBeforeCursor{this};
Utils::IntegerAspect readStringsAfterCursor{this}; Utils::IntegerAspect readStringsAfterCursor{this};
Utils::StringAspect specificInstractions{this}; Utils::BoolAspect useSystemPrompt{this};
Utils::BoolAspect useSpecificInstructions{this}; Utils::StringAspect systemPrompt{this};
Utils::BoolAspect useFilePathInContext{this}; Utils::BoolAspect useFilePathInContext{this};
Utils::BoolAspect useProjectChangesCache{this}; Utils::BoolAspect useProjectChangesCache{this};
Utils::IntegerAspect maxChangesCacheSize{this}; Utils::IntegerAspect maxChangesCacheSize{this};
Utils::BoolAspect useChatSystemPrompt{this};
Utils::StringAspect chatSystemPrompt{this};
ButtonAspect resetToDefaults{this}; ButtonAspect resetToDefaults{this};

View File

@ -26,8 +26,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include "QodeAssistConstants.hpp" #include "SettingsConstants.hpp"
#include "QodeAssisttr.h"
namespace QodeAssist::Settings { namespace QodeAssist::Settings {
@ -85,12 +84,13 @@ CustomPromptSettings::CustomPromptSettings()
setLayouter([this]() { setLayouter([this]() {
using namespace Layouting; using namespace Layouting;
return Column{Row{customJsonLabel, Stretch{1}, resetToDefaults}, return Column{Group{title(Tr::tr("Custom prompt for FIM model")),
Column{Row{customJsonLabel, Stretch{1}, resetToDefaults},
Row{customJsonTemplate, Row{customJsonTemplate,
Column{saveCustomTemplateButton, Column{saveCustomTemplateButton,
loadCustomTemplateButton, loadCustomTemplateButton,
customJsonLegend, customJsonLegend,
Stretch{1}}}}; Stretch{1}}}}}};
}); });
} }

View File

@ -19,7 +19,7 @@
#pragma once #pragma once
#include "settings/SettingsUtils.hpp" #include "SettingsUtils.hpp"
#include <utils/aspects.h> #include <utils/aspects.h>
namespace QodeAssist::Settings { namespace QodeAssist::Settings {

View File

@ -26,11 +26,12 @@
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
#include "LLMProvidersManager.hpp" #include "Logger.hpp"
#include "PromptTemplateManager.hpp" #include "PromptTemplateManager.hpp"
#include "QodeAssistConstants.hpp" #include "Provider.hpp"
#include "QodeAssistUtils.hpp" #include "ProvidersManager.hpp"
#include "QodeAssisttr.h" #include "SettingsConstants.hpp"
#include "SettingsUtils.hpp"
namespace QodeAssist::Settings { namespace QodeAssist::Settings {
@ -128,27 +129,34 @@ GeneralSettings::GeneralSettings()
chatPrompts.setSettingsKey(Constants::CHAT_PROMPTS); chatPrompts.setSettingsKey(Constants::CHAT_PROMPTS);
chatPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox); chatPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
chatTokensThreshold.setSettingsKey(Constants::CHAT_TOKENS_THRESHOLD);
chatTokensThreshold.setLabelText(Tr::tr("Chat History Token Limit"));
chatTokensThreshold.setToolTip(Tr::tr("Maximum number of tokens in chat history. When "
"exceeded, oldest messages will be removed."));
chatTokensThreshold.setRange(1000, 16000);
chatTokensThreshold.setDefaultValue(4000);
loadProviders(); loadProviders();
loadPrompts(); loadPrompts();
llmProviders.setDefaultValue(llmProviders.indexForDisplay("Ollama")); llmProviders.setDefaultValue(llmProviders.indexForDisplay("Ollama"));
chatLlmProviders.setDefaultValue(chatLlmProviders.indexForDisplay("Ollama")); chatLlmProviders.setDefaultValue(chatLlmProviders.indexForDisplay("Ollama"));
fimPrompts.setDefaultValue(fimPrompts.indexForDisplay("CodeLLama FIM")); fimPrompts.setDefaultValue(fimPrompts.indexForDisplay("CodeLlama FIM"));
chatPrompts.setDefaultValue(chatPrompts.indexForDisplay("CodeLLama Chat")); chatPrompts.setDefaultValue(chatPrompts.indexForDisplay("CodeLlama Chat"));
readSettings();
auto fimProviderName = llmProviders.displayForIndex(llmProviders.value()); auto fimProviderName = llmProviders.displayForIndex(llmProviders.value());
setCurrentFimProvider(fimProviderName); setCurrentFimProvider(fimProviderName);
auto chatProviderName = chatLlmProviders.displayForIndex(chatLlmProviders.value()); auto chatProviderName = chatLlmProviders.displayForIndex(chatLlmProviders.value());
setCurrentChatProvider(chatProviderName); setCurrentChatProvider(chatProviderName);
auto nameFimPromts = fimPrompts.displayForIndex(fimPrompts.value()); readSettings();
PromptTemplateManager::instance().setCurrentFimTemplate(nameFimPromts);
auto nameChatPromts = chatPrompts.displayForIndex(chatPrompts.value());
PromptTemplateManager::instance().setCurrentChatTemplate(nameChatPromts);
setLoggingEnabled(enableLogging()); auto nameFimPromts = fimPrompts.displayForIndex(fimPrompts.value());
LLMCore::PromptTemplateManager::instance().setCurrentFimTemplate(nameFimPromts);
auto nameChatPromts = chatPrompts.displayForIndex(chatPrompts.value());
LLMCore::PromptTemplateManager::instance().setCurrentChatTemplate(nameChatPromts);
Logger::instance().setLoggingEnabled(enableLogging());
setupConnections(); setupConnections();
@ -157,26 +165,26 @@ GeneralSettings::GeneralSettings()
auto rootLayout auto rootLayout
= Column{Row{enableQodeAssist, Stretch{1}, resetToDefaults}, = Column{Row{enableQodeAssist, Stretch{1}, resetToDefaults},
enableAutoComplete, Row{enableLogging, Stretch{1}},
Space{8},
Group{title(Tr::tr("AI Suggestions")),
Column{enableAutoComplete,
multiLineCompletion, multiLineCompletion,
Row{autoCompletionCharThreshold, Row{autoCompletionCharThreshold,
autoCompletionTypingInterval, autoCompletionTypingInterval,
startSuggestionTimer, startSuggestionTimer,
Stretch{1}}, Stretch{1}},
Space{8}, Row{llmProviders, Stretch{1}},
enableLogging,
Space{8},
Group{title(Tr::tr("AI Suggestions")),
Column{Row{llmProviders, Stretch{1}},
Row{url, endPoint, fimUrlIndicator}, Row{url, endPoint, fimUrlIndicator},
Row{selectModels, modelName, fimModelIndicator}, Row{selectModels, modelName, fimModelIndicator},
Row{fimPrompts, Stretch{1}}}}, Row{fimPrompts, Stretch{1}}}},
Space{16}, Space{16},
Group{title(Tr::tr("AI Chat(experimental)")), Group{title(Tr::tr("AI Chat")),
Column{Row{chatLlmProviders, Stretch{1}}, Column{Row{chatLlmProviders, Stretch{1}},
Row{chatUrl, chatEndPoint, chatUrlIndicator}, Row{chatUrl, chatEndPoint, chatUrlIndicator},
Row{chatSelectModels, chatModelName, chatModelIndicator}, Row{chatSelectModels, chatModelName, chatModelIndicator},
Row{chatPrompts, Stretch{1}}}}, Row{chatPrompts, Stretch{1}},
Row{chatTokensThreshold, Stretch{1}}}},
Stretch{1}}; Stretch{1}};
return rootLayout; return rootLayout;
}); });
@ -199,24 +207,26 @@ void GeneralSettings::setupConnections()
connect(&fimPrompts, &Utils::SelectionAspect::volatileValueChanged, this, [this]() { connect(&fimPrompts, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
int index = fimPrompts.volatileValue(); int index = fimPrompts.volatileValue();
PromptTemplateManager::instance().setCurrentFimTemplate(fimPrompts.displayForIndex(index)); LLMCore::PromptTemplateManager::instance().setCurrentFimTemplate(
fimPrompts.displayForIndex(index));
}); });
connect(&chatPrompts, &Utils::SelectionAspect::volatileValueChanged, this, [this]() { connect(&chatPrompts, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
int index = chatPrompts.volatileValue(); int index = chatPrompts.volatileValue();
PromptTemplateManager::instance().setCurrentChatTemplate(chatPrompts.displayForIndex(index)); LLMCore::PromptTemplateManager::instance().setCurrentChatTemplate(
chatPrompts.displayForIndex(index));
}); });
connect(&selectModels, &ButtonAspect::clicked, this, [this]() { connect(&selectModels, &ButtonAspect::clicked, this, [this]() {
auto *provider = LLMProvidersManager::instance().getCurrentFimProvider(); auto *provider = LLMCore::ProvidersManager::instance().getCurrentFimProvider();
showModelSelectionDialog(&modelName, provider); showModelSelectionDialog(&modelName, provider);
}); });
connect(&chatSelectModels, &ButtonAspect::clicked, this, [this]() { connect(&chatSelectModels, &ButtonAspect::clicked, this, [this]() {
auto *provider = LLMProvidersManager::instance().getCurrentChatProvider(); auto *provider = LLMCore::ProvidersManager::instance().getCurrentChatProvider();
showModelSelectionDialog(&chatModelName, provider); showModelSelectionDialog(&chatModelName, provider);
}); });
connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() { connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
setLoggingEnabled(enableLogging.volatileValue()); Logger::instance().setLoggingEnabled(enableLogging.volatileValue());
}); });
connect(&resetToDefaults, &ButtonAspect::clicked, this, &GeneralSettings::resetPageToDefaults); connect(&resetToDefaults, &ButtonAspect::clicked, this, &GeneralSettings::resetPageToDefaults);
@ -239,7 +249,7 @@ void GeneralSettings::setupConnections()
} }
void GeneralSettings::showModelSelectionDialog(Utils::StringAspect *modelNameObj, void GeneralSettings::showModelSelectionDialog(Utils::StringAspect *modelNameObj,
Providers::LLMProvider *provider) LLMCore::Provider *provider)
{ {
Utils::Environment env = Utils::Environment::systemEnvironment(); Utils::Environment env = Utils::Environment::systemEnvironment();
QString providerUrl = (modelNameObj == &modelName) ? url() : chatUrl(); QString providerUrl = (modelNameObj == &modelName) ? url() : chatUrl();
@ -282,6 +292,7 @@ void GeneralSettings::resetPageToDefaults()
resetAspect(chatLlmProviders); resetAspect(chatLlmProviders);
resetAspect(fimPrompts); resetAspect(fimPrompts);
resetAspect(chatPrompts); resetAspect(chatPrompts);
resetAspect(chatTokensThreshold);
} }
modelName.setVolatileValue(""); modelName.setVolatileValue("");
@ -301,12 +312,12 @@ void GeneralSettings::updateStatusIndicators()
bool fimPingSuccessful = false; bool fimPingSuccessful = false;
if (fimUrlValid) { if (fimUrlValid) {
QUrl pingUrl(url.volatileValue()); QUrl pingUrl(url.volatileValue());
fimPingSuccessful = QodeAssist::pingUrl(pingUrl); fimPingSuccessful = Settings::pingUrl(pingUrl);
} }
bool chatPingSuccessful = false; bool chatPingSuccessful = false;
if (chatUrlValid) { if (chatUrlValid) {
QUrl pingUrl(chatUrl.volatileValue()); QUrl pingUrl(chatUrl.volatileValue());
chatPingSuccessful = QodeAssist::pingUrl(pingUrl); chatPingSuccessful = Settings::pingUrl(pingUrl);
} }
setIndicatorStatus(fimModelIndicator, setIndicatorStatus(fimModelIndicator,
@ -339,7 +350,7 @@ void GeneralSettings::setIndicatorStatus(Utils::StringAspect &indicator,
void GeneralSettings::setCurrentFimProvider(const QString &name) void GeneralSettings::setCurrentFimProvider(const QString &name)
{ {
const auto provider = LLMProvidersManager::instance().setCurrentFimProvider(name); const auto provider = LLMCore::ProvidersManager::instance().setCurrentFimProvider(name);
if (!provider) if (!provider)
return; return;
@ -349,7 +360,7 @@ void GeneralSettings::setCurrentFimProvider(const QString &name)
void GeneralSettings::setCurrentChatProvider(const QString &name) void GeneralSettings::setCurrentChatProvider(const QString &name)
{ {
const auto provider = LLMProvidersManager::instance().setCurrentChatProvider(name); const auto provider = LLMCore::ProvidersManager::instance().setCurrentChatProvider(name);
if (!provider) if (!provider)
return; return;
@ -359,7 +370,7 @@ void GeneralSettings::setCurrentChatProvider(const QString &name)
void GeneralSettings::loadProviders() void GeneralSettings::loadProviders()
{ {
for (const auto &name : LLMProvidersManager::instance().providersNames()) { for (const auto &name : LLMCore::ProvidersManager::instance().providersNames()) {
llmProviders.addOption(name); llmProviders.addOption(name);
chatLlmProviders.addOption(name); chatLlmProviders.addOption(name);
} }
@ -367,10 +378,10 @@ void GeneralSettings::loadProviders()
void GeneralSettings::loadPrompts() void GeneralSettings::loadPrompts()
{ {
for (const auto &name : PromptTemplateManager::instance().fimTemplatesNames()) { for (const auto &name : LLMCore::PromptTemplateManager::instance().fimTemplatesNames()) {
fimPrompts.addOption(name); fimPrompts.addOption(name);
} }
for (const auto &name : PromptTemplateManager::instance().chatTemplatesNames()) { for (const auto &name : LLMCore::PromptTemplateManager::instance().chatTemplatesNames()) {
chatPrompts.addOption(name); chatPrompts.addOption(name);
} }
} }

View File

@ -21,9 +21,11 @@
#include <utils/aspects.h> #include <utils/aspects.h>
#include "providers/LLMProvider.hpp" #include "SettingsUtils.hpp"
#include "settings/SettingsUtils.hpp"
namespace QodeAssist::LLMCore {
class Provider;
}
namespace QodeAssist::Settings { namespace QodeAssist::Settings {
class GeneralSettings : public Utils::AspectContainer class GeneralSettings : public Utils::AspectContainer
@ -61,10 +63,11 @@ public:
Utils::StringAspect chatModelIndicator{this}; Utils::StringAspect chatModelIndicator{this};
Utils::StringAspect chatUrlIndicator{this}; Utils::StringAspect chatUrlIndicator{this};
Utils::IntegerAspect chatTokensThreshold{this};
private: private:
void setupConnections(); void setupConnections();
void showModelSelectionDialog(Utils::StringAspect *modelNameObj, void showModelSelectionDialog(Utils::StringAspect *modelNameObj, LLMCore::Provider *provider);
Providers::LLMProvider *provider);
void resetPageToDefaults(); void resetPageToDefaults();
void updateStatusIndicators(); void updateStatusIndicators();

View File

@ -24,8 +24,8 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include "QodeAssistConstants.hpp" #include "RequestType.hpp"
#include "QodeAssisttr.h" #include "SettingsConstants.hpp"
namespace QodeAssist::Settings { namespace QodeAssist::Settings {
@ -41,63 +41,121 @@ PresetPromptsSettings::PresetPromptsSettings()
setDisplayName(Tr::tr("Preset Prompts Params")); setDisplayName(Tr::tr("Preset Prompts Params"));
temperature.setSettingsKey(Constants::TEMPERATURE); fimTemperature.setSettingsKey(Constants::FIM_TEMPERATURE);
temperature.setLabelText(Tr::tr("Temperature:")); fimTemperature.setLabelText(Tr::tr("Temperature:"));
temperature.setDefaultValue(0.2); fimTemperature.setDefaultValue(0.2);
temperature.setRange(0.0, 10.0); fimTemperature.setRange(0.0, 10.0);
temperature.setSingleStep(0.1); fimTemperature.setSingleStep(0.1);
ollamaLivetime.setSettingsKey(Constants::OLLAMA_LIVETIME); chatTemperature.setSettingsKey(Constants::CHAT_TEMPERATURE);
ollamaLivetime.setLabelText( chatTemperature.setLabelText(Tr::tr("Temperature:"));
chatTemperature.setDefaultValue(0.5);
chatTemperature.setRange(0.0, 10.0);
chatTemperature.setSingleStep(0.1);
fimOllamaLivetime.setSettingsKey(Constants::FIM_OLLAMA_LIVETIME);
fimOllamaLivetime.setLabelText(
Tr::tr("Time to suspend Ollama after completion request (in minutes), " Tr::tr("Time to suspend Ollama after completion request (in minutes), "
"Only Ollama, -1 to disable")); "Only Ollama, -1 to disable"));
ollamaLivetime.setDefaultValue("5m"); fimOllamaLivetime.setDefaultValue("5m");
ollamaLivetime.setDisplayStyle(Utils::StringAspect::LineEditDisplay); fimOllamaLivetime.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
maxTokens.setSettingsKey(Constants::MAX_TOKENS); chatOllamaLivetime.setSettingsKey(Constants::CHAT_OLLAMA_LIVETIME);
maxTokens.setLabelText(Tr::tr("Max Tokens")); chatOllamaLivetime.setLabelText(
maxTokens.setRange(-1, 10000); Tr::tr("Time to suspend Ollama after completion request (in minutes), "
maxTokens.setDefaultValue(150); "Only Ollama, -1 to disable"));
chatOllamaLivetime.setDefaultValue("5m");
chatOllamaLivetime.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
useTopP.setSettingsKey(Constants::USE_TOP_P); fimMaxTokens.setSettingsKey(Constants::FIM_MAX_TOKENS);
useTopP.setDefaultValue(false); fimMaxTokens.setLabelText(Tr::tr("Max Tokens"));
fimMaxTokens.setRange(-1, 10000);
fimMaxTokens.setDefaultValue(50);
topP.setSettingsKey(Constants::TOP_P); chatMaxTokens.setSettingsKey(Constants::CHAT_MAX_TOKENS);
topP.setLabelText(Tr::tr("use top_p")); chatMaxTokens.setLabelText(Tr::tr("Max Tokens"));
topP.setDefaultValue(0.9); chatMaxTokens.setRange(-1, 10000);
topP.setRange(0.0, 1.0); chatMaxTokens.setDefaultValue(2000);
topP.setSingleStep(0.1);
useTopK.setSettingsKey(Constants::USE_TOP_K); fimUseTopP.setSettingsKey(Constants::FIM_USE_TOP_P);
useTopK.setDefaultValue(false); fimUseTopP.setDefaultValue(false);
topK.setSettingsKey(Constants::TOP_K); fimTopP.setSettingsKey(Constants::FIM_TOP_P);
topK.setLabelText(Tr::tr("use top_k")); fimTopP.setLabelText(Tr::tr("use top_p"));
topK.setDefaultValue(50); fimTopP.setDefaultValue(0.9);
topK.setRange(1, 1000); fimTopP.setRange(0.0, 1.0);
fimTopP.setSingleStep(0.1);
usePresencePenalty.setSettingsKey(Constants::USE_PRESENCE_PENALTY); chatUseTopP.setSettingsKey(Constants::CHAT_USE_TOP_P);
usePresencePenalty.setDefaultValue(false); chatUseTopP.setDefaultValue(false);
presencePenalty.setSettingsKey(Constants::PRESENCE_PENALTY); chatTopP.setSettingsKey(Constants::CHAT_TOP_P);
presencePenalty.setLabelText(Tr::tr("use presence_penalty")); chatTopP.setLabelText(Tr::tr("use top_p"));
presencePenalty.setDefaultValue(0.0); chatTopP.setDefaultValue(0.9);
presencePenalty.setRange(-2.0, 2.0); chatTopP.setRange(0.0, 1.0);
presencePenalty.setSingleStep(0.1); chatTopP.setSingleStep(0.1);
useFrequencyPenalty.setSettingsKey(Constants::USE_FREQUENCY_PENALTY); fimUseTopK.setSettingsKey(Constants::FIM_USE_TOP_K);
useFrequencyPenalty.setDefaultValue(false); fimUseTopK.setDefaultValue(false);
frequencyPenalty.setSettingsKey(Constants::FREQUENCY_PENALTY); fimTopK.setSettingsKey(Constants::FIM_TOP_K);
frequencyPenalty.setLabelText(Tr::tr("use frequency_penalty")); fimTopK.setLabelText(Tr::tr("use top_k"));
frequencyPenalty.setDefaultValue(0.0); fimTopK.setDefaultValue(50);
frequencyPenalty.setRange(-2.0, 2.0); fimTopK.setRange(1, 1000);
frequencyPenalty.setSingleStep(0.1);
apiKey.setSettingsKey(Constants::API_KEY); chatUseTopK.setSettingsKey(Constants::CHAT_USE_TOP_K);
apiKey.setLabelText(Tr::tr("API Key:")); chatUseTopK.setDefaultValue(false);
apiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
apiKey.setPlaceHolderText(Tr::tr("Enter your API key here")); chatTopK.setSettingsKey(Constants::CHAT_TOP_K);
chatTopK.setLabelText(Tr::tr("use top_k"));
chatTopK.setDefaultValue(50);
chatTopK.setRange(1, 1000);
fimUsePresencePenalty.setSettingsKey(Constants::FIM_USE_PRESENCE_PENALTY);
fimUsePresencePenalty.setDefaultValue(false);
fimPresencePenalty.setSettingsKey(Constants::FIM_PRESENCE_PENALTY);
fimPresencePenalty.setLabelText(Tr::tr("use presence_penalty"));
fimPresencePenalty.setDefaultValue(0.0);
fimPresencePenalty.setRange(-2.0, 2.0);
fimPresencePenalty.setSingleStep(0.1);
chatUsePresencePenalty.setSettingsKey(Constants::CHAT_USE_PRESENCE_PENALTY);
chatUsePresencePenalty.setDefaultValue(false);
chatPresencePenalty.setSettingsKey(Constants::CHAT_PRESENCE_PENALTY);
chatPresencePenalty.setLabelText(Tr::tr("use presence_penalty"));
chatPresencePenalty.setDefaultValue(0.0);
chatPresencePenalty.setRange(-2.0, 2.0);
chatPresencePenalty.setSingleStep(0.1);
fimUseFrequencyPenalty.setSettingsKey(Constants::FIM_USE_FREQUENCY_PENALTY);
fimUseFrequencyPenalty.setDefaultValue(false);
fimFrequencyPenalty.setSettingsKey(Constants::FIM_FREQUENCY_PENALTY);
fimFrequencyPenalty.setLabelText(Tr::tr("use frequency_penalty"));
fimFrequencyPenalty.setDefaultValue(0.0);
fimFrequencyPenalty.setRange(-2.0, 2.0);
fimFrequencyPenalty.setSingleStep(0.1);
chatUseFrequencyPenalty.setSettingsKey(Constants::CHAT_USE_FREQUENCY_PENALTY);
chatUseFrequencyPenalty.setDefaultValue(false);
chatFrequencyPenalty.setSettingsKey(Constants::CHAT_FREQUENCY_PENALTY);
chatFrequencyPenalty.setLabelText(Tr::tr("use frequency_penalty"));
chatFrequencyPenalty.setDefaultValue(0.0);
chatFrequencyPenalty.setRange(-2.0, 2.0);
chatFrequencyPenalty.setSingleStep(0.1);
fimApiKey.setSettingsKey(Constants::FIM_API_KEY);
fimApiKey.setLabelText(Tr::tr("API Key:"));
fimApiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
fimApiKey.setPlaceHolderText(Tr::tr("Enter your API key here"));
chatApiKey.setSettingsKey(Constants::CHAT_API_KEY);
chatApiKey.setLabelText(Tr::tr("API Key:"));
chatApiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
chatApiKey.setPlaceHolderText(Tr::tr("Enter your API key here"));
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults"); resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
@ -107,17 +165,64 @@ PresetPromptsSettings::PresetPromptsSettings()
setLayouter([this]() { setLayouter([this]() {
using namespace Layouting; using namespace Layouting;
return Column{Row{temperature, Stretch{1}, resetToDefaults}, return Column{Row{Stretch{1}, resetToDefaults},
Row{maxTokens, Stretch{1}}, Group{title(Tr::tr("Prompt settings for FIM")),
Row{useTopP, topP, Stretch{1}}, Column{Row{fimTemperature, Stretch{1}},
Row{useTopK, topK, Stretch{1}}, Row{fimMaxTokens, Stretch{1}},
Row{usePresencePenalty, presencePenalty, Stretch{1}}, Row{fimUseTopP, fimTopP, Stretch{1}},
Row{useFrequencyPenalty, frequencyPenalty, Stretch{1}}, Row{fimUseTopK, fimTopK, Stretch{1}},
apiKey, Row{fimUsePresencePenalty, fimPresencePenalty, Stretch{1}},
Row{fimUseFrequencyPenalty, fimFrequencyPenalty, Stretch{1}},
Row{fimOllamaLivetime, Stretch{1}},
fimApiKey}},
Space{16},
Group{title(Tr::tr("Prompt settings for Chat")),
Column{Row{chatTemperature, Stretch{1}},
Row{chatMaxTokens, Stretch{1}},
Row{chatUseTopP, chatTopP, Stretch{1}},
Row{chatUseTopK, chatTopK, Stretch{1}},
Row{fimUsePresencePenalty, fimPresencePenalty, Stretch{1}},
Row{fimUseFrequencyPenalty, fimFrequencyPenalty, Stretch{1}},
Row{chatOllamaLivetime, Stretch{1}},
chatApiKey}},
Stretch{1}}; Stretch{1}};
}); });
} }
PresetPromptsSettings::PromptSettings PresetPromptsSettings::getSettings(int type) const
{
auto reqtype = static_cast<LLMCore::RequestType>(type);
PromptSettings settings;
if (reqtype == LLMCore::RequestType::Fim) {
settings.temperature = fimTemperature();
settings.maxTokens = fimMaxTokens();
settings.useTopP = fimUseTopP();
settings.topP = fimTopP();
settings.useTopK = fimUseTopK();
settings.topK = fimTopK();
settings.usePresencePenalty = fimUsePresencePenalty();
settings.presencePenalty = fimPresencePenalty();
settings.useFrequencyPenalty = fimUseFrequencyPenalty();
settings.frequencyPenalty = fimFrequencyPenalty();
settings.ollamaLivetime = fimOllamaLivetime();
settings.apiKey = fimApiKey();
} else if (reqtype == LLMCore::RequestType::Chat) {
settings.temperature = chatTemperature();
settings.maxTokens = chatMaxTokens();
settings.useTopP = chatUseTopP();
settings.topP = chatTopP();
settings.useTopK = chatUseTopK();
settings.topK = chatTopK();
settings.usePresencePenalty = chatUsePresencePenalty();
settings.presencePenalty = chatPresencePenalty();
settings.useFrequencyPenalty = chatUseFrequencyPenalty();
settings.frequencyPenalty = chatFrequencyPenalty();
settings.ollamaLivetime = chatOllamaLivetime();
settings.apiKey = chatApiKey();
}
return settings;
}
void PresetPromptsSettings::setupConnections() void PresetPromptsSettings::setupConnections()
{ {
connect(&resetToDefaults, connect(&resetToDefaults,
@ -136,17 +241,28 @@ void PresetPromptsSettings::resetSettingsToDefaults()
QMessageBox::Yes | QMessageBox::No); QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) { if (reply == QMessageBox::Yes) {
resetAspect(temperature); resetAspect(fimTemperature);
resetAspect(maxTokens); resetAspect(fimMaxTokens);
resetAspect(ollamaLivetime); resetAspect(fimOllamaLivetime);
resetAspect(useTopP); resetAspect(fimUseTopP);
resetAspect(topP); resetAspect(fimTopP);
resetAspect(useTopK); resetAspect(fimUseTopK);
resetAspect(topK); resetAspect(fimTopK);
resetAspect(usePresencePenalty); resetAspect(fimUsePresencePenalty);
resetAspect(presencePenalty); resetAspect(fimPresencePenalty);
resetAspect(useFrequencyPenalty); resetAspect(fimUseFrequencyPenalty);
resetAspect(frequencyPenalty); resetAspect(fimFrequencyPenalty);
resetAspect(chatTemperature);
resetAspect(chatMaxTokens);
resetAspect(chatUseTopP);
resetAspect(chatTopP);
resetAspect(chatUseTopK);
resetAspect(chatTopK);
resetAspect(chatUsePresencePenalty);
resetAspect(chatPresencePenalty);
resetAspect(chatUseFrequencyPenalty);
resetAspect(chatFrequencyPenalty);
resetAspect(chatOllamaLivetime);
} }
} }

View File

@ -19,7 +19,7 @@
#pragma once #pragma once
#include "settings/SettingsUtils.hpp" #include "SettingsUtils.hpp"
#include <utils/aspects.h> #include <utils/aspects.h>
namespace QodeAssist::Settings { namespace QodeAssist::Settings {
@ -27,28 +27,63 @@ namespace QodeAssist::Settings {
class PresetPromptsSettings : public Utils::AspectContainer class PresetPromptsSettings : public Utils::AspectContainer
{ {
public: public:
struct PromptSettings
{
double temperature;
int maxTokens;
bool useTopP;
double topP;
bool useTopK;
int topK;
bool usePresencePenalty;
double presencePenalty;
bool useFrequencyPenalty;
double frequencyPenalty;
QString ollamaLivetime;
QString apiKey;
};
PresetPromptsSettings(); PresetPromptsSettings();
Utils::DoubleAspect temperature{this}; Utils::DoubleAspect fimTemperature{this};
Utils::IntegerAspect maxTokens{this}; Utils::IntegerAspect fimMaxTokens{this};
Utils::BoolAspect useTopP{this}; Utils::DoubleAspect chatTemperature{this};
Utils::DoubleAspect topP{this}; Utils::IntegerAspect chatMaxTokens{this};
Utils::BoolAspect useTopK{this}; Utils::BoolAspect fimUseTopP{this};
Utils::IntegerAspect topK{this}; Utils::DoubleAspect fimTopP{this};
Utils::BoolAspect usePresencePenalty{this}; Utils::BoolAspect chatUseTopP{this};
Utils::DoubleAspect presencePenalty{this}; Utils::DoubleAspect chatTopP{this};
Utils::BoolAspect useFrequencyPenalty{this}; Utils::BoolAspect fimUseTopK{this};
Utils::DoubleAspect frequencyPenalty{this}; Utils::IntegerAspect fimTopK{this};
Utils::StringAspect ollamaLivetime{this}; Utils::BoolAspect chatUseTopK{this};
Utils::StringAspect apiKey{this}; Utils::IntegerAspect chatTopK{this};
Utils::BoolAspect fimUsePresencePenalty{this};
Utils::DoubleAspect fimPresencePenalty{this};
Utils::BoolAspect chatUsePresencePenalty{this};
Utils::DoubleAspect chatPresencePenalty{this};
Utils::BoolAspect fimUseFrequencyPenalty{this};
Utils::DoubleAspect fimFrequencyPenalty{this};
Utils::BoolAspect chatUseFrequencyPenalty{this};
Utils::DoubleAspect chatFrequencyPenalty{this};
Utils::StringAspect fimOllamaLivetime{this};
Utils::StringAspect chatOllamaLivetime{this};
Utils::StringAspect fimApiKey{this};
Utils::StringAspect chatApiKey{this};
ButtonAspect resetToDefaults{this}; ButtonAspect resetToDefaults{this};
PromptSettings getSettings(int type) const;
private: private:
void setupConnections(); void setupConnections();
void resetSettingsToDefaults(); void resetSettingsToDefaults();

View File

@ -0,0 +1,102 @@
/*
* 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
namespace QodeAssist::Constants {
const char ACTION_ID[] = "QodeAssist.Action";
const char MENU_ID[] = "QodeAssist.Menu";
// settings
const char ENABLE_QODE_ASSIST[] = "QodeAssist.enableQodeAssist";
const char ENABLE_AUTO_COMPLETE[] = "QodeAssist.enableAutoComplete";
const char ENABLE_LOGGING[] = "QodeAssist.enableLogging";
const char LLM_PROVIDERS[] = "QodeAssist.llmProviders";
const char URL[] = "QodeAssist.url";
const char END_POINT[] = "QodeAssist.endPoint";
const char MODEL_NAME[] = "QodeAssist.modelName";
const char SELECT_MODELS[] = "QodeAssist.selectModels";
const char FIM_PROMPTS[] = "QodeAssist.fimPrompts";
const char PROVIDER_PATHS[] = "QodeAssist.providerPaths";
const char START_SUGGESTION_TIMER[] = "QodeAssist.startSuggestionTimer";
const char AUTO_COMPLETION_CHAR_THRESHOLD[] = "QodeAssist.autoCompletionCharThreshold";
const char AUTO_COMPLETION_TYPING_INTERVAL[] = "QodeAssist.autoCompletionTypingInterval";
const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
const char MULTILINE_COMPLETION[] = "QodeAssist.multilineCompletion";
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
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 CHAT_TOKENS_THRESHOLD[] = "QodeAssist.chatTokensThreshold";
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
const char QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID[] = "QodeAssist.1GeneralSettingsPageId";
const char QODE_ASSIST_CONTEXT_SETTINGS_PAGE_ID[] = "QodeAssist.2ContextSettingsPageId";
const char QODE_ASSIST_PRESET_PROMPTS_SETTINGS_PAGE_ID[]
= "QodeAssist.3PresetPromptsSettingsPageId";
const char QODE_ASSIST_CUSTOM_PROMPT_SETTINGS_PAGE_ID[] = "QodeAssist.4CustomPromptSettingsPageId";
const char QODE_ASSIST_GENERAL_OPTIONS_CATEGORY[] = "QodeAssist.Category";
const char QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY[] = "Qode Assist";
const char QODE_ASSIST_REQUEST_SUGGESTION[] = "QodeAssist.RequestSuggestion";
// context settings
const char READ_FULL_FILE[] = "QodeAssist.readFullFile";
const char READ_STRINGS_BEFORE_CURSOR[] = "QodeAssist.readStringsBeforeCursor";
const char READ_STRINGS_AFTER_CURSOR[] = "QodeAssist.readStringsAfterCursor";
const char USE_SYSTEM_PROMPT[] = "QodeAssist.useSystemPrompt";
const char USE_FILE_PATH_IN_CONTEXT[] = "QodeAssist.useFilePathInContext";
const char SYSTEM_PROMPT[] = "QodeAssist.systemPrompt";
const char USE_PROJECT_CHANGES_CACHE[] = "QodeAssist.useProjectChangesCache";
const char MAX_CHANGES_CACHE_SIZE[] = "QodeAssist.maxChangesCacheSize";
const char USE_CHAT_SYSTEM_PROMPT[] = "QodeAssist.useChatSystemPrompt";
const char CHAT_SYSTEM_PROMPT[] = "QodeAssist.chatSystemPrompt";
// preset prompt settings
const char FIM_TEMPERATURE[] = "QodeAssist.fimTemperature";
const char FIM_MAX_TOKENS[] = "QodeAssist.fimMaxTokens";
const char FIM_USE_TOP_P[] = "QodeAssist.fimUseTopP";
const char FIM_TOP_P[] = "QodeAssist.fimTopP";
const char FIM_USE_TOP_K[] = "QodeAssist.fimUseTopK";
const char FIM_TOP_K[] = "QodeAssist.fimTopK";
const char FIM_USE_PRESENCE_PENALTY[] = "QodeAssist.fimUsePresencePenalty";
const char FIM_PRESENCE_PENALTY[] = "QodeAssist.fimPresencePenalty";
const char FIM_USE_FREQUENCY_PENALTY[] = "QodeAssist.fimUseFrequencyPenalty";
const char FIM_FREQUENCY_PENALTY[] = "QodeAssist.fimFrequencyPenalty";
const char FIM_OLLAMA_LIVETIME[] = "QodeAssist.fimOllamaLivetime";
const char FIM_API_KEY[] = "QodeAssist.apiKey";
const char CHAT_TEMPERATURE[] = "QodeAssist.chatTemperature";
const char CHAT_MAX_TOKENS[] = "QodeAssist.chatMaxTokens";
const char CHAT_USE_TOP_P[] = "QodeAssist.chatUseTopP";
const char CHAT_TOP_P[] = "QodeAssist.chatTopP";
const char CHAT_USE_TOP_K[] = "QodeAssist.chatUseTopK";
const char CHAT_TOP_K[] = "QodeAssist.chatTopK";
const char CHAT_USE_PRESENCE_PENALTY[] = "QodeAssist.chatUsePresencePenalty";
const char CHAT_PRESENCE_PENALTY[] = "QodeAssist.chatPresencePenalty";
const char CHAT_USE_FREQUENCY_PENALTY[] = "QodeAssist.chatUseFrequencyPenalty";
const char CHAT_FREQUENCY_PENALTY[] = "QodeAssist.chatFrequencyPenalty";
const char CHAT_OLLAMA_LIVETIME[] = "QodeAssist.chatOllamaLivetime";
const char CHAT_API_KEY[] = "QodeAssist.chatApiKey";
} // namespace QodeAssist::Constants

View File

@ -19,12 +19,53 @@
#pragma once #pragma once
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QPushButton> #include <QPushButton>
#include <QtCore/qtimer.h>
#include <utils/aspects.h> #include <utils/aspects.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
namespace QodeAssist::Settings { namespace QodeAssist::Settings {
struct Tr
{
Q_DECLARE_TR_FUNCTIONS(QtC::QodeAssist)
};
inline bool pingUrl(const QUrl &url, int timeout = 5000)
{
if (!url.isValid()) {
return false;
}
QNetworkAccessManager manager;
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, true);
QScopedPointer<QNetworkReply> reply(manager.get(request));
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
QObject::connect(reply.data(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
timer.start(timeout);
loop.exec();
if (timer.isActive()) {
timer.stop();
return (reply->error() == QNetworkReply::NoError);
} else {
QObject::disconnect(reply.data(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
reply->abort();
return false;
}
}
template<typename AspectType> template<typename AspectType>
void resetAspect(AspectType &aspect) void resetAspect(AspectType &aspect)
{ {

View File

@ -20,19 +20,19 @@
#pragma once #pragma once
#include <QtCore/qjsonarray.h> #include <QtCore/qjsonarray.h>
#include "PromptTemplate.hpp" #include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates { namespace QodeAssist::Templates {
class CodeLlamaInstructTemplate : public PromptTemplate class CodeLlamaChat : public LLMCore::PromptTemplate
{ {
public: public:
TemplateType type() const override { return TemplateType::Chat; } LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
QString name() const override { return "CodeLLama Chat"; } QString name() const override { return "CodeLlama Chat"; }
QString promptTemplate() const override { return "[INST] %1 [/INST]"; } QString promptTemplate() const override { return "[INST] %1 [/INST]"; }
QStringList stopWords() const override { return QStringList() << "[INST]" << "[/INST]"; } QStringList stopWords() const override { return QStringList() << "[INST]" << "[/INST]"; }
void prepareRequest(QJsonObject &request, const ContextData &context) const override void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{ {
QString formattedPrompt = promptTemplate().arg(context.prefix); QString formattedPrompt = promptTemplate().arg(context.prefix);
QJsonArray messages = request["messages"].toArray(); QJsonArray messages = request["messages"].toArray();

View File

@ -19,24 +19,24 @@
#pragma once #pragma once
#include "PromptTemplate.hpp" #include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates { namespace QodeAssist::Templates {
class CodeLlamaFimTemplate : public PromptTemplate class CodeLlamaFim : public LLMCore::PromptTemplate
{ {
public: public:
TemplateType type() const override { return TemplateType::Fim; } LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
QString name() const override { return "CodeLLama 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
{ {
return QStringList() << "<EOT>" << "<PRE>" << "<SUF" << "<MID>"; return QStringList() << "<EOT>" << "<PRE>" << "<SUF" << "<MID>";
} }
void prepareRequest(QJsonObject &request, const ContextData &context) const override void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{ {
QString formattedPrompt = promptTemplate().arg(context.instriuctions, QString formattedPrompt = promptTemplate().arg(context.systemPrompt,
context.prefix, context.prefix,
context.suffix); context.suffix);
request["prompt"] = formattedPrompt; request["prompt"] = formattedPrompt;

View File

@ -19,20 +19,20 @@
#pragma once #pragma once
#include "PromptTemplate.hpp" #include "llmcore/PromptTemplate.hpp"
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include "QodeAssistUtils.hpp" #include "logger/Logger.hpp"
#include "settings/CustomPromptSettings.hpp" #include "settings/CustomPromptSettings.hpp"
namespace QodeAssist::Templates { namespace QodeAssist::Templates {
class CustomTemplate : public PromptTemplate class CustomTemplate : public LLMCore::PromptTemplate
{ {
public: public:
TemplateType type() const override { return TemplateType::Fim; } LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
QString name() const override { return "Custom FIM Template"; } QString name() const override { return "Custom FIM Template"; }
QString promptTemplate() const override QString promptTemplate() const override
{ {
@ -40,11 +40,11 @@ public:
} }
QStringList stopWords() const override { return QStringList(); } QStringList stopWords() const override { return QStringList(); }
void prepareRequest(QJsonObject &request, const ContextData &context) const override void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{ {
QJsonDocument doc = QJsonDocument::fromJson(promptTemplate().toUtf8()); QJsonDocument doc = QJsonDocument::fromJson(promptTemplate().toUtf8());
if (doc.isNull() || !doc.isObject()) { if (doc.isNull() || !doc.isObject()) {
logMessage(QString("Invalid JSON template in settings")); LOG_MESSAGE(QString("Invalid JSON template in settings"));
return; return;
} }
@ -58,11 +58,11 @@ public:
} }
private: private:
QJsonValue processJsonValue(const QJsonValue &value, const ContextData &context) const QJsonValue processJsonValue(const QJsonValue &value, const LLMCore::ContextData &context) const
{ {
if (value.isString()) { if (value.isString()) {
QString str = value.toString(); QString str = value.toString();
str.replace("{{QODE_INSTRUCTIONS}}", context.instriuctions); str.replace("{{QODE_INSTRUCTIONS}}", context.systemPrompt);
str.replace("{{QODE_PREFIX}}", context.prefix); str.replace("{{QODE_PREFIX}}", context.prefix);
str.replace("{{QODE_SUFFIX}}", context.suffix); str.replace("{{QODE_SUFFIX}}", context.suffix);
return str; return str;
@ -78,7 +78,8 @@ private:
return value; return value;
} }
QJsonObject processJsonTemplate(const QJsonObject &templateObj, const ContextData &context) const QJsonObject processJsonTemplate(const QJsonObject &templateObj,
const LLMCore::ContextData &context) const
{ {
QJsonObject result; QJsonObject result;
for (auto it = templateObj.begin(); it != templateObj.end(); ++it) { for (auto it = templateObj.begin(); it != templateObj.end(); ++it) {

View 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 "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class DeepSeekCoderChat : public LLMCore::PromptTemplate
{
public:
QString name() const override { return "DeepSeekCoder Chat"; }
LLMCore::TemplateType type() const override { return LLMCore::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 LLMCore::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

View File

@ -19,23 +19,23 @@
#pragma once #pragma once
#include "PromptTemplate.hpp" #include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates { namespace QodeAssist::Templates {
class DeepSeekCoderV2Template : public PromptTemplate class DeepSeekCoderFim : public LLMCore::PromptTemplate
{ {
public: public:
TemplateType type() const override { return TemplateType::Fim; } LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
QString name() const override { return "DeepSeekCoder 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>";
} }
QStringList stopWords() const override { return QStringList(); } QStringList stopWords() const override { return QStringList(); }
void prepareRequest(QJsonObject &request, const ContextData &context) const override void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{ {
QString formattedPrompt = promptTemplate().arg(context.instriuctions, QString formattedPrompt = promptTemplate().arg(context.systemPrompt,
context.prefix, context.prefix,
context.suffix); context.suffix);
request["prompt"] = formattedPrompt; request["prompt"] = formattedPrompt;

View File

@ -20,15 +20,15 @@
#pragma once #pragma once
#include <QJsonArray> #include <QJsonArray>
#include "PromptTemplate.hpp" #include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates { namespace QodeAssist::Templates {
class DeepSeekCoderChatTemplate : public PromptTemplate class QwenChat : public LLMCore::PromptTemplate
{ {
public: public:
QString name() const override { return "DeepSeek Coder Chat"; } QString name() const override { return "Qwen Chat"; }
TemplateType type() const override { return TemplateType::Chat; } LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
QString promptTemplate() const override { return "### Instruction:\n%1\n### Response:\n"; } QString promptTemplate() const override { return "### Instruction:\n%1\n### Response:\n"; }
@ -37,7 +37,7 @@ public:
return QStringList() << "### Instruction:" << "### Response:" << "\n\n### " << "<|EOT|>"; return QStringList() << "### Instruction:" << "### Response:" << "\n\n### " << "<|EOT|>";
} }
void prepareRequest(QJsonObject &request, const ContextData &context) const override void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{ {
QString formattedPrompt = promptTemplate().arg(context.prefix); QString formattedPrompt = promptTemplate().arg(context.prefix);
QJsonArray messages = request["messages"].toArray(); QJsonArray messages = request["messages"].toArray();

View File

@ -19,14 +19,14 @@
#pragma once #pragma once
#include "PromptTemplate.hpp" #include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates { namespace QodeAssist::Templates {
class StarCoder2Template : public PromptTemplate class StarCoder2Fim : public LLMCore::PromptTemplate
{ {
public: public:
TemplateType type() const override { return TemplateType::Fim; } LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
QString name() const override { return "StarCoder2 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
@ -34,9 +34,9 @@ public:
return QStringList() << "<|endoftext|>" << "<file_sep>" << "<fim_prefix>" << "<fim_suffix>" return QStringList() << "<|endoftext|>" << "<file_sep>" << "<fim_prefix>" << "<fim_suffix>"
<< "<fim_middle>"; << "<fim_middle>";
} }
void prepareRequest(QJsonObject &request, const ContextData &context) const override void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{ {
QString formattedPrompt = promptTemplate().arg(context.instriuctions, QString formattedPrompt = promptTemplate().arg(context.systemPrompt,
context.prefix, context.prefix,
context.suffix); context.suffix);
request["prompt"] = formattedPrompt; request["prompt"] = formattedPrompt;