mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-06-14 02:09:22 -04:00
refactor: Remove project rules
This commit is contained in:
@@ -55,6 +55,8 @@ Agent::Agent(AgentConfig config, Providers::Provider *providerOwned, QObject *pa
|
||||
return;
|
||||
}
|
||||
m_provider->setParent(this);
|
||||
m_provider->setPromptCaching(
|
||||
m_config.cachePrompt, m_config.cacheTtl == QLatin1StringView{"1h"});
|
||||
|
||||
QString tmplErr;
|
||||
m_promptTemplate = JsonPromptTemplate::fromConfig(m_config, &tmplErr);
|
||||
|
||||
@@ -39,6 +39,8 @@ struct AgentConfig
|
||||
|
||||
bool enableThinking = false;
|
||||
bool enableTools = false;
|
||||
bool cachePrompt = false;
|
||||
QString cacheTtl;
|
||||
|
||||
QJsonObject body;
|
||||
QString extendsName;
|
||||
|
||||
@@ -194,7 +194,7 @@ Agent *AgentFactory::createFromFile(
|
||||
{
|
||||
QString parseErr;
|
||||
QStringList warnings;
|
||||
auto cfgOpt = Agents::AgentLoader::parseFile(tomlPath, &parseErr, &warnings);
|
||||
auto cfgOpt = Agents::AgentLoader::parseFile(tomlPath, agentQrcPrefix(), &parseErr, &warnings);
|
||||
if (!cfgOpt) {
|
||||
if (errorOut) *errorOut = parseErr;
|
||||
return nullptr;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QHash>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
@@ -123,6 +124,8 @@ AgentConfig configFromMerged(const QJsonObject &obj)
|
||||
cfg.systemPrompt = obj.value("system_prompt").toString();
|
||||
cfg.enableThinking = obj.value("enable_thinking").toBool(false);
|
||||
cfg.enableTools = obj.value("enable_tools").toBool(false);
|
||||
cfg.cachePrompt = obj.value("cache_prompt").toBool(false);
|
||||
cfg.cacheTtl = obj.value("cache_ttl").toString();
|
||||
cfg.tags = stringArray(obj.value("tags"));
|
||||
|
||||
const QJsonObject matchObj = obj.value("match").toObject();
|
||||
@@ -147,6 +150,34 @@ struct RawEntry
|
||||
|
||||
constexpr int kMaxExtendsDepth = 32;
|
||||
|
||||
void scanDir(
|
||||
const QString &dir,
|
||||
bool isUserLayer,
|
||||
QHash<QString, RawEntry> &raw,
|
||||
QStringList &errors)
|
||||
{
|
||||
if (dir.isEmpty()) return;
|
||||
QDir d(dir);
|
||||
if (!d.exists()) return;
|
||||
const QStringList files = d.entryList({"*.toml"}, QDir::Files);
|
||||
for (const QString &fname : files) {
|
||||
const QString fullPath = d.filePath(fname);
|
||||
QString err;
|
||||
auto objOpt = parseTomlFile(fullPath, &err);
|
||||
if (!objOpt) {
|
||||
errors.append(err);
|
||||
continue;
|
||||
}
|
||||
const QString name = objOpt->value("name").toString();
|
||||
if (name.isEmpty()) {
|
||||
errors.append(QStringLiteral("Agent at %1 has no 'name'").arg(fullPath));
|
||||
continue;
|
||||
}
|
||||
const bool overrides = isUserLayer && raw.contains(name);
|
||||
raw.insert(name, {*objOpt, fullPath, overrides});
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject resolveExtends(
|
||||
const QString &name,
|
||||
const QHash<QString, RawEntry> &raw,
|
||||
@@ -190,12 +221,47 @@ QJsonObject resolveExtends(
|
||||
} // namespace
|
||||
|
||||
std::optional<AgentConfig> AgentLoader::parseFile(
|
||||
const QString &path, QString *error, QStringList * /*warnings*/)
|
||||
const QString &path,
|
||||
const QString &qrcPrefix,
|
||||
QString *error,
|
||||
QStringList * /*warnings*/)
|
||||
{
|
||||
auto objOpt = parseTomlFile(path, error);
|
||||
if (!objOpt) return std::nullopt;
|
||||
AgentConfig cfg = configFromMerged(*objOpt);
|
||||
|
||||
const QString name = objOpt->value("name").toString();
|
||||
if (name.isEmpty()) {
|
||||
if (error) *error = QStringLiteral("Agent at %1 has no 'name'").arg(path);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
QHash<QString, RawEntry> raw;
|
||||
QStringList scanErrors;
|
||||
scanDir(qrcPrefix, /*isUserLayer=*/false, raw, scanErrors);
|
||||
scanDir(QFileInfo(path).absolutePath(), /*isUserLayer=*/true, raw, scanErrors);
|
||||
raw.insert(name, {*objOpt, path, raw.contains(name)});
|
||||
|
||||
QSet<QString> visiting;
|
||||
QStringList resolveErrors;
|
||||
const QJsonObject merged = resolveExtends(name, raw, visiting, resolveErrors);
|
||||
if (!resolveErrors.isEmpty() || merged.isEmpty()) {
|
||||
if (error) {
|
||||
*error = resolveErrors.isEmpty()
|
||||
? QStringLiteral("Agent '%1' resolved to an empty config").arg(name)
|
||||
: resolveErrors.join(QStringLiteral("; "));
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
AgentConfig cfg = configFromMerged(merged);
|
||||
cfg.sourcePath = path;
|
||||
if (cfg.abstract) {
|
||||
if (error) {
|
||||
*error = QStringLiteral("Agent '%1' is abstract — extend it instead of "
|
||||
"loading it directly").arg(name);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@@ -204,31 +270,8 @@ AgentLoader::LoadResult AgentLoader::load(const QString &qrcPrefix, const QStrin
|
||||
LoadResult result;
|
||||
QHash<QString, RawEntry> raw;
|
||||
|
||||
auto scan = [&](const QString &dir, bool isUserLayer) {
|
||||
if (dir.isEmpty()) return;
|
||||
QDir d(dir);
|
||||
if (!d.exists()) return;
|
||||
const QStringList files = d.entryList({"*.toml"}, QDir::Files);
|
||||
for (const QString &fname : files) {
|
||||
const QString fullPath = d.filePath(fname);
|
||||
QString err;
|
||||
auto objOpt = parseTomlFile(fullPath, &err);
|
||||
if (!objOpt) {
|
||||
result.errors.append(err);
|
||||
continue;
|
||||
}
|
||||
const QString name = objOpt->value("name").toString();
|
||||
if (name.isEmpty()) {
|
||||
result.errors.append(QStringLiteral("Agent at %1 has no 'name'").arg(fullPath));
|
||||
continue;
|
||||
}
|
||||
const bool overrides = isUserLayer && raw.contains(name);
|
||||
raw.insert(name, {*objOpt, fullPath, overrides});
|
||||
}
|
||||
};
|
||||
|
||||
scan(qrcPrefix, /*isUserLayer=*/false);
|
||||
scan(userDir, /*isUserLayer=*/true);
|
||||
scanDir(qrcPrefix, /*isUserLayer=*/false, raw, result.errors);
|
||||
scanDir(userDir, /*isUserLayer=*/true, raw, result.errors);
|
||||
|
||||
for (auto it = raw.constBegin(); it != raw.constEnd(); ++it) {
|
||||
const QString &name = it.key();
|
||||
|
||||
@@ -25,7 +25,10 @@ public:
|
||||
static LoadResult load(const QString &qrcPrefix, const QString &userDir);
|
||||
|
||||
static std::optional<AgentConfig> parseFile(
|
||||
const QString &path, QString *error, QStringList *warnings = nullptr);
|
||||
const QString &path,
|
||||
const QString &qrcPrefix,
|
||||
QString *error,
|
||||
QStringList *warnings = nullptr);
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Agents
|
||||
|
||||
@@ -7,6 +7,7 @@ abstract = true
|
||||
provider_instance = "Claude"
|
||||
endpoint = "/v1/messages"
|
||||
enable_tools = true
|
||||
cache_prompt = true
|
||||
tags = ["chat", "claude", "anthropic", "cloud"]
|
||||
|
||||
system_prompt = """{{ agent_role() }}"""
|
||||
|
||||
@@ -3,8 +3,17 @@
|
||||
"role": {{ tojson(msg.role) }},
|
||||
"content": [
|
||||
{% for b in msg.content_blocks %}
|
||||
{% if b.type == "image" %}{% include "partials/anthropic_image.jinja" %}
|
||||
{% else %}{{ tojson(b) }},
|
||||
{% if b.type == "text" %}
|
||||
{ "type": "text", "text": {{ tojson(b.text) }} },
|
||||
{% else if b.type == "thinking" %}
|
||||
{ "type": "thinking", "thinking": {{ tojson(b.thinking) }}, "signature": {{ tojson(b.signature) }} },
|
||||
{% else if b.type == "redacted_thinking" %}
|
||||
{ "type": "redacted_thinking", "data": {{ tojson(b.data) }} },
|
||||
{% else if b.type == "tool_use" %}
|
||||
{ "type": "tool_use", "id": {{ tojson(b.id) }}, "name": {{ tojson(b.name) }}, "input": {{ tojson(b.input) }} },
|
||||
{% else if b.type == "tool_result" %}
|
||||
{ "type": "tool_result", "tool_use_id": {{ tojson(b.tool_use_id) }}, "content": {{ tojson(b.content) }} },
|
||||
{% else if b.type == "image" %}{% include "partials/anthropic_image.jinja" %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
]
|
||||
|
||||
@@ -2,15 +2,36 @@
|
||||
{ "role": "system", "content": {{ tojson(ctx.system_prompt) }} },
|
||||
{% endif %}
|
||||
{% for msg in ctx.history %}
|
||||
{
|
||||
"role": {{ tojson(msg.role) }},
|
||||
"content": {{ tojson(msg.content) }}
|
||||
{% if existsIn(msg, "images") %}
|
||||
, "images": [
|
||||
{% for img in msg.images %}
|
||||
{{ tojson(img.data) }},
|
||||
{% set tcalls = filter_by_type(msg.content_blocks, "tool_use") %}
|
||||
{% set tresults = filter_by_type(msg.content_blocks, "tool_result") %}
|
||||
{% if length(tresults) > 0 %}
|
||||
{% for b in tresults %}
|
||||
{
|
||||
"role": "tool",
|
||||
"content": {{ tojson(b.content) }}
|
||||
{% if b.name != "" %}
|
||||
, "tool_name": {{ tojson(b.name) }}
|
||||
{% endif %}
|
||||
},
|
||||
{% endfor %}
|
||||
]
|
||||
{% else %}
|
||||
{
|
||||
"role": {{ tojson(msg.role) }},
|
||||
"content": {{ tojson(msg.content) }}
|
||||
{% if length(tcalls) > 0 %}
|
||||
, "tool_calls": [
|
||||
{% for b in tcalls %}
|
||||
{ "type": "function", "function": { "name": {{ tojson(b.name) }}, "arguments": {{ tojson(b.input) }} } },
|
||||
{% endfor %}
|
||||
]
|
||||
{% endif %}
|
||||
{% if existsIn(msg, "images") %}
|
||||
, "images": [
|
||||
{% for img in msg.images %}
|
||||
{{ tojson(img.data) }},
|
||||
{% endfor %}
|
||||
]
|
||||
{% endif %}
|
||||
},
|
||||
{% endif %}
|
||||
},
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% set tcalls = filter_by_type(msg.content_blocks, "tool_use") %}
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": {{ tojson(msg.content) }}
|
||||
"content": {% if msg.content != "" %}{{ tojson(msg.content) }}{% else %}null{% endif %}
|
||||
{% if length(tcalls) > 0 %}
|
||||
, "tool_calls": [
|
||||
{% for b in tcalls %}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
[
|
||||
{ "type": "text", "text": {{ tojson(msg.content) }} }
|
||||
{% if msg.content != "" %}
|
||||
{ "type": "text", "text": {{ tojson(msg.content) }} },
|
||||
{% endif %}
|
||||
{% for img in msg.images %}
|
||||
,
|
||||
{% if img.is_url %}
|
||||
{ "type": "image_url", "image_url": { "url": {{ tojson(img.data) }} } }
|
||||
{ "type": "image_url", "image_url": { "url": {{ tojson(img.data) }} } },
|
||||
{% else %}
|
||||
{ "type": "image_url", "image_url": { "url": "data:{{ img.media_type }};base64,{{ img.data }}" } }
|
||||
{ "type": "image_url", "image_url": { "url": "data:{{ img.media_type }};base64,{{ img.data }}" } },
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user