Files
z-bar-qt/Plugins/ZShell/Llm/filetool.cpp
T
zach 1fe8a3a959
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m35s
Rust / fmt (pull_request) Successful in 36s
Rust / clippy (pull_request) Successful in 54s
chore: restructure llm plugin to be more coherent & fix various bugs in llm ui
2026-10-18 12:58:38 +02:00

137 lines
3.9 KiB
C++

#include "filetool.hpp"
#include <QFile>
#include <QFileInfo>
#include <QJsonArray>
namespace ZShell::llm {
FileReadTool::FileReadTool(ToolRegistry* registry, QObject* parent)
: LlmTool(registry, parent) {}
QString FileReadTool::name() const {
return QStringLiteral("readfile");
}
QString FileReadTool::description() const {
return QStringLiteral(
"Read a text file from the local filesystem and return part of "
"its content. At most a limited number of characters is returned "
"per call; use offset and limit to page through larger files. "
"Binary files are not supported. Full output of webfetch calls "
"is saved in the sandbox directory and can be read this way. "
"This tool is read-only.");
}
QJsonObject FileReadTool::parameters() const {
QJsonObject path;
path[QStringLiteral("type")] = QStringLiteral("string");
path[QStringLiteral("description")] =
QStringLiteral("Path of the file to read");
QJsonObject offset;
offset[QStringLiteral("type")] = QStringLiteral("integer");
offset[QStringLiteral("minimum")] = 0;
offset[QStringLiteral("description")] =
QStringLiteral("Byte offset to start reading from. Defaults to 0.");
QJsonObject limit;
limit[QStringLiteral("type")] = QStringLiteral("integer");
limit[QStringLiteral("minimum")] = 1;
limit[QStringLiteral("description")] = QStringLiteral(
"Maximum number of characters to return. Defaults "
"to a value that fits the model's context window.");
QJsonObject properties;
properties[QStringLiteral("path")] = path;
properties[QStringLiteral("offset")] = offset;
properties[QStringLiteral("limit")] = limit;
QJsonObject schema;
schema[QStringLiteral("type")] = QStringLiteral("object");
schema[QStringLiteral("properties")] = properties;
QJsonArray required;
required.append(QStringLiteral("path"));
schema[QStringLiteral("required")] = required;
return schema;
}
void FileReadTool::execute(
const ToolContext& context, const QJsonObject& args, ToolDone done) {
const QString path = args[QStringLiteral("path")].toString().trimmed();
if (path.isEmpty()) {
done({false, QStringLiteral("Missing required argument 'path'")});
return;
}
const int offset = qMax(0, args[QStringLiteral("offset")].toInt(0));
const int budget = context.outputBudgetChars > 0
? context.outputBudgetChars
: registry()->outputBudgetChars(1);
const int limit =
qBound(1, args[QStringLiteral("limit")].toInt(budget), budget);
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
const QFileInfo info(path);
if (info.exists() && info.isDir()) {
done(
{false,
QStringLiteral("Path is a directory, not a file: %1")
.arg(path)});
} else {
done({false, QStringLiteral("Cannot open file: %1").arg(path)});
}
return;
}
const qint64 fileSize = file.size();
if (fileSize == 0) {
file.close();
done({true, QString()});
return;
}
if (offset >= fileSize) {
file.close();
done(
{false,
QStringLiteral("Offset %1 is beyond the end of the file (%2 bytes)")
.arg(offset)
.arg(fileSize)});
return;
}
if (!file.seek(offset)) {
file.close();
done({false, QStringLiteral("Cannot seek in file: %1").arg(path)});
return;
}
const qint64 wanted = qint64(limit) * 4 + 16;
const QByteArray chunk = file.read(qMin(wanted, fileSize - offset));
file.close();
if (chunk.contains('\0')) {
done(
{false,
QStringLiteral("Binary files are not supported: %1").arg(path)});
return;
}
const QString content = QString::fromUtf8(chunk);
if (content.size() <= limit) {
done({true, content});
return;
}
const QString head = content.left(limit);
const int nextOffset = offset + head.toUtf8().size();
const QString output =
head +
QStringLiteral(
"\n\n[... truncated: showing %1 characters (bytes %2-%3 of a "
"%4 byte file). Continue with offset=%5.]")
.arg(limit)
.arg(offset)
.arg(nextOffset)
.arg(fileSize)
.arg(nextOffset);
done({true, output});
}
} // namespace ZShell::llm