mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-06-12 17:29:13 -04:00
Update LLMQore to v0.0.4 (#339)
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -78,3 +78,5 @@ CMakeLists.txt.user*
|
||||
/.cursor
|
||||
/.vscode
|
||||
.qtc_clangd/compile_commands.json
|
||||
CLAUDE.md
|
||||
/.claude
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -85,7 +85,7 @@ target_link_libraries(QodeAssistChatView
|
||||
Context
|
||||
QodeAssistUIControlsplugin
|
||||
QodeAssistLogger
|
||||
LLMCore
|
||||
LLMQore
|
||||
)
|
||||
|
||||
target_include_directories(QodeAssistChatView
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "ChatCompressor.hpp"
|
||||
|
||||
#include <LLMCore/BaseClient.hpp>
|
||||
#include <LLMQore/BaseClient.hpp>
|
||||
#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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "ClientInterface.hpp"
|
||||
|
||||
#include <LLMCore/BaseClient.hpp>
|
||||
#include <LLMQore/BaseClient.hpp>
|
||||
|
||||
#include <projectexplorer/buildconfiguration.h>
|
||||
#include <projectexplorer/target.h>
|
||||
@@ -42,7 +42,7 @@
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
|
||||
#include "tools/TodoTool.hpp"
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
#include "GeneralSettings.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "ProvidersManager.hpp"
|
||||
#include "RequestConfig.hpp"
|
||||
#include "ToolsSettings.hpp"
|
||||
#include <RulesLoader.hpp>
|
||||
#include <context/ChangesManager.h>
|
||||
@@ -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);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "LLMClientInterface.hpp"
|
||||
|
||||
#include <LLMCore/BaseClient.hpp>
|
||||
#include <LLMQore/BaseClient.hpp>
|
||||
#include <QJsonDocument>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "logger/Logger.hpp"
|
||||
#include "settings/CodeCompletionSettings.hpp"
|
||||
#include "settings/GeneralSettings.hpp"
|
||||
#include <pluginllmcore/RequestConfig.hpp>
|
||||
#include <pluginllmcore/RulesLoader.hpp>
|
||||
|
||||
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));
|
||||
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
|
||||
const RequestContext &ctx = it.value();
|
||||
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()
|
||||
const QString custom = isLanguageSpecify ? m_generalSettings.ccPreset1CustomEndpoint()
|
||||
: m_generalSettings.ccCustomEndpoint();
|
||||
} else if (endpointMode == "FIM") {
|
||||
endpoint = provider->completionEndpoint();
|
||||
} else if (endpointMode == "Chat") {
|
||||
endpoint = provider->chatEndpoint();
|
||||
}
|
||||
return endpoint;
|
||||
return !custom.isEmpty() ? custom : promptTemplate->endpoint();
|
||||
}
|
||||
|
||||
Context::ContextManager *LLMClientInterface::contextManager() const
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "QuickRefactorHandler.hpp"
|
||||
|
||||
#include <LLMCore/BaseClient.hpp>
|
||||
#include <LLMQore/BaseClient.hpp>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QUuid>
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <context/Utils.hpp>
|
||||
#include <pluginllmcore/PromptTemplateManager.hpp>
|
||||
#include <pluginllmcore/ProvidersManager.hpp>
|
||||
#include <pluginllmcore/RequestConfig.hpp>
|
||||
#include <pluginllmcore/RulesLoader.hpp>
|
||||
#include <logger/Logger.hpp>
|
||||
#include <settings/ChatAssistantSettings.hpp>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFuture>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
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<QString> 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
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
|
||||
@@ -19,8 +19,14 @@
|
||||
|
||||
#include "Provider.hpp"
|
||||
|
||||
#include <LLMCore/BaseClient.hpp>
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/BaseClient.hpp>
|
||||
#include <LLMQore/LlamaCppClient.hpp>
|
||||
#include <LLMQore/MistralClient.hpp>
|
||||
#include <LLMQore/OpenAIClient.hpp>
|
||||
#include <LLMQore/OpenAIResponsesClient.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <Logger.hpp>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,11 +26,10 @@
|
||||
#include <utils/environment.h>
|
||||
|
||||
#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
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PromptTemplate.hpp"
|
||||
#include "Provider.hpp"
|
||||
#include "RequestType.hpp"
|
||||
#include <QJsonObject>
|
||||
#include <QUrl>
|
||||
|
||||
namespace QodeAssist::PluginLLMCore {
|
||||
|
||||
struct LLMConfig
|
||||
{
|
||||
QUrl url;
|
||||
Provider *provider;
|
||||
PromptTemplate *promptTemplate;
|
||||
QJsonObject providerRequest;
|
||||
RequestType requestType;
|
||||
bool multiLineCompletion;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::PluginLLMCore
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "RequestHandlerBase.hpp"
|
||||
|
||||
namespace QodeAssist::LLMCore {
|
||||
|
||||
RequestHandlerBase::RequestHandlerBase(QObject *parent)
|
||||
: QObject(parent)
|
||||
{}
|
||||
|
||||
RequestHandlerBase::~RequestHandlerBase() = default;
|
||||
|
||||
} // namespace QodeAssist::LLMCore
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RequestConfig.hpp"
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
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
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
#include <LLMCore/ClaudeClient.hpp>
|
||||
#include <LLMQore/ClaudeClient.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "GoogleAIProvider.hpp"
|
||||
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
|
||||
#include <QJsonArray>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
#include <LLMCore/GoogleAIClient.hpp>
|
||||
#include <LLMQore/GoogleAIClient.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "LMStudioProvider.hpp"
|
||||
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
|
||||
#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<QList<QString>> 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;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/OpenAIClient.hpp>
|
||||
#include <LLMQore/OpenAIClient.hpp>
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "LlamaCppProvider.hpp"
|
||||
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
#include <LLMCore/LlamaCppClient.hpp>
|
||||
#include <LLMQore/LlamaCppClient.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "MistralAIProvider.hpp"
|
||||
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
#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<QList<QString>> 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;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/OpenAIClient.hpp>
|
||||
#include <LLMQore/MistralClient.hpp>
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "OllamaProvider.hpp"
|
||||
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
#include <LLMCore/OllamaClient.hpp>
|
||||
#include <LLMQore/OllamaClient.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "OpenAICompatProvider.hpp"
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/OpenAIClient.hpp>
|
||||
#include <LLMQore/OpenAIClient.hpp>
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "OpenAIProvider.hpp"
|
||||
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/OpenAIClient.hpp>
|
||||
#include <LLMQore/OpenAIClient.hpp>
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "OpenAIResponsesProvider.hpp"
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/OpenAIResponsesClient.hpp>
|
||||
#include <LLMQore/OpenAIResponsesClient.hpp>
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
add_library(QodeAssistSettings STATIC
|
||||
GeneralSettings.hpp GeneralSettings.cpp
|
||||
CustomPromptSettings.hpp CustomPromptSettings.cpp
|
||||
ConfigurationManager.hpp ConfigurationManager.cpp
|
||||
SettingsUtils.hpp
|
||||
SettingsConstants.hpp
|
||||
|
||||
@@ -54,7 +54,6 @@ QVector<AIConfiguration> 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<AIConfiguration> 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<AIConfiguration> 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<AIConfiguration> 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<AIConfiguration> 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<AIConfiguration> 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<AIConfiguration> 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CustomPromptSettings.hpp"
|
||||
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <utils/layoutbuilder.h>
|
||||
#include <QFileDialog>
|
||||
#include <QJsonParseError>
|
||||
#include <QMessageBox>
|
||||
|
||||
#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}}<fim_prefix>{{QODE_PREFIX}}<fim_suffix>{{QODE_SUFFIX}}<fim_middle>",
|
||||
"options": {
|
||||
"temperature": 0.7,
|
||||
"top_p": 0.95,
|
||||
"top_k": 40,
|
||||
"num_predict": 100,
|
||||
"stop": [
|
||||
"<|endoftext|>",
|
||||
"<file_sep>",
|
||||
"<fim_prefix>",
|
||||
"<fim_suffix>",
|
||||
"<fim_middle>"
|
||||
],
|
||||
"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
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utils/aspects.h>
|
||||
|
||||
#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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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");
|
||||
|
||||
1
sources/external/llmcore
vendored
1
sources/external/llmcore
vendored
Submodule sources/external/llmcore deleted from d1fb0dca95
1
sources/external/llmqore
vendored
Submodule
1
sources/external/llmqore
vendored
Submodule
Submodule sources/external/llmqore added at 55a4e293fe
@@ -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() << "<EOT>" << "<PRE>" << "<SUF" << "<MID>";
|
||||
|
||||
@@ -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() << "<SUF>" << "<PRE>" << "</PRE>" << "</SUF>" << "< EOT >" << "\\end"
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "llmcore/PromptTemplate.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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() << "<EOT>"; }
|
||||
void prepareRequest(QJsonObject &request, const PluginLLMCore::ContextData &context) const override
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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|>" << "<file_sep>" << "<fim_prefix>" << "<fim_suffix>"
|
||||
|
||||
@@ -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<Llama2>();
|
||||
templateManager.registerTemplate<Llama3>();
|
||||
templateManager.registerTemplate<StarCoder2Fim>();
|
||||
// templateManager.registerTemplate<DeepSeekCoderFim>();
|
||||
// templateManager.registerTemplate<CustomTemplate>();
|
||||
templateManager.registerTemplate<Qwen25CoderFIM>();
|
||||
templateManager.registerTemplate<Qwen3CoderFIM>();
|
||||
templateManager.registerTemplate<OpenAICompatible>();
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QSignalSpy>
|
||||
|
||||
#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 <coreplugin/editormanager/documentmodel.h>
|
||||
|
||||
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<QList<QString>> getInstalledModels(const QString &) override
|
||||
{
|
||||
return QtFuture::makeReadyFuture(QList<QString>{});
|
||||
}
|
||||
|
||||
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<MockProvider>();
|
||||
m_fimTemplate = std::make_unique<Templates::CodeLlamaQMLFim>();
|
||||
m_chatTemplate = std::make_unique<Templates::Claude>();
|
||||
|
||||
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<LLMClientInterface>(
|
||||
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<LLMClientInterface> m_client;
|
||||
std::unique_ptr<MockProvider> m_provider;
|
||||
std::unique_ptr<LLMCore::PromptTemplate> m_fimTemplate;
|
||||
std::unique_ptr<LLMCore::PromptTemplate> 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<LanguageServerProtocol::JsonRpcMessage>();
|
||||
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
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Povilas Kanapickas <povilas@radix.lt>
|
||||
*
|
||||
* This file is part of QodeAssist.
|
||||
*
|
||||
* QodeAssist is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QodeAssist is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <llmcore/RequestHandlerBase.hpp>
|
||||
|
||||
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<LLMConfig> &receivedRequests() const { return m_receivedRequests; }
|
||||
|
||||
private:
|
||||
QString m_fakeCompletion;
|
||||
QVector<LLMConfig> m_receivedRequests;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::LLMCore
|
||||
@@ -59,16 +59,16 @@ std::ostream &operator<<(std::ostream &out, const std::optional<T> &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
|
||||
|
||||
@@ -99,23 +99,23 @@ QJsonObject BuildProjectTool::parametersSchema() const
|
||||
return definition;
|
||||
}
|
||||
|
||||
QFuture<QString> BuildProjectTool::executeAsync(const QJsonObject &input)
|
||||
QFuture<LLMQore::ToolResult> 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<QString> BuildProjectTool::executeAsync(const QJsonObject &input)
|
||||
.arg(project->displayName())
|
||||
.arg(runAfterBuild ? QString(" (run after build)") : QString()));
|
||||
|
||||
auto promise = QSharedPointer<QPromise<QString>>::create();
|
||||
auto promise = QSharedPointer<QPromise<LLMQore::ToolResult>>::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();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/BaseTool.hpp>
|
||||
#include <LLMQore/BaseTool.hpp>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
@@ -34,7 +34,7 @@ namespace QodeAssist::Tools {
|
||||
|
||||
struct BuildInfo
|
||||
{
|
||||
QSharedPointer<QPromise<QString>> promise;
|
||||
QSharedPointer<QPromise<LLMQore::ToolResult>> promise;
|
||||
QPointer<ProjectExplorer::Project> 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<QString> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
QFuture<LLMQore::ToolResult> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
|
||||
private slots:
|
||||
void onBuildQueueFinished(bool success);
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
*/
|
||||
|
||||
#include "CreateNewFileTool.hpp"
|
||||
#include "ToolExceptions.hpp"
|
||||
|
||||
#include <LLMQore/ToolExceptions.hpp>
|
||||
|
||||
#include <context/ProjectUtils.hpp>
|
||||
#include <logger/Logger.hpp>
|
||||
@@ -73,13 +74,13 @@ QJsonObject CreateNewFileTool::parametersSchema() const
|
||||
return definition;
|
||||
}
|
||||
|
||||
QFuture<QString> CreateNewFileTool::executeAsync(const QJsonObject &input)
|
||||
QFuture<LLMQore::ToolResult> 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);
|
||||
@@ -90,7 +91,7 @@ QFuture<QString> CreateNewFileTool::executeAsync(const QJsonObject &input)
|
||||
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<QString> 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<QString> 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<QString> 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));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/BaseTool.hpp>
|
||||
#include <LLMQore/BaseTool.hpp>
|
||||
|
||||
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<QString> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
QFuture<LLMQore::ToolResult> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Tools
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
*/
|
||||
|
||||
#include "EditFileTool.hpp"
|
||||
#include "ToolExceptions.hpp"
|
||||
|
||||
#include <LLMQore/ToolExceptions.hpp>
|
||||
|
||||
#include <context/ChangesManager.h>
|
||||
#include <context/ProjectUtils.hpp>
|
||||
@@ -107,20 +108,20 @@ QJsonObject EditFileTool::parametersSchema() const
|
||||
return definition;
|
||||
}
|
||||
|
||||
QFuture<QString> EditFileTool::executeAsync(const QJsonObject &input)
|
||||
QFuture<LLMQore::ToolResult> 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<QString> 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<QString> 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<QString> 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<QString> EditFileTool::executeAsync(const QJsonObject &input)
|
||||
|
||||
QString resultStr = "QODEASSIST_FILE_EDIT:"
|
||||
+ QString::fromUtf8(QJsonDocument(result).toJson(QJsonDocument::Compact));
|
||||
return resultStr;
|
||||
return LLMQore::ToolResult::text(resultStr);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/BaseTool.hpp>
|
||||
#include <LLMQore/BaseTool.hpp>
|
||||
|
||||
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<QString> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
QFuture<LLMQore::ToolResult> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Tools
|
||||
|
||||
@@ -80,30 +80,32 @@ QJsonObject ExecuteTerminalCommandTool::parametersSchema() const
|
||||
return definition;
|
||||
}
|
||||
|
||||
QFuture<QString> ExecuteTerminalCommandTool::executeAsync(const QJsonObject &input)
|
||||
QFuture<LLMQore::ToolResult> 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<QString> 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<QString> 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<QString> 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<QString> ExecuteTerminalCommandTool::executeAsync(const QJsonObject &inp
|
||||
.arg(args.isEmpty() ? "(no args)" : args)
|
||||
.arg(workingDir));
|
||||
|
||||
auto promise = QSharedPointer<QPromise<QString>>::create();
|
||||
QFuture<QString> future = promise->future();
|
||||
auto promise = QSharedPointer<QPromise<ToolResult>>::create();
|
||||
QFuture<ToolResult> future = promise->future();
|
||||
promise->start();
|
||||
|
||||
auto resolved = std::make_shared<std::atomic<bool>>(false);
|
||||
@@ -206,11 +208,11 @@ QFuture<QString> 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<QString> 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<QString> 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<QString> 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();
|
||||
});
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/BaseTool.hpp>
|
||||
#include <LLMQore/BaseTool.hpp>
|
||||
#include <QObject>
|
||||
|
||||
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<QString> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
QFuture<LLMQore::ToolResult> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
|
||||
private:
|
||||
bool isCommandAllowed(const QString &command) const;
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
*/
|
||||
|
||||
#include "FindAndReadFileTool.hpp"
|
||||
#include "ToolExceptions.hpp"
|
||||
|
||||
#include <LLMQore/ToolExceptions.hpp>
|
||||
|
||||
#include <logger/Logger.hpp>
|
||||
#include <QJsonArray>
|
||||
@@ -71,12 +72,12 @@ QJsonObject FindAndReadFileTool::parametersSchema() const
|
||||
return definition;
|
||||
}
|
||||
|
||||
QFuture<QString> FindAndReadFileTool::executeAsync(const QJsonObject &input)
|
||||
QFuture<LLMQore::ToolResult> 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<QString> 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<QString> FindAndReadFileTool::executeAsync(const QJsonObject &input)
|
||||
}
|
||||
}
|
||||
|
||||
return formatResult(bestMatch, readContent);
|
||||
return LLMQore::ToolResult::text(formatResult(bestMatch, readContent));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
#include "FileSearchUtils.hpp"
|
||||
|
||||
#include <context/IgnoreManager.hpp>
|
||||
#include <LLMCore/BaseTool.hpp>
|
||||
#include <LLMQore/BaseTool.hpp>
|
||||
#include <QFuture>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
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<QString> executeAsync(const QJsonObject &input) override;
|
||||
QFuture<LLMQore::ToolResult> executeAsync(const QJsonObject &input) override;
|
||||
|
||||
private:
|
||||
QString formatResult(const FileSearchUtils::FileMatch &match, bool readContent) const;
|
||||
|
||||
@@ -155,16 +155,16 @@ QJsonObject GetIssuesListTool::parametersSchema() const
|
||||
return definition;
|
||||
}
|
||||
|
||||
QFuture<QString> GetIssuesListTool::executeAsync(const QJsonObject &input)
|
||||
QFuture<LLMQore::ToolResult> 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<QString> GetIssuesListTool::executeAsync(const QJsonObject &input)
|
||||
.arg(processedCount);
|
||||
results.prepend(summary);
|
||||
|
||||
return results.join("\n\n");
|
||||
return LLMQore::ToolResult::text(results.join("\n\n"));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/BaseTool.hpp>
|
||||
#include <LLMQore/BaseTool.hpp>
|
||||
#include <projectexplorer/task.h>
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
@@ -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<QString> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
QFuture<LLMQore::ToolResult> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Tools
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
*/
|
||||
|
||||
#include "ListProjectFilesTool.hpp"
|
||||
#include "ToolExceptions.hpp"
|
||||
|
||||
#include <LLMQore/ToolExceptions.hpp>
|
||||
|
||||
#include <logger/Logger.hpp>
|
||||
#include <projectexplorer/project.h>
|
||||
@@ -63,15 +64,15 @@ QJsonObject ListProjectFilesTool::parametersSchema() const
|
||||
return definition;
|
||||
}
|
||||
|
||||
QFuture<QString> ListProjectFilesTool::executeAsync(const QJsonObject &input)
|
||||
QFuture<LLMQore::ToolResult> ListProjectFilesTool::executeAsync(const QJsonObject &input)
|
||||
{
|
||||
Q_UNUSED(input)
|
||||
|
||||
return QtConcurrent::run([this]() -> QString {
|
||||
return QtConcurrent::run([this]() -> LLMQore::ToolResult {
|
||||
QList<ProjectExplorer::Project *> 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<QString> ListProjectFilesTool::executeAsync(const QJsonObject &input)
|
||||
result += "\n";
|
||||
}
|
||||
|
||||
return result.trimmed();
|
||||
return LLMQore::ToolResult::text(result.trimmed());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/BaseTool.hpp>
|
||||
#include <LLMQore/BaseTool.hpp>
|
||||
|
||||
#include <context/IgnoreManager.hpp>
|
||||
|
||||
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<QString> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
QFuture<LLMQore::ToolResult> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
|
||||
private:
|
||||
QString formatFileList(const QStringList &files) const;
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
*/
|
||||
|
||||
#include "ProjectSearchTool.hpp"
|
||||
#include "ToolExceptions.hpp"
|
||||
|
||||
#include <LLMQore/ToolExceptions.hpp>
|
||||
|
||||
#include <cplusplus/Overview.h>
|
||||
#include <cplusplus/Scope.h>
|
||||
@@ -97,17 +98,17 @@ QJsonObject ProjectSearchTool::parametersSchema() const
|
||||
return definition;
|
||||
}
|
||||
|
||||
QFuture<QString> ProjectSearchTool::executeAsync(const QJsonObject &input)
|
||||
QFuture<LLMQore::ToolResult> 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<QString> 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));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <context/IgnoreManager.hpp>
|
||||
#include <LLMCore/BaseTool.hpp>
|
||||
#include <LLMQore/BaseTool.hpp>
|
||||
#include <QFuture>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
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<QString> executeAsync(const QJsonObject &input) override;
|
||||
QFuture<LLMQore::ToolResult> executeAsync(const QJsonObject &input) override;
|
||||
|
||||
private:
|
||||
enum class SearchType { Text, Symbol };
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
*/
|
||||
|
||||
#include "TodoTool.hpp"
|
||||
#include "ToolExceptions.hpp"
|
||||
|
||||
#include <LLMQore/ToolExceptions.hpp>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
@@ -100,9 +101,9 @@ QJsonObject TodoTool::parametersSchema() const
|
||||
return definition;
|
||||
}
|
||||
|
||||
QFuture<QString> TodoTool::executeAsync(const QJsonObject &input)
|
||||
QFuture<LLMQore::ToolResult> 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<QString> 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<QString> 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<QString> 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<int> &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];
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMCore/BaseTool.hpp>
|
||||
#include <LLMQore/BaseTool.hpp>
|
||||
|
||||
#include <QHash>
|
||||
#include <QMutex>
|
||||
@@ -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<QString> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
QFuture<LLMQore::ToolResult> executeAsync(const QJsonObject &input = QJsonObject()) override;
|
||||
|
||||
void setCurrentSessionId(const QString &sessionId);
|
||||
void clearSession(const QString &sessionId);
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ToolHandler.hpp"
|
||||
#include "ToolExceptions.hpp"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QTimer>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
namespace QodeAssist::Tools {
|
||||
|
||||
ToolHandler::ToolHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
{}
|
||||
|
||||
QFuture<QString> 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<ToolExecution>();
|
||||
execution->requestId = requestId;
|
||||
execution->toolId = toolId;
|
||||
execution->toolName = tool->name();
|
||||
execution->watcher = new QFutureWatcher<QString>(this);
|
||||
|
||||
connect(execution->watcher, &QFutureWatcher<QString>::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
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QHash>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include <pluginllmcore/BaseTool.hpp>
|
||||
|
||||
namespace QodeAssist::Tools {
|
||||
|
||||
class ToolHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ToolHandler(QObject *parent = nullptr);
|
||||
|
||||
QFuture<QString> 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<QString> *watcher;
|
||||
};
|
||||
|
||||
QHash<QString, ToolExecution *> m_activeExecutions; // toolId -> execution
|
||||
|
||||
void onToolExecutionFinished(const QString &toolId);
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Tools
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ToolsFactory.hpp"
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
#include <settings/GeneralSettings.hpp>
|
||||
#include <settings/ToolsSettings.hpp>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#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<PluginLLMCore::BaseTool *> 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
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <pluginllmcore/BaseTool.hpp>
|
||||
|
||||
namespace QodeAssist::Tools {
|
||||
|
||||
class ToolsFactory : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ToolsFactory(QObject *parent = nullptr);
|
||||
~ToolsFactory() override = default;
|
||||
|
||||
QList<PluginLLMCore::BaseTool *> 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<QString, PluginLLMCore::BaseTool *> m_tools;
|
||||
};
|
||||
} // namespace QodeAssist::Tools
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ToolsManager.hpp"
|
||||
#include "TodoTool.hpp"
|
||||
#include "logger/Logger.hpp"
|
||||
#include <QTimer>
|
||||
|
||||
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<QString, QString> 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<QString, QString> ToolsManager::getToolResults(const QString &requestId) const
|
||||
{
|
||||
QHash<QString, QString> 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<TodoTool *>(m_toolsFactory->getToolByName("todo_tool"));
|
||||
if (todoTool) {
|
||||
todoTool->clearSession(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
void ToolsManager::setCurrentSessionId(const QString &sessionId)
|
||||
{
|
||||
m_currentSessionId = sessionId;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Tools
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
#include "ToolHandler.hpp"
|
||||
#include "ToolsFactory.hpp"
|
||||
#include <pluginllmcore/BaseTool.hpp>
|
||||
#include <pluginllmcore/IToolsManager.hpp>
|
||||
|
||||
namespace QodeAssist::Tools {
|
||||
|
||||
struct PendingTool
|
||||
{
|
||||
QString id;
|
||||
QString name;
|
||||
QJsonObject input;
|
||||
QString result;
|
||||
bool complete = false;
|
||||
};
|
||||
|
||||
struct ToolQueue
|
||||
{
|
||||
QList<PendingTool> queue;
|
||||
QHash<QString, PendingTool> 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<QString, QString> &toolResults);
|
||||
|
||||
private slots:
|
||||
void onToolFinished(
|
||||
const QString &requestId, const QString &toolId, const QString &result, bool success);
|
||||
|
||||
private:
|
||||
ToolsFactory *m_toolsFactory;
|
||||
ToolHandler *m_toolHandler;
|
||||
QHash<QString, ToolQueue> m_toolQueues;
|
||||
QString m_currentSessionId;
|
||||
|
||||
void executeNextTool(const QString &requestId);
|
||||
QHash<QString, QString> getToolResults(const QString &requestId) const;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Tools
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "ToolsRegistration.hpp"
|
||||
|
||||
#include <LLMCore/ToolsManager.hpp>
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
|
||||
#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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user