Compare commits

...

31 Commits

Author SHA1 Message Date
19c25043fb 🔖 chore: Bump version to 0.3.10 2024-11-26 11:48:10 +01:00
56b5ea8e68 feat: Improve OpenAI message handling 2024-11-26 11:43:51 +01:00
b475f15e3d feat: Improve system prompt for code completion 2024-11-26 11:29:20 +01:00
31f4516e7b feat: Add removing codeblock wrappers from code completion 2024-11-26 11:26:50 +01:00
bfdbc755e3 🐛 fix: Move api key from request json to config 2024-11-26 10:52:47 +01:00
30964d90d5 🐛 fix: Change format for context in system prompt 2024-11-26 10:15:20 +01:00
1261f913bb ♻️ refactor: Rework currents and add new templates
Add Alpaca, Llama3, LLama2, ChatML templates
2024-11-26 00:28:27 +01:00
36d5242a1f 🐛 fix: Removing message from chat after complete receiving 2024-11-25 23:00:53 +01:00
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
6d3bc362b3 Fix all warnings in QML 2024-11-11 11:18:37 +08:00
70 changed files with 1230 additions and 955 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

@ -15,7 +15,7 @@ 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
@ -40,14 +40,22 @@ add_qtc_plugin(QodeAssist
QodeAssistConstants.hpp
QodeAssisttr.h
LLMClientInterface.hpp LLMClientInterface.cpp
templates/Templates.hpp
templates/CodeLlamaFim.hpp
templates/StarCoder2Fim.hpp
templates/DeepSeekCoderFim.hpp
templates/CustomFimTemplate.hpp
templates/DeepSeekCoderChat.hpp
templates/CodeLlamaChat.hpp
templates/Qwen.hpp
templates/StarCoderChat.hpp
templates/Ollama.hpp
templates/BasicChat.hpp
templates/Llama3.hpp
templates/ChatML.hpp
templates/Alpaca.hpp
templates/Llama2.hpp
providers/Providers.hpp
providers/OllamaProvider.hpp providers/OllamaProvider.cpp
providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp
providers/OpenAICompatProvider.hpp providers/OpenAICompatProvider.cpp
@ -62,5 +70,3 @@ add_qtc_plugin(QodeAssist
chat/NavigationPanel.hpp chat/NavigationPanel.cpp
ConfigurationManager.hpp ConfigurationManager.cpp
)
target_link_libraries(QodeAssist PRIVATE )

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

@ -152,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;

View File

@ -24,7 +24,7 @@
#include <QAbstractListModel>
#include <QJsonArray>
#include <qqmlintegration.h>
#include <QtQmlIntegration>
namespace QodeAssist::Chat {
@ -60,7 +60,7 @@ public:
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;

View File

@ -28,8 +28,12 @@ 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)

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

@ -68,37 +68,46 @@ void ClientInterface::sendMessage(const QString &message, bool includeCurrentFil
{
cancelRequest();
m_chatModel->addMessage(message, ChatModel::ChatRole::User, "");
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 = chatAssistantSettings.systemPrompt();
QString systemPrompt;
if (chatAssistantSettings.useSystemPrompt())
systemPrompt = chatAssistantSettings.systemPrompt();
if (includeCurrentFile) {
QString fileContext = getCurrentFileContext();
if (!fileContext.isEmpty()) {
context.systemPrompt = QString("%1\n\n%2").arg(systemPrompt, fileContext);
LOG_MESSAGE("Using system prompt with file context");
} else {
context.systemPrompt = systemPrompt;
LOG_MESSAGE("Failed to get file context, using default system prompt");
systemPrompt = systemPrompt.append(fileContext);
}
} else {
context.systemPrompt = systemPrompt;
}
QJsonObject providerRequest;
providerRequest["model"] = Settings::generalSettings().caModel();
providerRequest["stream"] = true;
providerRequest["messages"] = m_chatModel->prepareMessagesForRequest(context);
providerRequest["messages"] = m_chatModel->prepareMessagesForRequest(systemPrompt);
if (promptTemplate)
promptTemplate->prepareRequest(providerRequest, context);
@ -117,11 +126,11 @@ void ClientInterface::sendMessage(const QString &message, bool includeCurrentFil
config.url = QString("%1%2").arg(Settings::generalSettings().caUrl(), provider->chatEndpoint());
config.providerRequest = providerRequest;
config.multiLineCompletion = false;
config.apiKey = Settings::chatAssistantSettings().apiKey();
QJsonObject request;
request["id"] = QUuid::createUuid().toString();
m_chatModel->addMessage(message, ChatModel::ChatRole::User, "");
m_requestHandler->sendLLMRequest(config, request);
}
@ -141,11 +150,16 @@ 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);
const auto message = response.trimmed();
if (isComplete) {
LOG_MESSAGE("Message completed. Final response for message " + messageId + ": " + response);
if (!message.isEmpty()) {
QString messageId = request["id"].toString();
m_chatModel->addMessage(message, ChatModel::ChatRole::Assistant, messageId);
if (isComplete) {
LOG_MESSAGE(
"Message completed. Final response for message " + messageId + ": " + response);
}
}
}

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,12 +74,12 @@ ChatRootView {
}
onCountChanged: {
scrollToBottom()
root.scrollToBottom()
}
onContentHeightChanged: {
if (atYEnd) {
scrollToBottom()
root.scrollToBottom()
}
}
}
@ -103,7 +106,7 @@ ChatRootView {
}
Keys.onPressed: function(event) {
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !(event.modifiers & Qt.ShiftModifier)) {
sendChatMessage()
root.sendChatMessage()
event.accepted = true;
}
}
@ -119,7 +122,7 @@ ChatRootView {
Layout.alignment: Qt.AlignBottom
text: qsTr("Send")
onClicked: sendChatMessage()
onClicked: root.sendChatMessage()
}
Button {
@ -135,7 +138,7 @@ ChatRootView {
Layout.alignment: Qt.AlignBottom
text: qsTr("Clear Chat")
onClicked: clearChat()
onClicked: root.clearChat()
}
CheckBox {

View File

@ -61,7 +61,7 @@ Rectangle {
text: root.code
readOnly: true
selectByMouse: true
font.family: monospaceFont
font.family: root.monospaceFont
font.pointSize: 12
color: parent.color.hslLightness > 0.5 ? "black" : "white"
wrapMode: Text.WordWrap

View File

@ -81,28 +81,38 @@ void ConfigurationManager::selectProvider()
void ConfigurationManager::selectModel()
{
const QString providerName = m_generalSettings.ccProvider.volatileValue();
auto *settingsButton = qobject_cast<ButtonAspect *>(sender());
if (!settingsButton)
return;
const auto providerUrl = (settingsButton == &m_generalSettings.ccSelectModel)
? m_generalSettings.ccUrl.volatileValue()
: m_generalSettings.caUrl.volatileValue();
const auto modelList = m_providersManager.getProviderByName(providerName)
->getInstalledModels(providerUrl);
const bool isCodeCompletion = (settingsButton == &m_generalSettings.ccSelectModel);
auto &targetSettings = (settingsButton == &m_generalSettings.ccSelectModel)
? m_generalSettings.ccModel
: m_generalSettings.caModel;
const QString providerName = isCodeCompletion ? m_generalSettings.ccProvider.volatileValue()
: m_generalSettings.caProvider.volatileValue();
QTimer::singleShot(0, &m_generalSettings, [this, modelList, &targetSettings]() {
m_generalSettings.showSelectionDialog(modelList,
targetSettings,
Tr::tr("Select LLM Model"),
Tr::tr("Models:"));
});
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()
@ -111,13 +121,13 @@ void ConfigurationManager::selectTemplate()
if (!settingsButton)
return;
const auto templateList = (settingsButton == &m_generalSettings.ccSelectTemplate)
? m_templateManger.fimTemplatesNames()
: m_templateManger.chatTemplatesNames();
const bool isCodeCompletion = (settingsButton == &m_generalSettings.ccSelectTemplate);
auto &targetSettings = (settingsButton == &m_generalSettings.ccSelectTemplate)
? m_generalSettings.ccTemplate
: m_generalSettings.caTemplate;
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,
@ -129,6 +139,10 @@ void ConfigurationManager::selectTemplate()
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();
@ -136,19 +150,12 @@ void ConfigurationManager::selectUrl()
urls.append(url);
}
auto *settingsButton = qobject_cast<ButtonAspect *>(sender());
if (!settingsButton)
return;
auto &targetSettings = (settingsButton == &m_generalSettings.ccSetUrl)
? m_generalSettings.ccUrl
: m_generalSettings.caUrl;
QTimer::singleShot(0, &m_generalSettings, [this, urls, &targetSettings]() {
m_generalSettings.showSelectionDialog(urls,
targetSettings,
Tr::tr("Select URL"),
Tr::tr("URLs:"));
m_generalSettings.showUrlSelectionDialog(targetSettings, urls);
});
}

View File

@ -207,9 +207,16 @@ 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.append("\n ").append(getLanguageAndFileInfo());
if (Settings::codeCompletionSettings().useProjectChangesCache())
fileContext.append("\n ").append(
ChangesManager::instance().getRecentChangesContext(m_textDocument));
return {contextBefore, contextAfter, fileContext};
}
QString DocumentContextReader::getContextBefore(int lineNumber, int cursorPosition) const
@ -239,17 +246,4 @@ QString DocumentContextReader::getContextAfter(int lineNumber, int cursorPositio
}
}
QString DocumentContextReader::getInstructions() const
{
QString instructions;
if (Settings::codeCompletionSettings().useFilePathInContext())
instructions += getLanguageAndFileInfo();
if (Settings::codeCompletionSettings().useProjectChangesCache())
instructions += ChangesManager::instance().getRecentChangesContext(m_textDocument);
return instructions;
}
} // namespace QodeAssist

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

@ -152,25 +152,43 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
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 = provider;
config.promptTemplate = promptTemplate;
config.url = QUrl(
QString("%1%2").arg(Settings::generalSettings().ccUrl(), provider->completionEndpoint()));
config.apiKey = Settings::codeCompletionSettings().apiKey();
config.providerRequest = {{"model", Settings::generalSettings().ccModel()}, {"stream", true}};
config.providerRequest = {{"model", Settings::generalSettings().ccModel()},
{"stream", true},
{"stop",
QJsonArray::fromStringList(config.promptTemplate->stopWords())}};
config.multiLineCompletion = completeSettings.multiLineCompletion();
QString systemPrompt;
if (completeSettings.useSystemPrompt())
config.providerRequest["system"] = completeSettings.systemPrompt();
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.6",
"Version" : "0.3.10",
"CompatVersion" : "${IDE_VERSION_COMPAT}",
"Vendor" : "Petr Mironychev",
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",

View File

@ -1,4 +1,5 @@
# 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)
@ -29,7 +30,7 @@ QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides
- Support for multiple LLM providers:
- Ollama
- LM Studio
- OpenAI-compatible local providers
- OpenAI-compatible providers(eg. https://openrouter.ai)
- Extensive library of model-specific templates
- Custom template support
- Easy configuration and model selection
@ -55,13 +56,17 @@ QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides
2. Install [Ollama](https://ollama.com). Make sure to review the system requirements before installation.
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
```
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:
@ -72,38 +77,32 @@ ollama run codellama:7b-instruct
## Configure Plugin
<details>
<summary>Configure plugins: (click to expand)</summary>
<img src="https://github.com/user-attachments/assets/00ad980f-b470-48eb-9aaa-077783d38798" width="600" alt="Configuere QodeAssist">
</details>
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 loaded 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.
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/P5P412V96G)
## Supported LLM Providers
QodeAssist currently supports the following LLM (Large Language Model) providers:
- [Ollama](https://ollama.com)
- [LM Studio](https://lmstudio.ai)
- OpenAI compatible providers
- [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:
- Llama
- Qwen2.5-coder
- CodeLlama
- StarCoder2
- DeepSeek-Coder-V2
- Qwen-2.5
### Ollama:
### For autocomplete(FIM)
@ -121,13 +120,6 @@ ollama run qwen2.5-coder:7b-instruct
ollama run deepseek-coder-v2
```
### LM Studio:
similar models, like for ollama
Please note that while these models have been specifically tested and confirmed to work well with QodeAssist, other models compatible with the supported providers may also work. We encourage users to experiment with different models and report their experiences.
If you've successfully used a model that's not listed here, please let us know by opening an issue or submitting a pull request to update this list.
## QtCreator Version Compatibility
- QtCreator 14.0.2 - 0.2.3 - 0.3.x
@ -190,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`
@ -209,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,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

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

View File

@ -38,5 +38,6 @@ public:
virtual QString promptTemplate() const = 0;
virtual QStringList stopWords() const = 0;
virtual void prepareRequest(QJsonObject &request, const ContextData &context) const = 0;
virtual QString description() const = 0;
};
} // namespace QodeAssist::LLMCore

View File

@ -37,6 +37,7 @@ 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;

View File

@ -35,6 +35,7 @@ struct LLMConfig
QJsonObject providerRequest;
RequestType requestType;
bool multiLineCompletion;
QString apiKey;
};
} // namespace QodeAssist::LLMCore

View File

@ -38,7 +38,8 @@ void RequestHandler::sendLLMRequest(const LLMConfig &config, const QJsonObject &
QJsonDocument(config.providerRequest).toJson(QJsonDocument::Indented))));
QNetworkRequest networkRequest(config.url);
prepareNetworkRequest(networkRequest, config.providerRequest);
if (!config.apiKey.isEmpty())
prepareNetworkRequest(networkRequest, config.apiKey);
QNetworkReply *reply = m_manager->post(networkRequest,
QJsonDocument(config.providerRequest).toJson());
@ -84,6 +85,8 @@ void RequestHandler::handleLLMResponse(QNetworkReply *reply,
if (isComplete) {
auto cleanedCompletion = removeStopWords(accumulatedResponse,
config.promptTemplate->stopWords());
removeCodeBlockWrappers(cleanedCompletion);
emit completionReceived(cleanedCompletion, request, true);
}
} else if (config.requestType == RequestType::Chat) {
@ -107,33 +110,30 @@ bool RequestHandler::cancelRequest(const QString &id)
return false;
}
void RequestHandler::prepareNetworkRequest(QNetworkRequest &networkRequest,
const QJsonObject &providerRequest)
void RequestHandler::prepareNetworkRequest(
QNetworkRequest &networkRequest, const QString &apiKey) const
{
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
if (providerRequest.contains("api_key")) {
QString apiKey = providerRequest["api_key"].toString();
networkRequest.setRawHeader("Authorization", QString("Bearer %1").arg(apiKey).toUtf8());
}
networkRequest.setRawHeader("Authorization", QString("Bearer %1").arg(apiKey).toUtf8());
}
bool RequestHandler::processSingleLineCompletion(QNetworkReply *reply,
const QJsonObject &request,
const QString &accumulatedResponse,
const LLMConfig &config)
bool RequestHandler::processSingleLineCompletion(
QNetworkReply *reply,
const QJsonObject &request,
const QString &accumulatedResponse,
const LLMConfig &config)
{
int newlinePos = accumulatedResponse.indexOf('\n');
QString cleanedResponse = accumulatedResponse;
removeCodeBlockWrappers(cleanedResponse);
int newlinePos = cleanedResponse.indexOf('\n');
if (newlinePos != -1) {
QString singleLineCompletion = accumulatedResponse.left(newlinePos).trimmed();
singleLineCompletion = removeStopWords(singleLineCompletion,
config.promptTemplate->stopWords());
QString singleLineCompletion = cleanedResponse.left(newlinePos).trimmed();
singleLineCompletion
= removeStopWords(singleLineCompletion, config.promptTemplate->stopWords());
emit completionReceived(singleLineCompletion, request, true);
m_accumulatedResponses.remove(reply);
reply->abort();
return true;
}
return false;
@ -150,4 +150,36 @@ QString RequestHandler::removeStopWords(const QStringView &completion, const QSt
return filteredCompletion;
}
void RequestHandler::removeCodeBlockWrappers(QString &response)
{
static const QRegularExpression
fullCodeBlockRegex(R"(```[\w\s]*\n([\s\S]*?)```)", QRegularExpression::MultilineOption);
static const QRegularExpression
partialStartBlockRegex(R"(```[\w\s]*\n([\s\S]*?)$)", QRegularExpression::MultilineOption);
static const QRegularExpression
partialEndBlockRegex(R"(^([\s\S]*?)```)", QRegularExpression::MultilineOption);
QRegularExpressionMatchIterator matchIterator = fullCodeBlockRegex.globalMatch(response);
while (matchIterator.hasNext()) {
QRegularExpressionMatch match = matchIterator.next();
QString codeBlock = match.captured(0);
QString codeContent = match.captured(1).trimmed();
response.replace(codeBlock, codeContent);
}
QRegularExpressionMatch startMatch = partialStartBlockRegex.match(response);
if (startMatch.hasMatch()) {
QString partialBlock = startMatch.captured(0);
QString codeContent = startMatch.captured(1).trimmed();
response.replace(partialBlock, codeContent);
}
QRegularExpressionMatch endMatch = partialEndBlockRegex.match(response);
if (endMatch.hasMatch()) {
QString partialBlock = endMatch.captured(0);
QString codeContent = endMatch.captured(1).trimmed();
response.replace(partialBlock, codeContent);
}
}
} // namespace QodeAssist::LLMCore

View File

@ -52,12 +52,13 @@ private:
QMap<QString, QNetworkReply *> m_activeRequests;
QMap<QNetworkReply *, QString> m_accumulatedResponses;
void prepareNetworkRequest(QNetworkRequest &networkRequest, const QJsonObject &providerRequest);
void prepareNetworkRequest(QNetworkRequest &networkRequest, const QString &apiKey) const;
bool processSingleLineCompletion(QNetworkReply *reply,
const QJsonObject &request,
const QString &accumulatedResponse,
const LLMConfig &config);
QString removeStopWords(const QStringView &completion, const QStringList &stopWords);
void removeCodeBlockWrappers(QString &response);
};
} // namespace QodeAssist::LLMCore

View File

@ -53,6 +53,11 @@ QString LMStudioProvider::chatEndpoint() const
return "/v1/chat/completions";
}
bool LMStudioProvider::supportsModelListing() const
{
return true;
}
void LMStudioProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
{
auto prepareMessages = [](QJsonObject &req) -> QJsonArray {

View File

@ -32,6 +32,7 @@ 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 QString &url) override;

View File

@ -53,6 +53,11 @@ QString OllamaProvider::chatEndpoint() const
return "/api/chat";
}
bool OllamaProvider::supportsModelListing() const
{
return true;
}
void OllamaProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
{
auto applySettings = [&request](const auto &settings) {

View File

@ -32,6 +32,7 @@ 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 QString &url) override;

View File

@ -26,13 +26,15 @@
#include <QJsonObject>
#include <QNetworkReply>
#include "logger/Logger.hpp"
namespace QodeAssist::Providers {
OpenAICompatProvider::OpenAICompatProvider() {}
QString OpenAICompatProvider::name() const
{
return "OpenAI Compatible (experimental)";
return "OpenAI Compatible";
}
QString OpenAICompatProvider::url() const
@ -50,6 +52,11 @@ QString OpenAICompatProvider::chatEndpoint() const
return "/v1/chat/completions";
}
bool OpenAICompatProvider::supportsModelListing() const
{
return false;
}
void OpenAICompatProvider::prepareRequest(QJsonObject &request, LLMCore::RequestType type)
{
auto prepareMessages = [](QJsonObject &req) -> QJsonArray {
@ -77,10 +84,6 @@ void OpenAICompatProvider::prepareRequest(QJsonObject &request, LLMCore::Request
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);
@ -98,24 +101,41 @@ void OpenAICompatProvider::prepareRequest(QJsonObject &request, LLMCore::Request
bool OpenAICompatProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
{
bool isComplete = false;
QString tempResponse = accumulatedResponse;
while (reply->canReadLine()) {
QByteArray line = reply->readLine().trimmed();
if (line.isEmpty()) {
continue;
}
if (line == "data: [DONE]") {
if (!line.startsWith("data:")) {
continue;
}
line = line.mid(6);
if (line == "[DONE]") {
isComplete = true;
break;
}
if (line.startsWith("data: ")) {
line = line.mid(6); // Remove "data: " prefix
}
QJsonDocument jsonResponse = QJsonDocument::fromJson(line);
if (jsonResponse.isNull()) {
qWarning() << "Invalid JSON response from LM Studio:" << line;
LOG_MESSAGE(
"Invalid JSON response from OpenAI compatible provider: " + QString::fromUtf8(line));
continue;
}
QJsonObject responseObj = jsonResponse.object();
if (responseObj.contains("error")) {
LOG_MESSAGE(
"OpenAI compatible provider error: "
+ QString::fromUtf8(QJsonDocument(responseObj).toJson(QJsonDocument::Indented)));
return false;
}
if (responseObj.contains("choices")) {
QJsonArray choices = responseObj["choices"].toArray();
if (!choices.isEmpty()) {
@ -123,16 +143,30 @@ bool OpenAICompatProvider::handleResponse(QNetworkReply *reply, QString &accumul
QJsonObject delta = choice["delta"].toObject();
if (delta.contains("content")) {
QString completion = delta["content"].toString();
accumulatedResponse += completion;
if (!completion.isEmpty()) {
tempResponse += completion;
}
}
if (choice["finish_reason"].toString() == "stop") {
QString finishReason = choice["finish_reason"].toString();
if (!finishReason.isNull() && finishReason == "stop") {
isComplete = true;
break;
}
}
}
if (responseObj.contains("usage")) {
QJsonObject usage = responseObj["usage"].toObject();
LOG_MESSAGE(QString("Token usage - Prompt: %1, Completion: %2, Total: %3")
.arg(usage["prompt_tokens"].toInt())
.arg(usage["completion_tokens"].toInt())
.arg(usage["total_tokens"].toInt()));
}
}
if (!tempResponse.isEmpty()) {
accumulatedResponse = tempResponse;
}
return isComplete;
}

View File

@ -32,6 +32,7 @@ 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 QString &url) override;

37
providers/Providers.hpp Normal file
View File

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

View File

@ -43,20 +43,9 @@
#include "QodeAssistClient.hpp"
#include "chat/ChatOutputPane.h"
#include "chat/NavigationPanel.hpp"
#include "llmcore/PromptTemplateManager.hpp"
#include "llmcore/ProvidersManager.hpp"
#include "providers/LMStudioProvider.hpp"
#include "providers/OllamaProvider.hpp"
#include "providers/OpenAICompatProvider.hpp"
#include "templates/CodeLlamaChat.hpp"
#include "templates/CodeLlamaFim.hpp"
#include "templates/CustomFimTemplate.hpp"
#include "templates/DeepSeekCoderChat.hpp"
#include "templates/DeepSeekCoderFim.hpp"
#include "templates/Qwen.hpp"
#include "templates/StarCoder2Fim.hpp"
#include "templates/StarCoderChat.hpp"
#include "providers/Providers.hpp"
#include "templates/Templates.hpp"
using namespace Utils;
using namespace Core;
@ -83,22 +72,8 @@ public:
void initialize() final
{
auto &providerManager = LLMCore::ProvidersManager::instance();
providerManager.registerProvider<Providers::OllamaProvider>();
providerManager.registerProvider<Providers::LMStudioProvider>();
providerManager.registerProvider<Providers::OpenAICompatProvider>();
auto &templateManager = LLMCore::PromptTemplateManager::instance();
templateManager.registerTemplate<Templates::CodeLlamaFim>();
templateManager.registerTemplate<Templates::StarCoder2Fim>();
templateManager.registerTemplate<Templates::DeepSeekCoderFim>();
templateManager.registerTemplate<Templates::CustomTemplate>();
templateManager.registerTemplate<Templates::DeepSeekCoderChat>();
templateManager.registerTemplate<Templates::CodeLlamaChat>();
templateManager.registerTemplate<Templates::LlamaChat>();
templateManager.registerTemplate<Templates::StarCoderChat>();
templateManager.registerTemplate<Templates::QwenChat>();
templateManager.registerTemplate<Templates::QwenFim>();
Providers::registerProviders();
Templates::registerTemplates();
Utils::Icon QCODEASSIST_ICON(
{{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}});

View File

@ -7,6 +7,7 @@ add_library(QodeAssistSettings STATIC
SettingsTr.hpp
CodeCompletionSettings.hpp CodeCompletionSettings.cpp
ChatAssistantSettings.hpp ChatAssistantSettings.cpp
SettingsDialog.hpp SettingsDialog.cpp
)
target_link_libraries(QodeAssistSettings

View File

@ -143,12 +143,11 @@ CodeCompletionSettings::CodeCompletionSettings()
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.");
systemPrompt.setDefaultValue("You are an expert C++, Qt, and QML code completion AI. ANSWER "
"should be SHORT and in CODE");
useFilePathInContext.setSettingsKey(Constants::CC_USE_FILE_PATH_IN_CONTEXT);
useFilePathInContext.setDefaultValue(false);
useFilePathInContext.setDefaultValue(true);
useFilePathInContext.setLabelText(Tr::tr("Use File Path in Context"));
useProjectChangesCache.setSettingsKey(Constants::CC_USE_PROJECT_CHANGES_CACHE);

View File

@ -1,181 +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"
#include "SettingsTr.hpp"
#include "SettingsUtils.hpp"
namespace QodeAssist::Settings {
ContextSettings &contextSettings()
{
static ContextSettings settings;
return settings;
}
ContextSettings::ContextSettings()
{
setAutoApply(false);
setDisplayName(Tr::tr("Context"));
readFullFile.setSettingsKey(Constants::CC_READ_FULL_FILE);
readFullFile.setLabelText(Tr::tr("Read Full File"));
readFullFile.setDefaultValue(false);
readStringsBeforeCursor.setSettingsKey(Constants::CC_READ_STRINGS_BEFORE_CURSOR);
readStringsBeforeCursor.setLabelText(Tr::tr("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);
useFilePathInContext.setSettingsKey(Constants::CC_USE_FILE_PATH_IN_CONTEXT);
useFilePathInContext.setDefaultValue(false);
useFilePathInContext.setLabelText(Tr::tr("Use File Path in Context"));
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. 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::CA_SYSTEM_PROMPT);
useChatSystemPrompt.setDefaultValue(true);
useChatSystemPrompt.setLabelText(Tr::tr("Use System Prompt for chat"));
chatSystemPrompt.setSettingsKey(Constants::CA_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::CC_USE_PROJECT_CHANGES_CACHE);
useProjectChangesCache.setDefaultValue(true);
useProjectChangesCache.setLabelText(Tr::tr("Use Project Changes Cache"));
maxChangesCacheSize.setSettingsKey(Constants::CC_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

@ -19,15 +19,22 @@
#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 "SettingsConstants.hpp"
#include "SettingsDialog.hpp"
#include "SettingsTr.hpp"
#include "SettingsUtils.hpp"
@ -59,11 +66,11 @@ GeneralSettings::GeneralSettings()
ccProvider.setReadOnly(true);
ccSelectProvider.m_buttonText = TrConstants::SELECT;
initStringAspect(ccModel, Constants::CC_MODEL, TrConstants::MODEL, "codellama:7b-code");
initStringAspect(ccModel, Constants::CC_MODEL, TrConstants::MODEL, "qwen2.5-coder:7b");
ccModel.setHistoryCompleter(Constants::CC_MODEL_HISTORY);
ccSelectModel.m_buttonText = TrConstants::SELECT;
initStringAspect(ccTemplate, Constants::CC_TEMPLATE, TrConstants::TEMPLATE, "CodeLlama FIM");
initStringAspect(ccTemplate, Constants::CC_TEMPLATE, TrConstants::TEMPLATE, "Ollama Auto FIM");
ccTemplate.setReadOnly(true);
ccSelectTemplate.m_buttonText = TrConstants::SELECT;
@ -80,11 +87,11 @@ GeneralSettings::GeneralSettings()
caProvider.setReadOnly(true);
caSelectProvider.m_buttonText = TrConstants::SELECT;
initStringAspect(caModel, Constants::CA_MODEL, TrConstants::MODEL, "codellama:7b-instruct");
initStringAspect(caModel, Constants::CA_MODEL, TrConstants::MODEL, "qwen2.5-coder:7b");
caModel.setHistoryCompleter(Constants::CA_MODEL_HISTORY);
caSelectModel.m_buttonText = TrConstants::SELECT;
initStringAspect(caTemplate, Constants::CA_TEMPLATE, TrConstants::TEMPLATE, "CodeLlama Chat");
initStringAspect(caTemplate, Constants::CA_TEMPLATE, TrConstants::TEMPLATE, "Ollama Auto Chat");
caTemplate.setReadOnly(true);
caSelectTemplate.m_buttonText = TrConstants::SELECT;
@ -109,16 +116,16 @@ GeneralSettings::GeneralSettings()
auto ccGrid = Grid{};
ccGrid.addRow({ccProvider, ccSelectProvider});
ccGrid.addRow({ccUrl, ccSetUrl});
ccGrid.addRow({ccModel, ccSelectModel});
ccGrid.addRow({ccTemplate, ccSelectTemplate});
ccGrid.addRow({ccUrl, ccSetUrl});
ccGrid.addRow({ccStatus, ccTest});
auto caGrid = Grid{};
caGrid.addRow({caProvider, caSelectProvider});
caGrid.addRow({caUrl, caSetUrl});
caGrid.addRow({caModel, caSelectModel});
caGrid.addRow({caTemplate, caSelectTemplate});
caGrid.addRow({caUrl, caSetUrl});
caGrid.addRow({caStatus, caTest});
auto ccGroup = Group{title(TrConstants::CODE_COMPLETION), ccGrid};
@ -161,6 +168,129 @@ void GeneralSettings::showSelectionDialog(const QStringList &data,
}
}
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]() {
@ -200,6 +330,8 @@ public:
setId(Constants::QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID);
setDisplayName(TrConstants::GENERAL);
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
setDisplayCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY);
setCategoryIconPath(":/resources/images/qoderassist-icon.png");
setSettingsProvider([] { return &generalSettings(); });
}
};

View File

@ -74,6 +74,12 @@ public:
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 resetPageToDefaults();

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 "SettingsConstants.hpp"
#include "SettingsTr.hpp"
#include "SettingsUtils.hpp"
namespace QodeAssist::Settings {
PresetPromptsSettings &presetPromptsSettings()
{
static PresetPromptsSettings settings;
return settings;
}
PresetPromptsSettings::PresetPromptsSettings()
{
setAutoApply(false);
setDisplayName(Tr::tr("Preset Prompts Params"));
fimTemperature.setSettingsKey(Constants::CC_TEMPERATURE);
fimTemperature.setLabelText(Tr::tr("Temperature:"));
fimTemperature.setDefaultValue(0.2);
fimTemperature.setRange(0.0, 10.0);
fimTemperature.setSingleStep(0.1);
chatTemperature.setSettingsKey(Constants::CA_TEMPERATURE);
chatTemperature.setLabelText(Tr::tr("Temperature:"));
chatTemperature.setDefaultValue(0.5);
chatTemperature.setRange(0.0, 10.0);
chatTemperature.setSingleStep(0.1);
fimOllamaLivetime.setSettingsKey(Constants::CC_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::CA_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::CC_MAX_TOKENS);
fimMaxTokens.setLabelText(Tr::tr("Max Tokens"));
fimMaxTokens.setRange(-1, 10000);
fimMaxTokens.setDefaultValue(50);
chatMaxTokens.setSettingsKey(Constants::CA_MAX_TOKENS);
chatMaxTokens.setLabelText(Tr::tr("Max Tokens"));
chatMaxTokens.setRange(-1, 10000);
chatMaxTokens.setDefaultValue(2000);
fimUseTopP.setSettingsKey(Constants::CC_USE_TOP_P);
fimUseTopP.setDefaultValue(false);
fimTopP.setSettingsKey(Constants::CC_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::CA_USE_TOP_P);
chatUseTopP.setDefaultValue(false);
chatTopP.setSettingsKey(Constants::CA_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::CC_USE_TOP_K);
fimUseTopK.setDefaultValue(false);
fimTopK.setSettingsKey(Constants::CC_TOP_K);
fimTopK.setLabelText(Tr::tr("use top_k"));
fimTopK.setDefaultValue(50);
fimTopK.setRange(1, 1000);
chatUseTopK.setSettingsKey(Constants::CA_USE_TOP_K);
chatUseTopK.setDefaultValue(false);
chatTopK.setSettingsKey(Constants::CA_TOP_K);
chatTopK.setLabelText(Tr::tr("use top_k"));
chatTopK.setDefaultValue(50);
chatTopK.setRange(1, 1000);
fimUsePresencePenalty.setSettingsKey(Constants::CC_USE_PRESENCE_PENALTY);
fimUsePresencePenalty.setDefaultValue(false);
fimPresencePenalty.setSettingsKey(Constants::CC_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::CA_USE_PRESENCE_PENALTY);
chatUsePresencePenalty.setDefaultValue(false);
chatPresencePenalty.setSettingsKey(Constants::CA_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::CC_USE_FREQUENCY_PENALTY);
fimUseFrequencyPenalty.setDefaultValue(false);
fimFrequencyPenalty.setSettingsKey(Constants::CC_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::CA_USE_FREQUENCY_PENALTY);
chatUseFrequencyPenalty.setDefaultValue(false);
chatFrequencyPenalty.setSettingsKey(Constants::CA_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::CC_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::CA_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
{
PromptSettings settings;
if (type == 0) {
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 (type = 1) {
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,95 +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 <utils/aspects.h>
#include "ButtonAspect.hpp"
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

@ -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 "ButtonAspect.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

View File

@ -42,6 +42,44 @@ inline const char *RESET_SETTINGS = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Reset
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

67
templates/Alpaca.hpp Normal file
View File

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

View File

@ -19,37 +19,22 @@
#pragma once
#include <QtCore/qjsonarray.h>
#include <QJsonArray>
#include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class CodeLlamaChat : public LLMCore::PromptTemplate
class BasicChat : public LLMCore::PromptTemplate
{
public:
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
QString name() const override { return "CodeLlama Chat"; }
QString promptTemplate() const override { return "[INST] %1 [/INST]"; }
QStringList stopWords() const override { return QStringList() << "[INST]" << "[/INST]"; }
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
{
QString formattedPrompt = promptTemplate().arg(context.prefix);
QJsonArray messages = request["messages"].toArray();
QJsonObject newMessage;
newMessage["role"] = "user";
newMessage["content"] = formattedPrompt;
messages.append(newMessage);
request["messages"] = messages;
}
};
class LlamaChat : public CodeLlamaChat
{
public:
QString name() const override { return "Llama Chat"; }
{}
QString description() const override { return "chat without tokens"; }
};
} // namespace QodeAssist::Templates

View File

@ -20,35 +20,41 @@
#pragma once
#include <QJsonArray>
#include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class DeepSeekCoderChat : public LLMCore::PromptTemplate
class ChatML : public LLMCore::PromptTemplate
{
public:
QString name() const override { return "DeepSeekCoder Chat"; }
QString name() const override { return "ChatML"; }
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
QString promptTemplate() const override { return "### Instruction:\n%1\n### Response:\n"; }
QString promptTemplate() const override { return {}; }
QStringList stopWords() const override
{
return QStringList() << "### Instruction:" << "### Response:" << "\n\n### " << "<|EOT|>";
return QStringList() << "<|im_start|>" << "<|im_end|>";
}
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);
for (int i = 0; i < messages.size(); ++i) {
QJsonObject message = messages[i].toObject();
QString role = message["role"].toString();
QString content = message["content"].toString();
message["content"] = QString("<|im_start|>%1\n%2\n<|im_end|>").arg(role, content);
messages[i] = message;
}
request["messages"] = messages;
}
QString description() const override
{
return "The message will contain the following tokens: <|im_start|>%1\n%2\n<|im_end|>";
}
};
} // namespace QodeAssist::Templates

View File

@ -28,19 +28,20 @@ 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>";
}
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;
}
QString description() const override
{
return "The message will contain the following tokens: <PRE> %1 <SUF>%2 <MID>";
}
};
} // namespace QodeAssist::Templates

View File

@ -39,7 +39,6 @@ public:
return Settings::customPromptSettings().customJsonTemplate();
}
QStringList stopWords() const override { return QStringList(); }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonDocument doc = QJsonDocument::fromJson(promptTemplate().toUtf8());
@ -56,13 +55,13 @@ public:
request[it.key()] = it.value();
}
}
QString description() const override { return promptTemplate(); }
private:
QJsonValue processJsonValue(const QJsonValue &value, const LLMCore::ContextData &context) const
{
if (value.isString()) {
QString str = value.toString();
str.replace("{{QODE_INSTRUCTIONS}}", context.systemPrompt);
str.replace("{{QODE_PREFIX}}", context.prefix);
str.replace("{{QODE_SUFFIX}}", context.suffix);
return str;

View File

@ -30,16 +30,19 @@ 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;
}
QString description() const override
{
return "The message will contain the following tokens: "
"<fim▁begin>%1<fim▁hole>%2<fim▁end>";
}
};
} // namespace QodeAssist::Templates

64
templates/Llama2.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 "llmcore/PromptTemplate.hpp"
#include <QJsonArray>
namespace QodeAssist::Templates {
class Llama2 : public LLMCore::PromptTemplate
{
public:
QString name() const override { return "Llama 2"; }
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
QString promptTemplate() const override { return {}; }
QStringList stopWords() const override { return QStringList() << "[INST]"; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages = request["messages"].toArray();
for (int i = 0; i < messages.size(); ++i) {
QJsonObject message = messages[i].toObject();
QString role = message["role"].toString();
QString content = message["content"].toString();
QString formattedContent;
if (role == "system") {
formattedContent = QString("[INST]<<SYS>>\n%1\n<</SYS>>[/INST]\n").arg(content);
} else if (role == "user") {
formattedContent = QString("[INST]%1[/INST]\n").arg(content);
} else if (role == "assistant") {
formattedContent = content + "\n";
}
message["content"] = formattedContent;
messages[i] = message;
}
request["messages"] = messages;
}
QString description() const override
{
return "The message will contain the following tokens: [INST]%1[/INST]\n";
}
};
} // namespace QodeAssist::Templates

View File

@ -20,32 +20,43 @@
#pragma once
#include <QJsonArray>
#include "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class StarCoderChat : public LLMCore::PromptTemplate
class Llama3 : public LLMCore::PromptTemplate
{
public:
QString name() const override { return "StarCoder Chat"; }
QString name() const override { return "Llama 3"; }
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Chat; }
QString promptTemplate() const override { return "### Instruction:\n%1\n### Response:\n"; }
QString promptTemplate() const override { return ""; }
QStringList stopWords() const override
{
return QStringList() << "###"
<< "<|endoftext|>" << "<file_sep>";
return QStringList() << "<|start_header_id|>" << "<|end_header_id|>" << "<|eot_id|>";
}
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);
for (int i = 0; i < messages.size(); ++i) {
QJsonObject message = messages[i].toObject();
QString role = message["role"].toString();
QString content = message["content"].toString();
message["content"]
= QString("<|start_header_id|>%1<|end_header_id|>%2<|eot_id|>").arg(role, content);
messages[i] = message;
}
request["messages"] = messages;
}
QString description() const override
{
return "The message will contain the following tokens: "
"<|start_header_id|>%1<|end_header_id|>%2<|eot_id|>";
}
};
} // namespace QodeAssist::Templates

65
templates/Ollama.hpp Normal file
View File

@ -0,0 +1,65 @@
/*
* 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;
}
QString description() const override { return "template will take from ollama modelfile"; }
};
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;
}
QString description() const override { return "template will take from ollama modelfile"; }
};
} // namespace QodeAssist::Templates

View File

@ -24,33 +24,6 @@
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:
@ -66,6 +39,11 @@ public:
QString formattedPrompt = promptTemplate().arg(context.prefix, context.suffix);
request["prompt"] = formattedPrompt;
}
QString description() const override
{
return "The message will contain the following tokens: "
"<|fim_prefix|>%1<|fim_suffix|>%2<|fim_middle|>";
}
};
} // 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,11 +36,14 @@ 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;
}
QString description() const override
{
return "The message will contain the following tokens: "
"<fim_prefix>%1<fim_suffix>%2<fim_middle>";
}
};
} // namespace QodeAssist::Templates

54
templates/Templates.hpp Normal file
View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "llmcore/PromptTemplateManager.hpp"
#include "templates/Alpaca.hpp"
#include "templates/BasicChat.hpp"
#include "templates/ChatML.hpp"
#include "templates/CodeLlamaFim.hpp"
#include "templates/CustomFimTemplate.hpp"
#include "templates/DeepSeekCoderFim.hpp"
#include "templates/Llama2.hpp"
#include "templates/Llama3.hpp"
#include "templates/Ollama.hpp"
#include "templates/Qwen.hpp"
#include "templates/StarCoder2Fim.hpp"
namespace QodeAssist::Templates {
inline void registerTemplates()
{
auto &templateManager = LLMCore::PromptTemplateManager::instance();
templateManager.registerTemplate<CodeLlamaFim>();
templateManager.registerTemplate<StarCoder2Fim>();
templateManager.registerTemplate<DeepSeekCoderFim>();
templateManager.registerTemplate<CustomTemplate>();
templateManager.registerTemplate<QwenFim>();
templateManager.registerTemplate<OllamaAutoFim>();
templateManager.registerTemplate<OllamaAutoChat>();
templateManager.registerTemplate<BasicChat>();
templateManager.registerTemplate<Llama3>();
templateManager.registerTemplate<ChatML>();
templateManager.registerTemplate<Alpaca>();
templateManager.registerTemplate<Llama2>();
}
} // namespace QodeAssist::Templates