Version 0.1.0

## What's New
- Reworked plugin settings to multipage settings
- Added creating, saving and loading custom templates in json format
- Added smart trigger for call suggestion after input by default 2 characters
This commit is contained in:
Petr Mironychev
2024-09-08 03:03:07 +02:00
committed by GitHub
27 changed files with 1301 additions and 630 deletions

View File

@ -34,14 +34,13 @@ add_qtc_plugin(QodeAssist
templates/PromptTemplate.hpp templates/PromptTemplate.hpp
templates/CodeLLamaTemplate.hpp templates/CodeLLamaTemplate.hpp
templates/StarCoder2Template.hpp templates/StarCoder2Template.hpp
templates/CodeQwenChat.hpp
templates/DeepSeekCoderV2.hpp templates/DeepSeekCoderV2.hpp
templates/CustomTemplate.hpp
providers/LLMProvider.hpp providers/LLMProvider.hpp
providers/OllamaProvider.hpp providers/OllamaProvider.cpp providers/OllamaProvider.hpp providers/OllamaProvider.cpp
providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp
providers/OpenAICompatProvider.hpp providers/OpenAICompatProvider.cpp providers/OpenAICompatProvider.hpp providers/OpenAICompatProvider.cpp
LLMProvidersManager.hpp LLMProvidersManager.cpp LLMProvidersManager.hpp LLMProvidersManager.cpp
QodeAssistSettings.hpp QodeAssistSettings.cpp
QodeAssist.qrc QodeAssist.qrc
LSPCompletion.hpp LSPCompletion.hpp
LLMSuggestion.hpp LLMSuggestion.cpp LLMSuggestion.hpp LLMSuggestion.cpp
@ -50,4 +49,9 @@ add_qtc_plugin(QodeAssist
DocumentContextReader.hpp DocumentContextReader.cpp DocumentContextReader.hpp DocumentContextReader.cpp
QodeAssistData.hpp QodeAssistData.hpp
utils/CounterTooltip.hpp utils/CounterTooltip.cpp utils/CounterTooltip.hpp utils/CounterTooltip.cpp
settings/GeneralSettings.hpp settings/GeneralSettings.cpp
settings/ContextSettings.hpp settings/ContextSettings.cpp
settings/CustomPromptSettings.hpp settings/CustomPromptSettings.cpp
settings/PresetPromptsSettings.hpp settings/PresetPromptsSettings.cpp
settings/SettingsUtils.hpp
) )

View File

@ -23,7 +23,7 @@
#include <QTextBlock> #include <QTextBlock>
#include <languageserverprotocol/lsptypes.h> #include <languageserverprotocol/lsptypes.h>
#include "QodeAssistSettings.hpp" #include "settings/ContextSettings.hpp"
const QRegularExpression &getYearRegex() const QRegularExpression &getYearRegex()
{ {
@ -136,7 +136,7 @@ QString DocumentContextReader::getLanguageAndFileInfo() const
QString DocumentContextReader::getSpecificInstructions() const QString DocumentContextReader::getSpecificInstructions() const
{ {
QString specificInstruction = settings().specificInstractions().arg( QString specificInstruction = Settings::contextSettings().specificInstractions().arg(
LanguageServerProtocol::TextDocumentItem::mimeTypeToLanguageId(m_textDocument->mimeType())); LanguageServerProtocol::TextDocumentItem::mimeTypeToLanguageId(m_textDocument->mimeType()));
return QString("%1").arg(specificInstruction); return QString("%1").arg(specificInstruction);
} }

View File

@ -28,8 +28,9 @@
#include "DocumentContextReader.hpp" #include "DocumentContextReader.hpp"
#include "LLMProvidersManager.hpp" #include "LLMProvidersManager.hpp"
#include "PromptTemplateManager.hpp" #include "PromptTemplateManager.hpp"
#include "QodeAssistSettings.hpp"
#include "QodeAssistUtils.hpp" #include "QodeAssistUtils.hpp"
#include "settings/ContextSettings.hpp"
#include "settings/GeneralSettings.hpp"
namespace QodeAssist { namespace QodeAssist {
@ -130,12 +131,13 @@ QString LLMClientInterface::сontextBefore(TextEditor::TextEditorWidget *widget,
return QString(); return QString();
QString contextBefore; QString contextBefore;
if (settings().readFullFile()) { if (Settings::contextSettings().readFullFile()) {
contextBefore = reader.readWholeFileBefore(lineNumber, cursorPosition); contextBefore = reader.readWholeFileBefore(lineNumber, cursorPosition);
} else { } else {
contextBefore = reader.getContextBefore(lineNumber, contextBefore
= reader.getContextBefore(lineNumber,
cursorPosition, cursorPosition,
settings().readStringsBeforeCursor()); Settings::contextSettings().readStringsBeforeCursor());
} }
return contextBefore; return contextBefore;
@ -153,12 +155,12 @@ QString LLMClientInterface::сontextAfter(TextEditor::TextEditorWidget *widget,
return QString(); return QString();
QString contextAfter; QString contextAfter;
if (settings().readFullFile()) { if (Settings::contextSettings().readFullFile()) {
contextAfter = reader.readWholeFileAfter(lineNumber, cursorPosition); contextAfter = reader.readWholeFileAfter(lineNumber, cursorPosition);
} else { } else {
contextAfter = reader.getContextAfter(lineNumber, contextAfter = reader.getContextAfter(lineNumber,
cursorPosition, cursorPosition,
settings().readStringsAfterCursor()); Settings::contextSettings().readStringsAfterCursor());
} }
return contextAfter; return contextAfter;
@ -227,7 +229,7 @@ void LLMClientInterface::handleLLMResponse(QNetworkReply *reply, const QJsonObje
QJsonObject position = request["params"].toObject()["doc"].toObject()["position"].toObject(); QJsonObject position = request["params"].toObject()["doc"].toObject()["position"].toObject();
if (!settings().multiLineCompletion() if (!Settings::generalSettings().multiLineCompletion()
&& processSingleLineCompletion(reply, request, accumulatedResponse)) { && processSingleLineCompletion(reply, request, accumulatedResponse)) {
return; return;
} }
@ -244,14 +246,14 @@ void LLMClientInterface::handleLLMResponse(QNetworkReply *reply, const QJsonObje
} }
void LLMClientInterface::handleCompletion(const QJsonObject &request, void LLMClientInterface::handleCompletion(const QJsonObject &request,
const QString &accumulatedCompletion) const QStringView &accumulatedCompletion)
{ {
auto updatedContext = prepareContext(request, accumulatedCompletion); auto updatedContext = prepareContext(request, accumulatedCompletion);
sendLLMRequest(request, updatedContext); sendLLMRequest(request, updatedContext);
} }
ContextData LLMClientInterface::prepareContext(const QJsonObject &request, ContextData LLMClientInterface::prepareContext(const QJsonObject &request,
const QString &accumulatedCompletion) const QStringView &accumulatedCompletion)
{ {
QJsonObject params = request["params"].toObject(); QJsonObject params = request["params"].toObject();
QJsonObject doc = params["doc"].toObject(); QJsonObject doc = params["doc"].toObject();
@ -264,7 +266,7 @@ ContextData LLMClientInterface::prepareContext(const QJsonObject &request,
if (!textDocument) { if (!textDocument) {
logMessage("Error: Document is not available for" + filePath.toString()); logMessage("Error: Document is not available for" + filePath.toString());
return {"", ""}; return ContextData{};
} }
int cursorPosition = position["character"].toInt(); int cursorPosition = position["character"].toInt();
@ -277,21 +279,20 @@ ContextData LLMClientInterface::prepareContext(const QJsonObject &request,
QString contextBefore = сontextBefore(widget, lineNumber, cursorPosition); QString contextBefore = сontextBefore(widget, lineNumber, cursorPosition);
QString contextAfter = сontextAfter(widget, lineNumber, cursorPosition); QString contextAfter = сontextAfter(widget, lineNumber, cursorPosition);
QString instructions = QString("%1%2").arg(settings().useSpecificInstructions() QString instructions = QString("%1%2").arg(Settings::contextSettings().useSpecificInstructions()
? reader.getSpecificInstructions() ? reader.getSpecificInstructions()
: QString(), : QString(),
settings().useFilePathInContext() Settings::contextSettings().useFilePathInContext()
? reader.getLanguageAndFileInfo() ? reader.getLanguageAndFileInfo()
: QString()); : QString());
QString updatedContextBefore = contextBefore + accumulatedCompletion; return {QString("%1%2").arg(contextBefore, accumulatedCompletion), contextAfter, instructions};
return {updatedContextBefore, contextAfter, instructions};
} }
void LLMClientInterface::updateProvider() 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, void LLMClientInterface::sendCompletionToClient(const QString &completion,
@ -332,7 +333,8 @@ void LLMClientInterface::sendCompletionToClient(const QString &completion,
void LLMClientInterface::sendLLMRequest(const QJsonObject &request, const ContextData &prompt) 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(); auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
currentTemplate->prepareRequest(providerRequest, prompt); currentTemplate->prepareRequest(providerRequest, prompt);
@ -378,9 +380,9 @@ void LLMClientInterface::sendLLMRequest(const QJsonObject &request, const Contex
}); });
} }
QString LLMClientInterface::removeStopWords(const QString &completion) QString LLMClientInterface::removeStopWords(const QStringView &completion)
{ {
QString filteredCompletion = completion; QString filteredCompletion = completion.toString();
auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate(); auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
QStringList stopWords = currentTemplate->stopWords(); QStringList stopWords = currentTemplate->stopWords();

View File

@ -45,12 +45,12 @@ public:
bool isComplete); bool isComplete);
void handleCompletion(const QJsonObject &request, void handleCompletion(const QJsonObject &request,
const QString &accumulatedCompletion = QString()); const QStringView &accumulatedCompletion = QString());
void sendLLMRequest(const QJsonObject &request, const ContextData &prompt); void sendLLMRequest(const QJsonObject &request, const ContextData &prompt);
void handleLLMResponse(QNetworkReply *reply, const QJsonObject &request); void handleLLMResponse(QNetworkReply *reply, const QJsonObject &request);
ContextData prepareContext(const QJsonObject &request, ContextData prepareContext(const QJsonObject &request,
const QString &accumulatedCompletion = QString{}); const QStringView &accumulatedCompletion = QString{});
void updateProvider(); void updateProvider();
protected: protected:
@ -71,7 +71,7 @@ private:
QString сontextBefore(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition); QString сontextBefore(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition);
QString сontextAfter(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition); QString сontextAfter(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition);
QString removeStopWords(const QString &completion); QString removeStopWords(const QStringView &completion);
QUrl m_serverUrl; QUrl m_serverUrl;
QNetworkAccessManager *m_manager; QNetworkAccessManager *m_manager;

View File

@ -1,6 +1,6 @@
{ {
"Name" : "QodeAssist", "Name" : "QodeAssist",
"Version" : "0.0.8", "Version" : "0.1.0",
"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",

View File

@ -31,7 +31,7 @@
#include "LLMClientInterface.hpp" #include "LLMClientInterface.hpp"
#include "LLMSuggestion.hpp" #include "LLMSuggestion.hpp"
#include "QodeAssistSettings.hpp" #include "settings/GeneralSettings.hpp"
using namespace LanguageServerProtocol; using namespace LanguageServerProtocol;
using namespace TextEditor; using namespace TextEditor;
@ -43,6 +43,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 +52,8 @@ QodeAssistClient::QodeAssistClient()
start(); start();
setupConnections(); setupConnections();
m_typingTimer.start();
} }
QodeAssistClient::~QodeAssistClient() QodeAssistClient::~QodeAssistClient()
@ -70,7 +73,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::generalSettings().enableAutoComplete())
return; return;
auto project = ProjectManager::projectForFile(document->filePath()); auto project = ProjectManager::projectForFile(document->filePath());
@ -86,7 +89,18 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document)
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;
m_recentCharCount += charsAdded;
if (m_typingTimer.elapsed()
> Settings::generalSettings().autoCompletionTypingInterval()) {
m_recentCharCount = charsAdded;
m_typingTimer.restart();
}
if (m_recentCharCount > Settings::generalSettings().autoCompletionCharThreshold()) {
scheduleRequest(widget); scheduleRequest(widget);
}
}); });
} }
@ -130,7 +144,8 @@ 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::generalSettings().autoCompletionCharThreshold())
requestCompletions(editor); requestCompletions(editor);
}); });
connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() { connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() {
@ -144,9 +159,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::generalSettings().startSuggestionTimer());
} }
void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &response, void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &response,
TextEditor::TextEditorWidget *editor) TextEditor::TextEditorWidget *editor)
{ {
@ -204,7 +218,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()

View File

@ -55,6 +55,9 @@ private:
QHash<TextEditor::TextEditorWidget *, QTimer *> m_scheduledRequests; QHash<TextEditor::TextEditorWidget *, QTimer *> m_scheduledRequests;
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

View File

@ -49,6 +49,8 @@ const char USE_FREQUENCY_PENALTY[] = "QodeAssist.useFrequencyPenalty";
const char FREQUENCY_PENALTY[] = "QodeAssist.frequencyPenalty"; const char FREQUENCY_PENALTY[] = "QodeAssist.frequencyPenalty";
const char PROVIDER_PATHS[] = "QodeAssist.providerPaths"; const char PROVIDER_PATHS[] = "QodeAssist.providerPaths";
const char START_SUGGESTION_TIMER[] = "QodeAssist.startSuggestionTimer"; 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 MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
const char OLLAMA_LIVETIME[] = "QodeAssist.ollamaLivetime"; const char OLLAMA_LIVETIME[] = "QodeAssist.ollamaLivetime";
const char SPECIFIC_INSTRUCTIONS[] = "QodeAssist.specificInstractions"; const char SPECIFIC_INSTRUCTIONS[] = "QodeAssist.specificInstractions";
@ -56,8 +58,15 @@ const char MULTILINE_COMPLETION[] = "QodeAssist.multilineCompletion";
const char API_KEY[] = "QodeAssist.apiKey"; const char API_KEY[] = "QodeAssist.apiKey";
const char USE_SPECIFIC_INSTRUCTIONS[] = "QodeAssist.useSpecificInstructions"; const char USE_SPECIFIC_INSTRUCTIONS[] = "QodeAssist.useSpecificInstructions";
const char USE_FILE_PATH_IN_CONTEXT[] = "QodeAssist.useFilePathInContext"; const char USE_FILE_PATH_IN_CONTEXT[] = "QodeAssist.useFilePathInContext";
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions"; const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
const char QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID[] = "QodeAssist.1GeneralSettingsPageId";
const char QODE_ASSIST_CONTEXT_SETTINGS_PAGE_ID[] = "QodeAssist.2ContextSettingsPageId";
const char QODE_ASSIST_PRESET_PROMPTS_SETTINGS_PAGE_ID[]
= "QodeAssist.3PresetPromptsSettingsPageId";
const char QODE_ASSIST_CUSTOM_PROMPT_SETTINGS_PAGE_ID[] = "QodeAssist.4CustomPromptSettingsPageId";
const char QODE_ASSIST_GENERAL_OPTIONS_CATEGORY[] = "QodeAssist.Category"; const char QODE_ASSIST_GENERAL_OPTIONS_CATEGORY[] = "QodeAssist.Category";
const char QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY[] = "Qode Assist"; const char QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY[] = "Qode Assist";

View File

@ -1,398 +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(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(
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(0);
fimPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
readFullFile.setSettingsKey(Constants::READ_FULL_FILE);
readFullFile.setLabelText(Tr::tr("Read Full File"));
readFullFile.setDefaultValue(false);
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(50);
readStringsAfterCursor.setSettingsKey(Constants::READ_STRINGS_AFTER_CURSOR);
readStringsAfterCursor.setLabelText(Tr::tr("Read Strings After Cursor"));
readStringsAfterCursor.setDefaultValue(30);
maxTokens.setSettingsKey(Constants::MAX_TOKENS);
maxTokens.setLabelText(Tr::tr("Max Tokens"));
maxTokens.setRange(-1, 10000);
maxTokens.setDefaultValue(150);
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, 1.0);
useTopK.setSettingsKey(Constants::USE_TOP_K);
useTopK.setDefaultValue(false);
topK.setSettingsKey(Constants::TOP_K);
topK.setLabelText(Tr::tr("top_k"));
topK.setDefaultValue(50);
topK.setRange(1, 1000);
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);
startSuggestionTimer.setSettingsKey(Constants::START_SUGGESTION_TIMER);
startSuggestionTimer.setLabelText(Tr::tr("Start Suggestion Timer:"));
startSuggestionTimer.setRange(10, 10000);
startSuggestionTimer.setDefaultValue(500);
useFilePathInContext.setSettingsKey(Constants::USE_FILE_PATH_IN_CONTEXT);
useFilePathInContext.setDefaultValue(false);
useFilePathInContext.setLabelText(Tr::tr("Use File Path in Context"));
useSpecificInstructions.setSettingsKey(Constants::USE_SPECIFIC_INSTRUCTIONS);
useSpecificInstructions.setDefaultValue(false);
useSpecificInstructions.setLabelText(Tr::tr("Use Specific Instructions"));
specificInstractions.setSettingsKey(Constants::SPECIFIC_INSTRUCTIONS);
specificInstractions.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
specificInstractions.setLabelText(
Tr::tr("Instructions: Please keep %1 for languge name, warning, it shouldn't too big"));
specificInstractions.setDefaultValue(
"You are an expert %1 code completion AI."
"CRITICAL: Please provide minimal the best possible code completion suggestions.\n");
resetToDefaults.m_buttonText = Tr::tr("Reset to Defaults");
multiLineCompletion.setSettingsKey(Constants::MULTILINE_COMPLETION);
multiLineCompletion.setDefaultValue(true);
multiLineCompletion.setLabelText(Tr::tr("Enable Multiline Completion"));
apiKey.setSettingsKey(Constants::API_KEY);
apiKey.setLabelText(Tr::tr("API Key:"));
apiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
apiKey.setPlaceHolderText(Tr::tr("Enter your API key here"));
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());
specificInstractions.setEnabled(useSpecificInstructions());
PromptTemplateManager::instance().setCurrentTemplate(fimPrompts.stringValue());
LLMProvidersManager::instance().setCurrentProvider(llmProviders.stringValue());
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,
readFullFile,
maxFileThreshold,
readStringsBeforeCursor,
readStringsAfterCursor,
ollamaLivetime,
apiKey,
useFilePathInContext,
useSpecificInstructions,
specificInstractions,
temperature,
maxTokens,
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());
});
connect(&useSpecificInstructions, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
specificInstractions.setEnabled(useSpecificInstructions.volatileValue());
});
}
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();
if (provider) {
Utils::Environment env = Utils::Environment::systemEnvironment();
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();
}
}
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(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);
resetAspect(specificInstractions);
resetAspect(multiLineCompletion);
resetAspect(useFilePathInContext);
resetAspect(useSpecificInstructions);
fimPrompts.setStringValue("StarCoder2");
llmProviders.setStringValue("Ollama");
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

View File

@ -1,115 +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 <QPushButton>
#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
namespace QodeAssist {
template<typename AspectType>
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();
};
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};
Utils::BoolAspect readFullFile{this};
Utils::IntegerAspect readStringsBeforeCursor{this};
Utils::IntegerAspect readStringsAfterCursor{this};
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};
Utils::IntegerAspect startSuggestionTimer{this};
Utils::IntegerAspect maxFileThreshold{this};
Utils::StringAspect ollamaLivetime{this};
Utils::StringAspect specificInstractions{this};
Utils::BoolAspect useSpecificInstructions{this};
Utils::BoolAspect useFilePathInContext{this};
Utils::BoolAspect multiLineCompletion{this};
Utils::StringAspect apiKey{this};
ButtonAspect resetToDefaults{this};
private:
void setupConnections();
void updateProviderSettings();
QStringList getInstalledModels();
void showModelSelectionDialog();
Utils::Environment getEnvironmentWithProviderPaths() const;
void resetSettingsToDefaults();
};
QodeAssistSettings &settings();
} // namespace QodeAssist

View File

@ -26,8 +26,8 @@
#include <QNetworkReply> #include <QNetworkReply>
#include "PromptTemplateManager.hpp" #include "PromptTemplateManager.hpp"
#include "QodeAssistSettings.hpp"
#include "QodeAssistUtils.hpp" #include "QodeAssistUtils.hpp"
#include "settings/PresetPromptsSettings.hpp"
namespace QodeAssist::Providers { namespace QodeAssist::Providers {
@ -50,25 +50,27 @@ QString LMStudioProvider::completionEndpoint() const
void LMStudioProvider::prepareRequest(QJsonObject &request) void LMStudioProvider::prepareRequest(QJsonObject &request)
{ {
auto &settings = Settings::presetPromptsSettings();
const auto &currentTemplate = PromptTemplateManager::instance().getCurrentTemplate(); const auto &currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
if (currentTemplate->name() == "Custom Template")
return;
if (request.contains("prompt")) { if (request.contains("prompt")) {
QJsonArray messages{ QJsonArray messages{
{QJsonObject{{"role", "user"}, {"content", request.take("prompt").toString()}}}}; {QJsonObject{{"role", "user"}, {"content", request.take("prompt").toString()}}}};
request["messages"] = std::move(messages); request["messages"] = std::move(messages);
} }
request["max_tokens"] = settings().maxTokens(); request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings().temperature(); request["temperature"] = settings.temperature();
request["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords()); request["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords());
if (settings().useTopP()) if (settings.useTopP())
request["top_p"] = settings().topP(); request["top_p"] = settings.topP();
if (settings().useTopK()) if (settings.useTopK())
request["top_k"] = settings().topK(); request["top_k"] = settings.topK();
if (settings().useFrequencyPenalty()) if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings().frequencyPenalty(); request["frequency_penalty"] = settings.frequencyPenalty();
if (settings().usePresencePenalty()) if (settings.usePresencePenalty())
request["presence_penalty"] = settings().presencePenalty(); request["presence_penalty"] = settings.presencePenalty();
} }
bool LMStudioProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse) bool LMStudioProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)

View File

@ -26,8 +26,8 @@
#include <QtCore/qeventloop.h> #include <QtCore/qeventloop.h>
#include "PromptTemplateManager.hpp" #include "PromptTemplateManager.hpp"
#include "QodeAssistSettings.hpp"
#include "QodeAssistUtils.hpp" #include "QodeAssistUtils.hpp"
#include "settings/PresetPromptsSettings.hpp"
namespace QodeAssist::Providers { namespace QodeAssist::Providers {
@ -50,21 +50,24 @@ QString OllamaProvider::completionEndpoint() const
void OllamaProvider::prepareRequest(QJsonObject &request) void OllamaProvider::prepareRequest(QJsonObject &request)
{ {
auto &settings = Settings::presetPromptsSettings();
auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate(); auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
if (currentTemplate->name() == "Custom Template")
return;
QJsonObject options; QJsonObject options;
options["num_predict"] = settings().maxTokens(); options["num_predict"] = settings.maxTokens();
options["keep_alive"] = settings().ollamaLivetime(); options["keep_alive"] = settings.ollamaLivetime();
options["temperature"] = settings().temperature(); options["temperature"] = settings.temperature();
options["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords()); options["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords());
if (settings().useTopP()) if (settings.useTopP())
options["top_p"] = settings().topP(); options["top_p"] = settings.topP();
if (settings().useTopK()) if (settings.useTopK())
options["top_k"] = settings().topK(); options["top_k"] = settings.topK();
if (settings().useFrequencyPenalty()) if (settings.useFrequencyPenalty())
options["frequency_penalty"] = settings().frequencyPenalty(); options["frequency_penalty"] = settings.frequencyPenalty();
if (settings().usePresencePenalty()) if (settings.usePresencePenalty())
options["presence_penalty"] = settings().presencePenalty(); options["presence_penalty"] = settings.presencePenalty();
request["options"] = options; request["options"] = options;
} }

View File

@ -25,7 +25,7 @@
#include <QNetworkReply> #include <QNetworkReply>
#include "PromptTemplateManager.hpp" #include "PromptTemplateManager.hpp"
#include "QodeAssistSettings.hpp" #include "settings/PresetPromptsSettings.hpp"
namespace QodeAssist::Providers { namespace QodeAssist::Providers {
@ -48,7 +48,10 @@ QString OpenAICompatProvider::completionEndpoint() const
void OpenAICompatProvider::prepareRequest(QJsonObject &request) void OpenAICompatProvider::prepareRequest(QJsonObject &request)
{ {
auto &settings = Settings::presetPromptsSettings();
const auto &currentTemplate = PromptTemplateManager::instance().getCurrentTemplate(); const auto &currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
if (currentTemplate->name() == "Custom Template")
return;
if (request.contains("prompt")) { if (request.contains("prompt")) {
QJsonArray messages{ QJsonArray messages{
@ -56,19 +59,19 @@ void OpenAICompatProvider::prepareRequest(QJsonObject &request)
request["messages"] = std::move(messages); request["messages"] = std::move(messages);
} }
request["max_tokens"] = settings().maxTokens(); request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings().temperature(); request["temperature"] = settings.temperature();
request["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords()); request["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords());
if (settings().useTopP()) if (settings.useTopP())
request["top_p"] = settings().topP(); request["top_p"] = settings.topP();
if (settings().useTopK()) if (settings.useTopK())
request["top_k"] = settings().topK(); request["top_k"] = settings.topK();
if (settings().useFrequencyPenalty()) if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings().frequencyPenalty(); request["frequency_penalty"] = settings.frequencyPenalty();
if (settings().usePresencePenalty()) if (settings.usePresencePenalty())
request["presence_penalty"] = settings().presencePenalty(); request["presence_penalty"] = settings.presencePenalty();
const QString &apiKey = settings().apiKey.value(); const QString &apiKey = settings.apiKey.value();
if (!apiKey.isEmpty()) { if (!apiKey.isEmpty()) {
request["api_key"] = apiKey; request["api_key"] = apiKey;
} }

View File

@ -45,7 +45,7 @@
#include "providers/OllamaProvider.hpp" #include "providers/OllamaProvider.hpp"
#include "providers/OpenAICompatProvider.hpp" #include "providers/OpenAICompatProvider.hpp"
#include "templates/CodeLLamaTemplate.hpp" #include "templates/CodeLLamaTemplate.hpp"
#include "templates/CodeQwenChat.hpp" #include "templates/CustomTemplate.hpp"
#include "templates/DeepSeekCoderV2.hpp" #include "templates/DeepSeekCoderV2.hpp"
#include "templates/StarCoder2Template.hpp" #include "templates/StarCoder2Template.hpp"
@ -80,8 +80,8 @@ public:
auto &templateManager = PromptTemplateManager::instance(); auto &templateManager = PromptTemplateManager::instance();
templateManager.registerTemplate<Templates::CodeLLamaTemplate>(); templateManager.registerTemplate<Templates::CodeLLamaTemplate>();
templateManager.registerTemplate<Templates::StarCoder2Template>(); templateManager.registerTemplate<Templates::StarCoder2Template>();
templateManager.registerTemplate<Templates::CodeQwenChatTemplate>();
templateManager.registerTemplate<Templates::DeepSeekCoderV2Template>(); templateManager.registerTemplate<Templates::DeepSeekCoderV2Template>();
templateManager.registerTemplate<Templates::CustomTemplate>();
Utils::Icon QCODEASSIST_ICON( Utils::Icon QCODEASSIST_ICON(
{{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}}); {{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}});

View 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
}

View 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
}

View File

@ -0,0 +1,148 @@
/*
* 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 "ContextSettings.hpp"
#include <QMessageBox>
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
#include <utils/layoutbuilder.h>
#include "QodeAssistConstants.hpp"
#include "QodeAssisttr.h"
namespace QodeAssist::Settings {
ContextSettings &contextSettings()
{
static ContextSettings settings;
return settings;
}
ContextSettings::ContextSettings()
{
setAutoApply(false);
setDisplayName(Tr::tr("Context"));
readFullFile.setSettingsKey(Constants::READ_FULL_FILE);
readFullFile.setLabelText(Tr::tr("Read Full File"));
readFullFile.setDefaultValue(false);
readStringsBeforeCursor.setSettingsKey(Constants::READ_STRINGS_BEFORE_CURSOR);
readStringsBeforeCursor.setLabelText(Tr::tr("Read Strings Before Cursor"));
readStringsBeforeCursor.setRange(0, 10000);
readStringsBeforeCursor.setDefaultValue(50);
readStringsAfterCursor.setSettingsKey(Constants::READ_STRINGS_AFTER_CURSOR);
readStringsAfterCursor.setLabelText(Tr::tr("Read Strings After Cursor"));
readStringsAfterCursor.setRange(0, 10000);
readStringsAfterCursor.setDefaultValue(30);
useFilePathInContext.setSettingsKey(Constants::USE_FILE_PATH_IN_CONTEXT);
useFilePathInContext.setDefaultValue(false);
useFilePathInContext.setLabelText(Tr::tr("Use File Path in Context"));
useSpecificInstructions.setSettingsKey(Constants::USE_SPECIFIC_INSTRUCTIONS);
useSpecificInstructions.setDefaultValue(false);
useSpecificInstructions.setLabelText(Tr::tr("Use Specific Instructions"));
specificInstractions.setSettingsKey(Constants::SPECIFIC_INSTRUCTIONS);
specificInstractions.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
specificInstractions.setLabelText(
Tr::tr("Instructions: Please keep %1 for languge name, warning, it shouldn't too big"));
specificInstractions.setDefaultValue(
"You are an expert %1 code completion AI."
"CRITICAL: Please provide minimal the best possible code completion suggestions.\n");
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
readSettings();
readStringsAfterCursor.setEnabled(!readFullFile());
readStringsBeforeCursor.setEnabled(!readFullFile());
specificInstractions.setEnabled(useSpecificInstructions());
setupConnection();
setLayouter([this]() {
using namespace Layouting;
return Column{Row{readFullFile, Stretch{1}, resetToDefaults},
readStringsBeforeCursor,
readStringsAfterCursor,
useFilePathInContext,
useSpecificInstructions,
specificInstractions,
Stretch{1}};
});
}
void ContextSettings::setupConnection()
{
connect(&readFullFile, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
readStringsAfterCursor.setEnabled(!readFullFile.volatileValue());
readStringsBeforeCursor.setEnabled(!readFullFile.volatileValue());
});
connect(&useSpecificInstructions, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
specificInstractions.setEnabled(useSpecificInstructions.volatileValue());
});
connect(&resetToDefaults, &ButtonAspect::clicked, this, &ContextSettings::resetPageToDefaults);
}
void ContextSettings::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(readFullFile);
resetAspect(readStringsBeforeCursor);
resetAspect(readStringsAfterCursor);
resetAspect(useFilePathInContext);
resetAspect(useSpecificInstructions);
resetAspect(specificInstractions);
}
QMessageBox::information(Core::ICore::dialogParent(),
Tr::tr("Settings Reset"),
Tr::tr("All settings have been reset to their default values."));
}
class ContextSettingsPage : public Core::IOptionsPage
{
public:
ContextSettingsPage()
{
setId(Constants::QODE_ASSIST_CONTEXT_SETTINGS_PAGE_ID);
setDisplayName(Tr::tr("Context"));
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
setDisplayCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY);
setCategoryIconPath(":/resources/images/qoderassist-icon.png");
setSettingsProvider([] { return &contextSettings(); });
}
};
const ContextSettingsPage contextSettingsPage;
} // namespace QodeAssist::Settings

View File

@ -0,0 +1,50 @@
/*
* 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 "SettingsUtils.hpp"
namespace QodeAssist::Settings {
class ContextSettings : public Utils::AspectContainer
{
public:
ContextSettings();
Utils::BoolAspect readFullFile{this};
Utils::IntegerAspect readStringsBeforeCursor{this};
Utils::IntegerAspect readStringsAfterCursor{this};
Utils::StringAspect specificInstractions{this};
Utils::BoolAspect useSpecificInstructions{this};
Utils::BoolAspect useFilePathInContext{this};
ButtonAspect resetToDefaults{this};
private:
void setupConnection();
void resetPageToDefaults();
};
ContextSettings &contextSettings();
} // namespace QodeAssist::Settings

View File

@ -0,0 +1,203 @@
/*
* 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 "QodeAssistConstants.hpp"
#include "QodeAssisttr.h"
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{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);
}
QMessageBox::information(Core::ICore::dialogParent(),
Tr::tr("Settings Reset"),
Tr::tr("All settings have been reset to their default values."));
}
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

View 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/>.
*/
#pragma once
#include "settings/SettingsUtils.hpp"
#include <utils/aspects.h>
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

View File

@ -0,0 +1,257 @@
/*
* 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 "LLMProvidersManager.hpp"
#include "PromptTemplateManager.hpp"
#include "QodeAssistConstants.hpp"
#include "QodeAssistUtils.hpp"
#include "QodeAssisttr.h"
namespace QodeAssist::Settings {
GeneralSettings &generalSettings()
{
static GeneralSettings settings;
return settings;
}
GeneralSettings::GeneralSettings()
{
setAutoApply(false);
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"));
startSuggestionTimer.setSettingsKey(Constants::START_SUGGESTION_TIMER);
startSuggestionTimer.setLabelText(Tr::tr("Start Suggestion Timer:"));
startSuggestionTimer.setRange(10, 10000);
startSuggestionTimer.setDefaultValue(500);
autoCompletionCharThreshold.setSettingsKey(Constants::AUTO_COMPLETION_CHAR_THRESHOLD);
autoCompletionCharThreshold.setLabelText(
Tr::tr("Character threshold for AI suggestion start:"));
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(1, 10);
autoCompletionCharThreshold.setDefaultValue(2);
autoCompletionTypingInterval.setSettingsKey(Constants::AUTO_COMPLETION_TYPING_INTERVAL);
autoCompletionTypingInterval.setLabelText(
Tr::tr("Typing interval for AI suggestion start(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);
llmProviders.setSettingsKey(Constants::LLM_PROVIDERS);
llmProviders.setDisplayName(Tr::tr("FIM Provider:"));
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("FIM 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 Fill-In-the-Middle Model");
fimPrompts.setDisplayName(Tr::tr("Fill-In-the-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;
auto rootLayout = Column{Row{enableQodeAssist, Stretch{1}, resetToDefaults},
enableAutoComplete,
startSuggestionTimer,
autoCompletionCharThreshold,
autoCompletionTypingInterval,
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.setVolatileValue(provider->url());
endPoint.setVolatileValue(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.setVolatileValue(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);
resetAspect(startSuggestionTimer);
resetAspect(autoCompletionTypingInterval);
resetAspect(autoCompletionCharThreshold);
}
fimPrompts.setStringValue("StarCoder2");
llmProviders.setStringValue("Ollama");
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:
GeneralSettingsPage()
{
setId(Constants::QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID);
setDisplayName(Tr::tr("General"));
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
setSettingsProvider([] { return &generalSettings(); });
}
};
const GeneralSettingsPage generalSettingsPage;
} // namespace QodeAssist::Settings

View File

@ -0,0 +1,59 @@
/*
* 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 "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::IntegerAspect startSuggestionTimer{this};
Utils::IntegerAspect autoCompletionCharThreshold{this};
Utils::IntegerAspect autoCompletionTypingInterval{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();
} // namespace QodeAssist::Settings

View File

@ -0,0 +1,185 @@
/*
* 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 "PresetPromptsSettings.hpp"
#include <QMessageBox>
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
#include <utils/layoutbuilder.h>
#include "QodeAssistConstants.hpp"
#include "QodeAssisttr.h"
namespace QodeAssist::Settings {
PresetPromptsSettings &presetPromptsSettings()
{
static PresetPromptsSettings settings;
return settings;
}
PresetPromptsSettings::PresetPromptsSettings()
{
setAutoApply(false);
setDisplayName(Tr::tr("Preset Prompts Params"));
temperature.setSettingsKey(Constants::TEMPERATURE);
temperature.setLabelText(Tr::tr("Temperature:"));
temperature.setDefaultValue(0.2);
temperature.setRange(0.0, 10.0);
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);
maxTokens.setSettingsKey(Constants::MAX_TOKENS);
maxTokens.setLabelText(Tr::tr("Max Tokens"));
maxTokens.setRange(-1, 10000);
maxTokens.setDefaultValue(150);
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, 1.0);
useTopK.setSettingsKey(Constants::USE_TOP_K);
useTopK.setDefaultValue(false);
topK.setSettingsKey(Constants::TOP_K);
topK.setLabelText(Tr::tr("top_k"));
topK.setDefaultValue(50);
topK.setRange(1, 1000);
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);
apiKey.setSettingsKey(Constants::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();
topK.setEnabled(useTopK());
topP.setEnabled(useTopP());
presencePenalty.setEnabled(usePresencePenalty());
frequencyPenalty.setEnabled(useFrequencyPenalty());
setupConnections();
setLayouter([this]() {
using namespace Layouting;
return Column{Row{temperature, Stretch{1}, resetToDefaults},
maxTokens,
Row{useTopP, topP, Stretch{1}},
Row{useTopK, topK, Stretch{1}},
Row{usePresencePenalty, presencePenalty, Stretch{1}},
Row{useFrequencyPenalty, frequencyPenalty, Stretch{1}},
apiKey,
Stretch{1}};
});
}
void PresetPromptsSettings::setupConnections()
{
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(&resetToDefaults,
&ButtonAspect::clicked,
this,
&PresetPromptsSettings::resetSettingsToDefaults);
}
void PresetPromptsSettings::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(temperature);
resetAspect(maxTokens);
resetAspect(ollamaLivetime);
resetAspect(useTopP);
resetAspect(topP);
resetAspect(useTopK);
resetAspect(topK);
resetAspect(usePresencePenalty);
resetAspect(presencePenalty);
resetAspect(useFrequencyPenalty);
resetAspect(frequencyPenalty);
}
QMessageBox::information(Core::ICore::dialogParent(),
Tr::tr("Settings Reset"),
Tr::tr("All settings have been reset to their default values."));
}
class PresetPromptsSettingsPage : public Core::IOptionsPage
{
public:
PresetPromptsSettingsPage()
{
setId(Constants::QODE_ASSIST_PRESET_PROMPTS_SETTINGS_PAGE_ID);
setDisplayName(Tr::tr("Preset Prompts Params"));
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
setSettingsProvider([] { return &presetPromptsSettings(); });
}
};
const PresetPromptsSettingsPage presetPromptsSettingsPage;
} // namespace QodeAssist::Settings

View File

@ -0,0 +1,59 @@
/*
* 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 "settings/SettingsUtils.hpp"
#include <utils/aspects.h>
namespace QodeAssist::Settings {
class PresetPromptsSettings : public Utils::AspectContainer
{
public:
PresetPromptsSettings();
Utils::DoubleAspect temperature{this};
Utils::IntegerAspect maxTokens{this};
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};
Utils::StringAspect ollamaLivetime{this};
Utils::StringAspect apiKey{this};
ButtonAspect resetToDefaults{this};
private:
void setupConnections();
void resetSettingsToDefaults();
};
PresetPromptsSettings &presetPromptsSettings();
} // namespace QodeAssist::Settings

View 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 <QPushButton>
#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
namespace QodeAssist::Settings {
template<typename AspectType>
void resetAspect(AspectType &aspect)
{
aspect.setVolatileValue(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

View File

@ -1,44 +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 "PromptTemplate.hpp"
namespace QodeAssist::Templates {
class CodeQwenChatTemplate : public PromptTemplate
{
public:
QString name() const override { return "CodeQwenChat (experimental)"; }
QString promptTemplate() const override { return "%1\n### Instruction:%2%3 ### Response:\n"; }
QStringList stopWords() const override
{
return QStringList() << "### Instruction:" << "### Response:" << "\n\n### ";
}
void prepareRequest(QJsonObject &request, const ContextData &context) const override
{
QString formattedPrompt = promptTemplate().arg(context.instriuctions,
context.prefix,
context.suffix);
request["prompt"] = formattedPrompt;
}
};
} // namespace QodeAssist::Templates

View File

@ -0,0 +1,89 @@
/*
* 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 "PromptTemplate.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include "QodeAssistUtils.hpp"
#include "settings/CustomPromptSettings.hpp"
namespace QodeAssist::Templates {
class CustomTemplate : public PromptTemplate
{
public:
QString name() const override { return "Custom Template"; }
QString promptTemplate() const override
{
return Settings::customPromptSettings().customJsonTemplate();
}
QStringList stopWords() const override { return QStringList(); }
void prepareRequest(QJsonObject &request, const ContextData &context) const override
{
QJsonDocument doc = QJsonDocument::fromJson(promptTemplate().toUtf8());
if (doc.isNull() || !doc.isObject()) {
logMessage(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 ContextData &context) const
{
if (value.isString()) {
QString str = value.toString();
str.replace("{{QODE_INSTRUCTIONS}}", context.instriuctions);
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 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