Compare commits

...

19 Commits

Author SHA1 Message Date
Petr Mironychev
989063f6c8 feat: Add support QtCreator 19.0.1 2026-04-25 02:28:17 +02:00
Petr Mironychev
e0ab5080ea feat: Add llama.cpp api key support 2026-04-24 10:57:38 +02:00
Petr Mironychev
6a8fbe1792 chore: Update LLMQore to 0.4.2 2026-04-23 20:57:07 +02:00
Petr Mironychev
d867a6f0be docs: Add MCP client description 2026-04-23 20:13:32 +02:00
Petr Mironychev
248530c746 chore: Update plugin version to 0.9.12 2026-04-23 19:48:32 +02:00
Petr Mironychev
c73b71f328 feat: Add max continuation tools settings 2026-04-23 19:23:18 +02:00
Petr Mironychev
d2c1e39a2e chore: Update default ollama models 2026-04-23 11:29:07 +02:00
Petr Mironychev
e86e7e103e feat: Improve assemble string after code suggestion 2026-04-23 11:14:46 +02:00
Petr Mironychev
42199024ff refactor: Improvement code completion auto trigger 2026-04-23 10:56:23 +02:00
Petr Mironychev
620fded2e1 feat: Add mcp client hub 2026-04-23 10:18:57 +02:00
Petr Mironychev
90b7ed26b1 docs: Update current features in README.md 2026-04-23 03:42:35 +02:00
Petr Mironychev
25c4d5f185 feat: LM Studio response API and Ollama OpenAI API 2026-04-23 03:35:56 +02:00
Petr Mironychev
7a551ed384 feat: Add qodeassist mcp server 2026-04-23 02:40:46 +02:00
Petr Mironychev
ca0a47b160 feat: Improve Chat UI
Move send and compress button to right bottom corner
2026-04-23 01:48:17 +02:00
Petr Mironychev
6b069b55e3 chore: Update copyrights 2026-04-21 08:57:06 +02:00
Petr Mironychev
2891b313d2 refactor: Separate and simplified tools (#340) 2026-04-19 18:12:15 +02:00
Petr Mironychev
ede2c01eb7 Update LLMQore to v0.0.4 (#339) 2026-04-19 11:58:54 +02:00
Petr Mironychev
6c05f0d594 refactor: Add external LLMCore lib (#334)
* feat: Add LLMCore submodule
2026-04-03 12:30:40 +02:00
Ivan Lebedev
15d714588f fix: Open Qt Creator settings delayed from ChatRootView (#332)
fix: Opens settings delayed

Co-authored-by: Ivan Lebedev <ilebedev1988@gmail.com>
2026-03-29 11:58:34 +02:00
355 changed files with 5720 additions and 12981 deletions

View File

@@ -50,12 +50,14 @@ jobs:
qt_creator_version: "18.0.2" qt_creator_version: "18.0.2"
} }
- { - {
qt_version: "6.10.2", qt_version: "6.10.3",
qt_creator_version: "19.0.0" qt_creator_version: "19.0.1"
} }
steps: steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955
with:
submodules: recursive
- name: Checkout submodules - name: Checkout submodules
id: git id: git

2
.gitignore vendored
View File

@@ -78,3 +78,5 @@ CMakeLists.txt.user*
/.cursor /.cursor
/.vscode /.vscode
.qtc_clangd/compile_commands.json .qtc_clangd/compile_commands.json
CLAUDE.md
/.claude

3
.gitmodules vendored
View File

@@ -0,0 +1,3 @@
[submodule "sources/external/llmqore"]
path = sources/external/llmqore
url = https://github.com/Palm1r/llmqore.git

View File

@@ -34,7 +34,8 @@ add_definitions(
-DQODEASSIST_QT_CREATOR_VERSION_PATCH=${QODEASSIST_QT_CREATOR_VERSION_PATCH} -DQODEASSIST_QT_CREATOR_VERSION_PATCH=${QODEASSIST_QT_CREATOR_VERSION_PATCH}
) )
add_subdirectory(llmcore) add_subdirectory(sources/external/llmqore)
add_subdirectory(pluginllmcore)
add_subdirectory(settings) add_subdirectory(settings)
add_subdirectory(logger) add_subdirectory(logger)
add_subdirectory(UIControls) add_subdirectory(UIControls)
@@ -61,6 +62,8 @@ add_qtc_plugin(QodeAssist
QtCreator::ExtensionSystem QtCreator::ExtensionSystem
QtCreator::Utils QtCreator::Utils
QtCreator::CPlusPlus QtCreator::CPlusPlus
LLMQore
PluginLLMCore
QodeAssistChatViewplugin QodeAssistChatViewplugin
SOURCES SOURCES
.github/workflows/build_cmake.yml .github/workflows/build_cmake.yml
@@ -93,10 +96,12 @@ add_qtc_plugin(QodeAssist
templates/OpenAIResponses.hpp templates/OpenAIResponses.hpp
providers/Providers.hpp providers/Providers.hpp
providers/OllamaProvider.hpp providers/OllamaProvider.cpp providers/OllamaProvider.hpp providers/OllamaProvider.cpp
providers/OllamaCompatProvider.hpp providers/OllamaCompatProvider.cpp
providers/ClaudeProvider.hpp providers/ClaudeProvider.cpp providers/ClaudeProvider.hpp providers/ClaudeProvider.cpp
providers/OpenAIProvider.hpp providers/OpenAIProvider.cpp providers/OpenAIProvider.hpp providers/OpenAIProvider.cpp
providers/MistralAIProvider.hpp providers/MistralAIProvider.cpp providers/MistralAIProvider.hpp providers/MistralAIProvider.cpp
providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp
providers/LMStudioResponsesProvider.hpp providers/LMStudioResponsesProvider.cpp
providers/OpenAICompatProvider.hpp providers/OpenAICompatProvider.cpp providers/OpenAICompatProvider.hpp providers/OpenAICompatProvider.cpp
providers/OpenRouterAIProvider.hpp providers/OpenRouterAIProvider.cpp providers/OpenRouterAIProvider.hpp providers/OpenRouterAIProvider.cpp
providers/GoogleAIProvider.hpp providers/GoogleAIProvider.cpp providers/GoogleAIProvider.hpp providers/GoogleAIProvider.cpp
@@ -112,7 +117,6 @@ add_qtc_plugin(QodeAssist
providers/OpenAIResponses/ItemTypesReference.hpp providers/OpenAIResponses/ItemTypesReference.hpp
providers/OpenAIResponsesRequestBuilder.hpp providers/OpenAIResponsesRequestBuilder.hpp
providers/OpenAIResponsesProvider.hpp providers/OpenAIResponsesProvider.cpp providers/OpenAIResponsesProvider.hpp providers/OpenAIResponsesProvider.cpp
providers/OpenAIResponsesMessage.hpp providers/OpenAIResponsesMessage.cpp
QodeAssist.qrc QodeAssist.qrc
LSPCompletion.hpp LSPCompletion.hpp
LLMSuggestion.hpp LLMSuggestion.cpp LLMSuggestion.hpp LLMSuggestion.cpp
@@ -141,23 +145,22 @@ add_qtc_plugin(QodeAssist
widgets/DiffStatistics.hpp widgets/DiffStatistics.hpp
QuickRefactorHandler.hpp QuickRefactorHandler.cpp QuickRefactorHandler.hpp QuickRefactorHandler.cpp
tools/ToolsFactory.hpp tools/ToolsFactory.cpp tools/ToolsRegistration.hpp tools/ToolsRegistration.cpp
tools/ToolHandler.hpp tools/ToolHandler.cpp
tools/ListProjectFilesTool.hpp tools/ListProjectFilesTool.cpp tools/ListProjectFilesTool.hpp tools/ListProjectFilesTool.cpp
tools/ToolsManager.hpp tools/ToolsManager.cpp
tools/GetIssuesListTool.hpp tools/GetIssuesListTool.cpp tools/GetIssuesListTool.hpp tools/GetIssuesListTool.cpp
tools/CreateNewFileTool.hpp tools/CreateNewFileTool.cpp tools/CreateNewFileTool.hpp tools/CreateNewFileTool.cpp
tools/EditFileTool.hpp tools/EditFileTool.cpp tools/EditFileTool.hpp tools/EditFileTool.cpp
tools/BuildProjectTool.hpp tools/BuildProjectTool.cpp tools/BuildProjectTool.hpp tools/BuildProjectTool.cpp
tools/ExecuteTerminalCommandTool.hpp tools/ExecuteTerminalCommandTool.cpp tools/ExecuteTerminalCommandTool.hpp tools/ExecuteTerminalCommandTool.cpp
tools/ProjectSearchTool.hpp tools/ProjectSearchTool.cpp tools/ProjectSearchTool.hpp tools/ProjectSearchTool.cpp
tools/FindAndReadFileTool.hpp tools/FindAndReadFileTool.cpp tools/FindFileTool.hpp tools/FindFileTool.cpp
tools/ReadFileTool.hpp tools/ReadFileTool.cpp
tools/FileSearchUtils.hpp tools/FileSearchUtils.cpp tools/FileSearchUtils.hpp tools/FileSearchUtils.cpp
tools/TodoTool.hpp tools/TodoTool.cpp tools/TodoTool.hpp tools/TodoTool.cpp
providers/ClaudeMessage.hpp providers/ClaudeMessage.cpp mcp/McpServerManager.hpp mcp/McpServerManager.cpp
providers/OpenAIMessage.hpp providers/OpenAIMessage.cpp mcp/McpServerConnection.hpp mcp/McpServerConnection.cpp
providers/OllamaMessage.hpp providers/OllamaMessage.cpp mcp/McpClientsManager.hpp mcp/McpClientsManager.cpp
providers/GoogleMessage.hpp providers/GoogleMessage.cpp settings/McpClientsListAspect.hpp settings/McpClientsListAspect.cpp
) )
get_target_property(QtCreatorCorePath QtCreator::Core LOCATION) get_target_property(QtCreatorCorePath QtCreator::Core LOCATION)

View File

@@ -80,11 +80,12 @@ target_link_libraries(QodeAssistChatView
Qt::Network Qt::Network
QtCreator::Core QtCreator::Core
QtCreator::Utils QtCreator::Utils
LLMCore PluginLLMCore
QodeAssistSettings QodeAssistSettings
Context Context
QodeAssistUIControlsplugin QodeAssistUIControlsplugin
QodeAssistLogger QodeAssistLogger
LLMQore
) )
target_include_directories(QodeAssistChatView target_include_directories(QodeAssistChatView

View File

@@ -1,23 +1,9 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "ChatCompressor.hpp" #include "ChatCompressor.hpp"
#include <LLMQore/BaseClient.hpp>
#include "ChatModel.hpp" #include "ChatModel.hpp"
#include "GeneralSettings.hpp" #include "GeneralSettings.hpp"
#include "PromptTemplateManager.hpp" #include "PromptTemplateManager.hpp"
@@ -56,7 +42,7 @@ void ChatCompressor::startCompression(const QString &chatFilePath, ChatModel *ch
} }
auto providerName = Settings::generalSettings().caProvider(); auto providerName = Settings::generalSettings().caProvider();
m_provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName); m_provider = PluginLLMCore::ProvidersManager::instance().getProviderByName(providerName);
if (!m_provider) { if (!m_provider) {
emit compressionFailed(tr("No provider available")); emit compressionFailed(tr("No provider available"));
@@ -64,7 +50,7 @@ void ChatCompressor::startCompression(const QString &chatFilePath, ChatModel *ch
} }
auto templateName = Settings::generalSettings().caTemplate(); auto templateName = Settings::generalSettings().caTemplate();
auto promptTemplate = LLMCore::PromptTemplateManager::instance().getChatTemplateByName( auto promptTemplate = PluginLLMCore::PromptTemplateManager::instance().getChatTemplateByName(
templateName); templateName);
if (!promptTemplate) { if (!promptTemplate) {
@@ -76,30 +62,22 @@ void ChatCompressor::startCompression(const QString &chatFilePath, ChatModel *ch
m_chatModel = chatModel; m_chatModel = chatModel;
m_originalChatPath = chatFilePath; m_originalChatPath = chatFilePath;
m_accumulatedSummary.clear(); m_accumulatedSummary.clear();
m_currentRequestId = QUuid::createUuid().toString(QUuid::WithoutBraces);
emit compressionStarted(); emit compressionStarted();
connectProviderSignals(); connectProviderSignals();
QUrl requestUrl; QJsonObject payload{
QJsonObject payload; {"model", Settings::generalSettings().caModel()}, {"stream", true}};
if (m_provider->providerID() == LLMCore::ProviderID::GoogleAI) {
requestUrl = QUrl(QString("%1/models/%2:streamGenerateContent?alt=sse")
.arg(Settings::generalSettings().caUrl(),
Settings::generalSettings().caModel()));
} else {
requestUrl = QUrl(QString("%1%2").arg(Settings::generalSettings().caUrl(),
m_provider->chatEndpoint()));
payload["model"] = Settings::generalSettings().caModel();
payload["stream"] = true;
}
buildRequestPayload(payload, promptTemplate); buildRequestPayload(payload, promptTemplate);
const QString customEndpoint = Settings::generalSettings().caCustomEndpoint();
const QString endpoint = !customEndpoint.isEmpty() ? customEndpoint
: promptTemplate->endpoint();
m_currentRequestId = m_provider->sendRequest(
QUrl(Settings::generalSettings().caUrl()), payload, endpoint);
LOG_MESSAGE(QString("Starting compression request: %1").arg(m_currentRequestId)); LOG_MESSAGE(QString("Starting compression request: %1").arg(m_currentRequestId));
m_provider->sendRequest(m_currentRequestId, requestUrl, payload);
} }
bool ChatCompressor::isCompressing() const bool ChatCompressor::isCompressing() const
@@ -188,28 +166,28 @@ QString ChatCompressor::buildCompressionPrompt() const
} }
void ChatCompressor::buildRequestPayload( void ChatCompressor::buildRequestPayload(
QJsonObject &payload, LLMCore::PromptTemplate *promptTemplate) QJsonObject &payload, PluginLLMCore::PromptTemplate *promptTemplate)
{ {
LLMCore::ContextData context; PluginLLMCore::ContextData context;
context.systemPrompt = QStringLiteral( context.systemPrompt = QStringLiteral(
"You are a helpful assistant that creates concise summaries of conversations. " "You are a helpful assistant that creates concise summaries of conversations. "
"Your summaries preserve key information, technical details, and the flow of discussion."); "Your summaries preserve key information, technical details, and the flow of discussion.");
QVector<LLMCore::Message> messages; QVector<PluginLLMCore::Message> messages;
for (const auto &msg : m_chatModel->getChatHistory()) { for (const auto &msg : m_chatModel->getChatHistory()) {
if (msg.role == ChatModel::ChatRole::Tool if (msg.role == ChatModel::ChatRole::Tool
|| msg.role == ChatModel::ChatRole::FileEdit || msg.role == ChatModel::ChatRole::FileEdit
|| msg.role == ChatModel::ChatRole::Thinking) || msg.role == ChatModel::ChatRole::Thinking)
continue; continue;
LLMCore::Message apiMessage; PluginLLMCore::Message apiMessage;
apiMessage.role = (msg.role == ChatModel::ChatRole::User) ? "user" : "assistant"; apiMessage.role = (msg.role == ChatModel::ChatRole::User) ? "user" : "assistant";
apiMessage.content = msg.content; apiMessage.content = msg.content;
messages.append(apiMessage); messages.append(apiMessage);
} }
LLMCore::Message compressionRequest; PluginLLMCore::Message compressionRequest;
compressionRequest.role = "user"; compressionRequest.role = "user";
compressionRequest.content = buildCompressionPrompt(); compressionRequest.content = buildCompressionPrompt();
messages.append(compressionRequest); messages.append(compressionRequest);
@@ -217,7 +195,7 @@ void ChatCompressor::buildRequestPayload(
context.history = messages; context.history = messages;
m_provider->prepareRequest( m_provider->prepareRequest(
payload, promptTemplate, context, LLMCore::RequestType::Chat, false, false); payload, promptTemplate, context, PluginLLMCore::RequestType::Chat, false, false);
} }
bool ChatCompressor::createCompressedChatFile( bool ChatCompressor::createCompressedChatFile(
@@ -266,23 +244,25 @@ bool ChatCompressor::createCompressedChatFile(
void ChatCompressor::connectProviderSignals() void ChatCompressor::connectProviderSignals()
{ {
auto *c = m_provider->client();
m_connections.append(connect( m_connections.append(connect(
m_provider, c,
&LLMCore::Provider::partialResponseReceived, &::LLMQore::BaseClient::chunkReceived,
this, this,
&ChatCompressor::onPartialResponseReceived, &ChatCompressor::onPartialResponseReceived,
Qt::UniqueConnection)); Qt::UniqueConnection));
m_connections.append(connect( m_connections.append(connect(
m_provider, c,
&LLMCore::Provider::fullResponseReceived, &::LLMQore::BaseClient::requestCompleted,
this, this,
&ChatCompressor::onFullResponseReceived, &ChatCompressor::onFullResponseReceived,
Qt::UniqueConnection)); Qt::UniqueConnection));
m_connections.append(connect( m_connections.append(connect(
m_provider, c,
&LLMCore::Provider::requestFailed, &::LLMQore::BaseClient::requestFailed,
this, this,
&ChatCompressor::onRequestFailed, &ChatCompressor::onRequestFailed,
Qt::UniqueConnection)); Qt::UniqueConnection));

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once
@@ -24,10 +8,10 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
namespace QodeAssist::LLMCore { namespace QodeAssist::PluginLLMCore {
class Provider; class Provider;
class PromptTemplate; class PromptTemplate;
} // namespace QodeAssist::LLMCore } // namespace QodeAssist::PluginLLMCore
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
@@ -64,13 +48,13 @@ private:
void disconnectAllSignals(); void disconnectAllSignals();
void cleanupState(); void cleanupState();
void handleCompressionError(const QString &error); void handleCompressionError(const QString &error);
void buildRequestPayload(QJsonObject &payload, LLMCore::PromptTemplate *promptTemplate); void buildRequestPayload(QJsonObject &payload, PluginLLMCore::PromptTemplate *promptTemplate);
bool m_isCompressing = false; bool m_isCompressing = false;
QString m_currentRequestId; QString m_currentRequestId;
QString m_originalChatPath; QString m_originalChatPath;
QString m_accumulatedSummary; QString m_accumulatedSummary;
LLMCore::Provider *m_provider = nullptr; PluginLLMCore::Provider *m_provider = nullptr;
ChatModel *m_chatModel = nullptr; ChatModel *m_chatModel = nullptr;
QList<QMetaObject::Connection> m_connections; QList<QMetaObject::Connection> m_connections;

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "ChatFileManager.hpp" #include "ChatFileManager.hpp"
#include "Logger.hpp" #include "Logger.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "ChatModel.hpp" #include "ChatModel.hpp"
#include <utils/aspects.h> #include <utils/aspects.h>

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "ChatRootView.hpp" #include "ChatRootView.hpp"
@@ -51,14 +35,14 @@
#include "context/ChangesManager.h" #include "context/ChangesManager.h"
#include "context/ContextManager.hpp" #include "context/ContextManager.hpp"
#include "context/TokenUtils.hpp" #include "context/TokenUtils.hpp"
#include "llmcore/RulesLoader.hpp" #include "pluginllmcore/RulesLoader.hpp"
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
ChatRootView::ChatRootView(QQuickItem *parent) ChatRootView::ChatRootView(QQuickItem *parent)
: QQuickItem(parent) : QQuickItem(parent)
, m_chatModel(new ChatModel(this)) , m_chatModel(new ChatModel(this))
, m_promptProvider(LLMCore::PromptTemplateManager::instance()) , m_promptProvider(PluginLLMCore::PromptTemplateManager::instance())
, m_clientInterface(new ClientInterface(m_chatModel, &m_promptProvider, this)) , m_clientInterface(new ClientInterface(m_chatModel, &m_promptProvider, this))
, m_fileManager(new ChatFileManager(this)) , m_fileManager(new ChatFileManager(this))
, m_isRequestInProgress(false) , m_isRequestInProgress(false)
@@ -750,7 +734,10 @@ void ChatRootView::openRulesFolder()
void ChatRootView::openSettings() void ChatRootView::openSettings()
{ {
Settings::showSettings(Constants::QODE_ASSIST_CHAT_ASSISTANT_SETTINGS_PAGE_ID); QMetaObject::invokeMethod(
this,
[]() { Settings::showSettings(Constants::QODE_ASSIST_CHAT_ASSISTANT_SETTINGS_PAGE_ID); },
Qt::QueuedConnection);
} }
void ChatRootView::openFileInEditor(const QString &filePath) void ChatRootView::openFileInEditor(const QString &filePath)
@@ -926,7 +913,7 @@ QString ChatRootView::getRuleContent(int index)
if (index < 0 || index >= m_activeRules.size()) if (index < 0 || index >= m_activeRules.size())
return QString(); return QString();
return LLMCore::RulesLoader::loadRuleFileContent( return PluginLLMCore::RulesLoader::loadRuleFileContent(
m_activeRules[index].toMap()["filePath"].toString()); m_activeRules[index].toMap()["filePath"].toString());
} }
@@ -934,7 +921,7 @@ void ChatRootView::refreshRules()
{ {
m_activeRules.clear(); m_activeRules.clear();
auto project = LLMCore::RulesLoader::getActiveProject(); auto project = PluginLLMCore::RulesLoader::getActiveProject();
if (!project) { if (!project) {
emit activeRulesChanged(); emit activeRulesChanged();
emit activeRulesCountChanged(); emit activeRulesCountChanged();
@@ -942,7 +929,7 @@ void ChatRootView::refreshRules()
} }
auto ruleFiles auto ruleFiles
= LLMCore::RulesLoader::getRuleFilesForProject(project, LLMCore::RulesContext::Chat); = PluginLLMCore::RulesLoader::getRuleFilesForProject(project, PluginLLMCore::RulesContext::Chat);
for (const auto &ruleFile : ruleFiles) { for (const auto &ruleFile : ruleFiles) {
QVariantMap ruleMap; QVariantMap ruleMap;
@@ -1293,9 +1280,9 @@ QString ChatRootView::lastInfoMessage() const
bool ChatRootView::isThinkingSupport() const bool ChatRootView::isThinkingSupport() const
{ {
auto providerName = Settings::generalSettings().caProvider(); auto providerName = Settings::generalSettings().caProvider();
auto provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName); auto provider = PluginLLMCore::ProvidersManager::instance().getProviderByName(providerName);
return provider && provider->supportThinking(); return provider && provider->capabilities().testFlag(PluginLLMCore::ProviderCapability::Thinking);
} }
QString ChatRootView::generateChatFileName(const QString &shortMessage, const QString &dir) const QString ChatRootView::generateChatFileName(const QString &shortMessage, const QString &dir) const
@@ -1397,8 +1384,6 @@ void ChatRootView::applyConfiguration(const QString &configName)
settings.caModel.setValue(config.model); settings.caModel.setValue(config.model);
settings.caTemplate.setValue(config.templateName); settings.caTemplate.setValue(config.templateName);
settings.caUrl.setValue(config.url); settings.caUrl.setValue(config.url);
settings.caEndpointMode.setValue(
settings.caEndpointMode.indexForDisplay(config.endpointMode));
settings.caCustomEndpoint.setValue(config.customEndpoint); settings.caCustomEndpoint.setValue(config.customEndpoint);
settings.writeSettings(); settings.writeSettings();

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once
@@ -25,7 +9,7 @@
#include "ChatFileManager.hpp" #include "ChatFileManager.hpp"
#include "ChatModel.hpp" #include "ChatModel.hpp"
#include "ClientInterface.hpp" #include "ClientInterface.hpp"
#include "llmcore/PromptProviderChat.hpp" #include "pluginllmcore/PromptProviderChat.hpp"
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
@@ -235,7 +219,7 @@ private:
bool hasImageAttachments(const QStringList &attachments) const; bool hasImageAttachments(const QStringList &attachments) const;
ChatModel *m_chatModel; ChatModel *m_chatModel;
LLMCore::PromptProviderChat m_promptProvider; PluginLLMCore::PromptProviderChat m_promptProvider;
ClientInterface *m_clientInterface; ClientInterface *m_clientInterface;
ChatFileManager *m_fileManager; ChatFileManager *m_fileManager;
QString m_currentTemplate; QString m_currentTemplate;

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "ChatSerializer.hpp" #include "ChatSerializer.hpp"
#include "Logger.hpp" #include "Logger.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

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

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "ChatView.hpp" #include "ChatView.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "ChatWidget.hpp" #include "ChatWidget.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,24 +1,10 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ClientInterface.hpp" #include "ClientInterface.hpp"
#include <LLMQore/BaseClient.hpp>
#include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
@@ -40,12 +26,15 @@
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <LLMQore/ToolsManager.hpp>
#include "tools/TodoTool.hpp"
#include "ChatAssistantSettings.hpp" #include "ChatAssistantSettings.hpp"
#include "ChatSerializer.hpp" #include "ChatSerializer.hpp"
#include "GeneralSettings.hpp" #include "GeneralSettings.hpp"
#include "Logger.hpp" #include "Logger.hpp"
#include "ProvidersManager.hpp" #include "ProvidersManager.hpp"
#include "RequestConfig.hpp"
#include "ToolsSettings.hpp" #include "ToolsSettings.hpp"
#include <RulesLoader.hpp> #include <RulesLoader.hpp>
#include <context/ChangesManager.h> #include <context/ChangesManager.h>
@@ -53,7 +42,7 @@
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
ClientInterface::ClientInterface( ClientInterface::ClientInterface(
ChatModel *chatModel, LLMCore::IPromptProvider *promptProvider, QObject *parent) ChatModel *chatModel, PluginLLMCore::IPromptProvider *promptProvider, QObject *parent)
: QObject(parent) : QObject(parent)
, m_chatModel(chatModel) , m_chatModel(chatModel)
, m_promptProvider(promptProvider) , m_promptProvider(promptProvider)
@@ -138,7 +127,7 @@ void ClientInterface::sendMessage(
auto &chatAssistantSettings = Settings::chatAssistantSettings(); auto &chatAssistantSettings = Settings::chatAssistantSettings();
auto providerName = Settings::generalSettings().caProvider(); auto providerName = Settings::generalSettings().caProvider();
auto provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName); auto provider = PluginLLMCore::ProvidersManager::instance().getProviderByName(providerName);
if (!provider) { if (!provider) {
LOG_MESSAGE(QString("No provider found with name: %1").arg(providerName)); LOG_MESSAGE(QString("No provider found with name: %1").arg(providerName));
@@ -153,7 +142,7 @@ void ClientInterface::sendMessage(
return; return;
} }
LLMCore::ContextData context; PluginLLMCore::ContextData context;
const bool isToolsEnabled = useTools; const bool isToolsEnabled = useTools;
@@ -167,7 +156,7 @@ void ClientInterface::sendMessage(
systemPrompt = systemPrompt + "\n\n" + role.systemPrompt; systemPrompt = systemPrompt + "\n\n" + role.systemPrompt;
} }
auto project = LLMCore::RulesLoader::getActiveProject(); auto project = PluginLLMCore::RulesLoader::getActiveProject();
if (project) { if (project) {
systemPrompt += QString("\n# Active project name: %1").arg(project->displayName()); systemPrompt += QString("\n# Active project name: %1").arg(project->displayName());
@@ -177,12 +166,12 @@ void ClientInterface::sendMessage(
if (auto target = project->activeTarget()) { if (auto target = project->activeTarget()) {
if (auto buildConfig = target->activeBuildConfiguration()) { if (auto buildConfig = target->activeBuildConfiguration()) {
systemPrompt += QString("\n# Active Build directory: %1") systemPrompt += QString("\n# Active Build directory: %1")
.arg(buildConfig->buildDirectory().toUrlishString()); .arg(buildConfig->buildDirectory().toUrlishString());
} }
} }
QString projectRules QString projectRules
= LLMCore::RulesLoader::loadRulesForProject(project, LLMCore::RulesContext::Chat); = PluginLLMCore::RulesLoader::loadRulesForProject(project, PluginLLMCore::RulesContext::Chat);
if (!projectRules.isEmpty()) { if (!projectRules.isEmpty()) {
systemPrompt += QString("\n# Project Rules\n\n") + projectRules; systemPrompt += QString("\n# Project Rules\n\n") + projectRules;
@@ -197,13 +186,13 @@ void ClientInterface::sendMessage(
context.systemPrompt = systemPrompt; context.systemPrompt = systemPrompt;
} }
QVector<LLMCore::Message> messages; QVector<PluginLLMCore::Message> messages;
for (const auto &msg : m_chatModel->getChatHistory()) { for (const auto &msg : m_chatModel->getChatHistory()) {
if (msg.role == ChatModel::ChatRole::Tool || msg.role == ChatModel::ChatRole::FileEdit) { if (msg.role == ChatModel::ChatRole::Tool || msg.role == ChatModel::ChatRole::FileEdit) {
continue; continue;
} }
LLMCore::Message apiMessage; PluginLLMCore::Message apiMessage;
apiMessage.role = msg.role == ChatModel::ChatRole::User ? "user" : "assistant"; apiMessage.role = msg.role == ChatModel::ChatRole::User ? "user" : "assistant";
apiMessage.content = msg.content; apiMessage.content = msg.content;
@@ -223,7 +212,8 @@ void ClientInterface::sendMessage(
apiMessage.isRedacted = msg.isRedacted; apiMessage.isRedacted = msg.isRedacted;
apiMessage.signature = msg.signature; apiMessage.signature = msg.signature;
if (provider->supportImage() && !m_chatFilePath.isEmpty() && !msg.images.isEmpty()) { if (provider->capabilities().testFlag(PluginLLMCore::ProviderCapability::Image)
&& !m_chatFilePath.isEmpty() && !msg.images.isEmpty()) {
auto apiImages = loadImagesFromStorage(msg.images); auto apiImages = loadImagesFromStorage(msg.images);
if (!apiImages.isEmpty()) { if (!apiImages.isEmpty()) {
apiMessage.images = apiImages; apiMessage.images = apiImages;
@@ -233,112 +223,97 @@ void ClientInterface::sendMessage(
messages.append(apiMessage); messages.append(apiMessage);
} }
if (!imageFiles.isEmpty() && !provider->supportImage()) { if (!imageFiles.isEmpty()
&& !provider->capabilities().testFlag(PluginLLMCore::ProviderCapability::Image)) {
LOG_MESSAGE(QString("Provider %1 doesn't support images, %2 ignored") LOG_MESSAGE(QString("Provider %1 doesn't support images, %2 ignored")
.arg(provider->name(), QString::number(imageFiles.size()))); .arg(provider->name(), QString::number(imageFiles.size())));
} }
context.history = messages; context.history = messages;
LLMCore::LLMConfig config; QJsonObject payload{
config.requestType = LLMCore::RequestType::Chat; {"model", Settings::generalSettings().caModel()}, {"stream", true}};
config.provider = provider;
config.promptTemplate = promptTemplate;
if (provider->providerID() == LLMCore::ProviderID::GoogleAI) {
QString stream = QString{"streamGenerateContent?alt=sse"};
config.url = QUrl(QString("%1/models/%2:%3")
.arg(
Settings::generalSettings().caUrl(),
Settings::generalSettings().caModel(),
stream));
} else {
config.url
= QString("%1%2").arg(Settings::generalSettings().caUrl(), provider->chatEndpoint());
config.providerRequest
= {{"model", Settings::generalSettings().caModel()}, {"stream", true}};
}
config.apiKey = provider->apiKey(); provider->prepareRequest(
payload,
config.provider->prepareRequest(
config.providerRequest,
promptTemplate, promptTemplate,
context, context,
LLMCore::RequestType::Chat, PluginLLMCore::RequestType::Chat,
useTools, useTools,
useThinking); useThinking);
QString requestId = QUuid::createUuid().toString(); provider->client()->setMaxToolContinuations(
Settings::toolsSettings().maxToolContinuations());
connect(
provider->client(),
&::LLMQore::BaseClient::chunkReceived,
this,
&ClientInterface::handlePartialResponse,
Qt::UniqueConnection);
connect(
provider->client(),
&::LLMQore::BaseClient::requestCompleted,
this,
&ClientInterface::handleFullResponse,
Qt::UniqueConnection);
connect(
provider->client(),
&::LLMQore::BaseClient::requestFailed,
this,
&ClientInterface::handleRequestFailed,
Qt::UniqueConnection);
connect(
provider->client(),
&::LLMQore::BaseClient::toolStarted,
this,
&ClientInterface::handleToolExecutionStarted,
Qt::UniqueConnection);
connect(
provider->client(),
&::LLMQore::BaseClient::toolResultReady,
this,
&ClientInterface::handleToolExecutionCompleted,
Qt::UniqueConnection);
connect(
provider->client(),
&::LLMQore::BaseClient::thinkingBlockReceived,
this,
&ClientInterface::handleThinkingBlockReceived,
Qt::UniqueConnection);
const QString customEndpoint = Settings::generalSettings().caCustomEndpoint();
const QString endpoint = !customEndpoint.isEmpty() ? customEndpoint
: promptTemplate->endpoint();
auto requestId
= provider->sendRequest(QUrl(Settings::generalSettings().caUrl()), payload, endpoint);
QJsonObject request{{"id", requestId}}; QJsonObject request{{"id", requestId}};
m_activeRequests[requestId] = {request, provider}; m_activeRequests[requestId] = {request, provider};
emit requestStarted(requestId); emit requestStarted(requestId);
connect(
provider,
&LLMCore::Provider::partialResponseReceived,
this,
&ClientInterface::handlePartialResponse,
Qt::UniqueConnection);
connect(
provider,
&LLMCore::Provider::fullResponseReceived,
this,
&ClientInterface::handleFullResponse,
Qt::UniqueConnection);
connect(
provider,
&LLMCore::Provider::requestFailed,
this,
&ClientInterface::handleRequestFailed,
Qt::UniqueConnection);
connect(
provider,
&LLMCore::Provider::toolExecutionStarted,
this,
&ClientInterface::handleToolExecutionStarted,
Qt::UniqueConnection);
connect(
provider,
&LLMCore::Provider::toolExecutionCompleted,
this,
&ClientInterface::handleToolExecutionCompleted,
Qt::UniqueConnection);
connect(
provider,
&LLMCore::Provider::continuationStarted,
this,
&ClientInterface::handleCleanAccumulatedData,
Qt::UniqueConnection);
connect(
provider,
&LLMCore::Provider::thinkingBlockReceived,
this,
&ClientInterface::handleThinkingBlockReceived,
Qt::UniqueConnection);
connect(
provider,
&LLMCore::Provider::redactedThinkingBlockReceived,
this,
&ClientInterface::handleRedactedThinkingBlockReceived,
Qt::UniqueConnection);
provider->sendRequest(requestId, config.url, config.providerRequest);
if (provider->supportsTools() && provider->toolsManager()) { if (provider->capabilities().testFlag(PluginLLMCore::ProviderCapability::Tools)
provider->toolsManager()->setCurrentSessionId(m_chatFilePath); && provider->toolsManager()) {
if (auto *todoTool = qobject_cast<QodeAssist::Tools::TodoTool *>(
provider->toolsManager()->tool("todo_tool"))) {
todoTool->setCurrentSessionId(m_chatFilePath);
}
} }
} }
void ClientInterface::clearMessages() void ClientInterface::clearMessages()
{ {
const auto providerName = Settings::generalSettings().caProvider(); const auto providerName = Settings::generalSettings().caProvider();
auto *provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName); auto *provider = PluginLLMCore::ProvidersManager::instance().getProviderByName(providerName);
if (provider && !m_chatFilePath.isEmpty() && provider->supportsTools() if (provider && !m_chatFilePath.isEmpty()
&& provider->capabilities().testFlag(PluginLLMCore::ProviderCapability::Tools)
&& provider->toolsManager()) { && provider->toolsManager()) {
provider->toolsManager()->clearTodoSession(m_chatFilePath); if (auto *todoTool = qobject_cast<QodeAssist::Tools::TodoTool *>(
provider->toolsManager()->tool("todo_tool"))) {
todoTool->clearSession(m_chatFilePath);
}
} }
m_chatModel->clear(); m_chatModel->clear();
@@ -346,7 +321,7 @@ void ClientInterface::clearMessages()
void ClientInterface::cancelRequest() void ClientInterface::cancelRequest()
{ {
QSet<LLMCore::Provider *> providers; QSet<PluginLLMCore::Provider *> providers;
for (auto it = m_activeRequests.begin(); it != m_activeRequests.end(); ++it) { for (auto it = m_activeRequests.begin(); it != m_activeRequests.end(); ++it) {
if (it.value().provider) { if (it.value().provider) {
providers.insert(it.value().provider); providers.insert(it.value().provider);
@@ -354,7 +329,7 @@ void ClientInterface::cancelRequest()
} }
for (auto *provider : providers) { for (auto *provider : providers) {
disconnect(provider, nullptr, this, nullptr); disconnect(provider->client(), nullptr, this, nullptr);
} }
for (auto it = m_activeRequests.begin(); it != m_activeRequests.end(); ++it) { for (auto it = m_activeRequests.begin(); it != m_activeRequests.end(); ++it) {
@@ -366,6 +341,7 @@ void ClientInterface::cancelRequest()
m_activeRequests.clear(); m_activeRequests.clear();
m_accumulatedResponses.clear(); m_accumulatedResponses.clear();
m_awaitingContinuation.clear();
LOG_MESSAGE("All requests cancelled and state cleared"); LOG_MESSAGE("All requests cancelled and state cleared");
} }
@@ -432,6 +408,12 @@ void ClientInterface::handlePartialResponse(const QString &requestId, const QStr
if (it == m_activeRequests.end()) if (it == m_activeRequests.end())
return; return;
if (m_awaitingContinuation.remove(requestId)) {
m_accumulatedResponses[requestId].clear();
LOG_MESSAGE(
QString("Cleared accumulated responses for continuation request %1").arg(requestId));
}
m_accumulatedResponses[requestId] += partialText; m_accumulatedResponses[requestId] += partialText;
const RequestContext &ctx = it.value(); const RequestContext &ctx = it.value();
@@ -462,12 +444,9 @@ void ClientInterface::handleFullResponse(const QString &requestId, const QString
+ ": " + finalText); + ": " + finalText);
emit messageReceivedCompletely(); emit messageReceivedCompletely();
if (it != m_activeRequests.end()) { m_activeRequests.erase(it);
m_activeRequests.erase(it); m_accumulatedResponses.remove(requestId);
} m_awaitingContinuation.remove(requestId);
if (m_accumulatedResponses.contains(requestId)) {
m_accumulatedResponses.remove(requestId);
}
} }
void ClientInterface::handleRequestFailed(const QString &requestId, const QString &error) void ClientInterface::handleRequestFailed(const QString &requestId, const QString &error)
@@ -479,18 +458,9 @@ void ClientInterface::handleRequestFailed(const QString &requestId, const QStrin
LOG_MESSAGE(QString("Chat request %1 failed: %2").arg(requestId, error)); LOG_MESSAGE(QString("Chat request %1 failed: %2").arg(requestId, error));
emit errorOccurred(error); emit errorOccurred(error);
if (it != m_activeRequests.end()) { m_activeRequests.erase(it);
m_activeRequests.erase(it); m_accumulatedResponses.remove(requestId);
} m_awaitingContinuation.remove(requestId);
if (m_accumulatedResponses.contains(requestId)) {
m_accumulatedResponses.remove(requestId);
}
}
void ClientInterface::handleCleanAccumulatedData(const QString &requestId)
{
m_accumulatedResponses[requestId].clear();
LOG_MESSAGE(QString("Cleared accumulated responses for continuation request %1").arg(requestId));
} }
void ClientInterface::handleThinkingBlockReceived( void ClientInterface::handleThinkingBlockReceived(
@@ -501,19 +471,17 @@ void ClientInterface::handleThinkingBlockReceived(
return; return;
} }
m_chatModel->addThinkingBlock(requestId, thinking, signature); if (m_awaitingContinuation.remove(requestId)) {
} m_accumulatedResponses[requestId].clear();
void ClientInterface::handleRedactedThinkingBlockReceived(
const QString &requestId, const QString &signature)
{
if (!m_activeRequests.contains(requestId)) {
LOG_MESSAGE( LOG_MESSAGE(
QString("Ignoring redacted thinking block for non-chat request: %1").arg(requestId)); QString("Cleared accumulated responses for continuation request %1").arg(requestId));
return;
} }
m_chatModel->addRedactedThinkingBlock(requestId, signature); if (thinking.isEmpty()) {
m_chatModel->addRedactedThinkingBlock(requestId, signature);
} else {
m_chatModel->addThinkingBlock(requestId, thinking, signature);
}
} }
void ClientInterface::handleToolExecutionStarted( void ClientInterface::handleToolExecutionStarted(
@@ -525,6 +493,7 @@ void ClientInterface::handleToolExecutionStarted(
} }
m_chatModel->addToolExecutionStatus(requestId, toolId, toolName); m_chatModel->addToolExecutionStatus(requestId, toolId, toolName);
m_awaitingContinuation.insert(requestId);
} }
void ClientInterface::handleToolExecutionCompleted( void ClientInterface::handleToolExecutionCompleted(
@@ -588,10 +557,10 @@ QString ClientInterface::encodeImageToBase64(const QString &filePath) const
return imageData.toBase64(); return imageData.toBase64();
} }
QVector<LLMCore::ImageAttachment> ClientInterface::loadImagesFromStorage( QVector<PluginLLMCore::ImageAttachment> ClientInterface::loadImagesFromStorage(
const QList<ChatModel::ImageAttachment> &storedImages) const const QList<ChatModel::ImageAttachment> &storedImages) const
{ {
QVector<LLMCore::ImageAttachment> apiImages; QVector<PluginLLMCore::ImageAttachment> apiImages;
for (const auto &storedImage : storedImages) { for (const auto &storedImage : storedImages) {
QString base64Data QString base64Data
@@ -601,7 +570,7 @@ QVector<LLMCore::ImageAttachment> ClientInterface::loadImagesFromStorage(
continue; continue;
} }
LLMCore::ImageAttachment apiImage; PluginLLMCore::ImageAttachment apiImage;
apiImage.data = base64Data; apiImage.data = base64Data;
apiImage.mediaType = storedImage.mediaType; apiImage.mediaType = storedImage.mediaType;
apiImage.isUrl = false; apiImage.isUrl = false;
@@ -616,10 +585,15 @@ void ClientInterface::setChatFilePath(const QString &filePath)
{ {
if (!m_chatFilePath.isEmpty() && m_chatFilePath != filePath) { if (!m_chatFilePath.isEmpty() && m_chatFilePath != filePath) {
const auto providerName = Settings::generalSettings().caProvider(); const auto providerName = Settings::generalSettings().caProvider();
auto *provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName); auto *provider = PluginLLMCore::ProvidersManager::instance().getProviderByName(providerName);
if (provider && provider->supportsTools() && provider->toolsManager()) { if (provider
provider->toolsManager()->clearTodoSession(m_chatFilePath); && provider->capabilities().testFlag(PluginLLMCore::ProviderCapability::Tools)
&& provider->toolsManager()) {
if (auto *todoTool = qobject_cast<QodeAssist::Tools::TodoTool *>(
provider->toolsManager()->tool("todo_tool"))) {
todoTool->clearSession(m_chatFilePath);
}
} }
} }

View File

@@ -1,31 +1,16 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once
#include <QObject> #include <QObject>
#include <QSet>
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include "ChatModel.hpp" #include "ChatModel.hpp"
#include "Provider.hpp" #include "Provider.hpp"
#include "llmcore/IPromptProvider.hpp" #include "pluginllmcore/IPromptProvider.hpp"
#include <context/ContextManager.hpp> #include <context/ContextManager.hpp>
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
@@ -36,7 +21,7 @@ class ClientInterface : public QObject
public: public:
explicit ClientInterface( explicit ClientInterface(
ChatModel *chatModel, LLMCore::IPromptProvider *promptProvider, QObject *parent = nullptr); ChatModel *chatModel, PluginLLMCore::IPromptProvider *promptProvider, QObject *parent = nullptr);
~ClientInterface(); ~ClientInterface();
void sendMessage( void sendMessage(
@@ -62,10 +47,8 @@ private slots:
void handlePartialResponse(const QString &requestId, const QString &partialText); void handlePartialResponse(const QString &requestId, const QString &partialText);
void handleFullResponse(const QString &requestId, const QString &fullText); void handleFullResponse(const QString &requestId, const QString &fullText);
void handleRequestFailed(const QString &requestId, const QString &error); void handleRequestFailed(const QString &requestId, const QString &error);
void handleCleanAccumulatedData(const QString &requestId);
void handleThinkingBlockReceived( void handleThinkingBlockReceived(
const QString &requestId, const QString &thinking, const QString &signature); const QString &requestId, const QString &thinking, const QString &signature);
void handleRedactedThinkingBlockReceived(const QString &requestId, const QString &signature);
void handleToolExecutionStarted( void handleToolExecutionStarted(
const QString &requestId, const QString &toolId, const QString &toolName); const QString &requestId, const QString &toolId, const QString &toolName);
void handleToolExecutionCompleted( void handleToolExecutionCompleted(
@@ -82,21 +65,22 @@ private:
bool isImageFile(const QString &filePath) const; bool isImageFile(const QString &filePath) const;
QString getMediaTypeForImage(const QString &filePath) const; QString getMediaTypeForImage(const QString &filePath) const;
QString encodeImageToBase64(const QString &filePath) const; QString encodeImageToBase64(const QString &filePath) const;
QVector<LLMCore::ImageAttachment> loadImagesFromStorage(const QList<ChatModel::ImageAttachment> &storedImages) const; QVector<PluginLLMCore::ImageAttachment> loadImagesFromStorage(const QList<ChatModel::ImageAttachment> &storedImages) const;
struct RequestContext struct RequestContext
{ {
QJsonObject originalRequest; QJsonObject originalRequest;
LLMCore::Provider *provider; PluginLLMCore::Provider *provider;
}; };
LLMCore::IPromptProvider *m_promptProvider = nullptr; PluginLLMCore::IPromptProvider *m_promptProvider = nullptr;
ChatModel *m_chatModel; ChatModel *m_chatModel;
Context::ContextManager *m_contextManager; Context::ContextManager *m_contextManager;
QString m_chatFilePath; QString m_chatFilePath;
QHash<QString, RequestContext> m_activeRequests; QHash<QString, RequestContext> m_activeRequests;
QHash<QString, QString> m_accumulatedResponses; QHash<QString, QString> m_accumulatedResponses;
QSet<QString> m_awaitingContinuation;
}; };
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "FileItem.hpp" #include "FileItem.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2026 Petr Mironychev
* Copyright (C) 2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "FileMentionItem.hpp" #include "FileMentionItem.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2026 Petr Mironychev
* Copyright (C) 2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls
@@ -78,7 +62,22 @@ ChatRootView {
} }
} }
QoABusyOverlay {
id: compressingOverlay
z: 50
anchors.fill: mainColumn
anchors.topMargin: topBar.height
anchors.bottomMargin: bottomBar.height
active: root.isCompressing
text: qsTr("Compressing chat…")
}
ColumnLayout { ColumnLayout {
id: mainColumn
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: 0
@@ -88,12 +87,9 @@ ChatRootView {
Layout.preferredWidth: parent.width Layout.preferredWidth: parent.width
Layout.preferredHeight: childrenRect.height + 10 Layout.preferredHeight: childrenRect.height + 10
isCompressing: root.isCompressing
saveButton.onClicked: root.showSaveDialog() saveButton.onClicked: root.showSaveDialog()
loadButton.onClicked: root.showLoadDialog() loadButton.onClicked: root.showLoadDialog()
clearButton.onClicked: root.clearChat() clearButton.onClicked: root.clearChat()
compressButton.onClicked: compressConfirmDialog.open()
cancelCompressButton.onClicked: root.cancelCompression()
tokensBadge { tokensBadge {
text: qsTr("%1/%2").arg(root.inputTokensCount).arg(root.chatModel.tokensThreshold) text: qsTr("%1/%2").arg(root.inputTokensCount).arg(root.chatModel.tokensThreshold)
} }
@@ -493,12 +489,16 @@ ChatRootView {
Layout.preferredWidth: parent.width Layout.preferredWidth: parent.width
Layout.preferredHeight: 40 Layout.preferredHeight: 40
isCompressing: root.isCompressing
sendButton.onClicked: !root.isRequestInProgress ? root.sendChatMessage() sendButton.onClicked: !root.isRequestInProgress ? root.sendChatMessage()
: root.cancelRequest() : root.cancelRequest()
sendButton.icon.source: !root.isRequestInProgress ? "qrc:/qt/qml/ChatView/icons/chat-icon.svg" sendButton.icon.source: !root.isRequestInProgress ? "qrc:/qt/qml/ChatView/icons/chat-icon.svg"
: "qrc:/qt/qml/ChatView/icons/chat-pause-icon.svg" : "qrc:/qt/qml/ChatView/icons/chat-pause-icon.svg"
sendButton.text: !root.isRequestInProgress ? qsTr("Send") : qsTr("Stop")
sendButton.ToolTip.text: !root.isRequestInProgress ? qsTr("Send message to LLM %1").arg(Qt.platform.os === "osx" ? "Cmd+Return" : "Ctrl+Return") sendButton.ToolTip.text: !root.isRequestInProgress ? qsTr("Send message to LLM %1").arg(Qt.platform.os === "osx" ? "Cmd+Return" : "Ctrl+Return")
: qsTr("Stop") : qsTr("Stop")
compressButton.onClicked: compressConfirmDialog.open()
cancelCompressButton.onClicked: root.cancelCompression()
syncOpenFiles { syncOpenFiles {
checked: root.isSyncOpenFiles checked: root.isSyncOpenFiles
onCheckedChanged: root.setIsSyncOpenFiles(bottomBar.syncOpenFiles.checked) onCheckedChanged: root.setIsSyncOpenFiles(bottomBar.syncOpenFiles.checked)

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import ChatView import ChatView

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import Qt.labs.platform as Platform import Qt.labs.platform as Platform

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import Qt.labs.platform as Platform import Qt.labs.platform as Platform

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import Qt.labs.platform as Platform import Qt.labs.platform as Platform

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls
@@ -31,6 +15,10 @@ Rectangle {
property alias attachFiles: attachFilesId property alias attachFiles: attachFilesId
property alias attachImages: attachImagesId property alias attachImages: attachImagesId
property alias linkFiles: linkFilesId property alias linkFiles: linkFilesId
property alias compressButton: compressButtonId
property alias cancelCompressButton: cancelCompressButtonId
property bool isCompressing: false
color: palette.window.hslLightness > 0.5 ? color: palette.window.hslLightness > 0.5 ?
Qt.darker(palette.window, 1.1) : Qt.darker(palette.window, 1.1) :
@@ -49,17 +37,6 @@ Rectangle {
spacing: 10 spacing: 10
QoAButton {
id: sendButtonId
icon {
height: 15
width: 15
}
ToolTip.visible: hovered
ToolTip.delay: 250
}
QoAButton { QoAButton {
id: attachFilesId id: attachFilesId
@@ -111,5 +88,66 @@ Rectangle {
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
} }
Row {
id: compressingRow
visible: root.isCompressing
spacing: 6
BusyIndicator {
id: compressBusyIndicator
anchors.verticalCenter: parent.verticalCenter
running: root.isCompressing
width: 16
height: 16
}
Text {
text: qsTr("Compressing...")
anchors.verticalCenter: parent.verticalCenter
color: palette.text
font.pixelSize: 12
}
QoAButton {
id: cancelCompressButtonId
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
ToolTip.visible: hovered
ToolTip.delay: 250
ToolTip.text: qsTr("Cancel compression")
}
}
QoAButton {
id: compressButtonId
visible: !root.isCompressing
text: qsTr("Compress")
icon {
source: "qrc:/qt/qml/ChatView/icons/compress-icon.svg"
height: 15
width: 15
}
ToolTip.visible: hovered
ToolTip.delay: 250
ToolTip.text: qsTr("Compress chat (create summarized copy using LLM)")
}
QoAButton {
id: sendButtonId
icon {
height: 15
width: 15
}
ToolTip.visible: hovered
ToolTip.delay: 250
}
} }
} }

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2026 Petr Mironychev
* Copyright (C) 2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Layouts import QtQuick.Layouts
@@ -29,8 +13,6 @@ Rectangle {
property alias saveButton: saveButtonId property alias saveButton: saveButtonId
property alias loadButton: loadButtonId property alias loadButton: loadButtonId
property alias clearButton: clearButtonId property alias clearButton: clearButtonId
property alias compressButton: compressButtonId
property alias cancelCompressButton: cancelCompressButtonId
property alias tokensBadge: tokensBadgeId property alias tokensBadge: tokensBadgeId
property alias recentPath: recentPathId property alias recentPath: recentPathId
property alias openChatHistory: openChatHistoryId property alias openChatHistory: openChatHistoryId
@@ -42,8 +24,6 @@ Rectangle {
property alias configSelector: configSelectorId property alias configSelector: configSelectorId
property alias roleSelector: roleSelector property alias roleSelector: roleSelector
property bool isCompressing: false
color: palette.window.hslLightness > 0.5 ? color: palette.window.hslLightness > 0.5 ?
Qt.darker(palette.window, 1.1) : Qt.darker(palette.window, 1.1) :
Qt.lighter(palette.window, 1.1) Qt.lighter(palette.window, 1.1)
@@ -258,55 +238,6 @@ Rectangle {
QoASeparator {} QoASeparator {}
QoAButton {
id: compressButtonId
visible: !root.isCompressing
icon {
source: "qrc:/qt/qml/ChatView/icons/compress-icon.svg"
height: 15
width: 15
}
ToolTip.visible: hovered
ToolTip.delay: 250
ToolTip.text: qsTr("Compress chat (create summarized copy using LLM)")
}
Row {
id: compressingRow
visible: root.isCompressing
spacing: 6
BusyIndicator {
id: compressBusyIndicator
anchors.verticalCenter: parent.verticalCenter
running: root.isCompressing
width: 16
height: 16
}
Text {
text: qsTr("Compressing...")
height: parent.height
color: palette.text
font.pixelSize: 12
verticalAlignment: Text.AlignVCenter
}
QoAButton {
id: cancelCompressButtonId
text: qsTr("Cancel")
ToolTip.visible: hovered
ToolTip.delay: 250
ToolTip.text: qsTr("Cancel compression")
}
}
QoAButton { QoAButton {
id: contextButtonId id: contextButtonId

View File

@@ -1,22 +1,6 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // Copyright (C) 2025 Povilas Kanapickas <povilas@radix.lt>
* Copyright (C) 2025 Povilas Kanapickas <povilas@radix.lt> // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "CodeHandler.hpp" #include "CodeHandler.hpp"
#include <settings/CodeCompletionSettings.hpp> #include <settings/CodeCompletionSettings.hpp>

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ConfigurationManager.hpp" #include "ConfigurationManager.hpp"
@@ -41,7 +25,7 @@ void ConfigurationManager::init()
void ConfigurationManager::updateTemplateDescription(const Utils::StringAspect &templateAspect) void ConfigurationManager::updateTemplateDescription(const Utils::StringAspect &templateAspect)
{ {
LLMCore::PromptTemplate *templ = m_templateManger.getFimTemplateByName(templateAspect.value()); PluginLLMCore::PromptTemplate *templ = m_templateManger.getFimTemplateByName(templateAspect.value());
if (!templ) { if (!templ) {
return; return;
@@ -65,7 +49,7 @@ void ConfigurationManager::updateAllTemplateDescriptions()
void ConfigurationManager::checkTemplate(const Utils::StringAspect &templateAspect) void ConfigurationManager::checkTemplate(const Utils::StringAspect &templateAspect)
{ {
LLMCore::PromptTemplate *templ = m_templateManger.getFimTemplateByName(templateAspect.value()); PluginLLMCore::PromptTemplate *templ = m_templateManger.getFimTemplateByName(templateAspect.value());
if (templ->name() == templateAspect.value()) if (templ->name() == templateAspect.value())
return; return;
@@ -86,8 +70,8 @@ void ConfigurationManager::checkAllTemplate()
ConfigurationManager::ConfigurationManager(QObject *parent) ConfigurationManager::ConfigurationManager(QObject *parent)
: QObject(parent) : QObject(parent)
, m_generalSettings(Settings::generalSettings()) , m_generalSettings(Settings::generalSettings())
, m_providersManager(LLMCore::ProvidersManager::instance()) , m_providersManager(PluginLLMCore::ProvidersManager::instance())
, m_templateManger(LLMCore::PromptTemplateManager::instance()) , m_templateManger(PluginLLMCore::PromptTemplateManager::instance())
{} {}
void ConfigurationManager::setupConnections() void ConfigurationManager::setupConnections()
@@ -176,7 +160,7 @@ void ConfigurationManager::selectModel()
: m_generalSettings.caModel); : m_generalSettings.caModel);
if (auto provider = m_providersManager.getProviderByName(providerName)) { if (auto provider = m_providersManager.getProviderByName(providerName)) {
if (!provider->supportsModelListing()) { if (!provider->capabilities().testFlag(PluginLLMCore::ProviderCapability::ModelListing)) {
m_generalSettings.showModelsNotSupportedDialog(*targetSettings); m_generalSettings.showModelsNotSupportedDialog(*targetSettings);
return; return;
} }

View File

@@ -1,28 +1,12 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once
#include <QObject> #include <QObject>
#include "llmcore/PromptTemplateManager.hpp" #include "pluginllmcore/PromptTemplateManager.hpp"
#include "llmcore/ProvidersManager.hpp" #include "pluginllmcore/ProvidersManager.hpp"
#include "settings/GeneralSettings.hpp" #include "settings/GeneralSettings.hpp"
namespace QodeAssist { namespace QodeAssist {
@@ -54,8 +38,8 @@ private:
ConfigurationManager &operator=(const ConfigurationManager &) = delete; ConfigurationManager &operator=(const ConfigurationManager &) = delete;
Settings::GeneralSettings &m_generalSettings; Settings::GeneralSettings &m_generalSettings;
LLMCore::ProvidersManager &m_providersManager; PluginLLMCore::ProvidersManager &m_providersManager;
LLMCore::PromptTemplateManager &m_templateManger; PluginLLMCore::PromptTemplateManager &m_templateManger;
void setupConnections(); void setupConnections();
}; };

View File

@@ -1,24 +1,9 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "LLMClientInterface.hpp" #include "LLMClientInterface.hpp"
#include <LLMQore/BaseClient.hpp>
#include <QJsonDocument> #include <QJsonDocument>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
@@ -29,16 +14,15 @@
#include "logger/Logger.hpp" #include "logger/Logger.hpp"
#include "settings/CodeCompletionSettings.hpp" #include "settings/CodeCompletionSettings.hpp"
#include "settings/GeneralSettings.hpp" #include "settings/GeneralSettings.hpp"
#include <llmcore/RequestConfig.hpp> #include <pluginllmcore/RulesLoader.hpp>
#include <llmcore/RulesLoader.hpp>
namespace QodeAssist { namespace QodeAssist {
LLMClientInterface::LLMClientInterface( LLMClientInterface::LLMClientInterface(
const Settings::GeneralSettings &generalSettings, const Settings::GeneralSettings &generalSettings,
const Settings::CodeCompletionSettings &completeSettings, const Settings::CodeCompletionSettings &completeSettings,
LLMCore::IProviderRegistry &providerRegistry, PluginLLMCore::IProviderRegistry &providerRegistry,
LLMCore::IPromptProvider *promptProvider, PluginLLMCore::IPromptProvider *promptProvider,
Context::IDocumentReader &documentReader, Context::IDocumentReader &documentReader,
IRequestPerformanceLogger &performanceLogger) IRequestPerformanceLogger &performanceLogger)
: m_generalSettings(generalSettings) : m_generalSettings(generalSettings)
@@ -85,14 +69,15 @@ void LLMClientInterface::handleRequestFailed(const QString &requestId, const QSt
if (it == m_activeRequests.end()) if (it == m_activeRequests.end())
return; return;
LOG_MESSAGE(QString("Request %1 failed: %2").arg(requestId, error));
// Send LSP error response to client
const RequestContext &ctx = it.value(); const RequestContext &ctx = it.value();
LOG_MESSAGE(QString("Request %1 failed: %2").arg(requestId, error));
// Send LSP error response to client
QJsonObject response; QJsonObject response;
response["jsonrpc"] = "2.0"; response["jsonrpc"] = "2.0";
response[LanguageServerProtocol::idKey] = ctx.originalRequest["id"]; response[LanguageServerProtocol::idKey] = ctx.originalRequest["id"];
QJsonObject errorObject; QJsonObject errorObject;
errorObject["code"] = -32603; // Internal error code errorObject["code"] = -32603; // Internal error code
errorObject["message"] = error; errorObject["message"] = error;
@@ -122,8 +107,6 @@ void LLMClientInterface::sendData(const QByteArray &data)
} else if (method == "textDocument/didOpen") { } else if (method == "textDocument/didOpen") {
handleTextDocumentDidOpen(request); handleTextDocumentDidOpen(request);
} else if (method == "getCompletionsCycling") { } else if (method == "getCompletionsCycling") {
QString requestId = request["id"].toString();
m_performanceLogger.startTimeMeasurement(requestId);
handleCompletion(request); handleCompletion(request);
} else if (method == "$/cancelRequest") { } else if (method == "$/cancelRequest") {
handleCancelRequest(); handleCancelRequest();
@@ -136,7 +119,7 @@ void LLMClientInterface::sendData(const QByteArray &data)
void LLMClientInterface::handleCancelRequest() void LLMClientInterface::handleCancelRequest()
{ {
QSet<LLMCore::Provider *> providers; QSet<PluginLLMCore::Provider *> providers;
for (auto it = m_activeRequests.begin(); it != m_activeRequests.end(); ++it) { for (auto it = m_activeRequests.begin(); it != m_activeRequests.end(); ++it) {
if (it.value().provider) { if (it.value().provider) {
providers.insert(it.value().provider); providers.insert(it.value().provider);
@@ -144,7 +127,7 @@ void LLMClientInterface::handleCancelRequest()
} }
for (auto *provider : providers) { for (auto *provider : providers) {
disconnect(provider, nullptr, this, nullptr); disconnect(provider->client(), nullptr, this, nullptr);
} }
for (auto it = m_activeRequests.begin(); it != m_activeRequests.end(); ++it) { for (auto it = m_activeRequests.begin(); it != m_activeRequests.end(); ++it) {
@@ -270,39 +253,24 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
return; return;
} }
// TODO refactor to dynamic presets system QJsonObject payload{{"model", modelName}, {"stream", true}};
LLMCore::LLMConfig config;
config.requestType = LLMCore::RequestType::CodeCompletion;
config.provider = provider;
config.promptTemplate = promptTemplate;
// TODO refactor networking
if (provider->providerID() == LLMCore::ProviderID::GoogleAI) {
QString stream = QString{"streamGenerateContent?alt=sse"};
config.url = QUrl(QString("%1/models/%2:%3").arg(url, modelName, stream));
} else {
config.url = QUrl(
QString("%1%2").arg(url, endpoint(provider, promptTemplate->type(), isPreset1Active)));
config.providerRequest = {{"model", modelName}, {"stream", true}};
}
config.apiKey = provider->apiKey();
config.multiLineCompletion = m_completeSettings.multiLineCompletion();
const auto stopWords = QJsonArray::fromStringList(config.promptTemplate->stopWords()); const auto stopWords = QJsonArray::fromStringList(promptTemplate->stopWords());
if (!stopWords.isEmpty()) if (!stopWords.isEmpty())
config.providerRequest["stop"] = stopWords; payload["stop"] = stopWords;
QString systemPrompt; QString systemPrompt;
if (m_completeSettings.useSystemPrompt()) if (m_completeSettings.useSystemPrompt())
systemPrompt.append( systemPrompt.append(
m_completeSettings.useUserMessageTemplateForCC() m_completeSettings.useUserMessageTemplateForCC()
&& promptTemplate->type() == LLMCore::TemplateType::Chat && promptTemplate->type() == PluginLLMCore::TemplateType::Chat
? m_completeSettings.systemPromptForNonFimModels() ? m_completeSettings.systemPromptForNonFimModels()
: m_completeSettings.systemPrompt()); : m_completeSettings.systemPrompt());
auto project = LLMCore::RulesLoader::getActiveProject(); auto project = PluginLLMCore::RulesLoader::getActiveProject();
if (project) { if (project) {
QString projectRules QString projectRules
= LLMCore::RulesLoader::loadRulesForProject(project, LLMCore::RulesContext::Completions); = PluginLLMCore::RulesLoader::loadRulesForProject(project, PluginLLMCore::RulesContext::Completions);
if (!projectRules.isEmpty()) { if (!projectRules.isEmpty()) {
systemPrompt += "\n\n# Project Rules\n\n" + projectRules; systemPrompt += "\n\n# Project Rules\n\n" + projectRules;
@@ -314,10 +282,10 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
systemPrompt.append(updatedContext.fileContext.value()); systemPrompt.append(updatedContext.fileContext.value());
if (m_completeSettings.useOpenFilesContext()) { if (m_completeSettings.useOpenFilesContext()) {
if (provider->providerID() == LLMCore::ProviderID::LlamaCpp) { if (provider->providerID() == PluginLLMCore::ProviderID::LlamaCpp) {
for (const auto openedFilePath : m_contextManager->openedFiles({filePath})) { for (const auto openedFilePath : m_contextManager->openedFiles({filePath})) {
if (!updatedContext.filesMetadata) { if (!updatedContext.filesMetadata) {
updatedContext.filesMetadata = QList<LLMCore::FileMetadata>(); updatedContext.filesMetadata = QList<PluginLLMCore::FileMetadata>();
} }
updatedContext.filesMetadata->append({openedFilePath.first, openedFilePath.second}); updatedContext.filesMetadata->append({openedFilePath.first, openedFilePath.second});
} }
@@ -328,7 +296,7 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
updatedContext.systemPrompt = systemPrompt; updatedContext.systemPrompt = systemPrompt;
if (promptTemplate->type() == LLMCore::TemplateType::Chat) { if (promptTemplate->type() == PluginLLMCore::TemplateType::Chat) {
QString userMessage; QString userMessage;
if (m_completeSettings.useUserMessageTemplateForCC()) { if (m_completeSettings.useUserMessageTemplateForCC()) {
userMessage = m_completeSettings.processMessageToFIM( userMessage = m_completeSettings.processMessageToFIM(
@@ -338,50 +306,39 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
} }
// TODO refactor add message // TODO refactor add message
QVector<LLMCore::Message> messages; QVector<PluginLLMCore::Message> messages;
messages.append({"user", userMessage}); messages.append({"user", userMessage});
updatedContext.history = messages; updatedContext.history = messages;
} }
config.provider->prepareRequest( provider->prepareRequest(
config.providerRequest, payload,
promptTemplate, promptTemplate,
updatedContext, updatedContext,
LLMCore::RequestType::CodeCompletion, PluginLLMCore::RequestType::CodeCompletion,
false, false,
false); false);
auto errors = config.provider->validateRequest(config.providerRequest, promptTemplate->type());
if (!errors.isEmpty()) {
QString error = QString("Request validation failed: %1").arg(errors.join("; "));
LOG_MESSAGE("Validate errors for request:");
LOG_MESSAGES(errors);
sendErrorResponse(request, error);
return;
}
QString requestId = request["id"].toString();
m_performanceLogger.startTimeMeasurement(requestId);
m_activeRequests[requestId] = {request, provider};
connect( connect(
provider, provider->client(),
&LLMCore::Provider::fullResponseReceived, &::LLMQore::BaseClient::requestCompleted,
this, this,
&LLMClientInterface::handleFullResponse, &LLMClientInterface::handleFullResponse,
Qt::UniqueConnection); Qt::UniqueConnection);
connect( connect(
provider, provider->client(),
&LLMCore::Provider::requestFailed, &::LLMQore::BaseClient::requestFailed,
this, this,
&LLMClientInterface::handleRequestFailed, &LLMClientInterface::handleRequestFailed,
Qt::UniqueConnection); Qt::UniqueConnection);
provider->sendRequest(requestId, config.url, config.providerRequest); auto requestId
= provider->sendRequest(QUrl(url), payload, resolveEndpoint(promptTemplate, isPreset1Active));
m_activeRequests[requestId] = {request, provider};
m_performanceLogger.startTimeMeasurement(requestId);
} }
LLMCore::ContextData LLMClientInterface::prepareContext( PluginLLMCore::ContextData LLMClientInterface::prepareContext(
const QJsonObject &request, const Context::DocumentInfo &documentInfo) const QJsonObject &request, const Context::DocumentInfo &documentInfo)
{ {
QJsonObject params = request["params"].toObject(); QJsonObject params = request["params"].toObject();
@@ -395,24 +352,12 @@ LLMCore::ContextData LLMClientInterface::prepareContext(
return reader.prepareContext(lineNumber, cursorPosition, m_completeSettings); return reader.prepareContext(lineNumber, cursorPosition, m_completeSettings);
} }
QString LLMClientInterface::endpoint( QString LLMClientInterface::resolveEndpoint(
LLMCore::Provider *provider, LLMCore::TemplateType type, bool isLanguageSpecify) PluginLLMCore::PromptTemplate *promptTemplate, bool isLanguageSpecify) const
{ {
QString endpoint; const QString custom = isLanguageSpecify ? m_generalSettings.ccPreset1CustomEndpoint()
auto endpointMode = isLanguageSpecify ? m_generalSettings.ccPreset1EndpointMode.stringValue() : m_generalSettings.ccCustomEndpoint();
: m_generalSettings.ccEndpointMode.stringValue(); return !custom.isEmpty() ? custom : promptTemplate->endpoint();
if (endpointMode == "Auto") {
endpoint = type == LLMCore::TemplateType::FIM ? provider->completionEndpoint()
: provider->chatEndpoint();
} else if (endpointMode == "Custom") {
endpoint = isLanguageSpecify ? m_generalSettings.ccPreset1CustomEndpoint()
: m_generalSettings.ccCustomEndpoint();
} else if (endpointMode == "FIM") {
endpoint = provider->completionEndpoint();
} else if (endpointMode == "Chat") {
endpoint = provider->chatEndpoint();
}
return endpoint;
} }
Context::ContextManager *LLMClientInterface::contextManager() const Context::ContextManager *LLMClientInterface::contextManager() const

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once
@@ -25,9 +9,9 @@
#include <context/ContextManager.hpp> #include <context/ContextManager.hpp>
#include <context/IDocumentReader.hpp> #include <context/IDocumentReader.hpp>
#include <context/ProgrammingLanguage.hpp> #include <context/ProgrammingLanguage.hpp>
#include <llmcore/ContextData.hpp> #include <pluginllmcore/ContextData.hpp>
#include <llmcore/IPromptProvider.hpp> #include <pluginllmcore/IPromptProvider.hpp>
#include <llmcore/IProviderRegistry.hpp> #include <pluginllmcore/IProviderRegistry.hpp>
#include <logger/IRequestPerformanceLogger.hpp> #include <logger/IRequestPerformanceLogger.hpp>
#include <settings/CodeCompletionSettings.hpp> #include <settings/CodeCompletionSettings.hpp>
#include <settings/GeneralSettings.hpp> #include <settings/GeneralSettings.hpp>
@@ -45,8 +29,8 @@ public:
LLMClientInterface( LLMClientInterface(
const Settings::GeneralSettings &generalSettings, const Settings::GeneralSettings &generalSettings,
const Settings::CodeCompletionSettings &completeSettings, const Settings::CodeCompletionSettings &completeSettings,
LLMCore::IProviderRegistry &providerRegistry, PluginLLMCore::IProviderRegistry &providerRegistry,
LLMCore::IPromptProvider *promptProvider, PluginLLMCore::IPromptProvider *promptProvider,
Context::IDocumentReader &documentReader, Context::IDocumentReader &documentReader,
IRequestPerformanceLogger &performanceLogger); IRequestPerformanceLogger &performanceLogger);
~LLMClientInterface() override; ~LLMClientInterface() override;
@@ -82,17 +66,19 @@ private:
struct RequestContext struct RequestContext
{ {
QJsonObject originalRequest; QJsonObject originalRequest;
LLMCore::Provider *provider; PluginLLMCore::Provider *provider;
}; };
LLMCore::ContextData prepareContext( PluginLLMCore::ContextData prepareContext(
const QJsonObject &request, const Context::DocumentInfo &documentInfo); const QJsonObject &request, const Context::DocumentInfo &documentInfo);
QString endpoint(LLMCore::Provider *provider, LLMCore::TemplateType type, bool isLanguageSpecify);
QString resolveEndpoint(
PluginLLMCore::PromptTemplate *promptTemplate, bool isLanguageSpecify) const;
const Settings::CodeCompletionSettings &m_completeSettings; const Settings::CodeCompletionSettings &m_completeSettings;
const Settings::GeneralSettings &m_generalSettings; const Settings::GeneralSettings &m_generalSettings;
LLMCore::IPromptProvider *m_promptProvider = nullptr; PluginLLMCore::IPromptProvider *m_promptProvider = nullptr;
LLMCore::IProviderRegistry &m_providerRegistry; PluginLLMCore::IProviderRegistry &m_providerRegistry;
Context::IDocumentReader &m_documentReader; Context::IDocumentReader &m_documentReader;
IRequestPerformanceLogger &m_performanceLogger; IRequestPerformanceLogger &m_performanceLogger;
QElapsedTimer m_completionTimer; QElapsedTimer m_completionTimer;

View File

@@ -1,26 +1,6 @@
/* // Copyright (C) 2023 The Qt Company Ltd.
* Copyright (C) 2023 The Qt Company Ltd. // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* This file is part of QodeAssist.
*
* The Qt Company portions:
* SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
*
* Petr Mironychev portions:
* 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 "LLMSuggestion.hpp" #include "LLMSuggestion.hpp"
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
@@ -29,56 +9,43 @@
namespace QodeAssist { namespace QodeAssist {
static QStringList extractTokens(const QString &str) static bool isClosingTail(const QString &s, int from)
{ {
QStringList tokens; static const QString closeChars = QStringLiteral("(){}[];,");
QString currentToken; for (int i = from; i < s.size(); ++i) {
for (const QChar &ch : str) { const QChar c = s.at(i);
if (ch.isLetterOrNumber() || ch == '_') { if (!c.isSpace() && !closeChars.contains(c))
currentToken += ch; return false;
} else {
if (!currentToken.isEmpty() && currentToken.length() > 1) {
tokens.append(currentToken);
}
currentToken.clear();
}
} }
if (!currentToken.isEmpty() && currentToken.length() > 1) { return true;
tokens.append(currentToken);
}
return tokens;
} }
int LLMSuggestion::calculateReplaceLength(const QString &suggestion, int LLMSuggestion::calculateReplaceLength(const QString &suggestion, const QString &rightText)
const QString &rightText,
const QString &entireLine)
{ {
if (rightText.isEmpty()) { if (rightText.isEmpty())
return 0; return 0;
const int maxN = qMin(suggestion.size(), rightText.size());
int lcp = 0;
while (lcp < maxN && suggestion.at(lcp) == rightText.at(lcp))
++lcp;
if (lcp > 0) {
if (isClosingTail(rightText, lcp))
return rightText.size();
return lcp;
} }
QString structuralChars = "{}[]()<>;,"; if (!isClosingTail(rightText, 0))
bool hasStructuralOverlap = false; return 0;
for (const QChar &ch : structuralChars) {
if (suggestion.contains(ch) && rightText.contains(ch)) { static const QString closeChars = QStringLiteral("(){}[];,");
hasStructuralOverlap = true; int i = suggestion.size() - 1;
break; while (i >= 0 && suggestion.at(i).isSpace())
} --i;
} if (i >= 0 && closeChars.contains(suggestion.at(i)) && rightText.contains(suggestion.at(i)))
return rightText.size();
if (hasStructuralOverlap) {
return rightText.length();
}
const QStringList suggestionTokens = extractTokens(suggestion);
const QStringList lineTokens = extractTokens(entireLine);
for (const auto &token : suggestionTokens) {
if (lineTokens.contains(token)) {
return rightText.length();
}
}
return 0; return 0;
} }
@@ -102,22 +69,21 @@ LLMSuggestion::LLMSuggestion(
QString rightText = blockText.mid(cursorPositionInBlock); QString rightText = blockText.mid(cursorPositionInBlock);
QString suggestionText = data.text; QString suggestionText = data.text;
QString entireLine = blockText;
if (!suggestionText.contains('\n')) { if (!suggestionText.contains('\n')) {
int replaceLength = calculateReplaceLength(suggestionText, rightText, entireLine); int replaceLength = calculateReplaceLength(suggestionText, rightText);
QString remainingRightText = (replaceLength > 0) ? rightText.mid(replaceLength) : rightText; QString remainingRightText = (replaceLength > 0) ? rightText.mid(replaceLength) : rightText;
QString displayText = leftText + suggestionText + remainingRightText; QString displayText = leftText + suggestionText + remainingRightText;
replacementDocument()->setPlainText(displayText); replacementDocument()->setPlainText(displayText);
} else { } else {
int firstLineEnd = suggestionText.indexOf('\n'); int firstLineEnd = suggestionText.indexOf('\n');
QString firstLine = suggestionText.left(firstLineEnd); QString firstLine = suggestionText.left(firstLineEnd);
QString restOfCompletion = suggestionText.mid(firstLineEnd); QString restOfCompletion = suggestionText.mid(firstLineEnd);
int replaceLength = calculateReplaceLength(firstLine, rightText, entireLine); int replaceLength = calculateReplaceLength(firstLine, rightText);
QString remainingRightText = (replaceLength > 0) ? rightText.mid(replaceLength) : rightText; QString remainingRightText = (replaceLength > 0) ? rightText.mid(replaceLength) : rightText;
QString displayText = leftText + firstLine + remainingRightText + restOfCompletion; QString displayText = leftText + firstLine + remainingRightText + restOfCompletion;
replacementDocument()->setPlainText(displayText); replacementDocument()->setPlainText(displayText);
} }
@@ -167,10 +133,9 @@ bool LLMSuggestion::applyPart(Part part, TextEditor::TextEditorWidget *widget)
if (startPos == 0) { if (startPos == 0) {
QTextBlock currentBlock = cursor.block(); QTextBlock currentBlock = cursor.block();
QString textAfterCursor = currentBlock.text().mid(cursor.positionInBlock()); QString textAfterCursor = currentBlock.text().mid(cursor.positionInBlock());
QString entireLine = currentBlock.text();
int replaceLength = calculateReplaceLength(text, textAfterCursor);
int replaceLength = calculateReplaceLength(text, textAfterCursor, entireLine);
if (replaceLength > 0) { if (replaceLength > 0) {
currentCursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, replaceLength); currentCursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, replaceLength);
currentCursor.removeSelectedText(); currentCursor.removeSelectedText();
@@ -220,9 +185,7 @@ bool LLMSuggestion::apply()
QString text = currentData.text; QString text = currentData.text;
QTextBlock currentBlock = cursor.block(); QTextBlock currentBlock = cursor.block();
QString textBeforeCursor = currentBlock.text().left(cursor.positionInBlock());
QString textAfterCursor = currentBlock.text().mid(cursor.positionInBlock()); QString textAfterCursor = currentBlock.text().mid(cursor.positionInBlock());
QString entireLine = currentBlock.text();
QTextCursor editCursor = cursor; QTextCursor editCursor = cursor;
editCursor.beginEditBlock(); editCursor.beginEditBlock();
@@ -232,22 +195,22 @@ bool LLMSuggestion::apply()
QString firstLine = text.left(firstLineEnd); QString firstLine = text.left(firstLineEnd);
QString restOfText = text.mid(firstLineEnd); QString restOfText = text.mid(firstLineEnd);
int replaceLength = calculateReplaceLength(firstLine, textAfterCursor, entireLine); int replaceLength = calculateReplaceLength(firstLine, textAfterCursor);
if (replaceLength > 0) { if (replaceLength > 0) {
editCursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, replaceLength); editCursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, replaceLength);
editCursor.removeSelectedText(); editCursor.removeSelectedText();
} }
editCursor.insertText(firstLine + restOfText); editCursor.insertText(firstLine + restOfText);
} else { } else {
int replaceLength = calculateReplaceLength(text, textAfterCursor, entireLine); int replaceLength = calculateReplaceLength(text, textAfterCursor);
if (replaceLength > 0) { if (replaceLength > 0) {
editCursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, replaceLength); editCursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, replaceLength);
editCursor.removeSelectedText(); editCursor.removeSelectedText();
} }
editCursor.insertText(text); editCursor.insertText(text);
} }

View File

@@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2023 The Qt Company Ltd. * Copyright (C) 2023 The Qt Company Ltd.
* Copyright (C) 2024-2025 Petr Mironychev * Copyright (C) 2024-2026 Petr Mironychev
* *
* This file is part of QodeAssist. * This file is part of QodeAssist.
* *
@@ -42,8 +42,6 @@ public:
bool applyPart(Part part, TextEditor::TextEditorWidget *widget); bool applyPart(Part part, TextEditor::TextEditorWidget *widget);
bool apply() override; bool apply() override;
static int calculateReplaceLength(const QString &suggestion, static int calculateReplaceLength(const QString &suggestion, const QString &rightText);
const QString &rightText,
const QString &entireLine);
}; };
} // namespace QodeAssist } // namespace QodeAssist

View File

@@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2023 The Qt Company Ltd. * Copyright (C) 2023 The Qt Company Ltd.
* Copyright (C) 2024-2025 Petr Mironychev * Copyright (C) 2024-2026 Petr Mironychev
* *
* This file is part of QodeAssist. * This file is part of QodeAssist.
* *

View File

@@ -1,7 +1,7 @@
{ {
"Id" : "qodeassist", "Id" : "qodeassist",
"Name" : "QodeAssist", "Name" : "QodeAssist",
"Version" : "0.9.11", "Version" : "0.9.12",
"CompatVersion" : "${IDE_VERSION}", "CompatVersion" : "${IDE_VERSION}",
"Vendor" : "Petr Mironychev", "Vendor" : "Petr Mironychev",
"VendorId" : "petrmironychev", "VendorId" : "petrmironychev",

View File

@@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2023 The Qt Company Ltd. * Copyright (C) 2023 The Qt Company Ltd.
* Copyright (C) 2024-2025 Petr Mironychev * Copyright (C) 2024-2026 Petr Mironychev
* *
* This file is part of QodeAssist. * This file is part of QodeAssist.
* *
@@ -54,6 +54,90 @@ using namespace Core;
namespace QodeAssist { namespace QodeAssist {
namespace {
Utils::Text::Position toTextPos(const Utils::Text::Position &pos)
{
return Utils::Text::Position{pos.line, pos.column};
}
bool isIdentifierChar(QChar c)
{
return c.isLetterOrNumber() || c == QLatin1Char('_');
}
bool isInsideIdentifier(const QTextCursor &cursor)
{
const QTextBlock block = cursor.block();
const int col = cursor.positionInBlock();
const QString text = block.text();
if (col <= 0 || col > text.size())
return false;
if (!isIdentifierChar(text.at(col - 1)))
return false;
return col < text.size() && isIdentifierChar(text.at(col));
}
bool isAfterMemberAccess(const QTextCursor &cursor)
{
const QTextBlock block = cursor.block();
const int col = cursor.positionInBlock();
const QString text = block.text();
if (col <= 0)
return false;
int i = col - 1;
while (i >= 0 && isIdentifierChar(text.at(i)))
--i;
if (i < 0)
return false;
const QChar c = text.at(i);
if (c == QLatin1Char('.'))
return true;
if (c == QLatin1Char('>') && i >= 1 && text.at(i - 1) == QLatin1Char('-'))
return true;
if (c == QLatin1Char(':') && i >= 1 && text.at(i - 1) == QLatin1Char(':'))
return true;
return false;
}
bool isFreshIndentedLine(const QTextCursor &cursor)
{
const QTextBlock block = cursor.block();
const int col = cursor.positionInBlock();
if (col == 0)
return false;
const QString leftText = block.text().left(col);
for (const QChar &ch : leftText) {
if (!ch.isSpace())
return false;
}
return true;
}
bool isAfterEagerTrigger(const QTextCursor &cursor)
{
const QTextBlock block = cursor.block();
const int col = cursor.positionInBlock();
const QString text = block.text();
int i = col - 1;
while (i >= 0 && text.at(i).isSpace())
--i;
if (i < 0)
return false;
const QChar c = text.at(i);
return c == QLatin1Char('{') || c == QLatin1Char('(') || c == QLatin1Char(',')
|| c == QLatin1Char('=') || c == QLatin1Char('[') || c == QLatin1Char(';')
|| c == QLatin1Char(':') || c == QLatin1Char('>');
}
bool isManualMode()
{
return Settings::codeCompletionSettings().completionMode.stringValue() == "Manual";
}
} // anonymous namespace
QodeAssistClient::QodeAssistClient(LLMClientInterface *clientInterface) QodeAssistClient::QodeAssistClient(LLMClientInterface *clientInterface)
: LanguageClient::Client(clientInterface) : LanguageClient::Client(clientInterface)
, m_llmClient(clientInterface) , m_llmClient(clientInterface)
@@ -69,10 +153,6 @@ QodeAssistClient::QodeAssistClient(LLMClientInterface *clientInterface)
m_typingTimer.start(); m_typingTimer.start();
m_hintHideTimer.setSingleShot(true);
m_hintHideTimer.setInterval(Settings::codeCompletionSettings().hintHideTimeout());
connect(&m_hintHideTimer, &QTimer::timeout, this, [this]() { m_hintHandler.hideHint(); });
m_refactorHoverHandler = new RefactorSuggestionHoverHandler(); m_refactorHoverHandler = new RefactorSuggestionHoverHandler();
m_refactorWidgetHandler = new RefactorWidgetHandler(this); m_refactorWidgetHandler = new RefactorWidgetHandler(this);
} }
@@ -108,6 +188,9 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document)
if (!Settings::codeCompletionSettings().autoCompletion()) if (!Settings::codeCompletionSettings().autoCompletion())
return; return;
if (isManualMode())
return;
auto project = ProjectManager::projectForFile(document->filePath()); auto project = ProjectManager::projectForFile(document->filePath());
if (!isEnabled(project)) if (!isEnabled(project))
return; return;
@@ -131,38 +214,29 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document)
if (charsRemoved > 0 || charsAdded <= 0) { if (charsRemoved > 0 || charsAdded <= 0) {
m_recentCharCount = 0; m_recentCharCount = 0;
m_typingTimer.restart(); m_typingTimer.restart();
// 0 = Hint-based, 1 = Automatic
const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode();
if (triggerMode != 1) {
m_hintHideTimer.stop();
m_hintHandler.hideHint();
}
return; return;
} }
QTextCursor cursor = widget->textCursor(); QTextCursor cursor = widget->textCursor();
cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1); cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
QString lastChar = cursor.selectedText(); const QString lastChar = cursor.selectedText();
if (lastChar.isEmpty())
return;
if (lastChar.isEmpty() || lastChar[0].isPunct()) { const QChar lastCh = lastChar[0];
if (lastCh == QLatin1Char('\n') || lastCh == QChar::ParagraphSeparator
|| lastCh == QChar::LineSeparator) {
m_recentCharCount = 0; m_recentCharCount = 0;
m_typingTimer.restart(); m_typingTimer.restart();
// 0 = Hint-based, 1 = Automatic
const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode();
if (triggerMode != 1) {
m_hintHideTimer.stop();
m_hintHandler.hideHint();
}
return; return;
} }
bool isSpaceOrTab = lastChar[0].isSpace(); const bool isSpaceOrTab = lastCh.isSpace();
bool ignoreWhitespace const bool ignoreWhitespace
= Settings::codeCompletionSettings().ignoreWhitespaceInCharCount(); = Settings::codeCompletionSettings().ignoreWhitespaceInCharCount();
if (!ignoreWhitespace || !isSpaceOrTab) { if (!ignoreWhitespace || !isSpaceOrTab)
m_recentCharCount += charsAdded; m_recentCharCount += charsAdded;
}
if (m_typingTimer.elapsed() if (m_typingTimer.elapsed()
> Settings::codeCompletionSettings().autoCompletionTypingInterval()) { > Settings::codeCompletionSettings().autoCompletionTypingInterval()) {
@@ -170,13 +244,7 @@ void QodeAssistClient::openDocument(TextEditor::TextDocument *document)
m_typingTimer.restart(); m_typingTimer.restart();
} }
// 0 = Hint-based, 1 = Automatic handleAutoRequestTrigger(widget);
const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode();
if (triggerMode == 1) {
handleAutoRequestTrigger(widget, charsAdded, isSpaceOrTab);
} else {
handleHintBasedTrigger(widget, charsAdded, isSpaceOrTab, cursor);
}
}); });
} }
@@ -205,11 +273,9 @@ void QodeAssistClient::requestCompletions(TextEditor::TextEditorWidget *editor)
if (cursor.hasMultipleCursors() || cursor.hasSelection() || editor->suggestionVisible()) if (cursor.hasMultipleCursors() || cursor.hasSelection() || editor->suggestionVisible())
return; return;
const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode(); const auto &settings = Settings::codeCompletionSettings();
if (settings.abortAssistOnRequest() && !settings.respectQtcPopup())
if (Settings::codeCompletionSettings().abortAssistOnRequest() && triggerMode == 0) {
editor->abortAssist(); editor->abortAssist();
}
const FilePath filePath = editor->textDocument()->filePath(); const FilePath filePath = editor->textDocument()->filePath();
GetCompletionRequest request{ GetCompletionRequest request{
@@ -270,33 +336,29 @@ void QodeAssistClient::requestQuickRefactor(
void QodeAssistClient::scheduleRequest(TextEditor::TextEditorWidget *editor) void QodeAssistClient::scheduleRequest(TextEditor::TextEditorWidget *editor)
{ {
cancelRunningRequest(editor); if (m_runningRequests.contains(editor)) {
if (Settings::codeCompletionSettings().cancelOnInput())
cancelRunningRequest(editor);
else
return;
}
auto it = m_scheduledRequests.find(editor); auto it = m_scheduledRequests.find(editor);
if (it == m_scheduledRequests.end()) { if (it == m_scheduledRequests.end()) {
auto timer = new QTimer(this); auto timer = new QTimer(this);
timer->setSingleShot(true); timer->setSingleShot(true);
connect(timer, &QTimer::timeout, this, [this, editor]() { connect(timer, &QTimer::timeout, this, [this, editor]() {
if (editor if (!editor || m_runningRequests.contains(editor))
&& editor->textCursor().position() return;
== m_scheduledRequests[editor]->property("cursorPosition").toInt() if (editor->textCursor().position()
&& m_recentCharCount != m_scheduledRequests[editor]->property("cursorPosition").toInt())
> Settings::codeCompletionSettings().autoCompletionCharThreshold()) return;
requestCompletions(editor); requestCompletions(editor);
}); });
connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() { connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() {
delete m_scheduledRequests.take(editor); delete m_scheduledRequests.take(editor);
cancelRunningRequest(editor); cancelRunningRequest(editor);
}); });
connect(editor, &TextEditorWidget::cursorPositionChanged, this, [this, editor] {
cancelRunningRequest(editor);
// 0 = Hint-based, 1 = Automatic
const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode();
if (triggerMode != 1) {
m_hintHideTimer.stop();
m_hintHandler.hideHint();
}
});
it = m_scheduledRequests.insert(editor, timer); it = m_scheduledRequests.insert(editor, timer);
} }
@@ -307,11 +369,9 @@ void QodeAssistClient::handleCompletions(
const GetCompletionRequest::Response &response, TextEditor::TextEditorWidget *editor) const GetCompletionRequest::Response &response, TextEditor::TextEditorWidget *editor)
{ {
m_progressHandler.hideProgress(); m_progressHandler.hideProgress();
const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode(); const auto &settings = Settings::codeCompletionSettings();
if (settings.abortAssistOnRequest() && !settings.respectQtcPopup())
if (Settings::codeCompletionSettings().abortAssistOnRequest() && triggerMode == 1) {
editor->abortAssist(); editor->abortAssist();
}
if (response.error()) { if (response.error()) {
log(*response.error()); log(*response.error());
@@ -325,12 +385,25 @@ void QodeAssistClient::handleCompletions(
requestPosition = requestParams->position().toPositionInDocument(editor->document()); requestPosition = requestParams->position().toPositionInDocument(editor->document());
const MultiTextCursor cursors = editor->multiTextCursor(); const MultiTextCursor cursors = editor->multiTextCursor();
if (cursors.hasMultipleCursors()) if (cursors.hasMultipleCursors() || cursors.hasSelection())
return; return;
if (cursors.hasSelection() || cursors.mainCursor().position() != requestPosition) const int currentPosition = cursors.mainCursor().position();
if (requestPosition < 0 || currentPosition < requestPosition)
return; return;
QString typedSinceRequest;
if (currentPosition > requestPosition) {
QTextCursor diffCursor(editor->document());
diffCursor.setPosition(requestPosition);
diffCursor.setPosition(currentPosition, QTextCursor::KeepAnchor);
typedSinceRequest = diffCursor.selectedText();
if (typedSinceRequest.contains(QChar::ParagraphSeparator)
|| typedSinceRequest.contains(QLatin1Char('\n'))) {
return;
}
}
if (const std::optional<GetCompletionResponse> result = response.result()) { if (const std::optional<GetCompletionResponse> result = response.result()) {
auto isValidCompletion = [](const Completion &completion) { auto isValidCompletion = [](const Completion &completion) {
return completion.isValid() && !completion.text().trimmed().isEmpty(); return completion.isValid() && !completion.text().trimmed().isEmpty();
@@ -338,34 +411,58 @@ void QodeAssistClient::handleCompletions(
QList<Completion> completions QList<Completion> completions
= Utils::filtered(result->completions().toListOrEmpty(), isValidCompletion); = Utils::filtered(result->completions().toListOrEmpty(), isValidCompletion);
QList<Completion> matchedCompletions;
matchedCompletions.reserve(completions.size());
for (Completion &completion : completions) { for (Completion &completion : completions) {
const LanguageServerProtocol::Range range = completion.range(); const LanguageServerProtocol::Range range = completion.range();
if (range.start().line() != range.end().line()) if (range.start().line() != range.end().line())
continue; continue;
const QString completionText = completion.text(); QString completionText = completion.text();
const int end = int(completionText.size()) - 1; const int end = int(completionText.size()) - 1;
int delta = 0; int delta = 0;
while (delta <= end && completionText[end - delta].isSpace()) while (delta <= end && completionText[end - delta].isSpace())
++delta; ++delta;
if (delta > 0) if (delta > 0)
completion.setText(completionText.chopped(delta)); completionText.chop(delta);
if (!typedSinceRequest.isEmpty()) {
if (!completionText.startsWith(typedSinceRequest))
continue;
completionText = completionText.mid(typedSinceRequest.size());
if (completionText.isEmpty())
continue;
}
completion.setText(completionText);
matchedCompletions.append(completion);
} }
auto suggestions = Utils::transform(completions, [](const Completion &c) {
if (matchedCompletions.isEmpty()) {
LOG_MESSAGE("No valid completions received");
return;
}
const Text::Position anchor = typedSinceRequest.isEmpty()
? Text::Position{}
: Text::Position::fromPositionInDocument(editor->document(), currentPosition);
const bool useAnchor = !typedSinceRequest.isEmpty();
auto suggestions = Utils::transform(matchedCompletions,
[useAnchor, &anchor](const Completion &c) {
auto toTextPos = [](const LanguageServerProtocol::Position pos) { auto toTextPos = [](const LanguageServerProtocol::Position pos) {
return Text::Position{pos.line() + 1, pos.character()}; return Text::Position{pos.line() + 1, pos.character()};
}; };
if (useAnchor) {
return TextSuggestion::Data{Text::Range{anchor, anchor}, anchor, c.text()};
}
Text::Range range{toTextPos(c.range().start()), toTextPos(c.range().end())}; Text::Range range{toTextPos(c.range().start()), toTextPos(c.range().end())};
Text::Position pos{toTextPos(c.position())}; Text::Position pos{toTextPos(c.position())};
return TextSuggestion::Data{range, pos, c.text()}; return TextSuggestion::Data{range, pos, c.text()};
}); });
if (completions.isEmpty()) {
LOG_MESSAGE("No valid completions received");
return;
}
editor->insertSuggestion(std::make_unique<LLMSuggestion>(suggestions, editor->document())); editor->insertSuggestion(std::make_unique<LLMSuggestion>(suggestions, editor->document()));
} }
} }
@@ -376,12 +473,6 @@ void QodeAssistClient::cancelRunningRequest(TextEditor::TextEditorWidget *editor
if (it == m_runningRequests.constEnd()) if (it == m_runningRequests.constEnd())
return; return;
m_progressHandler.hideProgress(); m_progressHandler.hideProgress();
// 0 = Hint-based, 1 = Automatic
const int triggerMode = Settings::codeCompletionSettings().completionTriggerMode();
if (triggerMode != 1) {
m_hintHideTimer.stop();
m_hintHandler.hideHint();
}
cancelRequest(it->id()); cancelRequest(it->id());
m_runningRequests.erase(it); m_runningRequests.erase(it);
} }
@@ -423,17 +514,6 @@ void QodeAssistClient::cleanupConnections()
m_scheduledRequests.clear(); m_scheduledRequests.clear();
} }
bool QodeAssistClient::isHintVisible() const
{
return m_hintHandler.isHintVisible();
}
void QodeAssistClient::hideHintAndRequestCompletion(TextEditor::TextEditorWidget *editor)
{
m_hintHandler.hideHint();
requestCompletions(editor);
}
void QodeAssistClient::handleRefactoringResult(const RefactorResult &result) void QodeAssistClient::handleRefactoringResult(const RefactorResult &result)
{ {
m_progressHandler.hideProgress(); m_progressHandler.hideProgress();
@@ -465,13 +545,6 @@ void QodeAssistClient::handleRefactoringResult(const RefactorResult &result)
} }
} }
namespace {
Utils::Text::Position toTextPos(const Utils::Text::Position &pos)
{
return Utils::Text::Position{pos.line, pos.column};
}
} // anonymous namespace
void QodeAssistClient::displayRefactoringSuggestion(const RefactorResult &result) void QodeAssistClient::displayRefactoringSuggestion(const RefactorResult &result)
{ {
TextEditorWidget *editorWidget = result.editor; TextEditorWidget *editorWidget = result.editor;
@@ -604,58 +677,20 @@ void QodeAssistClient::applyRefactoringEdit(TextEditor::TextEditorWidget *editor
editCursor.endEditBlock(); editCursor.endEditBlock();
} }
void QodeAssistClient::handleAutoRequestTrigger(TextEditor::TextEditorWidget *widget, void QodeAssistClient::handleAutoRequestTrigger(TextEditor::TextEditorWidget *widget)
int charsAdded,
bool isSpaceOrTab)
{ {
Q_UNUSED(isSpaceOrTab); const QTextCursor cursor = widget->textCursor();
const auto &settings = Settings::codeCompletionSettings();
const bool smart = settings.smartContextTrigger();
if (m_recentCharCount if (smart && (isInsideIdentifier(cursor) || isAfterMemberAccess(cursor)))
> Settings::codeCompletionSettings().autoCompletionCharThreshold()) { return;
const bool eager = smart && (isFreshIndentedLine(cursor) || isAfterEagerTrigger(cursor));
const int charThreshold = settings.autoCompletionCharThreshold();
if (eager || m_recentCharCount > charThreshold)
scheduleRequest(widget); scheduleRequest(widget);
}
}
void QodeAssistClient::handleHintBasedTrigger(TextEditor::TextEditorWidget *widget,
int charsAdded,
bool isSpaceOrTab,
QTextCursor &cursor)
{
Q_UNUSED(charsAdded);
const int hintThreshold = Settings::codeCompletionSettings().hintCharThreshold();
if (m_recentCharCount >= hintThreshold && !isSpaceOrTab) {
const QRect cursorRect = widget->cursorRect(cursor);
QPoint globalPos = widget->viewport()->mapToGlobal(cursorRect.topLeft());
QPoint localPos = widget->mapFromGlobal(globalPos);
int fontSize = widget->font().pixelSize();
if (fontSize <= 0) {
fontSize = widget->fontMetrics().height();
}
QTextCursor textCursor = widget->textCursor();
if (m_recentCharCount <= hintThreshold) {
textCursor
.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, m_recentCharCount);
} else {
textCursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, hintThreshold);
}
int x = localPos.x() + cursorRect.height();
int y = localPos.y() + cursorRect.height() / 4;
QPoint hintPos(x, y);
if (!m_hintHandler.isHintVisible()) {
m_hintHandler.showHint(widget, hintPos, fontSize);
} else {
m_hintHandler.updateHintPosition(widget, hintPos);
}
m_hintHideTimer.start();
}
} }
bool QodeAssistClient::eventFilter(QObject *watched, QEvent *event) bool QodeAssistClient::eventFilter(QObject *watched, QEvent *event)
@@ -667,46 +702,6 @@ bool QodeAssistClient::eventFilter(QObject *watched, QEvent *event)
if (event->type() == QEvent::KeyPress) { if (event->type() == QEvent::KeyPress) {
auto *keyEvent = static_cast<QKeyEvent *>(event); auto *keyEvent = static_cast<QKeyEvent *>(event);
// Check hint trigger key (0=Space, 1=Ctrl+Space, 2=Alt+Space, 3=Ctrl+Enter, 4=Tab, 5=Enter)
if (m_hintHandler.isHintVisible()) {
const int triggerKeyIndex = Settings::codeCompletionSettings().hintTriggerKey();
bool isMatchingKey = false;
const Qt::KeyboardModifiers modifiers = keyEvent->modifiers();
switch (triggerKeyIndex) {
case 0: // Space
isMatchingKey = (keyEvent->key() == Qt::Key_Space
&& (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier));
break;
case 1: // Ctrl+Space
isMatchingKey = (keyEvent->key() == Qt::Key_Space
&& (modifiers & Qt::ControlModifier));
break;
case 2: // Alt+Space
isMatchingKey = (keyEvent->key() == Qt::Key_Space
&& (modifiers & Qt::AltModifier));
break;
case 3: // Ctrl+Enter
isMatchingKey = ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter)
&& (modifiers & Qt::ControlModifier));
break;
case 4: // Tab
isMatchingKey = (keyEvent->key() == Qt::Key_Tab);
break;
case 5: // Enter
isMatchingKey = ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter)
&& (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier));
break;
}
if (isMatchingKey) {
m_hintHideTimer.stop();
m_hintHandler.hideHint();
requestCompletions(editor);
return true;
}
}
if (keyEvent->key() == Qt::Key_Escape) { if (keyEvent->key() == Qt::Key_Escape) {
if (m_runningRequests.contains(editor)) { if (m_runningRequests.contains(editor)) {
cancelRunningRequest(editor); cancelRunningRequest(editor);
@@ -724,8 +719,6 @@ bool QodeAssistClient::eventFilter(QObject *watched, QEvent *event)
} }
m_progressHandler.hideProgress(); m_progressHandler.hideProgress();
m_hintHideTimer.stop();
m_hintHandler.hideHint();
} }
} }

View File

@@ -1,26 +1,6 @@
/* // Copyright (C) 2023 The Qt Company Ltd.
* Copyright (C) 2023 The Qt Company Ltd. // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* This file is part of QodeAssist.
*
* The Qt Company portions:
* SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
*
* Petr Mironychev portions:
* 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 #pragma once
@@ -32,12 +12,11 @@
#include "RefactorSuggestionHoverHandler.hpp" #include "RefactorSuggestionHoverHandler.hpp"
#include "widgets/CompletionProgressHandler.hpp" #include "widgets/CompletionProgressHandler.hpp"
#include "widgets/CompletionErrorHandler.hpp" #include "widgets/CompletionErrorHandler.hpp"
#include "widgets/CompletionHintHandler.hpp"
#include "widgets/EditorChatButtonHandler.hpp" #include "widgets/EditorChatButtonHandler.hpp"
#include "widgets/RefactorWidgetHandler.hpp" #include "widgets/RefactorWidgetHandler.hpp"
#include <languageclient/client.h> #include <languageclient/client.h>
#include <llmcore/IPromptProvider.hpp> #include <pluginllmcore/IPromptProvider.hpp>
#include <llmcore/IProviderRegistry.hpp> #include <pluginllmcore/IProviderRegistry.hpp>
namespace QodeAssist { namespace QodeAssist {
@@ -54,9 +33,6 @@ public:
void requestCompletions(TextEditor::TextEditorWidget *editor); void requestCompletions(TextEditor::TextEditorWidget *editor);
void requestQuickRefactor( void requestQuickRefactor(
TextEditor::TextEditorWidget *editor, const QString &instructions = QString()); TextEditor::TextEditorWidget *editor, const QString &instructions = QString());
bool isHintVisible() const;
void hideHintAndRequestCompletion(TextEditor::TextEditorWidget *editor);
protected: protected:
bool eventFilter(QObject *watched, QEvent *event) override; bool eventFilter(QObject *watched, QEvent *event) override;
@@ -75,8 +51,7 @@ private:
void displayRefactoringWidget(const RefactorResult &result); void displayRefactoringWidget(const RefactorResult &result);
void applyRefactoringEdit(TextEditor::TextEditorWidget *editor, const Utils::Text::Range &range, const QString &text); void applyRefactoringEdit(TextEditor::TextEditorWidget *editor, const Utils::Text::Range &range, const QString &text);
void handleAutoRequestTrigger(TextEditor::TextEditorWidget *widget, int charsAdded, bool isSpaceOrTab); void handleAutoRequestTrigger(TextEditor::TextEditorWidget *widget);
void handleHintBasedTrigger(TextEditor::TextEditorWidget *widget, int charsAdded, bool isSpaceOrTab, QTextCursor &cursor);
QHash<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests; QHash<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests;
QHash<TextEditor::TextEditorWidget *, QTimer *> m_scheduledRequests; QHash<TextEditor::TextEditorWidget *, QTimer *> m_scheduledRequests;
@@ -85,10 +60,8 @@ private:
QElapsedTimer m_typingTimer; QElapsedTimer m_typingTimer;
int m_recentCharCount; int m_recentCharCount;
QTimer m_hintHideTimer;
CompletionProgressHandler m_progressHandler; CompletionProgressHandler m_progressHandler;
CompletionErrorHandler m_errorHandler; CompletionErrorHandler m_errorHandler;
CompletionHintHandler m_hintHandler;
EditorChatButtonHandler m_chatButtonHandler; EditorChatButtonHandler m_chatButtonHandler;
QuickRefactorHandler *m_refactorHandler{nullptr}; QuickRefactorHandler *m_refactorHandler{nullptr};
RefactorSuggestionHoverHandler *m_refactorHoverHandler{nullptr}; RefactorSuggestionHoverHandler *m_refactorHoverHandler{nullptr};

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,40 +1,25 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "QuickRefactorHandler.hpp" #include "QuickRefactorHandler.hpp"
#include <LLMQore/BaseClient.hpp>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QUuid> #include <QUuid>
#include <context/DocumentContextReader.hpp> #include <context/DocumentContextReader.hpp>
#include <llmcore/ResponseCleaner.hpp> #include <pluginllmcore/ResponseCleaner.hpp>
#include <context/DocumentReaderQtCreator.hpp> #include <context/DocumentReaderQtCreator.hpp>
#include <context/Utils.hpp> #include <context/Utils.hpp>
#include <llmcore/PromptTemplateManager.hpp> #include <pluginllmcore/PromptTemplateManager.hpp>
#include <llmcore/ProvidersManager.hpp> #include <pluginllmcore/ProvidersManager.hpp>
#include <llmcore/RequestConfig.hpp> #include <pluginllmcore/RulesLoader.hpp>
#include <llmcore/RulesLoader.hpp>
#include <logger/Logger.hpp> #include <logger/Logger.hpp>
#include <settings/ChatAssistantSettings.hpp> #include <settings/ChatAssistantSettings.hpp>
#include <settings/GeneralSettings.hpp> #include <settings/GeneralSettings.hpp>
#include <settings/QuickRefactorSettings.hpp> #include <settings/QuickRefactorSettings.hpp>
#include <settings/ToolsSettings.hpp>
namespace QodeAssist { namespace QodeAssist {
@@ -109,8 +94,8 @@ void QuickRefactorHandler::prepareAndSendRequest(
{ {
auto &settings = Settings::generalSettings(); auto &settings = Settings::generalSettings();
auto &providerRegistry = LLMCore::ProvidersManager::instance(); auto &providerRegistry = PluginLLMCore::ProvidersManager::instance();
auto &promptManager = LLMCore::PromptTemplateManager::instance(); auto &promptManager = PluginLLMCore::PromptTemplateManager::instance();
const auto providerName = settings.qrProvider(); const auto providerName = settings.qrProvider();
auto provider = providerRegistry.getProviderByName(providerName); auto provider = providerRegistry.getProviderByName(providerName);
@@ -140,70 +125,57 @@ void QuickRefactorHandler::prepareAndSendRequest(
return; return;
} }
LLMCore::LLMConfig config; QJsonObject payload{
config.requestType = LLMCore::RequestType::QuickRefactoring; {"model", Settings::generalSettings().qrModel()}, {"stream", true}};
config.provider = provider;
config.promptTemplate = promptTemplate;
config.url = QString("%1%2").arg(settings.qrUrl(), provider->chatEndpoint());
config.apiKey = provider->apiKey();
if (provider->providerID() == LLMCore::ProviderID::GoogleAI) { PluginLLMCore::ContextData context = prepareContext(editor, range, instructions);
QString stream = QString{"streamGenerateContent?alt=sse"};
config.url = QUrl(QString("%1/models/%2:%3")
.arg(
Settings::generalSettings().qrUrl(),
Settings::generalSettings().qrModel(),
stream));
} else {
config.url
= QString("%1%2").arg(Settings::generalSettings().qrUrl(), provider->chatEndpoint());
config.providerRequest
= {{"model", Settings::generalSettings().qrModel()}, {"stream", true}};
}
LLMCore::ContextData context = prepareContext(editor, range, instructions);
bool enableTools = Settings::quickRefactorSettings().useTools(); bool enableTools = Settings::quickRefactorSettings().useTools();
bool enableThinking = Settings::quickRefactorSettings().useThinking(); bool enableThinking = Settings::quickRefactorSettings().useThinking();
provider->prepareRequest( provider->prepareRequest(
config.providerRequest, payload,
promptTemplate, promptTemplate,
context, context,
LLMCore::RequestType::QuickRefactoring, PluginLLMCore::RequestType::QuickRefactoring,
enableTools, enableTools,
enableThinking); enableThinking);
QString requestId = QUuid::createUuid().toString(); provider->client()->setMaxToolContinuations(
m_lastRequestId = requestId; Settings::toolsSettings().maxToolContinuations());
QJsonObject request{{"id", requestId}};
m_isRefactoringInProgress = true; m_isRefactoringInProgress = true;
m_activeRequests[requestId] = {request, provider};
connect( connect(
provider, provider->client(),
&LLMCore::Provider::fullResponseReceived, &::LLMQore::BaseClient::requestCompleted,
this, this,
&QuickRefactorHandler::handleFullResponse, &QuickRefactorHandler::handleFullResponse,
Qt::UniqueConnection); Qt::UniqueConnection);
connect( connect(
provider, provider->client(),
&LLMCore::Provider::requestFailed, &::LLMQore::BaseClient::requestFailed,
this, this,
&QuickRefactorHandler::handleRequestFailed, &QuickRefactorHandler::handleRequestFailed,
Qt::UniqueConnection); Qt::UniqueConnection);
provider->sendRequest(requestId, config.url, config.providerRequest); const QString customEndpoint = Settings::generalSettings().qrCustomEndpoint();
const QString endpoint = !customEndpoint.isEmpty() ? customEndpoint
: promptTemplate->endpoint();
auto requestId
= provider->sendRequest(QUrl(Settings::generalSettings().qrUrl()), payload, endpoint);
m_lastRequestId = requestId;
QJsonObject request{{"id", requestId}};
m_activeRequests[requestId] = {request, provider};
} }
LLMCore::ContextData QuickRefactorHandler::prepareContext( PluginLLMCore::ContextData QuickRefactorHandler::prepareContext(
TextEditor::TextEditorWidget *editor, TextEditor::TextEditorWidget *editor,
const Utils::Text::Range &range, const Utils::Text::Range &range,
const QString &instructions) const QString &instructions)
{ {
LLMCore::ContextData context; PluginLLMCore::ContextData context;
auto textDocument = editor->textDocument(); auto textDocument = editor->textDocument();
Context::DocumentReaderQtCreator documentReader; Context::DocumentReaderQtCreator documentReader;
@@ -287,10 +259,10 @@ LLMCore::ContextData QuickRefactorHandler::prepareContext(
QString systemPrompt = Settings::quickRefactorSettings().systemPrompt(); QString systemPrompt = Settings::quickRefactorSettings().systemPrompt();
auto project = LLMCore::RulesLoader::getActiveProject(); auto project = PluginLLMCore::RulesLoader::getActiveProject();
if (project) { if (project) {
QString projectRules = LLMCore::RulesLoader::loadRulesForProject( QString projectRules = PluginLLMCore::RulesLoader::loadRulesForProject(
project, LLMCore::RulesContext::QuickRefactor); project, PluginLLMCore::RulesContext::QuickRefactor);
if (!projectRules.isEmpty()) { if (!projectRules.isEmpty()) {
systemPrompt += "\n\n# Project Rules\n\n" + projectRules; systemPrompt += "\n\n# Project Rules\n\n" + projectRules;
@@ -368,7 +340,7 @@ LLMCore::ContextData QuickRefactorHandler::prepareContext(
context.systemPrompt = systemPrompt; context.systemPrompt = systemPrompt;
QVector<LLMCore::Message> messages; QVector<PluginLLMCore::Message> messages;
messages.append( messages.append(
{"user", {"user",
instructions.isEmpty() ? "Refactor the code to improve its quality and maintainability." instructions.isEmpty() ? "Refactor the code to improve its quality and maintainability."
@@ -387,7 +359,7 @@ void QuickRefactorHandler::handleLLMResponse(
if (isComplete) { if (isComplete) {
m_isRefactoringInProgress = false; m_isRefactoringInProgress = false;
QString cleanedResponse = LLMCore::ResponseCleaner::clean(response); QString cleanedResponse = PluginLLMCore::ResponseCleaner::clean(response);
RefactorResult result; RefactorResult result;
result.newText = cleanedResponse; result.newText = cleanedResponse;

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once
@@ -27,8 +11,8 @@
#include <context/ContextManager.hpp> #include <context/ContextManager.hpp>
#include <context/IDocumentReader.hpp> #include <context/IDocumentReader.hpp>
#include <llmcore/ContextData.hpp> #include <pluginllmcore/ContextData.hpp>
#include <llmcore/Provider.hpp> #include <pluginllmcore/Provider.hpp>
namespace QodeAssist { namespace QodeAssist {
@@ -68,7 +52,7 @@ private:
const Utils::Text::Range &range); const Utils::Text::Range &range);
void handleLLMResponse(const QString &response, const QJsonObject &request, bool isComplete); void handleLLMResponse(const QString &response, const QJsonObject &request, bool isComplete);
LLMCore::ContextData prepareContext( PluginLLMCore::ContextData prepareContext(
TextEditor::TextEditorWidget *editor, TextEditor::TextEditorWidget *editor,
const Utils::Text::Range &range, const Utils::Text::Range &range,
const QString &instructions); const QString &instructions);
@@ -76,7 +60,7 @@ private:
struct RequestContext struct RequestContext
{ {
QJsonObject originalRequest; QJsonObject originalRequest;
LLMCore::Provider *provider; PluginLLMCore::Provider *provider;
}; };
QHash<QString, RequestContext> m_activeRequests; QHash<QString, RequestContext> m_activeRequests;

103
README.md
View File

@@ -1,10 +1,12 @@
# QodeAssist - AI-powered coding assistant plugin for Qt Creator # QodeAssist AI coding assistant for Qt Creator
[![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)
[![](https://dcbadge.limes.pink/api/server/BGMkUsXUgf?style=flat)](https://discord.gg/BGMkUsXUgf)
![qodeassist-icon](https://github.com/user-attachments/assets/dc336712-83cb-440d-8761-8d0a31de898d) QodeAssist is a comprehensive AI-powered coding assistant plugin for Qt Creator. It provides intelligent code completion, interactive chat with multiple interface options, inline quick refactoring, and AI function calling capabilities for C++ and QML development. Supporting both local providers (Ollama, llama.cpp, LM Studio) and cloud services (Claude, OpenAI, Google AI, Mistral AI), QodeAssist enhances your productivity with context-aware AI assistance, project-specific rules, and extensive customization options directly in your Qt development environment. [![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 Tag](https://img.shields.io/github/v/tag/Palm1r/QodeAssist?label=release)](https://github.com/Palm1r/QodeAssist/releases)
![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/Palm1r/QodeAssist/total?color=41%2C173%2C71&label=downloads)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Discord](https://dcbadge.limes.pink/api/server/BGMkUsXUgf?style=flat)](https://discord.gg/BGMkUsXUgf)
![qodeassist-icon](https://github.com/user-attachments/assets/dc336712-83cb-440d-8761-8d0a31de898d) **QodeAssist** brings a full AI coding workflow to Qt Creator for C++ and QML — smart code completion, multi-panel chat, inline quick refactoring, and project-aware tool calling. It works with local runtimes (Ollama, llama.cpp, LM Studio) and cloud providers (Claude, OpenAI, Google AI, Mistral), can run as an **MCP server** so other clients reuse its project context, and can also act as an **MCP client** to consume tools from external MCP servers (authenticated MCP servers are not supported yet).
⚠️ **Important Notice About Paid Providers** ⚠️ **Important Notice About Paid Providers**
> When using paid providers like Claude, OpenRouter or OpenAI-compatible services: > When using paid providers like Claude, OpenRouter or OpenAI-compatible services:
@@ -29,13 +31,15 @@
QodeAssist enhances Qt Creator with AI-powered coding assistance: QodeAssist enhances Qt Creator with AI-powered coding assistance:
- **Code Completion**: Intelligent, context-aware code suggestions for C++ and QML - **Code Completion** — intelligent, context-aware suggestions (FIM and chat models) for C++ and QML, with multiline support
- **Chat Assistant**: Multiple interface options (popup window, side panel, bottom panel) - **Chat Assistant** — side panel, bottom panel, or detached window; history with auto-save, token monitoring, extended thinking
- **Quick Refactoring**: Inline AI-assisted code improvements directly in editor with custom instructions library - **Quick Refactoring** — inline AI-assisted edits directly in the editor with a searchable custom-instructions library
- **File Context**: Attach or link files for better AI understanding - **Agent Tools** — read, search, create and edit files; build the project; run terminal commands; access linter/compiler issues; manage TODOs
- **Tool Calling**: AI can read project files, search code, and access diagnostics - **MCP Server** — expose QodeAssist's project-aware tools to external MCP clients (Claude Code, VS Code, Claude Desktop via bridge)
- **Multiple Providers**: Support for Ollama, Claude, OpenAI, Google AI, Mistral AI, llama.cpp, and more - **MCP Client Hub** — connect QodeAssist to external MCP servers and use their tools in Chat and Quick Refactor (authenticated MCP servers are not supported yet)
- **Customizable**: Project-specific rules, custom instructions, and extensive model templates - **File Context** — attach, link, or auto-sync open editor files for richer prompts
- **Many Providers** — Ollama, llama.cpp, LM Studio (Chat + Responses), Claude, OpenAI (Chat + Responses), Google AI, Mistral, Codestral, OpenRouter, any OpenAI-compatible endpoint
- **Customizable** — per-project rules (`.qodeassist/rules/`), agent roles, reusable refactor templates, full prompt-template control
**Join our [Discord Community](https://discord.gg/BGMkUsXUgf)** to get support and connect with other users! **Join our [Discord Community](https://discord.gg/BGMkUsXUgf)** to get support and connect with other users!
@@ -147,14 +151,19 @@ All settings (provider, model, template, URL) are configured automatically. Just
For advanced users or local models, choose your preferred provider and follow the detailed configuration guide: For advanced users or local models, choose your preferred provider and follow the detailed configuration guide:
- **[Ollama](docs/ollama-configuration.md)** - Local LLM provider **Local providers:**
- **[llama.cpp](docs/llamacpp-configuration.md)** - Local LLM server - **[Ollama](docs/ollama-configuration.md)** — native Ollama API
- **[Anthropic Claude](docs/claude-configuration.md)** - Cloud provider (manual setup) - **Ollama (OpenAI-compatible)** — Ollama's `/v1` endpoint for tool-calling models
- **[OpenAI](docs/openai-configuration.md)** - Cloud provider (includes Responses API support) - **[llama.cpp](docs/llamacpp-configuration.md)** — local `llama-server`
- **[Mistral AI](docs/mistral-configuration.md)** - Cloud provider - **LM Studio** — OpenAI-compatible Chat API
- **[Google AI](docs/google-ai-configuration.md)** - Cloud provider - **LM Studio (Responses API)** — newer models that require the Responses endpoint
- **LM Studio** - Local LLM provider
- **OpenAI-compatible** - Custom providers (OpenRouter, etc.) **Cloud providers:**
- **[Anthropic Claude](docs/claude-configuration.md)** — manual setup guide
- **[OpenAI](docs/openai-configuration.md)** — Chat Completions and Responses API
- **[Mistral AI](docs/mistral-configuration.md)** / **Codestral**
- **[Google AI](docs/google-ai-configuration.md)** — Gemini
- **OpenAI-compatible** — OpenRouter and any custom endpoint
### Recommended Models for Best Experience ### Recommended Models for Best Experience
@@ -228,10 +237,37 @@ Configure in: `Tools → Options → QodeAssist → Code Completion → General
- **[Learn more](docs/quick-refactoring.md)** - **[Learn more](docs/quick-refactoring.md)**
### Tools & Function Calling ### Tools & Function Calling
- Read project files
- List and search in project Chat and Quick Refactor can call tools to inspect and modify your project. Each tool can be individually enabled/disabled in settings.
- Access linter/compiler issues
- Enabled by default (can be disabled) | Tool | What it does |
|------|--------------|
| `list_project_files` | List files in the active project(s) |
| `find_file` | Find a file by name or partial path |
| `read_file` | Read file contents (project or absolute path) |
| `search_project` | Grep / symbol search across project sources |
| `create_new_file` | Create a new empty file on disk |
| `edit_file` | Replace content in a file (old → new) |
| `build_project` | Build the active project and return compiler output |
| `get_issues_list` | Read current linter / compiler diagnostics |
| `execute_terminal_command` | Run a shell command (with confirmation) |
| `todo_tool` | Track multi-step task progress during a conversation |
### MCP Server
QodeAssist can run an **MCP (Model Context Protocol) server** on `localhost`, exposing the tools above to external clients — so you can use QodeAssist's project awareness from Claude Code CLI, VS Code, Cursor, Claude Desktop, or any other MCP-capable client.
- **Enable** in `Tools → Options → QodeAssist → MCP Server`
- **Transport**: HTTP + SSE by default; a stdio bridge is provided for clients that only speak stdio (e.g. Claude Desktop)
- **Ready-to-copy snippets** for Claude Code, VS Code, and the bridge are available via the "Show connection instructions" button in settings
### MCP Client Hub
QodeAssist can also act as an **MCP client**, connecting to external MCP servers and making their tools available to Chat and Quick Refactor alongside the built-in ones.
- **Configure** servers in `Tools → Options → QodeAssist → MCP Client`
- **Transports**: stdio and HTTP/SSE
- **Limitation**: authenticated MCP servers (OAuth / token-protected) are **not supported yet** — only servers that accept unauthenticated local connections work for now
## Context Layers ## Context Layers
@@ -413,14 +449,16 @@ For additional support, join our [Discord Community](https://discord.gg/BGMkUsXU
## Development Progress ## Development Progress
- [x] Code completion functionality - [x] Code completion (FIM and chat models)
- [x] Chat assistant with multiple panels - [x] Chat assistant (side / bottom / detached panels)
- [x] Quick refactoring with custom-instructions library
- [x] Diff sharing with models - [x] Diff sharing with models
- [x] Tools/function calling support - [x] Tools / function calling (file I/O, build, terminal, diagnostics)
- [x] Project-specific rules - [x] Project-specific rules (`.qodeassist/rules/`)
- [x] MCP (Model Context Protocol) — QodeAssist as a server
- [x] MCP — QodeAssist as a client (consume external MCP tools; authenticated MCP servers not yet supported)
- [ ] Full project source sharing - [ ] Full project source sharing
- [ ] Additional provider support - [ ] Additional provider support
- [ ] MCP (Model Context Protocol) support
## Support the development of QodeAssist ## Support the development of QodeAssist
If you find QodeAssist helpful, there are several ways you can support the project: If you find QodeAssist helpful, there are several ways you can support the project:
@@ -439,6 +477,11 @@ If you find QodeAssist helpful, there are several ways you can support the proje
Every contribution, no matter how small, is greatly appreciated and helps keep the project alive! Every contribution, no matter how small, is greatly appreciated and helps keep the project alive!
## Related Projects
- **[LLMQore](https://github.com/Palm1r/llmqore)** — the standalone LLM-core library extracted from QodeAssist, reusable in other Qt/C++ projects
- **[QodeAssistUpdater](https://github.com/Palm1r/QodeAssistUpdater)** — CLI installer/updater for the plugin
## How to Build ## How to Build
### Prerequisites ### Prerequisites

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "RefactorSuggestion.hpp" #include "RefactorSuggestion.hpp"
#include "LLMSuggestion.hpp" #include "LLMSuggestion.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "RefactorSuggestionHoverHandler.hpp" #include "RefactorSuggestionHoverHandler.hpp"
#include "RefactorSuggestion.hpp" #include "RefactorSuggestion.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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:
*/
#include "FlowEditor.hpp" #include "FlowEditor.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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:
*/
#pragma once #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "GridBackground.hpp" #include "GridBackground.hpp"
#include <QPainter> #include <QPainter>

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "BaseTask.hpp" #include "BaseTask.hpp"
#include "TaskPort.hpp" #include "TaskPort.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "Flow.hpp" #include "Flow.hpp"
#include "TaskPort.hpp" #include "TaskPort.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "FlowManager.hpp" #include "FlowManager.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "TaskConnection.hpp" #include "TaskConnection.hpp"
#include "BaseTask.hpp" #include "BaseTask.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "TaskPort.hpp" #include "TaskPort.hpp"
#include "TaskConnection.hpp" #include "TaskConnection.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "TaskRegistry.hpp" #include "TaskRegistry.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -10,6 +10,7 @@ qt_add_qml_module(QodeAssistUIControls
QML_FILES QML_FILES
qml/Badge.qml qml/Badge.qml
qml/QoAButton.qml qml/QoAButton.qml
qml/QoABusyOverlay.qml
qml/QoATextSlider.qml qml/QoATextSlider.qml
qml/QoAComboBox.qml qml/QoAComboBox.qml
qml/FadeListItemAnimation.qml qml/FadeListItemAnimation.qml

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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

View File

@@ -0,0 +1,43 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
Rectangle {
id: root
property alias text: label.text
property bool active: false
visible: active
color: Qt.rgba(palette.window.r, palette.window.g, palette.window.b, 0.75)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.AllButtons
hoverEnabled: true
preventStealing: true
onWheel: function(wheel) { wheel.accepted = true }
}
Column {
anchors.centerIn: parent
spacing: 10
BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
running: root.active
implicitWidth: 36
implicitHeight: 36
}
Text {
id: label
anchors.horizontalCenter: parent.horizontalCenter
color: palette.text
font.pixelSize: 13
}
}
}

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls.Basic import QtQuick.Controls.Basic

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025-2026 Petr Mironychev
* Copyright (C) 2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls.Basic import QtQuick.Controls.Basic

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2026 Petr Mironychev
* Copyright (C) 2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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
import QtQuick.Controls import QtQuick.Controls

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "UpdateStatusWidget.hpp" #include "UpdateStatusWidget.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2025 Povilas Kanapickas <povilas@radix.lt>
* Copyright (C) 2025 Povilas Kanapickas <povilas@radix.lt> // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "ChatOutputPane.h" #include "ChatOutputPane.h"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "NavigationPanel.hpp" #include "NavigationPanel.hpp"

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 #pragma once

View File

@@ -20,7 +20,7 @@ target_link_libraries(Context
QtCreator::Utils QtCreator::Utils
QtCreator::ProjectExplorer QtCreator::ProjectExplorer
PRIVATE PRIVATE
LLMCore PluginLLMCore
QodeAssistSettings QodeAssistSettings
) )

View File

@@ -1,21 +1,5 @@
/* // Copyright (C) 2024-2026 Petr Mironychev
* Copyright (C) 2024-2025 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later
*
* 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 "ChangesManager.h" #include "ChangesManager.h"
#include "CodeCompletionSettings.hpp" #include "CodeCompletionSettings.hpp"

Some files were not shown because too many files have changed in this diff Show More