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:
@@ -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,24 +74,24 @@ 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);
|
||||
QString absolutePath = fileInfo.absoluteFilePath();
|
||||
|
||||
bool isInProject = Context::ProjectUtils::isFileInProject(absolutePath);
|
||||
|
||||
|
||||
if (!isInProject) {
|
||||
const auto &settings = Settings::toolsSettings();
|
||||
if (!settings.allowAccessOutsideProject()) {
|
||||
throw ToolRuntimeError(
|
||||
throw LLMQore::ToolRuntimeError(
|
||||
QString("Error: File path '%1' is not within the current project. "
|
||||
"Enable 'Allow file access outside project' in settings to create files outside project scope.")
|
||||
.arg(absolutePath));
|
||||
@@ -99,14 +100,14 @@ QFuture<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
|
||||
|
||||
Reference in New Issue
Block a user