mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-05-30 02:49:12 -04:00
fix: Add handling final argument for OpenAI responses tool calling
This commit is contained in:
@@ -174,13 +174,20 @@ void ClientInterface::sendMessage(
|
|||||||
auto project = PluginLLMCore::RulesLoader::getActiveProject();
|
auto project = PluginLLMCore::RulesLoader::getActiveProject();
|
||||||
|
|
||||||
if (project) {
|
if (project) {
|
||||||
systemPrompt += QString("\n# Active project name: %1").arg(project->displayName());
|
systemPrompt += QString("\n# Active project: %1").arg(project->displayName());
|
||||||
systemPrompt += QString("\n# Active Project path: %1")
|
systemPrompt += QString(
|
||||||
|
"\n# Project source root: %1"
|
||||||
|
"\n# All new source files, headers, QML and CMake edits MUST be "
|
||||||
|
"created or modified under this directory. Use absolute paths "
|
||||||
|
"rooted here, or project-relative paths.")
|
||||||
.arg(project->projectDirectory().toUrlishString());
|
.arg(project->projectDirectory().toUrlishString());
|
||||||
|
|
||||||
if (auto target = project->activeTarget()) {
|
if (auto target = project->activeTarget()) {
|
||||||
if (auto buildConfig = target->activeBuildConfiguration()) {
|
if (auto buildConfig = target->activeBuildConfiguration()) {
|
||||||
systemPrompt += QString("\n# Active Build directory: %1")
|
systemPrompt
|
||||||
|
+= QString(
|
||||||
|
"\n# Build output directory (compiler artifacts only — do NOT "
|
||||||
|
"create or edit source files here): %1")
|
||||||
.arg(buildConfig->buildDirectory().toUrlishString());
|
.arg(buildConfig->buildDirectory().toUrlishString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,9 @@ ToolsSettings::ToolsSettings()
|
|||||||
Tr::tr("Comma-separated list of terminal commands that AI is allowed to execute on Linux. "
|
Tr::tr("Comma-separated list of terminal commands that AI is allowed to execute on Linux. "
|
||||||
"Example: git, ls, cat, grep, find, cmake"));
|
"Example: git, ls, cat, grep, find, cmake"));
|
||||||
allowedTerminalCommandsLinux.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
allowedTerminalCommandsLinux.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
allowedTerminalCommandsLinux.setDefaultValue("git, ls, cat, grep, find");
|
allowedTerminalCommandsLinux.setDefaultValue(
|
||||||
|
"git, ls, cat, grep, find, pwd, echo, head, tail, wc, which, file, stat, tree, uname, "
|
||||||
|
"date, whoami, hostname");
|
||||||
|
|
||||||
allowedTerminalCommandsMacOS.setSettingsKey(Constants::CA_ALLOWED_TERMINAL_COMMANDS_MACOS);
|
allowedTerminalCommandsMacOS.setSettingsKey(Constants::CA_ALLOWED_TERMINAL_COMMANDS_MACOS);
|
||||||
allowedTerminalCommandsMacOS.setLabelText(Tr::tr("Allowed Commands (macOS)"));
|
allowedTerminalCommandsMacOS.setLabelText(Tr::tr("Allowed Commands (macOS)"));
|
||||||
@@ -141,7 +143,9 @@ ToolsSettings::ToolsSettings()
|
|||||||
Tr::tr("Comma-separated list of terminal commands that AI is allowed to execute on macOS. "
|
Tr::tr("Comma-separated list of terminal commands that AI is allowed to execute on macOS. "
|
||||||
"Example: git, ls, cat, grep, find, cmake"));
|
"Example: git, ls, cat, grep, find, cmake"));
|
||||||
allowedTerminalCommandsMacOS.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
allowedTerminalCommandsMacOS.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
allowedTerminalCommandsMacOS.setDefaultValue("git, ls, cat, grep, find");
|
allowedTerminalCommandsMacOS.setDefaultValue(
|
||||||
|
"git, ls, cat, grep, find, pwd, echo, head, tail, wc, which, file, stat, tree, uname, "
|
||||||
|
"date, whoami, hostname");
|
||||||
|
|
||||||
allowedTerminalCommandsWindows.setSettingsKey(Constants::CA_ALLOWED_TERMINAL_COMMANDS_WINDOWS);
|
allowedTerminalCommandsWindows.setSettingsKey(Constants::CA_ALLOWED_TERMINAL_COMMANDS_WINDOWS);
|
||||||
allowedTerminalCommandsWindows.setLabelText(Tr::tr("Allowed Commands (Windows)"));
|
allowedTerminalCommandsWindows.setLabelText(Tr::tr("Allowed Commands (Windows)"));
|
||||||
@@ -149,7 +153,8 @@ ToolsSettings::ToolsSettings()
|
|||||||
Tr::tr("Comma-separated list of terminal commands that AI is allowed to execute on Windows. "
|
Tr::tr("Comma-separated list of terminal commands that AI is allowed to execute on Windows. "
|
||||||
"Example: git, dir, type, findstr, where, cmake"));
|
"Example: git, dir, type, findstr, where, cmake"));
|
||||||
allowedTerminalCommandsWindows.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
allowedTerminalCommandsWindows.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
allowedTerminalCommandsWindows.setDefaultValue("git, dir, type, findstr, where");
|
allowedTerminalCommandsWindows.setDefaultValue(
|
||||||
|
"git, dir, type, findstr, where, echo, whoami, hostname, ver, tree, fc");
|
||||||
|
|
||||||
terminalCommandTimeout.setSettingsKey(Constants::CA_TERMINAL_COMMAND_TIMEOUT);
|
terminalCommandTimeout.setSettingsKey(Constants::CA_TERMINAL_COMMAND_TIMEOUT);
|
||||||
terminalCommandTimeout.setLabelText(Tr::tr("Command Timeout (seconds)"));
|
terminalCommandTimeout.setLabelText(Tr::tr("Command Timeout (seconds)"));
|
||||||
|
|||||||
2
sources/external/llmqore
vendored
2
sources/external/llmqore
vendored
Submodule sources/external/llmqore updated: ddbc38ffbd...95a7b3ac51
@@ -77,10 +77,20 @@ QFuture<LLMQore::ToolResult> CreateNewFileTool::executeAsync(const QJsonObject &
|
|||||||
if (!isInProject) {
|
if (!isInProject) {
|
||||||
const auto &settings = Settings::toolsSettings();
|
const auto &settings = Settings::toolsSettings();
|
||||||
if (!settings.allowAccessOutsideProject()) {
|
if (!settings.allowAccessOutsideProject()) {
|
||||||
|
const QString projectRoot = Context::ProjectUtils::getProjectRoot();
|
||||||
|
const QString hint = projectRoot.isEmpty()
|
||||||
|
? QStringLiteral(
|
||||||
|
"No project is currently open. Open a project in Qt Creator or "
|
||||||
|
"enable 'Allow file access outside project' in QodeAssist settings.")
|
||||||
|
: QString(
|
||||||
|
"Retry with a path under the active project root: '%1'. The build "
|
||||||
|
"directory is for compiler output only and cannot accept new source "
|
||||||
|
"files. If you really need to write outside the project, enable "
|
||||||
|
"'Allow file access outside project' in QodeAssist settings.")
|
||||||
|
.arg(projectRoot);
|
||||||
throw LLMQore::ToolRuntimeError(
|
throw LLMQore::ToolRuntimeError(
|
||||||
QString("Error: File path '%1' is not within the current project. "
|
QString("Error: File path '%1' is not within the current project. %2")
|
||||||
"Enable 'Allow file access outside project' in settings to create files outside project scope.")
|
.arg(absolutePath, hint));
|
||||||
.arg(absolutePath));
|
|
||||||
}
|
}
|
||||||
LOG_MESSAGE(QString("Creating file outside project scope: %1").arg(absolutePath));
|
LOG_MESSAGE(QString("Creating file outside project scope: %1").arg(absolutePath));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,10 +143,20 @@ QFuture<LLMQore::ToolResult> EditFileTool::executeAsync(const QJsonObject &input
|
|||||||
if (!isInProject) {
|
if (!isInProject) {
|
||||||
const auto &settings = Settings::toolsSettings();
|
const auto &settings = Settings::toolsSettings();
|
||||||
if (!settings.allowAccessOutsideProject()) {
|
if (!settings.allowAccessOutsideProject()) {
|
||||||
|
const QString projectRoot = Context::ProjectUtils::getProjectRoot();
|
||||||
|
const QString hint = projectRoot.isEmpty()
|
||||||
|
? QStringLiteral(
|
||||||
|
"No project is currently open. Open a project in Qt Creator or "
|
||||||
|
"enable 'Allow file access outside project' in QodeAssist settings.")
|
||||||
|
: QString(
|
||||||
|
"Retry with a path under the active project root: '%1'. The build "
|
||||||
|
"directory is for compiler output only — source files must live under "
|
||||||
|
"the project root. If you really need to edit outside the project, "
|
||||||
|
"enable 'Allow file access outside project' in QodeAssist settings.")
|
||||||
|
.arg(projectRoot);
|
||||||
throw LLMQore::ToolRuntimeError(
|
throw LLMQore::ToolRuntimeError(
|
||||||
QString("File path '%1' is not within the current project. "
|
QString("File path '%1' is not within the current project. %2")
|
||||||
"Enable 'Allow file access outside project' in settings to edit files outside the project.")
|
.arg(filePath, hint));
|
||||||
.arg(filePath));
|
|
||||||
}
|
}
|
||||||
LOG_MESSAGE(QString("Editing file outside project scope: %1").arg(filePath));
|
LOG_MESSAGE(QString("Editing file outside project scope: %1").arg(filePath));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QPromise>
|
#include <QPromise>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
@@ -45,18 +46,26 @@ QJsonObject ExecuteTerminalCommandTool::parametersSchema() const
|
|||||||
QJsonObject definition;
|
QJsonObject definition;
|
||||||
definition["type"] = "object";
|
definition["type"] = "object";
|
||||||
|
|
||||||
const QString commandDesc = getCommandDescription();
|
const QStringList allowed = getAllowedCommands();
|
||||||
|
const QString allowedList = allowed.isEmpty() ? "none" : allowed.join(", ");
|
||||||
|
|
||||||
QJsonObject properties;
|
QJsonObject properties;
|
||||||
properties["command"] = QJsonObject{
|
properties["command"] = QJsonObject{
|
||||||
{"type", "string"},
|
{"type", "string"},
|
||||||
{"description", commandDesc}};
|
{"description",
|
||||||
|
QString("Name of the executable to run, WITHOUT any arguments or flags. "
|
||||||
|
"Must be exactly one of the allowed commands: %1. "
|
||||||
|
"Put every flag and argument in the separate `args` field. "
|
||||||
|
"Correct: command=\"ls\", args=\"-R\". "
|
||||||
|
"Incorrect: command=\"ls -R\" (the whole line in one field will be rejected).")
|
||||||
|
.arg(allowedList)}};
|
||||||
|
|
||||||
properties["args"] = QJsonObject{
|
properties["args"] = QJsonObject{
|
||||||
{"type", "string"},
|
{"type", "string"},
|
||||||
{"description",
|
{"description",
|
||||||
"Optional arguments for the command. Arguments with spaces should be properly quoted. "
|
"Optional arguments and flags for the command, as a single string. Do NOT repeat the "
|
||||||
"Example: '--file \"path with spaces.txt\" --verbose'"}};
|
"command name here. Arguments with spaces should be quoted. "
|
||||||
|
"Example: args=\"--file \\\"path with spaces.txt\\\" --verbose\"."}};
|
||||||
|
|
||||||
definition["properties"] = properties;
|
definition["properties"] = properties;
|
||||||
definition["required"] = QJsonArray{"command"};
|
definition["required"] = QJsonArray{"command"};
|
||||||
@@ -68,14 +77,25 @@ QFuture<LLMQore::ToolResult> ExecuteTerminalCommandTool::executeAsync(const QJso
|
|||||||
{
|
{
|
||||||
using LLMQore::ToolResult;
|
using LLMQore::ToolResult;
|
||||||
|
|
||||||
const QString command = input.value("command").toString().trimmed();
|
QString command = input.value("command").toString().trimmed();
|
||||||
const QString args = input.value("args").toString().trimmed();
|
QString args = input.value("args").toString().trimmed();
|
||||||
|
|
||||||
if (command.isEmpty()) {
|
if (command.isEmpty()) {
|
||||||
LOG_MESSAGE("ExecuteTerminalCommandTool: Command is empty");
|
LOG_MESSAGE("ExecuteTerminalCommandTool: Command is empty");
|
||||||
return QtFuture::makeReadyFuture(ToolResult::error("Error: Command parameter is required."));
|
return QtFuture::makeReadyFuture(ToolResult::error("Error: Command parameter is required."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tolerate models that pack the whole command line into `command`. As long as `args` is
|
||||||
|
// empty we can safely split on the first whitespace — the allowlist check still validates
|
||||||
|
// the actual executable name.
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
const int firstSpace = command.indexOf(QRegularExpression("\\s"));
|
||||||
|
if (firstSpace > 0) {
|
||||||
|
args = command.mid(firstSpace + 1).trimmed();
|
||||||
|
command = command.left(firstSpace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (command.length() > MAX_COMMAND_LENGTH) {
|
if (command.length() > MAX_COMMAND_LENGTH) {
|
||||||
LOG_MESSAGE(QString("ExecuteTerminalCommandTool: Command too long (%1 chars)")
|
LOG_MESSAGE(QString("ExecuteTerminalCommandTool: Command too long (%1 chars)")
|
||||||
.arg(command.length()));
|
.arg(command.length()));
|
||||||
|
|||||||
Reference in New Issue
Block a user