diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f92826..fe51fc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,4 +55,5 @@ add_qtc_plugin(QodeAssist settings/ContextSettings.hpp settings/ContextSettings.cpp settings/CustomPromptSettings.hpp settings/CustomPromptSettings.cpp settings/PresetPromptsSettings.hpp settings/PresetPromptsSettings.cpp + settings/SettingsUtils.hpp ) diff --git a/LLMClientInterface.cpp b/LLMClientInterface.cpp index f7d314d..ee434d7 100644 --- a/LLMClientInterface.cpp +++ b/LLMClientInterface.cpp @@ -30,6 +30,7 @@ #include "PromptTemplateManager.hpp" #include "QodeAssistSettings.hpp" #include "QodeAssistUtils.hpp" +#include "settings/GeneralSettings.hpp" namespace QodeAssist { @@ -289,7 +290,8 @@ ContextData LLMClientInterface::prepareContext(const QJsonObject &request, void LLMClientInterface::updateProvider() { - m_serverUrl = QUrl(QString("%1%2").arg(settings().url(), settings().endPoint())); + m_serverUrl = QUrl(QString("%1%2").arg(Settings::generalSettings().url(), + Settings::generalSettings().endPoint())); } void LLMClientInterface::sendCompletionToClient(const QString &completion, @@ -330,7 +332,8 @@ void LLMClientInterface::sendCompletionToClient(const QString &completion, void LLMClientInterface::sendLLMRequest(const QJsonObject &request, const ContextData &prompt) { - QJsonObject providerRequest = {{"model", settings().modelName.value()}, {"stream", true}}; + QJsonObject providerRequest = {{"model", Settings::generalSettings().modelName.value()}, + {"stream", true}}; auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate(); currentTemplate->prepareRequest(providerRequest, prompt); diff --git a/QodeAssistClient.cpp b/QodeAssistClient.cpp index 296c519..cfaf367 100644 --- a/QodeAssistClient.cpp +++ b/QodeAssistClient.cpp @@ -32,6 +32,7 @@ #include "LLMClientInterface.hpp" #include "LLMSuggestion.hpp" #include "QodeAssistSettings.hpp" +#include "settings/GeneralSettings.hpp" using namespace LanguageServerProtocol; using namespace TextEditor; @@ -70,7 +71,7 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document) this, [this, document](int position, int charsRemoved, int charsAdded) { Q_UNUSED(charsRemoved) - if (!settings().enableAutoComplete()) + if (!Settings::generalSettings().enableAutoComplete()) return; auto project = ProjectManager::projectForFile(document->filePath()); @@ -204,7 +205,7 @@ void QodeAssistClient::cancelRunningRequest(TextEditor::TextEditorWidget *editor bool QodeAssistClient::isEnabled(ProjectExplorer::Project *project) const { - return settings().enableQodeAssist(); + return Settings::generalSettings().enableQodeAssist(); } void QodeAssistClient::setupConnections() diff --git a/QodeAssistSettings.cpp b/QodeAssistSettings.cpp index bcf7e28..a69f632 100644 --- a/QodeAssistSettings.cpp +++ b/QodeAssistSettings.cpp @@ -45,41 +45,11 @@ 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(0); - - 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); - - 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 Model"); ollamaLivetime.setSettingsKey(Constants::OLLAMA_LIVETIME); ollamaLivetime.setLabelText( @@ -88,11 +58,6 @@ QodeAssistSettings::QodeAssistSettings() ollamaLivetime.setDefaultValue("5m"); ollamaLivetime.setDisplayStyle(Utils::StringAspect::LineEditDisplay); - fimPrompts.setDisplayName(Tr::tr("Fill-In-Middle Prompt")); - fimPrompts.setSettingsKey(Constants::FIM_PROMPTS); - fimPrompts.setDefaultValue(0); - fimPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox); - readFullFile.setSettingsKey(Constants::READ_FULL_FILE); readFullFile.setLabelText(Tr::tr("Read Full File")); readFullFile.setDefaultValue(false); @@ -204,22 +169,6 @@ QodeAssistSettings::QodeAssistSettings() saveCustomTemplateButton.m_buttonText = (Tr::tr("Save Custom Template to JSON")); loadCustomTemplateButton.m_buttonText = (Tr::tr("Load Custom Template from JSON")); - 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()); @@ -229,29 +178,14 @@ QodeAssistSettings::QodeAssistSettings() readStringsAfterCursor.setEnabled(!readFullFile()); readStringsBeforeCursor.setEnabled(!readFullFile()); specificInstractions.setEnabled(useSpecificInstructions()); - PromptTemplateManager::instance().setCurrentTemplate(fimPrompts.stringValue()); - LLMProvidersManager::instance().setCurrentProvider(llmProviders.stringValue()); customJsonTemplate.setVisible(PromptTemplateManager::instance().getCurrentTemplate()->name() == "Custom Template"); - setLoggingEnabled(enableLogging()); - setLayouter([this]() { using namespace Layouting; - return Column{Group{title(Tr::tr("General Settings")), - Form{Column{enableQodeAssist, - enableAutoComplete, - multiLineCompletion, - enableLogging, - Row{Stretch{1}, resetToDefaults}}}}, - Group{title(Tr::tr("LLM Providers")), - Form{Column{llmProviders, Row{url, endPoint}}}}, - Group{title(Tr::tr("LLM Model Settings")), - Form{Column{Row{selectModels, modelName}}}}, - Group{title(Tr::tr("FIM Prompt Settings")), - Form{Column{fimPrompts, - Column{customJsonTemplate, + return Column{Group{title(Tr::tr("FIM Prompt Settings")), + Form{Column{Column{customJsonTemplate, Row{saveCustomTemplateButton, loadCustomTemplateButton, Stretch{1}}}, @@ -279,21 +213,6 @@ QodeAssistSettings::QodeAssistSettings() 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)); - customJsonTemplate.setVisible(fimPrompts.displayForIndex(index) == "Custom Template"); - }); - - connect(&selectModels, &ButtonAspect::clicked, this, [this]() { showModelSelectionDialog(); }); connect(&useTopP, &Utils::BoolAspect::volatileValueChanged, this, [this]() { topP.setEnabled(useTopP.volatileValue()); }); @@ -314,9 +233,7 @@ void QodeAssistSettings::setupConnections() &ButtonAspect::clicked, this, &QodeAssistSettings::resetSettingsToDefaults); - connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() { - setLoggingEnabled(enableLogging.volatileValue()); - }); + connect(&useSpecificInstructions, &Utils::BoolAspect::volatileValueChanged, this, [this]() { specificInstractions.setEnabled(useSpecificInstructions.volatileValue()); }); @@ -331,18 +248,6 @@ void QodeAssistSettings::setupConnections() &QodeAssistSettings::loadCustomTemplate); } -void QodeAssistSettings::updateProviderSettings() -{ - auto *provider = LLMProvidersManager::instance().getCurrentProvider(); - - if (provider) { - logMessage(QString("currentProvider %1").arg(provider->name())); - url.setValue(provider->url()); - endPoint.setValue(provider->completionEndpoint()); - ollamaLivetime.setEnabled(provider->name() == "Ollama"); - } -} - QStringList QodeAssistSettings::getInstalledModels() { auto *provider = LLMProvidersManager::instance().getCurrentProvider(); @@ -353,25 +258,6 @@ QStringList QodeAssistSettings::getInstalledModels() 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(); - } -} - void QodeAssistSettings::resetSettingsToDefaults() { QMessageBox::StandardButton reply; @@ -382,13 +268,13 @@ void QodeAssistSettings::resetSettingsToDefaults() QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { - resetAspect(enableQodeAssist); - resetAspect(enableAutoComplete); - resetAspect(llmProviders); - resetAspect(url); - resetAspect(endPoint); - resetAspect(modelName); - resetAspect(fimPrompts); + // resetAspect(enableQodeAssist); + // resetAspect(enableAutoComplete); + // resetAspect(llmProviders); + // resetAspect(url); + // resetAspect(endPoint); + // resetAspect(modelName); + // resetAspect(fimPrompts); resetAspect(temperature); resetAspect(maxTokens); resetAspect(readFullFile); @@ -404,7 +290,7 @@ void QodeAssistSettings::resetSettingsToDefaults() resetAspect(useFrequencyPenalty); resetAspect(frequencyPenalty); resetAspect(startSuggestionTimer); - resetAspect(enableLogging); + // resetAspect(enableLogging); resetAspect(ollamaLivetime); resetAspect(specificInstractions); resetAspect(multiLineCompletion); @@ -412,10 +298,9 @@ void QodeAssistSettings::resetSettingsToDefaults() resetAspect(useSpecificInstructions); resetAspect(customJsonTemplate); - fimPrompts.setStringValue("StarCoder2"); - llmProviders.setStringValue("Ollama"); + // fimPrompts.setStringValue("StarCoder2"); + // llmProviders.setStringValue("Ollama"); - updateProviderSettings(); apply(); QMessageBox::information(Core::ICore::dialogParent(), diff --git a/QodeAssistSettings.hpp b/QodeAssistSettings.hpp index d9802bb..c3962c6 100644 --- a/QodeAssistSettings.hpp +++ b/QodeAssistSettings.hpp @@ -57,18 +57,6 @@ class QodeAssistSettings : public Utils::AspectContainer public: QodeAssistSettings(); - Utils::BoolAspect enableQodeAssist{this}; - Utils::BoolAspect enableAutoComplete{this}; - Utils::BoolAspect enableLogging{this}; - - Utils::SelectionAspect llmProviders{this}; - Utils::StringAspect url{this}; - Utils::StringAspect endPoint{this}; - - Utils::StringAspect modelName{this}; - ButtonAspect selectModels{this}; - - Utils::SelectionAspect fimPrompts{this}; Utils::DoubleAspect temperature{this}; Utils::IntegerAspect maxTokens{this}; @@ -106,9 +94,7 @@ public: private: void setupConnections(); - void updateProviderSettings(); QStringList getInstalledModels(); - void showModelSelectionDialog(); Utils::Environment getEnvironmentWithProviderPaths() const; void resetSettingsToDefaults(); void saveCustomTemplate(); diff --git a/settings/GeneralSettings.cpp b/settings/GeneralSettings.cpp index 335a59c..b4ac424 100644 --- a/settings/GeneralSettings.cpp +++ b/settings/GeneralSettings.cpp @@ -19,10 +19,16 @@ #include "GeneralSettings.hpp" +#include +#include #include +#include #include +#include "LLMProvidersManager.hpp" +#include "PromptTemplateManager.hpp" #include "QodeAssistConstants.hpp" +#include "QodeAssistUtils.hpp" #include "QodeAssisttr.h" namespace QodeAssist::Settings { @@ -39,12 +45,174 @@ GeneralSettings::GeneralSettings() setDisplayName(Tr::tr("General")); + 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); + + multiLineCompletion.setSettingsKey(Constants::MULTILINE_COMPLETION); + multiLineCompletion.setDefaultValue(true); + multiLineCompletion.setLabelText(Tr::tr("Enable Multiline Completion")); + + llmProviders.setSettingsKey(Constants::LLM_PROVIDERS); + llmProviders.setDisplayName(Tr::tr("LLM Providers:")); + llmProviders.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox); + llmProviders.setDefaultValue(0); + + 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); + + modelName.setSettingsKey(Constants::MODEL_NAME); + modelName.setLabelText(Tr::tr("LLM Name:")); + modelName.setDisplayStyle(Utils::StringAspect::LineEditDisplay); + + selectModels.m_buttonText = Tr::tr("Select Model"); + + fimPrompts.setDisplayName(Tr::tr("Fill-In-Middle Prompt")); + fimPrompts.setSettingsKey(Constants::FIM_PROMPTS); + fimPrompts.setDefaultValue(0); + fimPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox); + resetToDefaults.m_buttonText = Tr::tr("Reset Page 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(); + + LLMProvidersManager::instance().setCurrentProvider(llmProviders.stringValue()); + PromptTemplateManager::instance().setCurrentTemplate(fimPrompts.stringValue()); + setLoggingEnabled(enableLogging()); + + setupConnections(); + setLayouter([this]() { using namespace Layouting; - return Column{Stretch{1}}; + + auto rootLayout = Column{Row{enableQodeAssist, Stretch{1}, resetToDefaults}, + enableAutoComplete, + multiLineCompletion, + Space{8}, + enableLogging, + Space{8}, + llmProviders, + Row{url, endPoint}, + Space{8}, + Row{selectModels, modelName}, + Space{8}, + fimPrompts, + Stretch{1}}; + return rootLayout; }); } +void GeneralSettings::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(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() { + setLoggingEnabled(enableLogging.volatileValue()); + }); + connect(&resetToDefaults, &ButtonAspect::clicked, this, &GeneralSettings::resetPageToDefaults); +} + +void GeneralSettings::updateProviderSettings() +{ + const auto provider = LLMProvidersManager::instance().getCurrentProvider(); + + if (provider) { + url.setValue(provider->url()); + endPoint.setValue(provider->completionEndpoint()); + } +} + +void GeneralSettings::showModelSelectionDialog() +{ + auto *provider = LLMProvidersManager::instance().getCurrentProvider(); + Utils::Environment env = Utils::Environment::systemEnvironment(); + + if (provider) { + QStringList models = provider->getInstalledModels(env); + 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(); + } + } +} + +void GeneralSettings::resetPageToDefaults() +{ + 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(endPoint); + resetAspect(modelName); + resetAspect(fimPrompts); + resetAspect(enableLogging); + } + + fimPrompts.setStringValue("StarCoder2"); + llmProviders.setStringValue("Ollama"); + + apply(); + + QMessageBox::information(Core::ICore::dialogParent(), + Tr::tr("Settings Reset"), + Tr::tr("All settings have been reset to their default values.")); +} + class GeneralSettingsPage : public Core::IOptionsPage { public: diff --git a/settings/GeneralSettings.hpp b/settings/GeneralSettings.hpp index be876c2..3b57d4a 100644 --- a/settings/GeneralSettings.hpp +++ b/settings/GeneralSettings.hpp @@ -21,12 +21,34 @@ #include +#include "settings/SettingsUtils.hpp" + namespace QodeAssist::Settings { class GeneralSettings : public Utils::AspectContainer { public: GeneralSettings(); + + Utils::BoolAspect enableQodeAssist{this}; + Utils::BoolAspect enableAutoComplete{this}; + Utils::BoolAspect multiLineCompletion{this}; + Utils::BoolAspect enableLogging{this}; + + Utils::SelectionAspect llmProviders{this}; + Utils::StringAspect url{this}; + Utils::StringAspect endPoint{this}; + + Utils::StringAspect modelName{this}; + ButtonAspect selectModels{this}; + Utils::SelectionAspect fimPrompts{this}; + ButtonAspect resetToDefaults{this}; + +private: + void setupConnections(); + void updateProviderSettings(); + void showModelSelectionDialog(); + void resetPageToDefaults(); }; GeneralSettings &generalSettings(); diff --git a/settings/SettingsUtils.hpp b/settings/SettingsUtils.hpp new file mode 100644 index 0000000..2844a0d --- /dev/null +++ b/settings/SettingsUtils.hpp @@ -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 . + */ + +#pragma once + +#include +#include +#include + +namespace QodeAssist::Settings { + +template +void resetAspect(AspectType &aspect) +{ + aspect.setValue(aspect.defaultValue()); +} + +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(); +}; + +} // namespace QodeAssist::Settings