mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-02-10 09:10:12 -05:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31133e3378 | |||
| a2f15fc843 | |||
| 2a0beb6c4c | |||
| e836b86569 | |||
| 288fefebe5 | |||
| 528badbf1e | |||
| b789e42602 | |||
| 4bf955462f | |||
| 5b99e68e53 | |||
| 0f1b277ef7 | |||
| 56995c9edf | |||
| 45aba6b6be | |||
| 1dfb3feb96 | |||
| 2c49d45297 | |||
| 31145f191b | |||
| 9096adde6f | |||
| b8e578d2d7 |
10
.github/workflows/build_cmake.yml
vendored
10
.github/workflows/build_cmake.yml
vendored
@ -13,8 +13,8 @@ on:
|
||||
env:
|
||||
PLUGIN_NAME: QodeAssist
|
||||
QT_VERSION: 6.8.1
|
||||
QT_CREATOR_VERSION: 15.0.0
|
||||
QT_CREATOR_VERSION_INTERNAL: 15.0.0
|
||||
QT_CREATOR_VERSION: 15.0.1
|
||||
QT_CREATOR_VERSION_INTERNAL: 15.0.1
|
||||
MACOS_DEPLOYMENT_TARGET: "11.0"
|
||||
CMAKE_VERSION: "3.29.6"
|
||||
NINJA_VERSION: "1.12.1"
|
||||
@ -41,12 +41,6 @@ jobs:
|
||||
platform: linux_x64,
|
||||
cc: "gcc", cxx: "g++"
|
||||
}
|
||||
- {
|
||||
name: "Ubuntu 22.04 GCC", artifact: "Linux-x64(Ubuntu-22.04-experimental)",
|
||||
os: ubuntu-22.04,
|
||||
platform: linux_x64,
|
||||
cc: "gcc", cxx: "g++"
|
||||
}
|
||||
- {
|
||||
name: "macOS Latest Clang", artifact: "macOS-universal",
|
||||
os: macos-latest,
|
||||
|
||||
@ -55,6 +55,7 @@ add_qtc_plugin(QodeAssist
|
||||
templates/Llama2.hpp
|
||||
templates/Claude.hpp
|
||||
templates/OpenAI.hpp
|
||||
templates/CodeLlamaQMLFim.hpp
|
||||
providers/Providers.hpp
|
||||
providers/OllamaProvider.hpp providers/OllamaProvider.cpp
|
||||
providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include "ChatRootView.hpp"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
@ -72,7 +73,7 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
||||
this,
|
||||
&ChatRootView::updateInputTokensCount);
|
||||
|
||||
connect(m_chatModel, &ChatModel::modelReseted, [this]() { setRecentFilePath(QString{}); });
|
||||
connect(m_chatModel, &ChatModel::modelReseted, this, [this]() { setRecentFilePath(QString{}); });
|
||||
connect(this, &ChatRootView::attachmentFilesChanged, &ChatRootView::updateInputTokensCount);
|
||||
connect(this, &ChatRootView::linkedFilesChanged, &ChatRootView::updateInputTokensCount);
|
||||
connect(&Settings::chatAssistantSettings().useSystemPrompt, &Utils::BaseAspect::changed,
|
||||
@ -82,9 +83,20 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
||||
|
||||
auto editors = Core::EditorManager::instance();
|
||||
|
||||
connect(editors, &Core::EditorManager::editorOpened, this, &ChatRootView::onEditorOpened);
|
||||
connect(editors, &Core::EditorManager::editorAboutToClose, this, &ChatRootView::onEditorAboutToClose);
|
||||
connect(editors, &Core::EditorManager::editorsClosed, this, &ChatRootView::onEditorsClosed);
|
||||
connect(editors, &Core::EditorManager::editorCreated, this, &ChatRootView::onEditorCreated);
|
||||
connect(
|
||||
editors,
|
||||
&Core::EditorManager::editorAboutToClose,
|
||||
this,
|
||||
&ChatRootView::onEditorAboutToClose);
|
||||
|
||||
connect(editors, &Core::EditorManager::currentEditorAboutToChange, this, [this]() {
|
||||
if (m_isSyncOpenFiles) {
|
||||
for (auto editor : std::as_const(m_currentEditors)) {
|
||||
onAppendLinkFileFromEditor(editor);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateInputTokensCount();
|
||||
}
|
||||
@ -173,6 +185,8 @@ void ChatRootView::saveHistory(const QString &filePath)
|
||||
auto result = ChatSerializer::saveToFile(m_chatModel, filePath);
|
||||
if (!result.success) {
|
||||
LOG_MESSAGE(QString("Failed to save chat history: %1").arg(result.errorMessage));
|
||||
} else {
|
||||
setRecentFilePath(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,17 +257,50 @@ QString ChatRootView::getSuggestedFileName() const
|
||||
{
|
||||
QStringList parts;
|
||||
|
||||
static const QRegularExpression saitizeSymbols = QRegularExpression("[\\/:*?\"<>|\\s]");
|
||||
static const QRegularExpression underSymbols = QRegularExpression("_+");
|
||||
|
||||
if (m_chatModel->rowCount() > 0) {
|
||||
QString firstMessage
|
||||
= m_chatModel->data(m_chatModel->index(0), ChatModel::Content).toString();
|
||||
QString shortMessage = firstMessage.split('\n').first().simplified().left(30);
|
||||
shortMessage.replace(QRegularExpression("[^a-zA-Z0-9_-]"), "_");
|
||||
parts << shortMessage;
|
||||
|
||||
QString sanitizedMessage = shortMessage;
|
||||
sanitizedMessage.replace(saitizeSymbols, "_");
|
||||
sanitizedMessage.replace(underSymbols, "_");
|
||||
sanitizedMessage = sanitizedMessage.trimmed();
|
||||
|
||||
if (!sanitizedMessage.isEmpty()) {
|
||||
if (sanitizedMessage.startsWith('_')) {
|
||||
sanitizedMessage.remove(0, 1);
|
||||
}
|
||||
if (sanitizedMessage.endsWith('_')) {
|
||||
sanitizedMessage.chop(1);
|
||||
}
|
||||
|
||||
QString targetDir = getChatsHistoryDir();
|
||||
QString fullPath = QDir(targetDir).filePath(sanitizedMessage);
|
||||
|
||||
QFileInfo fileInfo(fullPath);
|
||||
if (!fileInfo.exists() && QFileInfo(fileInfo.path()).isWritable()) {
|
||||
parts << sanitizedMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parts << QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm");
|
||||
|
||||
return parts.join("_");
|
||||
QString fileName = parts.join("_");
|
||||
|
||||
QString fullPath = QDir(getChatsHistoryDir()).filePath(fileName);
|
||||
QFileInfo finalCheck(fullPath);
|
||||
|
||||
if (fileName.isEmpty() || finalCheck.exists() || !QFileInfo(finalCheck.path()).isWritable()) {
|
||||
fileName = QString("chat_%1").arg(
|
||||
QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm"));
|
||||
}
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
void ChatRootView::autosave()
|
||||
@ -306,7 +353,7 @@ void ChatRootView::showAttachFilesDialog()
|
||||
QStringList newFilePaths = dialog.selectedFiles();
|
||||
if (!newFilePaths.isEmpty()) {
|
||||
bool filesAdded = false;
|
||||
for (const QString &filePath : newFilePaths) {
|
||||
for (const QString &filePath : std::as_const(newFilePaths)) {
|
||||
if (!m_attachmentFiles.contains(filePath)) {
|
||||
m_attachmentFiles.append(filePath);
|
||||
filesAdded = true;
|
||||
@ -340,7 +387,7 @@ void ChatRootView::showLinkFilesDialog()
|
||||
QStringList newFilePaths = dialog.selectedFiles();
|
||||
if (!newFilePaths.isEmpty()) {
|
||||
bool filesAdded = false;
|
||||
for (const QString &filePath : newFilePaths) {
|
||||
for (const QString &filePath : std::as_const(newFilePaths)) {
|
||||
if (!m_linkedFiles.contains(filePath)) {
|
||||
m_linkedFiles.append(filePath);
|
||||
filesAdded = true;
|
||||
@ -373,6 +420,31 @@ void ChatRootView::setIsSyncOpenFiles(bool state)
|
||||
m_isSyncOpenFiles = state;
|
||||
emit isSyncOpenFilesChanged();
|
||||
}
|
||||
|
||||
if (m_isSyncOpenFiles) {
|
||||
for (auto editor : std::as_const(m_currentEditors)) {
|
||||
onAppendLinkFileFromEditor(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChatRootView::openChatHistoryFolder()
|
||||
{
|
||||
QString path;
|
||||
if (auto project = ProjectExplorer::ProjectManager::startupProject()) {
|
||||
Settings::ProjectSettings projectSettings(project);
|
||||
path = projectSettings.chatHistoryPath().toString();
|
||||
} else {
|
||||
path = QString("%1/qodeassist/chat_history").arg(Core::ICore::userResourcePath().toString());
|
||||
}
|
||||
|
||||
QDir dir(path);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(".");
|
||||
}
|
||||
|
||||
QUrl url = QUrl::fromLocalFile(dir.absolutePath());
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
|
||||
void ChatRootView::updateInputTokensCount()
|
||||
@ -414,7 +486,20 @@ bool ChatRootView::isSyncOpenFiles() const
|
||||
return m_isSyncOpenFiles;
|
||||
}
|
||||
|
||||
void ChatRootView::onEditorOpened(Core::IEditor *editor)
|
||||
void ChatRootView::onEditorAboutToClose(Core::IEditor *editor)
|
||||
{
|
||||
if (auto document = editor->document(); document && isSyncOpenFiles()) {
|
||||
QString filePath = document->filePath().toString();
|
||||
m_linkedFiles.removeOne(filePath);
|
||||
emit linkedFilesChanged();
|
||||
}
|
||||
|
||||
if (editor) {
|
||||
m_currentEditors.removeOne(editor);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatRootView::onAppendLinkFileFromEditor(Core::IEditor *editor)
|
||||
{
|
||||
if (auto document = editor->document(); document && isSyncOpenFiles()) {
|
||||
QString filePath = document->filePath().toString();
|
||||
@ -425,25 +510,10 @@ void ChatRootView::onEditorOpened(Core::IEditor *editor)
|
||||
}
|
||||
}
|
||||
|
||||
void ChatRootView::onEditorAboutToClose(Core::IEditor *editor)
|
||||
void ChatRootView::onEditorCreated(Core::IEditor *editor, const Utils::FilePath &filePath)
|
||||
{
|
||||
if (auto document = editor->document(); document && isSyncOpenFiles()) {
|
||||
QString filePath = document->filePath().toString();
|
||||
m_linkedFiles.removeOne(filePath);
|
||||
emit linkedFilesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatRootView::onEditorsClosed(QList<Core::IEditor *> editors)
|
||||
{
|
||||
if (isSyncOpenFiles()) {
|
||||
for (Core::IEditor *editor : editors) {
|
||||
if (auto document = editor->document()) {
|
||||
QString filePath = document->filePath().toString();
|
||||
m_linkedFiles.removeOne(filePath);
|
||||
}
|
||||
}
|
||||
emit linkedFilesChanged();
|
||||
if (editor && editor->document()) {
|
||||
m_currentEditors.append(editor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -64,15 +64,16 @@ public:
|
||||
Q_INVOKABLE void removeFileFromLinkList(int index);
|
||||
Q_INVOKABLE void calculateMessageTokensCount(const QString &message);
|
||||
Q_INVOKABLE void setIsSyncOpenFiles(bool state);
|
||||
Q_INVOKABLE void openChatHistoryFolder();
|
||||
|
||||
Q_INVOKABLE void updateInputTokensCount();
|
||||
int inputTokensCount() const;
|
||||
|
||||
bool isSyncOpenFiles() const;
|
||||
|
||||
void onEditorOpened(Core::IEditor *editor);
|
||||
void onEditorAboutToClose(Core::IEditor *editor);
|
||||
void onEditorsClosed(QList<Core::IEditor *> editors);
|
||||
void onAppendLinkFileFromEditor(Core::IEditor *editor);
|
||||
void onEditorCreated(Core::IEditor *editor, const Utils::FilePath &filePath);
|
||||
|
||||
QString chatFileName() const;
|
||||
void setRecentFilePath(const QString &filePath);
|
||||
@ -106,6 +107,7 @@ private:
|
||||
int m_messageTokensCount{0};
|
||||
int m_inputTokensCount{0};
|
||||
bool m_isSyncOpenFiles;
|
||||
QList<Core::IEditor *> m_currentEditors;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Chat
|
||||
|
||||
@ -75,6 +75,7 @@ ChatRootView {
|
||||
recentPath {
|
||||
text: qsTr("Latest chat file name: %1").arg(root.chatFileName.length > 0 ? root.chatFileName : "Unsaved")
|
||||
}
|
||||
openChatHistory.onClicked: root.openChatHistoryFolder()
|
||||
}
|
||||
|
||||
ListView {
|
||||
|
||||
@ -29,6 +29,7 @@ Rectangle {
|
||||
property alias clearButton: clearButtonId
|
||||
property alias tokensBadge: tokensBadgeId
|
||||
property alias recentPath: recentPathId
|
||||
property alias openChatHistory: openChatHistoryId
|
||||
|
||||
color: palette.window.hslLightness > 0.5 ?
|
||||
Qt.darker(palette.window, 1.1) :
|
||||
@ -66,11 +67,16 @@ Rectangle {
|
||||
Text {
|
||||
id: recentPathId
|
||||
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideMiddle
|
||||
color: palette.text
|
||||
}
|
||||
|
||||
QoAButton {
|
||||
id: openChatHistoryId
|
||||
|
||||
text: qsTr("Show in system")
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
@ -57,6 +57,13 @@ void ConfigurationManager::setupConnections()
|
||||
connect(&m_generalSettings.caSelectTemplate, &Button::clicked, this, &Config::selectTemplate);
|
||||
connect(&m_generalSettings.ccSetUrl, &Button::clicked, this, &Config::selectUrl);
|
||||
connect(&m_generalSettings.caSetUrl, &Button::clicked, this, &Config::selectUrl);
|
||||
|
||||
connect(
|
||||
&m_generalSettings.ccPreset1SelectProvider, &Button::clicked, this, &Config::selectProvider);
|
||||
connect(&m_generalSettings.ccPreset1SetUrl, &Button::clicked, this, &Config::selectUrl);
|
||||
connect(&m_generalSettings.ccPreset1SelectModel, &Button::clicked, this, &Config::selectModel);
|
||||
connect(
|
||||
&m_generalSettings.ccPreset1SelectTemplate, &Button::clicked, this, &Config::selectTemplate);
|
||||
}
|
||||
|
||||
void ConfigurationManager::selectProvider()
|
||||
@ -69,6 +76,8 @@ void ConfigurationManager::selectProvider()
|
||||
|
||||
auto &targetSettings = (settingsButton == &m_generalSettings.ccSelectProvider)
|
||||
? m_generalSettings.ccProvider
|
||||
: settingsButton == &m_generalSettings.ccPreset1SelectProvider
|
||||
? m_generalSettings.ccPreset1Provider
|
||||
: m_generalSettings.caProvider;
|
||||
|
||||
QTimer::singleShot(0, this, [this, providersList, &targetSettings] {
|
||||
@ -86,14 +95,19 @@ void ConfigurationManager::selectModel()
|
||||
return;
|
||||
|
||||
const bool isCodeCompletion = (settingsButton == &m_generalSettings.ccSelectModel);
|
||||
const bool isPreset1 = (settingsButton == &m_generalSettings.ccPreset1SelectModel);
|
||||
|
||||
const QString providerName = isCodeCompletion ? m_generalSettings.ccProvider.volatileValue()
|
||||
: m_generalSettings.caProvider.volatileValue();
|
||||
: isPreset1 ? m_generalSettings.ccPreset1Provider.volatileValue()
|
||||
: m_generalSettings.caProvider.volatileValue();
|
||||
|
||||
const auto providerUrl = isCodeCompletion ? m_generalSettings.ccUrl.volatileValue()
|
||||
: isPreset1 ? m_generalSettings.ccPreset1Url.volatileValue()
|
||||
: m_generalSettings.caUrl.volatileValue();
|
||||
|
||||
auto &targetSettings = isCodeCompletion ? m_generalSettings.ccModel : m_generalSettings.caModel;
|
||||
auto &targetSettings = isCodeCompletion ? m_generalSettings.ccModel
|
||||
: isPreset1 ? m_generalSettings.ccPreset1Model
|
||||
: m_generalSettings.caModel;
|
||||
|
||||
if (auto provider = m_providersManager.getProviderByName(providerName)) {
|
||||
if (!provider->supportsModelListing()) {
|
||||
@ -122,11 +136,13 @@ void ConfigurationManager::selectTemplate()
|
||||
return;
|
||||
|
||||
const bool isCodeCompletion = (settingsButton == &m_generalSettings.ccSelectTemplate);
|
||||
const bool isPreset1 = (settingsButton == &m_generalSettings.ccPreset1SelectTemplate);
|
||||
|
||||
const auto templateList = isCodeCompletion ? m_templateManger.fimTemplatesNames()
|
||||
: m_templateManger.chatTemplatesNames();
|
||||
const auto templateList = isCodeCompletion || isPreset1 ? m_templateManger.fimTemplatesNames()
|
||||
: m_templateManger.chatTemplatesNames();
|
||||
|
||||
auto &targetSettings = isCodeCompletion ? m_generalSettings.ccTemplate
|
||||
: isPreset1 ? m_generalSettings.ccPreset1Template
|
||||
: m_generalSettings.caTemplate;
|
||||
|
||||
QTimer::singleShot(0, &m_generalSettings, [this, templateList, &targetSettings]() {
|
||||
@ -150,8 +166,9 @@ void ConfigurationManager::selectUrl()
|
||||
urls.append(url);
|
||||
}
|
||||
|
||||
auto &targetSettings = (settingsButton == &m_generalSettings.ccSetUrl)
|
||||
? m_generalSettings.ccUrl
|
||||
auto &targetSettings = (settingsButton == &m_generalSettings.ccSetUrl) ? m_generalSettings.ccUrl
|
||||
: settingsButton == &m_generalSettings.ccPreset1SetUrl
|
||||
? m_generalSettings.ccPreset1Url
|
||||
: m_generalSettings.caUrl;
|
||||
|
||||
QTimer::singleShot(0, &m_generalSettings, [this, urls, &targetSettings]() {
|
||||
|
||||
@ -146,20 +146,41 @@ void LLMClientInterface::handleExit(const QJsonObject &request)
|
||||
emit finished();
|
||||
}
|
||||
|
||||
bool QodeAssist::LLMClientInterface::isSpecifyCompletion(const QJsonObject &request)
|
||||
{
|
||||
auto &generalSettings = Settings::generalSettings();
|
||||
|
||||
Context::ProgrammingLanguage documentLanguage = getDocumentLanguage(request);
|
||||
Context::ProgrammingLanguage preset1Language = Context::ProgrammingLanguageUtils::fromString(
|
||||
generalSettings.preset1Language.displayForIndex(generalSettings.preset1Language()));
|
||||
|
||||
return generalSettings.specifyPreset1() && documentLanguage == preset1Language;
|
||||
}
|
||||
|
||||
void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
||||
{
|
||||
auto updatedContext = prepareContext(request);
|
||||
const auto updatedContext = prepareContext(request);
|
||||
auto &completeSettings = Settings::codeCompletionSettings();
|
||||
auto &generalSettings = Settings::generalSettings();
|
||||
|
||||
auto providerName = Settings::generalSettings().ccProvider();
|
||||
auto provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName);
|
||||
bool isPreset1Active = isSpecifyCompletion(request);
|
||||
|
||||
const auto providerName = !isPreset1Active ? generalSettings.ccProvider()
|
||||
: generalSettings.ccPreset1Provider();
|
||||
const auto modelName = !isPreset1Active ? generalSettings.ccModel()
|
||||
: generalSettings.ccPreset1Model();
|
||||
const auto url = !isPreset1Active ? generalSettings.ccUrl() : generalSettings.ccPreset1Url();
|
||||
|
||||
const 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 templateName = !isPreset1Active ? generalSettings.ccTemplate()
|
||||
: generalSettings.ccPreset1Template();
|
||||
|
||||
auto promptTemplate = LLMCore::PromptTemplateManager::instance().getFimTemplateByName(
|
||||
templateName);
|
||||
|
||||
@ -168,19 +189,18 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO refactor to dynamic presets system
|
||||
LLMCore::LLMConfig config;
|
||||
config.requestType = LLMCore::RequestType::CodeCompletion;
|
||||
config.provider = provider;
|
||||
config.promptTemplate = promptTemplate;
|
||||
config.url = QUrl(QString("%1%2").arg(
|
||||
Settings::generalSettings().ccUrl(),
|
||||
url,
|
||||
promptTemplate->type() == LLMCore::TemplateType::Fim ? provider->completionEndpoint()
|
||||
: provider->chatEndpoint()));
|
||||
config.apiKey = provider->apiKey();
|
||||
|
||||
config.providerRequest
|
||||
= {{"model", Settings::generalSettings().ccModel()},
|
||||
{"stream", Settings::codeCompletionSettings().stream()}};
|
||||
config.providerRequest = {{"model", modelName}, {"stream", completeSettings.stream()}};
|
||||
|
||||
config.multiLineCompletion = completeSettings.multiLineCompletion();
|
||||
|
||||
@ -246,11 +266,33 @@ LLMCore::ContextData LLMClientInterface::prepareContext(const QJsonObject &reque
|
||||
return reader.prepareContext(lineNumber, cursorPosition);
|
||||
}
|
||||
|
||||
Context::ProgrammingLanguage LLMClientInterface::getDocumentLanguage(const QJsonObject &request) const
|
||||
{
|
||||
QJsonObject params = request["params"].toObject();
|
||||
QJsonObject doc = params["doc"].toObject();
|
||||
QString uri = doc["uri"].toString();
|
||||
|
||||
Utils::FilePath filePath = Utils::FilePath::fromString(QUrl(uri).toLocalFile());
|
||||
TextEditor::TextDocument *textDocument = TextEditor::TextDocument::textDocumentForFilePath(
|
||||
filePath);
|
||||
|
||||
if (!textDocument) {
|
||||
LOG_MESSAGE("Error: Document is not available for" + filePath.toString());
|
||||
return Context::ProgrammingLanguage::Unknown;
|
||||
}
|
||||
|
||||
return Context::ProgrammingLanguageUtils::fromMimeType(textDocument->mimeType());
|
||||
}
|
||||
|
||||
void LLMClientInterface::sendCompletionToClient(const QString &completion,
|
||||
const QJsonObject &request,
|
||||
bool isComplete)
|
||||
{
|
||||
auto templateName = Settings::generalSettings().ccTemplate();
|
||||
bool isPreset1Active = isSpecifyCompletion(request);
|
||||
|
||||
auto templateName = !isPreset1Active ? Settings::generalSettings().ccTemplate()
|
||||
: Settings::generalSettings().ccPreset1Template();
|
||||
|
||||
auto promptTemplate = LLMCore::PromptTemplateManager::instance().getFimTemplateByName(
|
||||
templateName);
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include <languageclient/languageclientinterface.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <context/ProgrammingLanguage.hpp>
|
||||
#include <llmcore/ContextData.hpp>
|
||||
#include <llmcore/RequestHandler.hpp>
|
||||
|
||||
@ -58,8 +59,10 @@ private:
|
||||
void handleExit(const QJsonObject &request);
|
||||
void handleCancelRequest(const QJsonObject &request);
|
||||
|
||||
LLMCore::ContextData prepareContext(const QJsonObject &request,
|
||||
const QStringView &accumulatedCompletion = QString{});
|
||||
LLMCore::ContextData prepareContext(
|
||||
const QJsonObject &request, const QStringView &accumulatedCompletion = QString{});
|
||||
Context::ProgrammingLanguage getDocumentLanguage(const QJsonObject &request) const;
|
||||
bool isSpecifyCompletion(const QJsonObject &request);
|
||||
|
||||
LLMCore::RequestHandler m_requestHandler;
|
||||
QElapsedTimer m_completionTimer;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"Id" : "qodeassist",
|
||||
"Name" : "QodeAssist",
|
||||
"Version" : "0.4.7",
|
||||
"Version" : "0.4.13",
|
||||
"Vendor" : "Petr Mironychev",
|
||||
"VendorId" : "petrmironychev",
|
||||
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
|
||||
|
||||
81
README.md
81
README.md
@ -2,7 +2,7 @@
|
||||
[](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml)
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
 QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides intelligent code completion and suggestions for C++ and QML, leveraging large language models through local providers like Ollama. Enhance your coding productivity with context-aware AI assistance directly in your Qt development environment.
|
||||
@ -13,6 +13,15 @@
|
||||
> - The QodeAssist developer bears no responsibility for any charges incurred
|
||||
> - Please carefully review the provider's pricing and your account settings before use
|
||||
|
||||
⚠️ **Commercial Support and Custom Development**
|
||||
> The QodeAssist developer offers commercial services for:
|
||||
> - Adapting the plugin for specific Qt Creator versions
|
||||
> - Custom development for particular operating systems
|
||||
> - Integration with specific language models
|
||||
> - Implementing custom features and modifications
|
||||
>
|
||||
> For commercial inquiries, please contact: qodeassist.dev@pm.me
|
||||
|
||||
## Table of Contents
|
||||
1. [Overview](#overview)
|
||||
2. [Install plugin to QtCreator](#install-plugin-to-qtcreator)
|
||||
@ -20,19 +29,25 @@
|
||||
4. [Configure for OpenAI](#configure-for-openai)
|
||||
5. [Configure for using Ollama](#configure-for-using-ollama)
|
||||
6. [System Prompt Configuration](#system-prompt-configuration)
|
||||
7. [Template-Model Compatibility](#template-model-compatibility)
|
||||
8. [QtCreator Version Compatibility](#qtcreator-version-compatibility)
|
||||
9. [Development Progress](#development-progress)
|
||||
10. [Hotkeys](#hotkeys)
|
||||
11. [Troubleshooting](#troubleshooting)
|
||||
12. [Support the Development](#support-the-development-of-qodeassist)
|
||||
13. [How to Build](#how-to-build)
|
||||
7. [File Context Features](#file-context-features)
|
||||
8. [Template-Model Compatibility](#template-model-compatibility)
|
||||
9. [QtCreator Version Compatibility](#qtcreator-version-compatibility)
|
||||
10. [Development Progress](#development-progress)
|
||||
11. [Hotkeys](#hotkeys)
|
||||
12. [Troubleshooting](#troubleshooting)
|
||||
13. [Support the Development](#support-the-development-of-qodeassist)
|
||||
14. [How to Build](#how-to-build)
|
||||
|
||||
## Overview
|
||||
|
||||
- AI-powered code completion
|
||||
- Chat functionality:
|
||||
- Side and Bottom panels
|
||||
- Chat history autosave and restore
|
||||
- Token usage monitoring and management
|
||||
- Attach files for one-time code analysis
|
||||
- Link files for persistent context with auto update in conversations
|
||||
- Automatic syncing with open editor files (optional)
|
||||
- Support for multiple LLM providers:
|
||||
- Ollama
|
||||
- OpenAI
|
||||
@ -63,9 +78,15 @@
|
||||
<img width="326" alt="QodeAssistBottomPanel" src="https://github.com/user-attachments/assets/4cc64c23-a294-4df8-9153-39ad6fdab34b">
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Automatic syncing with open editor files: (click to expand)</summary>
|
||||
<img width="600" alt="OpenedDocumentsSync" src="https://github.com/user-attachments/assets/08efda2f-dc4d-44c3-927c-e6a975090d2f">
|
||||
</details>
|
||||
|
||||
## Install plugin to QtCreator
|
||||
1. Install Latest Qt Creator
|
||||
2. Download the QodeAssist plugin for your Qt Creator
|
||||
- Remove old version plugin if already was installed
|
||||
3. Launch Qt Creator and install the plugin:
|
||||
- Go to:
|
||||
- MacOS: Qt Creator -> About Plugins...
|
||||
@ -136,25 +157,59 @@ You're all set! QodeAssist is now ready to use in Qt Creator.
|
||||
|
||||
The plugin comes with default system prompts optimized for chat and instruct models, as these currently provide better results for code assistance. If you prefer using FIM (Fill-in-Middle) models, you can easily customize the system prompt in the settings.
|
||||
|
||||
## File Context Features
|
||||
|
||||
QodeAssist provides two powerful ways to include source code files in your chat conversations: Attachments and Linked Files. Each serves a distinct purpose and helps provide better context for the AI assistant.
|
||||
|
||||
### Attached Files
|
||||
|
||||
Attachments are designed for one-time code analysis and specific queries:
|
||||
- Files are included only in the current message
|
||||
- Content is discarded after the message is processed
|
||||
- Ideal for:
|
||||
- Getting specific feedback on code changes
|
||||
- Code review requests
|
||||
- Analyzing isolated code segments
|
||||
- Quick implementation questions
|
||||
- Files can be attached using the paperclip icon in the chat interface
|
||||
- Multiple files can be attached to a single message
|
||||
|
||||
### Linked Files
|
||||
|
||||
Linked files provide persistent context throughout the conversation:
|
||||
|
||||
- Files remain accessible for the entire chat session
|
||||
- Content is included in every message exchange
|
||||
- Files are automatically refreshed - always using latest content from disk
|
||||
- Perfect for:
|
||||
- Long-term refactoring discussions
|
||||
- Complex architectural changes
|
||||
- Multi-file implementations
|
||||
- Maintaining context across related questions
|
||||
- Can be managed using the link icon in the chat interface
|
||||
- Supports automatic syncing with open editor files (can be enabled in settings)
|
||||
- Files can be added/removed at any time during the conversation
|
||||
|
||||
## Template-Model Compatibility
|
||||
|
||||
| Template | Compatible Models | Purpose |
|
||||
|----------|------------------|----------|
|
||||
| CodeLlama FIM | `codellama:code` | Code completion |
|
||||
| DeepSeekCoder FIM | `deepseek-coder-v2`, `deepseek-v2.5` | Code completion |
|
||||
| Ollama Auto FIM | `Any Ollama base model` | Code completion |
|
||||
| Qwen FIM | `Qwen 2.5 models` | Code completion |
|
||||
| Ollama Auto FIM | `Any Ollama base/fim models` | Code completion |
|
||||
| Qwen FIM | `Qwen 2.5 models(exclude instruct)` | Code completion |
|
||||
| StarCoder2 FIM | `starcoder2 base model` | Code completion |
|
||||
| Alpaca | `starcoder2:instruct` | Chat assistance |
|
||||
| Basic Chat| `Messages without tokens` | Chat assistance |
|
||||
| ChatML | `Qwen 2.5 models` | Chat assistance |
|
||||
| ChatML | `Qwen 2.5 models(exclude base models)` | Chat assistance |
|
||||
| Llama2 | `llama2 model family`, `codellama:instruct` | Chat assistance |
|
||||
| Llama3 | `llama3 model family` | Chat assistance |
|
||||
| Ollama Auto Chat | `Any Ollama chat model` | Chat assistance |
|
||||
| Ollama Auto Chat | `Any Ollama chat/instruct models` | Chat assistance |
|
||||
|
||||
## QtCreator Version Compatibility
|
||||
|
||||
- QtCreator 15.0.0 - 0.4.x
|
||||
- QtCreator 15.0.1 - 0.4.8 - 0.4.x
|
||||
- QtCreator 15.0.0 - 0.4.0 - 0.4.7
|
||||
- QtCreator 14.0.2 - 0.2.3 - 0.3.x
|
||||
- QtCreator 14.0.1 - 0.2.2 plugin version and below
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ add_library(Context STATIC
|
||||
ContextManager.hpp ContextManager.cpp
|
||||
ContentFile.hpp
|
||||
TokenUtils.hpp TokenUtils.cpp
|
||||
ProgrammingLanguage.hpp ProgrammingLanguage.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(Context
|
||||
|
||||
70
context/ProgrammingLanguage.cpp
Normal file
70
context/ProgrammingLanguage.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 "ProgrammingLanguage.hpp"
|
||||
|
||||
namespace QodeAssist::Context {
|
||||
|
||||
ProgrammingLanguage ProgrammingLanguageUtils::fromMimeType(const QString &mimeType)
|
||||
{
|
||||
if (mimeType == "text/x-qml" || mimeType == "application/javascript"
|
||||
|| mimeType == "text/javascript" || mimeType == "text/x-javascript") {
|
||||
return ProgrammingLanguage::QML;
|
||||
}
|
||||
if (mimeType == "text/x-c++src" || mimeType == "text/x-c++hdr" || mimeType == "text/x-csrc"
|
||||
|| mimeType == "text/x-chdr") {
|
||||
return ProgrammingLanguage::Cpp;
|
||||
}
|
||||
if (mimeType == "text/x-python") {
|
||||
return ProgrammingLanguage::Python;
|
||||
}
|
||||
return ProgrammingLanguage::Unknown;
|
||||
}
|
||||
|
||||
QString ProgrammingLanguageUtils::toString(ProgrammingLanguage language)
|
||||
{
|
||||
switch (language) {
|
||||
case ProgrammingLanguage::Cpp:
|
||||
return "c/c++";
|
||||
case ProgrammingLanguage::QML:
|
||||
return "qml";
|
||||
case ProgrammingLanguage::Python:
|
||||
return "python";
|
||||
case ProgrammingLanguage::Unknown:
|
||||
default:
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
ProgrammingLanguage ProgrammingLanguageUtils::fromString(const QString &str)
|
||||
{
|
||||
QString lower = str.toLower();
|
||||
if (lower == "c/c++") {
|
||||
return ProgrammingLanguage::Cpp;
|
||||
}
|
||||
if (lower == "qml") {
|
||||
return ProgrammingLanguage::QML;
|
||||
}
|
||||
if (lower == "python") {
|
||||
return ProgrammingLanguage::Python;
|
||||
}
|
||||
return ProgrammingLanguage::Unknown;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Context
|
||||
43
context/ProgrammingLanguage.hpp
Normal file
43
context/ProgrammingLanguage.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* QodeAssist is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QodeAssist is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace QodeAssist::Context {
|
||||
|
||||
enum class ProgrammingLanguage {
|
||||
QML, // QML/JavaScript
|
||||
Cpp, // C/C++
|
||||
Python,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
namespace ProgrammingLanguageUtils {
|
||||
|
||||
ProgrammingLanguage fromMimeType(const QString &mimeType);
|
||||
|
||||
QString toString(ProgrammingLanguage language);
|
||||
|
||||
ProgrammingLanguage fromString(const QString &str);
|
||||
|
||||
} // namespace ProgrammingLanguageUtils
|
||||
|
||||
} // namespace QodeAssist::Context
|
||||
@ -155,7 +155,9 @@ private:
|
||||
|
||||
void handleUpdateCheckResult(const PluginUpdater::UpdateInfo &info)
|
||||
{
|
||||
if (!info.isUpdateAvailable)
|
||||
if (!info.isUpdateAvailable
|
||||
|| QVersionNumber::fromString(info.currentIdeVersion)
|
||||
> QVersionNumber::fromString(info.targetIdeVersion))
|
||||
return;
|
||||
|
||||
if (m_statusWidget)
|
||||
|
||||
@ -35,11 +35,26 @@ public:
|
||||
void addToLayoutImpl(Layouting::Layout &parent) override
|
||||
{
|
||||
auto button = new QPushButton(m_buttonText);
|
||||
button->setVisible(m_visible);
|
||||
connect(button, &QPushButton::clicked, this, &ButtonAspect::clicked);
|
||||
connect(this, &ButtonAspect::visibleChanged, button, &QPushButton::setVisible);
|
||||
parent.addItem(button);
|
||||
}
|
||||
|
||||
void updateVisibility(bool visible)
|
||||
{
|
||||
if (m_visible == visible)
|
||||
return;
|
||||
m_visible = visible;
|
||||
emit visibleChanged(visible);
|
||||
}
|
||||
|
||||
QString m_buttonText;
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
void visibleChanged(bool state);
|
||||
|
||||
private:
|
||||
bool m_visible = true;
|
||||
};
|
||||
|
||||
@ -89,6 +89,39 @@ GeneralSettings::GeneralSettings()
|
||||
ccStatus.setDefaultValue("");
|
||||
ccTest.m_buttonText = TrConstants::TEST;
|
||||
|
||||
// preset1
|
||||
specifyPreset1.setSettingsKey(Constants::CC_SPECIFY_PRESET1);
|
||||
specifyPreset1.setLabelText(TrConstants::ADD_NEW_PRESET);
|
||||
specifyPreset1.setDefaultValue(false);
|
||||
|
||||
preset1Language.setSettingsKey(Constants::CC_PRESET1_LANGUAGE);
|
||||
preset1Language.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
||||
// see ProgrammingLanguageUtils
|
||||
preset1Language.addOption("qml");
|
||||
preset1Language.addOption("c/c++");
|
||||
preset1Language.addOption("python");
|
||||
|
||||
initStringAspect(
|
||||
ccPreset1Provider, Constants::CC_PRESET1_PROVIDER, TrConstants::PROVIDER, "Ollama");
|
||||
ccPreset1Provider.setReadOnly(true);
|
||||
ccPreset1SelectProvider.m_buttonText = TrConstants::SELECT;
|
||||
|
||||
initStringAspect(
|
||||
ccPreset1Url, Constants::CC_PRESET1_URL, TrConstants::URL, "http://localhost:11434");
|
||||
ccPreset1Url.setHistoryCompleter(Constants::CC_PRESET1_URL_HISTORY);
|
||||
ccPreset1SetUrl.m_buttonText = TrConstants::SELECT;
|
||||
|
||||
initStringAspect(
|
||||
ccPreset1Model, Constants::CC_PRESET1_MODEL, TrConstants::MODEL, "qwen2.5-coder:7b");
|
||||
ccPreset1Model.setHistoryCompleter(Constants::CC_PRESET1_MODEL_HISTORY);
|
||||
ccPreset1SelectModel.m_buttonText = TrConstants::SELECT;
|
||||
|
||||
initStringAspect(
|
||||
ccPreset1Template, Constants::CC_PRESET1_TEMPLATE, TrConstants::TEMPLATE, "Ollama Auto FIM");
|
||||
ccPreset1Template.setReadOnly(true);
|
||||
ccPreset1SelectTemplate.m_buttonText = TrConstants::SELECT;
|
||||
|
||||
// chat assistance
|
||||
initStringAspect(caProvider, Constants::CA_PROVIDER, TrConstants::PROVIDER, "Ollama");
|
||||
caProvider.setReadOnly(true);
|
||||
caSelectProvider.m_buttonText = TrConstants::SELECT;
|
||||
@ -117,6 +150,8 @@ GeneralSettings::GeneralSettings()
|
||||
|
||||
setupConnections();
|
||||
|
||||
updatePreset1Visiblity(specifyPreset1.value());
|
||||
|
||||
setLayouter([this]() {
|
||||
using namespace Layouting;
|
||||
|
||||
@ -126,13 +161,21 @@ GeneralSettings::GeneralSettings()
|
||||
ccGrid.addRow({ccModel, ccSelectModel});
|
||||
ccGrid.addRow({ccTemplate, ccSelectTemplate});
|
||||
|
||||
auto ccPreset1Grid = Grid{};
|
||||
ccPreset1Grid.addRow({ccPreset1Provider, ccPreset1SelectProvider});
|
||||
ccPreset1Grid.addRow({ccPreset1Url, ccPreset1SetUrl});
|
||||
ccPreset1Grid.addRow({ccPreset1Model, ccPreset1SelectModel});
|
||||
ccPreset1Grid.addRow({ccPreset1Template, ccPreset1SelectTemplate});
|
||||
|
||||
auto caGrid = Grid{};
|
||||
caGrid.addRow({caProvider, caSelectProvider});
|
||||
caGrid.addRow({caUrl, caSetUrl});
|
||||
caGrid.addRow({caModel, caSelectModel});
|
||||
caGrid.addRow({caTemplate, caSelectTemplate});
|
||||
|
||||
auto ccGroup = Group{title(TrConstants::CODE_COMPLETION), ccGrid};
|
||||
auto ccGroup = Group{
|
||||
title(TrConstants::CODE_COMPLETION),
|
||||
Column{ccGrid, Row{specifyPreset1, preset1Language, Stretch{1}}, ccPreset1Grid}};
|
||||
auto caGroup = Group{title(TrConstants::CHAT_ASSISTANT), caGrid};
|
||||
|
||||
auto rootLayout = Column{
|
||||
@ -267,9 +310,11 @@ void GeneralSettings::showUrlSelectionDialog(
|
||||
dialog.addSpacing();
|
||||
|
||||
QStringList allUrls = predefinedUrls;
|
||||
QString key
|
||||
= QString("CompleterHistory/")
|
||||
.append((&aspect == &ccUrl) ? Constants::CC_URL_HISTORY : Constants::CA_URL_HISTORY);
|
||||
QString key = QString("CompleterHistory/")
|
||||
.append(
|
||||
(&aspect == &ccUrl) ? Constants::CC_URL_HISTORY
|
||||
: (&aspect == &ccPreset1Url) ? Constants::CC_PRESET1_URL_HISTORY
|
||||
: Constants::CA_URL_HISTORY);
|
||||
QStringList historyList = qtcSettings()->value(Utils::Key(key.toLocal8Bit())).toStringList();
|
||||
allUrls.append(historyList);
|
||||
allUrls.removeDuplicates();
|
||||
@ -297,6 +342,18 @@ void GeneralSettings::showUrlSelectionDialog(
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void GeneralSettings::updatePreset1Visiblity(bool state)
|
||||
{
|
||||
ccPreset1Provider.setVisible(specifyPreset1.volatileValue());
|
||||
ccPreset1SelectProvider.updateVisibility(specifyPreset1.volatileValue());
|
||||
ccPreset1Url.setVisible(specifyPreset1.volatileValue());
|
||||
ccPreset1SetUrl.updateVisibility(specifyPreset1.volatileValue());
|
||||
ccPreset1Model.setVisible(specifyPreset1.volatileValue());
|
||||
ccPreset1SelectModel.updateVisibility(specifyPreset1.volatileValue());
|
||||
ccPreset1Template.setVisible(specifyPreset1.volatileValue());
|
||||
ccPreset1SelectTemplate.updateVisibility(specifyPreset1.volatileValue());
|
||||
}
|
||||
|
||||
void GeneralSettings::setupConnections()
|
||||
{
|
||||
connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
||||
@ -306,6 +363,10 @@ void GeneralSettings::setupConnections()
|
||||
connect(&checkUpdate, &ButtonAspect::clicked, this, [this]() {
|
||||
QodeAssist::UpdateDialog::checkForUpdatesAndShow(Core::ICore::dialogParent());
|
||||
});
|
||||
|
||||
connect(&specifyPreset1, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
||||
updatePreset1Visiblity(specifyPreset1.volatileValue());
|
||||
});
|
||||
}
|
||||
|
||||
void GeneralSettings::resetPageToDefaults()
|
||||
@ -328,6 +389,12 @@ void GeneralSettings::resetPageToDefaults()
|
||||
resetAspect(caTemplate);
|
||||
resetAspect(caUrl);
|
||||
resetAspect(enableCheckUpdate);
|
||||
resetAspect(specifyPreset1);
|
||||
resetAspect(preset1Language);
|
||||
resetAspect(ccPreset1Provider);
|
||||
resetAspect(ccPreset1Model);
|
||||
resetAspect(ccPreset1Template);
|
||||
resetAspect(ccPreset1Url);
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +55,23 @@ public:
|
||||
Utils::StringAspect ccStatus{this};
|
||||
ButtonAspect ccTest{this};
|
||||
|
||||
// TODO create dynamic presets system
|
||||
// preset1 for code completion settings
|
||||
Utils::BoolAspect specifyPreset1{this};
|
||||
Utils::SelectionAspect preset1Language{this};
|
||||
|
||||
Utils::StringAspect ccPreset1Provider{this};
|
||||
ButtonAspect ccPreset1SelectProvider{this};
|
||||
|
||||
Utils::StringAspect ccPreset1Url{this};
|
||||
ButtonAspect ccPreset1SetUrl{this};
|
||||
|
||||
Utils::StringAspect ccPreset1Model{this};
|
||||
ButtonAspect ccPreset1SelectModel{this};
|
||||
|
||||
Utils::StringAspect ccPreset1Template{this};
|
||||
ButtonAspect ccPreset1SelectTemplate{this};
|
||||
|
||||
// chat assistant settings
|
||||
Utils::StringAspect caProvider{this};
|
||||
ButtonAspect caSelectProvider{this};
|
||||
@ -82,6 +99,8 @@ public:
|
||||
|
||||
void showUrlSelectionDialog(Utils::StringAspect &aspect, const QStringList &predefinedUrls);
|
||||
|
||||
void updatePreset1Visiblity(bool state);
|
||||
|
||||
private:
|
||||
void setupConnections();
|
||||
void resetPageToDefaults();
|
||||
|
||||
@ -160,12 +160,18 @@ void PluginUpdater::handleDownloadFinished()
|
||||
}
|
||||
|
||||
QString downloadPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)
|
||||
+ QDir::separator() + "qodeassisttemp";
|
||||
+ QDir::separator() + "QodeAssist_v" + m_lastUpdateInfo.version;
|
||||
QDir().mkpath(downloadPath);
|
||||
|
||||
QString filePath = downloadPath + "/" + m_lastUpdateInfo.fileName;
|
||||
QFile file(filePath);
|
||||
QString filePath = downloadPath + QDir::separator() + m_lastUpdateInfo.fileName;
|
||||
|
||||
if (QFile::exists(filePath)) {
|
||||
emit downloadError(tr("Update file already exists: %1").arg(filePath));
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
emit downloadError(tr("Could not save the update file"));
|
||||
reply->deleteLater();
|
||||
@ -175,17 +181,7 @@ void PluginUpdater::handleDownloadFinished()
|
||||
file.write(reply->readAll());
|
||||
file.close();
|
||||
|
||||
if (!Core::executePluginInstallWizard(Utils::FilePath::fromString(filePath))) {
|
||||
emit downloadError(tr("Failed to install the update"));
|
||||
} else {
|
||||
emit downloadFinished(filePath);
|
||||
}
|
||||
|
||||
auto tempDir = QDir(downloadPath);
|
||||
if (tempDir.exists()) {
|
||||
tempDir.removeRecursively();
|
||||
}
|
||||
|
||||
emit downloadFinished(filePath);
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
@ -46,6 +46,15 @@ const char CA_TEMPLATE[] = "QodeAssist.caTemplate";
|
||||
const char CA_URL[] = "QodeAssist.caUrl";
|
||||
const char CA_URL_HISTORY[] = "QodeAssist.caUrlHistory";
|
||||
|
||||
const char CC_SPECIFY_PRESET1[] = "QodeAssist.ccSpecifyPreset1";
|
||||
const char CC_PRESET1_LANGUAGE[] = "QodeAssist.ccPreset1Language";
|
||||
const char CC_PRESET1_PROVIDER[] = "QodeAssist.ccPreset1Provider";
|
||||
const char CC_PRESET1_MODEL[] = "QodeAssist.ccPreset1Model";
|
||||
const char CC_PRESET1_MODEL_HISTORY[] = "QodeAssist.ccPreset1ModelHistory";
|
||||
const char CC_PRESET1_TEMPLATE[] = "QodeAssist.ccPreset1Template";
|
||||
const char CC_PRESET1_URL[] = "QodeAssist.ccPreset1Url";
|
||||
const char CC_PRESET1_URL_HISTORY[] = "QodeAssist.ccPreset1UrlHistory";
|
||||
|
||||
// settings
|
||||
const char ENABLE_QODE_ASSIST[] = "QodeAssist.enableQodeAssist";
|
||||
const char CC_AUTO_COMPLETION[] = "QodeAssist.ccAutoCompletion";
|
||||
|
||||
@ -86,6 +86,9 @@ inline const char ENTER_MODEL_MANUALLY_BUTTON[]
|
||||
inline const char AUTO_COMPLETION_SETTINGS[]
|
||||
= QT_TRANSLATE_NOOP("QtC::QodeAssist", "Auto Completion Settings");
|
||||
|
||||
inline const char ADD_NEW_PRESET[]
|
||||
= QT_TRANSLATE_NOOP("QtC::QodeAssist", "Add new preset for language");
|
||||
|
||||
} // namespace TrConstants
|
||||
|
||||
struct Tr
|
||||
|
||||
@ -19,6 +19,11 @@
|
||||
|
||||
#include "UpdateDialog.hpp"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
#include <extensionsystem/pluginspec.h>
|
||||
#include <QDesktopServices>
|
||||
|
||||
namespace QodeAssist {
|
||||
|
||||
UpdateDialog::UpdateDialog(QWidget *parent)
|
||||
@ -60,6 +65,12 @@ UpdateDialog::UpdateDialog(QWidget *parent)
|
||||
m_versionLabel->setAlignment(Qt::AlignCenter);
|
||||
m_layout->addWidget(m_versionLabel);
|
||||
|
||||
m_releaseLink = new QLabel(this);
|
||||
m_releaseLink->setOpenExternalLinks(true);
|
||||
m_releaseLink->setTextFormat(Qt::RichText);
|
||||
m_releaseLink->setAlignment(Qt::AlignCenter);
|
||||
m_layout->addWidget(m_releaseLink);
|
||||
|
||||
if (!m_changelogLabel) {
|
||||
m_changelogLabel = new QLabel(tr("Release Notes:"), this);
|
||||
m_layout->addWidget(m_changelogLabel);
|
||||
@ -75,7 +86,7 @@ UpdateDialog::UpdateDialog(QWidget *parent)
|
||||
m_layout->addWidget(m_progress);
|
||||
|
||||
auto *buttonLayout = new QHBoxLayout;
|
||||
m_downloadButton = new QPushButton(tr("Download and Install"), this);
|
||||
m_downloadButton = new QPushButton(tr("Download"), this);
|
||||
m_downloadButton->setEnabled(false);
|
||||
buttonLayout->addWidget(m_downloadButton);
|
||||
|
||||
@ -104,6 +115,10 @@ void UpdateDialog::checkForUpdatesAndShow(QWidget *parent)
|
||||
|
||||
void UpdateDialog::handleUpdateInfo(const PluginUpdater::UpdateInfo &info)
|
||||
{
|
||||
m_releaseLink->setText(
|
||||
tr("<a href='https://github.com/Palm1r/QodeAssist/releases'>You can also download "
|
||||
"from GitHub Releases</a>"));
|
||||
|
||||
if (info.incompatibleIdeVersion) {
|
||||
m_titleLabel->setText(tr("Incompatible Qt Creator Version"));
|
||||
m_versionLabel->setText(tr("This update requires Qt Creator %1, current is %2.\n"
|
||||
@ -156,11 +171,32 @@ void UpdateDialog::updateProgress(qint64 received, qint64 total)
|
||||
void UpdateDialog::handleDownloadFinished(const QString &path)
|
||||
{
|
||||
m_progress->setVisible(false);
|
||||
QMessageBox::information(
|
||||
this,
|
||||
tr("Update Successful"),
|
||||
tr("Update has been downloaded and installed. "
|
||||
"Please restart Qt Creator to apply changes."));
|
||||
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setWindowTitle(tr("Update Downloaded"));
|
||||
msgBox.setText(tr("The update has been downloaded successfully.\n"
|
||||
"Would you like to close Qt Creator now and open the plugin folder?"));
|
||||
msgBox.setInformativeText(tr("To complete the update:\n\n"
|
||||
"1. Close Qt Creator completely\n"
|
||||
"2. Navigate to the plugin folder\n"
|
||||
"3. Remove the old version of QodeAssist plugin\n"
|
||||
"4. Install plugin as usual via About plugin menu"));
|
||||
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setDefaultButton(QMessageBox::Yes);
|
||||
msgBox.setIcon(QMessageBox::Information);
|
||||
|
||||
if (msgBox.exec() == QMessageBox::Yes) {
|
||||
const auto pluginSpecs = ExtensionSystem::PluginManager::plugins();
|
||||
for (const ExtensionSystem::PluginSpec *spec : pluginSpecs) {
|
||||
if (spec->name() == QLatin1String("QodeAssist")) {
|
||||
const auto pluginPath = spec->filePath().path();
|
||||
QFileInfo fileInfo(pluginPath);
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.absolutePath()));
|
||||
Core::ICore::exit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
accept();
|
||||
}
|
||||
|
||||
|
||||
@ -52,6 +52,7 @@ private:
|
||||
QVBoxLayout *m_layout;
|
||||
QLabel *m_titleLabel;
|
||||
QLabel *m_versionLabel;
|
||||
QLabel *m_releaseLink;
|
||||
QLabel *m_changelogLabel{nullptr};
|
||||
QTextEdit *m_changelogText{nullptr};
|
||||
QProgressBar *m_progress;
|
||||
|
||||
48
templates/CodeLlamaQMLFim.hpp
Normal file
48
templates/CodeLlamaQMLFim.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Petr Mironychev
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* QodeAssist is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QodeAssist is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "llmcore/PromptTemplate.hpp"
|
||||
|
||||
namespace QodeAssist::Templates {
|
||||
|
||||
class CodeLlamaQMLFim : public LLMCore::PromptTemplate
|
||||
{
|
||||
public:
|
||||
LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; }
|
||||
QString name() const override { return "CodeLlama QML FIM"; }
|
||||
QString promptTemplate() const override { return "<SUF>%1<PRE>%2<MID>"; }
|
||||
QStringList stopWords() const override
|
||||
{
|
||||
return QStringList() << "<SUF>" << "<PRE>" << "</PRE>" << "</SUF>" << "< EOT >" << "\\end"
|
||||
<< "<MID>" << "</MID>" << "##";
|
||||
}
|
||||
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
|
||||
{
|
||||
QString formattedPrompt = promptTemplate().arg(context.suffix, context.prefix);
|
||||
request["prompt"] = formattedPrompt;
|
||||
}
|
||||
QString description() const override
|
||||
{
|
||||
return "The message will contain the following tokens: <SUF>%1<PRE>%2<MID>";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Templates
|
||||
@ -25,6 +25,7 @@
|
||||
#include "templates/ChatML.hpp"
|
||||
#include "templates/Claude.hpp"
|
||||
#include "templates/CodeLlamaFim.hpp"
|
||||
#include "templates/CodeLlamaQMLFim.hpp"
|
||||
#include "templates/CustomFimTemplate.hpp"
|
||||
#include "templates/DeepSeekCoderFim.hpp"
|
||||
#include "templates/Llama2.hpp"
|
||||
@ -53,6 +54,7 @@ inline void registerTemplates()
|
||||
templateManager.registerTemplate<Llama2>();
|
||||
templateManager.registerTemplate<Claude>();
|
||||
templateManager.registerTemplate<OpenAI>();
|
||||
templateManager.registerTemplate<CodeLlamaQMLFim>();
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Templates
|
||||
|
||||
Reference in New Issue
Block a user