mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-02-12 10:10:44 -05:00
Compare commits
130 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 00fce5db99 | |||
| 251a9bae03 | |||
| 3dba9d7abe | |||
| 45b0f3f18e | |||
| 4432d4019d | |||
| f679d76d43 | |||
| 29f94561ef | |||
| cd6c766ed2 | |||
| 87393b681f | |||
| 5d496fee58 | |||
| 9902623ba0 | |||
| 61f1f0ae4f | |||
| bc93bce03b | |||
| 85d039cbd5 | |||
| 2acaef553d | |||
| b141e54e3e | |||
| 1ec6098210 | |||
| 9c945f066b | |||
| 4a82e9c046 | |||
| 838d69623c | |||
| 693e429bdd | |||
| 496d8feb66 | |||
| 40a568ebd9 | |||
| 5b43eb4fd2 | |||
| 9c2516cd4c | |||
| 2257e6e45f | |||
| 80eda8c167 | |||
| 3db2691114 | |||
| bf518b4a01 | |||
| 46829720d8 | |||
| 9158a3ac0d | |||
| d6e02d9d2a | |||
| 9c8cac4e3a | |||
| 965af4a945 | |||
| 95f29fefc7 | |||
| 1dd50b6c83 | |||
| 146e772514 | |||
| 4b851f1662 | |||
| 6fea300825 | |||
| 14bf0e6c94 | |||
| 0c045e65df | |||
| 15138b4644 | |||
| 5c98de7440 | |||
| b808d0ec10 | |||
| 30fcd7e019 | |||
| 7442256bab | |||
| 8be279a5fd | |||
| d77e13cddb | |||
| 162c068431 | |||
| 4e8ff55355 | |||
| 8df21e96bd | |||
| 57bec94ee4 | |||
| 1760a2d5ff | |||
| 1649a246e1 | |||
| 0ab4b51520 | |||
| d235d0fcdf | |||
| 1cbde3d55b | |||
| 9903ac8f7b | |||
| fbe363689f | |||
| d7f0cc92e6 | |||
| 8311db5b08 | |||
| cd6dd94cd2 | |||
| b559a17407 | |||
| 9745997952 | |||
| aac58d6933 | |||
| 93c69df1b9 | |||
| 4b0b589d0e | |||
| 04c44f5916 | |||
| 6e56646b4c | |||
| c89fe1451b | |||
| 938636ab48 | |||
| e3a2b5a64c | |||
| 0ae12e0fc6 | |||
| 793b855819 | |||
| 8e052ff45c | |||
| 2fb876ff00 | |||
| cd1a9e16e0 | |||
| f07610df5c | |||
| 7cd35082b2 | |||
| c1dd59e65c | |||
| faaf59f163 | |||
| 539a220771 | |||
| 397dd33a96 | |||
| 9361c27d6e | |||
| 15af137728 | |||
| 24ad5fd015 | |||
| 216c28aa5e | |||
| f64ea42071 | |||
| 314ba06db1 | |||
| 384e07ba62 | |||
| 4d06541a36 | |||
| 356f28a97b | |||
| b5ca11ed38 | |||
| d49cd07dd0 | |||
| 8e61651bae | |||
| 1e0063a7bb | |||
| 44add994b9 | |||
| 99136d7b76 | |||
| da7abbd9be | |||
| 16035d6de6 | |||
| 4891cc4b1e | |||
| 7370258485 | |||
| a1df602182 | |||
| a1b813206a | |||
| ada65e9ef7 | |||
| 753365ea52 | |||
| 4d9adf75ff | |||
| a974b0aa82 | |||
| 6703a7026d | |||
| d7fc62f94b | |||
| dec8967df2 | |||
| 4f0f9338dc | |||
| ec26a31ec5 | |||
| 55d359e44e | |||
| 46258a11f6 | |||
| 4bccd8db91 | |||
| e3495e10f0 | |||
| d97a3514cc | |||
| fa79803836 | |||
| b4f969908f | |||
| 9beb48ee97 | |||
| d6320b04f7 | |||
| 7797007160 | |||
| a613ea19f4 | |||
| 1201da6af3 | |||
| 6bd6edf54d | |||
| 61aae8e3ba | |||
| 089bd0594e | |||
| e799d0bd00 | |||
| fc58c38f63 |
15
.github/FUNDING.yml
vendored
Normal file
15
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: palm1r
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
|
polar: # Replace with a single Polar username
|
||||||
|
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||||
|
thanks_dev: # Replace with a single thanks.dev username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
6
.github/workflows/build_cmake.yml
vendored
6
.github/workflows/build_cmake.yml
vendored
@ -4,8 +4,8 @@ on: [push]
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
PLUGIN_NAME: QodeAssist
|
PLUGIN_NAME: QodeAssist
|
||||||
QT_VERSION: 6.7.2
|
QT_VERSION: 6.7.3
|
||||||
QT_CREATOR_VERSION: 14.0.0
|
QT_CREATOR_VERSION: 14.0.2
|
||||||
QT_CREATOR_SNAPSHOT: NO
|
QT_CREATOR_SNAPSHOT: NO
|
||||||
MACOS_DEPLOYMENT_TARGET: "11.0"
|
MACOS_DEPLOYMENT_TARGET: "11.0"
|
||||||
CMAKE_VERSION: "3.29.6"
|
CMAKE_VERSION: "3.29.6"
|
||||||
@ -100,7 +100,7 @@ jobs:
|
|||||||
COMMAND sudo apt update
|
COMMAND sudo apt update
|
||||||
)
|
)
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND sudo apt install libgl1-mesa-dev
|
COMMAND sudo apt install libgl1-mesa-dev libcups2-dev
|
||||||
RESULT_VARIABLE result
|
RESULT_VARIABLE result
|
||||||
)
|
)
|
||||||
if (NOT result EQUAL 0)
|
if (NOT result EQUAL 0)
|
||||||
|
|||||||
@ -10,18 +10,28 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
find_package(QtCreator REQUIRED COMPONENTS Core)
|
find_package(QtCreator REQUIRED COMPONENTS Core)
|
||||||
find_package(Qt6 COMPONENTS Widgets REQUIRED)
|
find_package(Qt6 COMPONENTS Core Gui Quick Widgets Network REQUIRED)
|
||||||
|
|
||||||
|
add_subdirectory(llmcore)
|
||||||
|
add_subdirectory(settings)
|
||||||
|
add_subdirectory(logger)
|
||||||
|
add_subdirectory(chatview)
|
||||||
|
|
||||||
add_qtc_plugin(QodeAssist
|
add_qtc_plugin(QodeAssist
|
||||||
PLUGIN_DEPENDS
|
PLUGIN_DEPENDS
|
||||||
QtCreator::Core
|
QtCreator::Core
|
||||||
QtCreator::LanguageClient
|
QtCreator::LanguageClient
|
||||||
QtCreator::TextEditor
|
QtCreator::TextEditor
|
||||||
|
QtCreator::ProjectExplorer
|
||||||
DEPENDS
|
DEPENDS
|
||||||
|
Qt::Core
|
||||||
|
Qt::Gui
|
||||||
|
Qt::Quick
|
||||||
Qt::Widgets
|
Qt::Widgets
|
||||||
|
Qt::Network
|
||||||
QtCreator::ExtensionSystem
|
QtCreator::ExtensionSystem
|
||||||
QtCreator::Utils
|
QtCreator::Utils
|
||||||
QtCreator::ProjectExplorer
|
QodeAssistChatViewplugin
|
||||||
SOURCES
|
SOURCES
|
||||||
.github/workflows/build_cmake.yml
|
.github/workflows/build_cmake.yml
|
||||||
.github/workflows/README.md
|
.github/workflows/README.md
|
||||||
@ -30,20 +40,27 @@ 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/CodeLLamaTemplate.hpp
|
templates/DeepSeekCoderFim.hpp
|
||||||
templates/StarCoder2Template.hpp
|
templates/CustomFimTemplate.hpp
|
||||||
providers/LLMProvider.hpp
|
templates/DeepSeekCoderChat.hpp
|
||||||
|
templates/CodeLlamaChat.hpp
|
||||||
|
templates/Qwen.hpp
|
||||||
|
templates/StarCoderChat.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
|
||||||
LLMProvidersManager.hpp LLMProvidersManager.cpp
|
providers/OpenAICompatProvider.hpp providers/OpenAICompatProvider.cpp
|
||||||
QodeAssistSettings.hpp QodeAssistSettings.cpp
|
|
||||||
QodeAssist.qrc
|
QodeAssist.qrc
|
||||||
LSPCompletion.hpp
|
LSPCompletion.hpp
|
||||||
LLMSuggestion.hpp LLMSuggestion.cpp
|
LLMSuggestion.hpp LLMSuggestion.cpp
|
||||||
QodeAssistHoverHandler.hpp QodeAssistHoverHandler.cpp
|
|
||||||
QodeAssistClient.hpp QodeAssistClient.cpp
|
QodeAssistClient.hpp QodeAssistClient.cpp
|
||||||
QodeAssistUtils.hpp
|
|
||||||
DocumentContextReader.hpp DocumentContextReader.cpp
|
DocumentContextReader.hpp DocumentContextReader.cpp
|
||||||
|
utils/CounterTooltip.hpp utils/CounterTooltip.cpp
|
||||||
|
core/ChangesManager.h core/ChangesManager.cpp
|
||||||
|
chat/ChatOutputPane.h chat/ChatOutputPane.cpp
|
||||||
|
chat/NavigationPanel.hpp chat/NavigationPanel.cpp
|
||||||
|
ConfigurationManager.hpp ConfigurationManager.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_link_libraries(QodeAssist PRIVATE )
|
||||||
|
|||||||
155
ConfigurationManager.cpp
Normal file
155
ConfigurationManager.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ConfigurationManager.hpp"
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
#include <settings/ButtonAspect.hpp>
|
||||||
|
|
||||||
|
#include "QodeAssisttr.h"
|
||||||
|
|
||||||
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
ConfigurationManager &ConfigurationManager::instance()
|
||||||
|
{
|
||||||
|
static ConfigurationManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigurationManager::init()
|
||||||
|
{
|
||||||
|
setupConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigurationManager::ConfigurationManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_generalSettings(Settings::generalSettings())
|
||||||
|
, m_providersManager(LLMCore::ProvidersManager::instance())
|
||||||
|
, m_templateManger(LLMCore::PromptTemplateManager::instance())
|
||||||
|
{}
|
||||||
|
|
||||||
|
void ConfigurationManager::setupConnections()
|
||||||
|
{
|
||||||
|
using Config = ConfigurationManager;
|
||||||
|
using Button = ButtonAspect;
|
||||||
|
|
||||||
|
connect(&m_generalSettings.ccSelectProvider, &Button::clicked, this, &Config::selectProvider);
|
||||||
|
connect(&m_generalSettings.caSelectProvider, &Button::clicked, this, &Config::selectProvider);
|
||||||
|
connect(&m_generalSettings.ccSelectModel, &Button::clicked, this, &Config::selectModel);
|
||||||
|
connect(&m_generalSettings.caSelectModel, &Button::clicked, this, &Config::selectModel);
|
||||||
|
connect(&m_generalSettings.ccSelectTemplate, &Button::clicked, this, &Config::selectTemplate);
|
||||||
|
connect(&m_generalSettings.caSelectTemplate, &Button::clicked, this, &Config::selectTemplate);
|
||||||
|
connect(&m_generalSettings.ccSetUrl, &Button::clicked, this, &Config::selectUrl);
|
||||||
|
connect(&m_generalSettings.caSetUrl, &Button::clicked, this, &Config::selectUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigurationManager::selectProvider()
|
||||||
|
{
|
||||||
|
const auto providersList = m_providersManager.providersNames();
|
||||||
|
|
||||||
|
auto *settingsButton = qobject_cast<ButtonAspect *>(sender());
|
||||||
|
if (!settingsButton)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto &targetSettings = (settingsButton == &m_generalSettings.ccSelectProvider)
|
||||||
|
? m_generalSettings.ccProvider
|
||||||
|
: m_generalSettings.caProvider;
|
||||||
|
|
||||||
|
QTimer::singleShot(0, this, [this, providersList, &targetSettings] {
|
||||||
|
m_generalSettings.showSelectionDialog(providersList,
|
||||||
|
targetSettings,
|
||||||
|
Tr::tr("Select LLM Provider"),
|
||||||
|
Tr::tr("Providers:"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigurationManager::selectModel()
|
||||||
|
{
|
||||||
|
const QString providerName = m_generalSettings.ccProvider.volatileValue();
|
||||||
|
|
||||||
|
auto *settingsButton = qobject_cast<ButtonAspect *>(sender());
|
||||||
|
if (!settingsButton)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto providerUrl = (settingsButton == &m_generalSettings.ccSelectModel)
|
||||||
|
? m_generalSettings.ccUrl.volatileValue()
|
||||||
|
: m_generalSettings.caUrl.volatileValue();
|
||||||
|
const auto modelList = m_providersManager.getProviderByName(providerName)
|
||||||
|
->getInstalledModels(providerUrl);
|
||||||
|
|
||||||
|
auto &targetSettings = (settingsButton == &m_generalSettings.ccSelectModel)
|
||||||
|
? m_generalSettings.ccModel
|
||||||
|
: m_generalSettings.caModel;
|
||||||
|
|
||||||
|
QTimer::singleShot(0, &m_generalSettings, [this, modelList, &targetSettings]() {
|
||||||
|
m_generalSettings.showSelectionDialog(modelList,
|
||||||
|
targetSettings,
|
||||||
|
Tr::tr("Select LLM Model"),
|
||||||
|
Tr::tr("Models:"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigurationManager::selectTemplate()
|
||||||
|
{
|
||||||
|
auto *settingsButton = qobject_cast<ButtonAspect *>(sender());
|
||||||
|
if (!settingsButton)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto templateList = (settingsButton == &m_generalSettings.ccSelectTemplate)
|
||||||
|
? m_templateManger.fimTemplatesNames()
|
||||||
|
: m_templateManger.chatTemplatesNames();
|
||||||
|
|
||||||
|
auto &targetSettings = (settingsButton == &m_generalSettings.ccSelectTemplate)
|
||||||
|
? m_generalSettings.ccTemplate
|
||||||
|
: m_generalSettings.caTemplate;
|
||||||
|
|
||||||
|
QTimer::singleShot(0, &m_generalSettings, [this, templateList, &targetSettings]() {
|
||||||
|
m_generalSettings.showSelectionDialog(templateList,
|
||||||
|
targetSettings,
|
||||||
|
Tr::tr("Select Template"),
|
||||||
|
Tr::tr("Templates:"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigurationManager::selectUrl()
|
||||||
|
{
|
||||||
|
QStringList urls;
|
||||||
|
for (const auto &name : m_providersManager.providersNames()) {
|
||||||
|
const auto url = m_providersManager.getProviderByName(name)->url();
|
||||||
|
if (!urls.contains(url))
|
||||||
|
urls.append(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *settingsButton = qobject_cast<ButtonAspect *>(sender());
|
||||||
|
if (!settingsButton)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto &targetSettings = (settingsButton == &m_generalSettings.ccSetUrl)
|
||||||
|
? m_generalSettings.ccUrl
|
||||||
|
: m_generalSettings.caUrl;
|
||||||
|
|
||||||
|
QTimer::singleShot(0, &m_generalSettings, [this, urls, &targetSettings]() {
|
||||||
|
m_generalSettings.showSelectionDialog(urls,
|
||||||
|
targetSettings,
|
||||||
|
Tr::tr("Select URL"),
|
||||||
|
Tr::tr("URLs:"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist
|
||||||
58
ConfigurationManager.hpp
Normal file
58
ConfigurationManager.hpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "llmcore/PromptTemplateManager.hpp"
|
||||||
|
#include "llmcore/ProvidersManager.hpp"
|
||||||
|
#include "settings/GeneralSettings.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
class ConfigurationManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ConfigurationManager &instance();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void selectProvider();
|
||||||
|
void selectModel();
|
||||||
|
void selectTemplate();
|
||||||
|
void selectUrl();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit ConfigurationManager(QObject *parent = nullptr);
|
||||||
|
~ConfigurationManager() = default;
|
||||||
|
ConfigurationManager(const ConfigurationManager &) = delete;
|
||||||
|
ConfigurationManager &operator=(const ConfigurationManager &) = delete;
|
||||||
|
|
||||||
|
Settings::GeneralSettings &m_generalSettings;
|
||||||
|
LLMCore::ProvidersManager &m_providersManager;
|
||||||
|
LLMCore::PromptTemplateManager &m_templateManger;
|
||||||
|
|
||||||
|
void setupConnections();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist
|
||||||
@ -19,10 +19,42 @@
|
|||||||
|
|
||||||
#include "DocumentContextReader.hpp"
|
#include "DocumentContextReader.hpp"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
|
#include <languageserverprotocol/lsptypes.h>
|
||||||
|
|
||||||
|
#include "core/ChangesManager.h"
|
||||||
|
#include "settings/CodeCompletionSettings.hpp"
|
||||||
|
|
||||||
|
const QRegularExpression &getYearRegex()
|
||||||
|
{
|
||||||
|
static const QRegularExpression yearRegex("\\b(19|20)\\d{2}\\b");
|
||||||
|
return yearRegex;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRegularExpression &getNameRegex()
|
||||||
|
{
|
||||||
|
static const QRegularExpression nameRegex("\\b[A-Z][a-z.]+ [A-Z][a-z.]+\\b");
|
||||||
|
return nameRegex;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRegularExpression &getCommentRegex()
|
||||||
|
{
|
||||||
|
static const QRegularExpression
|
||||||
|
commentRegex(R"((/\*[\s\S]*?\*/|//.*$|#.*$|//{2,}[\s\S]*?//{2,}))",
|
||||||
|
QRegularExpression::MultilineOption);
|
||||||
|
return commentRegex;
|
||||||
|
}
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
DocumentContextReader::DocumentContextReader(TextEditor::TextDocument *textDocument)
|
||||||
|
: m_textDocument(textDocument)
|
||||||
|
, m_document(textDocument->document())
|
||||||
|
{
|
||||||
|
m_copyrightInfo = findCopyright();
|
||||||
|
}
|
||||||
|
|
||||||
QString DocumentContextReader::getLineText(int lineNumber, int cursorPosition) const
|
QString DocumentContextReader::getLineText(int lineNumber, int cursorPosition) const
|
||||||
{
|
{
|
||||||
if (!m_document || lineNumber < 0)
|
if (!m_document || lineNumber < 0)
|
||||||
@ -51,76 +83,173 @@ QString DocumentContextReader::getContextBefore(int lineNumber,
|
|||||||
int cursorPosition,
|
int cursorPosition,
|
||||||
int linesCount) const
|
int linesCount) const
|
||||||
{
|
{
|
||||||
QString context;
|
int effectiveStartLine;
|
||||||
for (int i = qMax(0, lineNumber - linesCount); i <= lineNumber; ++i) {
|
if (m_copyrightInfo.found) {
|
||||||
QString line = getLineText(i, i == lineNumber ? cursorPosition : -1);
|
effectiveStartLine = qMax(m_copyrightInfo.endLine + 1, lineNumber - linesCount);
|
||||||
context += line;
|
} else {
|
||||||
if (i < lineNumber)
|
effectiveStartLine = qMax(0, lineNumber - linesCount);
|
||||||
context += "\n";
|
|
||||||
}
|
}
|
||||||
return context;
|
|
||||||
|
return getContextBetween(effectiveStartLine, lineNumber, cursorPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DocumentContextReader::getContextAfter(int lineNumber,
|
QString DocumentContextReader::getContextAfter(int lineNumber,
|
||||||
int cursorPosition,
|
int cursorPosition,
|
||||||
int linesCount) const
|
int linesCount) const
|
||||||
{
|
{
|
||||||
QString context;
|
int endLine = qMin(m_document->blockCount() - 1, lineNumber + linesCount);
|
||||||
int maxLine = lineNumber + linesCount;
|
return getContextBetween(lineNumber + 1, endLine, cursorPosition);
|
||||||
for (int i = lineNumber; i <= maxLine; ++i) {
|
|
||||||
QString line = getLineText(i);
|
|
||||||
if (i == lineNumber && cursorPosition >= 0) {
|
|
||||||
line = line.mid(cursorPosition);
|
|
||||||
}
|
|
||||||
context += line;
|
|
||||||
if (i < maxLine && !line.isEmpty())
|
|
||||||
context += "\n";
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DocumentContextReader::readWholeFileBefore(int lineNumber, int cursorPosition) const
|
QString DocumentContextReader::readWholeFileBefore(int lineNumber, int cursorPosition) const
|
||||||
{
|
{
|
||||||
QString content;
|
int startLine = 0;
|
||||||
QTextBlock block = m_document->begin();
|
if (m_copyrightInfo.found) {
|
||||||
int currentLine = 0;
|
startLine = m_copyrightInfo.endLine + 1;
|
||||||
|
|
||||||
while (block.isValid() && currentLine <= lineNumber) {
|
|
||||||
if (currentLine == lineNumber) {
|
|
||||||
content += block.text().left(cursorPosition);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
content += block.text() + "\n";
|
|
||||||
}
|
|
||||||
block = block.next();
|
|
||||||
currentLine++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return content;
|
startLine = qMin(startLine, lineNumber);
|
||||||
|
|
||||||
|
QString result = getContextBetween(startLine, lineNumber, cursorPosition);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DocumentContextReader::readWholeFileAfter(int lineNumber, int cursorPosition) const
|
QString DocumentContextReader::readWholeFileAfter(int lineNumber, int cursorPosition) const
|
||||||
{
|
{
|
||||||
QString content;
|
return getContextBetween(lineNumber, m_document->blockCount() - 1, cursorPosition);
|
||||||
QTextBlock block = m_document->begin();
|
}
|
||||||
int currentLine = 0;
|
|
||||||
|
|
||||||
while (block.isValid() && currentLine < lineNumber) {
|
QString DocumentContextReader::getLanguageAndFileInfo() const
|
||||||
block = block.next();
|
{
|
||||||
currentLine++;
|
if (!m_textDocument)
|
||||||
}
|
return QString();
|
||||||
|
|
||||||
while (block.isValid()) {
|
QString language = LanguageServerProtocol::TextDocumentItem::mimeTypeToLanguageId(
|
||||||
if (currentLine == lineNumber) {
|
m_textDocument->mimeType());
|
||||||
content += block.text().mid(cursorPosition) + "\n";
|
QString mimeType = m_textDocument->mimeType();
|
||||||
} else {
|
QString filePath = m_textDocument->filePath().toString();
|
||||||
content += block.text() + "\n";
|
QString fileExtension = QFileInfo(filePath).suffix();
|
||||||
|
|
||||||
|
return QString("Language: %1 (MIME: %2) filepath: %3(%4)\n\n")
|
||||||
|
.arg(language, mimeType, filePath, fileExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyrightInfo DocumentContextReader::findCopyright()
|
||||||
|
{
|
||||||
|
CopyrightInfo result = {-1, -1, false};
|
||||||
|
|
||||||
|
QString text = m_document->toPlainText();
|
||||||
|
QRegularExpressionMatchIterator matchIterator = getCommentRegex().globalMatch(text);
|
||||||
|
|
||||||
|
QList<CopyrightInfo> copyrightBlocks;
|
||||||
|
|
||||||
|
while (matchIterator.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = matchIterator.next();
|
||||||
|
QString matchedText = match.captured().toLower();
|
||||||
|
|
||||||
|
if (matchedText.contains("copyright") || matchedText.contains("(C)")
|
||||||
|
|| matchedText.contains("(c)") || matchedText.contains("©")
|
||||||
|
|| getYearRegex().match(text).hasMatch() || getNameRegex().match(text).hasMatch()) {
|
||||||
|
int startPos = match.capturedStart();
|
||||||
|
int endPos = match.capturedEnd();
|
||||||
|
|
||||||
|
CopyrightInfo info;
|
||||||
|
info.startLine = m_document->findBlock(startPos).blockNumber();
|
||||||
|
info.endLine = m_document->findBlock(endPos).blockNumber();
|
||||||
|
info.found = true;
|
||||||
|
|
||||||
|
copyrightBlocks.append(info);
|
||||||
}
|
}
|
||||||
block = block.next();
|
|
||||||
currentLine++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return content.trimmed();
|
for (int i = 0; i < copyrightBlocks.size() - 1; ++i) {
|
||||||
|
if (copyrightBlocks[i].endLine + 1 >= copyrightBlocks[i + 1].startLine) {
|
||||||
|
copyrightBlocks[i].endLine = copyrightBlocks[i + 1].endLine;
|
||||||
|
copyrightBlocks.removeAt(i + 1);
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!copyrightBlocks.isEmpty()) { // temproary solution, need cache
|
||||||
|
return copyrightBlocks.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DocumentContextReader::getContextBetween(int startLine,
|
||||||
|
int endLine,
|
||||||
|
int cursorPosition) const
|
||||||
|
{
|
||||||
|
QString context;
|
||||||
|
for (int i = startLine; i <= endLine; ++i) {
|
||||||
|
QTextBlock block = m_document->findBlockByNumber(i);
|
||||||
|
if (!block.isValid()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == endLine) {
|
||||||
|
context += block.text().left(cursorPosition);
|
||||||
|
} else {
|
||||||
|
context += block.text() + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyrightInfo DocumentContextReader::copyrightInfo() const
|
||||||
|
{
|
||||||
|
return m_copyrightInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLMCore::ContextData DocumentContextReader::prepareContext(int lineNumber, int cursorPosition) const
|
||||||
|
{
|
||||||
|
QString contextBefore = getContextBefore(lineNumber, cursorPosition);
|
||||||
|
QString contextAfter = getContextAfter(lineNumber, cursorPosition);
|
||||||
|
QString instructions = getInstructions();
|
||||||
|
|
||||||
|
return {contextBefore, contextAfter, instructions};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DocumentContextReader::getContextBefore(int lineNumber, int cursorPosition) const
|
||||||
|
{
|
||||||
|
if (Settings::codeCompletionSettings().readFullFile()) {
|
||||||
|
return readWholeFileBefore(lineNumber, cursorPosition);
|
||||||
|
} else {
|
||||||
|
int effectiveStartLine;
|
||||||
|
int beforeCursor = Settings::codeCompletionSettings().readStringsBeforeCursor();
|
||||||
|
if (m_copyrightInfo.found) {
|
||||||
|
effectiveStartLine = qMax(m_copyrightInfo.endLine + 1, lineNumber - beforeCursor);
|
||||||
|
} else {
|
||||||
|
effectiveStartLine = qMax(0, lineNumber - beforeCursor);
|
||||||
|
}
|
||||||
|
return getContextBetween(effectiveStartLine, lineNumber, cursorPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DocumentContextReader::getContextAfter(int lineNumber, int cursorPosition) const
|
||||||
|
{
|
||||||
|
if (Settings::codeCompletionSettings().readFullFile()) {
|
||||||
|
return readWholeFileAfter(lineNumber, cursorPosition);
|
||||||
|
} else {
|
||||||
|
int endLine = qMin(m_document->blockCount() - 1,
|
||||||
|
lineNumber + Settings::codeCompletionSettings().readStringsAfterCursor());
|
||||||
|
return getContextBetween(lineNumber + 1, endLine, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DocumentContextReader::getInstructions() const
|
||||||
|
{
|
||||||
|
QString instructions;
|
||||||
|
|
||||||
|
if (Settings::codeCompletionSettings().useFilePathInContext())
|
||||||
|
instructions += getLanguageAndFileInfo();
|
||||||
|
|
||||||
|
if (Settings::codeCompletionSettings().useProjectChangesCache())
|
||||||
|
instructions += ChangesManager::instance().getRecentChangesContext(m_textDocument);
|
||||||
|
|
||||||
|
return instructions;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -20,24 +20,46 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
|
#include <texteditor/textdocument.h>
|
||||||
|
|
||||||
|
#include <llmcore/ContextData.hpp>
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
struct CopyrightInfo
|
||||||
|
{
|
||||||
|
int startLine;
|
||||||
|
int endLine;
|
||||||
|
bool found;
|
||||||
|
};
|
||||||
|
|
||||||
class DocumentContextReader
|
class DocumentContextReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DocumentContextReader(QTextDocument *doc)
|
DocumentContextReader(TextEditor::TextDocument *textDocument);
|
||||||
: m_document(doc)
|
|
||||||
{}
|
|
||||||
|
|
||||||
QString getLineText(int lineNumber, int cursorPosition = -1) const;
|
QString getLineText(int lineNumber, int cursorPosition = -1) const;
|
||||||
QString getContextBefore(int lineNumber, int cursorPosition, int linesCount) const;
|
QString getContextBefore(int lineNumber, int cursorPosition, int linesCount) const;
|
||||||
QString getContextAfter(int lineNumber, int cursorPosition, int linesCount) const;
|
QString getContextAfter(int lineNumber, int cursorPosition, int linesCount) const;
|
||||||
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;
|
||||||
|
CopyrightInfo findCopyright();
|
||||||
|
QString getContextBetween(int startLine, int endLine, int cursorPosition) const;
|
||||||
|
|
||||||
|
CopyrightInfo copyrightInfo() const;
|
||||||
|
|
||||||
|
LLMCore::ContextData prepareContext(int lineNumber, int cursorPosition) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QString getContextBefore(int lineNumber, int cursorPosition) const;
|
||||||
|
QString getContextAfter(int lineNumber, int cursorPosition) const;
|
||||||
|
QString getInstructions() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TextEditor::TextDocument *m_textDocument;
|
||||||
QTextDocument *m_document;
|
QTextDocument *m_document;
|
||||||
|
CopyrightInfo m_copyrightInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -23,20 +23,25 @@
|
|||||||
#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 "llmcore/PromptTemplateManager.hpp"
|
||||||
#include "PromptTemplateManager.hpp"
|
#include "llmcore/ProvidersManager.hpp"
|
||||||
#include "QodeAssistSettings.hpp"
|
#include "logger/Logger.hpp"
|
||||||
#include "QodeAssistUtils.hpp"
|
#include "settings/CodeCompletionSettings.hpp"
|
||||||
|
#include "settings/GeneralSettings.hpp"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
LLMClientInterface::LLMClientInterface()
|
LLMClientInterface::LLMClientInterface()
|
||||||
: m_manager(new QNetworkAccessManager(this))
|
: m_requestHandler(this)
|
||||||
{
|
{
|
||||||
updateProvider();
|
connect(&m_requestHandler,
|
||||||
|
&LLMCore::RequestHandler::completionReceived,
|
||||||
|
this,
|
||||||
|
&LLMClientInterface::sendCompletionToClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::FilePath LLMClientInterface::serverDeviceTemplate() const
|
Utils::FilePath LLMClientInterface::serverDeviceTemplate() const
|
||||||
@ -51,8 +56,6 @@ void LLMClientInterface::startImpl()
|
|||||||
|
|
||||||
void LLMClientInterface::sendData(const QByteArray &data)
|
void LLMClientInterface::sendData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
updateProvider();
|
|
||||||
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||||
if (!doc.isObject())
|
if (!doc.isObject())
|
||||||
return;
|
return;
|
||||||
@ -69,72 +72,28 @@ void LLMClientInterface::sendData(const QByteArray &data)
|
|||||||
} else if (method == "textDocument/didOpen") {
|
} else if (method == "textDocument/didOpen") {
|
||||||
handleTextDocumentDidOpen(request);
|
handleTextDocumentDidOpen(request);
|
||||||
} else if (method == "getCompletionsCycling") {
|
} else if (method == "getCompletionsCycling") {
|
||||||
|
QString requestId = request["id"].toString();
|
||||||
|
startTimeMeasurement(requestId);
|
||||||
handleCompletion(request);
|
handleCompletion(request);
|
||||||
} else if (method == "$/cancelRequest") {
|
} else if (method == "$/cancelRequest") {
|
||||||
handleCancelRequest(request);
|
handleCancelRequest(request);
|
||||||
} 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::handleCancelRequest(const QJsonObject &request)
|
void LLMClientInterface::handleCancelRequest(const QJsonObject &request)
|
||||||
{
|
{
|
||||||
QString id = request["params"].toObject()["id"].toString();
|
QString id = request["params"].toObject()["id"].toString();
|
||||||
if (m_activeRequests.contains(id)) {
|
if (m_requestHandler.cancelRequest(id)) {
|
||||||
m_activeRequests[id]->abort();
|
LOG_MESSAGE(QString("Request %1 cancelled successfully").arg(id));
|
||||||
m_activeRequests.remove(id);
|
|
||||||
logMessage(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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString LLMClientInterface::сontextBefore(TextEditor::TextEditorWidget *widget,
|
|
||||||
int lineNumber,
|
|
||||||
int cursorPosition)
|
|
||||||
{
|
|
||||||
if (!widget)
|
|
||||||
return QString();
|
|
||||||
|
|
||||||
QTextDocument *doc = widget->document();
|
|
||||||
DocumentContextReader reader(doc);
|
|
||||||
|
|
||||||
QString contextBefore;
|
|
||||||
if (settings().readFullFile()) {
|
|
||||||
contextBefore = reader.readWholeFileBefore(lineNumber, cursorPosition);
|
|
||||||
} else {
|
|
||||||
contextBefore = reader.getContextBefore(lineNumber,
|
|
||||||
cursorPosition,
|
|
||||||
settings().readStringsBeforeCursor());
|
|
||||||
}
|
|
||||||
|
|
||||||
return contextBefore;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LLMClientInterface::сontextAfter(TextEditor::TextEditorWidget *widget,
|
|
||||||
int lineNumber,
|
|
||||||
int cursorPosition)
|
|
||||||
{
|
|
||||||
if (!widget)
|
|
||||||
return QString();
|
|
||||||
|
|
||||||
QTextDocument *doc = widget->document();
|
|
||||||
DocumentContextReader reader(doc);
|
|
||||||
|
|
||||||
QString contextAfter;
|
|
||||||
if (settings().readFullFile()) {
|
|
||||||
contextAfter = reader.readWholeFileAfter(lineNumber, cursorPosition);
|
|
||||||
} else {
|
|
||||||
contextAfter = reader.getContextAfter(lineNumber,
|
|
||||||
cursorPosition,
|
|
||||||
settings().readStringsAfterCursor());
|
|
||||||
}
|
|
||||||
|
|
||||||
return contextAfter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLMClientInterface::handleInitialize(const QJsonObject &request)
|
void LLMClientInterface::handleInitialize(const QJsonObject &request)
|
||||||
{
|
{
|
||||||
QJsonObject response;
|
QJsonObject response;
|
||||||
@ -149,7 +108,7 @@ void LLMClientInterface::handleInitialize(const QJsonObject &request)
|
|||||||
result["capabilities"] = capabilities;
|
result["capabilities"] = capabilities;
|
||||||
|
|
||||||
QJsonObject serverInfo;
|
QJsonObject serverInfo;
|
||||||
serverInfo["name"] = "Ollama LSP Server";
|
serverInfo["name"] = "QodeAssist LSP Server";
|
||||||
serverInfo["version"] = "0.1";
|
serverInfo["version"] = "0.1";
|
||||||
result["serverInfo"] = serverInfo;
|
result["serverInfo"] = serverInfo;
|
||||||
|
|
||||||
@ -168,9 +127,7 @@ void LLMClientInterface::handleShutdown(const QJsonObject &request)
|
|||||||
emit messageReceived(LanguageServerProtocol::JsonRpcMessage(response));
|
emit messageReceived(LanguageServerProtocol::JsonRpcMessage(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::handleTextDocumentDidOpen(const QJsonObject &request)
|
void LLMClientInterface::handleTextDocumentDidOpen(const QJsonObject &request) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLMClientInterface::handleInitialized(const QJsonObject &request)
|
void LLMClientInterface::handleInitialized(const QJsonObject &request)
|
||||||
{
|
{
|
||||||
@ -187,39 +144,42 @@ void LLMClientInterface::handleExit(const QJsonObject &request)
|
|||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::handleLLMResponse(QNetworkReply *reply, const QJsonObject &request)
|
void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[reply];
|
auto updatedContext = prepareContext(request);
|
||||||
|
auto &completeSettings = Settings::codeCompletionSettings();
|
||||||
|
|
||||||
auto &templateManager = PromptTemplateManager::instance();
|
auto providerName = Settings::generalSettings().ccProvider();
|
||||||
const Templates::PromptTemplate *currentTemplate = templateManager.getCurrentTemplate();
|
auto provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName);
|
||||||
|
|
||||||
auto &providerManager = LLMProvidersManager::instance();
|
auto templateName = Settings::generalSettings().ccTemplate();
|
||||||
bool isComplete = providerManager.getCurrentProvider()->handleResponse(reply,
|
auto promptTemplate = LLMCore::PromptTemplateManager::instance().getFimTemplateByName(
|
||||||
accumulatedResponse);
|
templateName);
|
||||||
|
|
||||||
QJsonObject position = request["params"].toObject()["doc"].toObject()["position"].toObject();
|
LLMCore::LLMConfig config;
|
||||||
|
config.requestType = LLMCore::RequestType::Fim;
|
||||||
|
config.provider = provider;
|
||||||
|
config.promptTemplate = promptTemplate;
|
||||||
|
config.url = QUrl(
|
||||||
|
QString("%1%2").arg(Settings::generalSettings().ccUrl(), provider->completionEndpoint()));
|
||||||
|
|
||||||
if (isComplete || reply->isFinished()) {
|
config.providerRequest = {{"model", Settings::generalSettings().ccModel()},
|
||||||
if (isComplete) {
|
{"stream", true},
|
||||||
auto cleanedCompletion = removeStopWords(accumulatedResponse);
|
{"stop",
|
||||||
sendCompletionToClient(cleanedCompletion, request, position, true);
|
QJsonArray::fromStringList(config.promptTemplate->stopWords())}};
|
||||||
} else {
|
config.multiLineCompletion = completeSettings.multiLineCompletion();
|
||||||
handleCompletion(request, accumulatedResponse);
|
|
||||||
}
|
if (completeSettings.useSystemPrompt())
|
||||||
m_accumulatedResponses.remove(reply);
|
config.providerRequest["system"] = completeSettings.systemPrompt();
|
||||||
}
|
|
||||||
|
config.promptTemplate->prepareRequest(config.providerRequest, updatedContext);
|
||||||
|
config.provider->prepareRequest(config.providerRequest, LLMCore::RequestType::Fim);
|
||||||
|
|
||||||
|
m_requestHandler.sendLLMRequest(config, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::handleCompletion(const QJsonObject &request,
|
LLMCore::ContextData LLMClientInterface::prepareContext(const QJsonObject &request,
|
||||||
const QString &accumulatedCompletion)
|
const QStringView &accumulatedCompletion)
|
||||||
{
|
|
||||||
auto updatedContext = prepareContext(request, accumulatedCompletion);
|
|
||||||
sendLLMRequest(request, updatedContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
LLMClientInterface::ContextPair LLMClientInterface::prepareContext(
|
|
||||||
const QJsonObject &request, const QString &accumulatedCompletion)
|
|
||||||
{
|
{
|
||||||
QJsonObject params = request["params"].toObject();
|
QJsonObject params = request["params"].toObject();
|
||||||
QJsonObject doc = params["doc"].toObject();
|
QJsonObject doc = params["doc"].toObject();
|
||||||
@ -231,37 +191,23 @@ LLMClientInterface::ContextPair LLMClientInterface::prepareContext(
|
|||||||
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 {"", ""};
|
return LLMCore::ContextData{};
|
||||||
}
|
}
|
||||||
|
|
||||||
int cursorPosition = position["character"].toInt();
|
int cursorPosition = position["character"].toInt();
|
||||||
int lineNumber = position["line"].toInt();
|
int lineNumber = position["line"].toInt();
|
||||||
|
|
||||||
auto textEditor = TextEditor::BaseTextEditor::currentTextEditor();
|
DocumentContextReader reader(textDocument);
|
||||||
TextEditor::TextEditorWidget *widget = textEditor->editorWidget();
|
return reader.prepareContext(lineNumber, cursorPosition);
|
||||||
|
|
||||||
QString contextBefore = сontextBefore(widget, lineNumber, cursorPosition);
|
|
||||||
QString contextAfter = сontextAfter(widget, lineNumber, cursorPosition);
|
|
||||||
|
|
||||||
QString updatedContextBefore = contextBefore + accumulatedCompletion;
|
|
||||||
|
|
||||||
return {updatedContextBefore, contextAfter};
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLMClientInterface::updateProvider()
|
|
||||||
{
|
|
||||||
m_serverUrl = QUrl(QString("%1:%2%3")
|
|
||||||
.arg(settings().url.value())
|
|
||||||
.arg(settings().port.value())
|
|
||||||
.arg(settings().endPoint.value()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::sendCompletionToClient(const QString &completion,
|
void LLMClientInterface::sendCompletionToClient(const QString &completion,
|
||||||
const QJsonObject &request,
|
const QJsonObject &request,
|
||||||
const QJsonObject &position,
|
|
||||||
bool isComplete)
|
bool isComplete)
|
||||||
{
|
{
|
||||||
|
QJsonObject position = request["params"].toObject()["doc"].toObject()["position"].toObject();
|
||||||
|
|
||||||
QJsonObject response;
|
QJsonObject response;
|
||||||
response["jsonrpc"] = "2.0";
|
response["jsonrpc"] = "2.0";
|
||||||
response[LanguageServerProtocol::idKey] = request["id"];
|
response[LanguageServerProtocol::idKey] = request["id"];
|
||||||
@ -281,73 +227,41 @@ 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();
|
||||||
|
endTimeMeasurement(requestId);
|
||||||
emit messageReceived(LanguageServerProtocol::JsonRpcMessage(response));
|
emit messageReceived(LanguageServerProtocol::JsonRpcMessage(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::sendLLMRequest(const QJsonObject &request, const ContextPair &prompt)
|
void LLMClientInterface::startTimeMeasurement(const QString &requestId)
|
||||||
{
|
{
|
||||||
QJsonObject ollamaRequest = {{"model", settings().modelName.value()}, {"stream", true}};
|
m_requestStartTimes[requestId] = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
}
|
||||||
|
|
||||||
auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
void LLMClientInterface::endTimeMeasurement(const QString &requestId)
|
||||||
currentTemplate->prepareRequest(ollamaRequest, prompt.prefix, prompt.suffix);
|
{
|
||||||
|
if (m_requestStartTimes.contains(requestId)) {
|
||||||
auto &providerManager = LLMProvidersManager::instance();
|
qint64 startTime = m_requestStartTimes[requestId];
|
||||||
providerManager.getCurrentProvider()->prepareRequest(ollamaRequest);
|
qint64 endTime = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
qint64 totalTime = endTime - startTime;
|
||||||
logMessage(
|
logPerformance(requestId, "TotalCompletionTime", totalTime);
|
||||||
QString("Sending request to llm: \nurl: %1\nRequest body:\n%2")
|
m_requestStartTimes.remove(requestId);
|
||||||
.arg(m_serverUrl.toString())
|
|
||||||
.arg(QString::fromUtf8(QJsonDocument(ollamaRequest).toJson(QJsonDocument::Indented))));
|
|
||||||
|
|
||||||
QNetworkRequest networkRequest(m_serverUrl);
|
|
||||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
||||||
QNetworkReply *reply = m_manager->post(networkRequest, QJsonDocument(ollamaRequest).toJson());
|
|
||||||
if (!reply) {
|
|
||||||
logMessage("Error: Failed to create network reply");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString requestId = request["id"].toString();
|
|
||||||
m_activeRequests[requestId] = reply;
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::readyRead, this, [this, reply, request]() {
|
|
||||||
handleLLMResponse(reply, request);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId]() {
|
|
||||||
reply->deleteLater();
|
|
||||||
m_activeRequests.remove(requestId);
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
|
||||||
logMessage(QString("Error in Ollama request: %1").arg(reply->errorString()));
|
|
||||||
} else {
|
|
||||||
logMessage("Request finished successfully");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString LLMClientInterface::removeStopWords(const QString &completion)
|
void LLMClientInterface::logPerformance(const QString &requestId,
|
||||||
|
const QString &operation,
|
||||||
|
qint64 elapsedMs)
|
||||||
{
|
{
|
||||||
QString filteredCompletion = completion;
|
LOG_MESSAGE(QString("Performance: %1 %2 took %3 ms").arg(requestId, operation).arg(elapsedMs));
|
||||||
|
|
||||||
auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
|
||||||
QStringList stopWords = currentTemplate->stopWords();
|
|
||||||
|
|
||||||
for (const QString &stopWord : stopWords) {
|
|
||||||
filteredCompletion = filteredCompletion.replace(stopWord, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
return filteredCompletion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::parseCurrentMessage()
|
void LLMClientInterface::parseCurrentMessage() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -22,6 +22,9 @@
|
|||||||
#include <languageclient/languageclientinterface.h>
|
#include <languageclient/languageclientinterface.h>
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
|
#include <llmcore/ContextData.hpp>
|
||||||
|
#include <llmcore/RequestHandler.hpp>
|
||||||
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
|
|
||||||
@ -34,28 +37,13 @@ class LLMClientInterface : public LanguageClient::BaseClientInterface
|
|||||||
public:
|
public:
|
||||||
LLMClientInterface();
|
LLMClientInterface();
|
||||||
|
|
||||||
public:
|
|
||||||
struct ContextPair
|
|
||||||
{
|
|
||||||
QString prefix;
|
|
||||||
QString suffix;
|
|
||||||
};
|
|
||||||
|
|
||||||
Utils::FilePath serverDeviceTemplate() const override;
|
Utils::FilePath serverDeviceTemplate() const override;
|
||||||
|
|
||||||
void sendCompletionToClient(const QString &completion,
|
void sendCompletionToClient(const QString &completion,
|
||||||
const QJsonObject &request,
|
const QJsonObject &request,
|
||||||
const QJsonObject &position,
|
|
||||||
bool isComplete);
|
bool isComplete);
|
||||||
|
|
||||||
void handleCompletion(const QJsonObject &request,
|
void handleCompletion(const QJsonObject &request);
|
||||||
const QString &accumulatedCompletion = QString());
|
|
||||||
void sendLLMRequest(const QJsonObject &request, const ContextPair &prompt);
|
|
||||||
void handleLLMResponse(QNetworkReply *reply, const QJsonObject &request);
|
|
||||||
|
|
||||||
ContextPair prepareContext(const QJsonObject &request,
|
|
||||||
const QString &accumulatedCompletion = QString{});
|
|
||||||
void updateProvider();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void startImpl() override;
|
void startImpl() override;
|
||||||
@ -70,14 +58,16 @@ private:
|
|||||||
void handleExit(const QJsonObject &request);
|
void handleExit(const QJsonObject &request);
|
||||||
void handleCancelRequest(const QJsonObject &request);
|
void handleCancelRequest(const QJsonObject &request);
|
||||||
|
|
||||||
QString сontextBefore(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition);
|
LLMCore::ContextData prepareContext(const QJsonObject &request,
|
||||||
QString сontextAfter(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition);
|
const QStringView &accumulatedCompletion = QString{});
|
||||||
QString removeStopWords(const QString &completion);
|
|
||||||
|
|
||||||
QUrl m_serverUrl;
|
LLMCore::RequestHandler m_requestHandler;
|
||||||
QNetworkAccessManager *m_manager;
|
QElapsedTimer m_completionTimer;
|
||||||
QMap<QString, QNetworkReply *> m_activeRequests;
|
QMap<QString, qint64> m_requestStartTimes;
|
||||||
QMap<QNetworkReply *, QString> m_accumulatedResponses;
|
|
||||||
|
void startTimeMeasurement(const QString &requestId);
|
||||||
|
void endTimeMeasurement(const QString &requestId);
|
||||||
|
void logPerformance(const QString &requestId, const QString &operation, qint64 elapsedMs);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -19,10 +19,17 @@
|
|||||||
|
|
||||||
#include "LLMSuggestion.hpp"
|
#include "LLMSuggestion.hpp"
|
||||||
|
|
||||||
|
#include <QTextCursor>
|
||||||
|
#include <QtWidgets/qtoolbar.h>
|
||||||
|
#include <texteditor/texteditor.h>
|
||||||
|
#include <utils/stringutils.h>
|
||||||
|
#include <utils/tooltip/tooltip.h>
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
LLMSuggestion::LLMSuggestion(const Completion &completion, QTextDocument *origin)
|
LLMSuggestion::LLMSuggestion(const Completion &completion, QTextDocument *origin)
|
||||||
: m_completion(completion)
|
: m_completion(completion)
|
||||||
|
, m_linesCount(0)
|
||||||
{
|
{
|
||||||
int startPos = completion.range().start().toPositionInDocument(origin);
|
int startPos = completion.range().start().toPositionInDocument(origin);
|
||||||
int endPos = completion.range().end().toPositionInDocument(origin);
|
int endPos = completion.range().end().toPositionInDocument(origin);
|
||||||
@ -63,8 +70,35 @@ bool LLMSuggestion::apply()
|
|||||||
|
|
||||||
bool LLMSuggestion::applyWord(TextEditor::TextEditorWidget *widget)
|
bool LLMSuggestion::applyWord(TextEditor::TextEditorWidget *widget)
|
||||||
{
|
{
|
||||||
Q_UNUSED(widget)
|
return applyNextLine(widget);
|
||||||
return apply();
|
}
|
||||||
|
|
||||||
|
bool LLMSuggestion::applyNextLine(TextEditor::TextEditorWidget *widget)
|
||||||
|
{
|
||||||
|
const QString text = m_completion.text();
|
||||||
|
QStringList lines = text.split('\n');
|
||||||
|
|
||||||
|
if (m_linesCount < lines.size())
|
||||||
|
m_linesCount++;
|
||||||
|
|
||||||
|
showTooltip(widget, m_linesCount);
|
||||||
|
|
||||||
|
return m_linesCount == lines.size() && !Utils::ToolTip::isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLMSuggestion::onCounterFinished(int count)
|
||||||
|
{
|
||||||
|
Utils::ToolTip::hide();
|
||||||
|
m_linesCount = 0;
|
||||||
|
QTextCursor cursor = m_completion.range().toSelection(m_start.document());
|
||||||
|
cursor.beginEditBlock();
|
||||||
|
cursor.removeSelectedText();
|
||||||
|
|
||||||
|
QStringList lines = m_completion.text().split('\n');
|
||||||
|
QString textToInsert = lines.mid(0, count).join('\n');
|
||||||
|
|
||||||
|
cursor.insertText(textToInsert);
|
||||||
|
cursor.endEditBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMSuggestion::reset()
|
void LLMSuggestion::reset()
|
||||||
@ -77,4 +111,14 @@ int LLMSuggestion::position()
|
|||||||
return m_start.position();
|
return m_start.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LLMSuggestion::showTooltip(TextEditor::TextEditorWidget *widget, int count)
|
||||||
|
{
|
||||||
|
Utils::ToolTip::hide();
|
||||||
|
QPoint pos = widget->mapToGlobal(widget->cursorRect().topRight());
|
||||||
|
pos += QPoint(-10, -50);
|
||||||
|
m_counterTooltip = new CounterTooltip(count);
|
||||||
|
Utils::ToolTip::show(pos, m_counterTooltip, widget);
|
||||||
|
connect(m_counterTooltip, &CounterTooltip::finished, this, &LLMSuggestion::onCounterFinished);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -19,27 +19,37 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "LSPCompletion.hpp"
|
||||||
#include <texteditor/textdocumentlayout.h>
|
#include <texteditor/textdocumentlayout.h>
|
||||||
|
|
||||||
#include "LSPCompletion.hpp"
|
#include "utils/CounterTooltip.hpp"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
class LLMSuggestion final : public TextEditor::TextSuggestion
|
class LLMSuggestion final : public QObject, public TextEditor::TextSuggestion
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
LLMSuggestion(const Completion &completion, QTextDocument *origin);
|
LLMSuggestion(const Completion &completion, QTextDocument *origin);
|
||||||
|
|
||||||
bool apply() final;
|
bool apply() final;
|
||||||
bool applyWord(TextEditor::TextEditorWidget *widget) final;
|
bool applyWord(TextEditor::TextEditorWidget *widget) final;
|
||||||
|
bool applyNextLine(TextEditor::TextEditorWidget *widget);
|
||||||
void reset() final;
|
void reset() final;
|
||||||
int position() final;
|
int position() final;
|
||||||
|
|
||||||
const Completion &completion() const { return m_completion; }
|
const Completion &completion() const { return m_completion; }
|
||||||
|
|
||||||
|
void showTooltip(TextEditor::TextEditorWidget *widget, int count);
|
||||||
|
void onCounterFinished(int count);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Completion m_completion;
|
Completion m_completion;
|
||||||
QTextCursor m_start;
|
QTextCursor m_start;
|
||||||
|
int m_linesCount;
|
||||||
|
|
||||||
|
CounterTooltip *m_counterTooltip = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -43,6 +43,10 @@ public:
|
|||||||
{
|
{
|
||||||
return typedValue<LanguageServerProtocol::Position>(LanguageServerProtocol::positionKey);
|
return typedValue<LanguageServerProtocol::Position>(LanguageServerProtocol::positionKey);
|
||||||
}
|
}
|
||||||
|
void setRange(const LanguageServerProtocol::Range &range)
|
||||||
|
{
|
||||||
|
insert(LanguageServerProtocol::rangeKey, range);
|
||||||
|
}
|
||||||
LanguageServerProtocol::Range range() const
|
LanguageServerProtocol::Range range() const
|
||||||
{
|
{
|
||||||
return typedValue<LanguageServerProtocol::Range>(LanguageServerProtocol::rangeKey);
|
return typedValue<LanguageServerProtocol::Range>(LanguageServerProtocol::rangeKey);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"Name" : "QodeAssist",
|
"Name" : "QodeAssist",
|
||||||
"Version" : "0.0.2",
|
"Version" : "0.3.7",
|
||||||
"CompatVersion" : "${IDE_VERSION_COMPAT}",
|
"CompatVersion" : "${IDE_VERSION_COMPAT}",
|
||||||
"Vendor" : "Petr Mironychev",
|
"Vendor" : "Petr Mironychev",
|
||||||
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
|
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
|
||||||
@ -11,6 +11,6 @@ Alternatively, this file may be used under the terms of the GNU General Public L
|
|||||||
"Prerequisites:",
|
"Prerequisites:",
|
||||||
"- One of the supported LLM providers installed (e.g., Ollama or LM Studio)",
|
"- One of the supported LLM providers installed (e.g., Ollama or LM Studio)",
|
||||||
"- A compatible large language model downloaded for your chosen provider (e.g., CodeLlama, StarCoder2)"],
|
"- A compatible large language model downloaded for your chosen provider (e.g., CodeLlama, StarCoder2)"],
|
||||||
"Url" : "https://github.com/Palm1r",
|
"Url" : "https://github.com/Palm1r/QodeAssist",
|
||||||
${IDE_PLUGIN_DEPENDENCIES}
|
${IDE_PLUGIN_DEPENDENCIES}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,9 @@
|
|||||||
|
|
||||||
#include "LLMClientInterface.hpp"
|
#include "LLMClientInterface.hpp"
|
||||||
#include "LLMSuggestion.hpp"
|
#include "LLMSuggestion.hpp"
|
||||||
#include "QodeAssistSettings.hpp"
|
#include "core/ChangesManager.h"
|
||||||
|
#include "settings/CodeCompletionSettings.hpp"
|
||||||
|
#include "settings/GeneralSettings.hpp"
|
||||||
|
|
||||||
using namespace LanguageServerProtocol;
|
using namespace LanguageServerProtocol;
|
||||||
using namespace TextEditor;
|
using namespace TextEditor;
|
||||||
@ -43,6 +45,7 @@ namespace QodeAssist {
|
|||||||
|
|
||||||
QodeAssistClient::QodeAssistClient()
|
QodeAssistClient::QodeAssistClient()
|
||||||
: LanguageClient::Client(new LLMClientInterface())
|
: LanguageClient::Client(new LLMClientInterface())
|
||||||
|
, m_recentCharCount(0)
|
||||||
{
|
{
|
||||||
setName("Qode Assist");
|
setName("Qode Assist");
|
||||||
LanguageClient::LanguageFilter filter;
|
LanguageClient::LanguageFilter filter;
|
||||||
@ -51,6 +54,8 @@ QodeAssistClient::QodeAssistClient()
|
|||||||
|
|
||||||
start();
|
start();
|
||||||
setupConnections();
|
setupConnections();
|
||||||
|
|
||||||
|
m_typingTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
QodeAssistClient::~QodeAssistClient()
|
QodeAssistClient::~QodeAssistClient()
|
||||||
@ -70,7 +75,7 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document)
|
|||||||
this,
|
this,
|
||||||
[this, document](int position, int charsRemoved, int charsAdded) {
|
[this, document](int position, int charsRemoved, int charsAdded) {
|
||||||
Q_UNUSED(charsRemoved)
|
Q_UNUSED(charsRemoved)
|
||||||
if (!settings().enableAutoComplete())
|
if (!Settings::codeCompletionSettings().autoCompletion())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto project = ProjectManager::projectForFile(document->filePath());
|
auto project = ProjectManager::projectForFile(document->filePath());
|
||||||
@ -80,13 +85,32 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document)
|
|||||||
auto textEditor = BaseTextEditor::currentTextEditor();
|
auto textEditor = BaseTextEditor::currentTextEditor();
|
||||||
if (!textEditor || textEditor->document() != document)
|
if (!textEditor || textEditor->document() != document)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (Settings::codeCompletionSettings().useProjectChangesCache())
|
||||||
|
ChangesManager::instance().addChange(document,
|
||||||
|
position,
|
||||||
|
charsRemoved,
|
||||||
|
charsAdded);
|
||||||
|
|
||||||
TextEditorWidget *widget = textEditor->editorWidget();
|
TextEditorWidget *widget = textEditor->editorWidget();
|
||||||
if (widget->isReadOnly() || widget->multiTextCursor().hasMultipleCursors())
|
if (widget->isReadOnly() || widget->multiTextCursor().hasMultipleCursors())
|
||||||
return;
|
return;
|
||||||
const int cursorPosition = widget->textCursor().position();
|
const int cursorPosition = widget->textCursor().position();
|
||||||
if (cursorPosition < position || cursorPosition > position + charsAdded)
|
if (cursorPosition < position || cursorPosition > position + charsAdded)
|
||||||
return;
|
return;
|
||||||
scheduleRequest(widget);
|
|
||||||
|
m_recentCharCount += charsAdded;
|
||||||
|
|
||||||
|
if (m_typingTimer.elapsed()
|
||||||
|
> Settings::codeCompletionSettings().autoCompletionTypingInterval()) {
|
||||||
|
m_recentCharCount = charsAdded;
|
||||||
|
m_typingTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_recentCharCount
|
||||||
|
> Settings::codeCompletionSettings().autoCompletionCharThreshold()) {
|
||||||
|
scheduleRequest(widget);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +154,9 @@ void QodeAssistClient::scheduleRequest(TextEditor::TextEditorWidget *editor)
|
|||||||
connect(timer, &QTimer::timeout, this, [this, editor]() {
|
connect(timer, &QTimer::timeout, this, [this, editor]() {
|
||||||
if (editor
|
if (editor
|
||||||
&& editor->textCursor().position()
|
&& editor->textCursor().position()
|
||||||
== m_scheduledRequests[editor]->property("cursorPosition").toInt())
|
== m_scheduledRequests[editor]->property("cursorPosition").toInt()
|
||||||
|
&& m_recentCharCount
|
||||||
|
> Settings::codeCompletionSettings().autoCompletionCharThreshold())
|
||||||
requestCompletions(editor);
|
requestCompletions(editor);
|
||||||
});
|
});
|
||||||
connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() {
|
connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() {
|
||||||
@ -144,9 +170,8 @@ void QodeAssistClient::scheduleRequest(TextEditor::TextEditorWidget *editor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
it.value()->setProperty("cursorPosition", editor->textCursor().position());
|
it.value()->setProperty("cursorPosition", editor->textCursor().position());
|
||||||
it.value()->start(settings().startSuggestionTimer());
|
it.value()->start(Settings::codeCompletionSettings().startSuggestionTimer());
|
||||||
}
|
}
|
||||||
|
|
||||||
void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &response,
|
void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &response,
|
||||||
TextEditor::TextEditorWidget *editor)
|
TextEditor::TextEditorWidget *editor)
|
||||||
{
|
{
|
||||||
@ -190,7 +215,6 @@ void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &r
|
|||||||
return;
|
return;
|
||||||
editor->insertSuggestion(
|
editor->insertSuggestion(
|
||||||
std::make_unique<LLMSuggestion>(completions.first(), editor->document()));
|
std::make_unique<LLMSuggestion>(completions.first(), editor->document()));
|
||||||
editor->addHoverHandler(&m_hoverHandler);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +229,7 @@ void QodeAssistClient::cancelRunningRequest(TextEditor::TextEditorWidget *editor
|
|||||||
|
|
||||||
bool QodeAssistClient::isEnabled(ProjectExplorer::Project *project) const
|
bool QodeAssistClient::isEnabled(ProjectExplorer::Project *project) const
|
||||||
{
|
{
|
||||||
return settings().enableQodeAssist();
|
return Settings::generalSettings().enableQodeAssist();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QodeAssistClient::setupConnections()
|
void QodeAssistClient::setupConnections()
|
||||||
@ -237,11 +261,6 @@ void QodeAssistClient::cleanupConnections()
|
|||||||
disconnect(m_documentOpenedConnection);
|
disconnect(m_documentOpenedConnection);
|
||||||
disconnect(m_documentClosedConnection);
|
disconnect(m_documentClosedConnection);
|
||||||
|
|
||||||
for (IEditor *editor : DocumentModel::editorsForOpenedDocuments()) {
|
|
||||||
if (auto textEditor = qobject_cast<BaseTextEditor *>(editor))
|
|
||||||
textEditor->editorWidget()->removeHoverHandler(&m_hoverHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
qDeleteAll(m_scheduledRequests);
|
qDeleteAll(m_scheduledRequests);
|
||||||
m_scheduledRequests.clear();
|
m_scheduledRequests.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,6 @@
|
|||||||
#include <languageclient/client.h>
|
#include <languageclient/client.h>
|
||||||
|
|
||||||
#include "LSPCompletion.hpp"
|
#include "LSPCompletion.hpp"
|
||||||
#include "QodeAssistHoverHandler.hpp"
|
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
@ -54,9 +53,11 @@ private:
|
|||||||
|
|
||||||
QHash<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests;
|
QHash<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests;
|
||||||
QHash<TextEditor::TextEditorWidget *, QTimer *> m_scheduledRequests;
|
QHash<TextEditor::TextEditorWidget *, QTimer *> m_scheduledRequests;
|
||||||
QodeAssistHoverHandler m_hoverHandler;
|
|
||||||
QMetaObject::Connection m_documentOpenedConnection;
|
QMetaObject::Connection m_documentOpenedConnection;
|
||||||
QMetaObject::Connection m_documentClosedConnection;
|
QMetaObject::Connection m_documentClosedConnection;
|
||||||
|
|
||||||
|
QElapsedTimer m_typingTimer;
|
||||||
|
int m_recentCharCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -24,39 +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 PORT[] = "QodeAssist.port";
|
|
||||||
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 MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
|
|
||||||
const char OLLAMA_LIVETIME[] = "QodeAssist.ollamaLivetime";
|
|
||||||
|
|
||||||
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
|
|
||||||
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
|
||||||
|
|||||||
@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2023 The Qt Company Ltd.
|
|
||||||
* Copyright (C) 2024 Petr Mironychev
|
|
||||||
*
|
|
||||||
* This file is part of Qode Assist.
|
|
||||||
*
|
|
||||||
* The Qt Company portions:
|
|
||||||
* SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
||||||
*
|
|
||||||
* Petr Mironychev portions:
|
|
||||||
* 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 "QodeAssistHoverHandler.hpp"
|
|
||||||
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QScopeGuard>
|
|
||||||
#include <QToolBar>
|
|
||||||
#include <QToolButton>
|
|
||||||
|
|
||||||
#include <texteditor/textdocument.h>
|
|
||||||
#include <texteditor/textdocumentlayout.h>
|
|
||||||
#include <texteditor/texteditor.h>
|
|
||||||
|
|
||||||
#include <utils/tooltip/tooltip.h>
|
|
||||||
#include <utils/utilsicons.h>
|
|
||||||
|
|
||||||
#include "LLMSuggestion.hpp"
|
|
||||||
#include "LSPCompletion.hpp"
|
|
||||||
#include "QodeAssisttr.h"
|
|
||||||
|
|
||||||
using namespace TextEditor;
|
|
||||||
using namespace LanguageServerProtocol;
|
|
||||||
using namespace Utils;
|
|
||||||
|
|
||||||
namespace QodeAssist {
|
|
||||||
|
|
||||||
class QodeAssistCompletionToolTip : public QToolBar
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QodeAssistCompletionToolTip(TextEditorWidget *editor)
|
|
||||||
: m_editor(editor)
|
|
||||||
{
|
|
||||||
auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString()));
|
|
||||||
connect(apply, &QAction::triggered, this, &QodeAssistCompletionToolTip::apply);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void apply()
|
|
||||||
{
|
|
||||||
if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
|
|
||||||
if (!suggestion->apply())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ToolTip::hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
TextEditorWidget *m_editor;
|
|
||||||
};
|
|
||||||
|
|
||||||
void QodeAssistHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
|
||||||
int pos,
|
|
||||||
ReportPriority report)
|
|
||||||
{
|
|
||||||
QScopeGuard cleanup([&] { report(Priority_None); });
|
|
||||||
if (!editorWidget->suggestionVisible())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QTextCursor cursor(editorWidget->document());
|
|
||||||
cursor.setPosition(pos);
|
|
||||||
m_block = cursor.block();
|
|
||||||
auto *suggestion = dynamic_cast<LLMSuggestion *>(TextDocumentLayout::suggestion(m_block));
|
|
||||||
|
|
||||||
if (!suggestion)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const Completion completion = suggestion->completion();
|
|
||||||
if (completion.text().isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
cleanup.dismiss();
|
|
||||||
report(Priority_Suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QodeAssistHoverHandler::operateTooltip(TextEditor::TextEditorWidget *editorWidget,
|
|
||||||
const QPoint &point)
|
|
||||||
{
|
|
||||||
Q_UNUSED(point)
|
|
||||||
auto *suggestion = dynamic_cast<LLMSuggestion *>(TextDocumentLayout::suggestion(m_block));
|
|
||||||
|
|
||||||
if (!suggestion)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto tooltipWidget = new QodeAssistCompletionToolTip(editorWidget);
|
|
||||||
|
|
||||||
const QRect cursorRect = editorWidget->cursorRect(editorWidget->textCursor());
|
|
||||||
QPoint pos = editorWidget->viewport()->mapToGlobal(cursorRect.topLeft())
|
|
||||||
- Utils::ToolTip::offsetFromPosition();
|
|
||||||
pos.ry() -= tooltipWidget->sizeHint().height();
|
|
||||||
ToolTip::show(pos, tooltipWidget, editorWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist
|
|
||||||
@ -1,378 +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 "QodeAssistSettings.hpp"
|
|
||||||
|
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QtWidgets/qmessagebox.h>
|
|
||||||
#include <coreplugin/dialogs/ioptionspage.h>
|
|
||||||
#include <coreplugin/icore.h>
|
|
||||||
|
|
||||||
#include "QodeAssistConstants.hpp"
|
|
||||||
#include "QodeAssisttr.h"
|
|
||||||
|
|
||||||
#include "LLMProvidersManager.hpp"
|
|
||||||
#include "PromptTemplateManager.hpp"
|
|
||||||
#include "QodeAssistUtils.hpp"
|
|
||||||
|
|
||||||
namespace QodeAssist {
|
|
||||||
|
|
||||||
QodeAssistSettings &settings()
|
|
||||||
{
|
|
||||||
static QodeAssistSettings settings;
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
QodeAssistSettings::QodeAssistSettings()
|
|
||||||
{
|
|
||||||
setAutoApply(false);
|
|
||||||
|
|
||||||
enableQodeAssist.setSettingsKey(Constants::ENABLE_QODE_ASSIST);
|
|
||||||
enableQodeAssist.setLabelText(Tr::tr("Enable Qode Assist"));
|
|
||||||
enableQodeAssist.setDefaultValue(true);
|
|
||||||
|
|
||||||
enableAutoComplete.setSettingsKey(Constants::ENABLE_AUTO_COMPLETE);
|
|
||||||
enableAutoComplete.setLabelText(Tr::tr("Enable Auto Complete"));
|
|
||||||
enableAutoComplete.setDefaultValue(true);
|
|
||||||
|
|
||||||
enableLogging.setSettingsKey(Constants::ENABLE_LOGGING);
|
|
||||||
enableLogging.setLabelText(Tr::tr("Enable Logging"));
|
|
||||||
enableLogging.setDefaultValue(false);
|
|
||||||
|
|
||||||
llmProviders.setSettingsKey(Constants::LLM_PROVIDERS);
|
|
||||||
llmProviders.setDisplayName(Tr::tr("LLM Providers:"));
|
|
||||||
llmProviders.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
|
||||||
llmProviders.setDefaultValue(1);
|
|
||||||
|
|
||||||
url.setSettingsKey(Constants::URL);
|
|
||||||
url.setLabelText(Tr::tr("URL:"));
|
|
||||||
url.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
|
||||||
|
|
||||||
endPoint.setSettingsKey(Constants::END_POINT);
|
|
||||||
endPoint.setLabelText(Tr::tr("Endpoint:"));
|
|
||||||
endPoint.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
|
||||||
|
|
||||||
port.setSettingsKey(Constants::PORT);
|
|
||||||
port.setLabelText(Tr::tr("Port:"));
|
|
||||||
port.setRange(1, 65535);
|
|
||||||
|
|
||||||
modelName.setSettingsKey(Constants::MODEL_NAME);
|
|
||||||
modelName.setLabelText(Tr::tr("LLM Name:"));
|
|
||||||
modelName.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
|
||||||
|
|
||||||
temperature.setSettingsKey(Constants::TEMPERATURE);
|
|
||||||
temperature.setLabelText(Tr::tr("Temperature:"));
|
|
||||||
temperature.setDefaultValue(0.2);
|
|
||||||
temperature.setRange(0.0, 10.0);
|
|
||||||
|
|
||||||
selectModels.m_buttonText = Tr::tr("Select Models");
|
|
||||||
|
|
||||||
ollamaLivetime.setSettingsKey(Constants::OLLAMA_LIVETIME);
|
|
||||||
ollamaLivetime.setLabelText(
|
|
||||||
Tr::tr("Time to suspend Ollama after completion request (in minutes), "
|
|
||||||
"Only Ollama, -1 to disable"));
|
|
||||||
ollamaLivetime.setDefaultValue("5m");
|
|
||||||
ollamaLivetime.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
|
||||||
|
|
||||||
fimPrompts.setDisplayName(Tr::tr("Fill-In-Middle Prompt"));
|
|
||||||
fimPrompts.setSettingsKey(Constants::FIM_PROMPTS);
|
|
||||||
fimPrompts.setDefaultValue(1);
|
|
||||||
fimPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
|
||||||
|
|
||||||
readFullFile.setSettingsKey(Constants::READ_FULL_FILE);
|
|
||||||
readFullFile.setLabelText(Tr::tr("Read Full File"));
|
|
||||||
readFullFile.setDefaultValue(true);
|
|
||||||
|
|
||||||
maxFileThreshold.setSettingsKey(Constants::MAX_FILE_THRESHOLD);
|
|
||||||
maxFileThreshold.setLabelText(Tr::tr("Max File Threshold:"));
|
|
||||||
maxFileThreshold.setRange(10, 100000);
|
|
||||||
maxFileThreshold.setDefaultValue(600);
|
|
||||||
|
|
||||||
readStringsBeforeCursor.setSettingsKey(Constants::READ_STRINGS_BEFORE_CURSOR);
|
|
||||||
readStringsBeforeCursor.setLabelText(Tr::tr("Read Strings Before Cursor"));
|
|
||||||
readStringsBeforeCursor.setDefaultValue(60);
|
|
||||||
|
|
||||||
readStringsAfterCursor.setSettingsKey(Constants::READ_STRINGS_AFTER_CURSOR);
|
|
||||||
readStringsAfterCursor.setLabelText(Tr::tr("Read Strings After Cursor"));
|
|
||||||
readStringsAfterCursor.setDefaultValue(40);
|
|
||||||
|
|
||||||
maxTokens.setSettingsKey(Constants::MAX_TOKENS);
|
|
||||||
maxTokens.setLabelText(Tr::tr("Max Tokens"));
|
|
||||||
maxTokens.setRange(-1, 10000);
|
|
||||||
maxTokens.setDefaultValue(250);
|
|
||||||
|
|
||||||
useTopP.setSettingsKey(Constants::USE_TOP_P);
|
|
||||||
useTopP.setDefaultValue(false);
|
|
||||||
|
|
||||||
topP.setSettingsKey(Constants::TOP_P);
|
|
||||||
topP.setLabelText(Tr::tr("top_p"));
|
|
||||||
topP.setDefaultValue(0.9);
|
|
||||||
topP.setRange(0.0, 10.0);
|
|
||||||
|
|
||||||
useTopK.setSettingsKey(Constants::USE_TOP_K);
|
|
||||||
useTopK.setDefaultValue(false);
|
|
||||||
|
|
||||||
topK.setSettingsKey(Constants::TOP_K);
|
|
||||||
topK.setLabelText(Tr::tr("top_k"));
|
|
||||||
topK.setDefaultValue(0.1);
|
|
||||||
topK.setRange(0, 10.0);
|
|
||||||
|
|
||||||
usePresencePenalty.setSettingsKey(Constants::USE_PRESENCE_PENALTY);
|
|
||||||
usePresencePenalty.setDefaultValue(false);
|
|
||||||
|
|
||||||
presencePenalty.setSettingsKey(Constants::PRESENCE_PENALTY);
|
|
||||||
presencePenalty.setLabelText(Tr::tr("presence_penalty"));
|
|
||||||
presencePenalty.setDefaultValue(0.0);
|
|
||||||
presencePenalty.setRange(-2.0, 2.0);
|
|
||||||
|
|
||||||
useFrequencyPenalty.setSettingsKey(Constants::USE_FREQUENCY_PENALTY);
|
|
||||||
useFrequencyPenalty.setDefaultValue(false);
|
|
||||||
|
|
||||||
frequencyPenalty.setSettingsKey(Constants::FREQUENCY_PENALTY);
|
|
||||||
frequencyPenalty.setLabelText(Tr::tr("frequency_penalty"));
|
|
||||||
frequencyPenalty.setDefaultValue(0.0);
|
|
||||||
frequencyPenalty.setRange(-2.0, 2.0);
|
|
||||||
|
|
||||||
providerPaths.setSettingsKey(Constants::PROVIDER_PATHS);
|
|
||||||
providerPaths.setLabelText(Tr::tr("Provider Paths:"));
|
|
||||||
|
|
||||||
startSuggestionTimer.setSettingsKey(Constants::START_SUGGESTION_TIMER);
|
|
||||||
startSuggestionTimer.setLabelText(Tr::tr("Start Suggestion Timer:"));
|
|
||||||
startSuggestionTimer.setRange(10, 10000);
|
|
||||||
startSuggestionTimer.setDefaultValue(500);
|
|
||||||
|
|
||||||
resetToDefaults.m_buttonText = Tr::tr("Reset to Defaults");
|
|
||||||
|
|
||||||
const auto &manager = LLMProvidersManager::instance();
|
|
||||||
if (!manager.getProviderNames().isEmpty()) {
|
|
||||||
const auto providerNames = manager.getProviderNames();
|
|
||||||
for (const QString &name : providerNames) {
|
|
||||||
llmProviders.addOption(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &promptManager = PromptTemplateManager::instance();
|
|
||||||
if (!promptManager.getTemplateNames().isEmpty()) {
|
|
||||||
const auto promptNames = promptManager.getTemplateNames();
|
|
||||||
for (const QString &name : promptNames) {
|
|
||||||
fimPrompts.addOption(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readSettings();
|
|
||||||
|
|
||||||
topK.setEnabled(useTopK());
|
|
||||||
topP.setEnabled(useTopP());
|
|
||||||
presencePenalty.setEnabled(usePresencePenalty());
|
|
||||||
frequencyPenalty.setEnabled(useFrequencyPenalty());
|
|
||||||
readStringsAfterCursor.setEnabled(!readFullFile());
|
|
||||||
readStringsBeforeCursor.setEnabled(!readFullFile());
|
|
||||||
PromptTemplateManager::instance().setCurrentTemplate(fimPrompts.stringValue());
|
|
||||||
LLMProvidersManager::instance().setCurrentProvider(llmProviders.stringValue());
|
|
||||||
updateProviderSettings();
|
|
||||||
|
|
||||||
setLoggingEnabled(enableLogging());
|
|
||||||
|
|
||||||
setLayouter([this]() {
|
|
||||||
using namespace Layouting;
|
|
||||||
|
|
||||||
return Column{Group{title(Tr::tr("General Settings")),
|
|
||||||
Form{Column{enableQodeAssist,
|
|
||||||
enableAutoComplete,
|
|
||||||
enableLogging,
|
|
||||||
Row{Stretch{1}, resetToDefaults}}}},
|
|
||||||
Group{title(Tr::tr("LLM Providers")),
|
|
||||||
Form{Column{llmProviders, Row{url, port, endPoint}, providerPaths}}},
|
|
||||||
Group{title(Tr::tr("LLM Model Settings")),
|
|
||||||
Form{Column{Row{selectModels, modelName}}}},
|
|
||||||
Group{title(Tr::tr("FIM Prompt Settings")),
|
|
||||||
Form{Column{fimPrompts,
|
|
||||||
readFullFile,
|
|
||||||
maxFileThreshold,
|
|
||||||
ollamaLivetime,
|
|
||||||
temperature,
|
|
||||||
maxTokens,
|
|
||||||
readStringsBeforeCursor,
|
|
||||||
readStringsAfterCursor,
|
|
||||||
startSuggestionTimer,
|
|
||||||
Row{useTopP, topP, Stretch{1}},
|
|
||||||
Row{useTopK, topK, Stretch{1}},
|
|
||||||
Row{usePresencePenalty, presencePenalty, Stretch{1}},
|
|
||||||
Row{useFrequencyPenalty, frequencyPenalty, Stretch{1}}}}},
|
|
||||||
st};
|
|
||||||
});
|
|
||||||
|
|
||||||
setupConnections();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QodeAssistSettings::setupConnections()
|
|
||||||
{
|
|
||||||
connect(&llmProviders, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
|
|
||||||
int index = llmProviders.volatileValue();
|
|
||||||
logMessage(QString("currentProvider %1").arg(llmProviders.displayForIndex(index)));
|
|
||||||
LLMProvidersManager::instance().setCurrentProvider(llmProviders.displayForIndex(index));
|
|
||||||
updateProviderSettings();
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(&fimPrompts, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
|
|
||||||
int index = fimPrompts.volatileValue();
|
|
||||||
logMessage(QString("currentPrompt %1").arg(fimPrompts.displayForIndex(index)));
|
|
||||||
PromptTemplateManager::instance().setCurrentTemplate(fimPrompts.displayForIndex(index));
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(&selectModels, &ButtonAspect::clicked, this, [this]() { showModelSelectionDialog(); });
|
|
||||||
connect(&useTopP, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
|
||||||
topP.setEnabled(useTopP.volatileValue());
|
|
||||||
});
|
|
||||||
connect(&useTopK, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
|
||||||
topK.setEnabled(useTopK.volatileValue());
|
|
||||||
});
|
|
||||||
connect(&usePresencePenalty, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
|
||||||
presencePenalty.setEnabled(usePresencePenalty.volatileValue());
|
|
||||||
});
|
|
||||||
connect(&useFrequencyPenalty, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
|
||||||
frequencyPenalty.setEnabled(useFrequencyPenalty.volatileValue());
|
|
||||||
});
|
|
||||||
connect(&readFullFile, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
|
||||||
readStringsAfterCursor.setEnabled(!readFullFile.volatileValue());
|
|
||||||
readStringsBeforeCursor.setEnabled(!readFullFile.volatileValue());
|
|
||||||
});
|
|
||||||
connect(&resetToDefaults,
|
|
||||||
&ButtonAspect::clicked,
|
|
||||||
this,
|
|
||||||
&QodeAssistSettings::resetSettingsToDefaults);
|
|
||||||
connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
|
||||||
setLoggingEnabled(enableLogging.volatileValue());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void QodeAssistSettings::updateProviderSettings()
|
|
||||||
{
|
|
||||||
auto *provider = LLMProvidersManager::instance().getCurrentProvider();
|
|
||||||
|
|
||||||
if (provider) {
|
|
||||||
logMessage(QString("currentProvider %1").arg(provider->name()));
|
|
||||||
url.setValue(provider->url());
|
|
||||||
port.setValue(provider->defaultPort());
|
|
||||||
endPoint.setValue(provider->completionEndpoint());
|
|
||||||
ollamaLivetime.setEnabled(provider->name() == "Ollama");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList QodeAssistSettings::getInstalledModels()
|
|
||||||
{
|
|
||||||
auto *provider = LLMProvidersManager::instance().getCurrentProvider();
|
|
||||||
if (provider) {
|
|
||||||
auto env = getEnvironmentWithProviderPaths();
|
|
||||||
return provider->getInstalledModels(env);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void QodeAssistSettings::showModelSelectionDialog()
|
|
||||||
{
|
|
||||||
QStringList models = getInstalledModels();
|
|
||||||
|
|
||||||
bool ok;
|
|
||||||
QString selectedModel = QInputDialog::getItem(Core::ICore::dialogParent(),
|
|
||||||
Tr::tr("Select LLM Model"),
|
|
||||||
Tr::tr("Choose a model:"),
|
|
||||||
models,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
&ok);
|
|
||||||
|
|
||||||
if (ok && !selectedModel.isEmpty()) {
|
|
||||||
modelName.setValue(selectedModel);
|
|
||||||
writeSettings();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils::Environment QodeAssistSettings::getEnvironmentWithProviderPaths() const
|
|
||||||
{
|
|
||||||
Utils::Environment env = Utils::Environment::systemEnvironment();
|
|
||||||
const QStringList additionalPaths = providerPaths.volatileValue();
|
|
||||||
for (const QString &path : additionalPaths) {
|
|
||||||
env.prependOrSetPath(path);
|
|
||||||
}
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QodeAssistSettings::resetSettingsToDefaults()
|
|
||||||
{
|
|
||||||
QMessageBox::StandardButton reply;
|
|
||||||
reply = QMessageBox::question(
|
|
||||||
Core::ICore::dialogParent(),
|
|
||||||
Tr::tr("Reset Settings"),
|
|
||||||
Tr::tr("Are you sure you want to reset all settings to default values?"),
|
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
|
||||||
|
|
||||||
if (reply == QMessageBox::Yes) {
|
|
||||||
resetAspect(enableQodeAssist);
|
|
||||||
resetAspect(enableAutoComplete);
|
|
||||||
resetAspect(llmProviders);
|
|
||||||
resetAspect(url);
|
|
||||||
resetAspect(port);
|
|
||||||
resetAspect(endPoint);
|
|
||||||
resetAspect(modelName);
|
|
||||||
resetAspect(fimPrompts);
|
|
||||||
resetAspect(temperature);
|
|
||||||
resetAspect(maxTokens);
|
|
||||||
resetAspect(readFullFile);
|
|
||||||
resetAspect(maxFileThreshold);
|
|
||||||
resetAspect(readStringsBeforeCursor);
|
|
||||||
resetAspect(readStringsAfterCursor);
|
|
||||||
resetAspect(useTopP);
|
|
||||||
resetAspect(topP);
|
|
||||||
resetAspect(useTopK);
|
|
||||||
resetAspect(topK);
|
|
||||||
resetAspect(usePresencePenalty);
|
|
||||||
resetAspect(presencePenalty);
|
|
||||||
resetAspect(useFrequencyPenalty);
|
|
||||||
resetAspect(frequencyPenalty);
|
|
||||||
resetAspect(startSuggestionTimer);
|
|
||||||
resetAspect(enableLogging);
|
|
||||||
resetAspect(ollamaLivetime);
|
|
||||||
|
|
||||||
updateProviderSettings();
|
|
||||||
apply();
|
|
||||||
|
|
||||||
QMessageBox::information(Core::ICore::dialogParent(),
|
|
||||||
Tr::tr("Settings Reset"),
|
|
||||||
Tr::tr("All settings have been reset to their default values."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class QodeAssistSettingsPage : public Core::IOptionsPage
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QodeAssistSettingsPage()
|
|
||||||
{
|
|
||||||
setId(Constants::QODE_ASSIST_GENERAL_OPTIONS_ID);
|
|
||||||
setDisplayName("Qode Assist");
|
|
||||||
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
|
|
||||||
setDisplayCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY);
|
|
||||||
setCategoryIconPath(":/resources/images/qoderassist-icon.png");
|
|
||||||
setSettingsProvider([] { return &settings(); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const QodeAssistSettingsPage settingsPage;
|
|
||||||
|
|
||||||
} // namespace QodeAssist
|
|
||||||
@ -1,68 +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 <QString>
|
|
||||||
#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("[QLLamaAssist] ") + 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;
|
|
||||||
for (const QString &message : messages) {
|
|
||||||
prefixedMessages << (QLatin1String("[Qode Assist] ") + message);
|
|
||||||
}
|
|
||||||
if (silent) {
|
|
||||||
Core::MessageManager::writeSilently(prefixedMessages);
|
|
||||||
} else {
|
|
||||||
Core::MessageManager::writeFlashing(prefixedMessages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist
|
|
||||||
198
README.md
198
README.md
@ -1,60 +1,186 @@
|
|||||||
# QodeAssist
|
# QodeAssist - AI-powered coding assistant plugin for Qt Creator
|
||||||
|
[](https://discord.gg/DGgMtTteAD)
|
||||||
|
[](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml)
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides intelligent code completion and suggestions for C++ and QML, leveraging large language models through local providers like Ollama. Enhance your coding productivity with context-aware AI assistance directly in your Qt development environment.
|
QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides intelligent code completion and suggestions for C++ and QML, leveraging large language models through local providers like Ollama. Enhance your coding productivity with context-aware AI assistance directly in your Qt development environment.
|
||||||
|
|
||||||
## Supported LLM Providers
|
## Table of Contents
|
||||||
QodeAssist currently supports the following LLM (Large Language Model) providers:
|
1. [Overview](#overview)
|
||||||
- [Ollama](https://ollama.com)
|
2. [Installation](#installation)
|
||||||
- [LM Studio](https://lmstudio.ai)
|
3. [Configure Plugin](#configure-plugin)
|
||||||
|
4. [Supported LLM Providers](#supported-llm-providers)
|
||||||
|
5. [Recommended Models](#recommended-models)
|
||||||
|
- [Ollama](#ollama)
|
||||||
|
- [LM Studio](#lm-studio)
|
||||||
|
6. [QtCreator Version Compatibility](#qtcreator-version-compatibility)
|
||||||
|
7. [Development Progress](#development-progress)
|
||||||
|
8. [Hotkeys](#hotkeys)
|
||||||
|
9. [Troubleshooting](#troubleshooting)
|
||||||
|
10. [Support the Development](#support-the-development-of-qodeassist)
|
||||||
|
11. [How to Build](#how-to-build)
|
||||||
|
|
||||||
## Supported Models
|
## Overview
|
||||||
QodeAssist has been tested with the following language models:
|
|
||||||
|
|
||||||
Ollama:
|
- AI-powered code completion
|
||||||
- [starcoder2](https://ollama.com/library/starcoder2)
|
- Chat functionality:
|
||||||
- [codellama](https://ollama.com/library/codellama)
|
- Side and Bottom panels
|
||||||
|
- Support for multiple LLM providers:
|
||||||
|
- Ollama
|
||||||
|
- LM Studio
|
||||||
|
- OpenAI-compatible local providers
|
||||||
|
- Extensive library of model-specific templates
|
||||||
|
- Custom template support
|
||||||
|
- Easy configuration and model selection
|
||||||
|
|
||||||
LM studio:
|
<details>
|
||||||
- [second-state/StarCoder2-7B-GGUF](https://huggingface.co/second-state/StarCoder2-7B-GGUF)
|
<summary>Code completion: (click to expand)</summary>
|
||||||
- [TheBloke/CodeLlama-7B-GGUF](https://huggingface.co/TheBloke/CodeLlama-7B-GGUF)
|
<img src="https://github.com/user-attachments/assets/255a52f1-5cc0-4ca3-b05c-c4cf9cdbe25a" width="600" alt="QodeAssistPreview">
|
||||||
|
</details>
|
||||||
|
|
||||||
Please note that while these models have been specifically tested and confirmed to work well with QodeAssist, other models compatible with the supported providers may also work. We encourage users to experiment with different models and report their experiences.
|
<details>
|
||||||
If you've successfully used a model that's not listed here, please let us know by opening an issue or submitting a pull request to update this list.
|
<summary>Chat with LLM models in side panels: (click to expand)</summary>
|
||||||
|
<img src="https://github.com/user-attachments/assets/ead5a5d9-b40a-4f17-af05-77fa2bcb3a61" width="600" alt="QodeAssistChat">
|
||||||
|
</details>
|
||||||
|
|
||||||
## Development Progress
|
<details>
|
||||||
|
<summary>Chat with LLM models in bottom panel: (click to expand)</summary>
|
||||||
|
<img width="326" alt="QodeAssistBottomPanel" src="https://github.com/user-attachments/assets/4cc64c23-a294-4df8-9153-39ad6fdab34b">
|
||||||
|
</details>
|
||||||
|
|
||||||
- [x] Basic plugin with code autocomplete functionality
|
## Installation
|
||||||
- [ ] Improve and automate settings
|
|
||||||
- [ ] Add chat functionality
|
|
||||||
- [ ] Support for more providers and models
|
|
||||||
|
|
||||||
## Installation Plugin
|
1. Install Latest QtCreator
|
||||||
|
|
||||||
1. Install QtCreator 14.0
|
|
||||||
2. Install [Ollama](https://ollama.com). Make sure to review the system requirements before installation.
|
2. Install [Ollama](https://ollama.com). Make sure to review the system requirements before installation.
|
||||||
3. Install a language model in Ollama. For example, you can run:
|
3. Install a language models in Ollama via terminal. For example, you can run:
|
||||||
|
|
||||||
|
For suggestions:
|
||||||
```
|
```
|
||||||
ollama run starcoder2:7b
|
ollama run codellama:7b-code
|
||||||
```
|
```
|
||||||
4. Download the QodeAssist plugin.
|
For chat:
|
||||||
|
```
|
||||||
|
ollama run codellama:7b-instruct
|
||||||
|
```
|
||||||
|
4. Download the QodeAssist plugin for your QtCreator.
|
||||||
5. Launch Qt Creator and install the plugin:
|
5. Launch Qt Creator and install the plugin:
|
||||||
- Go to About -> About Plugins
|
- Go to MacOS: Qt Creator -> About Plugins...
|
||||||
|
Windows\Linux: Help -> About Plugins...
|
||||||
- Click on "Install Plugin..."
|
- Click on "Install Plugin..."
|
||||||
- Select the downloaded QodeAssist plugin archive file
|
- Select the downloaded QodeAssist plugin archive file
|
||||||
|
|
||||||
## Configure Plugin
|
## Configure Plugin
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Configure plugins: (click to expand)</summary>
|
||||||
|
<img src="https://github.com/user-attachments/assets/00ad980f-b470-48eb-9aaa-077783d38798" width="600" alt="Configuere QodeAssist">
|
||||||
|
</details>
|
||||||
|
|
||||||
1. Open Qt Creator settings
|
1. Open Qt Creator settings
|
||||||
2. Navigate to the "Qode Assist" tab
|
2. Navigate to the "Qode Assist" tab
|
||||||
3. Choose your LLM provider (e.g., Ollama)
|
3. Select "General" page
|
||||||
- If you haven't added the provider to your system PATH, specify the path to the provider executable in the "Provider Paths" field
|
4. Choose your LLM provider (e.g., Ollama)
|
||||||
4. Select the installed model
|
5. Select the installed model by the "Select Model" button
|
||||||
- If you need to enter the model name manually, it indicates that the plugin cannot locate the provider's executable file. However, this doesn't affect the plugin's functionality – it will still work correctly. This autoselector input option is provided for your convenience, allowing you to easily select and use different models
|
- For LM Studio you will see current loaded model
|
||||||
5. Choose the prompt template that corresponds to your model
|
6. Choose the prompt template that corresponds to your model
|
||||||
6. Apply the settings
|
7. Apply the settings
|
||||||
|
|
||||||
You're all set! QodeAssist is now ready to use in Qt Creator.
|
You're all set! QodeAssist is now ready to use in Qt Creator.
|
||||||
|
|
||||||
|
[](https://ko-fi.com/P5P412V96G)
|
||||||
|
|
||||||
|
## Supported LLM Providers
|
||||||
|
QodeAssist currently supports the following LLM (Large Language Model) providers:
|
||||||
|
- [Ollama](https://ollama.com)
|
||||||
|
- [LM Studio](https://lmstudio.ai)
|
||||||
|
- OpenAI compatible providers
|
||||||
|
|
||||||
|
## Recommended Models:
|
||||||
|
QodeAssist has been thoroughly tested and optimized for use with the following language models:
|
||||||
|
|
||||||
|
- Llama
|
||||||
|
- CodeLlama
|
||||||
|
- StarCoder2
|
||||||
|
- DeepSeek-Coder-V2
|
||||||
|
- Qwen-2.5
|
||||||
|
|
||||||
|
### Ollama:
|
||||||
|
### For autocomplete(FIM)
|
||||||
|
```
|
||||||
|
ollama run codellama:7b-code
|
||||||
|
ollama run starcoder2:7b
|
||||||
|
ollama run qwen2.5-coder:7b-base
|
||||||
|
ollama run deepseek-coder-v2:16b-lite-base-q3_K_M
|
||||||
|
```
|
||||||
|
### For chat
|
||||||
|
```
|
||||||
|
ollama run codellama:7b-instruct
|
||||||
|
ollama run starcoder2:instruct
|
||||||
|
ollama run qwen2.5-coder:7b-instruct
|
||||||
|
ollama run deepseek-coder-v2
|
||||||
|
```
|
||||||
|
|
||||||
|
### LM Studio:
|
||||||
|
similar models, like for ollama
|
||||||
|
|
||||||
|
Please note that while these models have been specifically tested and confirmed to work well with QodeAssist, other models compatible with the supported providers may also work. We encourage users to experiment with different models and report their experiences.
|
||||||
|
|
||||||
|
If you've successfully used a model that's not listed here, please let us know by opening an issue or submitting a pull request to update this list.
|
||||||
|
|
||||||
|
## QtCreator Version Compatibility
|
||||||
|
|
||||||
|
- QtCreator 14.0.2 - 0.2.3 - 0.3.x
|
||||||
|
- QtCreator 14.0.1 - 0.2.2 plugin version and below
|
||||||
|
|
||||||
|
## Development Progress
|
||||||
|
|
||||||
|
- [x] Basic plugin with code autocomplete functionality
|
||||||
|
- [x] Improve and automate settings
|
||||||
|
- [x] Add chat functionality
|
||||||
|
- [x] Sharing diff with model
|
||||||
|
- [ ] Sharing project source with model
|
||||||
|
- [ ] Support for more providers and models
|
||||||
|
|
||||||
|
## Hotkeys
|
||||||
|
|
||||||
|
- To call manual request to suggestion, you can use or change it in settings
|
||||||
|
- on Mac: Option + Command + Q
|
||||||
|
- on Windows: Ctrl + Alt + Q
|
||||||
|
- To insert the full suggestion, you can use the TAB key
|
||||||
|
- To insert line by line, you can use the "Move cursor word right" shortcut:
|
||||||
|
- On Mac: Option + Right Arrow
|
||||||
|
- On Windows: Alt + Right Arrow
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If QodeAssist is having problems connecting to the LLM provider, please check the following:
|
||||||
|
|
||||||
|
1. Verify the IP address and port:
|
||||||
|
|
||||||
|
- For Ollama, the default is usually http://localhost:11434
|
||||||
|
- For LM Studio, the default is usually http://localhost:1234
|
||||||
|
|
||||||
|
2. Check the endpoint:
|
||||||
|
|
||||||
|
Make sure the endpoint in the settings matches the one required by your provider
|
||||||
|
- For Ollama, it should be /api/generate
|
||||||
|
- For LM Studio and OpenAI compatible providers, it's usually /v1/chat/completions
|
||||||
|
|
||||||
|
3. Confirm that the selected model and template are compatible:
|
||||||
|
|
||||||
|
Ensure you've chosen the correct model in the "Select Models" option
|
||||||
|
Verify that the selected prompt template matches the model you're using
|
||||||
|
|
||||||
|
If you're still experiencing issues with QodeAssist, you can try resetting the settings to their default values:
|
||||||
|
1. Open Qt Creator settings
|
||||||
|
2. Navigate to the "Qode Assist" tab
|
||||||
|
3. Pick settings page for reset
|
||||||
|
4. Click on the "Reset Page to Defaults" button
|
||||||
|
- The API key will not reset
|
||||||
|
- Select model after reset
|
||||||
|
|
||||||
## Support the development of QodeAssist
|
## Support the development of QodeAssist
|
||||||
If you find QodeAssist helpful, there are several ways you can support the project:
|
If you find QodeAssist helpful, there are several ways you can support the project:
|
||||||
|
|
||||||
@ -64,8 +190,8 @@ If you find QodeAssist helpful, there are several ways you can support the proje
|
|||||||
|
|
||||||
3. **Spread the Word**: Star our GitHub repository and share QodeAssist with your fellow developers.
|
3. **Spread the Word**: Star our GitHub repository and share QodeAssist with your fellow developers.
|
||||||
|
|
||||||
4. **Financial Support**: If you'd like to support the development financially, you can make a donation using one of the following cryptocurrency addresses:
|
4. **Financial Support**: If you'd like to support the development financially, you can make a donation using one of the following:
|
||||||
|
- [](https://ko-fi.com/P5P412V96G)
|
||||||
- Bitcoin (BTC): `bc1qndq7f0mpnlya48vk7kugvyqj5w89xrg4wzg68t`
|
- Bitcoin (BTC): `bc1qndq7f0mpnlya48vk7kugvyqj5w89xrg4wzg68t`
|
||||||
- Ethereum (ETH): `0xA5e8c37c94b24e25F9f1f292a01AF55F03099D8D`
|
- Ethereum (ETH): `0xA5e8c37c94b24e25F9f1f292a01AF55F03099D8D`
|
||||||
- Litecoin (LTC): `ltc1qlrxnk30s2pcjchzx4qrxvdjt5gzuervy5mv0vy`
|
- Litecoin (LTC): `ltc1qlrxnk30s2pcjchzx4qrxvdjt5gzuervy5mv0vy`
|
||||||
@ -83,4 +209,4 @@ Create a build directory and run
|
|||||||
where `<path_to_qtcreator>` is the relative or absolute path to a Qt Creator build directory, or to a
|
where `<path_to_qtcreator>` is the relative or absolute path to a Qt Creator build directory, or to a
|
||||||
combined binary and development package (Windows / Linux), or to the `Qt Creator.app/Contents/Resources/`
|
combined binary and development package (Windows / Linux), or to the `Qt Creator.app/Contents/Resources/`
|
||||||
directory of a combined binary and development package (macOS), and `<path_to_plugin_source>` is the
|
directory of a combined binary and development package (macOS), and `<path_to_plugin_source>` is the
|
||||||
relative or absolute path to this plugin directory.
|
relative or absolute path to this plugin directory.
|
||||||
|
|||||||
95
chat/ChatOutputPane.cpp
Normal file
95
chat/ChatOutputPane.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ChatOutputPane.h"
|
||||||
|
|
||||||
|
#include "QodeAssisttr.h"
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
ChatOutputPane::ChatOutputPane(QObject *parent)
|
||||||
|
: Core::IOutputPane(parent)
|
||||||
|
, m_chatWidget(new ChatWidget)
|
||||||
|
{
|
||||||
|
setId("QodeAssistChat");
|
||||||
|
setDisplayName(Tr::tr("QodeAssist Chat"));
|
||||||
|
setPriorityInStatusBar(-40);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatOutputPane::~ChatOutputPane()
|
||||||
|
{
|
||||||
|
delete m_chatWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *ChatOutputPane::outputWidget(QWidget *)
|
||||||
|
{
|
||||||
|
return m_chatWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QWidget *> ChatOutputPane::toolBarWidgets() const
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatOutputPane::clearContents()
|
||||||
|
{
|
||||||
|
m_chatWidget->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatOutputPane::visibilityChanged(bool visible)
|
||||||
|
{
|
||||||
|
if (visible)
|
||||||
|
m_chatWidget->scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatOutputPane::setFocus()
|
||||||
|
{
|
||||||
|
m_chatWidget->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatOutputPane::hasFocus() const
|
||||||
|
{
|
||||||
|
return m_chatWidget->hasFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatOutputPane::canFocus() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatOutputPane::canNavigate() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatOutputPane::canNext() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatOutputPane::canPrevious() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatOutputPane::goToNext() {}
|
||||||
|
|
||||||
|
void ChatOutputPane::goToPrev() {}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
||||||
52
chat/ChatOutputPane.h
Normal file
52
chat/ChatOutputPane.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "chatview/ChatWidget.hpp"
|
||||||
|
#include <coreplugin/ioutputpane.h>
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
class ChatOutputPane : public Core::IOutputPane
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ChatOutputPane(QObject *parent = nullptr);
|
||||||
|
~ChatOutputPane() override;
|
||||||
|
|
||||||
|
QWidget *outputWidget(QWidget *parent) override;
|
||||||
|
QList<QWidget *> toolBarWidgets() const override;
|
||||||
|
void clearContents() override;
|
||||||
|
void visibilityChanged(bool visible) override;
|
||||||
|
void setFocus() override;
|
||||||
|
bool hasFocus() const override;
|
||||||
|
bool canFocus() const override;
|
||||||
|
bool canNavigate() const override;
|
||||||
|
bool canNext() const override;
|
||||||
|
bool canPrevious() const override;
|
||||||
|
void goToNext() override;
|
||||||
|
void goToPrev() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ChatWidget *m_chatWidget;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
||||||
45
chat/NavigationPanel.cpp
Normal file
45
chat/NavigationPanel.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 "NavigationPanel.hpp"
|
||||||
|
|
||||||
|
#include "chatview/ChatWidget.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
NavigationPanel::NavigationPanel() {
|
||||||
|
setDisplayName(tr("QodeAssist Chat"));
|
||||||
|
setPriority(500);
|
||||||
|
setId("QodeAssistChat");
|
||||||
|
setActivationSequence(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_C));
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationPanel::~NavigationPanel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::NavigationView NavigationPanel::createWidget()
|
||||||
|
{
|
||||||
|
Core::NavigationView view;
|
||||||
|
view.widget = new ChatWidget;
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
37
chat/NavigationPanel.hpp
Normal file
37
chat/NavigationPanel.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <coreplugin/inavigationwidgetfactory.h>
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
class NavigationPanel : public Core::INavigationWidgetFactory
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit NavigationPanel();
|
||||||
|
~NavigationPanel();
|
||||||
|
|
||||||
|
Core::NavigationView createWidget() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
35
chatview/CMakeLists.txt
Normal file
35
chatview/CMakeLists.txt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
qt_add_library(QodeAssistChatView STATIC)
|
||||||
|
|
||||||
|
qt_add_qml_module(QodeAssistChatView
|
||||||
|
URI ChatView
|
||||||
|
VERSION 1.0
|
||||||
|
QML_FILES
|
||||||
|
qml/RootItem.qml
|
||||||
|
qml/ChatItem.qml
|
||||||
|
qml/Badge.qml
|
||||||
|
qml/dialog/CodeBlock.qml
|
||||||
|
qml/dialog/TextBlock.qml
|
||||||
|
SOURCES
|
||||||
|
ChatWidget.hpp ChatWidget.cpp
|
||||||
|
ChatModel.hpp ChatModel.cpp
|
||||||
|
ChatRootView.hpp ChatRootView.cpp
|
||||||
|
ClientInterface.hpp ClientInterface.cpp
|
||||||
|
MessagePart.hpp
|
||||||
|
ChatUtils.h ChatUtils.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(QodeAssistChatView
|
||||||
|
PUBLIC
|
||||||
|
Qt::Widgets
|
||||||
|
Qt::Quick
|
||||||
|
Qt::QuickWidgets
|
||||||
|
Qt::Network
|
||||||
|
QtCreator::Core
|
||||||
|
QtCreator::Utils
|
||||||
|
LLMCore
|
||||||
|
QodeAssistSettings
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(QodeAssistChatView
|
||||||
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
195
chatview/ChatModel.cpp
Normal file
195
chatview/ChatModel.cpp
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ChatModel.hpp"
|
||||||
|
#include <QtCore/qjsonobject.h>
|
||||||
|
#include <QtQml>
|
||||||
|
#include <utils/aspects.h>
|
||||||
|
|
||||||
|
#include "ChatAssistantSettings.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
ChatModel::ChatModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
, m_totalTokens(0)
|
||||||
|
{
|
||||||
|
auto &settings = Settings::chatAssistantSettings();
|
||||||
|
|
||||||
|
connect(&settings.chatTokensThreshold,
|
||||||
|
&Utils::BaseAspect::changed,
|
||||||
|
this,
|
||||||
|
&ChatModel::tokensThresholdChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChatModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return m_messages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ChatModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() >= m_messages.size())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
const Message &message = m_messages[index.row()];
|
||||||
|
switch (static_cast<Roles>(role)) {
|
||||||
|
case Roles::RoleType:
|
||||||
|
return QVariant::fromValue(message.role);
|
||||||
|
case Roles::Content: {
|
||||||
|
return message.content;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> ChatModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[Roles::RoleType] = "roleType";
|
||||||
|
roles[Roles::Content] = "content";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatModel::addMessage(const QString &content, ChatRole role, const QString &id)
|
||||||
|
{
|
||||||
|
int tokenCount = estimateTokenCount(content);
|
||||||
|
|
||||||
|
if (!m_messages.isEmpty() && !id.isEmpty() && m_messages.last().id == id) {
|
||||||
|
Message &lastMessage = m_messages.last();
|
||||||
|
int oldTokenCount = lastMessage.tokenCount;
|
||||||
|
lastMessage.content = content;
|
||||||
|
lastMessage.tokenCount = tokenCount;
|
||||||
|
m_totalTokens += (tokenCount - oldTokenCount);
|
||||||
|
emit dataChanged(index(m_messages.size() - 1), index(m_messages.size() - 1));
|
||||||
|
} else {
|
||||||
|
beginInsertRows(QModelIndex(), m_messages.size(), m_messages.size());
|
||||||
|
m_messages.append({role, content, tokenCount, id});
|
||||||
|
m_totalTokens += tokenCount;
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
trim();
|
||||||
|
emit totalTokensChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
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::clear()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_messages.clear();
|
||||||
|
m_totalTokens = 0;
|
||||||
|
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::chatAssistantSettings();
|
||||||
|
return settings.chatTokensThreshold();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatModel::lastMessageId() const
|
||||||
|
{
|
||||||
|
return !m_messages.isEmpty() ? m_messages.last().id : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
||||||
85
chatview/ChatModel.hpp
Normal file
85
chatview/ChatModel.hpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ContextData.hpp"
|
||||||
|
#include "MessagePart.hpp"
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
ChatRole role;
|
||||||
|
QString content;
|
||||||
|
int tokenCount;
|
||||||
|
QString id;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit ChatModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
Q_INVOKABLE void addMessage(const QString &content, ChatRole role, const QString &id);
|
||||||
|
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;
|
||||||
|
QString lastMessageId() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void totalTokensChanged();
|
||||||
|
void tokensThresholdChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void trim();
|
||||||
|
int estimateTokenCount(const QString &text) const;
|
||||||
|
|
||||||
|
QVector<Message> m_messages;
|
||||||
|
int m_totalTokens = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
||||||
|
Q_DECLARE_METATYPE(QodeAssist::Chat::ChatModel::Message)
|
||||||
|
Q_DECLARE_METATYPE(QodeAssist::Chat::MessagePart)
|
||||||
144
chatview/ChatRootView.cpp
Normal file
144
chatview/ChatRootView.cpp
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ChatRootView.hpp"
|
||||||
|
#include <QtGui/qclipboard.h>
|
||||||
|
#include <utils/theme/theme.h>
|
||||||
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
|
#include "ChatAssistantSettings.hpp"
|
||||||
|
#include "GeneralSettings.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
ChatRootView::ChatRootView(QQuickItem *parent)
|
||||||
|
: QQuickItem(parent)
|
||||||
|
, m_chatModel(new ChatModel(this))
|
||||||
|
, m_clientInterface(new ClientInterface(m_chatModel, this))
|
||||||
|
{
|
||||||
|
auto &settings = Settings::generalSettings();
|
||||||
|
|
||||||
|
connect(&settings.caModel,
|
||||||
|
&Utils::BaseAspect::changed,
|
||||||
|
this,
|
||||||
|
&ChatRootView::currentTemplateChanged);
|
||||||
|
|
||||||
|
connect(&Settings::chatAssistantSettings().sharingCurrentFile,
|
||||||
|
&Utils::BaseAspect::changed,
|
||||||
|
this,
|
||||||
|
&ChatRootView::isSharingCurrentFileChanged);
|
||||||
|
|
||||||
|
generateColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatModel *ChatRootView::chatModel() const
|
||||||
|
{
|
||||||
|
return m_chatModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor ChatRootView::backgroundColor() const
|
||||||
|
{
|
||||||
|
return Utils::creatorColor(Utils::Theme::BackgroundColorNormal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatRootView::sendMessage(const QString &message, bool sharingCurrentFile) const
|
||||||
|
{
|
||||||
|
m_clientInterface->sendMessage(message, sharingCurrentFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatRootView::copyToClipboard(const QString &text)
|
||||||
|
{
|
||||||
|
QGuiApplication::clipboard()->setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatRootView::cancelRequest()
|
||||||
|
{
|
||||||
|
m_clientInterface->cancelRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
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.caModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor ChatRootView::primaryColor() const
|
||||||
|
{
|
||||||
|
return m_primaryColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor ChatRootView::secondaryColor() const
|
||||||
|
{
|
||||||
|
return m_secondaryColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor ChatRootView::codeColor() const
|
||||||
|
{
|
||||||
|
return m_codeColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatRootView::isSharingCurrentFile() const
|
||||||
|
{
|
||||||
|
return Settings::chatAssistantSettings().sharingCurrentFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
||||||
82
chatview/ChatRootView.hpp
Normal file
82
chatview/ChatRootView.hpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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 <QQuickItem>
|
||||||
|
|
||||||
|
#include "ChatModel.hpp"
|
||||||
|
#include "ClientInterface.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
class ChatRootView : public QQuickItem
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
Q_PROPERTY(bool isSharingCurrentFile READ isSharingCurrentFile NOTIFY
|
||||||
|
isSharingCurrentFileChanged FINAL)
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChatRootView(QQuickItem *parent = nullptr);
|
||||||
|
|
||||||
|
ChatModel *chatModel() const;
|
||||||
|
QString currentTemplate() const;
|
||||||
|
|
||||||
|
QColor backgroundColor() const;
|
||||||
|
QColor primaryColor() const;
|
||||||
|
QColor secondaryColor() const;
|
||||||
|
|
||||||
|
QColor codeColor() const;
|
||||||
|
|
||||||
|
bool isSharingCurrentFile() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void sendMessage(const QString &message, bool sharingCurrentFile = false) const;
|
||||||
|
void copyToClipboard(const QString &text);
|
||||||
|
void cancelRequest();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void chatModelChanged();
|
||||||
|
void currentTemplateChanged();
|
||||||
|
|
||||||
|
void isSharingCurrentFileChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
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
|
||||||
32
chatview/ChatUtils.cpp
Normal file
32
chatview/ChatUtils.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ChatUtils.h"
|
||||||
|
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
void ChatUtils::copyToClipboard(const QString &text)
|
||||||
|
{
|
||||||
|
QGuiApplication::clipboard()->setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
||||||
40
chatview/ChatUtils.h
Normal file
40
chatview/ChatUtils.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <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
|
||||||
43
chatview/ChatWidget.cpp
Normal file
43
chatview/ChatWidget.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.hpp"
|
||||||
|
|
||||||
|
#include <QQmlContext>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
ChatWidget::ChatWidget(QWidget *parent)
|
||||||
|
: QQuickWidget(parent)
|
||||||
|
{
|
||||||
|
setSource(QUrl("qrc:/ChatView/qml/RootItem.qml"));
|
||||||
|
setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::clear()
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(rootObject(), "clearChat");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::scrollToBottom()
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(rootObject(), "scrollToBottom");
|
||||||
|
}
|
||||||
|
}
|
||||||
41
chatview/ChatWidget.hpp
Normal file
41
chatview/ChatWidget.hpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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 <QtQuickWidgets/QtQuickWidgets>
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
class ChatWidget : public QQuickWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ChatWidget(QWidget *parent = nullptr);
|
||||||
|
~ChatWidget() = default;
|
||||||
|
|
||||||
|
Q_INVOKABLE void clear();
|
||||||
|
Q_INVOKABLE void scrollToBottom();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void clearPressed();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
176
chatview/ClientInterface.cpp
Normal file
176
chatview/ClientInterface.cpp
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* 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 <QFileInfo>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QUuid>
|
||||||
|
#include <texteditor/textdocument.h>
|
||||||
|
|
||||||
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
#include <coreplugin/editormanager/ieditor.h>
|
||||||
|
#include <coreplugin/idocument.h>
|
||||||
|
|
||||||
|
#include <texteditor/textdocument.h>
|
||||||
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
|
#include "ChatAssistantSettings.hpp"
|
||||||
|
#include "GeneralSettings.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
#include "PromptTemplateManager.hpp"
|
||||||
|
#include "ProvidersManager.hpp"
|
||||||
|
|
||||||
|
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 &request, bool isComplete) {
|
||||||
|
handleLLMResponse(completion, request, 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, bool includeCurrentFile)
|
||||||
|
{
|
||||||
|
cancelRequest();
|
||||||
|
|
||||||
|
auto &chatAssistantSettings = Settings::chatAssistantSettings();
|
||||||
|
|
||||||
|
auto providerName = Settings::generalSettings().caProvider();
|
||||||
|
auto provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName);
|
||||||
|
|
||||||
|
auto templateName = Settings::generalSettings().caTemplate();
|
||||||
|
auto promptTemplate = LLMCore::PromptTemplateManager::instance().getChatTemplateByName(
|
||||||
|
templateName);
|
||||||
|
|
||||||
|
LLMCore::ContextData context;
|
||||||
|
context.prefix = message;
|
||||||
|
context.suffix = "";
|
||||||
|
|
||||||
|
QString systemPrompt = chatAssistantSettings.systemPrompt();
|
||||||
|
if (includeCurrentFile) {
|
||||||
|
QString fileContext = getCurrentFileContext();
|
||||||
|
if (!fileContext.isEmpty()) {
|
||||||
|
context.systemPrompt = QString("%1\n\n%2").arg(systemPrompt, fileContext);
|
||||||
|
LOG_MESSAGE("Using system prompt with file context");
|
||||||
|
} else {
|
||||||
|
context.systemPrompt = systemPrompt;
|
||||||
|
LOG_MESSAGE("Failed to get file context, using default system prompt");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.systemPrompt = systemPrompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject providerRequest;
|
||||||
|
providerRequest["model"] = Settings::generalSettings().caModel();
|
||||||
|
providerRequest["stream"] = true;
|
||||||
|
providerRequest["messages"] = m_chatModel->prepareMessagesForRequest(context);
|
||||||
|
|
||||||
|
if (promptTemplate)
|
||||||
|
promptTemplate->prepareRequest(providerRequest, context);
|
||||||
|
else
|
||||||
|
qWarning("No prompt template found");
|
||||||
|
|
||||||
|
if (provider)
|
||||||
|
provider->prepareRequest(providerRequest, LLMCore::RequestType::Chat);
|
||||||
|
else
|
||||||
|
qWarning("No provider found");
|
||||||
|
|
||||||
|
LLMCore::LLMConfig config;
|
||||||
|
config.requestType = LLMCore::RequestType::Chat;
|
||||||
|
config.provider = provider;
|
||||||
|
config.promptTemplate = promptTemplate;
|
||||||
|
config.url = QString("%1%2").arg(Settings::generalSettings().caUrl(), provider->chatEndpoint());
|
||||||
|
config.providerRequest = providerRequest;
|
||||||
|
config.multiLineCompletion = false;
|
||||||
|
|
||||||
|
QJsonObject request;
|
||||||
|
request["id"] = QUuid::createUuid().toString();
|
||||||
|
|
||||||
|
m_chatModel->addMessage(message, ChatModel::ChatRole::User, "");
|
||||||
|
m_requestHandler->sendLLMRequest(config, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientInterface::clearMessages()
|
||||||
|
{
|
||||||
|
m_chatModel->clear();
|
||||||
|
LOG_MESSAGE("Chat history cleared");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientInterface::cancelRequest()
|
||||||
|
{
|
||||||
|
auto id = m_chatModel->lastMessageId();
|
||||||
|
m_requestHandler->cancelRequest(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientInterface::handleLLMResponse(const QString &response,
|
||||||
|
const QJsonObject &request,
|
||||||
|
bool isComplete)
|
||||||
|
{
|
||||||
|
QString messageId = request["id"].toString();
|
||||||
|
m_chatModel->addMessage(response.trimmed(), ChatModel::ChatRole::Assistant, messageId);
|
||||||
|
|
||||||
|
if (isComplete) {
|
||||||
|
LOG_MESSAGE("Message completed. Final response for message " + messageId + ": " + response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ClientInterface::getCurrentFileContext() const
|
||||||
|
{
|
||||||
|
auto currentEditor = Core::EditorManager::currentEditor();
|
||||||
|
if (!currentEditor) {
|
||||||
|
LOG_MESSAGE("No active editor found");
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto textDocument = qobject_cast<TextEditor::TextDocument *>(currentEditor->document());
|
||||||
|
if (!textDocument) {
|
||||||
|
LOG_MESSAGE("Current document is not a text document");
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString fileInfo = QString("Language: %1\nFile: %2\n\n")
|
||||||
|
.arg(textDocument->mimeType(), textDocument->filePath().toString());
|
||||||
|
|
||||||
|
QString content = textDocument->document()->toPlainText();
|
||||||
|
|
||||||
|
LOG_MESSAGE(QString("Got context from file: %1").arg(textDocument->filePath().toString()));
|
||||||
|
|
||||||
|
return QString("Current file context:\n%1\nFile content:\n%2").arg(fileInfo, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
||||||
54
chatview/ClientInterface.hpp
Normal file
54
chatview/ClientInterface.hpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "ChatModel.hpp"
|
||||||
|
#include "RequestHandler.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
|
class ClientInterface : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ClientInterface(ChatModel *chatModel, QObject *parent = nullptr);
|
||||||
|
~ClientInterface();
|
||||||
|
|
||||||
|
void sendMessage(const QString &message, bool includeCurrentFile = false);
|
||||||
|
void clearMessages();
|
||||||
|
void cancelRequest();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void errorOccurred(const QString &error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleLLMResponse(const QString &response, const QJsonObject &request, bool isComplete);
|
||||||
|
QString getCurrentFileContext() const;
|
||||||
|
|
||||||
|
LLMCore::RequestHandler *m_requestHandler;
|
||||||
|
ChatModel *m_chatModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Chat
|
||||||
51
chatview/MessagePart.hpp
Normal file
51
chatview/MessagePart.hpp
Normal 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
40
chatview/qml/Badge.qml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
91
chatview/qml/ChatItem.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
183
chatview/qml/RootItem.qml
Normal file
183
chatview/qml/RootItem.qml
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
* 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 QtQuick.Controls.Basic as QQC
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import ChatView
|
||||||
|
|
||||||
|
ChatRootView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: bg
|
||||||
|
anchors.fill: parent
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: view
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 30
|
||||||
|
Layout.maximumHeight: root.height / 2
|
||||||
|
|
||||||
|
QQC.TextArea {
|
||||||
|
id: messageInput
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
Keys.onPressed: function(event) {
|
||||||
|
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !(event.modifiers & Qt.ShiftModifier)) {
|
||||||
|
sendChatMessage()
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: sendButton
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
text: qsTr("Send")
|
||||||
|
onClicked: sendChatMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: stopButton
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
text: qsTr("Stop")
|
||||||
|
onClicked: root.cancelRequest()
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: clearButton
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
text: qsTr("Clear Chat")
|
||||||
|
onClicked: clearChat()
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: sharingCurrentFile
|
||||||
|
|
||||||
|
text: "Share current file with models"
|
||||||
|
checked: root.isSharingCurrentFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: bar
|
||||||
|
|
||||||
|
layoutDirection: Qt.RightToLeft
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 5
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: scroll.width
|
||||||
|
}
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Badge {
|
||||||
|
text: qsTr("tokens:%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)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendChatMessage() {
|
||||||
|
root.sendMessage(messageInput.text, sharingCurrentFile.checked)
|
||||||
|
messageInput.text = ""
|
||||||
|
scrollToBottom()
|
||||||
|
}
|
||||||
|
}
|
||||||
100
chatview/qml/dialog/CodeBlock.qml
Normal file
100
chatview/qml/dialog/CodeBlock.qml
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
readonly property string monospaceFont: {
|
||||||
|
switch (Qt.platform.os) {
|
||||||
|
case "windows":
|
||||||
|
return "Consolas";
|
||||||
|
case "osx":
|
||||||
|
return "Menlo";
|
||||||
|
case "linux":
|
||||||
|
return "DejaVu Sans Mono";
|
||||||
|
default:
|
||||||
|
return "monospace";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: monospaceFont
|
||||||
|
font.pointSize: 12
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
chatview/qml/dialog/TextBlock.qml
Normal file
29
chatview/qml/dialog/TextBlock.qml
Normal 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
|
||||||
|
}
|
||||||
82
core/ChangesManager.cpp
Normal file
82
core/ChangesManager.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ChangesManager.h"
|
||||||
|
#include "settings/CodeCompletionSettings.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
ChangesManager &ChangesManager::instance()
|
||||||
|
{
|
||||||
|
static ChangesManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangesManager::ChangesManager()
|
||||||
|
: QObject(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangesManager::~ChangesManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChangesManager::addChange(TextEditor::TextDocument *document,
|
||||||
|
int position,
|
||||||
|
int charsRemoved,
|
||||||
|
int charsAdded)
|
||||||
|
{
|
||||||
|
auto &documentQueue = m_documentChanges[document];
|
||||||
|
|
||||||
|
QTextBlock block = document->document()->findBlock(position);
|
||||||
|
int lineNumber = block.blockNumber();
|
||||||
|
QString lineContent = block.text();
|
||||||
|
QString fileName = document->filePath().fileName();
|
||||||
|
|
||||||
|
ChangeInfo change{fileName, lineNumber, lineContent};
|
||||||
|
|
||||||
|
auto it = std::find_if(documentQueue.begin(),
|
||||||
|
documentQueue.end(),
|
||||||
|
[lineNumber](const ChangeInfo &c) { return c.lineNumber == lineNumber; });
|
||||||
|
|
||||||
|
if (it != documentQueue.end()) {
|
||||||
|
it->lineContent = lineContent;
|
||||||
|
} else {
|
||||||
|
documentQueue.enqueue(change);
|
||||||
|
|
||||||
|
if (documentQueue.size() > Settings::codeCompletionSettings().maxChangesCacheSize()) {
|
||||||
|
documentQueue.dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChangesManager::getRecentChangesContext(const TextEditor::TextDocument *currentDocument) const
|
||||||
|
{
|
||||||
|
QString context;
|
||||||
|
for (auto it = m_documentChanges.constBegin(); it != m_documentChanges.constEnd(); ++it) {
|
||||||
|
if (it.key() != currentDocument) {
|
||||||
|
for (const auto &change : it.value()) {
|
||||||
|
context += change.lineContent + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist
|
||||||
61
core/ChangesManager.h
Normal file
61
core/ChangesManager.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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 <QDateTime>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QQueue>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <texteditor/textdocument.h>
|
||||||
|
|
||||||
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
class ChangesManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct ChangeInfo
|
||||||
|
{
|
||||||
|
QString fileName;
|
||||||
|
int lineNumber;
|
||||||
|
QString lineContent;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ChangesManager &instance();
|
||||||
|
|
||||||
|
void addChange(TextEditor::TextDocument *document,
|
||||||
|
int position,
|
||||||
|
int charsRemoved,
|
||||||
|
int charsAdded);
|
||||||
|
QString getRecentChangesContext(const TextEditor::TextDocument *currentDocument) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ChangesManager();
|
||||||
|
~ChangesManager();
|
||||||
|
ChangesManager(const ChangesManager &) = delete;
|
||||||
|
ChangesManager &operator=(const ChangesManager &) = delete;
|
||||||
|
|
||||||
|
void cleanupOldChanges();
|
||||||
|
|
||||||
|
QHash<TextEditor::TextDocument *, QQueue<ChangeInfo>> m_documentChanges;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist
|
||||||
23
llmcore/CMakeLists.txt
Normal file
23
llmcore/CMakeLists.txt
Normal 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})
|
||||||
33
llmcore/ContextData.hpp
Normal file
33
llmcore/ContextData.hpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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 <QString>
|
||||||
|
|
||||||
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
|
struct ContextData
|
||||||
|
{
|
||||||
|
QString prefix;
|
||||||
|
QString suffix;
|
||||||
|
QString systemPrompt;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::LLMCore
|
||||||
@ -23,18 +23,20 @@
|
|||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
namespace QodeAssist::Templates {
|
#include "ContextData.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
|
enum class TemplateType { Chat, Fim };
|
||||||
|
|
||||||
class PromptTemplate
|
class PromptTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~PromptTemplate() = default;
|
virtual ~PromptTemplate() = default;
|
||||||
|
virtual TemplateType type() const = 0;
|
||||||
virtual QString name() const = 0;
|
virtual QString name() const = 0;
|
||||||
virtual QString promptTemplate() const = 0;
|
virtual QString promptTemplate() const = 0;
|
||||||
virtual QStringList stopWords() const = 0;
|
virtual QStringList stopWords() const = 0;
|
||||||
virtual void prepareRequest(QJsonObject &request,
|
virtual void prepareRequest(QJsonObject &request, const ContextData &context) const = 0;
|
||||||
const QString &prefix,
|
|
||||||
const QString &suffix) const
|
|
||||||
= 0;
|
|
||||||
};
|
};
|
||||||
} // namespace QodeAssist::Templates
|
} // namespace QodeAssist::LLMCore
|
||||||
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "PromptTemplateManager.hpp"
|
#include "PromptTemplateManager.hpp"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
PromptTemplateManager &PromptTemplateManager::instance()
|
PromptTemplateManager &PromptTemplateManager::instance()
|
||||||
{
|
{
|
||||||
@ -27,27 +27,30 @@ PromptTemplateManager &PromptTemplateManager::instance()
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PromptTemplateManager::setCurrentTemplate(const QString &name)
|
QStringList PromptTemplateManager::fimTemplatesNames() const
|
||||||
{
|
{
|
||||||
if (m_templates.contains(name)) {
|
return m_fimTemplates.keys();
|
||||||
m_currentTemplateName = name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Templates::PromptTemplate *PromptTemplateManager::getCurrentTemplate() const
|
QStringList PromptTemplateManager::chatTemplatesNames() const
|
||||||
{
|
{
|
||||||
auto it = m_templates.find(m_currentTemplateName);
|
return m_chatTemplates.keys();
|
||||||
return it != m_templates.end() ? it.value() : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList PromptTemplateManager::getTemplateNames() const
|
|
||||||
{
|
|
||||||
return m_templates.keys();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PromptTemplateManager::~PromptTemplateManager()
|
PromptTemplateManager::~PromptTemplateManager()
|
||||||
{
|
{
|
||||||
qDeleteAll(m_templates);
|
qDeleteAll(m_fimTemplates);
|
||||||
|
qDeleteAll(m_chatTemplates);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist
|
PromptTemplate *PromptTemplateManager::getFimTemplateByName(const QString &templateName)
|
||||||
|
{
|
||||||
|
return m_fimTemplates[templateName];
|
||||||
|
}
|
||||||
|
|
||||||
|
PromptTemplate *PromptTemplateManager::getChatTemplateByName(const QString &templateName)
|
||||||
|
{
|
||||||
|
return m_chatTemplates[templateName];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::LLMCore
|
||||||
@ -22,38 +22,43 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "templates/PromptTemplate.hpp"
|
#include "PromptTemplate.hpp"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
class PromptTemplateManager
|
class PromptTemplateManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static PromptTemplateManager &instance();
|
static PromptTemplateManager &instance();
|
||||||
|
~PromptTemplateManager();
|
||||||
|
|
||||||
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();
|
||||||
m_templates[name] = template_ptr;
|
if (template_ptr->type() == TemplateType::Fim) {
|
||||||
|
m_fimTemplates[name] = template_ptr;
|
||||||
|
} else if (template_ptr->type() == TemplateType::Chat) {
|
||||||
|
m_chatTemplates[name] = template_ptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCurrentTemplate(const QString &name);
|
PromptTemplate *getFimTemplateByName(const QString &templateName);
|
||||||
const Templates::PromptTemplate *getCurrentTemplate() const;
|
PromptTemplate *getChatTemplateByName(const QString &templateName);
|
||||||
QStringList getTemplateNames() const;
|
|
||||||
|
|
||||||
~PromptTemplateManager();
|
QStringList fimTemplatesNames() const;
|
||||||
|
QStringList chatTemplatesNames() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PromptTemplateManager() = default;
|
PromptTemplateManager() = default;
|
||||||
PromptTemplateManager(const PromptTemplateManager &) = delete;
|
PromptTemplateManager(const PromptTemplateManager &) = delete;
|
||||||
PromptTemplateManager &operator=(const PromptTemplateManager &) = delete;
|
PromptTemplateManager &operator=(const PromptTemplateManager &) = delete;
|
||||||
|
|
||||||
QMap<QString, Templates::PromptTemplate *> m_templates;
|
QMap<QString, PromptTemplate *> m_fimTemplates;
|
||||||
QString m_currentTemplateName;
|
QMap<QString, PromptTemplate *> m_chatTemplates;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist::LLMCore
|
||||||
@ -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 int defaultPort() const = 0;
|
|
||||||
virtual QString completionEndpoint() const = 0;
|
virtual QString completionEndpoint() const = 0;
|
||||||
|
virtual QString chatEndpoint() const = 0;
|
||||||
|
|
||||||
virtual void prepareRequest(QJsonObject &request) = 0;
|
virtual void prepareRequest(QJsonObject &request, 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) = 0;
|
virtual QList<QString> getInstalledModels(const QString &url) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::LLMCore
|
||||||
@ -17,40 +17,29 @@
|
|||||||
* 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"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
LLMProvidersManager &LLMProvidersManager::instance()
|
ProvidersManager &ProvidersManager::instance()
|
||||||
{
|
{
|
||||||
static LLMProvidersManager instance;
|
static ProvidersManager instance;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList LLMProvidersManager::getProviderNames() const
|
QStringList ProvidersManager::providersNames() const
|
||||||
{
|
{
|
||||||
return m_providers.keys();
|
return m_providers.keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMProvidersManager::setCurrentProvider(const QString &name)
|
ProvidersManager::~ProvidersManager()
|
||||||
{
|
|
||||||
if (m_providers.contains(name)) {
|
|
||||||
m_currentProviderName = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Providers::LLMProvider *LLMProvidersManager::getCurrentProvider()
|
|
||||||
{
|
|
||||||
if (m_currentProviderName.isEmpty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_providers[m_currentProviderName];
|
|
||||||
}
|
|
||||||
|
|
||||||
LLMProvidersManager::~LLMProvidersManager()
|
|
||||||
{
|
{
|
||||||
qDeleteAll(m_providers);
|
qDeleteAll(m_providers);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist
|
Provider *ProvidersManager::getProviderByName(const QString &providerName)
|
||||||
|
{
|
||||||
|
return m_providers[providerName];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::LLMCore
|
||||||
@ -21,38 +21,36 @@
|
|||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "providers/LLMProvider.hpp"
|
#include <QMap>
|
||||||
|
#include "Provider.hpp"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
class LLMProvidersManager
|
class ProvidersManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static LLMProvidersManager &instance();
|
static ProvidersManager &instance();
|
||||||
|
~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;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList getProviderNames() const;
|
Provider *getProviderByName(const QString &providerName);
|
||||||
void setCurrentProvider(const QString &name);
|
|
||||||
Providers::LLMProvider *getCurrentProvider();
|
|
||||||
|
|
||||||
~LLMProvidersManager();
|
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;
|
||||||
QString m_currentProviderName;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist::LLMCore
|
||||||
40
llmcore/RequestConfig.hpp
Normal file
40
llmcore/RequestConfig.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QUrl>
|
||||||
|
#include "PromptTemplate.hpp"
|
||||||
|
#include "Provider.hpp"
|
||||||
|
#include "RequestType.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
|
struct LLMConfig
|
||||||
|
{
|
||||||
|
QUrl url;
|
||||||
|
Provider *provider;
|
||||||
|
PromptTemplate *promptTemplate;
|
||||||
|
QJsonObject providerRequest;
|
||||||
|
RequestType requestType;
|
||||||
|
bool multiLineCompletion;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::LLMCore
|
||||||
153
llmcore/RequestHandler.cpp
Normal file
153
llmcore/RequestHandler.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RequestHandler.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
|
RequestHandler::RequestHandler(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_manager(new QNetworkAccessManager(this))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void RequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObject &request)
|
||||||
|
{
|
||||||
|
LOG_MESSAGE(QString("Sending request to llm: \nurl: %1\nRequest body:\n%2")
|
||||||
|
.arg(config.url.toString(),
|
||||||
|
QString::fromUtf8(
|
||||||
|
QJsonDocument(config.providerRequest).toJson(QJsonDocument::Indented))));
|
||||||
|
|
||||||
|
QNetworkRequest networkRequest(config.url);
|
||||||
|
prepareNetworkRequest(networkRequest, config.providerRequest);
|
||||||
|
|
||||||
|
QNetworkReply *reply = m_manager->post(networkRequest,
|
||||||
|
QJsonDocument(config.providerRequest).toJson());
|
||||||
|
if (!reply) {
|
||||||
|
LOG_MESSAGE("Error: Failed to create network reply");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString requestId = request["id"].toString();
|
||||||
|
m_activeRequests[requestId] = reply;
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::readyRead, this, [this, reply, request, config]() {
|
||||||
|
handleLLMResponse(reply, request, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId]() {
|
||||||
|
reply->deleteLater();
|
||||||
|
m_activeRequests.remove(requestId);
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
LOG_MESSAGE(QString("Error in QodeAssist request: %1").arg(reply->errorString()));
|
||||||
|
emit requestFinished(requestId, false, reply->errorString());
|
||||||
|
} else {
|
||||||
|
LOG_MESSAGE("Request finished successfully");
|
||||||
|
emit requestFinished(requestId, true, QString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestHandler::handleLLMResponse(QNetworkReply *reply,
|
||||||
|
const QJsonObject &request,
|
||||||
|
const LLMConfig &config)
|
||||||
|
{
|
||||||
|
QString &accumulatedResponse = m_accumulatedResponses[reply];
|
||||||
|
|
||||||
|
bool isComplete = config.provider->handleResponse(reply, accumulatedResponse);
|
||||||
|
|
||||||
|
if (config.requestType == RequestType::Fim) {
|
||||||
|
if (!config.multiLineCompletion
|
||||||
|
&& processSingleLineCompletion(reply, request, accumulatedResponse, config)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isComplete) {
|
||||||
|
auto cleanedCompletion = removeStopWords(accumulatedResponse,
|
||||||
|
config.promptTemplate->stopWords());
|
||||||
|
emit completionReceived(cleanedCompletion, request, true);
|
||||||
|
}
|
||||||
|
} else if (config.requestType == RequestType::Chat) {
|
||||||
|
emit completionReceived(accumulatedResponse, request, isComplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isComplete)
|
||||||
|
m_accumulatedResponses.remove(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequestHandler::cancelRequest(const QString &id)
|
||||||
|
{
|
||||||
|
if (m_activeRequests.contains(id)) {
|
||||||
|
QNetworkReply *reply = m_activeRequests[id];
|
||||||
|
reply->abort();
|
||||||
|
m_activeRequests.remove(id);
|
||||||
|
m_accumulatedResponses.remove(reply);
|
||||||
|
emit requestCancelled(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestHandler::prepareNetworkRequest(QNetworkRequest &networkRequest,
|
||||||
|
const QJsonObject &providerRequest)
|
||||||
|
{
|
||||||
|
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
if (providerRequest.contains("api_key")) {
|
||||||
|
QString apiKey = providerRequest["api_key"].toString();
|
||||||
|
networkRequest.setRawHeader("Authorization", QString("Bearer %1").arg(apiKey).toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequestHandler::processSingleLineCompletion(QNetworkReply *reply,
|
||||||
|
const QJsonObject &request,
|
||||||
|
const QString &accumulatedResponse,
|
||||||
|
const LLMConfig &config)
|
||||||
|
{
|
||||||
|
int newlinePos = accumulatedResponse.indexOf('\n');
|
||||||
|
|
||||||
|
if (newlinePos != -1) {
|
||||||
|
QString singleLineCompletion = accumulatedResponse.left(newlinePos).trimmed();
|
||||||
|
singleLineCompletion = removeStopWords(singleLineCompletion,
|
||||||
|
config.promptTemplate->stopWords());
|
||||||
|
|
||||||
|
emit completionReceived(singleLineCompletion, request, true);
|
||||||
|
m_accumulatedResponses.remove(reply);
|
||||||
|
reply->abort();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RequestHandler::removeStopWords(const QStringView &completion, const QStringList &stopWords)
|
||||||
|
{
|
||||||
|
QString filteredCompletion = completion.toString();
|
||||||
|
|
||||||
|
for (const QString &stopWord : stopWords) {
|
||||||
|
filteredCompletion = filteredCompletion.replace(stopWord, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredCompletion;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::LLMCore
|
||||||
63
llmcore/RequestHandler.hpp
Normal file
63
llmcore/RequestHandler.hpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "RequestConfig.hpp"
|
||||||
|
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
|
class RequestHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RequestHandler(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void sendLLMRequest(const LLMConfig &config, const QJsonObject &request);
|
||||||
|
void handleLLMResponse(QNetworkReply *reply,
|
||||||
|
const QJsonObject &request,
|
||||||
|
const LLMConfig &config);
|
||||||
|
bool cancelRequest(const QString &id);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void completionReceived(const QString &completion, const QJsonObject &request, bool isComplete);
|
||||||
|
void requestFinished(const QString &requestId, bool success, const QString &errorString);
|
||||||
|
void requestCancelled(const QString &id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager *m_manager;
|
||||||
|
QMap<QString, QNetworkReply *> m_activeRequests;
|
||||||
|
QMap<QNetworkReply *, QString> m_accumulatedResponses;
|
||||||
|
|
||||||
|
void prepareNetworkRequest(QNetworkRequest &networkRequest, const QJsonObject &providerRequest);
|
||||||
|
bool processSingleLineCompletion(QNetworkReply *reply,
|
||||||
|
const QJsonObject &request,
|
||||||
|
const QString &accumulatedResponse,
|
||||||
|
const LLMConfig &config);
|
||||||
|
QString removeStopWords(const QStringView &completion, const QStringList &stopWords);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::LLMCore
|
||||||
25
llmcore/RequestType.hpp
Normal file
25
llmcore/RequestType.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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::LLMCore {
|
||||||
|
|
||||||
|
enum RequestType { Fim, Chat };
|
||||||
|
}
|
||||||
14
logger/CMakeLists.txt
Normal file
14
logger/CMakeLists.txt
Normal 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
55
logger/Logger.cpp
Normal 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
33
logger/Logger.hpp
Normal 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
|
||||||
@ -19,14 +19,15 @@
|
|||||||
|
|
||||||
#include "LMStudioProvider.hpp"
|
#include "LMStudioProvider.hpp"
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QProcess>
|
|
||||||
|
|
||||||
#include "PromptTemplateManager.hpp"
|
#include "logger/Logger.hpp"
|
||||||
#include "QodeAssistSettings.hpp"
|
#include "settings/ChatAssistantSettings.hpp"
|
||||||
|
#include "settings/CodeCompletionSettings.hpp"
|
||||||
|
|
||||||
namespace QodeAssist::Providers {
|
namespace QodeAssist::Providers {
|
||||||
|
|
||||||
@ -39,12 +40,7 @@ QString LMStudioProvider::name() const
|
|||||||
|
|
||||||
QString LMStudioProvider::url() const
|
QString LMStudioProvider::url() const
|
||||||
{
|
{
|
||||||
return "http://localhost";
|
return "http://localhost:1234";
|
||||||
}
|
|
||||||
|
|
||||||
int LMStudioProvider::defaultPort() const
|
|
||||||
{
|
|
||||||
return 1234;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString LMStudioProvider::completionEndpoint() const
|
QString LMStudioProvider::completionEndpoint() const
|
||||||
@ -52,27 +48,50 @@ QString LMStudioProvider::completionEndpoint() const
|
|||||||
return "/v1/chat/completions";
|
return "/v1/chat/completions";
|
||||||
}
|
}
|
||||||
|
|
||||||
void LMStudioProvider::prepareRequest(QJsonObject &request)
|
QString LMStudioProvider::chatEndpoint() const
|
||||||
{
|
{
|
||||||
const auto ¤tTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
return "/v1/chat/completions";
|
||||||
|
}
|
||||||
|
|
||||||
if (request.contains("prompt")) {
|
void LMStudioProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
|
||||||
QJsonArray messages{
|
{
|
||||||
{QJsonObject{{"role", "user"}, {"content", request.take("prompt").toString()}}}};
|
auto prepareMessages = [](QJsonObject &req) -> QJsonArray {
|
||||||
|
QJsonArray messages;
|
||||||
|
if (req.contains("system")) {
|
||||||
|
messages.append(
|
||||||
|
QJsonObject{{"role", "system"}, {"content", req.take("system").toString()}});
|
||||||
|
}
|
||||||
|
if (req.contains("prompt")) {
|
||||||
|
messages.append(
|
||||||
|
QJsonObject{{"role", "user"}, {"content", req.take("prompt").toString()}});
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto applyModelParams = [&request](const auto &settings) {
|
||||||
|
request["max_tokens"] = settings.maxTokens();
|
||||||
|
request["temperature"] = settings.temperature();
|
||||||
|
|
||||||
|
if (settings.useTopP())
|
||||||
|
request["top_p"] = settings.topP();
|
||||||
|
if (settings.useTopK())
|
||||||
|
request["top_k"] = settings.topK();
|
||||||
|
if (settings.useFrequencyPenalty())
|
||||||
|
request["frequency_penalty"] = settings.frequencyPenalty();
|
||||||
|
if (settings.usePresencePenalty())
|
||||||
|
request["presence_penalty"] = settings.presencePenalty();
|
||||||
|
};
|
||||||
|
|
||||||
|
QJsonArray messages = prepareMessages(request);
|
||||||
|
if (!messages.isEmpty()) {
|
||||||
request["messages"] = std::move(messages);
|
request["messages"] = std::move(messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
request["max_tokens"] = settings().maxTokens();
|
if (type == LLMCore::RequestType::Fim) {
|
||||||
request["temperature"] = settings().temperature();
|
applyModelParams(Settings::codeCompletionSettings());
|
||||||
request["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords());
|
} else {
|
||||||
if (settings().useTopP())
|
applyModelParams(Settings::chatAssistantSettings());
|
||||||
request["top_p"] = settings().topP();
|
}
|
||||||
if (settings().useTopK())
|
|
||||||
request["top_k"] = settings().topK();
|
|
||||||
if (settings().useFrequencyPenalty())
|
|
||||||
request["frequency_penalty"] = settings().frequencyPenalty();
|
|
||||||
if (settings().usePresencePenalty())
|
|
||||||
request["presence_penalty"] = settings().presencePenalty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LMStudioProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
|
bool LMStudioProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
|
||||||
@ -116,55 +135,34 @@ bool LMStudioProvider::handleResponse(QNetworkReply *reply, QString &accumulated
|
|||||||
return isComplete;
|
return isComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QString> LMStudioProvider::getInstalledModels(const Utils::Environment &env)
|
QList<QString> LMStudioProvider::getInstalledModels(const QString &url)
|
||||||
{
|
{
|
||||||
QProcess process;
|
QList<QString> models;
|
||||||
process.setEnvironment(env.toStringList());
|
QNetworkAccessManager manager;
|
||||||
QString lmsConsoleName;
|
QNetworkRequest request(QString("%1%2").arg(url, "/v1/models"));
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
lmsConsoleName = "lms.exe";
|
|
||||||
#else
|
|
||||||
lmsConsoleName = "lms";
|
|
||||||
#endif
|
|
||||||
auto lmsPath = env.searchInPath(lmsConsoleName).toString();
|
|
||||||
|
|
||||||
if (!QFileInfo::exists(lmsPath)) {
|
QNetworkReply *reply = manager.get(request);
|
||||||
qWarning() << "LMS executable not found at" << lmsPath;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
process.start(lmsPath, QStringList() << "ls");
|
QEventLoop loop;
|
||||||
if (!process.waitForStarted()) {
|
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
qWarning() << "Failed to start LMS process:" << process.errorString();
|
loop.exec();
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.waitForFinished()) {
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
qWarning() << "LMS process did not finish:" << process.errorString();
|
QByteArray responseData = reply->readAll();
|
||||||
return {};
|
QJsonDocument jsonResponse = QJsonDocument::fromJson(responseData);
|
||||||
}
|
QJsonObject jsonObject = jsonResponse.object();
|
||||||
|
QJsonArray modelArray = jsonObject["data"].toArray();
|
||||||
|
|
||||||
QStringList models;
|
for (const QJsonValue &value : modelArray) {
|
||||||
if (process.exitCode() == 0) {
|
QJsonObject modelObject = value.toObject();
|
||||||
QString output = QString::fromUtf8(process.readAllStandardOutput());
|
QString modelId = modelObject["id"].toString();
|
||||||
QStringList lines = output.split('\n', Qt::SkipEmptyParts);
|
models.append(modelId);
|
||||||
|
|
||||||
// Skip the header lines
|
|
||||||
for (int i = 2; i < lines.size(); ++i) {
|
|
||||||
QString line = lines[i].trimmed();
|
|
||||||
if (!line.isEmpty()) {
|
|
||||||
// The model name is the first column
|
|
||||||
QString modelName = line.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts)
|
|
||||||
.first();
|
|
||||||
models.append(modelName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
qDebug() << "Models:" << models;
|
|
||||||
} else {
|
} else {
|
||||||
// Handle error
|
LOG_MESSAGE(QString("Error fetching models: %1").arg(reply->errorString()));
|
||||||
qWarning() << "Error running 'lms list':" << process.errorString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
return models;
|
return models;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,22 +19,22 @@
|
|||||||
|
|
||||||
#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();
|
||||||
|
|
||||||
QString name() const override;
|
QString name() const override;
|
||||||
QString url() const override;
|
QString url() const override;
|
||||||
int defaultPort() const override;
|
|
||||||
QString completionEndpoint() const override;
|
QString completionEndpoint() const override;
|
||||||
void prepareRequest(QJsonObject &request) override;
|
QString chatEndpoint() const 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) override;
|
QList<QString> getInstalledModels(const QString &url) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -23,10 +23,11 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QProcess>
|
#include <QtCore/qeventloop.h>
|
||||||
|
|
||||||
#include "PromptTemplateManager.hpp"
|
#include "logger/Logger.hpp"
|
||||||
#include "QodeAssistSettings.hpp"
|
#include "settings/ChatAssistantSettings.hpp"
|
||||||
|
#include "settings/CodeCompletionSettings.hpp"
|
||||||
|
|
||||||
namespace QodeAssist::Providers {
|
namespace QodeAssist::Providers {
|
||||||
|
|
||||||
@ -39,12 +40,7 @@ QString OllamaProvider::name() const
|
|||||||
|
|
||||||
QString OllamaProvider::url() const
|
QString OllamaProvider::url() const
|
||||||
{
|
{
|
||||||
return "http://localhost";
|
return "http://localhost:11434";
|
||||||
}
|
|
||||||
|
|
||||||
int OllamaProvider::defaultPort() const
|
|
||||||
{
|
|
||||||
return 11434;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OllamaProvider::completionEndpoint() const
|
QString OllamaProvider::completionEndpoint() const
|
||||||
@ -52,99 +48,116 @@ QString OllamaProvider::completionEndpoint() const
|
|||||||
return "/api/generate";
|
return "/api/generate";
|
||||||
}
|
}
|
||||||
|
|
||||||
void OllamaProvider::prepareRequest(QJsonObject &request)
|
QString OllamaProvider::chatEndpoint() const
|
||||||
{
|
{
|
||||||
auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
return "/api/chat";
|
||||||
|
}
|
||||||
|
|
||||||
QJsonObject options;
|
void OllamaProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
|
||||||
options["num_predict"] = settings().maxTokens();
|
{
|
||||||
options["keep_alive"] = settings().ollamaLivetime();
|
auto applySettings = [&request](const auto &settings) {
|
||||||
options["temperature"] = settings().temperature();
|
QJsonObject options;
|
||||||
options["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords());
|
options["num_predict"] = settings.maxTokens();
|
||||||
if (settings().useTopP())
|
options["temperature"] = settings.temperature();
|
||||||
options["top_p"] = settings().topP();
|
|
||||||
if (settings().useTopK())
|
if (settings.useTopP())
|
||||||
options["top_k"] = settings().topK();
|
options["top_p"] = settings.topP();
|
||||||
if (settings().useFrequencyPenalty())
|
if (settings.useTopK())
|
||||||
options["frequency_penalty"] = settings().frequencyPenalty();
|
options["top_k"] = settings.topK();
|
||||||
if (settings().usePresencePenalty())
|
if (settings.useFrequencyPenalty())
|
||||||
options["presence_penalty"] = settings().presencePenalty();
|
options["frequency_penalty"] = settings.frequencyPenalty();
|
||||||
request["options"] = options;
|
if (settings.usePresencePenalty())
|
||||||
|
options["presence_penalty"] = settings.presencePenalty();
|
||||||
|
|
||||||
|
request["options"] = options;
|
||||||
|
request["keep_alive"] = settings.ollamaLivetime();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type == LLMCore::RequestType::Fim) {
|
||||||
|
applySettings(Settings::codeCompletionSettings());
|
||||||
|
} else {
|
||||||
|
applySettings(Settings::chatAssistantSettings());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
|
bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
|
||||||
{
|
{
|
||||||
|
QString endpoint = reply->url().path();
|
||||||
|
|
||||||
bool isComplete = false;
|
bool isComplete = false;
|
||||||
while (reply->canReadLine()) {
|
while (reply->canReadLine()) {
|
||||||
QByteArray line = reply->readLine().trimmed();
|
QByteArray line = reply->readLine().trimmed();
|
||||||
if (line.isEmpty()) {
|
if (line.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(line);
|
|
||||||
if (jsonResponse.isNull()) {
|
QJsonDocument doc = QJsonDocument::fromJson(line);
|
||||||
qWarning() << "Invalid JSON response from Ollama:" << line;
|
if (doc.isNull()) {
|
||||||
|
LOG_MESSAGE("Invalid JSON response from Ollama: " + QString::fromUtf8(line));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QJsonObject responseObj = jsonResponse.object();
|
|
||||||
if (responseObj.contains("response")) {
|
|
||||||
QString completion = responseObj["response"].toString();
|
|
||||||
|
|
||||||
accumulatedResponse += completion;
|
QJsonObject responseObj = doc.object();
|
||||||
|
|
||||||
|
if (responseObj.contains("error")) {
|
||||||
|
QString errorMessage = responseObj["error"].toString();
|
||||||
|
LOG_MESSAGE("Error in Ollama response: " + errorMessage);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (responseObj["done"].toBool()) {
|
|
||||||
|
if (endpoint == completionEndpoint()) {
|
||||||
|
if (responseObj.contains("response")) {
|
||||||
|
QString completion = responseObj["response"].toString();
|
||||||
|
accumulatedResponse += completion;
|
||||||
|
}
|
||||||
|
} else if (endpoint == chatEndpoint()) {
|
||||||
|
if (responseObj.contains("message")) {
|
||||||
|
QJsonObject message = responseObj["message"].toObject();
|
||||||
|
if (message.contains("content")) {
|
||||||
|
QString content = message["content"].toString();
|
||||||
|
accumulatedResponse += content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_MESSAGE("Unknown endpoint: " + endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseObj.contains("done") && responseObj["done"].toBool()) {
|
||||||
isComplete = true;
|
isComplete = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return isComplete;
|
return isComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QString> OllamaProvider::getInstalledModels(const Utils::Environment &env)
|
QList<QString> OllamaProvider::getInstalledModels(const QString &url)
|
||||||
{
|
{
|
||||||
QProcess process;
|
QList<QString> models;
|
||||||
process.setEnvironment(env.toStringList());
|
QNetworkAccessManager manager;
|
||||||
QString ollamaConsoleName;
|
QNetworkRequest request(QString("%1%2").arg(url, "/api/tags"));
|
||||||
#ifdef Q_OS_WIN
|
QNetworkReply *reply = manager.get(request);
|
||||||
ollamaConsoleName = "ollama.exe";
|
|
||||||
#else
|
|
||||||
ollamaConsoleName = "ollama";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto ollamaPath = env.searchInPath(ollamaConsoleName).toString();
|
QEventLoop loop;
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
if (!QFileInfo::exists(ollamaPath)) {
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
qWarning() << "Ollama executable not found at" << ollamaPath;
|
QByteArray responseData = reply->readAll();
|
||||||
return {};
|
QJsonDocument jsonResponse = QJsonDocument::fromJson(responseData);
|
||||||
}
|
QJsonObject jsonObject = jsonResponse.object();
|
||||||
|
QJsonArray modelArray = jsonObject["models"].toArray();
|
||||||
|
|
||||||
process.start(ollamaPath, QStringList() << "list");
|
for (const QJsonValue &value : modelArray) {
|
||||||
if (!process.waitForStarted()) {
|
QJsonObject modelObject = value.toObject();
|
||||||
qWarning() << "Failed to start Ollama process:" << process.errorString();
|
QString modelName = modelObject["name"].toString();
|
||||||
return {};
|
models.append(modelName);
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.waitForFinished()) {
|
|
||||||
qWarning() << "Ollama process did not finish:" << process.errorString();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList models;
|
|
||||||
if (process.exitCode() == 0) {
|
|
||||||
QString output = QString::fromUtf8(process.readAllStandardOutput());
|
|
||||||
QStringList lines = output.split('\n', Qt::SkipEmptyParts);
|
|
||||||
|
|
||||||
for (int i = 1; i < lines.size(); ++i) {
|
|
||||||
QString line = lines[i].trimmed();
|
|
||||||
if (!line.isEmpty()) {
|
|
||||||
QString modelName = line.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts)
|
|
||||||
.first();
|
|
||||||
models.append(modelName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Error running 'ollama list':" << process.errorString();
|
LOG_MESSAGE(QString("Error fetching models: %1").arg(reply->errorString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
return models;
|
return models;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,22 +19,22 @@
|
|||||||
|
|
||||||
#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();
|
||||||
|
|
||||||
QString name() const override;
|
QString name() const override;
|
||||||
QString url() const override;
|
QString url() const override;
|
||||||
int defaultPort() const override;
|
|
||||||
QString completionEndpoint() const override;
|
QString completionEndpoint() const override;
|
||||||
void prepareRequest(QJsonObject &request) override;
|
QString chatEndpoint() const 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) override;
|
QList<QString> getInstalledModels(const QString &url) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
144
providers/OpenAICompatProvider.cpp
Normal file
144
providers/OpenAICompatProvider.cpp
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* 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 "OpenAICompatProvider.hpp"
|
||||||
|
#include "settings/ChatAssistantSettings.hpp"
|
||||||
|
#include "settings/CodeCompletionSettings.hpp"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
namespace QodeAssist::Providers {
|
||||||
|
|
||||||
|
OpenAICompatProvider::OpenAICompatProvider() {}
|
||||||
|
|
||||||
|
QString OpenAICompatProvider::name() const
|
||||||
|
{
|
||||||
|
return "OpenAI Compatible (experimental)";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OpenAICompatProvider::url() const
|
||||||
|
{
|
||||||
|
return "http://localhost:1234";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OpenAICompatProvider::completionEndpoint() const
|
||||||
|
{
|
||||||
|
return "/v1/chat/completions";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OpenAICompatProvider::chatEndpoint() const
|
||||||
|
{
|
||||||
|
return "/v1/chat/completions";
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenAICompatProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
|
||||||
|
{
|
||||||
|
auto prepareMessages = [](QJsonObject &req) -> QJsonArray {
|
||||||
|
QJsonArray messages;
|
||||||
|
if (req.contains("system")) {
|
||||||
|
messages.append(
|
||||||
|
QJsonObject{{"role", "system"}, {"content", req.take("system").toString()}});
|
||||||
|
}
|
||||||
|
if (req.contains("prompt")) {
|
||||||
|
messages.append(
|
||||||
|
QJsonObject{{"role", "user"}, {"content", req.take("prompt").toString()}});
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto applyModelParams = [&request](const auto &settings) {
|
||||||
|
request["max_tokens"] = settings.maxTokens();
|
||||||
|
request["temperature"] = settings.temperature();
|
||||||
|
|
||||||
|
if (settings.useTopP())
|
||||||
|
request["top_p"] = settings.topP();
|
||||||
|
if (settings.useTopK())
|
||||||
|
request["top_k"] = settings.topK();
|
||||||
|
if (settings.useFrequencyPenalty())
|
||||||
|
request["frequency_penalty"] = settings.frequencyPenalty();
|
||||||
|
if (settings.usePresencePenalty())
|
||||||
|
request["presence_penalty"] = settings.presencePenalty();
|
||||||
|
const QString &apiKey = settings.apiKey();
|
||||||
|
if (!apiKey.isEmpty()) {
|
||||||
|
request["api_key"] = apiKey;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QJsonArray messages = prepareMessages(request);
|
||||||
|
if (!messages.isEmpty()) {
|
||||||
|
request["messages"] = std::move(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == LLMCore::RequestType::Fim) {
|
||||||
|
applyModelParams(Settings::codeCompletionSettings());
|
||||||
|
} else {
|
||||||
|
applyModelParams(Settings::chatAssistantSettings());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenAICompatProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
|
||||||
|
{
|
||||||
|
bool isComplete = false;
|
||||||
|
while (reply->canReadLine()) {
|
||||||
|
QByteArray line = reply->readLine().trimmed();
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line == "data: [DONE]") {
|
||||||
|
isComplete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line.startsWith("data: ")) {
|
||||||
|
line = line.mid(6); // Remove "data: " prefix
|
||||||
|
}
|
||||||
|
QJsonDocument jsonResponse = QJsonDocument::fromJson(line);
|
||||||
|
if (jsonResponse.isNull()) {
|
||||||
|
qWarning() << "Invalid JSON response from LM Studio:" << line;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QJsonObject responseObj = jsonResponse.object();
|
||||||
|
if (responseObj.contains("choices")) {
|
||||||
|
QJsonArray choices = responseObj["choices"].toArray();
|
||||||
|
if (!choices.isEmpty()) {
|
||||||
|
QJsonObject choice = choices.first().toObject();
|
||||||
|
QJsonObject delta = choice["delta"].toObject();
|
||||||
|
if (delta.contains("content")) {
|
||||||
|
QString completion = delta["content"].toString();
|
||||||
|
|
||||||
|
accumulatedResponse += completion;
|
||||||
|
}
|
||||||
|
if (choice["finish_reason"].toString() == "stop") {
|
||||||
|
isComplete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QString> OpenAICompatProvider::getInstalledModels(const QString &url)
|
||||||
|
{
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Providers
|
||||||
40
providers/OpenAICompatProvider.hpp
Normal file
40
providers/OpenAICompatProvider.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "llmcore/Provider.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Providers {
|
||||||
|
|
||||||
|
class OpenAICompatProvider : public LLMCore::Provider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenAICompatProvider();
|
||||||
|
|
||||||
|
QString name() const override;
|
||||||
|
QString url() const override;
|
||||||
|
QString completionEndpoint() const override;
|
||||||
|
QString chatEndpoint() const override;
|
||||||
|
void prepareRequest(QJsonObject &request, LLMCore::RequestType type) override;
|
||||||
|
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
||||||
|
QList<QString> getInstalledModels(const QString &url) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Providers
|
||||||
@ -26,8 +26,9 @@
|
|||||||
#include <coreplugin/coreconstants.h>
|
#include <coreplugin/coreconstants.h>
|
||||||
#include <coreplugin/icontext.h>
|
#include <coreplugin/icontext.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/messagemanager.h>
|
||||||
|
#include <coreplugin/modemanager.h>
|
||||||
#include <coreplugin/statusbarmanager.h>
|
#include <coreplugin/statusbarmanager.h>
|
||||||
|
|
||||||
#include <extensionsystem/iplugin.h>
|
#include <extensionsystem/iplugin.h>
|
||||||
#include <languageclient/languageclientmanager.h>
|
#include <languageclient/languageclientmanager.h>
|
||||||
|
|
||||||
@ -38,13 +39,24 @@
|
|||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
#include <utils/icon.h>
|
#include <utils/icon.h>
|
||||||
|
|
||||||
#include "LLMProvidersManager.hpp"
|
#include "ConfigurationManager.hpp"
|
||||||
#include "PromptTemplateManager.hpp"
|
|
||||||
#include "QodeAssistClient.hpp"
|
#include "QodeAssistClient.hpp"
|
||||||
|
#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 "templates/CodeLLamaTemplate.hpp"
|
#include "providers/OpenAICompatProvider.hpp"
|
||||||
#include "templates/StarCoder2Template.hpp"
|
|
||||||
|
#include "templates/CodeLlamaChat.hpp"
|
||||||
|
#include "templates/CodeLlamaFim.hpp"
|
||||||
|
#include "templates/CustomFimTemplate.hpp"
|
||||||
|
#include "templates/DeepSeekCoderChat.hpp"
|
||||||
|
#include "templates/DeepSeekCoderFim.hpp"
|
||||||
|
#include "templates/Qwen.hpp"
|
||||||
|
#include "templates/StarCoder2Fim.hpp"
|
||||||
|
#include "templates/StarCoderChat.hpp"
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
@ -64,27 +76,40 @@ 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>();
|
||||||
|
|
||||||
auto &templateManager = PromptTemplateManager::instance();
|
auto &templateManager = LLMCore::PromptTemplateManager::instance();
|
||||||
templateManager.registerTemplate<Templates::CodeLLamaTemplate>();
|
templateManager.registerTemplate<Templates::CodeLlamaFim>();
|
||||||
templateManager.registerTemplate<Templates::StarCoder2Template>();
|
templateManager.registerTemplate<Templates::StarCoder2Fim>();
|
||||||
|
templateManager.registerTemplate<Templates::DeepSeekCoderFim>();
|
||||||
|
templateManager.registerTemplate<Templates::CustomTemplate>();
|
||||||
|
templateManager.registerTemplate<Templates::DeepSeekCoderChat>();
|
||||||
|
templateManager.registerTemplate<Templates::CodeLlamaChat>();
|
||||||
|
templateManager.registerTemplate<Templates::LlamaChat>();
|
||||||
|
templateManager.registerTemplate<Templates::StarCoderChat>();
|
||||||
|
templateManager.registerTemplate<Templates::QwenChat>();
|
||||||
|
templateManager.registerTemplate<Templates::QwenFim>();
|
||||||
|
|
||||||
Utils::Icon QCODEASSIST_ICON(
|
Utils::Icon QCODEASSIST_ICON(
|
||||||
{{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}});
|
{{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}});
|
||||||
|
|
||||||
ActionBuilder requestAction(this, Constants::QODE_ASSIST_REQUEST_SUGGESTION);
|
ActionBuilder requestAction(this, Constants::QODE_ASSIST_REQUEST_SUGGESTION);
|
||||||
requestAction.setToolTip(
|
requestAction.setToolTip(
|
||||||
Tr::tr("Request Ollama suggestion at the current editor's cursor position."));
|
Tr::tr("Generate Qode Assist suggestion at the current cursor position."));
|
||||||
requestAction.setText(Tr::tr("Request Ollama Suggestion"));
|
requestAction.setText(Tr::tr("Request QodeAssist Suggestion"));
|
||||||
requestAction.setIcon(QCODEASSIST_ICON.icon());
|
requestAction.setIcon(QCODEASSIST_ICON.icon());
|
||||||
|
const QKeySequence defaultShortcut = QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Q);
|
||||||
|
requestAction.setDefaultKeySequence(defaultShortcut);
|
||||||
requestAction.addOnTriggered(this, [this] {
|
requestAction.addOnTriggered(this, [this] {
|
||||||
if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) {
|
if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) {
|
||||||
if (m_qodeAssistClient && m_qodeAssistClient->reachable()) {
|
if (m_qodeAssistClient && m_qodeAssistClient->reachable()) {
|
||||||
@ -98,6 +123,11 @@ public:
|
|||||||
auto toggleButton = new QToolButton;
|
auto toggleButton = new QToolButton;
|
||||||
toggleButton->setDefaultAction(requestAction.contextAction());
|
toggleButton->setDefaultAction(requestAction.contextAction());
|
||||||
StatusBarManager::addStatusBarWidget(toggleButton, StatusBarManager::RightCorner);
|
StatusBarManager::addStatusBarWidget(toggleButton, StatusBarManager::RightCorner);
|
||||||
|
|
||||||
|
m_chatOutputPane = new Chat::ChatOutputPane(this);
|
||||||
|
m_navigationPanel = new Chat::NavigationPanel();
|
||||||
|
|
||||||
|
ConfigurationManager::instance().init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void extensionsInitialized() final
|
void extensionsInitialized() final
|
||||||
@ -131,6 +161,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<QodeAssistClient> m_qodeAssistClient;
|
QPointer<QodeAssistClient> m_qodeAssistClient;
|
||||||
|
QPointer<Chat::ChatOutputPane> m_chatOutputPane;
|
||||||
|
QPointer<Chat::NavigationPanel> m_navigationPanel;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Internal
|
} // namespace QodeAssist::Internal
|
||||||
|
|||||||
19
rawPromptExamples/OllamaStarCoder2FIM.json
Normal file
19
rawPromptExamples/OllamaStarCoder2FIM.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"prompt": "{{QODE_INSTRUCTIONS}}<fim_prefix>{{QODE_PREFIX}}<fim_suffix>{{QODE_SUFFIX}}<fim_middle>",
|
||||||
|
"options": {
|
||||||
|
"temperature": 0.7,
|
||||||
|
"top_p": 0.95,
|
||||||
|
"top_k": 40,
|
||||||
|
"num_predict": 175,
|
||||||
|
"stop": [
|
||||||
|
"<|endoftext|>",
|
||||||
|
"<file_sep>",
|
||||||
|
"<fim_prefix>",
|
||||||
|
"<fim_suffix>",
|
||||||
|
"<fim_middle>"
|
||||||
|
],
|
||||||
|
"frequency_penalty": 0,
|
||||||
|
"presence_penalty": 0
|
||||||
|
},
|
||||||
|
"stream": true
|
||||||
|
}
|
||||||
16
rawPromptExamples/OpenAICodeqwenChat.json
Normal file
16
rawPromptExamples/OpenAICodeqwenChat.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"max_tokens": 150,
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"content": "{{QODE_INSTRUCTIONS}}\n### Instruction:{{QODE_PREFIX}}{{QODE_SUFFIX}} ### Response:\n",
|
||||||
|
"role": "user"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stop": [
|
||||||
|
"### Instruction:",
|
||||||
|
"### Response:",
|
||||||
|
"\n\n### "
|
||||||
|
],
|
||||||
|
"stream": true,
|
||||||
|
"temperature": 0.2
|
||||||
|
}
|
||||||
31
settings/Assisttr.h
Normal file
31
settings/Assisttr.h
Normal 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
|
||||||
45
settings/ButtonAspect.hpp
Normal file
45
settings/ButtonAspect.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 <QPushButton>
|
||||||
|
#include <utils/aspects.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
|
|
||||||
|
class ButtonAspect : public Utils::BaseAspect
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ButtonAspect(Utils::AspectContainer *container = nullptr)
|
||||||
|
: Utils::BaseAspect(container)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void addToLayout(Layouting::Layout &parent) override
|
||||||
|
{
|
||||||
|
auto button = new QPushButton(m_buttonText);
|
||||||
|
connect(button, &QPushButton::clicked, this, &ButtonAspect::clicked);
|
||||||
|
parent.addItem(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString m_buttonText;
|
||||||
|
signals:
|
||||||
|
void clicked();
|
||||||
|
};
|
||||||
20
settings/CMakeLists.txt
Normal file
20
settings/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
add_library(QodeAssistSettings STATIC
|
||||||
|
GeneralSettings.hpp GeneralSettings.cpp
|
||||||
|
CustomPromptSettings.hpp CustomPromptSettings.cpp
|
||||||
|
SettingsUtils.hpp
|
||||||
|
SettingsConstants.hpp
|
||||||
|
ButtonAspect.hpp
|
||||||
|
SettingsTr.hpp
|
||||||
|
CodeCompletionSettings.hpp CodeCompletionSettings.cpp
|
||||||
|
ChatAssistantSettings.hpp ChatAssistantSettings.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(QodeAssistSettings
|
||||||
|
PUBLIC
|
||||||
|
Qt::Core
|
||||||
|
Qt::Network
|
||||||
|
QtCreator::Core
|
||||||
|
QtCreator::Utils
|
||||||
|
QodeAssistLogger
|
||||||
|
)
|
||||||
|
target_include_directories(QodeAssistSettings PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
236
settings/ChatAssistantSettings.cpp
Normal file
236
settings/ChatAssistantSettings.cpp
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ChatAssistantSettings.hpp"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
|
|
||||||
|
#include "SettingsConstants.hpp"
|
||||||
|
#include "SettingsTr.hpp"
|
||||||
|
#include "SettingsUtils.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
ChatAssistantSettings &chatAssistantSettings()
|
||||||
|
{
|
||||||
|
static ChatAssistantSettings settings;
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatAssistantSettings::ChatAssistantSettings()
|
||||||
|
{
|
||||||
|
setAutoApply(false);
|
||||||
|
|
||||||
|
setDisplayName(Tr::tr("Chat Assistant"));
|
||||||
|
|
||||||
|
// Chat Settings
|
||||||
|
chatTokensThreshold.setSettingsKey(Constants::CA_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(8000);
|
||||||
|
|
||||||
|
sharingCurrentFile.setSettingsKey(Constants::CA_SHARING_CURRENT_FILE);
|
||||||
|
sharingCurrentFile.setLabelText(Tr::tr("Share Current File With Assistant by Default"));
|
||||||
|
sharingCurrentFile.setDefaultValue(true);
|
||||||
|
|
||||||
|
// General Parameters Settings
|
||||||
|
temperature.setSettingsKey(Constants::CA_TEMPERATURE);
|
||||||
|
temperature.setLabelText(Tr::tr("Temperature:"));
|
||||||
|
temperature.setDefaultValue(0.5);
|
||||||
|
temperature.setRange(0.0, 2.0);
|
||||||
|
temperature.setSingleStep(0.1);
|
||||||
|
|
||||||
|
maxTokens.setSettingsKey(Constants::CA_MAX_TOKENS);
|
||||||
|
maxTokens.setLabelText(Tr::tr("Max Tokens:"));
|
||||||
|
maxTokens.setRange(-1, 10000);
|
||||||
|
maxTokens.setDefaultValue(2000);
|
||||||
|
|
||||||
|
// Advanced Parameters
|
||||||
|
useTopP.setSettingsKey(Constants::CA_USE_TOP_P);
|
||||||
|
useTopP.setDefaultValue(false);
|
||||||
|
useTopP.setLabelText(Tr::tr("Top P:"));
|
||||||
|
|
||||||
|
topP.setSettingsKey(Constants::CA_TOP_P);
|
||||||
|
topP.setDefaultValue(0.9);
|
||||||
|
topP.setRange(0.0, 1.0);
|
||||||
|
topP.setSingleStep(0.1);
|
||||||
|
|
||||||
|
useTopK.setSettingsKey(Constants::CA_USE_TOP_K);
|
||||||
|
useTopK.setDefaultValue(false);
|
||||||
|
useTopK.setLabelText(Tr::tr("Top K:"));
|
||||||
|
|
||||||
|
topK.setSettingsKey(Constants::CA_TOP_K);
|
||||||
|
topK.setDefaultValue(50);
|
||||||
|
topK.setRange(1, 1000);
|
||||||
|
|
||||||
|
usePresencePenalty.setSettingsKey(Constants::CA_USE_PRESENCE_PENALTY);
|
||||||
|
usePresencePenalty.setDefaultValue(false);
|
||||||
|
usePresencePenalty.setLabelText(Tr::tr("Presence Penalty:"));
|
||||||
|
|
||||||
|
presencePenalty.setSettingsKey(Constants::CA_PRESENCE_PENALTY);
|
||||||
|
presencePenalty.setDefaultValue(0.0);
|
||||||
|
presencePenalty.setRange(-2.0, 2.0);
|
||||||
|
presencePenalty.setSingleStep(0.1);
|
||||||
|
|
||||||
|
useFrequencyPenalty.setSettingsKey(Constants::CA_USE_FREQUENCY_PENALTY);
|
||||||
|
useFrequencyPenalty.setDefaultValue(false);
|
||||||
|
useFrequencyPenalty.setLabelText(Tr::tr("Frequency Penalty:"));
|
||||||
|
|
||||||
|
frequencyPenalty.setSettingsKey(Constants::CA_FREQUENCY_PENALTY);
|
||||||
|
frequencyPenalty.setDefaultValue(0.0);
|
||||||
|
frequencyPenalty.setRange(-2.0, 2.0);
|
||||||
|
frequencyPenalty.setSingleStep(0.1);
|
||||||
|
|
||||||
|
// Context Settings
|
||||||
|
useSystemPrompt.setSettingsKey(Constants::CA_USE_SYSTEM_PROMPT);
|
||||||
|
useSystemPrompt.setDefaultValue(true);
|
||||||
|
useSystemPrompt.setLabelText(Tr::tr("Use System Prompt"));
|
||||||
|
|
||||||
|
systemPrompt.setSettingsKey(Constants::CA_SYSTEM_PROMPT);
|
||||||
|
systemPrompt.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
|
||||||
|
systemPrompt.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.");
|
||||||
|
|
||||||
|
// Ollama Settings
|
||||||
|
ollamaLivetime.setSettingsKey(Constants::CA_OLLAMA_LIVETIME);
|
||||||
|
ollamaLivetime.setToolTip(
|
||||||
|
Tr::tr("Time to suspend Ollama after completion request (in minutes), "
|
||||||
|
"Only Ollama, -1 to disable"));
|
||||||
|
ollamaLivetime.setLabelText("Livetime:");
|
||||||
|
ollamaLivetime.setDefaultValue("5m");
|
||||||
|
ollamaLivetime.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
|
||||||
|
contextWindow.setSettingsKey(Constants::CA_OLLAMA_CONTEXT_WINDOW);
|
||||||
|
contextWindow.setLabelText(Tr::tr("Context Window:"));
|
||||||
|
contextWindow.setRange(-1, 10000);
|
||||||
|
contextWindow.setDefaultValue(2048);
|
||||||
|
|
||||||
|
// API Configuration Settings
|
||||||
|
apiKey.setSettingsKey(Constants::CA_API_KEY);
|
||||||
|
apiKey.setLabelText(Tr::tr("API Key:"));
|
||||||
|
apiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
apiKey.setPlaceHolderText(Tr::tr("Enter your API key here"));
|
||||||
|
|
||||||
|
resetToDefaults.m_buttonText = TrConstants::RESET_TO_DEFAULTS;
|
||||||
|
|
||||||
|
readSettings();
|
||||||
|
|
||||||
|
setupConnections();
|
||||||
|
|
||||||
|
setLayouter([this]() {
|
||||||
|
using namespace Layouting;
|
||||||
|
|
||||||
|
auto genGrid = Grid{};
|
||||||
|
genGrid.addRow({Row{temperature}});
|
||||||
|
genGrid.addRow({Row{maxTokens}});
|
||||||
|
|
||||||
|
auto advancedGrid = Grid{};
|
||||||
|
advancedGrid.addRow({useTopP, topP});
|
||||||
|
advancedGrid.addRow({useTopK, topK});
|
||||||
|
advancedGrid.addRow({usePresencePenalty, presencePenalty});
|
||||||
|
advancedGrid.addRow({useFrequencyPenalty, frequencyPenalty});
|
||||||
|
|
||||||
|
auto ollamaGrid = Grid{};
|
||||||
|
ollamaGrid.addRow({ollamaLivetime});
|
||||||
|
ollamaGrid.addRow({contextWindow});
|
||||||
|
|
||||||
|
return Column{Row{Stretch{1}, resetToDefaults},
|
||||||
|
Space{8},
|
||||||
|
Group{title(Tr::tr("Chat Settings")),
|
||||||
|
Column{Row{chatTokensThreshold, Stretch{1}}, sharingCurrentFile}},
|
||||||
|
Space{8},
|
||||||
|
Group{
|
||||||
|
title(Tr::tr("General Parameters")),
|
||||||
|
Row{genGrid, Stretch{1}},
|
||||||
|
},
|
||||||
|
Space{8},
|
||||||
|
Group{title(Tr::tr("Advanced Parameters")),
|
||||||
|
Column{Row{advancedGrid, Stretch{1}}}},
|
||||||
|
Space{8},
|
||||||
|
Group{title(Tr::tr("Context Settings")),
|
||||||
|
Column{
|
||||||
|
Row{useSystemPrompt, Stretch{1}},
|
||||||
|
systemPrompt,
|
||||||
|
}},
|
||||||
|
Group{title(Tr::tr("Ollama Settings")), Column{Row{ollamaGrid, Stretch{1}}}},
|
||||||
|
Space{8},
|
||||||
|
Group{title(Tr::tr("API Configuration")), Column{apiKey}},
|
||||||
|
Stretch{1}};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatAssistantSettings::setupConnections()
|
||||||
|
{
|
||||||
|
connect(&resetToDefaults,
|
||||||
|
&ButtonAspect::clicked,
|
||||||
|
this,
|
||||||
|
&ChatAssistantSettings::resetSettingsToDefaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatAssistantSettings::resetSettingsToDefaults()
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButton reply;
|
||||||
|
reply = QMessageBox::question(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
Tr::tr("Reset Settings"),
|
||||||
|
Tr::tr("Are you sure you want to reset all settings to default values?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (reply == QMessageBox::Yes) {
|
||||||
|
resetAspect(chatTokensThreshold);
|
||||||
|
resetAspect(temperature);
|
||||||
|
resetAspect(maxTokens);
|
||||||
|
resetAspect(useTopP);
|
||||||
|
resetAspect(topP);
|
||||||
|
resetAspect(useTopK);
|
||||||
|
resetAspect(topK);
|
||||||
|
resetAspect(usePresencePenalty);
|
||||||
|
resetAspect(presencePenalty);
|
||||||
|
resetAspect(useFrequencyPenalty);
|
||||||
|
resetAspect(frequencyPenalty);
|
||||||
|
resetAspect(useSystemPrompt);
|
||||||
|
resetAspect(systemPrompt);
|
||||||
|
resetAspect(ollamaLivetime);
|
||||||
|
resetAspect(contextWindow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChatAssistantSettingsPage : public Core::IOptionsPage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChatAssistantSettingsPage()
|
||||||
|
{
|
||||||
|
setId(Constants::QODE_ASSIST_CHAT_ASSISTANT_SETTINGS_PAGE_ID);
|
||||||
|
setDisplayName(Tr::tr("Chat Assistant"));
|
||||||
|
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
|
||||||
|
setSettingsProvider([] { return &chatAssistantSettings(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ChatAssistantSettingsPage chatAssistantSettingsPage;
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
74
settings/ChatAssistantSettings.hpp
Normal file
74
settings/ChatAssistantSettings.hpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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 <utils/aspects.h>
|
||||||
|
|
||||||
|
#include "ButtonAspect.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
class ChatAssistantSettings : public Utils::AspectContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChatAssistantSettings();
|
||||||
|
|
||||||
|
ButtonAspect resetToDefaults{this};
|
||||||
|
|
||||||
|
// Chat settings
|
||||||
|
Utils::IntegerAspect chatTokensThreshold{this};
|
||||||
|
Utils::BoolAspect sharingCurrentFile{this};
|
||||||
|
|
||||||
|
// General Parameters Settings
|
||||||
|
Utils::DoubleAspect temperature{this};
|
||||||
|
Utils::IntegerAspect maxTokens{this};
|
||||||
|
|
||||||
|
// Advanced Parameters
|
||||||
|
Utils::BoolAspect useTopP{this};
|
||||||
|
Utils::DoubleAspect topP{this};
|
||||||
|
|
||||||
|
Utils::BoolAspect useTopK{this};
|
||||||
|
Utils::IntegerAspect topK{this};
|
||||||
|
|
||||||
|
Utils::BoolAspect usePresencePenalty{this};
|
||||||
|
Utils::DoubleAspect presencePenalty{this};
|
||||||
|
|
||||||
|
Utils::BoolAspect useFrequencyPenalty{this};
|
||||||
|
Utils::DoubleAspect frequencyPenalty{this};
|
||||||
|
|
||||||
|
// Context Settings
|
||||||
|
Utils::BoolAspect useSystemPrompt{this};
|
||||||
|
Utils::StringAspect systemPrompt{this};
|
||||||
|
|
||||||
|
// Ollama Settings
|
||||||
|
Utils::StringAspect ollamaLivetime{this};
|
||||||
|
Utils::IntegerAspect contextWindow{this};
|
||||||
|
|
||||||
|
// API Configuration Settings
|
||||||
|
Utils::StringAspect apiKey{this};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupConnections();
|
||||||
|
void resetSettingsToDefaults();
|
||||||
|
};
|
||||||
|
|
||||||
|
ChatAssistantSettings &chatAssistantSettings();
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
317
settings/CodeCompletionSettings.cpp
Normal file
317
settings/CodeCompletionSettings.cpp
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
/*
|
||||||
|
* 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 "CodeCompletionSettings.hpp"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
|
|
||||||
|
#include "SettingsConstants.hpp"
|
||||||
|
#include "SettingsTr.hpp"
|
||||||
|
#include "SettingsUtils.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
CodeCompletionSettings &codeCompletionSettings()
|
||||||
|
{
|
||||||
|
static CodeCompletionSettings settings;
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeCompletionSettings::CodeCompletionSettings()
|
||||||
|
{
|
||||||
|
setAutoApply(false);
|
||||||
|
|
||||||
|
setDisplayName(Tr::tr("Code Completion"));
|
||||||
|
|
||||||
|
// Auto Completion Settings
|
||||||
|
autoCompletion.setSettingsKey(Constants::CC_AUTO_COMPLETION);
|
||||||
|
autoCompletion.setLabelText(Tr::tr("Enable Auto Complete"));
|
||||||
|
autoCompletion.setDefaultValue(true);
|
||||||
|
|
||||||
|
multiLineCompletion.setSettingsKey(Constants::CC_MULTILINE_COMPLETION);
|
||||||
|
multiLineCompletion.setDefaultValue(false);
|
||||||
|
multiLineCompletion.setLabelText(Tr::tr("Enable Multiline Completion(experimental)"));
|
||||||
|
|
||||||
|
startSuggestionTimer.setSettingsKey(Constants::СС_START_SUGGESTION_TIMER);
|
||||||
|
startSuggestionTimer.setLabelText(Tr::tr("with delay(ms)"));
|
||||||
|
startSuggestionTimer.setRange(10, 10000);
|
||||||
|
startSuggestionTimer.setDefaultValue(500);
|
||||||
|
|
||||||
|
autoCompletionCharThreshold.setSettingsKey(Constants::СС_AUTO_COMPLETION_CHAR_THRESHOLD);
|
||||||
|
autoCompletionCharThreshold.setLabelText(Tr::tr("AI suggestion triggers after typing"));
|
||||||
|
autoCompletionCharThreshold.setToolTip(
|
||||||
|
Tr::tr("The number of characters that need to be typed within the typing interval "
|
||||||
|
"before an AI suggestion request is sent."));
|
||||||
|
autoCompletionCharThreshold.setRange(0, 10);
|
||||||
|
autoCompletionCharThreshold.setDefaultValue(0);
|
||||||
|
|
||||||
|
autoCompletionTypingInterval.setSettingsKey(Constants::СС_AUTO_COMPLETION_TYPING_INTERVAL);
|
||||||
|
autoCompletionTypingInterval.setLabelText(Tr::tr("character(s) within(ms)"));
|
||||||
|
autoCompletionTypingInterval.setToolTip(
|
||||||
|
Tr::tr("The time window (in milliseconds) during which the character threshold "
|
||||||
|
"must be met to trigger an AI suggestion request."));
|
||||||
|
autoCompletionTypingInterval.setRange(500, 5000);
|
||||||
|
autoCompletionTypingInterval.setDefaultValue(2000);
|
||||||
|
|
||||||
|
// General Parameters Settings
|
||||||
|
temperature.setSettingsKey(Constants::CC_TEMPERATURE);
|
||||||
|
temperature.setLabelText(Tr::tr("Temperature:"));
|
||||||
|
temperature.setDefaultValue(0.2);
|
||||||
|
temperature.setRange(0.0, 2.0);
|
||||||
|
temperature.setSingleStep(0.1);
|
||||||
|
|
||||||
|
maxTokens.setSettingsKey(Constants::CC_MAX_TOKENS);
|
||||||
|
maxTokens.setLabelText(Tr::tr("Max Tokens:"));
|
||||||
|
maxTokens.setRange(-1, 10000);
|
||||||
|
maxTokens.setDefaultValue(50);
|
||||||
|
|
||||||
|
// Advanced Parameters
|
||||||
|
useTopP.setSettingsKey(Constants::CC_USE_TOP_P);
|
||||||
|
useTopP.setDefaultValue(false);
|
||||||
|
useTopP.setLabelText(Tr::tr("Top P:"));
|
||||||
|
|
||||||
|
topP.setSettingsKey(Constants::CC_TOP_P);
|
||||||
|
topP.setDefaultValue(0.9);
|
||||||
|
topP.setRange(0.0, 1.0);
|
||||||
|
topP.setSingleStep(0.1);
|
||||||
|
|
||||||
|
useTopK.setSettingsKey(Constants::CC_USE_TOP_K);
|
||||||
|
useTopK.setDefaultValue(false);
|
||||||
|
useTopK.setLabelText(Tr::tr("Top K:"));
|
||||||
|
|
||||||
|
topK.setSettingsKey(Constants::CC_TOP_K);
|
||||||
|
topK.setDefaultValue(50);
|
||||||
|
topK.setRange(1, 1000);
|
||||||
|
|
||||||
|
usePresencePenalty.setSettingsKey(Constants::CC_USE_PRESENCE_PENALTY);
|
||||||
|
usePresencePenalty.setDefaultValue(false);
|
||||||
|
usePresencePenalty.setLabelText(Tr::tr("Presence Penalty:"));
|
||||||
|
|
||||||
|
presencePenalty.setSettingsKey(Constants::CC_PRESENCE_PENALTY);
|
||||||
|
presencePenalty.setDefaultValue(0.0);
|
||||||
|
presencePenalty.setRange(-2.0, 2.0);
|
||||||
|
presencePenalty.setSingleStep(0.1);
|
||||||
|
|
||||||
|
useFrequencyPenalty.setSettingsKey(Constants::CC_USE_FREQUENCY_PENALTY);
|
||||||
|
useFrequencyPenalty.setDefaultValue(false);
|
||||||
|
useFrequencyPenalty.setLabelText(Tr::tr("Frequency Penalty:"));
|
||||||
|
|
||||||
|
frequencyPenalty.setSettingsKey(Constants::CC_FREQUENCY_PENALTY);
|
||||||
|
frequencyPenalty.setDefaultValue(0.0);
|
||||||
|
frequencyPenalty.setRange(-2.0, 2.0);
|
||||||
|
frequencyPenalty.setSingleStep(0.1);
|
||||||
|
|
||||||
|
// Context Settings
|
||||||
|
readFullFile.setSettingsKey(Constants::CC_READ_FULL_FILE);
|
||||||
|
readFullFile.setLabelText(Tr::tr("Read Full File"));
|
||||||
|
readFullFile.setDefaultValue(false);
|
||||||
|
|
||||||
|
readFileParts.setLabelText(Tr::tr("Read Strings Before Cursor:"));
|
||||||
|
readFileParts.setDefaultValue(true);
|
||||||
|
|
||||||
|
readStringsBeforeCursor.setSettingsKey(Constants::CC_READ_STRINGS_BEFORE_CURSOR);
|
||||||
|
readStringsBeforeCursor.setRange(0, 10000);
|
||||||
|
readStringsBeforeCursor.setDefaultValue(50);
|
||||||
|
|
||||||
|
readStringsAfterCursor.setSettingsKey(Constants::CC_READ_STRINGS_AFTER_CURSOR);
|
||||||
|
readStringsAfterCursor.setLabelText(Tr::tr("Read Strings After Cursor:"));
|
||||||
|
readStringsAfterCursor.setRange(0, 10000);
|
||||||
|
readStringsAfterCursor.setDefaultValue(30);
|
||||||
|
|
||||||
|
useSystemPrompt.setSettingsKey(Constants::CC_USE_SYSTEM_PROMPT);
|
||||||
|
useSystemPrompt.setDefaultValue(true);
|
||||||
|
useSystemPrompt.setLabelText(Tr::tr("Use System Prompt"));
|
||||||
|
|
||||||
|
systemPrompt.setSettingsKey(Constants::CC_SYSTEM_PROMPT);
|
||||||
|
systemPrompt.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
|
||||||
|
systemPrompt.setDefaultValue(
|
||||||
|
"You are an expert C++, Qt, and QML code completion AI. Your task is to provide accurate "
|
||||||
|
"and contextually appropriate code suggestions.");
|
||||||
|
|
||||||
|
useFilePathInContext.setSettingsKey(Constants::CC_USE_FILE_PATH_IN_CONTEXT);
|
||||||
|
useFilePathInContext.setDefaultValue(false);
|
||||||
|
useFilePathInContext.setLabelText(Tr::tr("Use File Path in Context"));
|
||||||
|
|
||||||
|
useProjectChangesCache.setSettingsKey(Constants::CC_USE_PROJECT_CHANGES_CACHE);
|
||||||
|
useProjectChangesCache.setDefaultValue(true);
|
||||||
|
useProjectChangesCache.setLabelText(Tr::tr("Max Changes Cache Size:"));
|
||||||
|
|
||||||
|
maxChangesCacheSize.setSettingsKey(Constants::CC_MAX_CHANGES_CACHE_SIZE);
|
||||||
|
maxChangesCacheSize.setRange(2, 1000);
|
||||||
|
maxChangesCacheSize.setDefaultValue(10);
|
||||||
|
|
||||||
|
// Ollama Settings
|
||||||
|
ollamaLivetime.setSettingsKey(Constants::CC_OLLAMA_LIVETIME);
|
||||||
|
ollamaLivetime.setToolTip(
|
||||||
|
Tr::tr("Time to suspend Ollama after completion request (in minutes), "
|
||||||
|
"Only Ollama, -1 to disable"));
|
||||||
|
ollamaLivetime.setLabelText("Livetime:");
|
||||||
|
ollamaLivetime.setDefaultValue("5m");
|
||||||
|
ollamaLivetime.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
|
||||||
|
contextWindow.setSettingsKey(Constants::CC_OLLAMA_CONTEXT_WINDOW);
|
||||||
|
contextWindow.setLabelText(Tr::tr("Context Window:"));
|
||||||
|
contextWindow.setRange(-1, 10000);
|
||||||
|
contextWindow.setDefaultValue(2048);
|
||||||
|
|
||||||
|
// API Configuration Settings
|
||||||
|
apiKey.setSettingsKey(Constants::CC_API_KEY);
|
||||||
|
apiKey.setLabelText(Tr::tr("API Key:"));
|
||||||
|
apiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
apiKey.setPlaceHolderText(Tr::tr("Enter your API key here"));
|
||||||
|
|
||||||
|
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
|
||||||
|
|
||||||
|
readSettings();
|
||||||
|
|
||||||
|
readFileParts.setValue(!readFullFile.value());
|
||||||
|
|
||||||
|
setupConnections();
|
||||||
|
|
||||||
|
setLayouter([this]() {
|
||||||
|
using namespace Layouting;
|
||||||
|
|
||||||
|
auto genGrid = Grid{};
|
||||||
|
genGrid.addRow({Row{temperature}});
|
||||||
|
genGrid.addRow({Row{maxTokens}});
|
||||||
|
|
||||||
|
auto advancedGrid = Grid{};
|
||||||
|
advancedGrid.addRow({useTopP, topP});
|
||||||
|
advancedGrid.addRow({useTopK, topK});
|
||||||
|
advancedGrid.addRow({usePresencePenalty, presencePenalty});
|
||||||
|
advancedGrid.addRow({useFrequencyPenalty, frequencyPenalty});
|
||||||
|
|
||||||
|
auto ollamaGrid = Grid{};
|
||||||
|
ollamaGrid.addRow({ollamaLivetime});
|
||||||
|
ollamaGrid.addRow({contextWindow});
|
||||||
|
|
||||||
|
auto contextGrid = Grid{};
|
||||||
|
contextGrid.addRow({Row{readFullFile}});
|
||||||
|
contextGrid.addRow({Row{readFileParts, readStringsBeforeCursor, readStringsAfterCursor}});
|
||||||
|
|
||||||
|
auto contextItem = Column{Row{contextGrid, Stretch{1}},
|
||||||
|
Row{useSystemPrompt, Stretch{1}},
|
||||||
|
systemPrompt,
|
||||||
|
Row{useFilePathInContext, Stretch{1}},
|
||||||
|
Row{useProjectChangesCache, maxChangesCacheSize, Stretch{1}}};
|
||||||
|
|
||||||
|
return Column{Row{Stretch{1}, resetToDefaults},
|
||||||
|
Space{8},
|
||||||
|
Group{title(Tr::tr("Auto Completion Settings")),
|
||||||
|
Column{autoCompletion,
|
||||||
|
Space{8},
|
||||||
|
multiLineCompletion,
|
||||||
|
Row{autoCompletionCharThreshold,
|
||||||
|
autoCompletionTypingInterval,
|
||||||
|
startSuggestionTimer,
|
||||||
|
Stretch{1}}}},
|
||||||
|
Space{8},
|
||||||
|
Group{title(Tr::tr("General Parameters")),
|
||||||
|
Column{
|
||||||
|
Row{genGrid, Stretch{1}},
|
||||||
|
}},
|
||||||
|
Space{8},
|
||||||
|
Group{title(Tr::tr("Advanced Parameters")),
|
||||||
|
Column{Row{advancedGrid, Stretch{1}}}},
|
||||||
|
Space{8},
|
||||||
|
Group{title(Tr::tr("Context Settings")), contextItem},
|
||||||
|
Space{8},
|
||||||
|
Group{title(Tr::tr("Ollama Settings")), Column{Row{ollamaGrid, Stretch{1}}}},
|
||||||
|
Space{8},
|
||||||
|
Group{title(Tr::tr("API Configuration")), Column{apiKey}},
|
||||||
|
Stretch{1}};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeCompletionSettings::setupConnections()
|
||||||
|
{
|
||||||
|
connect(&resetToDefaults,
|
||||||
|
&ButtonAspect::clicked,
|
||||||
|
this,
|
||||||
|
&CodeCompletionSettings::resetSettingsToDefaults);
|
||||||
|
|
||||||
|
connect(&readFullFile, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
||||||
|
if (readFullFile.volatileValue()) {
|
||||||
|
readFileParts.setValue(false);
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&readFileParts, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
||||||
|
if (readFileParts.volatileValue()) {
|
||||||
|
readFullFile.setValue(false);
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeCompletionSettings::resetSettingsToDefaults()
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButton reply;
|
||||||
|
reply = QMessageBox::question(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
Tr::tr("Reset Settings"),
|
||||||
|
Tr::tr("Are you sure you want to reset all settings to default values?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (reply == QMessageBox::Yes) {
|
||||||
|
resetAspect(autoCompletion);
|
||||||
|
resetAspect(multiLineCompletion);
|
||||||
|
resetAspect(temperature);
|
||||||
|
resetAspect(maxTokens);
|
||||||
|
resetAspect(useTopP);
|
||||||
|
resetAspect(topP);
|
||||||
|
resetAspect(useTopK);
|
||||||
|
resetAspect(topK);
|
||||||
|
resetAspect(usePresencePenalty);
|
||||||
|
resetAspect(presencePenalty);
|
||||||
|
resetAspect(useFrequencyPenalty);
|
||||||
|
resetAspect(frequencyPenalty);
|
||||||
|
resetAspect(readFullFile);
|
||||||
|
resetAspect(readFileParts);
|
||||||
|
resetAspect(readStringsBeforeCursor);
|
||||||
|
resetAspect(readStringsAfterCursor);
|
||||||
|
resetAspect(useSystemPrompt);
|
||||||
|
resetAspect(systemPrompt);
|
||||||
|
resetAspect(useFilePathInContext);
|
||||||
|
resetAspect(useProjectChangesCache);
|
||||||
|
resetAspect(maxChangesCacheSize);
|
||||||
|
resetAspect(ollamaLivetime);
|
||||||
|
resetAspect(contextWindow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CodeCompletionSettingsPage : public Core::IOptionsPage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CodeCompletionSettingsPage()
|
||||||
|
{
|
||||||
|
setId(Constants::QODE_ASSIST_CODE_COMPLETION_SETTINGS_PAGE_ID);
|
||||||
|
setDisplayName(Tr::tr("Code Completion"));
|
||||||
|
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
|
||||||
|
setSettingsProvider([] { return &codeCompletionSettings(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const CodeCompletionSettingsPage codeCompletionSettingsPage;
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
@ -19,69 +19,37 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <utils/aspects.h>
|
#include <utils/aspects.h>
|
||||||
#include <utils/layoutbuilder.h>
|
|
||||||
|
|
||||||
namespace QodeAssist {
|
#include "ButtonAspect.hpp"
|
||||||
|
|
||||||
template<typename AspectType>
|
namespace QodeAssist::Settings {
|
||||||
void resetAspect(AspectType &aspect)
|
|
||||||
{
|
|
||||||
aspect.setValue(aspect.defaultValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
class ButtonAspect : public Utils::BaseAspect
|
class CodeCompletionSettings : public Utils::AspectContainer
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
ButtonAspect(Utils::AspectContainer *container = nullptr)
|
|
||||||
: Utils::BaseAspect(container)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void addToLayout(Layouting::Layout &parent) override
|
|
||||||
{
|
|
||||||
auto button = new QPushButton(m_buttonText);
|
|
||||||
connect(button, &QPushButton::clicked, this, &ButtonAspect::clicked);
|
|
||||||
parent.addItem(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString m_buttonText;
|
|
||||||
signals:
|
|
||||||
void clicked();
|
|
||||||
};
|
|
||||||
|
|
||||||
class QodeAssistSettings : public Utils::AspectContainer
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QodeAssistSettings();
|
CodeCompletionSettings();
|
||||||
|
|
||||||
Utils::BoolAspect enableQodeAssist{this};
|
ButtonAspect resetToDefaults{this};
|
||||||
Utils::BoolAspect enableAutoComplete{this};
|
|
||||||
Utils::BoolAspect enableLogging{this};
|
|
||||||
|
|
||||||
Utils::SelectionAspect llmProviders{this};
|
// Auto Completion Settings
|
||||||
Utils::StringAspect url{this};
|
Utils::BoolAspect autoCompletion{this};
|
||||||
Utils::IntegerAspect port{this};
|
Utils::BoolAspect multiLineCompletion{this};
|
||||||
Utils::StringAspect endPoint{this};
|
|
||||||
|
|
||||||
Utils::StringAspect modelName{this};
|
Utils::IntegerAspect startSuggestionTimer{this};
|
||||||
ButtonAspect selectModels{this};
|
Utils::IntegerAspect autoCompletionCharThreshold{this};
|
||||||
|
Utils::IntegerAspect autoCompletionTypingInterval{this};
|
||||||
|
|
||||||
Utils::SelectionAspect fimPrompts{this};
|
// General Parameters Settings
|
||||||
Utils::DoubleAspect temperature{this};
|
Utils::DoubleAspect temperature{this};
|
||||||
Utils::IntegerAspect maxTokens{this};
|
Utils::IntegerAspect maxTokens{this};
|
||||||
|
|
||||||
Utils::BoolAspect readFullFile{this};
|
// Advanced Parameters
|
||||||
Utils::IntegerAspect readStringsBeforeCursor{this};
|
|
||||||
Utils::IntegerAspect readStringsAfterCursor{this};
|
|
||||||
|
|
||||||
Utils::BoolAspect useTopP{this};
|
Utils::BoolAspect useTopP{this};
|
||||||
Utils::DoubleAspect topP{this};
|
Utils::DoubleAspect topP{this};
|
||||||
|
|
||||||
Utils::BoolAspect useTopK{this};
|
Utils::BoolAspect useTopK{this};
|
||||||
Utils::DoubleAspect topK{this};
|
Utils::IntegerAspect topK{this};
|
||||||
|
|
||||||
Utils::BoolAspect usePresencePenalty{this};
|
Utils::BoolAspect usePresencePenalty{this};
|
||||||
Utils::DoubleAspect presencePenalty{this};
|
Utils::DoubleAspect presencePenalty{this};
|
||||||
@ -89,24 +57,29 @@ public:
|
|||||||
Utils::BoolAspect useFrequencyPenalty{this};
|
Utils::BoolAspect useFrequencyPenalty{this};
|
||||||
Utils::DoubleAspect frequencyPenalty{this};
|
Utils::DoubleAspect frequencyPenalty{this};
|
||||||
|
|
||||||
Utils::StringListAspect providerPaths{this};
|
// Context Settings
|
||||||
|
Utils::BoolAspect readFullFile{this};
|
||||||
Utils::IntegerAspect startSuggestionTimer{this};
|
Utils::BoolAspect readFileParts{this};
|
||||||
Utils::IntegerAspect maxFileThreshold{this};
|
Utils::IntegerAspect readStringsBeforeCursor{this};
|
||||||
|
Utils::IntegerAspect readStringsAfterCursor{this};
|
||||||
|
Utils::BoolAspect useSystemPrompt{this};
|
||||||
|
Utils::StringAspect systemPrompt{this};
|
||||||
|
Utils::BoolAspect useFilePathInContext{this};
|
||||||
|
Utils::BoolAspect useProjectChangesCache{this};
|
||||||
|
Utils::IntegerAspect maxChangesCacheSize{this};
|
||||||
|
|
||||||
|
// Ollama Settings
|
||||||
Utils::StringAspect ollamaLivetime{this};
|
Utils::StringAspect ollamaLivetime{this};
|
||||||
|
Utils::IntegerAspect contextWindow{this};
|
||||||
|
|
||||||
ButtonAspect resetToDefaults{this};
|
// API Configuration Settings
|
||||||
|
Utils::StringAspect apiKey{this};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupConnections();
|
void setupConnections();
|
||||||
void updateProviderSettings();
|
|
||||||
QStringList getInstalledModels();
|
|
||||||
void showModelSelectionDialog();
|
|
||||||
Utils::Environment getEnvironmentWithProviderPaths() const;
|
|
||||||
void resetSettingsToDefaults();
|
void resetSettingsToDefaults();
|
||||||
};
|
};
|
||||||
|
|
||||||
QodeAssistSettings &settings();
|
CodeCompletionSettings &codeCompletionSettings();
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist::Settings
|
||||||
201
settings/CustomPromptSettings.cpp
Normal file
201
settings/CustomPromptSettings.cpp
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* 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 "CustomPromptSettings.hpp"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QJsonParseError>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
|
|
||||||
|
#include "SettingsConstants.hpp"
|
||||||
|
#include "SettingsTr.hpp"
|
||||||
|
#include "SettingsUtils.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
CustomPromptSettings &customPromptSettings()
|
||||||
|
{
|
||||||
|
static CustomPromptSettings settings;
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomPromptSettings::CustomPromptSettings()
|
||||||
|
{
|
||||||
|
setAutoApply(false);
|
||||||
|
|
||||||
|
setDisplayName(Tr::tr("Custom Prompt"));
|
||||||
|
|
||||||
|
customJsonLabel.setLabelText("Custom JSON Template:");
|
||||||
|
customJsonLabel.setDisplayStyle(Utils::StringAspect::LabelDisplay);
|
||||||
|
|
||||||
|
customJsonLegend.setLabelText(Tr::tr(R"(Prompt components:
|
||||||
|
- model is set on General Page
|
||||||
|
- {{QODE_INSTRUCTIONS}}: Placeholder for specific instructions or context.
|
||||||
|
- {{QODE_PREFIX}}: Will be replaced with the actual code before the cursor.
|
||||||
|
- {{QODE_SUFFIX}}: Will be replaced with the actual code after the cursor.
|
||||||
|
)"));
|
||||||
|
|
||||||
|
customJsonTemplate.setSettingsKey(Constants::CUSTOM_JSON_TEMPLATE);
|
||||||
|
customJsonTemplate.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
|
||||||
|
|
||||||
|
customJsonTemplate.setDefaultValue(R"({
|
||||||
|
"prompt": "{{QODE_INSTRUCTIONS}}<fim_prefix>{{QODE_PREFIX}}<fim_suffix>{{QODE_SUFFIX}}<fim_middle>",
|
||||||
|
"options": {
|
||||||
|
"temperature": 0.7,
|
||||||
|
"top_p": 0.95,
|
||||||
|
"top_k": 40,
|
||||||
|
"num_predict": 100,
|
||||||
|
"stop": [
|
||||||
|
"<|endoftext|>",
|
||||||
|
"<file_sep>",
|
||||||
|
"<fim_prefix>",
|
||||||
|
"<fim_suffix>",
|
||||||
|
"<fim_middle>"
|
||||||
|
],
|
||||||
|
"frequency_penalty": 0,
|
||||||
|
"presence_penalty": 0
|
||||||
|
},
|
||||||
|
"stream": true
|
||||||
|
})");
|
||||||
|
saveCustomTemplateButton.m_buttonText = (Tr::tr("Save Custom Template to JSON"));
|
||||||
|
loadCustomTemplateButton.m_buttonText = (Tr::tr("Load Custom Template from JSON"));
|
||||||
|
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
|
||||||
|
|
||||||
|
readSettings();
|
||||||
|
|
||||||
|
setupConnection();
|
||||||
|
|
||||||
|
setLayouter([this]() {
|
||||||
|
using namespace Layouting;
|
||||||
|
return Column{Group{title(Tr::tr("Custom prompt for FIM model")),
|
||||||
|
Column{Row{customJsonLabel, Stretch{1}, resetToDefaults},
|
||||||
|
Row{customJsonTemplate,
|
||||||
|
Column{saveCustomTemplateButton,
|
||||||
|
loadCustomTemplateButton,
|
||||||
|
customJsonLegend,
|
||||||
|
Stretch{1}}}}}};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomPromptSettings::setupConnection()
|
||||||
|
{
|
||||||
|
connect(&resetToDefaults,
|
||||||
|
&ButtonAspect::clicked,
|
||||||
|
this,
|
||||||
|
&CustomPromptSettings::resetSettingsToDefaults);
|
||||||
|
connect(&saveCustomTemplateButton,
|
||||||
|
&ButtonAspect::clicked,
|
||||||
|
this,
|
||||||
|
&CustomPromptSettings::saveCustomTemplate);
|
||||||
|
connect(&loadCustomTemplateButton,
|
||||||
|
&ButtonAspect::clicked,
|
||||||
|
this,
|
||||||
|
&CustomPromptSettings::loadCustomTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomPromptSettings::resetSettingsToDefaults()
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButton reply;
|
||||||
|
reply = QMessageBox::question(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
Tr::tr("Reset Settings"),
|
||||||
|
Tr::tr("Are you sure you want to reset all settings to default values?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (reply == QMessageBox::Yes) {
|
||||||
|
resetAspect(customJsonTemplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomPromptSettings::saveCustomTemplate()
|
||||||
|
{
|
||||||
|
QString fileName = QFileDialog::getSaveFileName(nullptr,
|
||||||
|
Tr::tr("Save JSON Template"),
|
||||||
|
QString(),
|
||||||
|
Tr::tr("JSON Files (*.json)"));
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(fileName);
|
||||||
|
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
|
QTextStream out(&file);
|
||||||
|
out << customJsonTemplate.value();
|
||||||
|
file.close();
|
||||||
|
QMessageBox::information(nullptr,
|
||||||
|
Tr::tr("Save Successful"),
|
||||||
|
Tr::tr("JSON template has been saved successfully."));
|
||||||
|
} else {
|
||||||
|
QMessageBox::critical(nullptr,
|
||||||
|
Tr::tr("Save Failed"),
|
||||||
|
Tr::tr("Failed to save JSON template."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomPromptSettings::loadCustomTemplate()
|
||||||
|
{
|
||||||
|
QString fileName = QFileDialog::getOpenFileName(nullptr,
|
||||||
|
Tr::tr("Load JSON Template"),
|
||||||
|
QString(),
|
||||||
|
Tr::tr("JSON Files (*.json)"));
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(fileName);
|
||||||
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
QTextStream in(&file);
|
||||||
|
QString jsonContent = in.readAll();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
QJsonParseError parseError;
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonContent.toUtf8(), &parseError);
|
||||||
|
if (parseError.error == QJsonParseError::NoError) {
|
||||||
|
customJsonTemplate.setVolatileValue(jsonContent);
|
||||||
|
QMessageBox::information(nullptr,
|
||||||
|
Tr::tr("Load Successful"),
|
||||||
|
Tr::tr("JSON template has been loaded successfully."));
|
||||||
|
} else {
|
||||||
|
QMessageBox::critical(nullptr,
|
||||||
|
Tr::tr("Invalid JSON"),
|
||||||
|
Tr::tr("The selected file contains invalid JSON."));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QMessageBox::critical(nullptr,
|
||||||
|
Tr::tr("Load Failed"),
|
||||||
|
Tr::tr("Failed to load JSON template."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomPromptSettingsPage : public Core::IOptionsPage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CustomPromptSettingsPage()
|
||||||
|
{
|
||||||
|
setId(Constants::QODE_ASSIST_CUSTOM_PROMPT_SETTINGS_PAGE_ID);
|
||||||
|
setDisplayName(Tr::tr("Custom Prompt"));
|
||||||
|
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
|
||||||
|
setSettingsProvider([] { return &customPromptSettings(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const CustomPromptSettingsPage customPromptSettingsPage;
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
49
settings/CustomPromptSettings.hpp
Normal file
49
settings/CustomPromptSettings.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utils/aspects.h>
|
||||||
|
|
||||||
|
#include "ButtonAspect.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
class CustomPromptSettings : public Utils::AspectContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CustomPromptSettings();
|
||||||
|
|
||||||
|
Utils::StringAspect customJsonLabel{this};
|
||||||
|
Utils::StringAspect customJsonTemplate{this};
|
||||||
|
Utils::StringAspect customJsonLegend{this};
|
||||||
|
ButtonAspect saveCustomTemplateButton{this};
|
||||||
|
ButtonAspect loadCustomTemplateButton{this};
|
||||||
|
ButtonAspect resetToDefaults{this};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupConnection();
|
||||||
|
void resetSettingsToDefaults();
|
||||||
|
void saveCustomTemplate();
|
||||||
|
void loadCustomTemplate();
|
||||||
|
};
|
||||||
|
|
||||||
|
CustomPromptSettings &customPromptSettings();
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
211
settings/GeneralSettings.cpp
Normal file
211
settings/GeneralSettings.cpp
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* 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 "GeneralSettings.hpp"
|
||||||
|
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
|
#include "Logger.hpp"
|
||||||
|
#include "SettingsConstants.hpp"
|
||||||
|
#include "SettingsTr.hpp"
|
||||||
|
#include "SettingsUtils.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
GeneralSettings &generalSettings()
|
||||||
|
{
|
||||||
|
static GeneralSettings settings;
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
GeneralSettings::GeneralSettings()
|
||||||
|
{
|
||||||
|
setAutoApply(false);
|
||||||
|
|
||||||
|
setDisplayName(TrConstants::GENERAL);
|
||||||
|
|
||||||
|
enableQodeAssist.setSettingsKey(Constants::ENABLE_QODE_ASSIST);
|
||||||
|
enableQodeAssist.setLabelText(TrConstants::ENABLE_QODE_ASSIST);
|
||||||
|
enableQodeAssist.setDefaultValue(true);
|
||||||
|
|
||||||
|
enableLogging.setSettingsKey(Constants::ENABLE_LOGGING);
|
||||||
|
enableLogging.setLabelText(TrConstants::ENABLE_LOG);
|
||||||
|
enableLogging.setDefaultValue(false);
|
||||||
|
|
||||||
|
resetToDefaults.m_buttonText = TrConstants::RESET_TO_DEFAULTS;
|
||||||
|
|
||||||
|
initStringAspect(ccProvider, Constants::CC_PROVIDER, TrConstants::PROVIDER, "Ollama");
|
||||||
|
ccProvider.setReadOnly(true);
|
||||||
|
ccSelectProvider.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
|
initStringAspect(ccModel, Constants::CC_MODEL, TrConstants::MODEL, "codellama:7b-code");
|
||||||
|
ccModel.setHistoryCompleter(Constants::CC_MODEL_HISTORY);
|
||||||
|
ccSelectModel.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
|
initStringAspect(ccTemplate, Constants::CC_TEMPLATE, TrConstants::TEMPLATE, "CodeLlama FIM");
|
||||||
|
ccTemplate.setReadOnly(true);
|
||||||
|
ccSelectTemplate.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
|
initStringAspect(ccUrl, Constants::CC_URL, TrConstants::URL, "http://localhost:11434");
|
||||||
|
ccUrl.setHistoryCompleter(Constants::CC_URL_HISTORY);
|
||||||
|
ccSetUrl.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
|
ccStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay);
|
||||||
|
ccStatus.setLabelText(TrConstants::STATUS);
|
||||||
|
ccStatus.setDefaultValue("");
|
||||||
|
ccTest.m_buttonText = TrConstants::TEST;
|
||||||
|
|
||||||
|
initStringAspect(caProvider, Constants::CA_PROVIDER, TrConstants::PROVIDER, "Ollama");
|
||||||
|
caProvider.setReadOnly(true);
|
||||||
|
caSelectProvider.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
|
initStringAspect(caModel, Constants::CA_MODEL, TrConstants::MODEL, "codellama:7b-instruct");
|
||||||
|
caModel.setHistoryCompleter(Constants::CA_MODEL_HISTORY);
|
||||||
|
caSelectModel.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
|
initStringAspect(caTemplate, Constants::CA_TEMPLATE, TrConstants::TEMPLATE, "CodeLlama Chat");
|
||||||
|
caTemplate.setReadOnly(true);
|
||||||
|
|
||||||
|
caSelectTemplate.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
|
initStringAspect(caUrl, Constants::CA_URL, TrConstants::URL, "http://localhost:11434");
|
||||||
|
caUrl.setHistoryCompleter(Constants::CA_URL_HISTORY);
|
||||||
|
caSetUrl.m_buttonText = TrConstants::SELECT;
|
||||||
|
|
||||||
|
caStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay);
|
||||||
|
caStatus.setLabelText(TrConstants::STATUS);
|
||||||
|
caStatus.setDefaultValue("");
|
||||||
|
caTest.m_buttonText = TrConstants::TEST;
|
||||||
|
|
||||||
|
readSettings();
|
||||||
|
|
||||||
|
Logger::instance().setLoggingEnabled(enableLogging());
|
||||||
|
|
||||||
|
setupConnections();
|
||||||
|
|
||||||
|
setLayouter([this]() {
|
||||||
|
using namespace Layouting;
|
||||||
|
|
||||||
|
auto ccGrid = Grid{};
|
||||||
|
ccGrid.addRow({ccProvider, ccSelectProvider});
|
||||||
|
ccGrid.addRow({ccModel, ccSelectModel});
|
||||||
|
ccGrid.addRow({ccTemplate, ccSelectTemplate});
|
||||||
|
ccGrid.addRow({ccUrl, ccSetUrl});
|
||||||
|
ccGrid.addRow({ccStatus, ccTest});
|
||||||
|
|
||||||
|
auto caGrid = Grid{};
|
||||||
|
caGrid.addRow({caProvider, caSelectProvider});
|
||||||
|
caGrid.addRow({caModel, caSelectModel});
|
||||||
|
caGrid.addRow({caTemplate, caSelectTemplate});
|
||||||
|
caGrid.addRow({caUrl, caSetUrl});
|
||||||
|
caGrid.addRow({caStatus, caTest});
|
||||||
|
|
||||||
|
auto ccGroup = Group{title(TrConstants::CODE_COMPLETION), ccGrid};
|
||||||
|
auto caGroup = Group{title(TrConstants::CHAT_ASSISTANT), caGrid};
|
||||||
|
|
||||||
|
auto rootLayout = Column{Row{enableQodeAssist, Stretch{1}, resetToDefaults},
|
||||||
|
Row{enableLogging, Stretch{1}},
|
||||||
|
Space{8},
|
||||||
|
ccGroup,
|
||||||
|
Space{8},
|
||||||
|
caGroup,
|
||||||
|
Stretch{1}};
|
||||||
|
|
||||||
|
return rootLayout;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::showSelectionDialog(const QStringList &data,
|
||||||
|
Utils::StringAspect &aspect,
|
||||||
|
const QString &title,
|
||||||
|
const QString &text)
|
||||||
|
{
|
||||||
|
if (data.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
QInputDialog dialog(Core::ICore::dialogParent());
|
||||||
|
dialog.setWindowTitle(title);
|
||||||
|
dialog.setLabelText(text);
|
||||||
|
dialog.setComboBoxItems(data);
|
||||||
|
dialog.setComboBoxEditable(false);
|
||||||
|
dialog.setFixedSize(400, 150);
|
||||||
|
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
QString result = dialog.textValue();
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
aspect.setValue(result);
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::setupConnections()
|
||||||
|
{
|
||||||
|
connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
||||||
|
Logger::instance().setLoggingEnabled(enableLogging.volatileValue());
|
||||||
|
});
|
||||||
|
connect(&resetToDefaults, &ButtonAspect::clicked, this, &GeneralSettings::resetPageToDefaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::resetPageToDefaults()
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButton reply;
|
||||||
|
reply = QMessageBox::question(Core::ICore::dialogParent(),
|
||||||
|
TrConstants::RESET_SETTINGS,
|
||||||
|
TrConstants::CONFIRMATION,
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (reply == QMessageBox::Yes) {
|
||||||
|
resetAspect(enableQodeAssist);
|
||||||
|
resetAspect(enableLogging);
|
||||||
|
resetAspect(ccProvider);
|
||||||
|
resetAspect(ccModel);
|
||||||
|
resetAspect(ccTemplate);
|
||||||
|
resetAspect(ccUrl);
|
||||||
|
resetAspect(caProvider);
|
||||||
|
resetAspect(caModel);
|
||||||
|
resetAspect(caTemplate);
|
||||||
|
resetAspect(caUrl);
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GeneralSettingsPage : public Core::IOptionsPage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GeneralSettingsPage()
|
||||||
|
{
|
||||||
|
setId(Constants::QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID);
|
||||||
|
setDisplayName(TrConstants::GENERAL);
|
||||||
|
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
|
||||||
|
setDisplayCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY);
|
||||||
|
setCategoryIconPath(":/resources/images/qoderassist-icon.png");
|
||||||
|
setSettingsProvider([] { return &generalSettings(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const GeneralSettingsPage generalSettingsPage;
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
84
settings/GeneralSettings.hpp
Normal file
84
settings/GeneralSettings.hpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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 <utils/aspects.h>
|
||||||
|
|
||||||
|
#include "ButtonAspect.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::LLMCore {
|
||||||
|
class Provider;
|
||||||
|
}
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
class GeneralSettings : public Utils::AspectContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GeneralSettings();
|
||||||
|
|
||||||
|
Utils::BoolAspect enableQodeAssist{this};
|
||||||
|
Utils::BoolAspect enableLogging{this};
|
||||||
|
ButtonAspect resetToDefaults{this};
|
||||||
|
|
||||||
|
// code completion setttings
|
||||||
|
Utils::StringAspect ccProvider{this};
|
||||||
|
ButtonAspect ccSelectProvider{this};
|
||||||
|
|
||||||
|
Utils::StringAspect ccModel{this};
|
||||||
|
ButtonAspect ccSelectModel{this};
|
||||||
|
|
||||||
|
Utils::StringAspect ccTemplate{this};
|
||||||
|
ButtonAspect ccSelectTemplate{this};
|
||||||
|
|
||||||
|
Utils::StringAspect ccUrl{this};
|
||||||
|
ButtonAspect ccSetUrl{this};
|
||||||
|
|
||||||
|
Utils::StringAspect ccStatus{this};
|
||||||
|
ButtonAspect ccTest{this};
|
||||||
|
|
||||||
|
// chat assistant settings
|
||||||
|
Utils::StringAspect caProvider{this};
|
||||||
|
ButtonAspect caSelectProvider{this};
|
||||||
|
|
||||||
|
Utils::StringAspect caModel{this};
|
||||||
|
ButtonAspect caSelectModel{this};
|
||||||
|
|
||||||
|
Utils::StringAspect caTemplate{this};
|
||||||
|
ButtonAspect caSelectTemplate{this};
|
||||||
|
|
||||||
|
Utils::StringAspect caUrl{this};
|
||||||
|
ButtonAspect caSetUrl{this};
|
||||||
|
|
||||||
|
Utils::StringAspect caStatus{this};
|
||||||
|
ButtonAspect caTest{this};
|
||||||
|
|
||||||
|
void showSelectionDialog(const QStringList &data,
|
||||||
|
Utils::StringAspect &aspect,
|
||||||
|
const QString &title = {},
|
||||||
|
const QString &text = {});
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupConnections();
|
||||||
|
void resetPageToDefaults();
|
||||||
|
};
|
||||||
|
|
||||||
|
GeneralSettings &generalSettings();
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
109
settings/SettingsConstants.hpp
Normal file
109
settings/SettingsConstants.hpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* 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";
|
||||||
|
|
||||||
|
// new settings
|
||||||
|
const char CC_PROVIDER[] = "QodeAssist.ccProvider";
|
||||||
|
const char CC_MODEL[] = "QodeAssist.ccModel";
|
||||||
|
const char CC_MODEL_HISTORY[] = "QodeAssist.ccModelHistory";
|
||||||
|
const char CC_TEMPLATE[] = "QodeAssist.ccTemplate";
|
||||||
|
const char CC_URL[] = "QodeAssist.ccUrl";
|
||||||
|
const char CC_URL_HISTORY[] = "QodeAssist.ccUrlHistory";
|
||||||
|
|
||||||
|
const char CA_PROVIDER[] = "QodeAssist.caProvider";
|
||||||
|
const char CA_MODEL[] = "QodeAssist.caModel";
|
||||||
|
const char CA_MODEL_HISTORY[] = "QodeAssist.caModelHistory";
|
||||||
|
const char CA_TEMPLATE[] = "QodeAssist.caTemplate";
|
||||||
|
const char CA_URL[] = "QodeAssist.caUrl";
|
||||||
|
const char CA_URL_HISTORY[] = "QodeAssist.caUrlHistory";
|
||||||
|
|
||||||
|
// settings
|
||||||
|
const char ENABLE_QODE_ASSIST[] = "QodeAssist.enableQodeAssist";
|
||||||
|
const char CC_AUTO_COMPLETION[] = "QodeAssist.ccAutoCompletion";
|
||||||
|
const char ENABLE_LOGGING[] = "QodeAssist.enableLogging";
|
||||||
|
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 CC_MULTILINE_COMPLETION[] = "QodeAssist.ccMultilineCompletion";
|
||||||
|
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
|
||||||
|
const char CA_TOKENS_THRESHOLD[] = "QodeAssist.caTokensThreshold";
|
||||||
|
const char CA_SHARING_CURRENT_FILE[] = "QodeAssist.caSharingCurrentFile";
|
||||||
|
|
||||||
|
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
|
||||||
|
const char QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID[] = "QodeAssist.1GeneralSettingsPageId";
|
||||||
|
const char QODE_ASSIST_CODE_COMPLETION_SETTINGS_PAGE_ID[]
|
||||||
|
= "QodeAssist.2CodeCompletionSettingsPageId";
|
||||||
|
const char QODE_ASSIST_CHAT_ASSISTANT_SETTINGS_PAGE_ID[]
|
||||||
|
= "QodeAssist.3ChatAssistantSettingsPageId";
|
||||||
|
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 CC_READ_FULL_FILE[] = "QodeAssist.ccReadFullFile";
|
||||||
|
const char CC_READ_STRINGS_BEFORE_CURSOR[] = "QodeAssist.ccReadStringsBeforeCursor";
|
||||||
|
const char CC_READ_STRINGS_AFTER_CURSOR[] = "QodeAssist.ccReadStringsAfterCursor";
|
||||||
|
const char CC_USE_SYSTEM_PROMPT[] = "QodeAssist.ccUseSystemPrompt";
|
||||||
|
const char CC_USE_FILE_PATH_IN_CONTEXT[] = "QodeAssist.ccUseFilePathInContext";
|
||||||
|
const char CC_SYSTEM_PROMPT[] = "QodeAssist.ccSystemPrompt";
|
||||||
|
const char CC_USE_PROJECT_CHANGES_CACHE[] = "QodeAssist.ccUseProjectChangesCache";
|
||||||
|
const char CC_MAX_CHANGES_CACHE_SIZE[] = "QodeAssist.ccMaxChangesCacheSize";
|
||||||
|
const char CA_USE_SYSTEM_PROMPT[] = "QodeAssist.useChatSystemPrompt";
|
||||||
|
const char CA_SYSTEM_PROMPT[] = "QodeAssist.chatSystemPrompt";
|
||||||
|
|
||||||
|
// preset prompt settings
|
||||||
|
const char CC_TEMPERATURE[] = "QodeAssist.ccTemperature";
|
||||||
|
const char CC_MAX_TOKENS[] = "QodeAssist.ccMaxTokens";
|
||||||
|
const char CC_USE_TOP_P[] = "QodeAssist.ccUseTopP";
|
||||||
|
const char CC_TOP_P[] = "QodeAssist.ccTopP";
|
||||||
|
const char CC_USE_TOP_K[] = "QodeAssist.ccUseTopK";
|
||||||
|
const char CC_TOP_K[] = "QodeAssist.ccTopK";
|
||||||
|
const char CC_USE_PRESENCE_PENALTY[] = "QodeAssist.ccUsePresencePenalty";
|
||||||
|
const char CC_PRESENCE_PENALTY[] = "QodeAssist.ccPresencePenalty";
|
||||||
|
const char CC_USE_FREQUENCY_PENALTY[] = "QodeAssist.fimUseFrequencyPenalty";
|
||||||
|
const char CC_FREQUENCY_PENALTY[] = "QodeAssist.fimFrequencyPenalty";
|
||||||
|
const char CC_OLLAMA_LIVETIME[] = "QodeAssist.fimOllamaLivetime";
|
||||||
|
const char CC_OLLAMA_CONTEXT_WINDOW[] = "QodeAssist.ccOllamaContextWindow";
|
||||||
|
const char CC_API_KEY[] = "QodeAssist.apiKey";
|
||||||
|
const char CA_TEMPERATURE[] = "QodeAssist.chatTemperature";
|
||||||
|
const char CA_MAX_TOKENS[] = "QodeAssist.chatMaxTokens";
|
||||||
|
const char CA_USE_TOP_P[] = "QodeAssist.chatUseTopP";
|
||||||
|
const char CA_TOP_P[] = "QodeAssist.chatTopP";
|
||||||
|
const char CA_USE_TOP_K[] = "QodeAssist.chatUseTopK";
|
||||||
|
const char CA_TOP_K[] = "QodeAssist.chatTopK";
|
||||||
|
const char CA_USE_PRESENCE_PENALTY[] = "QodeAssist.chatUsePresencePenalty";
|
||||||
|
const char CA_PRESENCE_PENALTY[] = "QodeAssist.chatPresencePenalty";
|
||||||
|
const char CA_USE_FREQUENCY_PENALTY[] = "QodeAssist.chatUseFrequencyPenalty";
|
||||||
|
const char CA_FREQUENCY_PENALTY[] = "QodeAssist.chatFrequencyPenalty";
|
||||||
|
const char CA_OLLAMA_LIVETIME[] = "QodeAssist.chatOllamaLivetime";
|
||||||
|
const char CA_OLLAMA_CONTEXT_WINDOW[] = "QodeAssist.caOllamaContextWindow";
|
||||||
|
const char CA_API_KEY[] = "QodeAssist.chatApiKey";
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Constants
|
||||||
52
settings/SettingsTr.hpp
Normal file
52
settings/SettingsTr.hpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
namespace TrConstants {
|
||||||
|
inline const char *ENABLE_QODE_ASSIST = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Enable Qode Assist");
|
||||||
|
inline const char *GENERAL = QT_TRANSLATE_NOOP("QtC::QodeAssist", "General");
|
||||||
|
inline const char *RESET_TO_DEFAULTS = QT_TRANSLATE_NOOP("QtC::QodeAssist",
|
||||||
|
"Reset Page to Defaults");
|
||||||
|
inline const char *SELECT = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Select...");
|
||||||
|
inline const char *PROVIDER = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Provider:");
|
||||||
|
inline const char *MODEL = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Model:");
|
||||||
|
inline const char *TEMPLATE = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Template:");
|
||||||
|
inline const char *URL = QT_TRANSLATE_NOOP("QtC::QodeAssist", "URL:");
|
||||||
|
inline const char *STATUS = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Status:");
|
||||||
|
inline const char *TEST = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Test");
|
||||||
|
inline const char *ENABLE_LOG = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Enable Logging");
|
||||||
|
inline const char *CODE_COMPLETION = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Code Completion");
|
||||||
|
inline const char *CHAT_ASSISTANT = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Chat Assistant");
|
||||||
|
inline const char *RESET_SETTINGS = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Reset Settings");
|
||||||
|
inline const char *CONFIRMATION
|
||||||
|
= QT_TRANSLATE_NOOP("QtC::QodeAssist",
|
||||||
|
"Are you sure you want to reset all settings to default values?");
|
||||||
|
} // namespace TrConstants
|
||||||
|
|
||||||
|
struct Tr
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(QtC::QodeAssist)
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
81
settings/SettingsUtils.hpp
Normal file
81
settings/SettingsUtils.hpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QtCore/qtimer.h>
|
||||||
|
#include <utils/aspects.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
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>
|
||||||
|
void resetAspect(AspectType &aspect)
|
||||||
|
{
|
||||||
|
aspect.setVolatileValue(aspect.defaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void initStringAspect(Utils::StringAspect &aspect,
|
||||||
|
const Utils::Key &settingsKey,
|
||||||
|
const QString &labelText,
|
||||||
|
const QString &defaultValue)
|
||||||
|
{
|
||||||
|
aspect.setSettingsKey(settingsKey);
|
||||||
|
aspect.setLabelText(labelText);
|
||||||
|
aspect.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
aspect.setDefaultValue(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
55
templates/CodeLlamaChat.hpp
Normal file
55
templates/CodeLlamaChat.hpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtCore/qjsonarray.h>
|
||||||
|
#include "llmcore/PromptTemplate.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Templates {
|
||||||
|
|
||||||
|
class CodeLlamaChat : public LLMCore::PromptTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
|
||||||
|
QString name() const override { return "CodeLlama Chat"; }
|
||||||
|
QString promptTemplate() const override { return "[INST] %1 [/INST]"; }
|
||||||
|
QStringList stopWords() const override { return QStringList() << "[INST]" << "[/INST]"; }
|
||||||
|
|
||||||
|
void prepareRequest(QJsonObject &request, const 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LlamaChat : public CodeLlamaChat
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString name() const override { return "Llama Chat"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Templates
|
||||||
@ -19,25 +19,26 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PromptTemplate.hpp"
|
#include "llmcore/PromptTemplate.hpp"
|
||||||
|
|
||||||
namespace QodeAssist::Templates {
|
namespace QodeAssist::Templates {
|
||||||
|
|
||||||
class CodeLLamaTemplate : public PromptTemplate
|
class CodeLlamaFim : public LLMCore::PromptTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QString name() const override { return "CodeLlama"; }
|
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
|
||||||
QString promptTemplate() const override { return "<PRE> %1 <SUF>%2 <MID>"; }
|
QString name() const override { return "CodeLlama FIM"; }
|
||||||
|
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,
|
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
|
||||||
const QString &prefix,
|
|
||||||
const QString &suffix) const override
|
|
||||||
{
|
{
|
||||||
QString formattedPrompt = promptTemplate().arg(prefix, suffix);
|
QString formattedPrompt = promptTemplate().arg(context.systemPrompt,
|
||||||
|
context.prefix,
|
||||||
|
context.suffix);
|
||||||
request["prompt"] = formattedPrompt;
|
request["prompt"] = formattedPrompt;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
91
templates/CustomFimTemplate.hpp
Normal file
91
templates/CustomFimTemplate.hpp
Normal 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "llmcore/PromptTemplate.hpp"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "logger/Logger.hpp"
|
||||||
|
#include "settings/CustomPromptSettings.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Templates {
|
||||||
|
|
||||||
|
class CustomTemplate : public LLMCore::PromptTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
|
||||||
|
QString name() const override { return "Custom FIM Template"; }
|
||||||
|
QString promptTemplate() const override
|
||||||
|
{
|
||||||
|
return Settings::customPromptSettings().customJsonTemplate();
|
||||||
|
}
|
||||||
|
QStringList stopWords() const override { return QStringList(); }
|
||||||
|
|
||||||
|
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
|
||||||
|
{
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(promptTemplate().toUtf8());
|
||||||
|
if (doc.isNull() || !doc.isObject()) {
|
||||||
|
LOG_MESSAGE(QString("Invalid JSON template in settings"));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject templateObj = doc.object();
|
||||||
|
QJsonObject processedObj = processJsonTemplate(templateObj, context);
|
||||||
|
|
||||||
|
for (auto it = processedObj.begin(); it != processedObj.end(); ++it) {
|
||||||
|
request[it.key()] = it.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QJsonValue processJsonValue(const QJsonValue &value, const LLMCore::ContextData &context) const
|
||||||
|
{
|
||||||
|
if (value.isString()) {
|
||||||
|
QString str = value.toString();
|
||||||
|
str.replace("{{QODE_INSTRUCTIONS}}", context.systemPrompt);
|
||||||
|
str.replace("{{QODE_PREFIX}}", context.prefix);
|
||||||
|
str.replace("{{QODE_SUFFIX}}", context.suffix);
|
||||||
|
return str;
|
||||||
|
} else if (value.isObject()) {
|
||||||
|
return processJsonTemplate(value.toObject(), context);
|
||||||
|
} else if (value.isArray()) {
|
||||||
|
QJsonArray newArray;
|
||||||
|
for (const QJsonValue &arrayValue : value.toArray()) {
|
||||||
|
newArray.append(processJsonValue(arrayValue, context));
|
||||||
|
}
|
||||||
|
return newArray;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject processJsonTemplate(const QJsonObject &templateObj,
|
||||||
|
const LLMCore::ContextData &context) const
|
||||||
|
{
|
||||||
|
QJsonObject result;
|
||||||
|
for (auto it = templateObj.begin(); it != templateObj.end(); ++it) {
|
||||||
|
result[it.key()] = processJsonValue(it.value(), context);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace QodeAssist::Templates
|
||||||
54
templates/DeepSeekCoderChat.hpp
Normal file
54
templates/DeepSeekCoderChat.hpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include "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
|
||||||
45
templates/DeepSeekCoderFim.hpp
Normal file
45
templates/DeepSeekCoderFim.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 "llmcore/PromptTemplate.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Templates {
|
||||||
|
|
||||||
|
class DeepSeekCoderFim : public LLMCore::PromptTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
|
||||||
|
QString name() const override { return "DeepSeekCoder FIM"; }
|
||||||
|
QString promptTemplate() const override
|
||||||
|
{
|
||||||
|
return "%1<|fim▁begin|>%2<|fim▁hole|>%3<|fim▁end|>";
|
||||||
|
}
|
||||||
|
QStringList stopWords() const override { return QStringList(); }
|
||||||
|
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
|
||||||
|
{
|
||||||
|
QString formattedPrompt = promptTemplate().arg(context.systemPrompt,
|
||||||
|
context.prefix,
|
||||||
|
context.suffix);
|
||||||
|
request["prompt"] = formattedPrompt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Templates
|
||||||
71
templates/Qwen.hpp
Normal file
71
templates/Qwen.hpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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 QwenChat : public LLMCore::PromptTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString name() const override { return "Qwen 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class QwenFim : public LLMCore::PromptTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString name() const override { return "Qwen FIM"; }
|
||||||
|
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
|
||||||
|
QString promptTemplate() const override
|
||||||
|
{
|
||||||
|
return "<|fim_prefix|>%1<|fim_suffix|>%2<|fim_middle|>";
|
||||||
|
}
|
||||||
|
QStringList stopWords() const override { return QStringList() << "<|endoftext|>" << "<|EOT|>"; }
|
||||||
|
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
|
||||||
|
{
|
||||||
|
QString formattedPrompt = promptTemplate().arg(context.prefix, context.suffix);
|
||||||
|
request["prompt"] = formattedPrompt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Templates
|
||||||
@ -19,25 +19,26 @@
|
|||||||
|
|
||||||
#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:
|
||||||
QString name() const override { return "StarCoder2"; }
|
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
|
||||||
QString promptTemplate() const override { return "<fim_prefix>%1<fim_suffix>%2<fim_middle>"; }
|
QString name() const override { return "StarCoder2 FIM"; }
|
||||||
|
QString promptTemplate() const override { return "%1<fim_prefix>%2<fim_suffix>%3<fim_middle>"; }
|
||||||
QStringList stopWords() const override
|
QStringList stopWords() const override
|
||||||
{
|
{
|
||||||
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,
|
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
|
||||||
const QString &prefix,
|
|
||||||
const QString &suffix) const override
|
|
||||||
{
|
{
|
||||||
QString formattedPrompt = promptTemplate().arg(prefix, suffix);
|
QString formattedPrompt = promptTemplate().arg(context.systemPrompt,
|
||||||
|
context.prefix,
|
||||||
|
context.suffix);
|
||||||
request["prompt"] = formattedPrompt;
|
request["prompt"] = formattedPrompt;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
51
templates/StarCoderChat.hpp
Normal file
51
templates/StarCoderChat.hpp
Normal 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 <QJsonArray>
|
||||||
|
#include "llmcore/PromptTemplate.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Templates {
|
||||||
|
|
||||||
|
class StarCoderChat : public LLMCore::PromptTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString name() const override { return "StarCoder 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() << "###"
|
||||||
|
<< "<|endoftext|>" << "<file_sep>";
|
||||||
|
}
|
||||||
|
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
|
||||||
48
utils/CounterTooltip.cpp
Normal file
48
utils/CounterTooltip.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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 "CounterTooltip.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
CounterTooltip::CounterTooltip(int count)
|
||||||
|
: m_count(count)
|
||||||
|
{
|
||||||
|
m_label = new QLabel(this);
|
||||||
|
addWidget(m_label);
|
||||||
|
updateLabel();
|
||||||
|
|
||||||
|
m_timer = new QTimer(this);
|
||||||
|
m_timer->setSingleShot(true);
|
||||||
|
m_timer->setInterval(2000);
|
||||||
|
|
||||||
|
connect(m_timer, &QTimer::timeout, this, [this] { emit finished(m_count); });
|
||||||
|
|
||||||
|
m_timer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
CounterTooltip::~CounterTooltip() {}
|
||||||
|
|
||||||
|
void CounterTooltip::updateLabel()
|
||||||
|
{
|
||||||
|
const auto hotkey = QKeySequence(QKeySequence::MoveToNextWord).toString();
|
||||||
|
m_label->setText(QString("Insert Next %1 line(s) (%2)").arg(m_count).arg(hotkey));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist
|
||||||
@ -1,13 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2023 The Qt Company Ltd.
|
|
||||||
* Copyright (C) 2024 Petr Mironychev
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
*
|
*
|
||||||
* This file is part of Qode Assist.
|
* This file is part of QodeAssist.
|
||||||
*
|
*
|
||||||
* The Qt Company portions:
|
|
||||||
* SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
||||||
*
|
|
||||||
* Petr Mironychev portions:
|
|
||||||
* QodeAssist is free software: you can redistribute it and/or modify
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -24,24 +19,30 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QTextBlock>
|
#include <QLabel>
|
||||||
#include <texteditor/basehoverhandler.h>
|
#include <QTimer>
|
||||||
|
#include <QToolBar>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
class QodeAssistHoverHandler : public TextEditor::BaseHoverHandler
|
class CounterTooltip : public QToolBar
|
||||||
{
|
{
|
||||||
public:
|
Q_OBJECT
|
||||||
QodeAssistHoverHandler() = default;
|
|
||||||
|
|
||||||
protected:
|
public:
|
||||||
void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
CounterTooltip(int count);
|
||||||
int pos,
|
~CounterTooltip();
|
||||||
ReportPriority report) final;
|
|
||||||
void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) final;
|
signals:
|
||||||
|
void finished(int count);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTextBlock m_block;
|
void updateLabel();
|
||||||
|
|
||||||
|
QLabel *m_label;
|
||||||
|
QTimer *m_timer;
|
||||||
|
int m_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
Reference in New Issue
Block a user