Compare commits

...

57 Commits

Author SHA1 Message Date
6503887091 Upgrade to version 0.3.9 2024-11-23 21:59:35 +01:00
50087aa744 Change configure part in description
- Replace codellama to qwen models
- Add prefer auto template for ollama provider
2024-11-23 21:55:57 +01:00
4f2dc0c450 feat: Add Ollama auto template for chat 2024-11-23 21:15:34 +01:00
80fe388bdd feat: Add automatic template handling for Ollama models (#43)
* feat: Add automatic template handling for Ollama models

- Add OllamaAutoFim
- Use native Ollama API format when possible
- Remove need for manual template selection for most Ollama models
- Default to model-specific format from Ollama modelfile
- Fallback to manual template selection if needed

This change simplifies configuration by automatically using
the correct template format for each Ollama model.
2024-11-23 19:37:55 +01:00
8375d85f7d Upgrade to 0.3.8 2024-11-16 15:42:10 +01:00
54b2cc7011 Update FUNDING.yml 2024-11-16 15:34:22 +01:00
f209cb75a2 Impove UX general setting by added helpers dialogs for user (#42)
- Added dialogs for selecting url, model and custom model when provider doesn't provide model list or setup of qode assist is not finishing
2024-11-16 15:25:28 +01:00
5e813ba402 Fix systemPrompt and context working 2024-11-16 10:20:57 +01:00
7af8fc2ddc Add projects code style improvements
* Add qmlls to gitignore
* Fix qmlformat file
* Add project code style description to README.md
2024-11-16 00:47:57 +01:00
46300f7635 Update issue templates 2024-11-16 00:35:24 +01:00
0a1c941d8b Fix selecting chat models
Old code linked to fim provider, changed to chat provider now
2024-11-12 08:15:06 +01:00
f86182408d Add clang-format and qmlformat files 2024-11-12 00:12:18 +01:00
252db4c5f7 Add clang-format and qmlformat files 2024-11-12 00:07:12 +01:00
e5af3a2884 Merge pull request #36 from Palm1r/fix-pr30-building
Fix pr30 build
2024-11-11 21:51:08 +01:00
bb543d1f40 Fix pr30 build 2024-11-11 21:45:38 +01:00
a184916d7b Fix double call building in build_cmake.yml 2024-11-11 21:43:46 +01:00
7ad8ddfee4 Merge pull request #30 from SidneyCogdill/patch/fix-qml-warnings
Fix all warnings in QML
2024-11-11 21:30:26 +01:00
0ed6fb4a6b Add pull_request to build_cmake.yml 2024-11-11 21:27:31 +01:00
00fce5db99 Version 0.3.7
Fix settings category icon and name
2024-11-11 11:30:37 +01:00
251a9bae03 Fix settings category icon and name 2024-11-11 11:26:11 +01:00
3dba9d7abe Fix link to discord README.md 2024-11-11 11:03:48 +01:00
45b0f3f18e Add discord link to README.md 2024-11-11 10:50:53 +01:00
4432d4019d Version 0.3.6
Upgrade plugin version to 0.3.6
2024-11-11 08:29:12 +01:00
f679d76d43 Upgrade version to 0.3.6 2024-11-11 08:25:05 +01:00
29f94561ef Fix openai api key
Restore the ability to use API key
2024-11-11 07:21:18 +01:00
cd6c766ed2 Restore the ability to use API key 2024-11-11 14:09:32 +08:00
6d3bc362b3 Fix all warnings in QML 2024-11-11 11:18:37 +08:00
87393b681f Version 0.3.5
- Rework General, Code Completion and Chat Assist pages in settings 
- Add Sharing current open file with model and settings for default behavior for this
- Fixed a bug on WindowsOS where settings were set incorrectly
2024-11-11 01:08:18 +01:00
5d496fee58 Upgrade version 2024-11-11 01:01:26 +01:00
9902623ba0 Fix problem with wrong reading settings 2024-11-11 01:00:54 +01:00
61f1f0ae4f Add sharing current file with model 2024-11-11 00:52:49 +01:00
bc93bce03b Adapt new settings 2024-11-11 00:03:38 +01:00
85d039cbd5 Rework Chat Assistant Settings 2024-11-10 22:50:47 +01:00
2acaef553d Rework Code Completion Settings 2024-11-10 21:21:37 +01:00
b141e54e3e Rework General Settings 2024-11-10 20:42:10 +01:00
1ec6098210 Move ButtonAspect 2024-11-10 18:56:57 +01:00
9c945f066b Rework README.md
- Add badges
- Add table of contents
2024-10-21 08:28:20 +02:00
4a82e9c046 Version 0.3.4
Add support Qwen model for autocompete(FIM)
based on qwen2.5-coder:7b-base
2024-10-21 01:58:55 +02:00
838d69623c Upgrade to 0.3.4 2024-10-21 01:54:27 +02:00
693e429bdd Fix name in bar badge 2024-10-19 20:11:09 +02:00
496d8feb66 Add Qwen fim template 2024-10-19 19:13:42 +02:00
40a568ebd9 Version 0.3.3
- Add streaming response to chat
- Add stopping chat request and button
2024-10-17 00:10:47 +02:00
5b43eb4fd2 Increase default max tokens for chat before deleting first messages 2024-10-17 00:05:31 +02:00
9c2516cd4c Add stopping chat requests and button 2024-10-17 00:03:12 +02:00
2257e6e45f Fix unbehavior settings of provider and template 2024-10-17 00:02:14 +02:00
80eda8c167 Add stream text to chat 2024-10-16 22:51:34 +02:00
3db2691114 Upgrade version 2024-10-16 22:44:51 +02:00
bf518b4a01 Version 0.3.2
Add StarCoder2 instruct support
2024-10-16 10:50:19 +02:00
46829720d8 Add StarCoder2 instruct support 2024-10-16 10:45:48 +02:00
9158a3ac0d Add Llama support to README.md 2024-10-14 21:52:36 +02:00
d6e02d9d2a Version 0.3.1
Improve chat text input
Add Llama chat support
Fix monospace font
2024-10-14 21:41:04 +02:00
9c8cac4e3a Upgrade to version 0.3.1 2024-10-14 21:35:49 +02:00
965af4a945 Add Llama chat support 2024-10-14 21:35:17 +02:00
95f29fefc7 Fix monospace font 2024-10-14 21:25:18 +02:00
1dd50b6c83 Replace textinput to textfield 2024-10-14 21:18:48 +02:00
146e772514 Update README.md 2024-10-14 01:38:50 +02:00
4b851f1662 Update README.md for 0.3.0 2024-10-14 01:36:48 +02:00
82 changed files with 2792 additions and 1644 deletions

108
.clang-format Normal file
View File

@ -0,0 +1,108 @@
# .clang-format from Qt Creator
# https://github.com/qt-creator/qt-creator/blob/master/.clang-format
#
# yaml-language-server: $schema=https://json.schemastore.org/clang-format.json
#
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: DontAlign
AlignOperands: Align
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: true
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- forever # avoids { wrapped to next line
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeCategories:
- Regex: '^<Q.*'
Priority: 200
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
# Do not add QT_BEGIN_NAMESPACE/QT_END_NAMESPACE as this will indent lines in between.
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 88
PenaltyBreakBeforeFirstCallParameter: 300
PenaltyBreakComment: 500
PenaltyBreakFirstLessLess: 400
PenaltyBreakString: 600
PenaltyExcessCharacter: 50
PenaltyReturnTypeOnItsOwnLine: 300
PointerAlignment: Right
ReflowComments: false
SortIncludes: CaseSensitive
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++17
TabWidth: 4
UseTab: Never

2
.github/FUNDING.yml vendored
View File

@ -3,7 +3,7 @@
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: palm1r
ko_fi: qodeassist
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,7 +1,14 @@
name: Build plugin
on: [push]
on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
branches:
- main
env:
PLUGIN_NAME: QodeAssist
QT_VERSION: 6.7.3

1
.gitignore vendored
View File

@ -34,6 +34,7 @@ Thumbs.db
*.rc
/.qmake.cache
/.qmake.stash
.qmlls.ini
# qtcreator generated files
*.pro.user*

3
.qmlformat.ini Normal file
View File

@ -0,0 +1,3 @@
[General]
IndentWidth=4
NewlineType=native

View File

@ -10,23 +10,28 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(QtCreator REQUIRED COMPONENTS Core)
find_package(Qt6 COMPONENTS Core Gui Widgets Network REQUIRED)
find_package(Qt6 COMPONENTS Core Gui Quick Widgets Network REQUIRED)
add_subdirectory(llmcore)
add_subdirectory(settings)
add_subdirectory(logger)
add_subdirectory(chatview)
add_subdirectory(ChatView)
add_qtc_plugin(QodeAssist
PLUGIN_DEPENDS
QtCreator::Core
QtCreator::LanguageClient
QtCreator::TextEditor
QtCreator::ProjectExplorer
DEPENDS
Qt::Core
Qt::Gui
Qt::Quick
Qt::Widgets
Qt::Network
QtCreator::ExtensionSystem
QtCreator::Utils
QtCreator::ProjectExplorer
QodeAssistChatViewplugin
SOURCES
.github/workflows/build_cmake.yml
.github/workflows/README.md
@ -41,7 +46,10 @@ add_qtc_plugin(QodeAssist
templates/CustomFimTemplate.hpp
templates/DeepSeekCoderChat.hpp
templates/CodeLlamaChat.hpp
templates/QwenChat.hpp
templates/Qwen.hpp
templates/StarCoderChat.hpp
templates/Ollama.hpp
templates/BasicChat.hpp
providers/OllamaProvider.hpp providers/OllamaProvider.cpp
providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp
providers/OpenAICompatProvider.hpp providers/OpenAICompatProvider.cpp
@ -54,6 +62,5 @@ add_qtc_plugin(QodeAssist
core/ChangesManager.h core/ChangesManager.cpp
chat/ChatOutputPane.h chat/ChatOutputPane.cpp
chat/NavigationPanel.hpp chat/NavigationPanel.cpp
ConfigurationManager.hpp ConfigurationManager.cpp
)
target_link_libraries(QodeAssist PRIVATE QodeAssistChatViewplugin)

View File

@ -1,8 +1,12 @@
qt_add_library(QodeAssistChatView STATIC)
qt_policy(SET QTP0001 NEW)
# URI name should match the subdirectory name to suppress the warning
qt_add_qml_module(QodeAssistChatView
URI ChatView
VERSION 1.0
DEPENDENCIES QtQuick
QML_FILES
qml/RootItem.qml
qml/ChatItem.qml
@ -31,5 +35,5 @@ target_link_libraries(QodeAssistChatView
)
target_include_directories(QodeAssistChatView
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
)

View File

@ -22,7 +22,7 @@
#include <QtQml>
#include <utils/aspects.h>
#include "GeneralSettings.hpp"
#include "ChatAssistantSettings.hpp"
namespace QodeAssist::Chat {
@ -30,7 +30,7 @@ ChatModel::ChatModel(QObject *parent)
: QAbstractListModel(parent)
, m_totalTokens(0)
{
auto &settings = Settings::generalSettings();
auto &settings = Settings::chatAssistantSettings();
connect(&settings.chatTokensThreshold,
&Utils::BaseAspect::changed,
@ -68,6 +68,28 @@ QHash<int, QByteArray> ChatModel::roleNames() const
return roles;
}
void ChatModel::addMessage(const QString &content, ChatRole role, const QString &id)
{
int tokenCount = estimateTokenCount(content);
if (!m_messages.isEmpty() && !id.isEmpty() && m_messages.last().id == id) {
Message &lastMessage = m_messages.last();
int oldTokenCount = lastMessage.tokenCount;
lastMessage.content = content;
lastMessage.tokenCount = tokenCount;
m_totalTokens += (tokenCount - oldTokenCount);
emit dataChanged(index(m_messages.size() - 1), index(m_messages.size() - 1));
} else {
beginInsertRows(QModelIndex(), m_messages.size(), m_messages.size());
m_messages.append({role, content, tokenCount, id});
m_totalTokens += tokenCount;
endInsertRows();
}
trim();
emit totalTokensChanged();
}
QVector<ChatModel::Message> ChatModel::getChatHistory() const
{
return m_messages;
@ -92,17 +114,6 @@ int ChatModel::estimateTokenCount(const QString &text) const
return text.length() / 4;
}
void ChatModel::addMessage(const QString &content, ChatRole role)
{
int tokenCount = estimateTokenCount(content);
beginInsertRows(QModelIndex(), m_messages.size(), m_messages.size());
m_messages.append({role, content, tokenCount});
m_totalTokens += tokenCount;
endInsertRows();
trim();
emit totalTokensChanged();
}
void ChatModel::clear()
{
beginResetModel();
@ -141,11 +152,11 @@ QList<MessagePart> ChatModel::processMessageContent(const QString &content) cons
return parts;
}
QJsonArray ChatModel::prepareMessagesForRequest(LLMCore::ContextData context) const
QJsonArray ChatModel::prepareMessagesForRequest(const QString &systemPrompt) const
{
QJsonArray messages;
messages.append(QJsonObject{{"role", "system"}, {"content", context.systemPrompt}});
messages.append(QJsonObject{{"role", "system"}, {"content", systemPrompt}});
for (const auto &message : m_messages) {
QString role;
@ -172,8 +183,13 @@ int ChatModel::totalTokens() const
int ChatModel::tokensThreshold() const
{
auto &settings = Settings::generalSettings();
auto &settings = Settings::chatAssistantSettings();
return settings.chatTokensThreshold();
}
QString ChatModel::lastMessageId() const
{
return !m_messages.isEmpty() ? m_messages.last().id : "";
}
} // namespace QodeAssist::Chat

View File

@ -24,7 +24,7 @@
#include <QAbstractListModel>
#include <QJsonArray>
#include <qqmlintegration.h>
#include <QtQmlIntegration>
namespace QodeAssist::Chat {
@ -46,6 +46,7 @@ public:
ChatRole role;
QString content;
int tokenCount;
QString id;
};
explicit ChatModel(QObject *parent = nullptr);
@ -54,17 +55,18 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void addMessage(const QString &content, ChatRole role);
Q_INVOKABLE void addMessage(const QString &content, ChatRole role, const QString &id);
Q_INVOKABLE void clear();
Q_INVOKABLE QList<MessagePart> processMessageContent(const QString &content) const;
QVector<Message> getChatHistory() const;
QJsonArray prepareMessagesForRequest(LLMCore::ContextData context) const;
QJsonArray prepareMessagesForRequest(const QString &systemPrompt) const;
int totalTokens() const;
int tokensThreshold() const;
QString currentModel() const;
QString lastMessageId() const;
signals:
void totalTokensChanged();

View File

@ -22,6 +22,7 @@
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
#include "ChatAssistantSettings.hpp"
#include "GeneralSettings.hpp"
namespace QodeAssist::Chat {
@ -33,10 +34,16 @@ ChatRootView::ChatRootView(QQuickItem *parent)
{
auto &settings = Settings::generalSettings();
connect(&settings.chatModelName,
connect(&settings.caModel,
&Utils::BaseAspect::changed,
this,
&ChatRootView::currentTemplateChanged);
connect(&Settings::chatAssistantSettings().sharingCurrentFile,
&Utils::BaseAspect::changed,
this,
&ChatRootView::isSharingCurrentFileChanged);
generateColors();
}
@ -50,9 +57,9 @@ QColor ChatRootView::backgroundColor() const
return Utils::creatorColor(Utils::Theme::BackgroundColorNormal);
}
void ChatRootView::sendMessage(const QString &message) const
void ChatRootView::sendMessage(const QString &message, bool sharingCurrentFile) const
{
m_clientInterface->sendMessage(message);
m_clientInterface->sendMessage(message, sharingCurrentFile);
}
void ChatRootView::copyToClipboard(const QString &text)
@ -60,6 +67,11 @@ void ChatRootView::copyToClipboard(const QString &text)
QGuiApplication::clipboard()->setText(text);
}
void ChatRootView::cancelRequest()
{
m_clientInterface->cancelRequest();
}
void ChatRootView::generateColors()
{
QColor baseColor = backgroundColor();
@ -106,7 +118,7 @@ QColor ChatRootView::generateColor(const QColor &baseColor,
QString ChatRootView::currentTemplate() const
{
auto &settings = Settings::generalSettings();
return settings.chatModelName();
return settings.caModel();
}
QColor ChatRootView::primaryColor() const
@ -124,4 +136,9 @@ QColor ChatRootView::codeColor() const
return m_codeColor;
}
bool ChatRootView::isSharingCurrentFile() const
{
return Settings::chatAssistantSettings().sharingCurrentFile();
}
} // namespace QodeAssist::Chat

View File

@ -28,13 +28,19 @@ namespace QodeAssist::Chat {
class ChatRootView : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(ChatModel *chatModel READ chatModel NOTIFY chatModelChanged FINAL)
Q_OBJECT
// Possibly Qt bug: QTBUG-131004
// The class type name must be fully qualified
// including the namespace.
// Otherwise qmlls can't find it.
Q_PROPERTY(QodeAssist::Chat::ChatModel *chatModel READ chatModel NOTIFY chatModelChanged FINAL)
Q_PROPERTY(QString currentTemplate READ currentTemplate NOTIFY currentTemplateChanged FINAL)
Q_PROPERTY(QColor backgroundColor READ backgroundColor CONSTANT FINAL)
Q_PROPERTY(QColor primaryColor READ primaryColor CONSTANT FINAL)
Q_PROPERTY(QColor secondaryColor READ secondaryColor CONSTANT FINAL)
Q_PROPERTY(QColor codeColor READ codeColor CONSTANT FINAL)
Q_PROPERTY(bool isSharingCurrentFile READ isSharingCurrentFile NOTIFY
isSharingCurrentFileChanged FINAL)
QML_ELEMENT
public:
@ -49,14 +55,19 @@ public:
QColor codeColor() const;
bool isSharingCurrentFile() const;
public slots:
void sendMessage(const QString &message) const;
void sendMessage(const QString &message, bool sharingCurrentFile = false) const;
void copyToClipboard(const QString &text);
void cancelRequest();
signals:
void chatModelChanged();
void currentTemplateChanged();
void isSharingCurrentFileChanged();
private:
void generateColors();
QColor generateColor(const QColor &baseColor,

32
ChatView/ChatUtils.cpp Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ChatUtils.h"
#include <QClipboard>
#include <QGuiApplication>
namespace QodeAssist::Chat {
void ChatUtils::copyToClipboard(const QString &text)
{
QGuiApplication::clipboard()->setText(text);
}
} // namespace QodeAssist::Chat

40
ChatView/ChatUtils.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <qobject.h>
#include <qqmlintegration.h>
namespace QodeAssist::Chat {
// Q_NAMESPACE
class ChatUtils : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(ChatUtils)
public:
explicit ChatUtils(QObject *parent = nullptr)
: QObject(parent) {};
Q_INVOKABLE void copyToClipboard(const QString &text);
};
} // namespace QodeAssist::Chat

View File

@ -27,7 +27,7 @@ namespace QodeAssist::Chat {
ChatWidget::ChatWidget(QWidget *parent)
: QQuickWidget(parent)
{
setSource(QUrl("qrc:/ChatView/qml/RootItem.qml"));
setSource(QUrl("qrc:/qt/qml/ChatView/qml/RootItem.qml"));
setResizeMode(QQuickWidget::SizeRootObjectToView);
}

View File

@ -0,0 +1,183 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ClientInterface.hpp"
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QUuid>
#include <texteditor/textdocument.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/idocument.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
#include "ChatAssistantSettings.hpp"
#include "GeneralSettings.hpp"
#include "Logger.hpp"
#include "PromptTemplateManager.hpp"
#include "ProvidersManager.hpp"
namespace QodeAssist::Chat {
ClientInterface::ClientInterface(ChatModel *chatModel, QObject *parent)
: QObject(parent)
, m_requestHandler(new LLMCore::RequestHandler(this))
, m_chatModel(chatModel)
{
connect(m_requestHandler,
&LLMCore::RequestHandler::completionReceived,
this,
[this](const QString &completion, const QJsonObject &request, bool isComplete) {
handleLLMResponse(completion, request, isComplete);
});
connect(m_requestHandler,
&LLMCore::RequestHandler::requestFinished,
this,
[this](const QString &, bool success, const QString &errorString) {
if (!success) {
emit errorOccurred(errorString);
}
});
}
ClientInterface::~ClientInterface() = default;
void ClientInterface::sendMessage(const QString &message, bool includeCurrentFile)
{
cancelRequest();
auto &chatAssistantSettings = Settings::chatAssistantSettings();
auto providerName = Settings::generalSettings().caProvider();
auto provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName);
if (!provider) {
LOG_MESSAGE(QString("No provider found with name: %1").arg(providerName));
return;
}
auto templateName = Settings::generalSettings().caTemplate();
auto promptTemplate = LLMCore::PromptTemplateManager::instance().getChatTemplateByName(
templateName);
if (!promptTemplate) {
LOG_MESSAGE(QString("No template found with name: %1").arg(templateName));
return;
}
LLMCore::ContextData context;
context.prefix = message;
context.suffix = "";
QString systemPrompt;
if (chatAssistantSettings.useSystemPrompt())
systemPrompt = chatAssistantSettings.systemPrompt();
if (includeCurrentFile) {
QString fileContext = getCurrentFileContext();
if (!fileContext.isEmpty()) {
systemPrompt = systemPrompt.append(fileContext);
}
}
QJsonObject providerRequest;
providerRequest["model"] = Settings::generalSettings().caModel();
providerRequest["stream"] = true;
providerRequest["messages"] = m_chatModel->prepareMessagesForRequest(systemPrompt);
if (promptTemplate)
promptTemplate->prepareRequest(providerRequest, context);
else
qWarning("No prompt template found");
if (provider)
provider->prepareRequest(providerRequest, LLMCore::RequestType::Chat);
else
qWarning("No provider found");
LLMCore::LLMConfig config;
config.requestType = LLMCore::RequestType::Chat;
config.provider = provider;
config.promptTemplate = promptTemplate;
config.url = QString("%1%2").arg(Settings::generalSettings().caUrl(), provider->chatEndpoint());
config.providerRequest = providerRequest;
config.multiLineCompletion = false;
QJsonObject request;
request["id"] = QUuid::createUuid().toString();
m_chatModel->addMessage(message, ChatModel::ChatRole::User, "");
m_requestHandler->sendLLMRequest(config, request);
}
void ClientInterface::clearMessages()
{
m_chatModel->clear();
LOG_MESSAGE("Chat history cleared");
}
void ClientInterface::cancelRequest()
{
auto id = m_chatModel->lastMessageId();
m_requestHandler->cancelRequest(id);
}
void ClientInterface::handleLLMResponse(const QString &response,
const QJsonObject &request,
bool isComplete)
{
QString messageId = request["id"].toString();
m_chatModel->addMessage(response.trimmed(), ChatModel::ChatRole::Assistant, messageId);
if (isComplete) {
LOG_MESSAGE("Message completed. Final response for message " + messageId + ": " + response);
}
}
QString ClientInterface::getCurrentFileContext() const
{
auto currentEditor = Core::EditorManager::currentEditor();
if (!currentEditor) {
LOG_MESSAGE("No active editor found");
return QString();
}
auto textDocument = qobject_cast<TextEditor::TextDocument *>(currentEditor->document());
if (!textDocument) {
LOG_MESSAGE("Current document is not a text document");
return QString();
}
QString fileInfo = QString("Language: %1\nFile: %2\n\n")
.arg(textDocument->mimeType(), textDocument->filePath().toString());
QString content = textDocument->document()->toPlainText();
LOG_MESSAGE(QString("Got context from file: %1").arg(textDocument->filePath().toString()));
return QString("Current file context:\n%1\nFile content:\n%2").arg(fileInfo, content);
}
} // namespace QodeAssist::Chat

View File

@ -36,18 +36,18 @@ public:
explicit ClientInterface(ChatModel *chatModel, QObject *parent = nullptr);
~ClientInterface();
void sendMessage(const QString &message);
void sendMessage(const QString &message, bool includeCurrentFile = false);
void clearMessages();
void cancelRequest();
signals:
void messageReceived(const QString &message);
void errorOccurred(const QString &error);
private:
void handleLLMResponse(const QString &response, bool isComplete);
void handleLLMResponse(const QString &response, const QJsonObject &request, bool isComplete);
QString getCurrentFileContext() const;
LLMCore::RequestHandler *m_requestHandler;
QString m_accumulatedResponse;
ChatModel *m_chatModel;
};

111
ChatView/qml/ChatItem.qml Normal file
View File

@ -0,0 +1,111 @@
/*
* 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 ComponentBehavior: Bound
import QtQuick
import ChatView
import "./dialog"
Rectangle {
id: root
property alias msgModel: msgCreator.model
property color fontColor
property color codeBgColor
property color selectionColor
height: msgColumn.height
radius: 8
Column {
id: msgColumn
anchors.verticalCenter: parent.verticalCenter
width: parent.width
spacing: 5
Repeater {
id: msgCreator
delegate: Loader {
id: msgCreatorDelegate
// Fix me:
// why does `required property MessagePart modelData` not work?
required property var modelData
width: parent.width
sourceComponent: {
// If `required property MessagePart modelData` is used
// and conversion to MessagePart fails, you're left
// with a nullptr. This tests that to prevent crashing.
if(!modelData) {
return undefined;
}
switch(modelData.type) {
case MessagePart.Text: return textComponent;
case MessagePart.Code: return codeBlockComponent;
default: return textComponent;
}
}
Component {
id: textComponent
TextComponent {
itemData: msgCreatorDelegate.modelData
}
}
Component {
id: codeBlockComponent
CodeBlockComponent {
itemData: msgCreatorDelegate.modelData
}
}
}
}
}
component TextComponent : TextBlock {
required property var itemData
height: implicitHeight + 10
verticalAlignment: Text.AlignVCenter
leftPadding: 10
text: itemData.text
color: root.fontColor
selectionColor: root.selectionColor
}
component CodeBlockComponent : CodeBlock {
required property var itemData
anchors {
left: parent.left
leftMargin: 10
right: parent.right
rightMargin: 10
}
code: itemData.text
language: itemData.language
color: root.codeBgColor
selectionColor: root.selectionColor
}
}

View File

@ -17,6 +17,8 @@
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic as QQC
@ -51,6 +53,7 @@ ChatRootView {
cacheBuffer: 2000
delegate: ChatItem {
required property var model
width: ListView.view.width - scroll.width
msgModel: root.chatModel.processMessageContent(model.content)
color: model.roleType === ChatModel.User ? root.primaryColor : root.secondaryColor
@ -71,27 +74,26 @@ ChatRootView {
}
onCountChanged: {
scrollToBottom()
root.scrollToBottom()
}
onContentHeightChanged: {
if (atYEnd) {
scrollToBottom()
root.scrollToBottom()
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: 5
ScrollView {
id: view
QQC.TextField {
Layout.fillWidth: true
Layout.minimumHeight: 30
Layout.maximumHeight: root.height / 2
QQC.TextArea {
id: messageInput
Layout.fillWidth: true
Layout.minimumWidth: 60
Layout.minimumHeight: 30
rightInset: -(parent.width - sendButton.width - clearButton.width)
placeholderText: qsTr("Type your message here...")
placeholderTextColor: "#888"
color: root.primaryColor.hslLightness > 0.5 ? "black" : "white"
@ -102,31 +104,48 @@ ChatRootView {
: Qt.darker(root.primaryColor, 1.5)
border.width: 1
}
onAccepted: sendButton.clicked()
Keys.onPressed: function(event) {
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !(event.modifiers & Qt.ShiftModifier)) {
root.sendChatMessage()
event.accepted = true;
}
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: 5
Button {
id: sendButton
Layout.alignment: Qt.AlignVCenter
Layout.minimumHeight: 30
Layout.alignment: Qt.AlignBottom
text: qsTr("Send")
onClicked: {
if (messageInput.text.trim() !== "") {
root.sendMessage(messageInput.text);
messageInput.text = ""
scrollToBottom()
}
}
onClicked: root.sendChatMessage()
}
Button {
id: stopButton
Layout.alignment: Qt.AlignBottom
text: qsTr("Stop")
onClicked: root.cancelRequest()
}
Button {
id: clearButton
Layout.alignment: Qt.AlignVCenter
Layout.minimumHeight: 30
text: qsTr("Clear")
onClicked: clearChat()
Layout.alignment: Qt.AlignBottom
text: qsTr("Clear Chat")
onClicked: root.clearChat()
}
CheckBox {
id: sharingCurrentFile
text: "Share current file with models"
checked: root.isSharingCurrentFile
}
}
}
@ -145,7 +164,7 @@ ChatRootView {
spacing: 10
Badge {
text: "%1/%2".arg(root.chatModel.totalTokens).arg(root.chatModel.tokensThreshold)
text: qsTr("tokens:%1/%2").arg(root.chatModel.totalTokens).arg(root.chatModel.tokensThreshold)
color: root.codeColor
fontColor: root.primaryColor.hslLightness > 0.5 ? "black" : "white"
}
@ -158,4 +177,10 @@ ChatRootView {
function scrollToBottom() {
Qt.callLater(chatListView.positionViewAtEnd)
}
function sendChatMessage() {
root.sendMessage(messageInput.text, sharingCurrentFile.checked)
messageInput.text = ""
scrollToBottom()
}
}

View File

@ -28,6 +28,19 @@ Rectangle {
property string language: ""
property color selectionColor
readonly property string monospaceFont: {
switch (Qt.platform.os) {
case "windows":
return "Consolas";
case "osx":
return "Menlo";
case "linux":
return "DejaVu Sans Mono";
default:
return "monospace";
}
}
border.color: root.color.hslLightness > 0.5 ? Qt.darker(root.color, 1.3)
: Qt.lighter(root.color, 1.3)
border.width: 2
@ -48,7 +61,8 @@ Rectangle {
text: root.code
readOnly: true
selectByMouse: true
font.family: "monospace"
font.family: root.monospaceFont
font.pointSize: 12
color: parent.color.hslLightness > 0.5 ? "black" : "white"
wrapMode: Text.WordWrap
selectionColor: root.selectionColor

162
ConfigurationManager.cpp Normal file
View File

@ -0,0 +1,162 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ConfigurationManager.hpp"
#include <QTimer>
#include <settings/ButtonAspect.hpp>
#include "QodeAssisttr.h"
namespace QodeAssist {
ConfigurationManager &ConfigurationManager::instance()
{
static ConfigurationManager instance;
return instance;
}
void ConfigurationManager::init()
{
setupConnections();
}
ConfigurationManager::ConfigurationManager(QObject *parent)
: QObject(parent)
, m_generalSettings(Settings::generalSettings())
, m_providersManager(LLMCore::ProvidersManager::instance())
, m_templateManger(LLMCore::PromptTemplateManager::instance())
{}
void ConfigurationManager::setupConnections()
{
using Config = ConfigurationManager;
using Button = ButtonAspect;
connect(&m_generalSettings.ccSelectProvider, &Button::clicked, this, &Config::selectProvider);
connect(&m_generalSettings.caSelectProvider, &Button::clicked, this, &Config::selectProvider);
connect(&m_generalSettings.ccSelectModel, &Button::clicked, this, &Config::selectModel);
connect(&m_generalSettings.caSelectModel, &Button::clicked, this, &Config::selectModel);
connect(&m_generalSettings.ccSelectTemplate, &Button::clicked, this, &Config::selectTemplate);
connect(&m_generalSettings.caSelectTemplate, &Button::clicked, this, &Config::selectTemplate);
connect(&m_generalSettings.ccSetUrl, &Button::clicked, this, &Config::selectUrl);
connect(&m_generalSettings.caSetUrl, &Button::clicked, this, &Config::selectUrl);
}
void ConfigurationManager::selectProvider()
{
const auto providersList = m_providersManager.providersNames();
auto *settingsButton = qobject_cast<ButtonAspect *>(sender());
if (!settingsButton)
return;
auto &targetSettings = (settingsButton == &m_generalSettings.ccSelectProvider)
? m_generalSettings.ccProvider
: m_generalSettings.caProvider;
QTimer::singleShot(0, this, [this, providersList, &targetSettings] {
m_generalSettings.showSelectionDialog(providersList,
targetSettings,
Tr::tr("Select LLM Provider"),
Tr::tr("Providers:"));
});
}
void ConfigurationManager::selectModel()
{
auto *settingsButton = qobject_cast<ButtonAspect *>(sender());
if (!settingsButton)
return;
const bool isCodeCompletion = (settingsButton == &m_generalSettings.ccSelectModel);
const QString providerName = isCodeCompletion ? m_generalSettings.ccProvider.volatileValue()
: m_generalSettings.caProvider.volatileValue();
const auto providerUrl = isCodeCompletion ? m_generalSettings.ccUrl.volatileValue()
: m_generalSettings.caUrl.volatileValue();
auto &targetSettings = isCodeCompletion ? m_generalSettings.ccModel : m_generalSettings.caModel;
if (auto provider = m_providersManager.getProviderByName(providerName)) {
if (!provider->supportsModelListing()) {
m_generalSettings.showModelsNotSupportedDialog(targetSettings);
return;
}
const auto modelList = provider->getInstalledModels(providerUrl);
if (modelList.isEmpty()) {
m_generalSettings.showModelsNotFoundDialog(targetSettings);
return;
}
QTimer::singleShot(0, &m_generalSettings, [this, modelList, &targetSettings]() {
m_generalSettings.showSelectionDialog(
modelList, targetSettings, Tr::tr("Select LLM Model"), Tr::tr("Models:"));
});
}
}
void ConfigurationManager::selectTemplate()
{
auto *settingsButton = qobject_cast<ButtonAspect *>(sender());
if (!settingsButton)
return;
const bool isCodeCompletion = (settingsButton == &m_generalSettings.ccSelectTemplate);
const auto templateList = isCodeCompletion ? m_templateManger.fimTemplatesNames()
: m_templateManger.chatTemplatesNames();
auto &targetSettings = isCodeCompletion ? m_generalSettings.ccTemplate
: m_generalSettings.caTemplate;
QTimer::singleShot(0, &m_generalSettings, [this, templateList, &targetSettings]() {
m_generalSettings.showSelectionDialog(templateList,
targetSettings,
Tr::tr("Select Template"),
Tr::tr("Templates:"));
});
}
void ConfigurationManager::selectUrl()
{
auto *settingsButton = qobject_cast<ButtonAspect *>(sender());
if (!settingsButton)
return;
QStringList urls;
for (const auto &name : m_providersManager.providersNames()) {
const auto url = m_providersManager.getProviderByName(name)->url();
if (!urls.contains(url))
urls.append(url);
}
auto &targetSettings = (settingsButton == &m_generalSettings.ccSetUrl)
? m_generalSettings.ccUrl
: m_generalSettings.caUrl;
QTimer::singleShot(0, &m_generalSettings, [this, urls, &targetSettings]() {
m_generalSettings.showUrlSelectionDialog(targetSettings, urls);
});
}
} // namespace QodeAssist

58
ConfigurationManager.hpp Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QObject>
#include "llmcore/PromptTemplateManager.hpp"
#include "llmcore/ProvidersManager.hpp"
#include "settings/GeneralSettings.hpp"
namespace QodeAssist {
class ConfigurationManager : public QObject
{
Q_OBJECT
public:
static ConfigurationManager &instance();
void init();
public slots:
void selectProvider();
void selectModel();
void selectTemplate();
void selectUrl();
private:
explicit ConfigurationManager(QObject *parent = nullptr);
~ConfigurationManager() = default;
ConfigurationManager(const ConfigurationManager &) = delete;
ConfigurationManager &operator=(const ConfigurationManager &) = delete;
Settings::GeneralSettings &m_generalSettings;
LLMCore::ProvidersManager &m_providersManager;
LLMCore::PromptTemplateManager &m_templateManger;
void setupConnections();
};
} // namespace QodeAssist

View File

@ -24,7 +24,7 @@
#include <languageserverprotocol/lsptypes.h>
#include "core/ChangesManager.h"
#include "settings/ContextSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
const QRegularExpression &getYearRegex()
{
@ -207,18 +207,24 @@ LLMCore::ContextData DocumentContextReader::prepareContext(int lineNumber, int c
{
QString contextBefore = getContextBefore(lineNumber, cursorPosition);
QString contextAfter = getContextAfter(lineNumber, cursorPosition);
QString instructions = getInstructions();
return {contextBefore, contextAfter, instructions};
QString fileContext;
if (Settings::codeCompletionSettings().useFilePathInContext())
fileContext += getLanguageAndFileInfo();
if (Settings::codeCompletionSettings().useProjectChangesCache())
fileContext += ChangesManager::instance().getRecentChangesContext(m_textDocument);
return {contextBefore, contextAfter, fileContext};
}
QString DocumentContextReader::getContextBefore(int lineNumber, int cursorPosition) const
{
if (Settings::contextSettings().readFullFile()) {
if (Settings::codeCompletionSettings().readFullFile()) {
return readWholeFileBefore(lineNumber, cursorPosition);
} else {
int effectiveStartLine;
int beforeCursor = Settings::contextSettings().readStringsBeforeCursor();
int beforeCursor = Settings::codeCompletionSettings().readStringsBeforeCursor();
if (m_copyrightInfo.found) {
effectiveStartLine = qMax(m_copyrightInfo.endLine + 1, lineNumber - beforeCursor);
} else {
@ -230,26 +236,13 @@ QString DocumentContextReader::getContextBefore(int lineNumber, int cursorPositi
QString DocumentContextReader::getContextAfter(int lineNumber, int cursorPosition) const
{
if (Settings::contextSettings().readFullFile()) {
if (Settings::codeCompletionSettings().readFullFile()) {
return readWholeFileAfter(lineNumber, cursorPosition);
} else {
int endLine = qMin(m_document->blockCount() - 1,
lineNumber + Settings::contextSettings().readStringsAfterCursor());
lineNumber + Settings::codeCompletionSettings().readStringsAfterCursor());
return getContextBetween(lineNumber + 1, endLine, -1);
}
}
QString DocumentContextReader::getInstructions() const
{
QString instructions;
if (Settings::contextSettings().useFilePathInContext())
instructions += getLanguageAndFileInfo();
if (Settings::contextSettings().useProjectChangesCache())
instructions += ChangesManager::instance().getRecentChangesContext(m_textDocument);
return instructions;
}
} // namespace QodeAssist

View File

@ -54,7 +54,6 @@ public:
private:
QString getContextBefore(int lineNumber, int cursorPosition) const;
QString getContextAfter(int lineNumber, int cursorPosition) const;
QString getInstructions() const;
private:
TextEditor::TextDocument *m_textDocument;

View File

@ -27,10 +27,10 @@
#include <texteditor/textdocument.h>
#include "DocumentContextReader.hpp"
#include "logger/Logger.hpp"
#include "llmcore/PromptTemplateManager.hpp"
#include "llmcore/ProvidersManager.hpp"
#include "settings/ContextSettings.hpp"
#include "logger/Logger.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/GeneralSettings.hpp"
namespace QodeAssist {
@ -147,22 +147,47 @@ void LLMClientInterface::handleExit(const QJsonObject &request)
void LLMClientInterface::handleCompletion(const QJsonObject &request)
{
auto updatedContext = prepareContext(request);
auto &completeSettings = Settings::codeCompletionSettings();
auto providerName = Settings::generalSettings().ccProvider();
auto provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName);
if (!provider) {
LOG_MESSAGE(QString("No provider found with name: %1").arg(providerName));
return;
}
auto templateName = Settings::generalSettings().ccTemplate();
auto promptTemplate = LLMCore::PromptTemplateManager::instance().getFimTemplateByName(
templateName);
if (!promptTemplate) {
LOG_MESSAGE(QString("No template found with name: %1").arg(templateName));
return;
}
LLMCore::LLMConfig config;
config.requestType = LLMCore::RequestType::Fim;
config.provider = LLMCore::ProvidersManager::instance().getCurrentFimProvider();
config.promptTemplate = LLMCore::PromptTemplateManager::instance().getCurrentFimTemplate();
config.url = QUrl(QString("%1%2").arg(Settings::generalSettings().url(),
Settings::generalSettings().endPoint()));
config.provider = provider;
config.promptTemplate = promptTemplate;
config.url = QUrl(
QString("%1%2").arg(Settings::generalSettings().ccUrl(), provider->completionEndpoint()));
config.providerRequest = {{"model", Settings::generalSettings().modelName.value()},
{"stream", true},
{"stop",
QJsonArray::fromStringList(config.promptTemplate->stopWords())}};
config.multiLineCompletion = Settings::generalSettings().multiLineCompletion();
config.providerRequest = {{"model", Settings::generalSettings().ccModel()}, {"stream", true}};
if (Settings::contextSettings().useSystemPrompt())
config.providerRequest["system"] = Settings::contextSettings().systemPrompt();
config.multiLineCompletion = completeSettings.multiLineCompletion();
QString systemPrompt;
if (completeSettings.useSystemPrompt())
systemPrompt.append(completeSettings.systemPrompt());
if (!updatedContext.fileContext.isEmpty())
systemPrompt.append(updatedContext.fileContext);
if (!systemPrompt.isEmpty())
config.providerRequest["system"] = systemPrompt;
const auto stopWords = QJsonArray::fromStringList(config.promptTemplate->stopWords());
if (!stopWords.isEmpty())
config.providerRequest["stop"] = stopWords;
config.promptTemplate->prepareRequest(config.providerRequest, updatedContext);
config.provider->prepareRequest(config.providerRequest, LLMCore::RequestType::Fim);

View File

@ -1,6 +1,6 @@
{
"Name" : "QodeAssist",
"Version" : "0.3.0",
"Version" : "0.3.9",
"CompatVersion" : "${IDE_VERSION_COMPAT}",
"Vendor" : "Petr Mironychev",
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",

View File

@ -32,7 +32,7 @@
#include "LLMClientInterface.hpp"
#include "LLMSuggestion.hpp"
#include "core/ChangesManager.h"
#include "settings/ContextSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/GeneralSettings.hpp"
using namespace LanguageServerProtocol;
@ -75,7 +75,7 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document)
this,
[this, document](int position, int charsRemoved, int charsAdded) {
Q_UNUSED(charsRemoved)
if (!Settings::generalSettings().enableAutoComplete())
if (!Settings::codeCompletionSettings().autoCompletion())
return;
auto project = ProjectManager::projectForFile(document->filePath());
@ -86,7 +86,7 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document)
if (!textEditor || textEditor->document() != document)
return;
if (Settings::contextSettings().useProjectChangesCache())
if (Settings::codeCompletionSettings().useProjectChangesCache())
ChangesManager::instance().addChange(document,
position,
charsRemoved,
@ -102,12 +102,13 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document)
m_recentCharCount += charsAdded;
if (m_typingTimer.elapsed()
> Settings::generalSettings().autoCompletionTypingInterval()) {
> Settings::codeCompletionSettings().autoCompletionTypingInterval()) {
m_recentCharCount = charsAdded;
m_typingTimer.restart();
}
if (m_recentCharCount > Settings::generalSettings().autoCompletionCharThreshold()) {
if (m_recentCharCount
> Settings::codeCompletionSettings().autoCompletionCharThreshold()) {
scheduleRequest(widget);
}
});
@ -154,7 +155,8 @@ void QodeAssistClient::scheduleRequest(TextEditor::TextEditorWidget *editor)
if (editor
&& editor->textCursor().position()
== m_scheduledRequests[editor]->property("cursorPosition").toInt()
&& m_recentCharCount > Settings::generalSettings().autoCompletionCharThreshold())
&& m_recentCharCount
> Settings::codeCompletionSettings().autoCompletionCharThreshold())
requestCompletions(editor);
});
connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() {
@ -168,7 +170,7 @@ void QodeAssistClient::scheduleRequest(TextEditor::TextEditorWidget *editor)
}
it.value()->setProperty("cursorPosition", editor->textCursor().position());
it.value()->start(Settings::generalSettings().startSuggestionTimer());
it.value()->start(Settings::codeCompletionSettings().startSuggestionTimer());
}
void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &response,
TextEditor::TextEditorWidget *editor)

187
README.md
View File

@ -1,87 +1,74 @@
# QodeAssist
# QodeAssist - AI-powered coding assistant plugin for Qt Creator
[![Discord](https://dcbadge.limes.pink/api/server/https://discord.gg/DGgMtTteAD?style=flat?theme=discord)](https://discord.gg/DGgMtTteAD)
[![Build plugin](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml/badge.svg?branch=main)](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml)
![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/Palm1r/QodeAssist/total?color=41%2C173%2C71)
![GitHub Tag](https://img.shields.io/github/v/tag/Palm1r/QodeAssist)
![Static Badge](https://img.shields.io/badge/QtCreator-14.0.2-brightgreen)
QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides intelligent code completion and suggestions for C++ and QML, leveraging large language models through local providers like Ollama. Enhance your coding productivity with context-aware AI assistance directly in your Qt development environment.
## Table of Contents
1. [Overview](#overview)
2. [Installation](#installation)
3. [Configure Plugin](#configure-plugin)
4. [Supported LLM Providers](#supported-llm-providers)
5. [Recommended Models](#recommended-models)
- [Ollama](#ollama)
- [LM Studio](#lm-studio)
6. [QtCreator Version Compatibility](#qtcreator-version-compatibility)
7. [Development Progress](#development-progress)
8. [Hotkeys](#hotkeys)
9. [Troubleshooting](#troubleshooting)
10. [Support the Development](#support-the-development-of-qodeassist)
11. [How to Build](#how-to-build)
<img src="https://github.com/user-attachments/assets/255a52f1-5cc0-4ca3-b05c-c4cf9cdbe25a" width="600" alt="QodeAssistPreview">
## Overview
## Supported LLM Providers
QodeAssist currently supports the following LLM (Large Language Model) providers:
- [Ollama](https://ollama.com)
- [LM Studio](https://lmstudio.ai)
- OpenAI compatible providers
- AI-powered code completion
- Chat functionality:
- Side and Bottom panels
- Support for multiple LLM providers:
- Ollama
- LM Studio (experimental)
- OpenAI-compatible providers (experimental)
- Extensive library of model-specific templates
- Custom template support
- Easy configuration and model selection
## QtCreator Version Compatibility
<details>
<summary>Code completion: (click to expand)</summary>
<img src="https://github.com/user-attachments/assets/255a52f1-5cc0-4ca3-b05c-c4cf9cdbe25a" width="600" alt="QodeAssistPreview">
</details>
- Since version 0.2.3: Compatible with QtCreator 14.0.2
- QtCreator 14.0.1 and below are not supported anymore (latest compatible version: 0.2.2)
<details>
<summary>Chat with LLM models in side panels: (click to expand)</summary>
<img src="https://github.com/user-attachments/assets/ead5a5d9-b40a-4f17-af05-77fa2bcb3a61" width="600" alt="QodeAssistChat">
</details>
## Supported Models
<details>
<summary>Chat with LLM models in bottom panel: (click to expand)</summary>
<img width="326" alt="QodeAssistBottomPanel" src="https://github.com/user-attachments/assets/4cc64c23-a294-4df8-9153-39ad6fdab34b">
</details>
QodeAssist has been thoroughly tested and optimized for use with the following language models, all of which are specifically trained for Fill-in-the-Middle (FIM) tasks:
## Installation
- CodeLlama
- StarCoder2
- DeepSeek-Coder-V2-Lite-Base
These models have demonstrated excellent performance in code completion and assistance tasks within the QodeAssist environment.
### Custom Prompts
For advanced users or those with specific requirements, QodeAssist offers the flexibility to create, save, and load custom prompts using JSON templates. This feature allows you to tailor the AI's behavior to your exact needs.
To get started with custom prompts:
1. Navigate to the "Custom Template" option in the FIM Prompt Settings.
2. Create your custom JSON prompt template.
3. Use the "Save Custom Template to JSON" button to store your template for future use.
4. To use a previously saved template, click "Load Custom Template from JSON".
5. Make sure to select "Custom Template" from the dropdown menu in the FIM Prompt Settings on the General page to activate your custom template.
For inspiration and examples of effective custom prompts, please refer to the `rawPromptExamples` folder in our repository.
<img width="600" alt="Custom template" src="https://github.com/user-attachments/assets/4a14c552-baba-4531-ab4f-cb1f9ac6620b">
<img width="600" alt="Select custom template" src="https://github.com/user-attachments/assets/3651dafd-83f9-4df9-943f-69c28cd3d8a3">
### Tested Models
#### Ollama:
- [starcoder2](https://ollama.com/library/starcoder2)
- [codellama](https://ollama.com/library/codellama)
#### LM Studio:
- [second-state/StarCoder2-7B-GGUF](https://huggingface.co/second-state/StarCoder2-7B-GGUF)
- [TheBloke/CodeLlama-7B-GGUF](https://huggingface.co/TheBloke/CodeLlama-7B-GGUF)
Please note that while these models have been specifically tested and confirmed to work well with QodeAssist, other models compatible with the supported providers may also work. We encourage users to experiment with different models and report their experiences.
If you've successfully used a model that's not listed here, please let us know by opening an issue or submitting a pull request to update this list.
## Development Progress
- [x] Basic plugin with code autocomplete functionality
- [x] Improve and automate settings
- [ ] Add chat functionality
- [x] Sharing diff with model
- [ ] Sharing project source with model
- [ ] Support for more providers and models
## Plugin installation using Ollama as an example
1. Install QtCreator 14.0
1. Install Latest QtCreator
2. Install [Ollama](https://ollama.com). Make sure to review the system requirements before installation.
3. Install a language models in Ollama. For example, you can run:
3. Install a language models in Ollama via terminal. For example, you can run:
For suggestions:
For standard computers (minimum 8GB RAM):
```
ollama run codellama:7b-code
ollama run qwen2.5-coder:7b
```
For chat:
For better performance (16GB+ RAM):
```
ollama run codellama:7b-instruct
ollama run qwen2.5-coder:14b
```
4. Download the QodeAssist plugin.
For high-end systems (32GB+ RAM):
```
ollama run qwen2.5-coder:32b
```
4. Download the QodeAssist plugin for your QtCreator.
5. Launch Qt Creator and install the plugin:
- Go to MacOS: Qt Creator -> About Plugins...
Windows\Linux: Help -> About Plugins...
@ -90,19 +77,63 @@ ollama run codellama:7b-instruct
## Configure Plugin
<img src="https://github.com/user-attachments/assets/0743d09e-1f02-44ed-9a1a-85e2a0a0c01a" width="800" alt="QodeAssist в действии">
QodeAssist comes with default settings that should work immediately after installing a language model. The plugin is pre-configured to use Ollama with standard templates, so you may only need to verify the settings.
1. Open Qt Creator settings
1. Open Qt Creator settings (Edit > Preferences on Linux/Windows, Qt Creator > Preferences on macOS)
2. Navigate to the "Qode Assist" tab
3. Select "General" page
4. Choose your LLM provider (e.g., Ollama)
5. Select the installed model by the "Select Model" button
- For LM Studio you will see current load model
6. Choose the prompt template that corresponds to your model
7. Apply the settings
3. On the "General" page, verify:
- Ollama is selected as your LLM provider
- The URL is set to http://localhost:11434
- Your installed model appears in the model selection
- The prompt template is Ollama Auto FIM
4. Click Apply if you made any changes
You're all set! QodeAssist is now ready to use in Qt Creator.
## Supported LLM Providers
QodeAssist currently supports the following LLM (Large Language Model) providers:
- [Ollama](https://ollama.com)
- [LM Studio](https://lmstudio.ai) (experimental)
- OpenAI compatible providers (experimental)
## Recommended Models:
QodeAssist has been thoroughly tested and optimized for use with the following language models:
- Qwen2.5-coder
- CodeLlama
- StarCoder2
- DeepSeek-Coder-V2
### Ollama:
### For autocomplete(FIM)
```
ollama run codellama:7b-code
ollama run starcoder2:7b
ollama run qwen2.5-coder:7b-base
ollama run deepseek-coder-v2:16b-lite-base-q3_K_M
```
### For chat
```
ollama run codellama:7b-instruct
ollama run starcoder2:instruct
ollama run qwen2.5-coder:7b-instruct
ollama run deepseek-coder-v2
```
## QtCreator Version Compatibility
- QtCreator 14.0.2 - 0.2.3 - 0.3.x
- QtCreator 14.0.1 - 0.2.2 plugin version and below
## Development Progress
- [x] Basic plugin with code autocomplete functionality
- [x] Improve and automate settings
- [x] Add chat functionality
- [x] Sharing diff with model
- [ ] Sharing project source with model
- [ ] Support for more providers and models
## Hotkeys
- To call manual request to suggestion, you can use or change it in settings
@ -151,7 +182,6 @@ If you find QodeAssist helpful, there are several ways you can support the proje
3. **Spread the Word**: Star our GitHub repository and share QodeAssist with your fellow developers.
4. **Financial Support**: If you'd like to support the development financially, you can make a donation using one of the following:
- [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/P5P412V96G)
- Bitcoin (BTC): `bc1qndq7f0mpnlya48vk7kugvyqj5w89xrg4wzg68t`
- Ethereum (ETH): `0xA5e8c37c94b24e25F9f1f292a01AF55F03099D8D`
- Litecoin (LTC): `ltc1qlrxnk30s2pcjchzx4qrxvdjt5gzuervy5mv0vy`
@ -170,3 +200,8 @@ where `<path_to_qtcreator>` is the relative or absolute path to a Qt Creator bui
combined binary and development package (Windows / Linux), or to the `Qt Creator.app/Contents/Resources/`
directory of a combined binary and development package (macOS), and `<path_to_plugin_source>` is the
relative or absolute path to this plugin directory.
## For Contributors
QML code style: Preferably follow the following guidelines https://github.com/Furkanzmc/QML-Coding-Guide, thank you @Furkanzmc for collect them
C++ code style: check use .clang-fortmat in project

View File

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

View File

@ -19,7 +19,7 @@
#include "NavigationPanel.hpp"
#include "chatview/ChatWidget.hpp"
#include "ChatView/ChatWidget.hpp"
namespace QodeAssist::Chat {

View File

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

View File

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

View File

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

View File

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

View File

@ -18,8 +18,7 @@
*/
#include "ChangesManager.h"
#include "logger/Logger.hpp"
#include "settings/ContextSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
namespace QodeAssist {
@ -61,7 +60,7 @@ void ChangesManager::addChange(TextEditor::TextDocument *document,
} else {
documentQueue.enqueue(change);
if (documentQueue.size() > Settings::contextSettings().maxChangesCacheSize()) {
if (documentQueue.size() > Settings::codeCompletionSettings().maxChangesCacheSize()) {
documentQueue.dequeue();
}
}

View File

@ -27,7 +27,7 @@ struct ContextData
{
QString prefix;
QString suffix;
QString systemPrompt;
QString fileContext;
};
} // namespace QodeAssist::LLMCore

View File

@ -19,8 +19,6 @@
#include "PromptTemplateManager.hpp"
#include "Logger.hpp"
namespace QodeAssist::LLMCore {
PromptTemplateManager &PromptTemplateManager::instance()
@ -29,46 +27,6 @@ PromptTemplateManager &PromptTemplateManager::instance()
return instance;
}
void PromptTemplateManager::setCurrentFimTemplate(const QString &name)
{
LOG_MESSAGE("Setting current FIM provider to: " + name);
if (!m_fimTemplates.contains(name) || m_fimTemplates[name] == nullptr) {
LOG_MESSAGE("Error to set current FIM template" + name);
return;
}
m_currentFimTemplate = m_fimTemplates[name];
}
PromptTemplate *PromptTemplateManager::getCurrentFimTemplate()
{
if (m_currentFimTemplate == nullptr) {
LOG_MESSAGE("Current fim provider is null");
return nullptr;
}
return m_currentFimTemplate;
}
void PromptTemplateManager::setCurrentChatTemplate(const QString &name)
{
LOG_MESSAGE("Setting current chat provider to: " + name);
if (!m_chatTemplates.contains(name) || m_chatTemplates[name] == nullptr) {
LOG_MESSAGE("Error to set current chat template" + name);
return;
}
m_currentChatTemplate = m_chatTemplates[name];
}
PromptTemplate *PromptTemplateManager::getCurrentChatTemplate()
{
if (m_currentChatTemplate == nullptr)
LOG_MESSAGE("Current chat provider is null");
return m_currentChatTemplate;
}
QStringList PromptTemplateManager::fimTemplatesNames() const
{
return m_fimTemplates.keys();
@ -85,4 +43,14 @@ PromptTemplateManager::~PromptTemplateManager()
qDeleteAll(m_chatTemplates);
}
PromptTemplate *PromptTemplateManager::getFimTemplateByName(const QString &templateName)
{
return m_fimTemplates[templateName];
}
PromptTemplate *PromptTemplateManager::getChatTemplateByName(const QString &templateName)
{
return m_chatTemplates[templateName];
}
} // namespace QodeAssist::LLMCore

View File

@ -46,11 +46,8 @@ public:
}
}
void setCurrentFimTemplate(const QString &name);
PromptTemplate *getCurrentFimTemplate();
void setCurrentChatTemplate(const QString &name);
PromptTemplate *getCurrentChatTemplate();
PromptTemplate *getFimTemplateByName(const QString &templateName);
PromptTemplate *getChatTemplateByName(const QString &templateName);
QStringList fimTemplatesNames() const;
QStringList chatTemplatesNames() const;
@ -62,8 +59,6 @@ private:
QMap<QString, PromptTemplate *> m_fimTemplates;
QMap<QString, PromptTemplate *> m_chatTemplates;
PromptTemplate *m_currentFimTemplate;
PromptTemplate *m_currentChatTemplate;
};
} // namespace QodeAssist::LLMCore

View File

@ -37,10 +37,11 @@ public:
virtual QString url() const = 0;
virtual QString completionEndpoint() const = 0;
virtual QString chatEndpoint() const = 0;
virtual bool supportsModelListing() const = 0;
virtual void prepareRequest(QJsonObject &request, RequestType type) = 0;
virtual bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) = 0;
virtual QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) = 0;
virtual QList<QString> getInstalledModels(const QString &url) = 0;
};
} // namespace QodeAssist::LLMCore

View File

@ -18,8 +18,6 @@
*/
#include "ProvidersManager.hpp"
#include "Logger.hpp"
#include <coreplugin/messagemanager.h>
namespace QodeAssist::LLMCore {
@ -29,50 +27,6 @@ ProvidersManager &ProvidersManager::instance()
return instance;
}
Provider *ProvidersManager::setCurrentFimProvider(const QString &name)
{
LOG_MESSAGE("Setting current FIM provider to: " + name);
if (!m_providers.contains(name)) {
LOG_MESSAGE("Can't find provider with name: " + name);
return nullptr;
}
m_currentFimProvider = m_providers[name];
return m_currentFimProvider;
}
Provider *ProvidersManager::setCurrentChatProvider(const QString &name)
{
LOG_MESSAGE("Setting current chat provider to: " + name);
if (!m_providers.contains(name)) {
LOG_MESSAGE("Can't find chat provider with name: " + name);
return nullptr;
}
m_currentChatProvider = m_providers[name];
return m_currentChatProvider;
}
Provider *ProvidersManager::getCurrentFimProvider()
{
if (m_currentFimProvider == nullptr) {
LOG_MESSAGE("Current fim provider is null");
return nullptr;
}
return m_currentFimProvider;
}
Provider *ProvidersManager::getCurrentChatProvider()
{
if (m_currentChatProvider == nullptr) {
LOG_MESSAGE("Current chat provider is null");
return nullptr;
}
return m_currentChatProvider;
}
QStringList ProvidersManager::providersNames() const
{
return m_providers.keys();
@ -83,4 +37,9 @@ ProvidersManager::~ProvidersManager()
qDeleteAll(m_providers);
}
Provider *ProvidersManager::getProviderByName(const QString &providerName)
{
return m_providers[providerName];
}
} // namespace QodeAssist::LLMCore

View File

@ -21,6 +21,7 @@
#include <QString>
#include <QMap>
#include "Provider.hpp"
namespace QodeAssist::LLMCore {
@ -40,11 +41,7 @@ public:
m_providers[name] = provider;
}
Provider *setCurrentFimProvider(const QString &name);
Provider *setCurrentChatProvider(const QString &name);
Provider *getCurrentFimProvider();
Provider *getCurrentChatProvider();
Provider *getProviderByName(const QString &providerName);
QStringList providersNames() const;
@ -54,8 +51,6 @@ private:
ProvidersManager &operator=(const ProvidersManager &) = delete;
QMap<QString, Provider *> m_providers;
Provider *m_currentFimProvider = nullptr;
Provider *m_currentChatProvider = nullptr;
};
} // namespace QodeAssist::LLMCore

View File

@ -80,22 +80,18 @@ void RequestHandler::handleLLMResponse(QNetworkReply *reply,
&& processSingleLineCompletion(reply, request, accumulatedResponse, config)) {
return;
}
if (isComplete) {
auto cleanedCompletion = removeStopWords(accumulatedResponse,
config.promptTemplate->stopWords());
emit completionReceived(cleanedCompletion, request, true);
}
} else if (config.requestType == RequestType::Chat) {
emit completionReceived(accumulatedResponse, request, isComplete);
}
if (isComplete || reply->isFinished()) {
if (isComplete) {
if (config.requestType == RequestType::Fim) {
auto cleanedCompletion = removeStopWords(accumulatedResponse,
config.promptTemplate->stopWords());
emit completionReceived(cleanedCompletion, request, true);
} else {
emit completionReceived(accumulatedResponse, request, true);
}
} else {
emit completionReceived(accumulatedResponse, request, false);
}
if (isComplete)
m_accumulatedResponses.remove(reply);
}
}
bool RequestHandler::cancelRequest(const QString &id)

View File

@ -1,3 +1,22 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
namespace QodeAssist::LLMCore {

View File

@ -26,7 +26,8 @@
#include <QNetworkReply>
#include "logger/Logger.hpp"
#include "settings/PresetPromptsSettings.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
namespace QodeAssist::Providers {
@ -52,38 +53,50 @@ QString LMStudioProvider::chatEndpoint() const
return "/v1/chat/completions";
}
bool LMStudioProvider::supportsModelListing() const
{
return true;
}
void LMStudioProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
{
auto &promptSettings = Settings::presetPromptsSettings();
auto settings = promptSettings.getSettings(type);
auto prepareMessages = [](QJsonObject &req) -> QJsonArray {
QJsonArray messages;
if (req.contains("system")) {
messages.append(
QJsonObject{{"role", "system"}, {"content", req.take("system").toString()}});
}
if (req.contains("prompt")) {
messages.append(
QJsonObject{{"role", "user"}, {"content", req.take("prompt").toString()}});
}
return messages;
};
QJsonArray messages;
auto applyModelParams = [&request](const auto &settings) {
request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings.temperature();
if (request.contains("system")) {
QJsonObject systemMessage{{"role", "system"},
{"content", request.take("system").toString()}};
messages.append(systemMessage);
}
if (request.contains("prompt")) {
QJsonObject userMessage{{"role", "user"}, {"content", request.take("prompt").toString()}};
messages.append(userMessage);
}
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useTopK())
request["top_k"] = settings.topK();
if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
request["presence_penalty"] = settings.presencePenalty();
};
QJsonArray messages = prepareMessages(request);
if (!messages.isEmpty()) {
request["messages"] = std::move(messages);
}
request["max_tokens"] = settings.maxTokens;
request["temperature"] = settings.temperature;
if (settings.useTopP)
request["top_p"] = settings.topP;
if (settings.useTopK)
request["top_k"] = settings.topK;
if (settings.useFrequencyPenalty)
request["frequency_penalty"] = settings.frequencyPenalty;
if (settings.usePresencePenalty)
request["presence_penalty"] = settings.presencePenalty;
if (type == LLMCore::RequestType::Fim) {
applyModelParams(Settings::codeCompletionSettings());
} else {
applyModelParams(Settings::chatAssistantSettings());
}
}
bool LMStudioProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
@ -127,8 +140,7 @@ bool LMStudioProvider::handleResponse(QNetworkReply *reply, QString &accumulated
return isComplete;
}
QList<QString> LMStudioProvider::getInstalledModels(const Utils::Environment &env,
const QString &url)
QList<QString> LMStudioProvider::getInstalledModels(const QString &url)
{
QList<QString> models;
QNetworkAccessManager manager;

View File

@ -32,9 +32,10 @@ public:
QString url() const override;
QString completionEndpoint() const override;
QString chatEndpoint() const override;
bool supportsModelListing() const override;
void prepareRequest(QJsonObject &request, LLMCore::RequestType type) override;
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) override;
QList<QString> getInstalledModels(const QString &url) override;
};
} // namespace QodeAssist::Providers

View File

@ -25,9 +25,9 @@
#include <QNetworkReply>
#include <QtCore/qeventloop.h>
#include "llmcore/PromptTemplateManager.hpp"
#include "logger/Logger.hpp"
#include "settings/PresetPromptsSettings.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
namespace QodeAssist::Providers {
@ -53,24 +53,36 @@ QString OllamaProvider::chatEndpoint() const
return "/api/chat";
}
bool OllamaProvider::supportsModelListing() const
{
return true;
}
void OllamaProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
{
auto &promptSettings = Settings::presetPromptsSettings();
auto settings = promptSettings.getSettings(type);
auto applySettings = [&request](const auto &settings) {
QJsonObject options;
options["num_predict"] = settings.maxTokens();
options["temperature"] = settings.temperature();
QJsonObject options;
options["num_predict"] = settings.maxTokens;
options["temperature"] = settings.temperature;
if (settings.useTopP)
options["top_p"] = settings.topP;
if (settings.useTopK)
options["top_k"] = settings.topK;
if (settings.useFrequencyPenalty)
options["frequency_penalty"] = settings.frequencyPenalty;
if (settings.usePresencePenalty)
options["presence_penalty"] = settings.presencePenalty;
request["options"] = options;
request["keep_alive"] = settings.ollamaLivetime;
if (settings.useTopP())
options["top_p"] = settings.topP();
if (settings.useTopK())
options["top_k"] = settings.topK();
if (settings.useFrequencyPenalty())
options["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
options["presence_penalty"] = settings.presencePenalty();
request["options"] = options;
request["keep_alive"] = settings.ollamaLivetime();
};
if (type == LLMCore::RequestType::Fim) {
applySettings(Settings::codeCompletionSettings());
} else {
applySettings(Settings::chatAssistantSettings());
}
}
bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
@ -124,7 +136,7 @@ bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedRe
return isComplete;
}
QList<QString> OllamaProvider::getInstalledModels(const Utils::Environment &env, const QString &url)
QList<QString> OllamaProvider::getInstalledModels(const QString &url)
{
QList<QString> models;
QNetworkAccessManager manager;

View File

@ -32,9 +32,10 @@ public:
QString url() const override;
QString completionEndpoint() const override;
QString chatEndpoint() const override;
bool supportsModelListing() const override;
void prepareRequest(QJsonObject &request, LLMCore::RequestType type) override;
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) override;
QList<QString> getInstalledModels(const QString &url) override;
};
} // namespace QodeAssist::Providers

View File

@ -18,14 +18,14 @@
*/
#include "OpenAICompatProvider.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include "settings/PresetPromptsSettings.hpp"
namespace QodeAssist::Providers {
OpenAICompatProvider::OpenAICompatProvider() {}
@ -50,41 +50,53 @@ QString OpenAICompatProvider::chatEndpoint() const
return "/v1/chat/completions";
}
bool OpenAICompatProvider::supportsModelListing() const
{
return false;
}
void OpenAICompatProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
{
auto &promptSettings = Settings::presetPromptsSettings();
auto settings = promptSettings.getSettings(type);
QJsonArray messages;
auto prepareMessages = [](QJsonObject &req) -> QJsonArray {
QJsonArray messages;
if (req.contains("system")) {
messages.append(
QJsonObject{{"role", "system"}, {"content", req.take("system").toString()}});
}
if (req.contains("prompt")) {
messages.append(
QJsonObject{{"role", "user"}, {"content", req.take("prompt").toString()}});
}
return messages;
};
if (request.contains("system")) {
QJsonObject systemMessage{{"role", "system"},
{"content", request.take("system").toString()}};
messages.append(systemMessage);
}
auto applyModelParams = [&request](const auto &settings) {
request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings.temperature();
if (request.contains("prompt")) {
QJsonObject userMessage{{"role", "user"}, {"content", request.take("prompt").toString()}};
messages.append(userMessage);
}
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useTopK())
request["top_k"] = settings.topK();
if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
request["presence_penalty"] = settings.presencePenalty();
const QString &apiKey = settings.apiKey();
if (!apiKey.isEmpty()) {
request["api_key"] = apiKey;
}
};
QJsonArray messages = prepareMessages(request);
if (!messages.isEmpty()) {
request["messages"] = std::move(messages);
}
request["max_tokens"] = settings.maxTokens;
request["temperature"] = settings.temperature;
if (settings.useTopP)
request["top_p"] = settings.topP;
if (settings.useTopK)
request["top_k"] = settings.topK;
if (settings.useFrequencyPenalty)
request["frequency_penalty"] = settings.frequencyPenalty;
if (settings.usePresencePenalty)
request["presence_penalty"] = settings.presencePenalty;
const QString &apiKey = settings.apiKey;
if (!apiKey.isEmpty()) {
request["api_key"] = apiKey;
if (type == LLMCore::RequestType::Fim) {
applyModelParams(Settings::codeCompletionSettings());
} else {
applyModelParams(Settings::chatAssistantSettings());
}
}
@ -129,8 +141,7 @@ bool OpenAICompatProvider::handleResponse(QNetworkReply *reply, QString &accumul
return isComplete;
}
QList<QString> OpenAICompatProvider::getInstalledModels(const Utils::Environment &env,
const QString &url)
QList<QString> OpenAICompatProvider::getInstalledModels(const QString &url)
{
return QStringList();
}

View File

@ -32,9 +32,10 @@ public:
QString url() const override;
QString completionEndpoint() const override;
QString chatEndpoint() const override;
bool supportsModelListing() const override;
void prepareRequest(QJsonObject &request, LLMCore::RequestType type) override;
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
QList<QString> getInstalledModels(const Utils::Environment &env, const QString &url) override;
QList<QString> getInstalledModels(const QString &url) override;
};
} // namespace QodeAssist::Providers

View File

@ -39,6 +39,7 @@
#include <texteditor/texteditor.h>
#include <utils/icon.h>
#include "ConfigurationManager.hpp"
#include "QodeAssistClient.hpp"
#include "chat/ChatOutputPane.h"
#include "chat/NavigationPanel.hpp"
@ -48,13 +49,16 @@
#include "providers/OllamaProvider.hpp"
#include "providers/OpenAICompatProvider.hpp"
#include "templates/BasicChat.hpp"
#include "templates/CodeLlamaChat.hpp"
#include "templates/CodeLlamaFim.hpp"
#include "templates/CustomFimTemplate.hpp"
#include "templates/DeepSeekCoderChat.hpp"
#include "templates/DeepSeekCoderFim.hpp"
#include "templates/QwenChat.hpp"
#include "templates/Ollama.hpp"
#include "templates/Qwen.hpp"
#include "templates/StarCoder2Fim.hpp"
#include "templates/StarCoderChat.hpp"
using namespace Utils;
using namespace Core;
@ -93,7 +97,13 @@ public:
templateManager.registerTemplate<Templates::CustomTemplate>();
templateManager.registerTemplate<Templates::DeepSeekCoderChat>();
templateManager.registerTemplate<Templates::CodeLlamaChat>();
templateManager.registerTemplate<Templates::LlamaChat>();
templateManager.registerTemplate<Templates::StarCoderChat>();
templateManager.registerTemplate<Templates::QwenChat>();
templateManager.registerTemplate<Templates::QwenFim>();
templateManager.registerTemplate<Templates::OllamaAutoFim>();
templateManager.registerTemplate<Templates::OllamaAutoChat>();
templateManager.registerTemplate<Templates::BasicChat>();
Utils::Icon QCODEASSIST_ICON(
{{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}});
@ -121,6 +131,8 @@ public:
m_chatOutputPane = new Chat::ChatOutputPane(this);
m_navigationPanel = new Chat::NavigationPanel();
ConfigurationManager::instance().init();
}
void extensionsInitialized() final

45
settings/ButtonAspect.hpp Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QPushButton>
#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
class ButtonAspect : public Utils::BaseAspect
{
Q_OBJECT
public:
ButtonAspect(Utils::AspectContainer *container = nullptr)
: Utils::BaseAspect(container)
{}
void addToLayout(Layouting::Layout &parent) override
{
auto button = new QPushButton(m_buttonText);
connect(button, &QPushButton::clicked, this, &ButtonAspect::clicked);
parent.addItem(button);
}
QString m_buttonText;
signals:
void clicked();
};

View File

@ -1,10 +1,13 @@
add_library(QodeAssistSettings STATIC
GeneralSettings.hpp GeneralSettings.cpp
ContextSettings.hpp ContextSettings.cpp
CustomPromptSettings.hpp CustomPromptSettings.cpp
PresetPromptsSettings.hpp PresetPromptsSettings.cpp
SettingsUtils.hpp
SettingsConstants.hpp
ButtonAspect.hpp
SettingsTr.hpp
CodeCompletionSettings.hpp CodeCompletionSettings.cpp
ChatAssistantSettings.hpp ChatAssistantSettings.cpp
SettingsDialog.hpp SettingsDialog.cpp
)
target_link_libraries(QodeAssistSettings
@ -14,7 +17,5 @@ target_link_libraries(QodeAssistSettings
QtCreator::Core
QtCreator::Utils
QodeAssistLogger
PRIVATE
LLMCore
)
target_include_directories(QodeAssistSettings PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -0,0 +1,236 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ChatAssistantSettings.hpp"
#include <QMessageBox>
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
#include <utils/layoutbuilder.h>
#include "SettingsConstants.hpp"
#include "SettingsTr.hpp"
#include "SettingsUtils.hpp"
namespace QodeAssist::Settings {
ChatAssistantSettings &chatAssistantSettings()
{
static ChatAssistantSettings settings;
return settings;
}
ChatAssistantSettings::ChatAssistantSettings()
{
setAutoApply(false);
setDisplayName(Tr::tr("Chat Assistant"));
// Chat Settings
chatTokensThreshold.setSettingsKey(Constants::CA_TOKENS_THRESHOLD);
chatTokensThreshold.setLabelText(Tr::tr("Chat History Token Limit:"));
chatTokensThreshold.setToolTip(Tr::tr("Maximum number of tokens in chat history. When "
"exceeded, oldest messages will be removed."));
chatTokensThreshold.setRange(1000, 16000);
chatTokensThreshold.setDefaultValue(8000);
sharingCurrentFile.setSettingsKey(Constants::CA_SHARING_CURRENT_FILE);
sharingCurrentFile.setLabelText(Tr::tr("Share Current File With Assistant by Default"));
sharingCurrentFile.setDefaultValue(true);
// General Parameters Settings
temperature.setSettingsKey(Constants::CA_TEMPERATURE);
temperature.setLabelText(Tr::tr("Temperature:"));
temperature.setDefaultValue(0.5);
temperature.setRange(0.0, 2.0);
temperature.setSingleStep(0.1);
maxTokens.setSettingsKey(Constants::CA_MAX_TOKENS);
maxTokens.setLabelText(Tr::tr("Max Tokens:"));
maxTokens.setRange(-1, 10000);
maxTokens.setDefaultValue(2000);
// Advanced Parameters
useTopP.setSettingsKey(Constants::CA_USE_TOP_P);
useTopP.setDefaultValue(false);
useTopP.setLabelText(Tr::tr("Top P:"));
topP.setSettingsKey(Constants::CA_TOP_P);
topP.setDefaultValue(0.9);
topP.setRange(0.0, 1.0);
topP.setSingleStep(0.1);
useTopK.setSettingsKey(Constants::CA_USE_TOP_K);
useTopK.setDefaultValue(false);
useTopK.setLabelText(Tr::tr("Top K:"));
topK.setSettingsKey(Constants::CA_TOP_K);
topK.setDefaultValue(50);
topK.setRange(1, 1000);
usePresencePenalty.setSettingsKey(Constants::CA_USE_PRESENCE_PENALTY);
usePresencePenalty.setDefaultValue(false);
usePresencePenalty.setLabelText(Tr::tr("Presence Penalty:"));
presencePenalty.setSettingsKey(Constants::CA_PRESENCE_PENALTY);
presencePenalty.setDefaultValue(0.0);
presencePenalty.setRange(-2.0, 2.0);
presencePenalty.setSingleStep(0.1);
useFrequencyPenalty.setSettingsKey(Constants::CA_USE_FREQUENCY_PENALTY);
useFrequencyPenalty.setDefaultValue(false);
useFrequencyPenalty.setLabelText(Tr::tr("Frequency Penalty:"));
frequencyPenalty.setSettingsKey(Constants::CA_FREQUENCY_PENALTY);
frequencyPenalty.setDefaultValue(0.0);
frequencyPenalty.setRange(-2.0, 2.0);
frequencyPenalty.setSingleStep(0.1);
// Context Settings
useSystemPrompt.setSettingsKey(Constants::CA_USE_SYSTEM_PROMPT);
useSystemPrompt.setDefaultValue(true);
useSystemPrompt.setLabelText(Tr::tr("Use System Prompt"));
systemPrompt.setSettingsKey(Constants::CA_SYSTEM_PROMPT);
systemPrompt.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
systemPrompt.setDefaultValue(
"You are an advanced AI assistant specializing in C++, Qt, and QML development. Your role "
"is to provide helpful, accurate, and detailed responses to questions about coding, "
"debugging, "
"and best practices in these technologies.");
// Ollama Settings
ollamaLivetime.setSettingsKey(Constants::CA_OLLAMA_LIVETIME);
ollamaLivetime.setToolTip(
Tr::tr("Time to suspend Ollama after completion request (in minutes), "
"Only Ollama, -1 to disable"));
ollamaLivetime.setLabelText("Livetime:");
ollamaLivetime.setDefaultValue("5m");
ollamaLivetime.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
contextWindow.setSettingsKey(Constants::CA_OLLAMA_CONTEXT_WINDOW);
contextWindow.setLabelText(Tr::tr("Context Window:"));
contextWindow.setRange(-1, 10000);
contextWindow.setDefaultValue(2048);
// API Configuration Settings
apiKey.setSettingsKey(Constants::CA_API_KEY);
apiKey.setLabelText(Tr::tr("API Key:"));
apiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
apiKey.setPlaceHolderText(Tr::tr("Enter your API key here"));
resetToDefaults.m_buttonText = TrConstants::RESET_TO_DEFAULTS;
readSettings();
setupConnections();
setLayouter([this]() {
using namespace Layouting;
auto genGrid = Grid{};
genGrid.addRow({Row{temperature}});
genGrid.addRow({Row{maxTokens}});
auto advancedGrid = Grid{};
advancedGrid.addRow({useTopP, topP});
advancedGrid.addRow({useTopK, topK});
advancedGrid.addRow({usePresencePenalty, presencePenalty});
advancedGrid.addRow({useFrequencyPenalty, frequencyPenalty});
auto ollamaGrid = Grid{};
ollamaGrid.addRow({ollamaLivetime});
ollamaGrid.addRow({contextWindow});
return Column{Row{Stretch{1}, resetToDefaults},
Space{8},
Group{title(Tr::tr("Chat Settings")),
Column{Row{chatTokensThreshold, Stretch{1}}, sharingCurrentFile}},
Space{8},
Group{
title(Tr::tr("General Parameters")),
Row{genGrid, Stretch{1}},
},
Space{8},
Group{title(Tr::tr("Advanced Parameters")),
Column{Row{advancedGrid, Stretch{1}}}},
Space{8},
Group{title(Tr::tr("Context Settings")),
Column{
Row{useSystemPrompt, Stretch{1}},
systemPrompt,
}},
Group{title(Tr::tr("Ollama Settings")), Column{Row{ollamaGrid, Stretch{1}}}},
Space{8},
Group{title(Tr::tr("API Configuration")), Column{apiKey}},
Stretch{1}};
});
}
void ChatAssistantSettings::setupConnections()
{
connect(&resetToDefaults,
&ButtonAspect::clicked,
this,
&ChatAssistantSettings::resetSettingsToDefaults);
}
void ChatAssistantSettings::resetSettingsToDefaults()
{
QMessageBox::StandardButton reply;
reply = QMessageBox::question(
Core::ICore::dialogParent(),
Tr::tr("Reset Settings"),
Tr::tr("Are you sure you want to reset all settings to default values?"),
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
resetAspect(chatTokensThreshold);
resetAspect(temperature);
resetAspect(maxTokens);
resetAspect(useTopP);
resetAspect(topP);
resetAspect(useTopK);
resetAspect(topK);
resetAspect(usePresencePenalty);
resetAspect(presencePenalty);
resetAspect(useFrequencyPenalty);
resetAspect(frequencyPenalty);
resetAspect(useSystemPrompt);
resetAspect(systemPrompt);
resetAspect(ollamaLivetime);
resetAspect(contextWindow);
}
}
class ChatAssistantSettingsPage : public Core::IOptionsPage
{
public:
ChatAssistantSettingsPage()
{
setId(Constants::QODE_ASSIST_CHAT_ASSISTANT_SETTINGS_PAGE_ID);
setDisplayName(Tr::tr("Chat Assistant"));
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
setSettingsProvider([] { return &chatAssistantSettings(); });
}
};
const ChatAssistantSettingsPage chatAssistantSettingsPage;
} // namespace QodeAssist::Settings

View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <utils/aspects.h>
#include "ButtonAspect.hpp"
namespace QodeAssist::Settings {
class ChatAssistantSettings : public Utils::AspectContainer
{
public:
ChatAssistantSettings();
ButtonAspect resetToDefaults{this};
// Chat settings
Utils::IntegerAspect chatTokensThreshold{this};
Utils::BoolAspect sharingCurrentFile{this};
// General Parameters Settings
Utils::DoubleAspect temperature{this};
Utils::IntegerAspect maxTokens{this};
// Advanced Parameters
Utils::BoolAspect useTopP{this};
Utils::DoubleAspect topP{this};
Utils::BoolAspect useTopK{this};
Utils::IntegerAspect topK{this};
Utils::BoolAspect usePresencePenalty{this};
Utils::DoubleAspect presencePenalty{this};
Utils::BoolAspect useFrequencyPenalty{this};
Utils::DoubleAspect frequencyPenalty{this};
// Context Settings
Utils::BoolAspect useSystemPrompt{this};
Utils::StringAspect systemPrompt{this};
// Ollama Settings
Utils::StringAspect ollamaLivetime{this};
Utils::IntegerAspect contextWindow{this};
// API Configuration Settings
Utils::StringAspect apiKey{this};
private:
void setupConnections();
void resetSettingsToDefaults();
};
ChatAssistantSettings &chatAssistantSettings();
} // namespace QodeAssist::Settings

View File

@ -0,0 +1,317 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#include "CodeCompletionSettings.hpp"
#include <QMessageBox>
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
#include <utils/layoutbuilder.h>
#include "SettingsConstants.hpp"
#include "SettingsTr.hpp"
#include "SettingsUtils.hpp"
namespace QodeAssist::Settings {
CodeCompletionSettings &codeCompletionSettings()
{
static CodeCompletionSettings settings;
return settings;
}
CodeCompletionSettings::CodeCompletionSettings()
{
setAutoApply(false);
setDisplayName(Tr::tr("Code Completion"));
// Auto Completion Settings
autoCompletion.setSettingsKey(Constants::CC_AUTO_COMPLETION);
autoCompletion.setLabelText(Tr::tr("Enable Auto Complete"));
autoCompletion.setDefaultValue(true);
multiLineCompletion.setSettingsKey(Constants::CC_MULTILINE_COMPLETION);
multiLineCompletion.setDefaultValue(false);
multiLineCompletion.setLabelText(Tr::tr("Enable Multiline Completion(experimental)"));
startSuggestionTimer.setSettingsKey(Constants::СС_START_SUGGESTION_TIMER);
startSuggestionTimer.setLabelText(Tr::tr("with delay(ms)"));
startSuggestionTimer.setRange(10, 10000);
startSuggestionTimer.setDefaultValue(500);
autoCompletionCharThreshold.setSettingsKey(Constants::СС_AUTO_COMPLETION_CHAR_THRESHOLD);
autoCompletionCharThreshold.setLabelText(Tr::tr("AI suggestion triggers after typing"));
autoCompletionCharThreshold.setToolTip(
Tr::tr("The number of characters that need to be typed within the typing interval "
"before an AI suggestion request is sent."));
autoCompletionCharThreshold.setRange(0, 10);
autoCompletionCharThreshold.setDefaultValue(0);
autoCompletionTypingInterval.setSettingsKey(Constants::СС_AUTO_COMPLETION_TYPING_INTERVAL);
autoCompletionTypingInterval.setLabelText(Tr::tr("character(s) within(ms)"));
autoCompletionTypingInterval.setToolTip(
Tr::tr("The time window (in milliseconds) during which the character threshold "
"must be met to trigger an AI suggestion request."));
autoCompletionTypingInterval.setRange(500, 5000);
autoCompletionTypingInterval.setDefaultValue(2000);
// General Parameters Settings
temperature.setSettingsKey(Constants::CC_TEMPERATURE);
temperature.setLabelText(Tr::tr("Temperature:"));
temperature.setDefaultValue(0.2);
temperature.setRange(0.0, 2.0);
temperature.setSingleStep(0.1);
maxTokens.setSettingsKey(Constants::CC_MAX_TOKENS);
maxTokens.setLabelText(Tr::tr("Max Tokens:"));
maxTokens.setRange(-1, 10000);
maxTokens.setDefaultValue(50);
// Advanced Parameters
useTopP.setSettingsKey(Constants::CC_USE_TOP_P);
useTopP.setDefaultValue(false);
useTopP.setLabelText(Tr::tr("Top P:"));
topP.setSettingsKey(Constants::CC_TOP_P);
topP.setDefaultValue(0.9);
topP.setRange(0.0, 1.0);
topP.setSingleStep(0.1);
useTopK.setSettingsKey(Constants::CC_USE_TOP_K);
useTopK.setDefaultValue(false);
useTopK.setLabelText(Tr::tr("Top K:"));
topK.setSettingsKey(Constants::CC_TOP_K);
topK.setDefaultValue(50);
topK.setRange(1, 1000);
usePresencePenalty.setSettingsKey(Constants::CC_USE_PRESENCE_PENALTY);
usePresencePenalty.setDefaultValue(false);
usePresencePenalty.setLabelText(Tr::tr("Presence Penalty:"));
presencePenalty.setSettingsKey(Constants::CC_PRESENCE_PENALTY);
presencePenalty.setDefaultValue(0.0);
presencePenalty.setRange(-2.0, 2.0);
presencePenalty.setSingleStep(0.1);
useFrequencyPenalty.setSettingsKey(Constants::CC_USE_FREQUENCY_PENALTY);
useFrequencyPenalty.setDefaultValue(false);
useFrequencyPenalty.setLabelText(Tr::tr("Frequency Penalty:"));
frequencyPenalty.setSettingsKey(Constants::CC_FREQUENCY_PENALTY);
frequencyPenalty.setDefaultValue(0.0);
frequencyPenalty.setRange(-2.0, 2.0);
frequencyPenalty.setSingleStep(0.1);
// Context Settings
readFullFile.setSettingsKey(Constants::CC_READ_FULL_FILE);
readFullFile.setLabelText(Tr::tr("Read Full File"));
readFullFile.setDefaultValue(false);
readFileParts.setLabelText(Tr::tr("Read Strings Before Cursor:"));
readFileParts.setDefaultValue(true);
readStringsBeforeCursor.setSettingsKey(Constants::CC_READ_STRINGS_BEFORE_CURSOR);
readStringsBeforeCursor.setRange(0, 10000);
readStringsBeforeCursor.setDefaultValue(50);
readStringsAfterCursor.setSettingsKey(Constants::CC_READ_STRINGS_AFTER_CURSOR);
readStringsAfterCursor.setLabelText(Tr::tr("Read Strings After Cursor:"));
readStringsAfterCursor.setRange(0, 10000);
readStringsAfterCursor.setDefaultValue(30);
useSystemPrompt.setSettingsKey(Constants::CC_USE_SYSTEM_PROMPT);
useSystemPrompt.setDefaultValue(true);
useSystemPrompt.setLabelText(Tr::tr("Use System Prompt"));
systemPrompt.setSettingsKey(Constants::CC_SYSTEM_PROMPT);
systemPrompt.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
systemPrompt.setDefaultValue(
"You are an expert C++, Qt, and QML code completion AI. Your task is to provide accurate "
"and contextually appropriate code suggestions.");
useFilePathInContext.setSettingsKey(Constants::CC_USE_FILE_PATH_IN_CONTEXT);
useFilePathInContext.setDefaultValue(true);
useFilePathInContext.setLabelText(Tr::tr("Use File Path in Context"));
useProjectChangesCache.setSettingsKey(Constants::CC_USE_PROJECT_CHANGES_CACHE);
useProjectChangesCache.setDefaultValue(true);
useProjectChangesCache.setLabelText(Tr::tr("Max Changes Cache Size:"));
maxChangesCacheSize.setSettingsKey(Constants::CC_MAX_CHANGES_CACHE_SIZE);
maxChangesCacheSize.setRange(2, 1000);
maxChangesCacheSize.setDefaultValue(10);
// Ollama Settings
ollamaLivetime.setSettingsKey(Constants::CC_OLLAMA_LIVETIME);
ollamaLivetime.setToolTip(
Tr::tr("Time to suspend Ollama after completion request (in minutes), "
"Only Ollama, -1 to disable"));
ollamaLivetime.setLabelText("Livetime:");
ollamaLivetime.setDefaultValue("5m");
ollamaLivetime.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
contextWindow.setSettingsKey(Constants::CC_OLLAMA_CONTEXT_WINDOW);
contextWindow.setLabelText(Tr::tr("Context Window:"));
contextWindow.setRange(-1, 10000);
contextWindow.setDefaultValue(2048);
// API Configuration Settings
apiKey.setSettingsKey(Constants::CC_API_KEY);
apiKey.setLabelText(Tr::tr("API Key:"));
apiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
apiKey.setPlaceHolderText(Tr::tr("Enter your API key here"));
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
readSettings();
readFileParts.setValue(!readFullFile.value());
setupConnections();
setLayouter([this]() {
using namespace Layouting;
auto genGrid = Grid{};
genGrid.addRow({Row{temperature}});
genGrid.addRow({Row{maxTokens}});
auto advancedGrid = Grid{};
advancedGrid.addRow({useTopP, topP});
advancedGrid.addRow({useTopK, topK});
advancedGrid.addRow({usePresencePenalty, presencePenalty});
advancedGrid.addRow({useFrequencyPenalty, frequencyPenalty});
auto ollamaGrid = Grid{};
ollamaGrid.addRow({ollamaLivetime});
ollamaGrid.addRow({contextWindow});
auto contextGrid = Grid{};
contextGrid.addRow({Row{readFullFile}});
contextGrid.addRow({Row{readFileParts, readStringsBeforeCursor, readStringsAfterCursor}});
auto contextItem = Column{Row{contextGrid, Stretch{1}},
Row{useSystemPrompt, Stretch{1}},
systemPrompt,
Row{useFilePathInContext, Stretch{1}},
Row{useProjectChangesCache, maxChangesCacheSize, Stretch{1}}};
return Column{Row{Stretch{1}, resetToDefaults},
Space{8},
Group{title(Tr::tr("Auto Completion Settings")),
Column{autoCompletion,
Space{8},
multiLineCompletion,
Row{autoCompletionCharThreshold,
autoCompletionTypingInterval,
startSuggestionTimer,
Stretch{1}}}},
Space{8},
Group{title(Tr::tr("General Parameters")),
Column{
Row{genGrid, Stretch{1}},
}},
Space{8},
Group{title(Tr::tr("Advanced Parameters")),
Column{Row{advancedGrid, Stretch{1}}}},
Space{8},
Group{title(Tr::tr("Context Settings")), contextItem},
Space{8},
Group{title(Tr::tr("Ollama Settings")), Column{Row{ollamaGrid, Stretch{1}}}},
Space{8},
Group{title(Tr::tr("API Configuration")), Column{apiKey}},
Stretch{1}};
});
}
void CodeCompletionSettings::setupConnections()
{
connect(&resetToDefaults,
&ButtonAspect::clicked,
this,
&CodeCompletionSettings::resetSettingsToDefaults);
connect(&readFullFile, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
if (readFullFile.volatileValue()) {
readFileParts.setValue(false);
writeSettings();
}
});
connect(&readFileParts, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
if (readFileParts.volatileValue()) {
readFullFile.setValue(false);
writeSettings();
}
});
}
void CodeCompletionSettings::resetSettingsToDefaults()
{
QMessageBox::StandardButton reply;
reply = QMessageBox::question(
Core::ICore::dialogParent(),
Tr::tr("Reset Settings"),
Tr::tr("Are you sure you want to reset all settings to default values?"),
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
resetAspect(autoCompletion);
resetAspect(multiLineCompletion);
resetAspect(temperature);
resetAspect(maxTokens);
resetAspect(useTopP);
resetAspect(topP);
resetAspect(useTopK);
resetAspect(topK);
resetAspect(usePresencePenalty);
resetAspect(presencePenalty);
resetAspect(useFrequencyPenalty);
resetAspect(frequencyPenalty);
resetAspect(readFullFile);
resetAspect(readFileParts);
resetAspect(readStringsBeforeCursor);
resetAspect(readStringsAfterCursor);
resetAspect(useSystemPrompt);
resetAspect(systemPrompt);
resetAspect(useFilePathInContext);
resetAspect(useProjectChangesCache);
resetAspect(maxChangesCacheSize);
resetAspect(ollamaLivetime);
resetAspect(contextWindow);
}
}
class CodeCompletionSettingsPage : public Core::IOptionsPage
{
public:
CodeCompletionSettingsPage()
{
setId(Constants::QODE_ASSIST_CODE_COMPLETION_SETTINGS_PAGE_ID);
setDisplayName(Tr::tr("Code Completion"));
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
setSettingsProvider([] { return &codeCompletionSettings(); });
}
};
const CodeCompletionSettingsPage codeCompletionSettingsPage;
} // namespace QodeAssist::Settings

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <utils/aspects.h>
#include "ButtonAspect.hpp"
namespace QodeAssist::Settings {
class CodeCompletionSettings : public Utils::AspectContainer
{
public:
CodeCompletionSettings();
ButtonAspect resetToDefaults{this};
// Auto Completion Settings
Utils::BoolAspect autoCompletion{this};
Utils::BoolAspect multiLineCompletion{this};
Utils::IntegerAspect startSuggestionTimer{this};
Utils::IntegerAspect autoCompletionCharThreshold{this};
Utils::IntegerAspect autoCompletionTypingInterval{this};
// General Parameters Settings
Utils::DoubleAspect temperature{this};
Utils::IntegerAspect maxTokens{this};
// Advanced Parameters
Utils::BoolAspect useTopP{this};
Utils::DoubleAspect topP{this};
Utils::BoolAspect useTopK{this};
Utils::IntegerAspect topK{this};
Utils::BoolAspect usePresencePenalty{this};
Utils::DoubleAspect presencePenalty{this};
Utils::BoolAspect useFrequencyPenalty{this};
Utils::DoubleAspect frequencyPenalty{this};
// Context Settings
Utils::BoolAspect readFullFile{this};
Utils::BoolAspect readFileParts{this};
Utils::IntegerAspect readStringsBeforeCursor{this};
Utils::IntegerAspect readStringsAfterCursor{this};
Utils::BoolAspect useSystemPrompt{this};
Utils::StringAspect systemPrompt{this};
Utils::BoolAspect useFilePathInContext{this};
Utils::BoolAspect useProjectChangesCache{this};
Utils::IntegerAspect maxChangesCacheSize{this};
// Ollama Settings
Utils::StringAspect ollamaLivetime{this};
Utils::IntegerAspect contextWindow{this};
// API Configuration Settings
Utils::StringAspect apiKey{this};
private:
void setupConnections();
void resetSettingsToDefaults();
};
CodeCompletionSettings &codeCompletionSettings();
} // namespace QodeAssist::Settings

View File

@ -1,179 +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 "ContextSettings.hpp"
#include <QMessageBox>
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
#include <utils/layoutbuilder.h>
#include "SettingsConstants.hpp"
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"));
useSystemPrompt.setSettingsKey(Constants::USE_SYSTEM_PROMPT);
useSystemPrompt.setDefaultValue(true);
useSystemPrompt.setLabelText(Tr::tr("Use System Prompt"));
systemPrompt.setSettingsKey(Constants::SYSTEM_PROMPT);
systemPrompt.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
systemPrompt.setDefaultValue(
"You are an expert C++, Qt, and QML code completion AI. Your task is to provide accurate "
"and "
"contextually appropriate code suggestions. Focus on completing the code in a way that "
"follows best practices, is efficient, and matches the surrounding code style. Prioritize "
"Qt and QML-specific completions when appropriate. Avoid adding comments or explanations "
"in your completions.");
useChatSystemPrompt.setSettingsKey(Constants::USE_CHAT_SYSTEM_PROMPT);
useChatSystemPrompt.setDefaultValue(true);
useChatSystemPrompt.setLabelText(Tr::tr("Use System Prompt for chat"));
chatSystemPrompt.setSettingsKey(Constants::CHAT_SYSTEM_PROMPT);
chatSystemPrompt.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
chatSystemPrompt.setDefaultValue(
"You are an advanced AI assistant specializing in C++, Qt, and QML development. Your role "
"is "
"to provide helpful, accurate, and detailed responses to questions about coding, "
"debugging, "
"and best practices in these technologies. Offer clear explanations, code examples when "
"appropriate, and guidance on Qt Creator usage. Always prioritize officially recommended "
"Qt "
"and C++ practices. If you're unsure about something, state it clearly and suggest where "
"the "
"user might find more information.");
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
useProjectChangesCache.setSettingsKey(Constants::USE_PROJECT_CHANGES_CACHE);
useProjectChangesCache.setDefaultValue(true);
useProjectChangesCache.setLabelText(Tr::tr("Use Project Changes Cache"));
maxChangesCacheSize.setSettingsKey(Constants::MAX_CHANGES_CACHE_SIZE);
maxChangesCacheSize.setLabelText(Tr::tr("Max Changes Cache Size"));
maxChangesCacheSize.setRange(2, 1000);
maxChangesCacheSize.setDefaultValue(20);
readSettings();
readStringsAfterCursor.setEnabled(!readFullFile());
readStringsBeforeCursor.setEnabled(!readFullFile());
systemPrompt.setEnabled(useSystemPrompt());
setupConnection();
setLayouter([this]() {
using namespace Layouting;
return Column{Row{Stretch{1}, resetToDefaults},
Group{title(Tr::tr("AI Suggestions Context")),
Column{Row{readFullFile, Stretch{1}},
Row{readStringsBeforeCursor, Stretch{1}},
Row{readStringsAfterCursor, Stretch{1}},
useFilePathInContext,
useSystemPrompt,
systemPrompt,
useProjectChangesCache,
Row{maxChangesCacheSize, Stretch{1}},
Stretch{1}}},
Space{16},
Group{title(Tr::tr("AI Chat Context")),
Column{useChatSystemPrompt, chatSystemPrompt}}};
});
}
void ContextSettings::setupConnection()
{
connect(&readFullFile, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
readStringsAfterCursor.setEnabled(!readFullFile.volatileValue());
readStringsBeforeCursor.setEnabled(!readFullFile.volatileValue());
});
connect(&useSystemPrompt, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
systemPrompt.setEnabled(useSystemPrompt.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(useSystemPrompt);
resetAspect(systemPrompt);
resetAspect(useChatSystemPrompt);
resetAspect(chatSystemPrompt);
}
}
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

@ -27,6 +27,8 @@
#include <utils/layoutbuilder.h>
#include "SettingsConstants.hpp"
#include "SettingsTr.hpp"
#include "SettingsUtils.hpp"
namespace QodeAssist::Settings {

View File

@ -19,9 +19,10 @@
#pragma once
#include "SettingsUtils.hpp"
#include <utils/aspects.h>
#include "ButtonAspect.hpp"
namespace QodeAssist::Settings {
class CustomPromptSettings : public Utils::AspectContainer

View File

@ -19,18 +19,23 @@
#include "GeneralSettings.hpp"
#include <QInputDialog>
#include <QMessageBox>
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
#include <utils/layoutbuilder.h>
#include <utils/utilsicons.h>
#include <QInputDialog>
#include <QMessageBox>
#include <QTimer>
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qcompleter.h>
#include <QtWidgets/qgroupbox.h>
#include <QtWidgets/qradiobutton.h>
#include <QtWidgets/qstackedwidget.h>
#include "Logger.hpp"
#include "PromptTemplateManager.hpp"
#include "Provider.hpp"
#include "ProvidersManager.hpp"
#include "SettingsConstants.hpp"
#include "SettingsDialog.hpp"
#include "SettingsTr.hpp"
#include "SettingsUtils.hpp"
namespace QodeAssist::Settings {
@ -45,117 +50,63 @@ GeneralSettings::GeneralSettings()
{
setAutoApply(false);
setDisplayName(Tr::tr("General"));
setDisplayName(TrConstants::GENERAL);
enableQodeAssist.setSettingsKey(Constants::ENABLE_QODE_ASSIST);
enableQodeAssist.setLabelText(Tr::tr("Enable Qode Assist"));
enableQodeAssist.setLabelText(TrConstants::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.setLabelText(TrConstants::ENABLE_LOG);
enableLogging.setDefaultValue(false);
multiLineCompletion.setSettingsKey(Constants::MULTILINE_COMPLETION);
multiLineCompletion.setDefaultValue(false);
multiLineCompletion.setLabelText(Tr::tr("Enable Multiline Completion(experimental)"));
resetToDefaults.m_buttonText = TrConstants::RESET_TO_DEFAULTS;
startSuggestionTimer.setSettingsKey(Constants::START_SUGGESTION_TIMER);
startSuggestionTimer.setLabelText(Tr::tr("with delay(ms)"));
startSuggestionTimer.setRange(10, 10000);
startSuggestionTimer.setDefaultValue(500);
initStringAspect(ccProvider, Constants::CC_PROVIDER, TrConstants::PROVIDER, "Ollama");
ccProvider.setReadOnly(true);
ccSelectProvider.m_buttonText = TrConstants::SELECT;
autoCompletionCharThreshold.setSettingsKey(Constants::AUTO_COMPLETION_CHAR_THRESHOLD);
autoCompletionCharThreshold.setLabelText(Tr::tr("AI suggestion triggers after typing"));
autoCompletionCharThreshold.setToolTip(
Tr::tr("The number of characters that need to be typed within the typing interval "
"before an AI suggestion request is sent."));
autoCompletionCharThreshold.setRange(0, 10);
autoCompletionCharThreshold.setDefaultValue(0);
initStringAspect(ccModel, Constants::CC_MODEL, TrConstants::MODEL, "qwen2.5-coder:7b");
ccModel.setHistoryCompleter(Constants::CC_MODEL_HISTORY);
ccSelectModel.m_buttonText = TrConstants::SELECT;
autoCompletionTypingInterval.setSettingsKey(Constants::AUTO_COMPLETION_TYPING_INTERVAL);
autoCompletionTypingInterval.setLabelText(Tr::tr("character(s) within(ms)"));
autoCompletionTypingInterval.setToolTip(
Tr::tr("The time window (in milliseconds) during which the character threshold "
"must be met to trigger an AI suggestion request."));
autoCompletionTypingInterval.setRange(500, 5000);
autoCompletionTypingInterval.setDefaultValue(2000);
initStringAspect(ccTemplate, Constants::CC_TEMPLATE, TrConstants::TEMPLATE, "Ollama Auto FIM");
ccTemplate.setReadOnly(true);
ccSelectTemplate.m_buttonText = TrConstants::SELECT;
llmProviders.setSettingsKey(Constants::LLM_PROVIDERS);
llmProviders.setDisplayName(Tr::tr("AI Suggest Provider:"));
llmProviders.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
initStringAspect(ccUrl, Constants::CC_URL, TrConstants::URL, "http://localhost:11434");
ccUrl.setHistoryCompleter(Constants::CC_URL_HISTORY);
ccSetUrl.m_buttonText = TrConstants::SELECT;
url.setSettingsKey(Constants::URL);
url.setLabelText(Tr::tr("URL:"));
url.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
ccStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay);
ccStatus.setLabelText(TrConstants::STATUS);
ccStatus.setDefaultValue("");
ccTest.m_buttonText = TrConstants::TEST;
endPoint.setSettingsKey(Constants::END_POINT);
endPoint.setLabelText(Tr::tr("FIM Endpoint:"));
endPoint.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
initStringAspect(caProvider, Constants::CA_PROVIDER, TrConstants::PROVIDER, "Ollama");
caProvider.setReadOnly(true);
caSelectProvider.m_buttonText = TrConstants::SELECT;
modelName.setSettingsKey(Constants::MODEL_NAME);
modelName.setLabelText(Tr::tr("Model name:"));
modelName.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
initStringAspect(caModel, Constants::CA_MODEL, TrConstants::MODEL, "qwen2.5-coder:7b");
caModel.setHistoryCompleter(Constants::CA_MODEL_HISTORY);
caSelectModel.m_buttonText = TrConstants::SELECT;
selectModels.m_buttonText = Tr::tr("Select Fill-In-the-Middle Model");
initStringAspect(caTemplate, Constants::CA_TEMPLATE, TrConstants::TEMPLATE, "Ollama Auto Chat");
caTemplate.setReadOnly(true);
fimPrompts.setDisplayName(Tr::tr("Fill-In-the-Middle Prompt"));
fimPrompts.setSettingsKey(Constants::FIM_PROMPTS);
fimPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
caSelectTemplate.m_buttonText = TrConstants::SELECT;
chatLlmProviders.setSettingsKey(Constants::CHAT_LLM_PROVIDERS);
chatLlmProviders.setDisplayName(Tr::tr("AI Chat Provider:"));
chatLlmProviders.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
initStringAspect(caUrl, Constants::CA_URL, TrConstants::URL, "http://localhost:11434");
caUrl.setHistoryCompleter(Constants::CA_URL_HISTORY);
caSetUrl.m_buttonText = TrConstants::SELECT;
chatUrl.setSettingsKey(Constants::CHAT_URL);
chatUrl.setLabelText(Tr::tr("URL:"));
chatUrl.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
chatEndPoint.setSettingsKey(Constants::CHAT_END_POINT);
chatEndPoint.setLabelText(Tr::tr("Chat Endpoint:"));
chatEndPoint.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
chatModelName.setSettingsKey(Constants::CHAT_MODEL_NAME);
chatModelName.setLabelText(Tr::tr("Model name:"));
chatModelName.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
chatSelectModels.m_buttonText = Tr::tr("Select Chat Model");
chatPrompts.setDisplayName(Tr::tr("Chat Prompt"));
chatPrompts.setSettingsKey(Constants::CHAT_PROMPTS);
chatPrompts.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
chatTokensThreshold.setSettingsKey(Constants::CHAT_TOKENS_THRESHOLD);
chatTokensThreshold.setLabelText(Tr::tr("Chat History Token Limit"));
chatTokensThreshold.setToolTip(Tr::tr("Maximum number of tokens in chat history. When "
"exceeded, oldest messages will be removed."));
chatTokensThreshold.setRange(1000, 16000);
chatTokensThreshold.setDefaultValue(4000);
loadProviders();
loadPrompts();
llmProviders.setDefaultValue(llmProviders.indexForDisplay("Ollama"));
chatLlmProviders.setDefaultValue(chatLlmProviders.indexForDisplay("Ollama"));
fimPrompts.setDefaultValue(fimPrompts.indexForDisplay("CodeLlama FIM"));
chatPrompts.setDefaultValue(chatPrompts.indexForDisplay("CodeLlama Chat"));
auto fimProviderName = llmProviders.displayForIndex(llmProviders.value());
setCurrentFimProvider(fimProviderName);
auto chatProviderName = chatLlmProviders.displayForIndex(chatLlmProviders.value());
setCurrentChatProvider(chatProviderName);
caStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay);
caStatus.setLabelText(TrConstants::STATUS);
caStatus.setDefaultValue("");
caTest.m_buttonText = TrConstants::TEST;
readSettings();
auto nameFimPromts = fimPrompts.displayForIndex(fimPrompts.value());
LLMCore::PromptTemplateManager::instance().setCurrentFimTemplate(nameFimPromts);
auto nameChatPromts = chatPrompts.displayForIndex(chatPrompts.value());
LLMCore::PromptTemplateManager::instance().setCurrentChatTemplate(nameChatPromts);
Logger::instance().setLoggingEnabled(enableLogging());
setupConnections();
@ -163,226 +114,211 @@ GeneralSettings::GeneralSettings()
setLayouter([this]() {
using namespace Layouting;
auto rootLayout
= Column{Row{enableQodeAssist, Stretch{1}, resetToDefaults},
Row{enableLogging, Stretch{1}},
Space{8},
Group{title(Tr::tr("AI Suggestions")),
Column{enableAutoComplete,
multiLineCompletion,
Row{autoCompletionCharThreshold,
autoCompletionTypingInterval,
startSuggestionTimer,
Stretch{1}},
Row{llmProviders, Stretch{1}},
Row{url, endPoint, fimUrlIndicator},
Row{selectModels, modelName, fimModelIndicator},
Row{fimPrompts, Stretch{1}}}},
Space{16},
Group{title(Tr::tr("AI Chat")),
Column{Row{chatLlmProviders, Stretch{1}},
Row{chatUrl, chatEndPoint, chatUrlIndicator},
Row{chatSelectModels, chatModelName, chatModelIndicator},
Row{chatPrompts, Stretch{1}},
Row{chatTokensThreshold, Stretch{1}}}},
Stretch{1}};
auto ccGrid = Grid{};
ccGrid.addRow({ccProvider, ccSelectProvider});
ccGrid.addRow({ccUrl, ccSetUrl});
ccGrid.addRow({ccModel, ccSelectModel});
ccGrid.addRow({ccTemplate, ccSelectTemplate});
ccGrid.addRow({ccStatus, ccTest});
auto caGrid = Grid{};
caGrid.addRow({caProvider, caSelectProvider});
caGrid.addRow({caUrl, caSetUrl});
caGrid.addRow({caModel, caSelectModel});
caGrid.addRow({caTemplate, caSelectTemplate});
caGrid.addRow({caStatus, caTest});
auto ccGroup = Group{title(TrConstants::CODE_COMPLETION), ccGrid};
auto caGroup = Group{title(TrConstants::CHAT_ASSISTANT), caGrid};
auto rootLayout = Column{Row{enableQodeAssist, Stretch{1}, resetToDefaults},
Row{enableLogging, Stretch{1}},
Space{8},
ccGroup,
Space{8},
caGroup,
Stretch{1}};
return rootLayout;
});
updateStatusIndicators();
}
void GeneralSettings::setupConnections()
void GeneralSettings::showSelectionDialog(const QStringList &data,
Utils::StringAspect &aspect,
const QString &title,
const QString &text)
{
connect(&llmProviders, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
auto providerName = llmProviders.displayForIndex(llmProviders.volatileValue());
setCurrentFimProvider(providerName);
modelName.setVolatileValue("");
});
connect(&chatLlmProviders, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
auto providerName = chatLlmProviders.displayForIndex(chatLlmProviders.volatileValue());
setCurrentChatProvider(providerName);
chatModelName.setVolatileValue("");
});
if (data.isEmpty())
return;
connect(&fimPrompts, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
int index = fimPrompts.volatileValue();
LLMCore::PromptTemplateManager::instance().setCurrentFimTemplate(
fimPrompts.displayForIndex(index));
});
connect(&chatPrompts, &Utils::SelectionAspect::volatileValueChanged, this, [this]() {
int index = chatPrompts.volatileValue();
LLMCore::PromptTemplateManager::instance().setCurrentChatTemplate(
chatPrompts.displayForIndex(index));
});
bool ok;
QInputDialog dialog(Core::ICore::dialogParent());
dialog.setWindowTitle(title);
dialog.setLabelText(text);
dialog.setComboBoxItems(data);
dialog.setComboBoxEditable(false);
dialog.setFixedSize(400, 150);
connect(&selectModels, &ButtonAspect::clicked, this, [this]() {
auto *provider = LLMCore::ProvidersManager::instance().getCurrentFimProvider();
showModelSelectionDialog(&modelName, provider);
});
connect(&chatSelectModels, &ButtonAspect::clicked, this, [this]() {
auto *provider = LLMCore::ProvidersManager::instance().getCurrentChatProvider();
showModelSelectionDialog(&chatModelName, provider);
});
connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
Logger::instance().setLoggingEnabled(enableLogging.volatileValue());
});
connect(&resetToDefaults, &ButtonAspect::clicked, this, &GeneralSettings::resetPageToDefaults);
connect(&url,
&Utils::StringAspect::volatileValueChanged,
this,
&GeneralSettings::updateStatusIndicators);
connect(&modelName,
&Utils::StringAspect::volatileValueChanged,
this,
&GeneralSettings::updateStatusIndicators);
connect(&chatUrl,
&Utils::StringAspect::volatileValueChanged,
this,
&GeneralSettings::updateStatusIndicators);
connect(&chatModelName,
&Utils::StringAspect::volatileValueChanged,
this,
&GeneralSettings::updateStatusIndicators);
}
void GeneralSettings::showModelSelectionDialog(Utils::StringAspect *modelNameObj,
LLMCore::Provider *provider)
{
Utils::Environment env = Utils::Environment::systemEnvironment();
QString providerUrl = (modelNameObj == &modelName) ? url() : chatUrl();
if (provider) {
QStringList models = provider->getInstalledModels(env, providerUrl);
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()) {
modelNameObj->setVolatileValue(selectedModel);
if (dialog.exec() == QDialog::Accepted) {
QString result = dialog.textValue();
if (!result.isEmpty()) {
aspect.setValue(result);
writeSettings();
}
}
}
void GeneralSettings::showModelsNotFoundDialog(Utils::StringAspect &aspect)
{
SettingsDialog dialog(TrConstants::CONNECTION_ERROR);
dialog.addLabel(TrConstants::NO_MODELS_FOUND);
dialog.addLabel(TrConstants::CHECK_CONNECTION);
dialog.addSpacing();
ButtonAspect *providerButton = nullptr;
ButtonAspect *urlButton = nullptr;
if (&aspect == &ccModel) {
providerButton = &ccSelectProvider;
urlButton = &ccSetUrl;
} else if (&aspect == &caModel) {
providerButton = &caSelectProvider;
urlButton = &caSetUrl;
}
if (providerButton && urlButton) {
auto selectProviderBtn = new QPushButton(TrConstants::SELECT_PROVIDER);
auto selectUrlBtn = new QPushButton(TrConstants::SELECT_URL);
auto enterManuallyBtn = new QPushButton(TrConstants::ENTER_MODEL_MANUALLY);
connect(selectProviderBtn, &QPushButton::clicked, &dialog, [this, providerButton, &dialog]() {
dialog.close();
emit providerButton->clicked();
});
connect(selectUrlBtn, &QPushButton::clicked, &dialog, [this, urlButton, &dialog]() {
dialog.close();
emit urlButton->clicked();
});
connect(enterManuallyBtn, &QPushButton::clicked, &dialog, [this, &aspect, &dialog]() {
dialog.close();
showModelsNotSupportedDialog(aspect);
});
dialog.buttonLayout()->addWidget(selectProviderBtn);
dialog.buttonLayout()->addWidget(selectUrlBtn);
dialog.buttonLayout()->addWidget(enterManuallyBtn);
}
auto closeBtn = new QPushButton(TrConstants::CLOSE);
connect(closeBtn, &QPushButton::clicked, &dialog, &QDialog::close);
dialog.buttonLayout()->addWidget(closeBtn);
dialog.exec();
}
void GeneralSettings::showModelsNotSupportedDialog(Utils::StringAspect &aspect)
{
SettingsDialog dialog(TrConstants::MODEL_SELECTION);
dialog.addLabel(TrConstants::MODEL_LISTING_NOT_SUPPORTED_INFO);
dialog.addSpacing();
QString key = QString("CompleterHistory/")
.append(
(&aspect == &ccModel) ? Constants::CC_MODEL_HISTORY
: Constants::CA_MODEL_HISTORY);
QStringList historyList = qtcSettings()->value(Utils::Key(key.toLocal8Bit())).toStringList();
auto modelList = dialog.addComboBox(historyList, aspect.value());
dialog.addSpacing();
auto okButton = new QPushButton(TrConstants::OK);
connect(okButton, &QPushButton::clicked, &dialog, [this, &aspect, modelList, &dialog]() {
QString value = modelList->currentText().trimmed();
if (!value.isEmpty()) {
aspect.setValue(value);
writeSettings();
dialog.accept();
}
});
auto cancelButton = new QPushButton(TrConstants::CANCEL);
connect(cancelButton, &QPushButton::clicked, &dialog, &QDialog::reject);
dialog.buttonLayout()->addWidget(cancelButton);
dialog.buttonLayout()->addWidget(okButton);
modelList->setFocus();
dialog.exec();
}
void GeneralSettings::showUrlSelectionDialog(
Utils::StringAspect &aspect, const QStringList &predefinedUrls)
{
SettingsDialog dialog(TrConstants::URL_SELECTION);
dialog.addLabel(TrConstants::URL_SELECTION_INFO);
dialog.addSpacing();
QStringList allUrls = predefinedUrls;
QString key
= QString("CompleterHistory/")
.append((&aspect == &ccUrl) ? Constants::CC_URL_HISTORY : Constants::CA_URL_HISTORY);
QStringList historyList = qtcSettings()->value(Utils::Key(key.toLocal8Bit())).toStringList();
allUrls.append(historyList);
allUrls.removeDuplicates();
auto urlList = dialog.addComboBox(allUrls, aspect.value());
dialog.addSpacing();
auto okButton = new QPushButton(TrConstants::OK);
connect(okButton, &QPushButton::clicked, &dialog, [this, &aspect, urlList, &dialog]() {
QString value = urlList->currentText().trimmed();
if (!value.isEmpty()) {
aspect.setValue(value);
writeSettings();
dialog.accept();
}
});
auto cancelButton = new QPushButton(TrConstants::CANCEL);
connect(cancelButton, &QPushButton::clicked, &dialog, &QDialog::reject);
dialog.buttonLayout()->addWidget(cancelButton);
dialog.buttonLayout()->addWidget(okButton);
urlList->setFocus();
dialog.exec();
}
void GeneralSettings::setupConnections()
{
connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
Logger::instance().setLoggingEnabled(enableLogging.volatileValue());
});
connect(&resetToDefaults, &ButtonAspect::clicked, this, &GeneralSettings::resetPageToDefaults);
}
void GeneralSettings::resetPageToDefaults()
{
QMessageBox::StandardButton reply;
reply = QMessageBox::question(
Core::ICore::dialogParent(),
Tr::tr("Reset Settings"),
Tr::tr("Are you sure you want to reset all settings to default values?"),
QMessageBox::Yes | QMessageBox::No);
reply = QMessageBox::question(Core::ICore::dialogParent(),
TrConstants::RESET_SETTINGS,
TrConstants::CONFIRMATION,
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
resetAspect(enableQodeAssist);
resetAspect(enableAutoComplete);
resetAspect(enableLogging);
resetAspect(startSuggestionTimer);
resetAspect(autoCompletionTypingInterval);
resetAspect(autoCompletionCharThreshold);
resetAspect(llmProviders);
resetAspect(chatLlmProviders);
resetAspect(fimPrompts);
resetAspect(chatPrompts);
resetAspect(chatTokensThreshold);
}
modelName.setVolatileValue("");
chatModelName.setVolatileValue("");
updateStatusIndicators();
}
void GeneralSettings::updateStatusIndicators()
{
bool fimUrlValid = !url.volatileValue().isEmpty() && !endPoint.volatileValue().isEmpty();
bool fimModelValid = !modelName.volatileValue().isEmpty();
bool chatUrlValid = !chatUrl.volatileValue().isEmpty()
&& !chatEndPoint.volatileValue().isEmpty();
bool chatModelValid = !chatModelName.volatileValue().isEmpty();
bool fimPingSuccessful = false;
if (fimUrlValid) {
QUrl pingUrl(url.volatileValue());
fimPingSuccessful = Settings::pingUrl(pingUrl);
}
bool chatPingSuccessful = false;
if (chatUrlValid) {
QUrl pingUrl(chatUrl.volatileValue());
chatPingSuccessful = Settings::pingUrl(pingUrl);
}
setIndicatorStatus(fimModelIndicator,
fimModelValid ? tr("Model is properly configured")
: tr("No model selected or model name is invalid"),
fimModelValid);
setIndicatorStatus(fimUrlIndicator,
fimPingSuccessful ? tr("Server is reachable")
: tr("Server is not reachable or URL is invalid"),
fimPingSuccessful);
setIndicatorStatus(chatModelIndicator,
chatModelValid ? tr("Model is properly configured")
: tr("No model selected or model name is invalid"),
chatModelValid);
setIndicatorStatus(chatUrlIndicator,
chatPingSuccessful ? tr("Server is reachable")
: tr("Server is not reachable or URL is invalid"),
chatPingSuccessful);
}
void GeneralSettings::setIndicatorStatus(Utils::StringAspect &indicator,
const QString &tooltip,
bool isValid)
{
const Utils::Icon &icon = isValid ? Utils::Icons::OK : Utils::Icons::WARNING;
indicator.setLabelPixmap(icon.pixmap());
indicator.setToolTip(tooltip);
}
void GeneralSettings::setCurrentFimProvider(const QString &name)
{
const auto provider = LLMCore::ProvidersManager::instance().setCurrentFimProvider(name);
if (!provider)
return;
url.setValue(provider->url());
endPoint.setValue(provider->completionEndpoint());
}
void GeneralSettings::setCurrentChatProvider(const QString &name)
{
const auto provider = LLMCore::ProvidersManager::instance().setCurrentChatProvider(name);
if (!provider)
return;
chatUrl.setValue(provider->url());
chatEndPoint.setValue(provider->chatEndpoint());
}
void GeneralSettings::loadProviders()
{
for (const auto &name : LLMCore::ProvidersManager::instance().providersNames()) {
llmProviders.addOption(name);
chatLlmProviders.addOption(name);
}
}
void GeneralSettings::loadPrompts()
{
for (const auto &name : LLMCore::PromptTemplateManager::instance().fimTemplatesNames()) {
fimPrompts.addOption(name);
}
for (const auto &name : LLMCore::PromptTemplateManager::instance().chatTemplatesNames()) {
chatPrompts.addOption(name);
resetAspect(ccProvider);
resetAspect(ccModel);
resetAspect(ccTemplate);
resetAspect(ccUrl);
resetAspect(caProvider);
resetAspect(caModel);
resetAspect(caTemplate);
resetAspect(caUrl);
writeSettings();
}
}
@ -392,8 +328,10 @@ public:
GeneralSettingsPage()
{
setId(Constants::QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID);
setDisplayName(Tr::tr("General"));
setDisplayName(TrConstants::GENERAL);
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
setDisplayCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY);
setCategoryIconPath(":/resources/images/qoderassist-icon.png");
setSettingsProvider([] { return &generalSettings(); });
}
};

View File

@ -21,7 +21,7 @@
#include <utils/aspects.h>
#include "SettingsUtils.hpp"
#include "ButtonAspect.hpp"
namespace QodeAssist::LLMCore {
class Provider;
@ -34,50 +34,55 @@ 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};
Utils::SelectionAspect chatLlmProviders{this};
Utils::StringAspect chatUrl{this};
Utils::StringAspect chatEndPoint{this};
// code completion setttings
Utils::StringAspect ccProvider{this};
ButtonAspect ccSelectProvider{this};
Utils::StringAspect chatModelName{this};
ButtonAspect chatSelectModels{this};
Utils::SelectionAspect chatPrompts{this};
Utils::StringAspect ccModel{this};
ButtonAspect ccSelectModel{this};
Utils::StringAspect fimModelIndicator{this};
Utils::StringAspect fimUrlIndicator{this};
Utils::StringAspect chatModelIndicator{this};
Utils::StringAspect chatUrlIndicator{this};
Utils::StringAspect ccTemplate{this};
ButtonAspect ccSelectTemplate{this};
Utils::IntegerAspect chatTokensThreshold{this};
Utils::StringAspect ccUrl{this};
ButtonAspect ccSetUrl{this};
Utils::StringAspect ccStatus{this};
ButtonAspect ccTest{this};
// chat assistant settings
Utils::StringAspect caProvider{this};
ButtonAspect caSelectProvider{this};
Utils::StringAspect caModel{this};
ButtonAspect caSelectModel{this};
Utils::StringAspect caTemplate{this};
ButtonAspect caSelectTemplate{this};
Utils::StringAspect caUrl{this};
ButtonAspect caSetUrl{this};
Utils::StringAspect caStatus{this};
ButtonAspect caTest{this};
void showSelectionDialog(const QStringList &data,
Utils::StringAspect &aspect,
const QString &title = {},
const QString &text = {});
void showModelsNotFoundDialog(Utils::StringAspect &aspect);
void showModelsNotSupportedDialog(Utils::StringAspect &aspect);
void showUrlSelectionDialog(Utils::StringAspect &aspect, const QStringList &predefinedUrls);
private:
void setupConnections();
void showModelSelectionDialog(Utils::StringAspect *modelNameObj, LLMCore::Provider *provider);
void resetPageToDefaults();
void updateStatusIndicators();
void setIndicatorStatus(Utils::StringAspect &indicator, const QString &tooltip, bool isValid);
void setCurrentFimProvider(const QString &name);
void setCurrentChatProvider(const QString &name);
void loadProviders();
void loadPrompts();
};
GeneralSettings &generalSettings();

View File

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

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

View File

@ -24,36 +24,41 @@ namespace QodeAssist::Constants {
const char ACTION_ID[] = "QodeAssist.Action";
const char MENU_ID[] = "QodeAssist.Menu";
// new settings
const char CC_PROVIDER[] = "QodeAssist.ccProvider";
const char CC_MODEL[] = "QodeAssist.ccModel";
const char CC_MODEL_HISTORY[] = "QodeAssist.ccModelHistory";
const char CC_TEMPLATE[] = "QodeAssist.ccTemplate";
const char CC_URL[] = "QodeAssist.ccUrl";
const char CC_URL_HISTORY[] = "QodeAssist.ccUrlHistory";
const char CA_PROVIDER[] = "QodeAssist.caProvider";
const char CA_MODEL[] = "QodeAssist.caModel";
const char CA_MODEL_HISTORY[] = "QodeAssist.caModelHistory";
const char CA_TEMPLATE[] = "QodeAssist.caTemplate";
const char CA_URL[] = "QodeAssist.caUrl";
const char CA_URL_HISTORY[] = "QodeAssist.caUrlHistory";
// settings
const char ENABLE_QODE_ASSIST[] = "QodeAssist.enableQodeAssist";
const char ENABLE_AUTO_COMPLETE[] = "QodeAssist.enableAutoComplete";
const char CC_AUTO_COMPLETION[] = "QodeAssist.ccAutoCompletion";
const char ENABLE_LOGGING[] = "QodeAssist.enableLogging";
const char LLM_PROVIDERS[] = "QodeAssist.llmProviders";
const char URL[] = "QodeAssist.url";
const char END_POINT[] = "QodeAssist.endPoint";
const char MODEL_NAME[] = "QodeAssist.modelName";
const char SELECT_MODELS[] = "QodeAssist.selectModels";
const char FIM_PROMPTS[] = "QodeAssist.fimPrompts";
const char PROVIDER_PATHS[] = "QodeAssist.providerPaths";
const char START_SUGGESTION_TIMER[] = "QodeAssist.startSuggestionTimer";
const char AUTO_COMPLETION_CHAR_THRESHOLD[] = "QodeAssist.autoCompletionCharThreshold";
const char AUTO_COMPLETION_TYPING_INTERVAL[] = "QodeAssist.autoCompletionTypingInterval";
const char СС_START_SUGGESTION_TIMER[] = "QodeAssist.startSuggestionTimer";
const char СС_AUTO_COMPLETION_CHAR_THRESHOLD[] = "QodeAssist.autoCompletionCharThreshold";
const char СС_AUTO_COMPLETION_TYPING_INTERVAL[] = "QodeAssist.autoCompletionTypingInterval";
const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
const char MULTILINE_COMPLETION[] = "QodeAssist.multilineCompletion";
const char CC_MULTILINE_COMPLETION[] = "QodeAssist.ccMultilineCompletion";
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
const char CHAT_LLM_PROVIDERS[] = "QodeAssist.chatLlmProviders";
const char CHAT_URL[] = "QodeAssist.chatUrl";
const char CHAT_END_POINT[] = "QodeAssist.chatEndPoint";
const char CHAT_MODEL_NAME[] = "QodeAssist.chatModelName";
const char CHAT_SELECT_MODELS[] = "QodeAssist.chatSelectModels";
const char CHAT_PROMPTS[] = "QodeAssist.chatPrompts";
const char CHAT_TOKENS_THRESHOLD[] = "QodeAssist.chatTokensThreshold";
const char CA_TOKENS_THRESHOLD[] = "QodeAssist.caTokensThreshold";
const char CA_SHARING_CURRENT_FILE[] = "QodeAssist.caSharingCurrentFile";
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
const char QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID[] = "QodeAssist.1GeneralSettingsPageId";
const char QODE_ASSIST_CONTEXT_SETTINGS_PAGE_ID[] = "QodeAssist.2ContextSettingsPageId";
const char QODE_ASSIST_PRESET_PROMPTS_SETTINGS_PAGE_ID[]
= "QodeAssist.3PresetPromptsSettingsPageId";
const char QODE_ASSIST_CODE_COMPLETION_SETTINGS_PAGE_ID[]
= "QodeAssist.2CodeCompletionSettingsPageId";
const char QODE_ASSIST_CHAT_ASSISTANT_SETTINGS_PAGE_ID[]
= "QodeAssist.3ChatAssistantSettingsPageId";
const char QODE_ASSIST_CUSTOM_PROMPT_SETTINGS_PAGE_ID[] = "QodeAssist.4CustomPromptSettingsPageId";
const char QODE_ASSIST_GENERAL_OPTIONS_CATEGORY[] = "QodeAssist.Category";
@ -62,41 +67,43 @@ const char QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY[] = "Qode Assist";
const char QODE_ASSIST_REQUEST_SUGGESTION[] = "QodeAssist.RequestSuggestion";
// context settings
const char READ_FULL_FILE[] = "QodeAssist.readFullFile";
const char READ_STRINGS_BEFORE_CURSOR[] = "QodeAssist.readStringsBeforeCursor";
const char READ_STRINGS_AFTER_CURSOR[] = "QodeAssist.readStringsAfterCursor";
const char USE_SYSTEM_PROMPT[] = "QodeAssist.useSystemPrompt";
const char USE_FILE_PATH_IN_CONTEXT[] = "QodeAssist.useFilePathInContext";
const char SYSTEM_PROMPT[] = "QodeAssist.systemPrompt";
const char USE_PROJECT_CHANGES_CACHE[] = "QodeAssist.useProjectChangesCache";
const char MAX_CHANGES_CACHE_SIZE[] = "QodeAssist.maxChangesCacheSize";
const char USE_CHAT_SYSTEM_PROMPT[] = "QodeAssist.useChatSystemPrompt";
const char CHAT_SYSTEM_PROMPT[] = "QodeAssist.chatSystemPrompt";
const char CC_READ_FULL_FILE[] = "QodeAssist.ccReadFullFile";
const char CC_READ_STRINGS_BEFORE_CURSOR[] = "QodeAssist.ccReadStringsBeforeCursor";
const char CC_READ_STRINGS_AFTER_CURSOR[] = "QodeAssist.ccReadStringsAfterCursor";
const char CC_USE_SYSTEM_PROMPT[] = "QodeAssist.ccUseSystemPrompt";
const char CC_USE_FILE_PATH_IN_CONTEXT[] = "QodeAssist.ccUseFilePathInContext";
const char CC_SYSTEM_PROMPT[] = "QodeAssist.ccSystemPrompt";
const char CC_USE_PROJECT_CHANGES_CACHE[] = "QodeAssist.ccUseProjectChangesCache";
const char CC_MAX_CHANGES_CACHE_SIZE[] = "QodeAssist.ccMaxChangesCacheSize";
const char CA_USE_SYSTEM_PROMPT[] = "QodeAssist.useChatSystemPrompt";
const char CA_SYSTEM_PROMPT[] = "QodeAssist.chatSystemPrompt";
// preset prompt settings
const char FIM_TEMPERATURE[] = "QodeAssist.fimTemperature";
const char FIM_MAX_TOKENS[] = "QodeAssist.fimMaxTokens";
const char FIM_USE_TOP_P[] = "QodeAssist.fimUseTopP";
const char FIM_TOP_P[] = "QodeAssist.fimTopP";
const char FIM_USE_TOP_K[] = "QodeAssist.fimUseTopK";
const char FIM_TOP_K[] = "QodeAssist.fimTopK";
const char FIM_USE_PRESENCE_PENALTY[] = "QodeAssist.fimUsePresencePenalty";
const char FIM_PRESENCE_PENALTY[] = "QodeAssist.fimPresencePenalty";
const char FIM_USE_FREQUENCY_PENALTY[] = "QodeAssist.fimUseFrequencyPenalty";
const char FIM_FREQUENCY_PENALTY[] = "QodeAssist.fimFrequencyPenalty";
const char FIM_OLLAMA_LIVETIME[] = "QodeAssist.fimOllamaLivetime";
const char FIM_API_KEY[] = "QodeAssist.apiKey";
const char CHAT_TEMPERATURE[] = "QodeAssist.chatTemperature";
const char CHAT_MAX_TOKENS[] = "QodeAssist.chatMaxTokens";
const char CHAT_USE_TOP_P[] = "QodeAssist.chatUseTopP";
const char CHAT_TOP_P[] = "QodeAssist.chatTopP";
const char CHAT_USE_TOP_K[] = "QodeAssist.chatUseTopK";
const char CHAT_TOP_K[] = "QodeAssist.chatTopK";
const char CHAT_USE_PRESENCE_PENALTY[] = "QodeAssist.chatUsePresencePenalty";
const char CHAT_PRESENCE_PENALTY[] = "QodeAssist.chatPresencePenalty";
const char CHAT_USE_FREQUENCY_PENALTY[] = "QodeAssist.chatUseFrequencyPenalty";
const char CHAT_FREQUENCY_PENALTY[] = "QodeAssist.chatFrequencyPenalty";
const char CHAT_OLLAMA_LIVETIME[] = "QodeAssist.chatOllamaLivetime";
const char CHAT_API_KEY[] = "QodeAssist.chatApiKey";
const char CC_TEMPERATURE[] = "QodeAssist.ccTemperature";
const char CC_MAX_TOKENS[] = "QodeAssist.ccMaxTokens";
const char CC_USE_TOP_P[] = "QodeAssist.ccUseTopP";
const char CC_TOP_P[] = "QodeAssist.ccTopP";
const char CC_USE_TOP_K[] = "QodeAssist.ccUseTopK";
const char CC_TOP_K[] = "QodeAssist.ccTopK";
const char CC_USE_PRESENCE_PENALTY[] = "QodeAssist.ccUsePresencePenalty";
const char CC_PRESENCE_PENALTY[] = "QodeAssist.ccPresencePenalty";
const char CC_USE_FREQUENCY_PENALTY[] = "QodeAssist.fimUseFrequencyPenalty";
const char CC_FREQUENCY_PENALTY[] = "QodeAssist.fimFrequencyPenalty";
const char CC_OLLAMA_LIVETIME[] = "QodeAssist.fimOllamaLivetime";
const char CC_OLLAMA_CONTEXT_WINDOW[] = "QodeAssist.ccOllamaContextWindow";
const char CC_API_KEY[] = "QodeAssist.apiKey";
const char CA_TEMPERATURE[] = "QodeAssist.chatTemperature";
const char CA_MAX_TOKENS[] = "QodeAssist.chatMaxTokens";
const char CA_USE_TOP_P[] = "QodeAssist.chatUseTopP";
const char CA_TOP_P[] = "QodeAssist.chatTopP";
const char CA_USE_TOP_K[] = "QodeAssist.chatUseTopK";
const char CA_TOP_K[] = "QodeAssist.chatTopK";
const char CA_USE_PRESENCE_PENALTY[] = "QodeAssist.chatUsePresencePenalty";
const char CA_PRESENCE_PENALTY[] = "QodeAssist.chatPresencePenalty";
const char CA_USE_FREQUENCY_PENALTY[] = "QodeAssist.chatUseFrequencyPenalty";
const char CA_FREQUENCY_PENALTY[] = "QodeAssist.chatFrequencyPenalty";
const char CA_OLLAMA_LIVETIME[] = "QodeAssist.chatOllamaLivetime";
const char CA_OLLAMA_CONTEXT_WINDOW[] = "QodeAssist.caOllamaContextWindow";
const char CA_API_KEY[] = "QodeAssist.chatApiKey";
} // namespace QodeAssist::Constants

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/>.
*/
#include "SettingsDialog.hpp"
namespace QodeAssist::Settings {
SettingsDialog::SettingsDialog(const QString &title, QWidget *parent)
: QDialog(parent)
, m_mainLayout(new QVBoxLayout(this))
, m_buttonLayout(nullptr)
{
setWindowTitle(title);
m_mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
}
QLabel *SettingsDialog::addLabel(const QString &text)
{
auto label = new QLabel(text, this);
label->setWordWrap(true);
label->setMinimumWidth(300);
m_mainLayout->addWidget(label);
return label;
}
QLineEdit *SettingsDialog::addInputField(const QString &labelText, const QString &value)
{
auto inputLayout = new QGridLayout;
auto inputLabel = new QLabel(labelText, this);
auto inputField = new QLineEdit(value, this);
inputField->setMinimumWidth(200);
inputLayout->addWidget(inputLabel, 0, 0);
inputLayout->addWidget(inputField, 0, 1);
inputLayout->setColumnStretch(1, 1);
m_mainLayout->addLayout(inputLayout);
return inputField;
}
void SettingsDialog::addSpacing(int space)
{
m_mainLayout->addSpacing(space);
}
QHBoxLayout *SettingsDialog::buttonLayout()
{
if (!m_buttonLayout) {
m_buttonLayout = new QHBoxLayout;
m_buttonLayout->addStretch();
m_mainLayout->addLayout(m_buttonLayout);
}
return m_buttonLayout;
}
QComboBox *SettingsDialog::addComboBox(
const QStringList &items, const QString &currentText, bool editable)
{
auto comboBox = new QComboBox(this);
comboBox->addItems(items);
comboBox->setCurrentText(currentText);
comboBox->setMinimumWidth(300);
comboBox->setEditable(editable);
m_mainLayout->addWidget(comboBox);
return comboBox;
}
QVBoxLayout *SettingsDialog::mainLayout() const
{
return m_mainLayout;
}
} // namespace QodeAssist::Settings

View File

@ -17,38 +17,34 @@
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QComboBox>
#include <QDialog>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
#include <QWidget>
#include <utils/aspects.h>
#include "SettingsUtils.hpp"
#include <coreplugin/icore.h>
namespace QodeAssist::Settings {
class ContextSettings : public Utils::AspectContainer
class SettingsDialog : public QDialog
{
public:
ContextSettings();
explicit SettingsDialog(const QString &title, QWidget *parent = Core::ICore::dialogParent());
Utils::BoolAspect readFullFile{this};
Utils::IntegerAspect readStringsBeforeCursor{this};
Utils::IntegerAspect readStringsAfterCursor{this};
QLabel *addLabel(const QString &text);
QLineEdit *addInputField(const QString &labelText, const QString &value);
void addSpacing(int space = 12);
QHBoxLayout *buttonLayout();
QVBoxLayout *mainLayout() const;
Utils::BoolAspect useSystemPrompt{this};
Utils::StringAspect systemPrompt{this};
Utils::BoolAspect useFilePathInContext{this};
Utils::BoolAspect useProjectChangesCache{this};
Utils::IntegerAspect maxChangesCacheSize{this};
Utils::BoolAspect useChatSystemPrompt{this};
Utils::StringAspect chatSystemPrompt{this};
ButtonAspect resetToDefaults{this};
QComboBox *addComboBox(
const QStringList &items, const QString &currentText = QString(), bool editable = true);
private:
void setupConnection();
void resetPageToDefaults();
QVBoxLayout *m_mainLayout;
QHBoxLayout *m_buttonLayout;
};
ContextSettings &contextSettings();
} // namespace QodeAssist::Settings

90
settings/SettingsTr.hpp Normal file
View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QCoreApplication>
namespace QodeAssist::Settings {
namespace TrConstants {
inline const char *ENABLE_QODE_ASSIST = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Enable Qode Assist");
inline const char *GENERAL = QT_TRANSLATE_NOOP("QtC::QodeAssist", "General");
inline const char *RESET_TO_DEFAULTS = QT_TRANSLATE_NOOP("QtC::QodeAssist",
"Reset Page to Defaults");
inline const char *SELECT = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Select...");
inline const char *PROVIDER = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Provider:");
inline const char *MODEL = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Model:");
inline const char *TEMPLATE = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Template:");
inline const char *URL = QT_TRANSLATE_NOOP("QtC::QodeAssist", "URL:");
inline const char *STATUS = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Status:");
inline const char *TEST = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Test");
inline const char *ENABLE_LOG = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Enable Logging");
inline const char *CODE_COMPLETION = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Code Completion");
inline const char *CHAT_ASSISTANT = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Chat Assistant");
inline const char *RESET_SETTINGS = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Reset Settings");
inline const char *CONFIRMATION
= QT_TRANSLATE_NOOP("QtC::QodeAssist",
"Are you sure you want to reset all settings to default values?");
inline const char CONNECTION_ERROR[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Connection Error");
inline const char NO_MODELS_FOUND[]
= QT_TRANSLATE_NOOP("QtC::QodeAssist", "Unable to retrieve the list of models from the server.");
inline const char CHECK_CONNECTION[] = QT_TRANSLATE_NOOP(
"QtC::QodeAssist",
"Please verify the following:\n"
"- Server is running and accessible\n"
"- URL is correct\n"
"- Provider is properly configured\n\n"
"You can try selecting a different provider or changing the URL:");
inline const char SELECT_PROVIDER[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Select Provider");
inline const char SELECT_URL[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Select URL");
inline const char CLOSE[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Close");
inline const char MODEL_SELECTION[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Model Selection");
inline const char MODEL_LISTING_NOT_SUPPORTED_INFO[] = QT_TRANSLATE_NOOP(
"QtC::QodeAssist",
"Select from previously used models or enter a new model name.\n\n"
"If entering a new model name:\n"
"• For providers with automatic listing - ensure the model is installed\n"
"• For providers without listing support - check provider's documentation\n"
"• Make sure the model name matches exactly");
inline const char MODEL_NAME[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Model name:");
inline const char OK[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "OK");
inline const char CANCEL[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Cancel");
inline const char ENTER_MODEL_MANUALLY[]
= QT_TRANSLATE_NOOP("QtC::QodeAssist", "Enter Model Manually");
inline const char URL_SELECTION[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "URL Selection");
inline const char URL_SELECTION_INFO[] = QT_TRANSLATE_NOOP(
"QtC::QodeAssist",
"Select from the list of default and previously used URLs, or enter a custom one.\n"
"Please ensure the selected URL is accessible and the service is running.");
inline const char PREDEFINED_URL[]
= QT_TRANSLATE_NOOP("QtC::QodeAssist", "Use default provider URL or from history");
inline const char CUSTOM_URL[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Enter custom URL");
inline const char ENTER_MODEL_MANUALLY_BUTTON[]
= QT_TRANSLATE_NOOP("QtC::QodeAssist", "Enter Model Name Manually");
} // namespace TrConstants
struct Tr
{
Q_DECLARE_TR_FUNCTIONS(QtC::QodeAssist)
};
} // namespace QodeAssist::Settings

View File

@ -29,11 +29,6 @@
namespace QodeAssist::Settings {
struct Tr
{
Q_DECLARE_TR_FUNCTIONS(QtC::QodeAssist)
};
inline bool pingUrl(const QUrl &url, int timeout = 5000)
{
if (!url.isValid()) {
@ -72,25 +67,15 @@ void resetAspect(AspectType &aspect)
aspect.setVolatileValue(aspect.defaultValue());
}
class ButtonAspect : public Utils::BaseAspect
inline void initStringAspect(Utils::StringAspect &aspect,
const Utils::Key &settingsKey,
const QString &labelText,
const QString &defaultValue)
{
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();
};
aspect.setSettingsKey(settingsKey);
aspect.setLabelText(labelText);
aspect.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
aspect.setDefaultValue(defaultValue);
}
} // namespace QodeAssist::Settings

49
templates/BasicChat.hpp Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QJsonArray>
#include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class BasicChat : public LLMCore::PromptTemplate
{
public:
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
QString name() const override { return "Basic Chat"; }
QString promptTemplate() const override { return {}; }
QStringList stopWords() const override { return QStringList(); }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages = request["messages"].toArray();
QJsonObject newMessage;
newMessage["role"] = "user";
newMessage["content"] = context.prefix;
messages.append(newMessage);
request["messages"] = messages;
}
};
} // namespace QodeAssist::Templates

View File

@ -19,7 +19,8 @@
#pragma once
#include <QtCore/qjsonarray.h>
#include <QJsonArray>
#include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
@ -46,4 +47,10 @@ public:
}
};
class LlamaChat : public CodeLlamaChat
{
public:
QString name() const override { return "Llama Chat"; }
};
} // namespace QodeAssist::Templates

View File

@ -28,7 +28,7 @@ class CodeLlamaFim : public LLMCore::PromptTemplate
public:
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
QString name() const override { return "CodeLlama FIM"; }
QString promptTemplate() const override { return "%1<PRE> %2 <SUF>%3 <MID>"; }
QString promptTemplate() const override { return "<PRE> %1 <SUF>%2 <MID>"; }
QStringList stopWords() const override
{
return QStringList() << "<EOT>" << "<PRE>" << "<SUF" << "<MID>";
@ -36,9 +36,7 @@ public:
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QString formattedPrompt = promptTemplate().arg(context.systemPrompt,
context.prefix,
context.suffix);
QString formattedPrompt = promptTemplate().arg(context.prefix, context.suffix);
request["prompt"] = formattedPrompt;
}
};

View File

@ -62,7 +62,6 @@ private:
{
if (value.isString()) {
QString str = value.toString();
str.replace("{{QODE_INSTRUCTIONS}}", context.systemPrompt);
str.replace("{{QODE_PREFIX}}", context.prefix);
str.replace("{{QODE_SUFFIX}}", context.suffix);
return str;

View File

@ -30,14 +30,12 @@ public:
QString name() const override { return "DeepSeekCoder FIM"; }
QString promptTemplate() const override
{
return "%1<fim▁begin>%2<fim▁hole>%3<fim▁end>";
return "<fim▁begin>%1<fim▁hole>%2<fim▁end>";
}
QStringList stopWords() const override { return QStringList(); }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QString formattedPrompt = promptTemplate().arg(context.systemPrompt,
context.prefix,
context.suffix);
QString formattedPrompt = promptTemplate().arg(context.prefix, context.suffix);
request["prompt"] = formattedPrompt;
}
};

64
templates/Ollama.hpp Normal file
View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QJsonArray>
#include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class OllamaAutoFim : public LLMCore::PromptTemplate
{
public:
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
QString name() const override { return "Ollama Auto FIM"; }
QString promptTemplate() const override { return {}; }
QStringList stopWords() const override { return QStringList(); }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
request["prompt"] = context.prefix;
request["suffix"] = context.suffix;
}
};
class OllamaAutoChat : public LLMCore::PromptTemplate
{
public:
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
QString name() const override { return "Ollama Auto Chat"; }
QString promptTemplate() const override { return {}; }
QStringList stopWords() const override { return QStringList(); }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages = request["messages"].toArray();
QJsonObject newMessage;
newMessage["role"] = "user";
newMessage["content"] = context.prefix;
messages.append(newMessage);
request["messages"] = messages;
}
};
} // namespace QodeAssist::Templates

71
templates/Qwen.hpp Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QJsonArray>
#include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class QwenChat : public LLMCore::PromptTemplate
{
public:
QString name() const override { return "Qwen Chat"; }
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
QString promptTemplate() const override { return "### Instruction:\n%1\n### Response:\n"; }
QStringList stopWords() const override
{
return QStringList() << "### Instruction:" << "### Response:" << "\n\n### " << "<|EOT|>";
}
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QString formattedPrompt = promptTemplate().arg(context.prefix);
QJsonArray messages = request["messages"].toArray();
QJsonObject newMessage;
newMessage["role"] = "user";
newMessage["content"] = formattedPrompt;
messages.append(newMessage);
request["messages"] = messages;
}
};
class QwenFim : public LLMCore::PromptTemplate
{
public:
QString name() const override { return "Qwen FIM"; }
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
QString promptTemplate() const override
{
return "<|fim_prefix|>%1<|fim_suffix|>%2<|fim_middle|>";
}
QStringList stopWords() const override { return QStringList() << "<|endoftext|>" << "<|EOT|>"; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QString formattedPrompt = promptTemplate().arg(context.prefix, context.suffix);
request["prompt"] = formattedPrompt;
}
};
} // namespace QodeAssist::Templates

View File

@ -28,7 +28,7 @@ class StarCoder2Fim : public LLMCore::PromptTemplate
public:
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
QString name() const override { return "StarCoder2 FIM"; }
QString promptTemplate() const override { return "%1<fim_prefix>%2<fim_suffix>%3<fim_middle>"; }
QString promptTemplate() const override { return "<fim_prefix>%1<fim_suffix>%2<fim_middle>"; }
QStringList stopWords() const override
{
return QStringList() << "<|endoftext|>" << "<file_sep>" << "<fim_prefix>" << "<fim_suffix>"
@ -36,9 +36,7 @@ public:
}
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QString formattedPrompt = promptTemplate().arg(context.systemPrompt,
context.prefix,
context.suffix);
QString formattedPrompt = promptTemplate().arg(context.prefix, context.suffix);
request["prompt"] = formattedPrompt;
}
};

View File

@ -24,19 +24,17 @@
namespace QodeAssist::Templates {
class QwenChat : public LLMCore::PromptTemplate
class StarCoderChat : public LLMCore::PromptTemplate
{
public:
QString name() const override { return "Qwen Chat"; }
QString name() const override { return "StarCoder Chat"; }
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
QString promptTemplate() const override { return "### Instruction:\n%1\n### Response:\n"; }
QStringList stopWords() const override
{
return QStringList() << "### Instruction:" << "### Response:" << "\n\n### " << "<|EOT|>";
return QStringList() << "###"
<< "<|endoftext|>" << "<file_sep>";
}
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QString formattedPrompt = promptTemplate().arg(context.prefix);
@ -50,5 +48,4 @@ public:
request["messages"] = messages;
}
};
} // namespace QodeAssist::Templates