From ede2c01eb72a348bb8a45828ab9553492800d40e Mon Sep 17 00:00:00 2001 From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:58:54 +0200 Subject: [PATCH] Update LLMQore to v0.0.4 (#339) --- .gitignore | 2 + .gitmodules | 6 +- CMakeLists.txt | 4 +- ChatView/CMakeLists.txt | 2 +- ChatView/ChatCompressor.cpp | 29 ++-- ChatView/ChatRootView.cpp | 2 - ChatView/ClientInterface.cpp | 52 +++--- LLMClientInterface.cpp | 68 +++----- LLMClientInterface.hpp | 4 +- QuickRefactorHandler.cpp | 43 ++--- pluginllmcore/BaseTool.cpp | 73 --------- pluginllmcore/BaseTool.hpp | 70 -------- pluginllmcore/CMakeLists.txt | 4 +- pluginllmcore/IToolsManager.hpp | 51 ------ pluginllmcore/PromptTemplate.hpp | 8 + pluginllmcore/Provider.cpp | 52 ++++-- pluginllmcore/Provider.hpp | 14 +- pluginllmcore/RequestConfig.hpp | 40 ----- pluginllmcore/RequestHandlerBase.cpp | 30 ---- pluginllmcore/RequestHandlerBase.hpp | 45 ------ providers/ClaudeProvider.cpp | 16 +- providers/ClaudeProvider.hpp | 8 +- providers/GoogleAIProvider.cpp | 34 ++-- providers/GoogleAIProvider.hpp | 11 +- providers/LMStudioProvider.cpp | 18 +-- providers/LMStudioProvider.hpp | 8 +- providers/LlamaCppProvider.cpp | 16 +- providers/LlamaCppProvider.hpp | 8 +- providers/MistralAIProvider.cpp | 16 +- providers/MistralAIProvider.hpp | 8 +- providers/OllamaProvider.cpp | 16 +- providers/OllamaProvider.hpp | 8 +- providers/OpenAICompatProvider.cpp | 18 +-- providers/OpenAICompatProvider.hpp | 8 +- providers/OpenAIProvider.cpp | 18 +-- providers/OpenAIProvider.hpp | 8 +- providers/OpenAIResponsesProvider.cpp | 18 +-- providers/OpenAIResponsesProvider.hpp | 8 +- settings/CMakeLists.txt | 1 - settings/ConfigurationManager.cpp | 14 +- settings/ConfigurationManager.hpp | 2 +- settings/CustomPromptSettings.cpp | 200 ----------------------- settings/CustomPromptSettings.hpp | 49 ------ settings/GeneralSettings.cpp | 86 ++-------- settings/GeneralSettings.hpp | 4 - settings/SettingsConstants.hpp | 10 +- settings/SettingsTr.hpp | 2 +- sources/external/llmcore | 1 - sources/external/llmqore | 1 + templates/CodeLlamaFim.hpp | 1 + templates/CodeLlamaQMLFim.hpp | 1 + templates/CustomFimTemplate.hpp | 90 ----------- templates/DeepSeekCoderFim.hpp | 6 +- templates/LlamaCppFim.hpp | 1 + templates/MistralAI.hpp | 1 + templates/Ollama.hpp | 1 + templates/Qwen25CoderFIM.hpp | 1 + templates/StarCoder2Fim.hpp | 1 + templates/Templates.hpp | 4 - test/CMakeLists.txt | 2 +- test/DocumentContextReaderTest.cpp | 2 +- test/LLMClientInterfaceTests.cpp | 209 ------------------------ test/MockRequestHandler.hpp | 59 ------- test/TestUtils.hpp | 8 +- tools/BuildProjectTool.cpp | 14 +- tools/BuildProjectTool.hpp | 8 +- tools/CreateNewFileTool.cpp | 21 +-- tools/CreateNewFileTool.hpp | 6 +- tools/EditFileTool.cpp | 21 +-- tools/EditFileTool.hpp | 6 +- tools/ExecuteTerminalCommandTool.cpp | 52 +++--- tools/ExecuteTerminalCommandTool.hpp | 6 +- tools/FindAndReadFileTool.cpp | 13 +- tools/FindAndReadFileTool.hpp | 6 +- tools/GetIssuesListTool.cpp | 8 +- tools/GetIssuesListTool.hpp | 6 +- tools/ListProjectFilesTool.cpp | 11 +- tools/ListProjectFilesTool.hpp | 6 +- tools/ProjectSearchTool.cpp | 15 +- tools/ProjectSearchTool.hpp | 6 +- tools/TodoTool.cpp | 29 ++-- tools/TodoTool.hpp | 6 +- tools/ToolHandler.cpp | 142 ---------------- tools/ToolHandler.hpp | 65 -------- tools/ToolsFactory.cpp | 187 --------------------- tools/ToolsFactory.hpp | 48 ------ tools/ToolsManager.cpp | 223 -------------------------- tools/ToolsManager.hpp | 90 ----------- tools/ToolsRegistration.cpp | 4 +- tools/ToolsRegistration.hpp | 4 +- widgets/QuickRefactorDialog.cpp | 2 - 91 files changed, 381 insertions(+), 2225 deletions(-) delete mode 100644 pluginllmcore/BaseTool.cpp delete mode 100644 pluginllmcore/BaseTool.hpp delete mode 100644 pluginllmcore/IToolsManager.hpp delete mode 100644 pluginllmcore/RequestConfig.hpp delete mode 100644 pluginllmcore/RequestHandlerBase.cpp delete mode 100644 pluginllmcore/RequestHandlerBase.hpp delete mode 100644 settings/CustomPromptSettings.cpp delete mode 100644 settings/CustomPromptSettings.hpp delete mode 160000 sources/external/llmcore create mode 160000 sources/external/llmqore delete mode 100644 templates/CustomFimTemplate.hpp delete mode 100644 test/LLMClientInterfaceTests.cpp delete mode 100644 test/MockRequestHandler.hpp delete mode 100644 tools/ToolHandler.cpp delete mode 100644 tools/ToolHandler.hpp delete mode 100644 tools/ToolsFactory.cpp delete mode 100644 tools/ToolsFactory.hpp delete mode 100644 tools/ToolsManager.cpp delete mode 100644 tools/ToolsManager.hpp diff --git a/.gitignore b/.gitignore index 0495742..93b52e6 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,5 @@ CMakeLists.txt.user* /.cursor /.vscode .qtc_clangd/compile_commands.json +CLAUDE.md +/.claude diff --git a/.gitmodules b/.gitmodules index 3f41606..13e517a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "sources/external/llmcore"] - path = sources/external/llmcore - url = https://github.com/Palm1r/llmcore.git +[submodule "sources/external/llmqore"] + path = sources/external/llmqore + url = https://github.com/Palm1r/llmqore.git diff --git a/CMakeLists.txt b/CMakeLists.txt index c703d62..d51512f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ add_definitions( -DQODEASSIST_QT_CREATOR_VERSION_PATCH=${QODEASSIST_QT_CREATOR_VERSION_PATCH} ) -add_subdirectory(sources/external/llmcore) +add_subdirectory(sources/external/llmqore) add_subdirectory(pluginllmcore) add_subdirectory(settings) add_subdirectory(logger) @@ -62,7 +62,7 @@ add_qtc_plugin(QodeAssist QtCreator::ExtensionSystem QtCreator::Utils QtCreator::CPlusPlus - LLMCore + LLMQore PluginLLMCore QodeAssistChatViewplugin SOURCES diff --git a/ChatView/CMakeLists.txt b/ChatView/CMakeLists.txt index 59b2687..e93aed4 100644 --- a/ChatView/CMakeLists.txt +++ b/ChatView/CMakeLists.txt @@ -85,7 +85,7 @@ target_link_libraries(QodeAssistChatView Context QodeAssistUIControlsplugin QodeAssistLogger - LLMCore + LLMQore ) target_include_directories(QodeAssistChatView diff --git a/ChatView/ChatCompressor.cpp b/ChatView/ChatCompressor.cpp index 4aa5966..5f34ce8 100644 --- a/ChatView/ChatCompressor.cpp +++ b/ChatView/ChatCompressor.cpp @@ -19,7 +19,7 @@ #include "ChatCompressor.hpp" -#include +#include #include "ChatModel.hpp" #include "GeneralSettings.hpp" #include "PromptTemplateManager.hpp" @@ -83,23 +83,16 @@ void ChatCompressor::startCompression(const QString &chatFilePath, ChatModel *ch connectProviderSignals(); - QUrl requestUrl; - QJsonObject payload; - - if (m_provider->providerID() == PluginLLMCore::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; - } + QJsonObject payload{ + {"model", Settings::generalSettings().caModel()}, {"stream", true}}; buildRequestPayload(payload, promptTemplate); - m_currentRequestId = m_provider->sendRequest(requestUrl, payload); + 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)); } @@ -271,21 +264,21 @@ void ChatCompressor::connectProviderSignals() m_connections.append(connect( c, - &::LLMCore::BaseClient::chunkReceived, + &::LLMQore::BaseClient::chunkReceived, this, &ChatCompressor::onPartialResponseReceived, Qt::UniqueConnection)); m_connections.append(connect( c, - &::LLMCore::BaseClient::requestCompleted, + &::LLMQore::BaseClient::requestCompleted, this, &ChatCompressor::onFullResponseReceived, Qt::UniqueConnection)); m_connections.append(connect( c, - &::LLMCore::BaseClient::requestFailed, + &::LLMQore::BaseClient::requestFailed, this, &ChatCompressor::onRequestFailed, Qt::UniqueConnection)); diff --git a/ChatView/ChatRootView.cpp b/ChatView/ChatRootView.cpp index ec6dcea..adbb45a 100644 --- a/ChatView/ChatRootView.cpp +++ b/ChatView/ChatRootView.cpp @@ -1400,8 +1400,6 @@ void ChatRootView::applyConfiguration(const QString &configName) settings.caModel.setValue(config.model); settings.caTemplate.setValue(config.templateName); settings.caUrl.setValue(config.url); - settings.caEndpointMode.setValue( - settings.caEndpointMode.indexForDisplay(config.endpointMode)); settings.caCustomEndpoint.setValue(config.customEndpoint); settings.writeSettings(); diff --git a/ChatView/ClientInterface.cpp b/ChatView/ClientInterface.cpp index cd6d21b..5c616c4 100644 --- a/ChatView/ClientInterface.cpp +++ b/ChatView/ClientInterface.cpp @@ -19,7 +19,7 @@ #include "ClientInterface.hpp" -#include +#include #include #include @@ -42,7 +42,7 @@ #include #include -#include +#include #include "tools/TodoTool.hpp" @@ -51,7 +51,6 @@ #include "GeneralSettings.hpp" #include "Logger.hpp" #include "ProvidersManager.hpp" -#include "RequestConfig.hpp" #include "ToolsSettings.hpp" #include #include @@ -248,26 +247,11 @@ void ClientInterface::sendMessage( context.history = messages; - PluginLLMCore::LLMConfig config; - config.requestType = PluginLLMCore::RequestType::Chat; - config.provider = provider; - config.promptTemplate = promptTemplate; - if (provider->providerID() == PluginLLMCore::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}}; - } + QJsonObject payload{ + {"model", Settings::generalSettings().caModel()}, {"stream", true}}; - config.provider->prepareRequest( - config.providerRequest, + provider->prepareRequest( + payload, promptTemplate, context, PluginLLMCore::RequestType::Chat, @@ -276,42 +260,46 @@ void ClientInterface::sendMessage( connect( provider->client(), - &::LLMCore::BaseClient::chunkReceived, + &::LLMQore::BaseClient::chunkReceived, this, &ClientInterface::handlePartialResponse, Qt::UniqueConnection); connect( provider->client(), - &::LLMCore::BaseClient::requestCompleted, + &::LLMQore::BaseClient::requestCompleted, this, &ClientInterface::handleFullResponse, Qt::UniqueConnection); connect( provider->client(), - &::LLMCore::BaseClient::requestFailed, + &::LLMQore::BaseClient::requestFailed, this, &ClientInterface::handleRequestFailed, Qt::UniqueConnection); connect( provider->client(), - &::LLMCore::BaseClient::toolStarted, + &::LLMQore::BaseClient::toolStarted, this, &ClientInterface::handleToolExecutionStarted, Qt::UniqueConnection); connect( provider->client(), - &::LLMCore::BaseClient::toolResultReady, + &::LLMQore::BaseClient::toolResultReady, this, &ClientInterface::handleToolExecutionCompleted, Qt::UniqueConnection); connect( provider->client(), - &::LLMCore::BaseClient::thinkingBlockReceived, + &::LLMQore::BaseClient::thinkingBlockReceived, this, &ClientInterface::handleThinkingBlockReceived, Qt::UniqueConnection); - auto requestId = provider->sendRequest(config.url, config.providerRequest); + 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}}; m_activeRequests[requestId] = {request, provider}; @@ -480,8 +468,10 @@ void ClientInterface::handleRequestFailed(const QString &requestId, const QStrin if (it == m_activeRequests.end()) return; - LOG_MESSAGE(QString("Chat request %1 failed: %2").arg(requestId, error)); - emit errorOccurred(error); + QString enriched = it->provider ? it->provider->enrichErrorMessage(error) : error; + + LOG_MESSAGE(QString("Chat request %1 failed: %2").arg(requestId, enriched)); + emit errorOccurred(enriched); m_activeRequests.erase(it); m_accumulatedResponses.remove(requestId); diff --git a/LLMClientInterface.cpp b/LLMClientInterface.cpp index eea6ce3..e2d9ecf 100644 --- a/LLMClientInterface.cpp +++ b/LLMClientInterface.cpp @@ -19,7 +19,7 @@ #include "LLMClientInterface.hpp" -#include +#include #include #include #include @@ -30,7 +30,6 @@ #include "logger/Logger.hpp" #include "settings/CodeCompletionSettings.hpp" #include "settings/GeneralSettings.hpp" -#include #include namespace QodeAssist { @@ -86,17 +85,19 @@ void LLMClientInterface::handleRequestFailed(const QString &requestId, const QSt if (it == m_activeRequests.end()) return; - LOG_MESSAGE(QString("Request %1 failed: %2").arg(requestId, error)); - - // Send LSP error response to client const RequestContext &ctx = it.value(); + QString enriched = ctx.provider ? ctx.provider->enrichErrorMessage(error) : error; + + LOG_MESSAGE(QString("Request %1 failed: %2").arg(requestId, enriched)); + + // Send LSP error response to client QJsonObject response; response["jsonrpc"] = "2.0"; response[LanguageServerProtocol::idKey] = ctx.originalRequest["id"]; - + QJsonObject errorObject; errorObject["code"] = -32603; // Internal error code - errorObject["message"] = error; + errorObject["message"] = enriched; response["error"] = errorObject; emit messageReceived(LanguageServerProtocol::JsonRpcMessage(response)); @@ -269,25 +270,11 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request) return; } - // TODO refactor to dynamic presets system - PluginLLMCore::LLMConfig config; - config.requestType = PluginLLMCore::RequestType::CodeCompletion; - config.provider = provider; - config.promptTemplate = promptTemplate; - // TODO refactor networking - if (provider->providerID() == PluginLLMCore::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.multiLineCompletion = m_completeSettings.multiLineCompletion(); + QJsonObject payload{{"model", modelName}, {"stream", true}}; - const auto stopWords = QJsonArray::fromStringList(config.promptTemplate->stopWords()); + const auto stopWords = QJsonArray::fromStringList(promptTemplate->stopWords()); if (!stopWords.isEmpty()) - config.providerRequest["stop"] = stopWords; + payload["stop"] = stopWords; QString systemPrompt; if (m_completeSettings.useSystemPrompt()) @@ -341,8 +328,8 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request) updatedContext.history = messages; } - config.provider->prepareRequest( - config.providerRequest, + provider->prepareRequest( + payload, promptTemplate, updatedContext, PluginLLMCore::RequestType::CodeCompletion, @@ -351,18 +338,19 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request) connect( provider->client(), - &::LLMCore::BaseClient::requestCompleted, + &::LLMQore::BaseClient::requestCompleted, this, &LLMClientInterface::handleFullResponse, Qt::UniqueConnection); connect( provider->client(), - &::LLMCore::BaseClient::requestFailed, + &::LLMQore::BaseClient::requestFailed, this, &LLMClientInterface::handleRequestFailed, Qt::UniqueConnection); - auto requestId = provider->sendRequest(config.url, config.providerRequest); + auto requestId + = provider->sendRequest(QUrl(url), payload, resolveEndpoint(promptTemplate, isPreset1Active)); m_activeRequests[requestId] = {request, provider}; m_performanceLogger.startTimeMeasurement(requestId); } @@ -381,24 +369,12 @@ PluginLLMCore::ContextData LLMClientInterface::prepareContext( return reader.prepareContext(lineNumber, cursorPosition, m_completeSettings); } -QString LLMClientInterface::endpoint( - PluginLLMCore::Provider *provider, PluginLLMCore::TemplateType type, bool isLanguageSpecify) +QString LLMClientInterface::resolveEndpoint( + PluginLLMCore::PromptTemplate *promptTemplate, bool isLanguageSpecify) const { - QString endpoint; - auto endpointMode = isLanguageSpecify ? m_generalSettings.ccPreset1EndpointMode.stringValue() - : m_generalSettings.ccEndpointMode.stringValue(); - if (endpointMode == "Auto") { - endpoint = type == PluginLLMCore::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; + const QString custom = isLanguageSpecify ? m_generalSettings.ccPreset1CustomEndpoint() + : m_generalSettings.ccCustomEndpoint(); + return !custom.isEmpty() ? custom : promptTemplate->endpoint(); } Context::ContextManager *LLMClientInterface::contextManager() const diff --git a/LLMClientInterface.hpp b/LLMClientInterface.hpp index 7ecfb05..763ed6c 100644 --- a/LLMClientInterface.hpp +++ b/LLMClientInterface.hpp @@ -87,7 +87,9 @@ private: PluginLLMCore::ContextData prepareContext( const QJsonObject &request, const Context::DocumentInfo &documentInfo); - QString endpoint(PluginLLMCore::Provider *provider, PluginLLMCore::TemplateType type, bool isLanguageSpecify); + + QString resolveEndpoint( + PluginLLMCore::PromptTemplate *promptTemplate, bool isLanguageSpecify) const; const Settings::CodeCompletionSettings &m_completeSettings; const Settings::GeneralSettings &m_generalSettings; diff --git a/QuickRefactorHandler.cpp b/QuickRefactorHandler.cpp index 84cbb44..8b682ee 100644 --- a/QuickRefactorHandler.cpp +++ b/QuickRefactorHandler.cpp @@ -19,7 +19,7 @@ #include "QuickRefactorHandler.hpp" -#include +#include #include #include #include @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -141,32 +140,15 @@ void QuickRefactorHandler::prepareAndSendRequest( return; } - PluginLLMCore::LLMConfig config; - config.requestType = PluginLLMCore::RequestType::QuickRefactoring; - config.provider = provider; - config.promptTemplate = promptTemplate; - config.url = QString("%1%2").arg(settings.qrUrl(), provider->chatEndpoint()); - - if (provider->providerID() == PluginLLMCore::ProviderID::GoogleAI) { - 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}}; - } + QJsonObject payload{ + {"model", Settings::generalSettings().qrModel()}, {"stream", true}}; PluginLLMCore::ContextData context = prepareContext(editor, range, instructions); bool enableTools = Settings::quickRefactorSettings().useTools(); bool enableThinking = Settings::quickRefactorSettings().useThinking(); provider->prepareRequest( - config.providerRequest, + payload, promptTemplate, context, PluginLLMCore::RequestType::QuickRefactoring, @@ -177,19 +159,23 @@ void QuickRefactorHandler::prepareAndSendRequest( connect( provider->client(), - &::LLMCore::BaseClient::requestCompleted, + &::LLMQore::BaseClient::requestCompleted, this, &QuickRefactorHandler::handleFullResponse, Qt::UniqueConnection); connect( provider->client(), - &::LLMCore::BaseClient::requestFailed, + &::LLMQore::BaseClient::requestFailed, this, &QuickRefactorHandler::handleRequestFailed, Qt::UniqueConnection); - auto requestId = provider->sendRequest(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}}; @@ -437,11 +423,16 @@ void QuickRefactorHandler::handleFullResponse(const QString &requestId, const QS void QuickRefactorHandler::handleRequestFailed(const QString &requestId, const QString &error) { if (requestId == m_lastRequestId) { + auto it = m_activeRequests.find(requestId); + QString enriched = (it != m_activeRequests.end() && it->provider) + ? it->provider->enrichErrorMessage(error) + : error; + m_activeRequests.remove(requestId); m_isRefactoringInProgress = false; RefactorResult result; result.success = false; - result.errorMessage = error; + result.errorMessage = enriched; result.editor = m_currentEditor; emit refactoringCompleted(result); } diff --git a/pluginllmcore/BaseTool.cpp b/pluginllmcore/BaseTool.cpp deleted file mode 100644 index ce53d08..0000000 --- a/pluginllmcore/BaseTool.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#include "BaseTool.hpp" - -namespace QodeAssist::PluginLLMCore { - -BaseTool::BaseTool(QObject *parent) - : QObject(parent) -{} - -QJsonObject BaseTool::customizeForOpenAI(const QJsonObject &baseDefinition) const -{ - QJsonObject function; - function["name"] = name(); - function["description"] = description(); - function["parameters"] = baseDefinition; - - QJsonObject tool; - tool["type"] = "function"; - tool["function"] = function; - - return tool; -} - -QJsonObject BaseTool::customizeForClaude(const QJsonObject &baseDefinition) const -{ - QJsonObject tool; - tool["name"] = name(); - tool["description"] = description(); - tool["input_schema"] = baseDefinition; - - return tool; -} - -QJsonObject BaseTool::customizeForOllama(const QJsonObject &baseDefinition) const -{ - return customizeForOpenAI(baseDefinition); -} - -QJsonObject BaseTool::customizeForGoogle(const QJsonObject &baseDefinition) const -{ - QJsonObject functionDeclaration; - functionDeclaration["name"] = name(); - functionDeclaration["description"] = description(); - functionDeclaration["parameters"] = baseDefinition; - - QJsonArray functionDeclarations; - functionDeclarations.append(functionDeclaration); - - QJsonObject tool; - tool["function_declarations"] = functionDeclarations; - - return tool; -} - -} // namespace QodeAssist::PluginLLMCore diff --git a/pluginllmcore/BaseTool.hpp b/pluginllmcore/BaseTool.hpp deleted file mode 100644 index 7421bb7..0000000 --- a/pluginllmcore/BaseTool.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace QodeAssist::PluginLLMCore { - -enum class ToolSchemaFormat { OpenAI, Claude, Ollama, Google }; - -enum ToolPermission { - None = 0, - FileSystemRead = 1 << 0, - FileSystemWrite = 1 << 1, - NetworkAccess = 1 << 2 -}; -Q_DECLARE_FLAGS(ToolPermissions, ToolPermission) -Q_DECLARE_OPERATORS_FOR_FLAGS(ToolPermissions) - -enum class RunToolsFilter { - ALL, // Run all tools (no filtering) - OnlyRead, // Run only read tools (FileSystemRead + None) - OnlyWrite, // Run only write tools (FileSystemWrite) - OnlyNetworking // Run only network tools (NetworkAccess) -}; - -class BaseTool : public QObject -{ - Q_OBJECT -public: - explicit BaseTool(QObject *parent = nullptr); - ~BaseTool() override = default; - - virtual QString name() const = 0; - virtual QString stringName() const = 0; - virtual QString description() const = 0; - virtual QJsonObject getDefinition(ToolSchemaFormat format) const = 0; - virtual ToolPermissions requiredPermissions() const = 0; - - virtual QFuture executeAsync(const QJsonObject &input = QJsonObject()) = 0; - -protected: - virtual QJsonObject customizeForOpenAI(const QJsonObject &baseDefinition) const; - virtual QJsonObject customizeForClaude(const QJsonObject &baseDefinition) const; - virtual QJsonObject customizeForOllama(const QJsonObject &baseDefinition) const; - virtual QJsonObject customizeForGoogle(const QJsonObject &baseDefinition) const; -}; - -} // namespace QodeAssist::PluginLLMCore diff --git a/pluginllmcore/CMakeLists.txt b/pluginllmcore/CMakeLists.txt index 9072785..58e0c35 100644 --- a/pluginllmcore/CMakeLists.txt +++ b/pluginllmcore/CMakeLists.txt @@ -9,12 +9,10 @@ add_library(PluginLLMCore STATIC PromptProviderFim.hpp PromptTemplate.hpp PromptTemplateManager.hpp PromptTemplateManager.cpp - RequestConfig.hpp ProviderID.hpp HttpClient.hpp HttpClient.cpp DataBuffers.hpp SSEBuffer.hpp SSEBuffer.cpp - BaseTool.hpp BaseTool.cpp ContentBlocks.hpp RulesLoader.hpp RulesLoader.cpp ResponseCleaner.hpp @@ -27,7 +25,7 @@ target_link_libraries(PluginLLMCore QtCreator::Core QtCreator::Utils QtCreator::ExtensionSystem - LLMCore + LLMQore PRIVATE QodeAssistLogger ) diff --git a/pluginllmcore/IToolsManager.hpp b/pluginllmcore/IToolsManager.hpp deleted file mode 100644 index 92c74d1..0000000 --- a/pluginllmcore/IToolsManager.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#pragma once - -#include -#include -#include -#include - -#include "BaseTool.hpp" - -namespace QodeAssist::PluginLLMCore { - -class IToolsManager -{ -public: - virtual ~IToolsManager() = default; - - virtual void executeToolCall( - const QString &requestId, - const QString &toolId, - const QString &toolName, - const QJsonObject &input) = 0; - - virtual QJsonArray getToolsDefinitions( - ToolSchemaFormat format, - RunToolsFilter filter = RunToolsFilter::ALL) const = 0; - - virtual void cleanupRequest(const QString &requestId) = 0; - virtual void setCurrentSessionId(const QString &sessionId) = 0; - virtual void clearTodoSession(const QString &sessionId) = 0; -}; - -} // namespace QodeAssist::LLMCore diff --git a/pluginllmcore/PromptTemplate.hpp b/pluginllmcore/PromptTemplate.hpp index d320041..98c3690 100644 --- a/pluginllmcore/PromptTemplate.hpp +++ b/pluginllmcore/PromptTemplate.hpp @@ -40,5 +40,13 @@ public: virtual void prepareRequest(QJsonObject &request, const ContextData &context) const = 0; virtual QString description() const = 0; virtual bool isSupportProvider(ProviderID id) const = 0; + + // Endpoint path this template expects to be sent to. Empty string + // (default) means "let the provider's client use its standard chat + // path" (/chat/completions, /api/chat, /v1/messages, ...). Templates + // producing non-chat payload shapes (e.g. {prompt, suffix} for + // Mistral FIM, {input_prefix, input_suffix} for llama.cpp infill) + // must override this to the path their payload is valid for. + virtual QString endpoint() const { return {}; } }; } // namespace QodeAssist::PluginLLMCore diff --git a/pluginllmcore/Provider.cpp b/pluginllmcore/Provider.cpp index fd2e03b..02aad8b 100644 --- a/pluginllmcore/Provider.cpp +++ b/pluginllmcore/Provider.cpp @@ -19,8 +19,14 @@ #include "Provider.hpp" -#include -#include +#include +#include +#include +#include +#include +#include + +#include #include @@ -30,19 +36,21 @@ Provider::Provider(QObject *parent) : QObject(parent) {} -RequestID Provider::sendRequest(const QUrl &url, const QJsonObject &payload) +RequestID Provider::sendRequest( + const QUrl &url, const QJsonObject &payload, const QString &endpoint) { auto *c = client(); - QUrl baseUrl(url); - baseUrl.setPath(""); - c->setUrl(baseUrl.toString()); + c->setUrl(url.toString()); c->setApiKey(apiKey()); - auto requestId = c->sendMessage(payload); + auto requestId = c->sendMessage(payload, endpoint); LOG_MESSAGE( - QString("%1: Sending request %2 to %3").arg(name(), requestId, url.toString())); + QString("%1: Sending request %2 to %3%4").arg(name(), requestId, url.toString(), endpoint)); + LOG_MESSAGE( + QString("%1: Payload:\n%2") + .arg(name(), QString::fromUtf8(QJsonDocument(payload).toJson(QJsonDocument::Indented)))); return requestId; } @@ -53,9 +61,35 @@ void Provider::cancelRequest(const RequestID &requestId) client()->cancelRequest(requestId); } -::LLMCore::ToolsManager *Provider::toolsManager() const +::LLMQore::ToolsManager *Provider::toolsManager() const { return client()->tools(); } +QString Provider::enrichErrorMessage(const QString &error) const +{ + auto *c = client(); + + if (qobject_cast<::LLMQore::MistralClient *>(c)) + return error; + + const bool isOpenAICompatible = qobject_cast<::LLMQore::OpenAIClient *>(c) + || qobject_cast<::LLMQore::OpenAIResponsesClient *>(c) + || qobject_cast<::LLMQore::LlamaCppClient *>(c); + if (!isOpenAICompatible) + return error; + + const QString baseUrl = c->url(); + const QString path = QUrl(baseUrl).path(); + const bool hasV1Segment = path.contains("/v1/") || path.endsWith("/v1"); + if (hasV1Segment) + return error; + + return error + + tr("\n\nHint: Your base URL (%1) does not contain a '/v1' path segment. " + "Most OpenAI-compatible servers require it (e.g., %1/v1). " + "Try updating the URL in settings.") + .arg(baseUrl); +} + } // namespace QodeAssist::PluginLLMCore diff --git a/pluginllmcore/Provider.hpp b/pluginllmcore/Provider.hpp index 1c36b13..21b7e1f 100644 --- a/pluginllmcore/Provider.hpp +++ b/pluginllmcore/Provider.hpp @@ -26,11 +26,10 @@ #include #include "ContextData.hpp" -#include "IToolsManager.hpp" #include "PromptTemplate.hpp" #include "RequestType.hpp" -namespace LLMCore { +namespace LLMQore { class BaseClient; class ToolsManager; } @@ -58,8 +57,6 @@ public: virtual QString name() const = 0; virtual QString url() const = 0; - virtual QString completionEndpoint() const = 0; - virtual QString chatEndpoint() const = 0; virtual void prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -72,12 +69,15 @@ public: virtual ProviderID providerID() const = 0; virtual ProviderCapabilities capabilities() const { return {}; } - virtual ::LLMCore::BaseClient *client() const = 0; + virtual ::LLMQore::BaseClient *client() const = 0; virtual QString apiKey() const = 0; - RequestID sendRequest(const QUrl &url, const QJsonObject &payload); + virtual RequestID sendRequest( + const QUrl &url, const QJsonObject &payload, const QString &endpoint); void cancelRequest(const RequestID &requestId); - ::LLMCore::ToolsManager *toolsManager() const; + ::LLMQore::ToolsManager *toolsManager() const; + + QString enrichErrorMessage(const QString &error) const; }; } // namespace QodeAssist::PluginLLMCore diff --git a/pluginllmcore/RequestConfig.hpp b/pluginllmcore/RequestConfig.hpp deleted file mode 100644 index 113536a..0000000 --- a/pluginllmcore/RequestConfig.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2024-2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#pragma once - -#include "PromptTemplate.hpp" -#include "Provider.hpp" -#include "RequestType.hpp" -#include -#include - -namespace QodeAssist::PluginLLMCore { - -struct LLMConfig -{ - QUrl url; - Provider *provider; - PromptTemplate *promptTemplate; - QJsonObject providerRequest; - RequestType requestType; - bool multiLineCompletion; -}; - -} // namespace QodeAssist::PluginLLMCore diff --git a/pluginllmcore/RequestHandlerBase.cpp b/pluginllmcore/RequestHandlerBase.cpp deleted file mode 100644 index c13bb8f..0000000 --- a/pluginllmcore/RequestHandlerBase.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024-2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#include "RequestHandlerBase.hpp" - -namespace QodeAssist::LLMCore { - -RequestHandlerBase::RequestHandlerBase(QObject *parent) - : QObject(parent) -{} - -RequestHandlerBase::~RequestHandlerBase() = default; - -} // namespace QodeAssist::LLMCore diff --git a/pluginllmcore/RequestHandlerBase.hpp b/pluginllmcore/RequestHandlerBase.hpp deleted file mode 100644 index 767cdae..0000000 --- a/pluginllmcore/RequestHandlerBase.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024-2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#pragma once - -#include "RequestConfig.hpp" -#include -#include - -namespace QodeAssist::LLMCore { - -class RequestHandlerBase : public QObject -{ - Q_OBJECT - -public: - explicit RequestHandlerBase(QObject *parent = nullptr); - ~RequestHandlerBase() override; - - virtual void sendLLMRequest(const LLMConfig &config, const QJsonObject &request) = 0; - virtual bool cancelRequest(const QString &id) = 0; - -signals: - void completionReceived(const QString &completion, const QJsonObject &request, bool isComplete); - void requestFinished(const QString &requestId, bool success, const QString &errorString); - void requestCancelled(const QString &id); -}; - -} // namespace QodeAssist::LLMCore diff --git a/providers/ClaudeProvider.cpp b/providers/ClaudeProvider.cpp index 8da5cd4..003a644 100644 --- a/providers/ClaudeProvider.cpp +++ b/providers/ClaudeProvider.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include "logger/Logger.hpp" #include "settings/ChatAssistantSettings.hpp" @@ -37,7 +37,7 @@ namespace QodeAssist::Providers { ClaudeProvider::ClaudeProvider(QObject *parent) : PluginLLMCore::Provider(parent) - , m_client(new ::LLMCore::ClaudeClient(QString(), QString(), QString(), this)) + , m_client(new ::LLMQore::ClaudeClient(QString(), QString(), QString(), this)) { Tools::registerQodeAssistTools(m_client->tools()); } @@ -57,16 +57,6 @@ QString ClaudeProvider::url() const return "https://api.anthropic.com"; } -QString ClaudeProvider::completionEndpoint() const -{ - return "/v1/messages"; -} - -QString ClaudeProvider::chatEndpoint() const -{ - return "/v1/messages"; -} - void ClaudeProvider::prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -151,7 +141,7 @@ PluginLLMCore::ProviderCapabilities ClaudeProvider::capabilities() const | PluginLLMCore::ProviderCapability::ModelListing; } -::LLMCore::BaseClient *ClaudeProvider::client() const +::LLMQore::BaseClient *ClaudeProvider::client() const { return m_client; } diff --git a/providers/ClaudeProvider.hpp b/providers/ClaudeProvider.hpp index 143bab2..2fc0677 100644 --- a/providers/ClaudeProvider.hpp +++ b/providers/ClaudeProvider.hpp @@ -21,7 +21,7 @@ #include -#include +#include namespace QodeAssist::Providers { @@ -33,8 +33,6 @@ public: QString name() const override; QString url() const override; - QString completionEndpoint() const override; - QString chatEndpoint() const override; void prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -46,11 +44,11 @@ public: PluginLLMCore::ProviderID providerID() const override; PluginLLMCore::ProviderCapabilities capabilities() const override; - ::LLMCore::BaseClient *client() const override; + ::LLMQore::BaseClient *client() const override; QString apiKey() const override; private: - ::LLMCore::ClaudeClient *m_client; + ::LLMQore::ClaudeClient *m_client; }; } // namespace QodeAssist::Providers diff --git a/providers/GoogleAIProvider.cpp b/providers/GoogleAIProvider.cpp index f37855c..f8d8290 100644 --- a/providers/GoogleAIProvider.cpp +++ b/providers/GoogleAIProvider.cpp @@ -19,7 +19,7 @@ #include "GoogleAIProvider.hpp" -#include +#include #include #include "tools/ToolsRegistration.hpp" @@ -38,7 +38,7 @@ namespace QodeAssist::Providers { GoogleAIProvider::GoogleAIProvider(QObject *parent) : PluginLLMCore::Provider(parent) - , m_client(new ::LLMCore::GoogleAIClient(QString(), QString(), QString(), this)) + , m_client(new ::LLMQore::GoogleAIClient(QString(), QString(), QString(), this)) { Tools::registerQodeAssistTools(m_client->tools()); } @@ -58,16 +58,6 @@ QString GoogleAIProvider::url() const return "https://generativelanguage.googleapis.com/v1beta"; } -QString GoogleAIProvider::completionEndpoint() const -{ - return {}; -} - -QString GoogleAIProvider::chatEndpoint() const -{ - return {}; -} - void GoogleAIProvider::prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -165,7 +155,25 @@ PluginLLMCore::ProviderCapabilities GoogleAIProvider::capabilities() const | PluginLLMCore::ProviderCapability::ModelListing; } -::LLMCore::BaseClient *GoogleAIProvider::client() const +PluginLLMCore::RequestID GoogleAIProvider::sendRequest( + const QUrl &url, const QJsonObject &payload, const QString &endpoint) +{ + // Gemini takes the model from the URL path and streaming from the + // action suffix (:streamGenerateContent vs :generateContent), and + // rejects unknown top-level body fields. The shared call-site seeds + // payload with {model, stream}; consume them here into client state + // before they hit the wire. + QJsonObject cleaned = payload; + if (cleaned.contains("model")) { + m_client->setModel(cleaned["model"].toString()); + cleaned.remove("model"); + } + cleaned.remove("stream"); + + return PluginLLMCore::Provider::sendRequest(url, cleaned, endpoint); +} + +::LLMQore::BaseClient *GoogleAIProvider::client() const { return m_client; } diff --git a/providers/GoogleAIProvider.hpp b/providers/GoogleAIProvider.hpp index 67a5b40..8f6bbc5 100644 --- a/providers/GoogleAIProvider.hpp +++ b/providers/GoogleAIProvider.hpp @@ -21,7 +21,7 @@ #include -#include +#include namespace QodeAssist::Providers { @@ -33,8 +33,6 @@ public: QString name() const override; QString url() const override; - QString completionEndpoint() const override; - QString chatEndpoint() const override; void prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -46,11 +44,14 @@ public: PluginLLMCore::ProviderID providerID() const override; PluginLLMCore::ProviderCapabilities capabilities() const override; - ::LLMCore::BaseClient *client() const override; + PluginLLMCore::RequestID sendRequest( + const QUrl &url, const QJsonObject &payload, const QString &endpoint) override; + + ::LLMQore::BaseClient *client() const override; QString apiKey() const override; private: - ::LLMCore::GoogleAIClient *m_client; + ::LLMQore::GoogleAIClient *m_client; }; } // namespace QodeAssist::Providers diff --git a/providers/LMStudioProvider.cpp b/providers/LMStudioProvider.cpp index eb42b2c..53cb0cf 100644 --- a/providers/LMStudioProvider.cpp +++ b/providers/LMStudioProvider.cpp @@ -19,7 +19,7 @@ #include "LMStudioProvider.hpp" -#include +#include #include "tools/ToolsRegistration.hpp" #include "logger/Logger.hpp" @@ -37,7 +37,7 @@ namespace QodeAssist::Providers { LMStudioProvider::LMStudioProvider(QObject *parent) : PluginLLMCore::Provider(parent) - , m_client(new ::LLMCore::OpenAIClient(QString(), QString(), QString(), this)) + , m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this)) { Tools::registerQodeAssistTools(m_client->tools()); } @@ -54,17 +54,7 @@ QString LMStudioProvider::apiKey() const QString LMStudioProvider::url() const { - return "http://localhost:1234"; -} - -QString LMStudioProvider::completionEndpoint() const -{ - return "/v1/completions"; -} - -QString LMStudioProvider::chatEndpoint() const -{ - return "/v1/chat/completions"; + return "http://localhost:1234/v1"; } QFuture> LMStudioProvider::getInstalledModels(const QString &url) @@ -130,7 +120,7 @@ void LMStudioProvider::prepareRequest( } } -::LLMCore::BaseClient *LMStudioProvider::client() const +::LLMQore::BaseClient *LMStudioProvider::client() const { return m_client; } diff --git a/providers/LMStudioProvider.hpp b/providers/LMStudioProvider.hpp index b3ec744..7e81674 100644 --- a/providers/LMStudioProvider.hpp +++ b/providers/LMStudioProvider.hpp @@ -19,7 +19,7 @@ #pragma once -#include +#include #include namespace QodeAssist::Providers { @@ -32,8 +32,6 @@ public: QString name() const override; QString url() const override; - QString completionEndpoint() const override; - QString chatEndpoint() const override; void prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -45,11 +43,11 @@ public: PluginLLMCore::ProviderID providerID() const override; PluginLLMCore::ProviderCapabilities capabilities() const override; - ::LLMCore::BaseClient *client() const override; + ::LLMQore::BaseClient *client() const override; QString apiKey() const override; private: - ::LLMCore::OpenAIClient *m_client; + ::LLMQore::OpenAIClient *m_client; }; } // namespace QodeAssist::Providers diff --git a/providers/LlamaCppProvider.cpp b/providers/LlamaCppProvider.cpp index 35065f5..41f140d 100644 --- a/providers/LlamaCppProvider.cpp +++ b/providers/LlamaCppProvider.cpp @@ -19,7 +19,7 @@ #include "LlamaCppProvider.hpp" -#include +#include #include "logger/Logger.hpp" #include "settings/ChatAssistantSettings.hpp" #include "settings/CodeCompletionSettings.hpp" @@ -35,7 +35,7 @@ namespace QodeAssist::Providers { LlamaCppProvider::LlamaCppProvider(QObject *parent) : PluginLLMCore::Provider(parent) - , m_client(new ::LLMCore::LlamaCppClient(QString(), QString(), QString(), this)) + , m_client(new ::LLMQore::LlamaCppClient(QString(), QString(), QString(), this)) { Tools::registerQodeAssistTools(m_client->tools()); } @@ -55,16 +55,6 @@ QString LlamaCppProvider::url() const return "http://localhost:8080"; } -QString LlamaCppProvider::completionEndpoint() const -{ - return "/infill"; -} - -QString LlamaCppProvider::chatEndpoint() const -{ - return "/v1/chat/completions"; -} - void LlamaCppProvider::prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -125,7 +115,7 @@ PluginLLMCore::ProviderCapabilities LlamaCppProvider::capabilities() const return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image; } -::LLMCore::BaseClient *LlamaCppProvider::client() const +::LLMQore::BaseClient *LlamaCppProvider::client() const { return m_client; } diff --git a/providers/LlamaCppProvider.hpp b/providers/LlamaCppProvider.hpp index ccc8137..2001c49 100644 --- a/providers/LlamaCppProvider.hpp +++ b/providers/LlamaCppProvider.hpp @@ -21,7 +21,7 @@ #include -#include +#include namespace QodeAssist::Providers { @@ -33,8 +33,6 @@ public: QString name() const override; QString url() const override; - QString completionEndpoint() const override; - QString chatEndpoint() const override; void prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -46,11 +44,11 @@ public: PluginLLMCore::ProviderID providerID() const override; PluginLLMCore::ProviderCapabilities capabilities() const override; - ::LLMCore::BaseClient *client() const override; + ::LLMQore::BaseClient *client() const override; QString apiKey() const override; private: - ::LLMCore::LlamaCppClient *m_client; + ::LLMQore::LlamaCppClient *m_client; }; } // namespace QodeAssist::Providers diff --git a/providers/MistralAIProvider.cpp b/providers/MistralAIProvider.cpp index 8249446..2e85a68 100644 --- a/providers/MistralAIProvider.cpp +++ b/providers/MistralAIProvider.cpp @@ -19,7 +19,7 @@ #include "MistralAIProvider.hpp" -#include +#include #include "logger/Logger.hpp" #include "settings/ChatAssistantSettings.hpp" #include "settings/CodeCompletionSettings.hpp" @@ -36,7 +36,7 @@ namespace QodeAssist::Providers { MistralAIProvider::MistralAIProvider(QObject *parent) : PluginLLMCore::Provider(parent) - , m_client(new ::LLMCore::OpenAIClient(QString(), QString(), QString(), this)) + , m_client(new ::LLMQore::MistralClient(QString(), QString(), QString(), this)) { Tools::registerQodeAssistTools(m_client->tools()); } @@ -56,16 +56,6 @@ QString MistralAIProvider::url() const return "https://api.mistral.ai"; } -QString MistralAIProvider::completionEndpoint() const -{ - return "/v1/fim/completions"; -} - -QString MistralAIProvider::chatEndpoint() const -{ - return "/v1/chat/completions"; -} - QFuture> MistralAIProvider::getInstalledModels(const QString &url) { m_client->setUrl(url); @@ -129,7 +119,7 @@ void MistralAIProvider::prepareRequest( } } -::LLMCore::BaseClient *MistralAIProvider::client() const +::LLMQore::BaseClient *MistralAIProvider::client() const { return m_client; } diff --git a/providers/MistralAIProvider.hpp b/providers/MistralAIProvider.hpp index 57d2137..97405e7 100644 --- a/providers/MistralAIProvider.hpp +++ b/providers/MistralAIProvider.hpp @@ -19,7 +19,7 @@ #pragma once -#include +#include #include namespace QodeAssist::Providers { @@ -32,8 +32,6 @@ public: QString name() const override; QString url() const override; - QString completionEndpoint() const override; - QString chatEndpoint() const override; void prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -45,11 +43,11 @@ public: PluginLLMCore::ProviderID providerID() const override; PluginLLMCore::ProviderCapabilities capabilities() const override; - ::LLMCore::BaseClient *client() const override; + ::LLMQore::BaseClient *client() const override; QString apiKey() const override; private: - ::LLMCore::OpenAIClient *m_client; + ::LLMQore::MistralClient *m_client; }; } // namespace QodeAssist::Providers diff --git a/providers/OllamaProvider.cpp b/providers/OllamaProvider.cpp index 4801eaa..57006e1 100644 --- a/providers/OllamaProvider.cpp +++ b/providers/OllamaProvider.cpp @@ -19,7 +19,7 @@ #include "OllamaProvider.hpp" -#include +#include #include #include @@ -37,7 +37,7 @@ namespace QodeAssist::Providers { OllamaProvider::OllamaProvider(QObject *parent) : PluginLLMCore::Provider(parent) - , m_client(new ::LLMCore::OllamaClient(QString(), QString(), QString(), this)) + , m_client(new ::LLMQore::OllamaClient(QString(), QString(), QString(), this)) { Tools::registerQodeAssistTools(m_client->tools()); } @@ -57,16 +57,6 @@ QString OllamaProvider::url() const return "http://localhost:11434"; } -QString OllamaProvider::completionEndpoint() const -{ - return "/api/generate"; -} - -QString OllamaProvider::chatEndpoint() const -{ - return "/api/chat"; -} - void OllamaProvider::prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -156,7 +146,7 @@ PluginLLMCore::ProviderCapabilities OllamaProvider::capabilities() const | PluginLLMCore::ProviderCapability::ModelListing; } -::LLMCore::BaseClient *OllamaProvider::client() const +::LLMQore::BaseClient *OllamaProvider::client() const { return m_client; } diff --git a/providers/OllamaProvider.hpp b/providers/OllamaProvider.hpp index 04c47d4..d922f43 100644 --- a/providers/OllamaProvider.hpp +++ b/providers/OllamaProvider.hpp @@ -21,7 +21,7 @@ #include -#include +#include namespace QodeAssist::Providers { @@ -33,8 +33,6 @@ public: QString name() const override; QString url() const override; - QString completionEndpoint() const override; - QString chatEndpoint() const override; void prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -46,11 +44,11 @@ public: PluginLLMCore::ProviderID providerID() const override; PluginLLMCore::ProviderCapabilities capabilities() const override; - ::LLMCore::BaseClient *client() const override; + ::LLMQore::BaseClient *client() const override; QString apiKey() const override; private: - ::LLMCore::OllamaClient *m_client; + ::LLMQore::OllamaClient *m_client; }; } // namespace QodeAssist::Providers diff --git a/providers/OpenAICompatProvider.cpp b/providers/OpenAICompatProvider.cpp index 309a172..47c3afc 100644 --- a/providers/OpenAICompatProvider.cpp +++ b/providers/OpenAICompatProvider.cpp @@ -18,7 +18,7 @@ */ #include "OpenAICompatProvider.hpp" -#include +#include #include "tools/ToolsRegistration.hpp" #include "logger/Logger.hpp" @@ -36,7 +36,7 @@ namespace QodeAssist::Providers { OpenAICompatProvider::OpenAICompatProvider(QObject *parent) : PluginLLMCore::Provider(parent) - , m_client(new ::LLMCore::OpenAIClient(QString(), QString(), QString(), this)) + , m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this)) { Tools::registerQodeAssistTools(m_client->tools()); } @@ -53,17 +53,7 @@ QString OpenAICompatProvider::apiKey() const QString OpenAICompatProvider::url() const { - return "http://localhost:1234"; -} - -QString OpenAICompatProvider::completionEndpoint() const -{ - return "/v1/chat/completions"; -} - -QString OpenAICompatProvider::chatEndpoint() const -{ - return "/v1/chat/completions"; + return "http://localhost:1234/v1"; } void OpenAICompatProvider::prepareRequest( @@ -127,7 +117,7 @@ PluginLLMCore::ProviderCapabilities OpenAICompatProvider::capabilities() const return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image; } -::LLMCore::BaseClient *OpenAICompatProvider::client() const +::LLMQore::BaseClient *OpenAICompatProvider::client() const { return m_client; } diff --git a/providers/OpenAICompatProvider.hpp b/providers/OpenAICompatProvider.hpp index 4a7766f..83ca6a0 100644 --- a/providers/OpenAICompatProvider.hpp +++ b/providers/OpenAICompatProvider.hpp @@ -19,7 +19,7 @@ #pragma once -#include +#include #include namespace QodeAssist::Providers { @@ -32,8 +32,6 @@ public: QString name() const override; QString url() const override; - QString completionEndpoint() const override; - QString chatEndpoint() const override; void prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -45,11 +43,11 @@ public: PluginLLMCore::ProviderID providerID() const override; PluginLLMCore::ProviderCapabilities capabilities() const override; - ::LLMCore::BaseClient *client() const override; + ::LLMQore::BaseClient *client() const override; QString apiKey() const override; private: - ::LLMCore::OpenAIClient *m_client; + ::LLMQore::OpenAIClient *m_client; }; } // namespace QodeAssist::Providers diff --git a/providers/OpenAIProvider.cpp b/providers/OpenAIProvider.cpp index 6d20ab7..2ef7b59 100644 --- a/providers/OpenAIProvider.cpp +++ b/providers/OpenAIProvider.cpp @@ -19,7 +19,7 @@ #include "OpenAIProvider.hpp" -#include +#include #include "tools/ToolsRegistration.hpp" #include "logger/Logger.hpp" #include "settings/ChatAssistantSettings.hpp" @@ -36,7 +36,7 @@ namespace QodeAssist::Providers { OpenAIProvider::OpenAIProvider(QObject *parent) : PluginLLMCore::Provider(parent) - , m_client(new ::LLMCore::OpenAIClient(QString(), QString(), QString(), this)) + , m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this)) { Tools::registerQodeAssistTools(m_client->tools()); } @@ -53,17 +53,7 @@ QString OpenAIProvider::apiKey() const QString OpenAIProvider::url() const { - return "https://api.openai.com"; -} - -QString OpenAIProvider::completionEndpoint() const -{ - return "/v1/chat/completions"; -} - -QString OpenAIProvider::chatEndpoint() const -{ - return "/v1/chat/completions"; + return "https://api.openai.com/v1"; } void OpenAIProvider::prepareRequest( @@ -158,7 +148,7 @@ PluginLLMCore::ProviderCapabilities OpenAIProvider::capabilities() const | PluginLLMCore::ProviderCapability::ModelListing; } -::LLMCore::BaseClient *OpenAIProvider::client() const +::LLMQore::BaseClient *OpenAIProvider::client() const { return m_client; } diff --git a/providers/OpenAIProvider.hpp b/providers/OpenAIProvider.hpp index bf24f0f..b54ce5c 100644 --- a/providers/OpenAIProvider.hpp +++ b/providers/OpenAIProvider.hpp @@ -19,7 +19,7 @@ #pragma once -#include +#include #include namespace QodeAssist::Providers { @@ -32,8 +32,6 @@ public: QString name() const override; QString url() const override; - QString completionEndpoint() const override; - QString chatEndpoint() const override; void prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -45,11 +43,11 @@ public: PluginLLMCore::ProviderID providerID() const override; PluginLLMCore::ProviderCapabilities capabilities() const override; - ::LLMCore::BaseClient *client() const override; + ::LLMQore::BaseClient *client() const override; QString apiKey() const override; private: - ::LLMCore::OpenAIClient *m_client; + ::LLMQore::OpenAIClient *m_client; }; } // namespace QodeAssist::Providers diff --git a/providers/OpenAIResponsesProvider.cpp b/providers/OpenAIResponsesProvider.cpp index d08920e..8c800bb 100644 --- a/providers/OpenAIResponsesProvider.cpp +++ b/providers/OpenAIResponsesProvider.cpp @@ -18,7 +18,7 @@ */ #include "OpenAIResponsesProvider.hpp" -#include +#include #include "tools/ToolsRegistration.hpp" #include "logger/Logger.hpp" @@ -36,7 +36,7 @@ namespace QodeAssist::Providers { OpenAIResponsesProvider::OpenAIResponsesProvider(QObject *parent) : PluginLLMCore::Provider(parent) - , m_client(new ::LLMCore::OpenAIResponsesClient(QString(), QString(), QString(), this)) + , m_client(new ::LLMQore::OpenAIResponsesClient(QString(), QString(), QString(), this)) { Tools::registerQodeAssistTools(m_client->tools()); } @@ -53,17 +53,7 @@ QString OpenAIResponsesProvider::apiKey() const QString OpenAIResponsesProvider::url() const { - return "https://api.openai.com"; -} - -QString OpenAIResponsesProvider::completionEndpoint() const -{ - return "/v1/responses"; -} - -QString OpenAIResponsesProvider::chatEndpoint() const -{ - return "/v1/responses"; + return "https://api.openai.com/v1"; } void OpenAIResponsesProvider::prepareRequest( @@ -179,7 +169,7 @@ PluginLLMCore::ProviderCapabilities OpenAIResponsesProvider::capabilities() cons | PluginLLMCore::ProviderCapability::ModelListing; } -::LLMCore::BaseClient *OpenAIResponsesProvider::client() const +::LLMQore::BaseClient *OpenAIResponsesProvider::client() const { return m_client; } diff --git a/providers/OpenAIResponsesProvider.hpp b/providers/OpenAIResponsesProvider.hpp index fd6a324..587570f 100644 --- a/providers/OpenAIResponsesProvider.hpp +++ b/providers/OpenAIResponsesProvider.hpp @@ -19,7 +19,7 @@ #pragma once -#include +#include #include namespace QodeAssist::Providers { @@ -32,8 +32,6 @@ public: QString name() const override; QString url() const override; - QString completionEndpoint() const override; - QString chatEndpoint() const override; void prepareRequest( QJsonObject &request, PluginLLMCore::PromptTemplate *prompt, @@ -45,11 +43,11 @@ public: PluginLLMCore::ProviderID providerID() const override; PluginLLMCore::ProviderCapabilities capabilities() const override; - ::LLMCore::BaseClient *client() const override; + ::LLMQore::BaseClient *client() const override; QString apiKey() const override; private: - ::LLMCore::OpenAIResponsesClient *m_client; + ::LLMQore::OpenAIResponsesClient *m_client; }; } // namespace QodeAssist::Providers diff --git a/settings/CMakeLists.txt b/settings/CMakeLists.txt index fce22de..19173e7 100644 --- a/settings/CMakeLists.txt +++ b/settings/CMakeLists.txt @@ -1,6 +1,5 @@ add_library(QodeAssistSettings STATIC GeneralSettings.hpp GeneralSettings.cpp - CustomPromptSettings.hpp CustomPromptSettings.cpp ConfigurationManager.hpp ConfigurationManager.cpp SettingsUtils.hpp SettingsConstants.hpp diff --git a/settings/ConfigurationManager.cpp b/settings/ConfigurationManager.cpp index c6a6dd4..75f7081 100644 --- a/settings/ConfigurationManager.cpp +++ b/settings/ConfigurationManager.cpp @@ -54,7 +54,6 @@ QVector ConfigurationManager::getPredefinedConfigurations( claudeOpus.provider = "Claude"; claudeOpus.model = "claude-opus-4-6"; claudeOpus.url = "https://api.anthropic.com"; - claudeOpus.endpointMode = "Auto"; claudeOpus.customEndpoint = ""; claudeOpus.templateName = "Claude"; claudeOpus.type = type; @@ -66,7 +65,6 @@ QVector ConfigurationManager::getPredefinedConfigurations( claudeSonnet.provider = "Claude"; claudeSonnet.model = "claude-sonnet-4-6"; claudeSonnet.url = "https://api.anthropic.com"; - claudeSonnet.endpointMode = "Auto"; claudeSonnet.customEndpoint = ""; claudeSonnet.templateName = "Claude"; claudeSonnet.type = type; @@ -78,7 +76,6 @@ QVector ConfigurationManager::getPredefinedConfigurations( claudeHaiku.provider = "Claude"; claudeHaiku.model = "claude-haiku-4-5-20251001"; claudeHaiku.url = "https://api.anthropic.com"; - claudeHaiku.endpointMode = "Auto"; claudeHaiku.customEndpoint = ""; claudeHaiku.templateName = "Claude"; claudeHaiku.type = type; @@ -90,7 +87,6 @@ QVector ConfigurationManager::getPredefinedConfigurations( codestral.provider = "Codestral"; codestral.model = "codestral-latest"; codestral.url = "https://codestral.mistral.ai"; - codestral.endpointMode = "Auto"; codestral.customEndpoint = ""; codestral.templateName = type == ConfigurationType::CodeCompletion ? "Mistral AI FIM" : "Mistral AI Chat"; codestral.type = type; @@ -100,9 +96,8 @@ QVector ConfigurationManager::getPredefinedConfigurations( mistral.id = "preset_mistral"; mistral.name = "Mistral"; mistral.provider = "Mistral AI"; - mistral.model = type == ConfigurationType::CodeCompletion ? "mistral-medium-latest" : "mistral-large-latest"; + mistral.model = type == ConfigurationType::CodeCompletion ? "codestral-latest" : "mistral-large-latest"; mistral.url = "https://api.mistral.ai"; - mistral.endpointMode = "Auto"; mistral.customEndpoint = ""; mistral.templateName = type == ConfigurationType::CodeCompletion ? "Mistral AI FIM" : "Mistral AI Chat"; mistral.type = type; @@ -114,7 +109,6 @@ QVector ConfigurationManager::getPredefinedConfigurations( geminiFlash.provider = "Google AI"; geminiFlash.model = "gemini-2.5-flash"; geminiFlash.url = "https://generativelanguage.googleapis.com/v1beta"; - geminiFlash.endpointMode = "Auto"; geminiFlash.customEndpoint = ""; geminiFlash.templateName = "Google AI"; geminiFlash.type = type; @@ -125,8 +119,7 @@ QVector ConfigurationManager::getPredefinedConfigurations( gpt.name = "gpt-5.4"; gpt.provider = "OpenAI Responses"; gpt.model = "gpt-5.4"; - gpt.url = "https://api.openai.com"; - gpt.endpointMode = "Auto"; + gpt.url = "https://api.openai.com/v1"; gpt.customEndpoint = ""; gpt.templateName = "OpenAI Responses"; gpt.type = type; @@ -230,10 +223,10 @@ bool ConfigurationManager::loadConfigurations(ConfigurationType type) config.model = obj["model"].toString(); config.templateName = obj["template"].toString(); config.url = obj["url"].toString(); - config.endpointMode = obj["endpointMode"].toString(); config.customEndpoint = obj["customEndpoint"].toString(); config.type = type; config.formatVersion = obj.value("formatVersion").toInt(1); + config.isPredefined = false; if (config.id.isEmpty() || config.name.isEmpty()) { @@ -263,7 +256,6 @@ bool ConfigurationManager::saveConfiguration(const AIConfiguration &config) obj["model"] = config.model; obj["template"] = config.templateName; obj["url"] = config.url; - obj["endpointMode"] = config.endpointMode; obj["customEndpoint"] = config.customEndpoint; QString sanitizedName = config.name; diff --git a/settings/ConfigurationManager.hpp b/settings/ConfigurationManager.hpp index 8fbc163..709a0c1 100644 --- a/settings/ConfigurationManager.hpp +++ b/settings/ConfigurationManager.hpp @@ -37,7 +37,7 @@ struct AIConfiguration QString model; QString templateName; QString url; - QString endpointMode; + // Empty = use the template's endpoint; non-empty = override path. QString customEndpoint; ConfigurationType type; int formatVersion = CONFIGURATION_FORMAT_VERSION; diff --git a/settings/CustomPromptSettings.cpp b/settings/CustomPromptSettings.cpp deleted file mode 100644 index 533cf47..0000000 --- a/settings/CustomPromptSettings.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2024-2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#include "CustomPromptSettings.hpp" - -#include -#include -#include -#include -#include -#include - -#include "SettingsConstants.hpp" -#include "SettingsTr.hpp" -#include "SettingsUtils.hpp" - -namespace QodeAssist::Settings { - -CustomPromptSettings &customPromptSettings() -{ - static CustomPromptSettings settings; - return settings; -} - -CustomPromptSettings::CustomPromptSettings() -{ - setAutoApply(false); - - setDisplayName(Tr::tr("Custom Prompt")); - - customJsonLabel.setLabelText("Custom JSON Template:"); - customJsonLabel.setDisplayStyle(Utils::StringAspect::LabelDisplay); - - customJsonLegend.setLabelText(Tr::tr(R"(Prompt components: -- model is set on General Page -- {{QODE_INSTRUCTIONS}}: Placeholder for specific instructions or context. -- {{QODE_PREFIX}}: Will be replaced with the actual code before the cursor. -- {{QODE_SUFFIX}}: Will be replaced with the actual code after the cursor. -)")); - - customJsonTemplate.setSettingsKey(Constants::CUSTOM_JSON_TEMPLATE); - customJsonTemplate.setDisplayStyle(Utils::StringAspect::TextEditDisplay); - - customJsonTemplate.setDefaultValue(R"({ - "prompt": "{{QODE_INSTRUCTIONS}}{{QODE_PREFIX}}{{QODE_SUFFIX}}", - "options": { - "temperature": 0.7, - "top_p": 0.95, - "top_k": 40, - "num_predict": 100, - "stop": [ - "<|endoftext|>", - "", - "", - "", - "" - ], - "frequency_penalty": 0, - "presence_penalty": 0 - }, - "stream": true -})"); - saveCustomTemplateButton.m_buttonText = (Tr::tr("Save Custom Template to JSON")); - loadCustomTemplateButton.m_buttonText = (Tr::tr("Load Custom Template from JSON")); - resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults"); - - readSettings(); - - setupConnection(); - - setLayouter([this]() { - using namespace Layouting; - return Column{Group{ - title(Tr::tr("Custom prompt for FIM model")), - Column{ - Row{customJsonLabel, Stretch{1}, resetToDefaults}, - Row{customJsonTemplate, - Column{ - saveCustomTemplateButton, - loadCustomTemplateButton, - customJsonLegend, - Stretch{1}}}}}}; - }); -} - -void CustomPromptSettings::setupConnection() -{ - connect( - &resetToDefaults, - &ButtonAspect::clicked, - this, - &CustomPromptSettings::resetSettingsToDefaults); - connect( - &saveCustomTemplateButton, - &ButtonAspect::clicked, - this, - &CustomPromptSettings::saveCustomTemplate); - connect( - &loadCustomTemplateButton, - &ButtonAspect::clicked, - this, - &CustomPromptSettings::loadCustomTemplate); -} - -void CustomPromptSettings::resetSettingsToDefaults() -{ - QMessageBox::StandardButton reply; - reply = QMessageBox::question( - Core::ICore::dialogParent(), - Tr::tr("Reset Settings"), - Tr::tr("Are you sure you want to reset all settings to default values?"), - QMessageBox::Yes | QMessageBox::No); - - if (reply == QMessageBox::Yes) { - resetAspect(customJsonTemplate); - } -} - -void CustomPromptSettings::saveCustomTemplate() -{ - QString fileName = QFileDialog::getSaveFileName( - nullptr, Tr::tr("Save JSON Template"), QString(), Tr::tr("JSON Files (*.json)")); - if (fileName.isEmpty()) - return; - - QFile file(fileName); - if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { - QTextStream out(&file); - out << customJsonTemplate.value(); - file.close(); - QMessageBox::information( - nullptr, - Tr::tr("Save Successful"), - Tr::tr("JSON template has been saved successfully.")); - } else { - QMessageBox::critical(nullptr, Tr::tr("Save Failed"), Tr::tr("Failed to save JSON template.")); - } -} - -void CustomPromptSettings::loadCustomTemplate() -{ - QString fileName = QFileDialog::getOpenFileName( - nullptr, Tr::tr("Load JSON Template"), QString(), Tr::tr("JSON Files (*.json)")); - if (fileName.isEmpty()) - return; - - QFile file(fileName); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QTextStream in(&file); - QString jsonContent = in.readAll(); - file.close(); - - QJsonParseError parseError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonContent.toUtf8(), &parseError); - if (parseError.error == QJsonParseError::NoError) { - customJsonTemplate.setVolatileValue(jsonContent); - QMessageBox::information( - nullptr, - Tr::tr("Load Successful"), - Tr::tr("JSON template has been loaded successfully.")); - } else { - QMessageBox::critical( - nullptr, Tr::tr("Invalid JSON"), Tr::tr("The selected file contains invalid JSON.")); - } - } else { - QMessageBox::critical(nullptr, Tr::tr("Load Failed"), Tr::tr("Failed to load JSON template.")); - } -} - -class CustomPromptSettingsPage : public Core::IOptionsPage -{ -public: - CustomPromptSettingsPage() - { - setId(Constants::QODE_ASSIST_CUSTOM_PROMPT_SETTINGS_PAGE_ID); - setDisplayName(Tr::tr("Custom Prompt")); - setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY); - setSettingsProvider([] { return &customPromptSettings(); }); - } -}; - -const CustomPromptSettingsPage customPromptSettingsPage; - -} // namespace QodeAssist::Settings diff --git a/settings/CustomPromptSettings.hpp b/settings/CustomPromptSettings.hpp deleted file mode 100644 index 7cc3399..0000000 --- a/settings/CustomPromptSettings.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024-2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#pragma once - -#include - -#include "ButtonAspect.hpp" - -namespace QodeAssist::Settings { - -class CustomPromptSettings : public Utils::AspectContainer -{ -public: - CustomPromptSettings(); - - Utils::StringAspect customJsonLabel{this}; - Utils::StringAspect customJsonTemplate{this}; - Utils::StringAspect customJsonLegend{this}; - ButtonAspect saveCustomTemplateButton{this}; - ButtonAspect loadCustomTemplateButton{this}; - ButtonAspect resetToDefaults{this}; - -private: - void setupConnection(); - void resetSettingsToDefaults(); - void saveCustomTemplate(); - void loadCustomTemplate(); -}; - -CustomPromptSettings &customPromptSettings(); - -} // namespace QodeAssist::Settings diff --git a/settings/GeneralSettings.cpp b/settings/GeneralSettings.cpp index 50124d2..3b54fd6 100644 --- a/settings/GeneralSettings.cpp +++ b/settings/GeneralSettings.cpp @@ -124,18 +124,10 @@ GeneralSettings::GeneralSettings() ccSelectTemplate.m_buttonText = TrConstants::SELECT; initStringAspect(ccUrl, Constants::CC_URL, TrConstants::URL, "http://localhost:11434"); - ccUrl.setHistoryCompleter(Constants::CC_CUSTOM_ENDPOINT_HISTORY); + ccUrl.setHistoryCompleter(Constants::CC_URL_HISTORY); ccSetUrl.m_buttonText = TrConstants::SELECT; - ccEndpointMode.setSettingsKey(Constants::CC_ENDPOINT_MODE); - ccEndpointMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox); - ccEndpointMode.addOption("Auto"); - ccEndpointMode.addOption("Custom"); - ccEndpointMode.addOption("FIM"); - ccEndpointMode.addOption("Chat"); - ccEndpointMode.setDefaultValue("Auto"); - - initStringAspect(ccCustomEndpoint, Constants::CC_CUSTOM_ENDPOINT, TrConstants::ENDPOINT_MODE, ""); + initStringAspect(ccCustomEndpoint, Constants::CC_CUSTOM_ENDPOINT, TrConstants::CUSTOM_ENDPOINT, ""); ccCustomEndpoint.setHistoryCompleter(Constants::CC_CUSTOM_ENDPOINT_HISTORY); ccStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay); @@ -176,18 +168,10 @@ GeneralSettings::GeneralSettings() ccPreset1Url.setHistoryCompleter(Constants::CC_PRESET1_URL_HISTORY); ccPreset1SetUrl.m_buttonText = TrConstants::SELECT; - ccPreset1EndpointMode.setSettingsKey(Constants::CC_PRESET1_ENDPOINT_MODE); - ccPreset1EndpointMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox); - ccPreset1EndpointMode.addOption("Auto"); - ccPreset1EndpointMode.addOption("Custom"); - ccPreset1EndpointMode.addOption("FIM"); - ccPreset1EndpointMode.addOption("Chat"); - ccPreset1EndpointMode.setDefaultValue("Auto"); - initStringAspect( ccPreset1CustomEndpoint, Constants::CC_PRESET1_CUSTOM_ENDPOINT, - TrConstants::ENDPOINT_MODE, + TrConstants::CUSTOM_ENDPOINT, ""); ccPreset1CustomEndpoint.setHistoryCompleter(Constants::CC_PRESET1_CUSTOM_ENDPOINT_HISTORY); @@ -219,15 +203,7 @@ GeneralSettings::GeneralSettings() caUrl.setHistoryCompleter(Constants::CA_URL_HISTORY); caSetUrl.m_buttonText = TrConstants::SELECT; - caEndpointMode.setSettingsKey(Constants::CA_ENDPOINT_MODE); - caEndpointMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox); - caEndpointMode.addOption("Auto"); - caEndpointMode.addOption("Custom"); - caEndpointMode.addOption("FIM"); - caEndpointMode.addOption("Chat"); - caEndpointMode.setDefaultValue("Auto"); - - initStringAspect(caCustomEndpoint, Constants::CA_CUSTOM_ENDPOINT, TrConstants::ENDPOINT_MODE, ""); + initStringAspect(caCustomEndpoint, Constants::CA_CUSTOM_ENDPOINT, TrConstants::CUSTOM_ENDPOINT, ""); caCustomEndpoint.setHistoryCompleter(Constants::CA_CUSTOM_ENDPOINT_HISTORY); caStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay); @@ -264,15 +240,7 @@ GeneralSettings::GeneralSettings() qrUrl.setHistoryCompleter(Constants::QR_URL_HISTORY); qrSetUrl.m_buttonText = TrConstants::SELECT; - qrEndpointMode.setSettingsKey(Constants::QR_ENDPOINT_MODE); - qrEndpointMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox); - qrEndpointMode.addOption("Auto"); - qrEndpointMode.addOption("Custom"); - qrEndpointMode.addOption("FIM"); - qrEndpointMode.addOption("Chat"); - qrEndpointMode.setDefaultValue("Auto"); - - initStringAspect(qrCustomEndpoint, Constants::QR_CUSTOM_ENDPOINT, TrConstants::ENDPOINT_MODE, ""); + initStringAspect(qrCustomEndpoint, Constants::QR_CUSTOM_ENDPOINT, TrConstants::CUSTOM_ENDPOINT, ""); qrCustomEndpoint.setHistoryCompleter(Constants::QR_CUSTOM_ENDPOINT_HISTORY); qrStatus.setDisplayStyle(Utils::StringAspect::LabelDisplay); @@ -310,10 +278,6 @@ GeneralSettings::GeneralSettings() setupConnections(); updatePreset1Visiblity(specifyPreset1.value()); - ccCustomEndpoint.setEnabled(ccEndpointMode.stringValue() == "Custom"); - ccPreset1CustomEndpoint.setEnabled(ccPreset1EndpointMode.stringValue() == "Custom"); - caCustomEndpoint.setEnabled(caEndpointMode.stringValue() == "Custom"); - qrCustomEndpoint.setEnabled(qrEndpointMode.stringValue() == "Custom"); setLayouter([this]() { using namespace Layouting; @@ -321,28 +285,28 @@ GeneralSettings::GeneralSettings() auto ccGrid = Grid{}; ccGrid.addRow({ccProvider, ccSelectProvider}); ccGrid.addRow({ccUrl, ccSetUrl}); - ccGrid.addRow({ccCustomEndpoint, ccEndpointMode}); + ccGrid.addRow({ccCustomEndpoint}); ccGrid.addRow({ccModel, ccSelectModel}); ccGrid.addRow({ccTemplate, ccSelectTemplate, ccShowTemplateInfo}); auto ccPreset1Grid = Grid{}; ccPreset1Grid.addRow({ccPreset1Provider, ccPreset1SelectProvider}); ccPreset1Grid.addRow({ccPreset1Url, ccPreset1SetUrl}); - ccPreset1Grid.addRow({ccPreset1CustomEndpoint, ccPreset1EndpointMode}); + ccPreset1Grid.addRow({ccPreset1CustomEndpoint}); ccPreset1Grid.addRow({ccPreset1Model, ccPreset1SelectModel}); ccPreset1Grid.addRow({ccPreset1Template, ccPreset1SelectTemplate}); auto caGrid = Grid{}; caGrid.addRow({caProvider, caSelectProvider}); caGrid.addRow({caUrl, caSetUrl}); - caGrid.addRow({caCustomEndpoint, caEndpointMode}); + caGrid.addRow({caCustomEndpoint}); caGrid.addRow({caModel, caSelectModel}); caGrid.addRow({caTemplate, caSelectTemplate, caShowTemplateInfo}); auto qrGrid = Grid{}; qrGrid.addRow({qrProvider, qrSelectProvider}); qrGrid.addRow({qrUrl, qrSetUrl}); - qrGrid.addRow({qrCustomEndpoint, qrEndpointMode}); + qrGrid.addRow({qrCustomEndpoint}); qrGrid.addRow({qrModel, qrSelectModel}); qrGrid.addRow({qrTemplate, qrSelectTemplate, qrShowTemplateInfo}); @@ -589,7 +553,6 @@ void GeneralSettings::updatePreset1Visiblity(bool state) ccPreset1SelectModel.updateVisibility(specifyPreset1.volatileValue()); ccPreset1Template.setVisible(specifyPreset1.volatileValue()); ccPreset1SelectTemplate.updateVisibility(specifyPreset1.volatileValue()); - ccPreset1EndpointMode.setVisible(specifyPreset1.volatileValue()); ccPreset1CustomEndpoint.setVisible(specifyPreset1.volatileValue()); } @@ -633,24 +596,6 @@ void GeneralSettings::setupConnections() connect(&specifyPreset1, &Utils::BoolAspect::volatileValueChanged, this, [this]() { updatePreset1Visiblity(specifyPreset1.volatileValue()); }); - connect(&ccEndpointMode, &Utils::BaseAspect::volatileValueChanged, this, [this]() { - ccCustomEndpoint.setEnabled( - ccEndpointMode.volatileValue() == ccEndpointMode.indexForDisplay("Custom")); - }); - connect(&ccPreset1EndpointMode, &Utils::BaseAspect::volatileValueChanged, this, [this]() { - ccPreset1CustomEndpoint.setEnabled( - ccPreset1EndpointMode.volatileValue() - == ccPreset1EndpointMode.indexForDisplay("Custom")); - }); - connect(&caEndpointMode, &Utils::BaseAspect::volatileValueChanged, this, [this]() { - caCustomEndpoint.setEnabled( - caEndpointMode.volatileValue() == caEndpointMode.indexForDisplay("Custom")); - }); - connect(&qrEndpointMode, &Utils::BaseAspect::volatileValueChanged, this, [this]() { - qrCustomEndpoint.setEnabled( - qrEndpointMode.volatileValue() == qrEndpointMode.indexForDisplay("Custom")); - }); - connect(&ccShowTemplateInfo, &ButtonAspect::clicked, this, [this]() { showTemplateInfoDialog(ccTemplateDescription, ccTemplate.value()); }); @@ -733,17 +678,13 @@ void GeneralSettings::resetPageToDefaults() resetAspect(ccPreset1Model); resetAspect(ccPreset1Template); resetAspect(ccPreset1Url); - resetAspect(ccEndpointMode); resetAspect(ccCustomEndpoint); - resetAspect(ccPreset1EndpointMode); resetAspect(ccPreset1CustomEndpoint); - resetAspect(caEndpointMode); resetAspect(caCustomEndpoint); resetAspect(qrProvider); resetAspect(qrModel); resetAspect(qrTemplate); resetAspect(qrUrl); - resetAspect(qrEndpointMode); resetAspect(qrCustomEndpoint); writeSettings(); } @@ -773,7 +714,6 @@ void GeneralSettings::onSaveConfiguration(const QString &prefix) config.model = ccModel.value(); config.templateName = ccTemplate.value(); config.url = ccUrl.value(); - config.endpointMode = ccEndpointMode.stringValue(); config.customEndpoint = ccCustomEndpoint.value(); config.type = ConfigurationType::CodeCompletion; } else if (prefix == "ca") { @@ -781,7 +721,6 @@ void GeneralSettings::onSaveConfiguration(const QString &prefix) config.model = caModel.value(); config.templateName = caTemplate.value(); config.url = caUrl.value(); - config.endpointMode = caEndpointMode.stringValue(); config.customEndpoint = caCustomEndpoint.value(); config.type = ConfigurationType::Chat; } else if (prefix == "qr") { @@ -789,7 +728,6 @@ void GeneralSettings::onSaveConfiguration(const QString &prefix) config.model = qrModel.value(); config.templateName = qrTemplate.value(); config.url = qrUrl.value(); - config.endpointMode = qrEndpointMode.stringValue(); config.customEndpoint = qrCustomEndpoint.value(); config.type = ConfigurationType::QuickRefactor; } @@ -926,21 +864,18 @@ void GeneralSettings::onLoadConfiguration(const QString &prefix) ccModel.setValue(config.model); ccTemplate.setValue(config.templateName); ccUrl.setValue(config.url); - ccEndpointMode.setValue(ccEndpointMode.indexForDisplay(config.endpointMode)); ccCustomEndpoint.setValue(config.customEndpoint); } else if (prefix == "ca") { caProvider.setValue(config.provider); caModel.setValue(config.model); caTemplate.setValue(config.templateName); caUrl.setValue(config.url); - caEndpointMode.setValue(caEndpointMode.indexForDisplay(config.endpointMode)); caCustomEndpoint.setValue(config.customEndpoint); } else if (prefix == "qr") { qrProvider.setValue(config.provider); qrModel.setValue(config.model); qrTemplate.setValue(config.templateName); qrUrl.setValue(config.url); - qrEndpointMode.setValue(qrEndpointMode.indexForDisplay(config.endpointMode)); qrCustomEndpoint.setValue(config.customEndpoint); } @@ -1008,21 +943,18 @@ void GeneralSettings::applyPresetConfiguration(int index, ConfigurationType type ccModel.setValue(config.model); ccTemplate.setValue(config.templateName); ccUrl.setValue(config.url); - ccEndpointMode.setValue(ccEndpointMode.indexForDisplay(config.endpointMode)); ccCustomEndpoint.setValue(config.customEndpoint); } else if (type == ConfigurationType::Chat) { caProvider.setValue(config.provider); caModel.setValue(config.model); caTemplate.setValue(config.templateName); caUrl.setValue(config.url); - caEndpointMode.setValue(caEndpointMode.indexForDisplay(config.endpointMode)); caCustomEndpoint.setValue(config.customEndpoint); } else if (type == ConfigurationType::QuickRefactor) { qrProvider.setValue(config.provider); qrModel.setValue(config.model); qrTemplate.setValue(config.templateName); qrUrl.setValue(config.url); - qrEndpointMode.setValue(qrEndpointMode.indexForDisplay(config.endpointMode)); qrCustomEndpoint.setValue(config.customEndpoint); } diff --git a/settings/GeneralSettings.hpp b/settings/GeneralSettings.hpp index d7bdc08..98c8de4 100644 --- a/settings/GeneralSettings.hpp +++ b/settings/GeneralSettings.hpp @@ -62,7 +62,6 @@ public: Utils::StringAspect ccUrl{this}; ButtonAspect ccSetUrl{this}; - Utils::SelectionAspect ccEndpointMode{this}; Utils::StringAspect ccCustomEndpoint{this}; Utils::StringAspect ccStatus{this}; @@ -85,7 +84,6 @@ public: Utils::StringAspect ccPreset1Url{this}; ButtonAspect ccPreset1SetUrl{this}; - Utils::SelectionAspect ccPreset1EndpointMode{this}; Utils::StringAspect ccPreset1CustomEndpoint{this}; Utils::StringAspect ccPreset1Model{this}; @@ -110,7 +108,6 @@ public: Utils::StringAspect caUrl{this}; ButtonAspect caSetUrl{this}; - Utils::SelectionAspect caEndpointMode{this}; Utils::StringAspect caCustomEndpoint{this}; Utils::StringAspect caStatus{this}; @@ -138,7 +135,6 @@ public: Utils::StringAspect qrUrl{this}; ButtonAspect qrSetUrl{this}; - Utils::SelectionAspect qrEndpointMode{this}; Utils::StringAspect qrCustomEndpoint{this}; Utils::StringAspect qrStatus{this}; diff --git a/settings/SettingsConstants.hpp b/settings/SettingsConstants.hpp index f035648..1ca2242 100644 --- a/settings/SettingsConstants.hpp +++ b/settings/SettingsConstants.hpp @@ -35,7 +35,6 @@ const char CC_MODEL_HISTORY[] = "QodeAssist.ccModelHistory"; const char CC_TEMPLATE[] = "QodeAssist.ccTemplate"; const char CC_URL[] = "QodeAssist.ccUrl"; const char CC_URL_HISTORY[] = "QodeAssist.ccUrlHistory"; -const char CC_ENDPOINT_MODE[] = "QodeAssist.ccEndpointMode"; const char CC_CUSTOM_ENDPOINT[] = "QodeAssist.ccCustomEndpoint"; const char CC_CUSTOM_ENDPOINT_HISTORY[] = "QodeAssist.ccCustomEndpointHistory"; @@ -45,7 +44,6 @@ const char CA_MODEL_HISTORY[] = "QodeAssist.caModelHistory"; const char CA_TEMPLATE[] = "QodeAssist.caTemplate"; const char CA_URL[] = "QodeAssist.caUrl"; const char CA_URL_HISTORY[] = "QodeAssist.caUrlHistory"; -const char CA_ENDPOINT_MODE[] = "QodeAssist.caEndpointMode"; const char CA_CUSTOM_ENDPOINT[] = "QodeAssist.caCustomEndpoint"; const char CA_CUSTOM_ENDPOINT_HISTORY[] = "QodeAssist.caCustomEndpointHistory"; @@ -56,7 +54,6 @@ const char QR_MODEL_HISTORY[] = "QodeAssist.qrModelHistory"; const char QR_TEMPLATE[] = "QodeAssist.qrTemplate"; const char QR_URL[] = "QodeAssist.qrUrl"; const char QR_URL_HISTORY[] = "QodeAssist.qrUrlHistory"; -const char QR_ENDPOINT_MODE[] = "QodeAssist.qrEndpointMode"; const char QR_CUSTOM_ENDPOINT[] = "QodeAssist.qrCustomEndpoint"; const char QR_CUSTOM_ENDPOINT_HISTORY[] = "QodeAssist.qrCustomEndpointHistory"; @@ -68,9 +65,8 @@ const char CC_PRESET1_MODEL_HISTORY[] = "QodeAssist.ccPreset1ModelHistory"; const char CC_PRESET1_TEMPLATE[] = "QodeAssist.ccPreset1Template"; const char CC_PRESET1_URL[] = "QodeAssist.ccPreset1Url"; const char CC_PRESET1_URL_HISTORY[] = "QodeAssist.ccPreset1UrlHistory"; -const char CC_PRESET1_ENDPOINT_MODE[] = "QodeAssist.caPreset1EndpointMode"; -const char CC_PRESET1_CUSTOM_ENDPOINT[] = "QodeAssist.caPreset1CustomEndpointHistory"; -const char CC_PRESET1_CUSTOM_ENDPOINT_HISTORY[] = "QodeAssist.caPreset1CustomEndpointHistory"; +const char CC_PRESET1_CUSTOM_ENDPOINT[] = "QodeAssist.ccPreset1CustomEndpoint"; +const char CC_PRESET1_CUSTOM_ENDPOINT_HISTORY[] = "QodeAssist.ccPreset1CustomEndpointHistory"; // settings const char ENABLE_QODE_ASSIST[] = "QodeAssist.enableQodeAssist"; @@ -93,7 +89,6 @@ const char CC_IGNORE_WHITESPACE_IN_CHAR_COUNT[] = "QodeAssist.ccIgnoreWhitespace const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold"; const char CC_MULTILINE_COMPLETION[] = "QodeAssist.ccMultilineCompletion"; const char CC_MODEL_OUTPUT_HANDLER[] = "QodeAssist.ccModelOutputHandler"; -const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate"; const char CA_AUTO_APPLY_FILE_EDITS[] = "QodeAssist.caAutoApplyFileEdits"; const char CA_TOKENS_THRESHOLD[] = "QodeAssist.caTokensThreshold"; const char CA_LINK_OPEN_FILES[] = "QodeAssist.caLinkOpenFiles"; @@ -127,7 +122,6 @@ const char QODE_ASSIST_CHAT_ASSISTANT_SETTINGS_PAGE_ID[] const char QODE_ASSIST_QUICK_REFACTOR_SETTINGS_PAGE_ID[] = "QodeAssist.4QuickRefactorSettingsPageId"; const char QODE_ASSIST_TOOLS_SETTINGS_PAGE_ID[] = "QodeAssist.5ToolsSettingsPageId"; -const char QODE_ASSIST_CUSTOM_PROMPT_SETTINGS_PAGE_ID[] = "QodeAssist.6CustomPromptSettingsPageId"; const char QODE_ASSIST_GENERAL_OPTIONS_CATEGORY[] = "QodeAssist.Category"; const char QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY[] = "QodeAssist"; diff --git a/settings/SettingsTr.hpp b/settings/SettingsTr.hpp index 4b367c7..35337d4 100644 --- a/settings/SettingsTr.hpp +++ b/settings/SettingsTr.hpp @@ -44,7 +44,7 @@ inline const char *ENABLE_CHECK_UPDATE_ON_START inline const char *ENABLE_CHAT = QT_TRANSLATE_NOOP( "QtC::QodeAssist", "Enable Chat(If you have performance issues try disabling this, need restart QtC)"); -inline const char *ENDPOINT_MODE = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Endpoint Mode:"); +inline const char *CUSTOM_ENDPOINT = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Custom endpoint:"); inline const char *CODE_COMPLETION = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Code Completion"); inline const char *CHAT_ASSISTANT = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Chat Assistant"); diff --git a/sources/external/llmcore b/sources/external/llmcore deleted file mode 160000 index d1fb0dc..0000000 --- a/sources/external/llmcore +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d1fb0dca95d0c6960b2499265dec34f8a78e34f5 diff --git a/sources/external/llmqore b/sources/external/llmqore new file mode 160000 index 0000000..55a4e29 --- /dev/null +++ b/sources/external/llmqore @@ -0,0 +1 @@ +Subproject commit 55a4e293fe2b95596e8626a793dfd36ad67144f6 diff --git a/templates/CodeLlamaFim.hpp b/templates/CodeLlamaFim.hpp index 9b8da89..c40247f 100644 --- a/templates/CodeLlamaFim.hpp +++ b/templates/CodeLlamaFim.hpp @@ -28,6 +28,7 @@ class CodeLlamaFim : public PluginLLMCore::PromptTemplate public: PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::FIM; } QString name() const override { return "CodeLlama FIM"; } + QString endpoint() const override { return QStringLiteral("/api/generate"); } QStringList stopWords() const override { return QStringList() << "" << "
" << "";
diff --git a/templates/CodeLlamaQMLFim.hpp b/templates/CodeLlamaQMLFim.hpp
index 6af51bf..2d3cf4f 100644
--- a/templates/CodeLlamaQMLFim.hpp
+++ b/templates/CodeLlamaQMLFim.hpp
@@ -28,6 +28,7 @@ class CodeLlamaQMLFim : public PluginLLMCore::PromptTemplate
 public:
     PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::FIM; }
     QString name() const override { return "CodeLlama QML FIM"; }
+    QString endpoint() const override { return QStringLiteral("/api/generate"); }
     QStringList stopWords() const override
     {
         return QStringList() << "" << "
" << "
" << "
" << "< EOT >" << "\\end" diff --git a/templates/CustomFimTemplate.hpp b/templates/CustomFimTemplate.hpp deleted file mode 100644 index 5cad26d..0000000 --- a/templates/CustomFimTemplate.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2024 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#pragma once - -#include "llmcore/PromptTemplate.hpp" - -#include -#include - -#include "logger/Logger.hpp" -#include "settings/CustomPromptSettings.hpp" - -namespace QodeAssist::Templates { - -class CustomTemplate : public LLMCore::PromptTemplate -{ -public: - LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; } - QString name() const override { return "Custom FIM Template"; } - QString promptTemplate() const override - { - return Settings::customPromptSettings().customJsonTemplate(); - } - QStringList stopWords() const override { return QStringList(); } - void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override - { - QJsonDocument doc = QJsonDocument::fromJson(promptTemplate().toUtf8()); - if (doc.isNull() || !doc.isObject()) { - LOG_MESSAGE(QString("Invalid JSON template in settings")); - - return; - } - - QJsonObject templateObj = doc.object(); - QJsonObject processedObj = processJsonTemplate(templateObj, context); - - for (auto it = processedObj.begin(); it != processedObj.end(); ++it) { - request[it.key()] = it.value(); - } - } - QString description() const override { return promptTemplate(); } - -private: - QJsonValue processJsonValue(const QJsonValue &value, const LLMCore::ContextData &context) const - { - if (value.isString()) { - QString str = value.toString(); - str.replace("{{QODE_PREFIX}}", context.prefix); - str.replace("{{QODE_SUFFIX}}", context.suffix); - return str; - } else if (value.isObject()) { - return processJsonTemplate(value.toObject(), context); - } else if (value.isArray()) { - QJsonArray newArray; - for (const QJsonValue &arrayValue : value.toArray()) { - newArray.append(processJsonValue(arrayValue, context)); - } - return newArray; - } - return value; - } - - QJsonObject processJsonTemplate( - const QJsonObject &templateObj, const LLMCore::ContextData &context) const - { - QJsonObject result; - for (auto it = templateObj.begin(); it != templateObj.end(); ++it) { - result[it.key()] = processJsonValue(it.value(), context); - } - return result; - } -}; -} // namespace QodeAssist::Templates diff --git a/templates/DeepSeekCoderFim.hpp b/templates/DeepSeekCoderFim.hpp index 8bfdd0a..6c89198 100644 --- a/templates/DeepSeekCoderFim.hpp +++ b/templates/DeepSeekCoderFim.hpp @@ -23,17 +23,17 @@ namespace QodeAssist::Templates { -class DeepSeekCoderFim : public LLMCore::PromptTemplate +class DeepSeekCoderFim : public LLMQore::PromptTemplate { public: - LLMCore::TemplateType type() const override { return LLMCore::TemplateType::Fim; } + LLMQore::TemplateType type() const override { return LLMQore::TemplateType::Fim; } QString name() const override { return "DeepSeekCoder FIM"; } QString promptTemplate() const override { return "<|fim▁begin|>%1<|fim▁hole|>%2<|fim▁end|>"; } QStringList stopWords() const override { return QStringList(); } - void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override + void prepareRequest(QJsonObject &request, const LLMQore::ContextData &context) const override { QString formattedPrompt = promptTemplate().arg(context.prefix, context.suffix); request["prompt"] = formattedPrompt; diff --git a/templates/LlamaCppFim.hpp b/templates/LlamaCppFim.hpp index d9fa286..ca1616a 100644 --- a/templates/LlamaCppFim.hpp +++ b/templates/LlamaCppFim.hpp @@ -30,6 +30,7 @@ class LlamaCppFim : public PluginLLMCore::PromptTemplate public: PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::FIM; } QString name() const override { return "llama.cpp FIM"; } + QString endpoint() const override { return QStringLiteral("/infill"); } QStringList stopWords() const override { return {}; } void prepareRequest(QJsonObject &request, const PluginLLMCore::ContextData &context) const override diff --git a/templates/MistralAI.hpp b/templates/MistralAI.hpp index fdf8f91..89fa268 100644 --- a/templates/MistralAI.hpp +++ b/templates/MistralAI.hpp @@ -30,6 +30,7 @@ class MistralAIFim : public PluginLLMCore::PromptTemplate public: PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::FIM; } QString name() const override { return "Mistral AI FIM"; } + QString endpoint() const override { return QStringLiteral("/v1/fim/completions"); } QStringList stopWords() const override { return QStringList(); } void prepareRequest(QJsonObject &request, const PluginLLMCore::ContextData &context) const override { diff --git a/templates/Ollama.hpp b/templates/Ollama.hpp index de0f799..cacb98a 100644 --- a/templates/Ollama.hpp +++ b/templates/Ollama.hpp @@ -30,6 +30,7 @@ class OllamaFim : public PluginLLMCore::PromptTemplate public: PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::FIM; } QString name() const override { return "Ollama FIM"; } + QString endpoint() const override { return QStringLiteral("/api/generate"); } QStringList stopWords() const override { return QStringList() << ""; } void prepareRequest(QJsonObject &request, const PluginLLMCore::ContextData &context) const override { diff --git a/templates/Qwen25CoderFIM.hpp b/templates/Qwen25CoderFIM.hpp index bda401e..739396b 100644 --- a/templates/Qwen25CoderFIM.hpp +++ b/templates/Qwen25CoderFIM.hpp @@ -29,6 +29,7 @@ class Qwen25CoderFIM : public PluginLLMCore::PromptTemplate public: QString name() const override { return "Qwen2.5 Coder FIM"; } PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::FIM; } + QString endpoint() const override { return QStringLiteral("/api/generate"); } QStringList stopWords() const override { return QStringList() << "<|endoftext|>" << "<|EOT|>"; } void prepareRequest(QJsonObject &request, const PluginLLMCore::ContextData &context) const override { diff --git a/templates/StarCoder2Fim.hpp b/templates/StarCoder2Fim.hpp index 71e20f6..a01d458 100644 --- a/templates/StarCoder2Fim.hpp +++ b/templates/StarCoder2Fim.hpp @@ -28,6 +28,7 @@ class StarCoder2Fim : public PluginLLMCore::PromptTemplate public: PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::FIM; } QString name() const override { return "StarCoder2 FIM"; } + QString endpoint() const override { return QStringLiteral("/api/generate"); } QStringList stopWords() const override { return QStringList() << "<|endoftext|>" << "" << "" << "" diff --git a/templates/Templates.hpp b/templates/Templates.hpp index 940f653..c02af3b 100644 --- a/templates/Templates.hpp +++ b/templates/Templates.hpp @@ -30,8 +30,6 @@ #include "templates/OpenAI.hpp" #include "templates/OpenAICompatible.hpp" #include "templates/OpenAIResponses.hpp" -// #include "templates/CustomFimTemplate.hpp" -// #include "templates/DeepSeekCoderFim.hpp" #include "templates/GoogleAI.hpp" #include "templates/Llama2.hpp" #include "templates/Llama3.hpp" @@ -58,8 +56,6 @@ inline void registerTemplates() templateManager.registerTemplate(); templateManager.registerTemplate(); templateManager.registerTemplate(); - // templateManager.registerTemplate(); - // templateManager.registerTemplate(); templateManager.registerTemplate(); templateManager.registerTemplate(); templateManager.registerTemplate(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 581bc88..2d772a9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,7 +18,7 @@ target_link_libraries(QodeAssistTest PRIVATE QtCreator::LanguageClient Context PluginLLMCore - LLMCore + LLMQore ) target_compile_definitions(QodeAssistTest PRIVATE CMAKE_CURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/test/DocumentContextReaderTest.cpp b/test/DocumentContextReaderTest.cpp index 91ce5b0..e8b3c1b 100644 --- a/test/DocumentContextReaderTest.cpp +++ b/test/DocumentContextReaderTest.cpp @@ -38,7 +38,7 @@ void PrintTo(const ContextData &data, std::ostream *os) } // namespace QodeAssist::PluginLLMCore using namespace QodeAssist::Context; -using namespace QodeAssist::LLMCore; +using namespace QodeAssist::PluginLLMCore; using namespace QodeAssist::Settings; class DocumentContextReaderTest : public QObject, public testing::Test diff --git a/test/LLMClientInterfaceTests.cpp b/test/LLMClientInterfaceTests.cpp deleted file mode 100644 index 0b31cc5..0000000 --- a/test/LLMClientInterfaceTests.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2025 Povilas Kanapickas - * - * 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 . - */ - -#include -#include - -#include -#include -#include -#include - -#include "LLMClientInterface.hpp" -#include "MockDocumentReader.hpp" -#include "MockRequestHandler.hpp" -#include "llmcore/IPromptProvider.hpp" -#include "llmcore/IProviderRegistry.hpp" -#include "logger/EmptyRequestPerformanceLogger.hpp" -#include "settings/CodeCompletionSettings.hpp" -#include "settings/GeneralSettings.hpp" -#include "templates/Templates.hpp" -#include - -using namespace testing; - -namespace QodeAssist { - -class MockPromptProvider : public LLMCore::IPromptProvider -{ -public: - MOCK_METHOD(LLMCore::PromptTemplate *, getTemplateByName, (const QString &), (const override)); - MOCK_METHOD(QStringList, templatesNames, (), (const override)); - MOCK_METHOD(QStringList, getTemplatesForProvider, (LLMCore::ProviderID id), (const override)); -}; - -class MockProviderRegistry : public LLMCore::IProviderRegistry -{ -public: - MOCK_METHOD(LLMCore::Provider *, getProviderByName, (const QString &), (override)); - MOCK_METHOD(QStringList, providersNames, (), (const override)); -}; - -class MockProvider : public LLMCore::Provider -{ -public: - QString name() const override { return "mock_provider"; } - QString url() const override { return "https://mock_url"; } - QString completionEndpoint() const override { return "/v1/completions"; } - QString chatEndpoint() const override { return "/v1/chat/completions"; } - void prepareRequest( - QJsonObject &request, - LLMCore::PromptTemplate *promptTemplate, - LLMCore::ContextData context, - LLMCore::RequestType requestType, - bool isToolsEnabled, - bool isThinkingEnabled) override - { - promptTemplate->prepareRequest(request, context); - } - - bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override - { - return true; - } - - QFuture> getInstalledModels(const QString &) override - { - return QtFuture::makeReadyFuture(QList{}); - } - - LLMCore::ProviderID providerID() const override { return LLMCore::ProviderID::OpenAI; } -}; - -class LLMClientInterfaceTest : public Test -{ -protected: - void SetUp() override - { - Core::DocumentModel::init(); - - m_provider = std::make_unique(); - m_fimTemplate = std::make_unique(); - m_chatTemplate = std::make_unique(); - - ON_CALL(m_providerRegistry, getProviderByName(_)).WillByDefault(Return(m_provider.get())); - ON_CALL(m_promptProvider, getTemplateByName(_)).WillByDefault(Return(m_fimTemplate.get())); - - EXPECT_CALL(m_providerRegistry, getProviderByName(_)).Times(testing::AnyNumber()); - EXPECT_CALL(m_promptProvider, getTemplateByName(_)).Times(testing::AnyNumber()); - - m_generalSettings.ccProvider.setValue("mock_provider"); - m_generalSettings.ccModel.setValue("mock_model"); - m_generalSettings.ccTemplate.setValue("mock_template"); - m_generalSettings.ccUrl.setValue("http://localhost:8000"); - - m_completeSettings.systemPromptForNonFimModels.setValue("system prompt non fim"); - m_completeSettings.systemPrompt.setValue("system prompt"); - m_completeSettings.userMessageTemplateForCC.setValue( - "user message template prefix:\n${prefix}\nsuffix:\n${suffix}\n"); - - m_client = std::make_unique( - m_generalSettings, - m_completeSettings, - m_providerRegistry, - &m_promptProvider, - m_documentReader, - m_performanceLogger); - } - - void TearDown() override { Core::DocumentModel::destroy(); } - - QJsonObject createInitializeRequest() - { - QJsonObject request; - request["jsonrpc"] = "2.0"; - request["id"] = "init-1"; - request["method"] = "initialize"; - return request; - } - - QString buildTestFilePath() { return QString(CMAKE_CURRENT_SOURCE_DIR) + "/test_file.py"; } - - QJsonObject createCompletionRequest() - { - QJsonObject position; - position["line"] = 2; - position["character"] = 5; - - QJsonObject doc; - // change next line to link to test_file.py in current directory of the cmake project - doc["uri"] = "file://" + buildTestFilePath(); - doc["position"] = position; - - QJsonObject params; - params["doc"] = doc; - - QJsonObject request; - request["jsonrpc"] = "2.0"; - request["id"] = "completion-1"; - request["method"] = "getCompletionsCycling"; - request["params"] = params; - - return request; - } - - QJsonObject createCancelRequest(const QString &idToCancel) - { - QJsonObject params; - params["id"] = idToCancel; - - QJsonObject request; - request["jsonrpc"] = "2.0"; - request["id"] = "cancel-1"; - request["method"] = "$/cancelRequest"; - request["params"] = params; - - return request; - } - - Settings::GeneralSettings m_generalSettings; - Settings::CodeCompletionSettings m_completeSettings; - MockProviderRegistry m_providerRegistry; - MockPromptProvider m_promptProvider; - MockDocumentReader m_documentReader; - EmptyRequestPerformanceLogger m_performanceLogger; - std::unique_ptr m_client; - std::unique_ptr m_provider; - std::unique_ptr m_fimTemplate; - std::unique_ptr m_chatTemplate; -}; - -TEST_F(LLMClientInterfaceTest, initialize) -{ - QSignalSpy spy(m_client.get(), &LanguageClient::BaseClientInterface::messageReceived); - - QJsonObject request = createInitializeRequest(); - m_client->sendData(QJsonDocument(request).toJson()); - - ASSERT_EQ(spy.count(), 1); - auto message = spy.takeFirst().at(0).value(); - QJsonObject response = message.toJsonObject(); - - EXPECT_EQ(response["id"].toString(), "init-1"); - EXPECT_TRUE(response.contains("result")); - EXPECT_TRUE(response["result"].toObject().contains("capabilities")); - EXPECT_TRUE(response["result"].toObject().contains("serverInfo")); -} - -TEST_F(LLMClientInterfaceTest, ServerDeviceTemplate) -{ - EXPECT_EQ(m_client->serverDeviceTemplate().toFSPathString(), "QodeAssist"); -} - -} // namespace QodeAssist diff --git a/test/MockRequestHandler.hpp b/test/MockRequestHandler.hpp deleted file mode 100644 index 94522cb..0000000 --- a/test/MockRequestHandler.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2025 Povilas Kanapickas - * - * 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 . - */ - -#pragma once - -#include - -namespace QodeAssist::LLMCore { - -class MockRequestHandler : public RequestHandlerBase -{ -public: - explicit MockRequestHandler(QObject *parent = nullptr) - : RequestHandlerBase(parent) - , m_fakeCompletion("") - {} - - void setFakeCompletion(const QString &completion) { m_fakeCompletion = completion; } - - void sendLLMRequest(const LLMConfig &config, const QJsonObject &request) override - { - m_receivedRequests.append(config); - - emit completionReceived(m_fakeCompletion, request, true); - - QString requestId = request["id"].toString(); - emit requestFinished(requestId, true, QString()); - } - - bool cancelRequest(const QString &id) override - { - emit requestCancelled(id); - return true; - } - - const QVector &receivedRequests() const { return m_receivedRequests; } - -private: - QString m_fakeCompletion; - QVector m_receivedRequests; -}; - -} // namespace QodeAssist::LLMCore diff --git a/test/TestUtils.hpp b/test/TestUtils.hpp index 00567b2..1e3594e 100644 --- a/test/TestUtils.hpp +++ b/test/TestUtils.hpp @@ -59,16 +59,16 @@ std::ostream &operator<<(std::ostream &out, const std::optional &value) return out; } -namespace QodeAssist::LLMCore { +namespace QodeAssist::PluginLLMCore { -inline std::ostream &operator<<(std::ostream &out, const PluginLLMCore::Message &value) +inline std::ostream &operator<<(std::ostream &out, const Message &value) { out << "Message{" << "role=" << value.role << "content=" << value.content << "}"; return out; } -inline std::ostream &operator<<(std::ostream &out, const PluginLLMCore::ContextData &value) +inline std::ostream &operator<<(std::ostream &out, const ContextData &value) { out << "ContextData{" << "\n systemPrompt=" << value.systemPrompt << "\n prefix=" << value.prefix @@ -77,4 +77,4 @@ inline std::ostream &operator<<(std::ostream &out, const PluginLLMCore::ContextD return out; } -} // namespace QodeAssist::LLMCore +} // namespace QodeAssist::PluginLLMCore diff --git a/tools/BuildProjectTool.cpp b/tools/BuildProjectTool.cpp index 93fdb76..ede39bb 100644 --- a/tools/BuildProjectTool.cpp +++ b/tools/BuildProjectTool.cpp @@ -99,23 +99,23 @@ QJsonObject BuildProjectTool::parametersSchema() const return definition; } -QFuture BuildProjectTool::executeAsync(const QJsonObject &input) +QFuture BuildProjectTool::executeAsync(const QJsonObject &input) { auto *project = ProjectExplorer::ProjectManager::startupProject(); if (!project) { return QtFuture::makeReadyFuture( - QString("Error: No active project found. Please open a project in Qt Creator.")); + LLMQore::ToolResult::error("Error: No active project found. Please open a project in Qt Creator.")); } if (ProjectExplorer::BuildManager::isBuilding(project)) { return QtFuture::makeReadyFuture( - QString("Error: Build is already in progress. Please wait for it to complete.")); + LLMQore::ToolResult::error("Error: Build is already in progress. Please wait for it to complete.")); } if (m_activeBuilds.contains(project)) { return QtFuture::makeReadyFuture( - QString("Error: Build is already being tracked for project '%1'.") - .arg(project->displayName())); + LLMQore::ToolResult::error(QString("Error: Build is already being tracked for project '%1'.") + .arg(project->displayName()))); } bool rebuild = input.value("rebuild").toBool(false); @@ -126,7 +126,7 @@ QFuture BuildProjectTool::executeAsync(const QJsonObject &input) .arg(project->displayName()) .arg(runAfterBuild ? QString(" (run after build)") : QString())); - auto promise = QSharedPointer>::create(); + auto promise = QSharedPointer>::create(); promise->start(); BuildInfo buildInfo; @@ -187,7 +187,7 @@ void BuildProjectTool::onBuildQueueFinished(bool success) } if (info.promise) { - info.promise->addResult(result); + info.promise->addResult(LLMQore::ToolResult::text(result)); info.promise->finish(); } diff --git a/tools/BuildProjectTool.hpp b/tools/BuildProjectTool.hpp index 72a6bb7..12476a0 100644 --- a/tools/BuildProjectTool.hpp +++ b/tools/BuildProjectTool.hpp @@ -19,7 +19,7 @@ #pragma once -#include +#include #include #include #include @@ -34,7 +34,7 @@ namespace QodeAssist::Tools { struct BuildInfo { - QSharedPointer> promise; + QSharedPointer> promise; QPointer project; QString projectName; bool isRebuild = false; @@ -42,7 +42,7 @@ struct BuildInfo QMetaObject::Connection buildFinishedConnection; }; -class BuildProjectTool : public ::LLMCore::BaseTool +class BuildProjectTool : public ::LLMQore::BaseTool { Q_OBJECT public: @@ -54,7 +54,7 @@ public: QString description() const override; QJsonObject parametersSchema() const override; - QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; + QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; private slots: void onBuildQueueFinished(bool success); diff --git a/tools/CreateNewFileTool.cpp b/tools/CreateNewFileTool.cpp index 1777521..35b1eba 100644 --- a/tools/CreateNewFileTool.cpp +++ b/tools/CreateNewFileTool.cpp @@ -18,7 +18,8 @@ */ #include "CreateNewFileTool.hpp" -#include "ToolExceptions.hpp" + +#include #include #include @@ -73,24 +74,24 @@ QJsonObject CreateNewFileTool::parametersSchema() const return definition; } -QFuture CreateNewFileTool::executeAsync(const QJsonObject &input) +QFuture CreateNewFileTool::executeAsync(const QJsonObject &input) { - return QtConcurrent::run([this, input]() -> QString { + return QtConcurrent::run([this, input]() -> LLMQore::ToolResult { QString filePath = input["filepath"].toString(); if (filePath.isEmpty()) { - throw ToolInvalidArgument("Error: 'filepath' parameter is required"); + throw LLMQore::ToolInvalidArgument("Error: 'filepath' parameter is required"); } QFileInfo fileInfo(filePath); QString absolutePath = fileInfo.absoluteFilePath(); bool isInProject = Context::ProjectUtils::isFileInProject(absolutePath); - + if (!isInProject) { const auto &settings = Settings::toolsSettings(); if (!settings.allowAccessOutsideProject()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( QString("Error: File path '%1' is not within the current project. " "Enable 'Allow file access outside project' in settings to create files outside project scope.") .arg(absolutePath)); @@ -99,14 +100,14 @@ QFuture CreateNewFileTool::executeAsync(const QJsonObject &input) } if (fileInfo.exists()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( QString("Error: File already exists at path '%1'").arg(filePath)); } QDir dir = fileInfo.absoluteDir(); if (!dir.exists()) { if (!dir.mkpath(".")) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( QString("Error: Could not create directory: '%1'").arg(dir.absolutePath())); } LOG_MESSAGE(QString("Created directory path: %1").arg(dir.absolutePath())); @@ -114,7 +115,7 @@ QFuture CreateNewFileTool::executeAsync(const QJsonObject &input) QFile file(absolutePath); if (!file.open(QIODevice::WriteOnly)) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( QString("Error: Could not create file '%1': %2").arg(absolutePath, file.errorString())); } @@ -122,7 +123,7 @@ QFuture CreateNewFileTool::executeAsync(const QJsonObject &input) LOG_MESSAGE(QString("Successfully created new file: %1").arg(absolutePath)); - return QString("Successfully created new file: %1").arg(absolutePath); + return LLMQore::ToolResult::text(QString("Successfully created new file: %1").arg(absolutePath)); }); } diff --git a/tools/CreateNewFileTool.hpp b/tools/CreateNewFileTool.hpp index 71b2c3e..82a0177 100644 --- a/tools/CreateNewFileTool.hpp +++ b/tools/CreateNewFileTool.hpp @@ -19,11 +19,11 @@ #pragma once -#include +#include namespace QodeAssist::Tools { -class CreateNewFileTool : public ::LLMCore::BaseTool +class CreateNewFileTool : public ::LLMQore::BaseTool { Q_OBJECT public: @@ -34,7 +34,7 @@ public: QString description() const override; QJsonObject parametersSchema() const override; - QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; + QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; }; } // namespace QodeAssist::Tools diff --git a/tools/EditFileTool.cpp b/tools/EditFileTool.cpp index f3a656b..3dec3c8 100644 --- a/tools/EditFileTool.cpp +++ b/tools/EditFileTool.cpp @@ -18,7 +18,8 @@ */ #include "EditFileTool.hpp" -#include "ToolExceptions.hpp" + +#include #include #include @@ -107,20 +108,20 @@ QJsonObject EditFileTool::parametersSchema() const return definition; } -QFuture EditFileTool::executeAsync(const QJsonObject &input) +QFuture EditFileTool::executeAsync(const QJsonObject &input) { - return QtConcurrent::run([this, input]() -> QString { + return QtConcurrent::run([this, input]() -> LLMQore::ToolResult { QString filename = input["filename"].toString().trimmed(); QString oldContent = input["old_content"].toString(); QString newContent = input["new_content"].toString(); QString requestId = input["_request_id"].toString(); if (filename.isEmpty()) { - throw ToolInvalidArgument("'filename' parameter is required and cannot be empty"); + throw LLMQore::ToolInvalidArgument("'filename' parameter is required and cannot be empty"); } if (newContent.isEmpty()) { - throw ToolInvalidArgument("'new_content' parameter is required and cannot be empty"); + throw LLMQore::ToolInvalidArgument("'new_content' parameter is required and cannot be empty"); } @@ -132,7 +133,7 @@ QFuture EditFileTool::executeAsync(const QJsonObject &input) } else { QString projectRoot = Context::ProjectUtils::getProjectRoot(); if (projectRoot.isEmpty()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( QString("Cannot resolve relative path '%1': no project is open. " "Please provide an absolute path or open a project.") .arg(filename)); @@ -145,12 +146,12 @@ QFuture EditFileTool::executeAsync(const QJsonObject &input) QFile file(filePath); if (!file.exists()) { - throw ToolRuntimeError(QString("File does not exist: %1").arg(filePath)); + throw LLMQore::ToolRuntimeError(QString("File does not exist: %1").arg(filePath)); } QFileInfo finalFileInfo(filePath); if (!finalFileInfo.isWritable()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( QString("File is not writable (read-only or permission denied): %1").arg(filePath)); } @@ -158,7 +159,7 @@ QFuture EditFileTool::executeAsync(const QJsonObject &input) if (!isInProject) { const auto &settings = Settings::toolsSettings(); if (!settings.allowAccessOutsideProject()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( QString("File path '%1' is not within the current project. " "Enable 'Allow file access outside project' in settings to edit files outside the project.") .arg(filePath)); @@ -220,7 +221,7 @@ QFuture EditFileTool::executeAsync(const QJsonObject &input) QString resultStr = "QODEASSIST_FILE_EDIT:" + QString::fromUtf8(QJsonDocument(result).toJson(QJsonDocument::Compact)); - return resultStr; + return LLMQore::ToolResult::text(resultStr); }); } diff --git a/tools/EditFileTool.hpp b/tools/EditFileTool.hpp index f3957a1..c47e6fb 100644 --- a/tools/EditFileTool.hpp +++ b/tools/EditFileTool.hpp @@ -19,11 +19,11 @@ #pragma once -#include +#include namespace QodeAssist::Tools { -class EditFileTool : public ::LLMCore::BaseTool +class EditFileTool : public ::LLMQore::BaseTool { Q_OBJECT public: @@ -34,7 +34,7 @@ public: QString description() const override; QJsonObject parametersSchema() const override; - QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; + QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; }; } // namespace QodeAssist::Tools diff --git a/tools/ExecuteTerminalCommandTool.cpp b/tools/ExecuteTerminalCommandTool.cpp index f0d8a2a..43e06ff 100644 --- a/tools/ExecuteTerminalCommandTool.cpp +++ b/tools/ExecuteTerminalCommandTool.cpp @@ -80,30 +80,32 @@ QJsonObject ExecuteTerminalCommandTool::parametersSchema() const return definition; } -QFuture ExecuteTerminalCommandTool::executeAsync(const QJsonObject &input) +QFuture ExecuteTerminalCommandTool::executeAsync(const QJsonObject &input) { + using LLMQore::ToolResult; + const QString command = input.value("command").toString().trimmed(); const QString args = input.value("args").toString().trimmed(); if (command.isEmpty()) { LOG_MESSAGE("ExecuteTerminalCommandTool: Command is empty"); - return QtFuture::makeReadyFuture(QString("Error: Command parameter is required.")); + return QtFuture::makeReadyFuture(ToolResult::error("Error: Command parameter is required.")); } if (command.length() > MAX_COMMAND_LENGTH) { LOG_MESSAGE(QString("ExecuteTerminalCommandTool: Command too long (%1 chars)") .arg(command.length())); return QtFuture::makeReadyFuture( - QString("Error: Command exceeds maximum length of %1 characters.") - .arg(MAX_COMMAND_LENGTH)); + ToolResult::error(QString("Error: Command exceeds maximum length of %1 characters.") + .arg(MAX_COMMAND_LENGTH))); } if (args.length() > MAX_ARGS_LENGTH) { LOG_MESSAGE(QString("ExecuteTerminalCommandTool: Arguments too long (%1 chars)") .arg(args.length())); return QtFuture::makeReadyFuture( - QString("Error: Arguments exceed maximum length of %1 characters.") - .arg(MAX_ARGS_LENGTH)); + ToolResult::error(QString("Error: Arguments exceed maximum length of %1 characters.") + .arg(MAX_ARGS_LENGTH))); } if (!isCommandAllowed(command)) { @@ -112,9 +114,9 @@ QFuture ExecuteTerminalCommandTool::executeAsync(const QJsonObject &inp const QStringList allowed = getAllowedCommands(); const QString allowedList = allowed.isEmpty() ? "none" : allowed.join(", "); return QtFuture::makeReadyFuture( - QString("Error: Command '%1' is not in the allowed list. Allowed commands: %2") + ToolResult::error(QString("Error: Command '%1' is not in the allowed list. Allowed commands: %2") .arg(command) - .arg(allowedList)); + .arg(allowedList))); } if (!isCommandSafe(command)) { @@ -127,18 +129,18 @@ QFuture ExecuteTerminalCommandTool::executeAsync(const QJsonObject &inp const QString allowedChars = "alphanumeric characters, hyphens, underscores, dots, and slashes"; #endif return QtFuture::makeReadyFuture( - QString("Error: Command '%1' contains potentially dangerous characters. " + ToolResult::error(QString("Error: Command '%1' contains potentially dangerous characters. " "Only %2 are allowed.") .arg(command) - .arg(allowedChars)); + .arg(allowedChars))); } if (!args.isEmpty() && !areArgumentsSafe(args)) { LOG_MESSAGE(QString("ExecuteTerminalCommandTool: Arguments contain unsafe patterns: '%1'") .arg(args)); return QtFuture::makeReadyFuture( - QString("Error: Arguments contain potentially dangerous patterns (command chaining, " - "redirection, or pipe operators).")); + ToolResult::error(QString("Error: Arguments contain potentially dangerous patterns (command chaining, " + "redirection, or pipe operators)."))); } auto *project = ProjectExplorer::ProjectManager::startupProject(); @@ -159,8 +161,8 @@ QFuture ExecuteTerminalCommandTool::executeAsync(const QJsonObject &inp QString("ExecuteTerminalCommandTool: Working directory '%1' is not accessible") .arg(workingDir)); return QtFuture::makeReadyFuture( - QString("Error: Working directory '%1' does not exist or is not accessible.") - .arg(workingDir)); + ToolResult::error(QString("Error: Working directory '%1' does not exist or is not accessible.") + .arg(workingDir))); } LOG_MESSAGE(QString("ExecuteTerminalCommandTool: Executing command '%1' with args '%2' in '%3'") @@ -168,8 +170,8 @@ QFuture ExecuteTerminalCommandTool::executeAsync(const QJsonObject &inp .arg(args.isEmpty() ? "(no args)" : args) .arg(workingDir)); - auto promise = QSharedPointer>::create(); - QFuture future = promise->future(); + auto promise = QSharedPointer>::create(); + QFuture future = promise->future(); promise->start(); auto resolved = std::make_shared>(false); @@ -206,11 +208,11 @@ QFuture ExecuteTerminalCommandTool::executeAsync(const QJsonObject &inp process->deleteLater(); }); - promise->addResult(QString("Error: Command '%1 %2' timed out after %3 seconds. " + promise->addResult(ToolResult::error(QString("Error: Command '%1 %2' timed out after %3 seconds. " "The process has been terminated.") .arg(command) .arg(args.isEmpty() ? "" : args) - .arg(timeoutMs / 1000)); + .arg(timeoutMs / 1000))); promise->finish(); timeoutTimer->deleteLater(); }); @@ -241,21 +243,21 @@ QFuture ExecuteTerminalCommandTool::executeAsync(const QJsonObject &inp "successfully (output size: %2 bytes)") .arg(fullCommand) .arg(outputSize)); - promise->addResult( + promise->addResult(ToolResult::text( QString("Command '%1' executed successfully.\n\nOutput:\n%2") .arg(fullCommand) - .arg(output.isEmpty() ? "(no output)" : output)); + .arg(output.isEmpty() ? "(no output)" : output))); } else { LOG_MESSAGE(QString("ExecuteTerminalCommandTool: Command '%1' failed with " "exit code %2 (output size: %3 bytes)") .arg(fullCommand) .arg(exitCode) .arg(outputSize)); - promise->addResult( + promise->addResult(ToolResult::error( QString("Command '%1' failed with exit code %2.\n\nOutput:\n%3") .arg(fullCommand) .arg(exitCode) - .arg(output.isEmpty() ? "(no output)" : output)); + .arg(output.isEmpty() ? "(no output)" : output))); } } else { LOG_MESSAGE(QString("ExecuteTerminalCommandTool: Command '%1' crashed or was " @@ -263,11 +265,11 @@ QFuture ExecuteTerminalCommandTool::executeAsync(const QJsonObject &inp .arg(fullCommand) .arg(outputSize)); const QString error = process->errorString(); - promise->addResult( + promise->addResult(ToolResult::error( QString("Command '%1' crashed or was terminated.\n\nError: %2\n\nOutput:\n%3") .arg(fullCommand) .arg(error) - .arg(output.isEmpty() ? "(no output)" : output)); + .arg(output.isEmpty() ? "(no output)" : output))); } promise->finish(); @@ -317,7 +319,7 @@ QFuture ExecuteTerminalCommandTool::executeAsync(const QJsonObject &inp break; } - promise->addResult(QString("Error: %1").arg(errorMessage)); + promise->addResult(ToolResult::error(QString("Error: %1").arg(errorMessage))); promise->finish(); process->deleteLater(); }); diff --git a/tools/ExecuteTerminalCommandTool.hpp b/tools/ExecuteTerminalCommandTool.hpp index e497b23..aefba3e 100644 --- a/tools/ExecuteTerminalCommandTool.hpp +++ b/tools/ExecuteTerminalCommandTool.hpp @@ -19,12 +19,12 @@ #pragma once -#include +#include #include namespace QodeAssist::Tools { -class ExecuteTerminalCommandTool : public ::LLMCore::BaseTool +class ExecuteTerminalCommandTool : public ::LLMQore::BaseTool { Q_OBJECT public: @@ -35,7 +35,7 @@ public: QString description() const override; QJsonObject parametersSchema() const override; - QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; + QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; private: bool isCommandAllowed(const QString &command) const; diff --git a/tools/FindAndReadFileTool.cpp b/tools/FindAndReadFileTool.cpp index 1493ac0..7377684 100644 --- a/tools/FindAndReadFileTool.cpp +++ b/tools/FindAndReadFileTool.cpp @@ -18,7 +18,8 @@ */ #include "FindAndReadFileTool.hpp" -#include "ToolExceptions.hpp" + +#include #include #include @@ -71,12 +72,12 @@ QJsonObject FindAndReadFileTool::parametersSchema() const return definition; } -QFuture FindAndReadFileTool::executeAsync(const QJsonObject &input) +QFuture FindAndReadFileTool::executeAsync(const QJsonObject &input) { - return QtConcurrent::run([this, input]() -> QString { + return QtConcurrent::run([this, input]() -> LLMQore::ToolResult { QString query = input["query"].toString().trimmed(); if (query.isEmpty()) { - throw ToolInvalidArgument("Query parameter is required"); + throw LLMQore::ToolInvalidArgument("Query parameter is required"); } QString filePattern = input["file_pattern"].toString(); @@ -90,7 +91,7 @@ QFuture FindAndReadFileTool::executeAsync(const QJsonObject &input) query, filePattern, 10, m_ignoreManager); if (bestMatch.absolutePath.isEmpty()) { - return QString("No file found matching '%1'").arg(query); + return LLMQore::ToolResult::text(QString("No file found matching '%1'").arg(query)); } if (readContent) { @@ -100,7 +101,7 @@ QFuture FindAndReadFileTool::executeAsync(const QJsonObject &input) } } - return formatResult(bestMatch, readContent); + return LLMQore::ToolResult::text(formatResult(bestMatch, readContent)); }); } diff --git a/tools/FindAndReadFileTool.hpp b/tools/FindAndReadFileTool.hpp index dc22cc5..f58d92e 100644 --- a/tools/FindAndReadFileTool.hpp +++ b/tools/FindAndReadFileTool.hpp @@ -22,14 +22,14 @@ #include "FileSearchUtils.hpp" #include -#include +#include #include #include #include namespace QodeAssist::Tools { -class FindAndReadFileTool : public ::LLMCore::BaseTool +class FindAndReadFileTool : public ::LLMQore::BaseTool { Q_OBJECT @@ -40,7 +40,7 @@ public: QString displayName() const override; QString description() const override; QJsonObject parametersSchema() const override; - QFuture executeAsync(const QJsonObject &input) override; + QFuture executeAsync(const QJsonObject &input) override; private: QString formatResult(const FileSearchUtils::FileMatch &match, bool readContent) const; diff --git a/tools/GetIssuesListTool.cpp b/tools/GetIssuesListTool.cpp index 70f2700..30506f6 100644 --- a/tools/GetIssuesListTool.cpp +++ b/tools/GetIssuesListTool.cpp @@ -155,16 +155,16 @@ QJsonObject GetIssuesListTool::parametersSchema() const return definition; } -QFuture GetIssuesListTool::executeAsync(const QJsonObject &input) +QFuture GetIssuesListTool::executeAsync(const QJsonObject &input) { - return QtConcurrent::run([input]() -> QString { + return QtConcurrent::run([input]() -> LLMQore::ToolResult { QString severityFilter = input.value("severity").toString("all"); const auto tasks = IssuesTracker::instance().getTasks(); if (tasks.isEmpty()) { - return "No issues found in Qt Creator Issues panel."; + return LLMQore::ToolResult::text("No issues found in Qt Creator Issues panel."); } QStringList results; @@ -235,7 +235,7 @@ QFuture GetIssuesListTool::executeAsync(const QJsonObject &input) .arg(processedCount); results.prepend(summary); - return results.join("\n\n"); + return LLMQore::ToolResult::text(results.join("\n\n")); }); } diff --git a/tools/GetIssuesListTool.hpp b/tools/GetIssuesListTool.hpp index a56941b..8ec1989 100644 --- a/tools/GetIssuesListTool.hpp +++ b/tools/GetIssuesListTool.hpp @@ -19,7 +19,7 @@ #pragma once -#include +#include #include #include #include @@ -46,7 +46,7 @@ private: mutable QMutex m_mutex; }; -class GetIssuesListTool : public ::LLMCore::BaseTool +class GetIssuesListTool : public ::LLMQore::BaseTool { Q_OBJECT public: @@ -57,7 +57,7 @@ public: QString description() const override; QJsonObject parametersSchema() const override; - QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; + QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; }; } // namespace QodeAssist::Tools diff --git a/tools/ListProjectFilesTool.cpp b/tools/ListProjectFilesTool.cpp index c91f9e0..a7722b7 100644 --- a/tools/ListProjectFilesTool.cpp +++ b/tools/ListProjectFilesTool.cpp @@ -18,7 +18,8 @@ */ #include "ListProjectFilesTool.hpp" -#include "ToolExceptions.hpp" + +#include #include #include @@ -63,15 +64,15 @@ QJsonObject ListProjectFilesTool::parametersSchema() const return definition; } -QFuture ListProjectFilesTool::executeAsync(const QJsonObject &input) +QFuture ListProjectFilesTool::executeAsync(const QJsonObject &input) { Q_UNUSED(input) - return QtConcurrent::run([this]() -> QString { + return QtConcurrent::run([this]() -> LLMQore::ToolResult { QList projects = ProjectExplorer::ProjectManager::projects(); if (projects.isEmpty()) { QString error = "No projects found"; - throw ToolRuntimeError(error); + throw LLMQore::ToolRuntimeError(error); } QString result; @@ -123,7 +124,7 @@ QFuture ListProjectFilesTool::executeAsync(const QJsonObject &input) result += "\n"; } - return result.trimmed(); + return LLMQore::ToolResult::text(result.trimmed()); }); } diff --git a/tools/ListProjectFilesTool.hpp b/tools/ListProjectFilesTool.hpp index 58a219f..5f3fbf1 100644 --- a/tools/ListProjectFilesTool.hpp +++ b/tools/ListProjectFilesTool.hpp @@ -19,13 +19,13 @@ #pragma once -#include +#include #include namespace QodeAssist::Tools { -class ListProjectFilesTool : public ::LLMCore::BaseTool +class ListProjectFilesTool : public ::LLMQore::BaseTool { Q_OBJECT public: @@ -36,7 +36,7 @@ public: QString description() const override; QJsonObject parametersSchema() const override; - QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; + QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; private: QString formatFileList(const QStringList &files) const; diff --git a/tools/ProjectSearchTool.cpp b/tools/ProjectSearchTool.cpp index 49420a9..f7187f7 100644 --- a/tools/ProjectSearchTool.cpp +++ b/tools/ProjectSearchTool.cpp @@ -18,7 +18,8 @@ */ #include "ProjectSearchTool.hpp" -#include "ToolExceptions.hpp" + +#include #include #include @@ -97,17 +98,17 @@ QJsonObject ProjectSearchTool::parametersSchema() const return definition; } -QFuture ProjectSearchTool::executeAsync(const QJsonObject &input) +QFuture ProjectSearchTool::executeAsync(const QJsonObject &input) { - return QtConcurrent::run([this, input]() -> QString { + return QtConcurrent::run([this, input]() -> LLMQore::ToolResult { QString query = input["query"].toString().trimmed(); if (query.isEmpty()) { - throw ToolInvalidArgument("Query parameter is required"); + throw LLMQore::ToolInvalidArgument("Query parameter is required"); } QString searchTypeStr = input["search_type"].toString(); if (searchTypeStr != "text" && searchTypeStr != "symbol") { - throw ToolInvalidArgument("search_type must be 'text' or 'symbol'"); + throw LLMQore::ToolInvalidArgument("search_type must be 'text' or 'symbol'"); } SearchType searchType = (searchTypeStr == "symbol") ? SearchType::Symbol : SearchType::Text; @@ -129,10 +130,10 @@ QFuture ProjectSearchTool::executeAsync(const QJsonObject &input) } if (results.isEmpty()) { - return QString("No matches found for '%1'").arg(query); + return LLMQore::ToolResult::text(QString("No matches found for '%1'").arg(query)); } - return formatResults(results, query); + return LLMQore::ToolResult::text(formatResults(results, query)); }); } diff --git a/tools/ProjectSearchTool.hpp b/tools/ProjectSearchTool.hpp index a4b09fd..62f0d69 100644 --- a/tools/ProjectSearchTool.hpp +++ b/tools/ProjectSearchTool.hpp @@ -20,14 +20,14 @@ #pragma once #include -#include +#include #include #include #include namespace QodeAssist::Tools { -class ProjectSearchTool : public ::LLMCore::BaseTool +class ProjectSearchTool : public ::LLMQore::BaseTool { Q_OBJECT @@ -38,7 +38,7 @@ public: QString displayName() const override; QString description() const override; QJsonObject parametersSchema() const override; - QFuture executeAsync(const QJsonObject &input) override; + QFuture executeAsync(const QJsonObject &input) override; private: enum class SearchType { Text, Symbol }; diff --git a/tools/TodoTool.cpp b/tools/TodoTool.cpp index cbea19b..7ddc515 100644 --- a/tools/TodoTool.cpp +++ b/tools/TodoTool.cpp @@ -18,7 +18,8 @@ */ #include "TodoTool.hpp" -#include "ToolExceptions.hpp" + +#include #include #include @@ -100,9 +101,9 @@ QJsonObject TodoTool::parametersSchema() const return definition; } -QFuture TodoTool::executeAsync(const QJsonObject &input) +QFuture TodoTool::executeAsync(const QJsonObject &input) { - return QtConcurrent::run([this, input]() -> QString { + return QtConcurrent::run([this, input]() -> LLMQore::ToolResult { QMutexLocker sessionLocker(&m_mutex); QString sessionId = m_currentSessionId.isEmpty() ? "current" : m_currentSessionId; sessionLocker.unlock(); @@ -111,14 +112,14 @@ QFuture TodoTool::executeAsync(const QJsonObject &input) if (operation == "add") { if (!input.contains("tasks") || !input.value("tasks").isArray()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( tr("Error: 'tasks' parameter (array) is required for 'add' operation. " "Example: {\"operation\": \"add\", \"tasks\": [\"Task 1\", \"Task 2\"]}")); } const QJsonArray tasksArray = input.value("tasks").toArray(); if (tasksArray.isEmpty()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( tr("Error: 'tasks' array cannot be empty. Provide at least one task.")); } @@ -131,22 +132,22 @@ QFuture TodoTool::executeAsync(const QJsonObject &input) } if (tasks.isEmpty()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( tr("Error: All tasks in 'tasks' array are empty strings.")); } - return addTodos(sessionId, tasks); + return LLMQore::ToolResult::text(addTodos(sessionId, tasks)); } else if (operation == "complete") { if (!input.contains("todo_ids") || !input.value("todo_ids").isArray()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( tr("Error: 'todo_ids' parameter (array) is required for 'complete' operation. " "Example: {\"operation\": \"complete\", \"todo_ids\": [1, 2, 3]}")); } const QJsonArray idsArray = input.value("todo_ids").toArray(); if (idsArray.isEmpty()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( tr("Error: 'todo_ids' array cannot be empty. Provide at least one ID.")); } @@ -159,18 +160,18 @@ QFuture TodoTool::executeAsync(const QJsonObject &input) } if (ids.isEmpty()) { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( tr("Error: All IDs in 'todo_ids' array are invalid. IDs must be positive " "integers.")); } - return completeTodos(sessionId, ids); + return LLMQore::ToolResult::text(completeTodos(sessionId, ids)); } else if (operation == "list") { - return listTodos(sessionId); + return LLMQore::ToolResult::text(listTodos(sessionId)); } else { - throw ToolRuntimeError( + throw LLMQore::ToolRuntimeError( tr("Error: Unknown operation '%1'. Valid operations: 'add', 'complete', 'list'") .arg(operation)); } @@ -215,7 +216,7 @@ QString TodoTool::completeTodos(const QString &sessionId, const QList &todo QMutexLocker locker(&m_mutex); if (!m_sessionTodos.contains(sessionId)) { - throw ToolRuntimeError(tr("Error: No todos found in this session")); + throw LLMQore::ToolRuntimeError(tr("Error: No todos found in this session")); } auto &todos = m_sessionTodos[sessionId]; diff --git a/tools/TodoTool.hpp b/tools/TodoTool.hpp index 8c0ef34..e536842 100644 --- a/tools/TodoTool.hpp +++ b/tools/TodoTool.hpp @@ -19,7 +19,7 @@ #pragma once -#include +#include #include #include @@ -34,7 +34,7 @@ struct TodoItem bool completed; }; -class TodoTool : public ::LLMCore::BaseTool +class TodoTool : public ::LLMQore::BaseTool { Q_OBJECT @@ -46,7 +46,7 @@ public: QString description() const override; QJsonObject parametersSchema() const override; - QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; + QFuture executeAsync(const QJsonObject &input = QJsonObject()) override; void setCurrentSessionId(const QString &sessionId); void clearSession(const QString &sessionId); diff --git a/tools/ToolHandler.cpp b/tools/ToolHandler.cpp deleted file mode 100644 index ebc5bca..0000000 --- a/tools/ToolHandler.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#include "ToolHandler.hpp" -#include "ToolExceptions.hpp" - -#include -#include -#include - -#include "logger/Logger.hpp" - -namespace QodeAssist::Tools { - -ToolHandler::ToolHandler(QObject *parent) - : QObject(parent) -{} - -QFuture ToolHandler::executeToolAsync( - const QString &requestId, - const QString &toolId, - PluginLLMCore::BaseTool *tool, - const QJsonObject &input) -{ - if (!tool) { - return QtConcurrent::run([]() -> QString { throw std::runtime_error("Tool is null"); }); - } - - auto execution = std::make_unique(); - execution->requestId = requestId; - execution->toolId = toolId; - execution->toolName = tool->name(); - execution->watcher = new QFutureWatcher(this); - - connect(execution->watcher, &QFutureWatcher::finished, this, [this, toolId]() { - onToolExecutionFinished(toolId); - }); - - LOG_MESSAGE(QString("Starting tool execution: %1 (ID: %2)").arg(tool->name(), toolId)); - - auto future = tool->executeAsync(input); - execution->watcher->setFuture(future); - m_activeExecutions.insert(toolId, execution.release()); - - return future; -} - -void ToolHandler::cleanupRequest(const QString &requestId) -{ - auto it = m_activeExecutions.begin(); - while (it != m_activeExecutions.end()) { - if (it.value()->requestId == requestId) { - auto execution = it.value(); - LOG_MESSAGE( - QString("Canceling tool %1 for request %2").arg(execution->toolName, requestId)); - - if (execution->watcher) { - execution->watcher->cancel(); - execution->watcher->deleteLater(); - } - delete execution; - it = m_activeExecutions.erase(it); - } else { - ++it; - } - } -} - -void ToolHandler::onToolExecutionFinished(const QString &toolId) -{ - if (!m_activeExecutions.contains(toolId)) { - return; - } - - auto execution = m_activeExecutions.take(toolId); - - try { - QString result = execution->watcher->result(); - LOG_MESSAGE(QString("Tool %1 completed").arg(execution->toolName)); - emit toolCompleted(execution->requestId, execution->toolId, result); - } catch (const ToolException &e) { - QString error = e.message(); - if (error.isEmpty()) { - error = "Tool execution failed with empty error message"; - } - LOG_MESSAGE(QString("Tool %1 failed: %2").arg(execution->toolName, error)); - emit toolFailed(execution->requestId, execution->toolId, error); - } catch (const QException &e) { - QString error = QString::fromUtf8(e.what()); - if (error.isEmpty()) { - error = "Tool execution failed (QException with empty message)"; - } - LOG_MESSAGE(QString("Tool %1 failed: %2").arg(execution->toolName, error)); - emit toolFailed(execution->requestId, execution->toolId, error); - } catch (const std::runtime_error &e) { - QString error = QString::fromStdString(e.what()); - if (error.isEmpty()) { - error = "Unknown runtime error occurred"; - } - LOG_MESSAGE(QString("Tool %1 failed: %2").arg(execution->toolName, error)); - emit toolFailed(execution->requestId, execution->toolId, error); - } catch (const std::invalid_argument &e) { - QString error = QString::fromStdString(e.what()); - if (error.isEmpty()) { - error = "Invalid argument provided"; - } - LOG_MESSAGE(QString("Tool %1 failed: %2").arg(execution->toolName, error)); - emit toolFailed(execution->requestId, execution->toolId, error); - } catch (const std::exception &e) { - QString error = QString::fromStdString(e.what()); - if (error.isEmpty()) { - error = "Unknown exception occurred"; - } - LOG_MESSAGE(QString("Tool %1 failed: %2").arg(execution->toolName, error)); - emit toolFailed(execution->requestId, execution->toolId, error); - } catch (...) { - QString error = "Unknown error occurred during tool execution"; - LOG_MESSAGE(QString("Tool %1 failed: %2").arg(execution->toolName, error)); - emit toolFailed(execution->requestId, execution->toolId, error); - } - - execution->watcher->deleteLater(); - delete execution; -} - -} // namespace QodeAssist::Tools diff --git a/tools/ToolHandler.hpp b/tools/ToolHandler.hpp deleted file mode 100644 index ba31f81..0000000 --- a/tools/ToolHandler.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include - -namespace QodeAssist::Tools { - -class ToolHandler : public QObject -{ - Q_OBJECT - -public: - explicit ToolHandler(QObject *parent = nullptr); - - QFuture executeToolAsync( - const QString &requestId, - const QString &toolId, - PluginLLMCore::BaseTool *tool, - const QJsonObject &input); - - void cleanupRequest(const QString &requestId); - -signals: - void toolCompleted(const QString &requestId, const QString &toolId, const QString &result); - void toolFailed(const QString &requestId, const QString &toolId, const QString &error); - -private: - struct ToolExecution - { - QString requestId; - QString toolId; - QString toolName; - QFutureWatcher *watcher; - }; - - QHash m_activeExecutions; // toolId -> execution - - void onToolExecutionFinished(const QString &toolId); -}; - -} // namespace QodeAssist::Tools diff --git a/tools/ToolsFactory.cpp b/tools/ToolsFactory.cpp deleted file mode 100644 index dabd424..0000000 --- a/tools/ToolsFactory.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#include "ToolsFactory.hpp" - -#include "logger/Logger.hpp" -#include -#include -#include -#include - -#include "BuildProjectTool.hpp" -#include "CreateNewFileTool.hpp" -#include "EditFileTool.hpp" -#include "ExecuteTerminalCommandTool.hpp" -#include "FindAndReadFileTool.hpp" -#include "GetIssuesListTool.hpp" -#include "ListProjectFilesTool.hpp" -#include "ProjectSearchTool.hpp" -#include "TodoTool.hpp" - -namespace QodeAssist::Tools { - -ToolsFactory::ToolsFactory(QObject *parent) - : QObject(parent) -{ - registerTools(); -} - -void ToolsFactory::registerTools() -{ - registerTool(new ListProjectFilesTool(this)); - registerTool(new GetIssuesListTool(this)); - registerTool(new CreateNewFileTool(this)); - registerTool(new EditFileTool(this)); - registerTool(new BuildProjectTool(this)); - registerTool(new ExecuteTerminalCommandTool(this)); - registerTool(new ProjectSearchTool(this)); - registerTool(new FindAndReadFileTool(this)); - registerTool(new TodoTool(this)); - - LOG_MESSAGE(QString("Registered %1 tools").arg(m_tools.size())); -} - -void ToolsFactory::registerTool(PluginLLMCore::BaseTool *tool) -{ - if (!tool) { - LOG_MESSAGE("Warning: Attempted to register null tool"); - return; - } - - const QString toolName = tool->name(); - if (m_tools.contains(toolName)) { - LOG_MESSAGE(QString("Warning: Tool '%1' already registered, replacing").arg(toolName)); - } - - m_tools.insert(toolName, tool); -} - -QList ToolsFactory::getAvailableTools() const -{ - return m_tools.values(); -} - -PluginLLMCore::BaseTool *ToolsFactory::getToolByName(const QString &name) const -{ - return m_tools.value(name, nullptr); -} - -QJsonArray ToolsFactory::getToolsDefinitions( - PluginLLMCore::ToolSchemaFormat format, PluginLLMCore::RunToolsFilter filter) const -{ - QJsonArray toolsArray; - const auto &settings = Settings::toolsSettings(); - - for (auto it = m_tools.constBegin(); it != m_tools.constEnd(); ++it) { - if (!it.value()) { - continue; - } - - if (it.value()->name() == "edit_file" && !settings.enableEditFileTool()) { - continue; - } - - if (it.value()->name() == "build_project" && !settings.enableBuildProjectTool()) { - continue; - } - - if (it.value()->name() == "execute_terminal_command" - && !settings.enableTerminalCommandTool()) { - continue; - } - - if (it.value()->name() == "todo_tool" && !settings.enableTodoTool()) { - continue; - } - - const auto requiredPerms = it.value()->requiredPermissions(); - - if (filter != PluginLLMCore::RunToolsFilter::ALL) { - bool matchesFilter = false; - - switch (filter) { - case PluginLLMCore::RunToolsFilter::OnlyRead: - if (requiredPerms == PluginLLMCore::ToolPermission::None - || requiredPerms.testFlag(PluginLLMCore::ToolPermission::FileSystemRead)) { - matchesFilter = true; - } - break; - - case PluginLLMCore::RunToolsFilter::OnlyWrite: - if (requiredPerms.testFlag(PluginLLMCore::ToolPermission::FileSystemWrite)) { - matchesFilter = true; - } - break; - - case PluginLLMCore::RunToolsFilter::OnlyNetworking: - if (requiredPerms.testFlag(PluginLLMCore::ToolPermission::NetworkAccess)) { - matchesFilter = true; - } - break; - - case PluginLLMCore::RunToolsFilter::ALL: - matchesFilter = true; - break; - } - - if (!matchesFilter) { - LOG_MESSAGE(QString("Tool '%1' skipped by tools filter") - .arg(it.value()->name())); - continue; - } - } - - bool hasPermission = true; - - if (requiredPerms.testFlag(PluginLLMCore::ToolPermission::FileSystemRead)) { - if (!settings.allowFileSystemRead()) { - hasPermission = false; - } - } - - if (requiredPerms.testFlag(PluginLLMCore::ToolPermission::FileSystemWrite)) { - if (!settings.allowFileSystemWrite()) { - hasPermission = false; - } - } - - if (requiredPerms.testFlag(PluginLLMCore::ToolPermission::NetworkAccess)) { - if (!settings.allowNetworkAccess()) { - hasPermission = false; - } - } - - if (hasPermission) { - toolsArray.append(it.value()->getDefinition(format)); - } else { - LOG_MESSAGE( - QString("Tool '%1' skipped due to missing permissions").arg(it.value()->name())); - } - } - - return toolsArray; -} - -QString ToolsFactory::getStringName(const QString &name) const -{ - return m_tools.contains(name) ? m_tools.value(name)->stringName() : QString("Unknown tools"); -} - -} // namespace QodeAssist::Tools diff --git a/tools/ToolsFactory.hpp b/tools/ToolsFactory.hpp deleted file mode 100644 index e14f7d9..0000000 --- a/tools/ToolsFactory.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#pragma once - -#include - -#include - -namespace QodeAssist::Tools { - -class ToolsFactory : public QObject -{ - Q_OBJECT -public: - ToolsFactory(QObject *parent = nullptr); - ~ToolsFactory() override = default; - - QList getAvailableTools() const; - PluginLLMCore::BaseTool *getToolByName(const QString &name) const; - QJsonArray getToolsDefinitions( - PluginLLMCore::ToolSchemaFormat format, - PluginLLMCore::RunToolsFilter filter = PluginLLMCore::RunToolsFilter::ALL) const; - QString getStringName(const QString &name) const; - -private: - void registerTools(); - void registerTool(PluginLLMCore::BaseTool *tool); - - QHash m_tools; -}; -} // namespace QodeAssist::Tools diff --git a/tools/ToolsManager.cpp b/tools/ToolsManager.cpp deleted file mode 100644 index 1edd39f..0000000 --- a/tools/ToolsManager.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#include "ToolsManager.hpp" -#include "TodoTool.hpp" -#include "logger/Logger.hpp" -#include - -namespace { -constexpr int kToolExecutionDelayMs = 300; -} - -namespace QodeAssist::Tools { - -ToolsManager::ToolsManager(QObject *parent) - : QObject(parent) - , m_toolsFactory(new ToolsFactory(this)) - , m_toolHandler(new ToolHandler(this)) -{ - connect( - m_toolHandler, - &ToolHandler::toolCompleted, - this, - [this](const QString &requestId, const QString &toolId, const QString &result) { - onToolFinished(requestId, toolId, result, true); - }); - - connect( - m_toolHandler, - &ToolHandler::toolFailed, - this, - [this](const QString &requestId, const QString &toolId, const QString &error) { - onToolFinished(requestId, toolId, error, false); - }); -} - -void ToolsManager::executeToolCall( - const QString &requestId, - const QString &toolId, - const QString &toolName, - const QJsonObject &input) -{ - LOG_MESSAGE(QString("ToolsManager: Queueing tool %1 (ID: %2) for request %3") - .arg(toolName, toolId, requestId)); - - if (!m_toolQueues.contains(requestId)) { - m_toolQueues[requestId] = ToolQueue(); - } - - auto &queue = m_toolQueues[requestId]; - - for (const auto &tool : queue.queue) { - if (tool.id == toolId) { - LOG_MESSAGE(QString("Tool %1 already in queue for request %2").arg(toolId, requestId)); - return; - } - } - - if (queue.completed.contains(toolId)) { - LOG_MESSAGE( - QString("Tool %1 already completed for request %2").arg(toolId, requestId)); - return; - } - - QJsonObject modifiedInput = input; - modifiedInput["_request_id"] = requestId; - - if (!m_currentSessionId.isEmpty()) { - modifiedInput["session_id"] = m_currentSessionId; - } - - PendingTool pendingTool{toolId, toolName, modifiedInput, "", false}; - queue.queue.append(pendingTool); - - LOG_MESSAGE(QString("ToolsManager: Tool %1 added to queue (position %2)") - .arg(toolName) - .arg(queue.queue.size())); - - if (!queue.isExecuting) { - executeNextTool(requestId); - } -} - -void ToolsManager::executeNextTool(const QString &requestId) -{ - if (!m_toolQueues.contains(requestId)) { - return; - } - - auto &queue = m_toolQueues[requestId]; - - if (queue.queue.isEmpty()) { - LOG_MESSAGE(QString("ToolsManager: All tools complete for request %1, emitting results") - .arg(requestId)); - QHash results = getToolResults(requestId); - emit toolExecutionComplete(requestId, results); - queue.isExecuting = false; - return; - } - - PendingTool tool = queue.queue.takeFirst(); - queue.isExecuting = true; - - LOG_MESSAGE(QString("ToolsManager: Executing tool %1 (ID: %2) for request %3 (%4 remaining)") - .arg(tool.name, tool.id, requestId) - .arg(queue.queue.size())); - - auto toolInstance = m_toolsFactory->getToolByName(tool.name); - if (!toolInstance) { - LOG_MESSAGE(QString("ToolsManager: Tool not found: %1").arg(tool.name)); - tool.result = QString("Error: Tool not found: %1").arg(tool.name); - tool.complete = true; - queue.completed[tool.id] = tool; - executeNextTool(requestId); - return; - } - - queue.completed[tool.id] = tool; - - m_toolHandler->executeToolAsync(requestId, tool.id, toolInstance, tool.input); - LOG_MESSAGE(QString("ToolsManager: Started async execution of %1").arg(tool.name)); -} - -QJsonArray ToolsManager::getToolsDefinitions( - PluginLLMCore::ToolSchemaFormat format, PluginLLMCore::RunToolsFilter filter) const -{ - if (!m_toolsFactory) { - return QJsonArray(); - } - - return m_toolsFactory->getToolsDefinitions(format, filter); -} - -void ToolsManager::cleanupRequest(const QString &requestId) -{ - if (m_toolQueues.contains(requestId)) { - m_toolHandler->cleanupRequest(requestId); - m_toolQueues.remove(requestId); - } -} - -void ToolsManager::onToolFinished( - const QString &requestId, const QString &toolId, const QString &result, bool success) -{ - if (!m_toolQueues.contains(requestId)) { - return; - } - - auto &queue = m_toolQueues[requestId]; - - if (!queue.completed.contains(toolId)) { - return; - } - - PendingTool &tool = queue.completed[toolId]; - tool.result = success ? result : QString("Error: %1").arg(result); - tool.complete = true; - - LOG_MESSAGE(QString("ToolsManager: Tool %1 %2 for request %3") - .arg(toolId) - .arg(success ? QString("completed") : QString("failed")) - .arg(requestId)); - - if (kToolExecutionDelayMs > 0 && !queue.queue.isEmpty()) { - QTimer::singleShot(kToolExecutionDelayMs, this, [this, requestId]() { - executeNextTool(requestId); - }); - } else { - executeNextTool(requestId); - } -} - -ToolsFactory *ToolsManager::toolsFactory() const -{ - return m_toolsFactory; -} - -QHash ToolsManager::getToolResults(const QString &requestId) const -{ - QHash results; - - if (m_toolQueues.contains(requestId)) { - const auto &queue = m_toolQueues[requestId]; - for (auto it = queue.completed.begin(); it != queue.completed.end(); ++it) { - if (it.value().complete) { - results[it.key()] = it.value().result; - } - } - } - - return results; -} - -void ToolsManager::clearTodoSession(const QString &sessionId) -{ - auto *todoTool = qobject_cast(m_toolsFactory->getToolByName("todo_tool")); - if (todoTool) { - todoTool->clearSession(sessionId); - } -} - -void ToolsManager::setCurrentSessionId(const QString &sessionId) -{ - m_currentSessionId = sessionId; -} - -} // namespace QodeAssist::Tools diff --git a/tools/ToolsManager.hpp b/tools/ToolsManager.hpp deleted file mode 100644 index a7d4f96..0000000 --- a/tools/ToolsManager.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2025 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -#pragma once - -#include -#include -#include -#include - -#include "ToolHandler.hpp" -#include "ToolsFactory.hpp" -#include -#include - -namespace QodeAssist::Tools { - -struct PendingTool -{ - QString id; - QString name; - QJsonObject input; - QString result; - bool complete = false; -}; - -struct ToolQueue -{ - QList queue; - QHash completed; - bool isExecuting = false; -}; - -class ToolsManager : public QObject, public PluginLLMCore::IToolsManager -{ - Q_OBJECT - -public: - explicit ToolsManager(QObject *parent = nullptr); - - void executeToolCall( - const QString &requestId, - const QString &toolId, - const QString &toolName, - const QJsonObject &input) override; - - QJsonArray getToolsDefinitions( - PluginLLMCore::ToolSchemaFormat format, - PluginLLMCore::RunToolsFilter filter = PluginLLMCore::RunToolsFilter::ALL) const override; - - void cleanupRequest(const QString &requestId) override; - void setCurrentSessionId(const QString &sessionId) override; - void clearTodoSession(const QString &sessionId) override; - - ToolsFactory *toolsFactory() const; - -signals: - void toolExecutionComplete(const QString &requestId, const QHash &toolResults); - -private slots: - void onToolFinished( - const QString &requestId, const QString &toolId, const QString &result, bool success); - -private: - ToolsFactory *m_toolsFactory; - ToolHandler *m_toolHandler; - QHash m_toolQueues; - QString m_currentSessionId; - - void executeNextTool(const QString &requestId); - QHash getToolResults(const QString &requestId) const; -}; - -} // namespace QodeAssist::Tools diff --git a/tools/ToolsRegistration.cpp b/tools/ToolsRegistration.cpp index 62c4267..91922a9 100644 --- a/tools/ToolsRegistration.cpp +++ b/tools/ToolsRegistration.cpp @@ -19,7 +19,7 @@ #include "ToolsRegistration.hpp" -#include +#include #include "BuildProjectTool.hpp" #include "CreateNewFileTool.hpp" @@ -33,7 +33,7 @@ namespace QodeAssist::Tools { -void registerQodeAssistTools(::LLMCore::ToolsManager *manager) +void registerQodeAssistTools(::LLMQore::ToolsManager *manager) { manager->addTool(new ListProjectFilesTool(manager)); manager->addTool(new GetIssuesListTool(manager)); diff --git a/tools/ToolsRegistration.hpp b/tools/ToolsRegistration.hpp index 617768f..a80c694 100644 --- a/tools/ToolsRegistration.hpp +++ b/tools/ToolsRegistration.hpp @@ -19,12 +19,12 @@ #pragma once -namespace LLMCore { +namespace LLMQore { class ToolsManager; } namespace QodeAssist::Tools { -void registerQodeAssistTools(::LLMCore::ToolsManager *manager); +void registerQodeAssistTools(::LLMQore::ToolsManager *manager); } // namespace QodeAssist::Tools diff --git a/widgets/QuickRefactorDialog.cpp b/widgets/QuickRefactorDialog.cpp index 7fb4811..0aeaa0f 100644 --- a/widgets/QuickRefactorDialog.cpp +++ b/widgets/QuickRefactorDialog.cpp @@ -640,8 +640,6 @@ void QuickRefactorDialog::onConfigurationChanged(int index) settings.qrModel.setValue(config.model); settings.qrTemplate.setValue(config.templateName); settings.qrUrl.setValue(config.url); - settings.qrEndpointMode.setValue( - settings.qrEndpointMode.indexForDisplay(config.endpointMode)); settings.qrCustomEndpoint.setValue(config.customEndpoint); settings.writeSettings();