refactor: add to template agent roles

This commit is contained in:
Petr Mironychev
2026-06-04 16:21:34 +02:00
parent c151c5030b
commit 3179c0c358
113 changed files with 383 additions and 5292 deletions

View File

@@ -248,6 +248,15 @@ void AgentFactory::setModelOverride(const QString &agentName, const QString &mod
saveModelOverrides();
}
QString AgentFactory::effectiveModel(const QString &agentName) const
{
const QString ov = m_modelOverrides.value(agentName);
if (!ov.isEmpty())
return ov;
const AgentConfig *cfg = configByName(agentName);
return cfg ? cfg->model : QString();
}
namespace {
QString modelOverridesPath()
{

View File

@@ -59,6 +59,10 @@ public:
[[nodiscard]] QString modelOverride(const QString &agentName) const;
void setModelOverride(const QString &agentName, const QString &model);
// The model that will actually be sent for this agent: the settings
// override if set, otherwise the agent TOML's default `model`.
[[nodiscard]] QString effectiveModel(const QString &agentName) const;
[[nodiscard]] Providers::ProviderInstanceFactory *instanceFactory() const noexcept;
[[nodiscard]] Providers::ProviderSecretsStore *secretsStore() const noexcept;

View File

@@ -8,8 +8,12 @@
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QStringList>
#include <coreplugin/icore.h>
#include <filesystem>
#include <inja/inja.hpp>
@@ -155,6 +159,45 @@ void registerStringHelpers(inja::Environment &env)
});
}
// Read a role's system prompt from the role JSON written by the settings Roles
// UI (AgentRolesManager). Returns "" if the role doesn't exist.
std::string roleSystemPrompt(const QString &id)
{
if (id.isEmpty())
return {};
const QString path
= Core::ICore::userResourcePath(
QStringLiteral("qodeassist/agent_roles/%1.json").arg(id))
.toFSPathString();
QFile f(path);
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning("[QodeAssist] agent_role: role '%s' not found at %s",
qUtf8Printable(id), qUtf8Printable(path));
return {};
}
return QJsonDocument::fromJson(f.readAll())
.object()
.value("systemPrompt")
.toString()
.toStdString();
}
// Building blocks for composing a profile's `system_prompt` (alongside
// read_file/file_exists):
// {{ agent_role() }} — the runtime-selected role (Bindings.roleId, which
// the chat sets; falls back to "developer")
// {{ agent_role("<id>") }} — a specific role by id
void registerAgentRole(inja::Environment &env, const Bindings &b)
{
const QString runtimeRole = b.roleId.isEmpty() ? QStringLiteral("developer") : b.roleId;
env.add_callback("agent_role", 0, [runtimeRole](inja::Arguments &) -> nlohmann::json {
return roleSystemPrompt(runtimeRole);
});
env.add_callback("agent_role", 1, [](inja::Arguments &args) -> nlohmann::json {
return roleSystemPrompt(QString::fromStdString(args.at(0)->get<std::string>()));
});
}
void registerSandbox(inja::Environment &env)
{
@@ -183,6 +226,7 @@ QString render(const QString &templateSource, const Bindings &bindings, QString
registerFileExists(env, bindings);
registerReadDir(env, bindings);
registerStringHelpers(env);
registerAgentRole(env, bindings);
inja::Template tpl;
try {

View File

@@ -12,6 +12,9 @@ struct Bindings
{
QString projectDir;
QString homeDir;
// Role id selected at runtime (e.g. in the chat). Used by the no-arg
// `{{ agent_role() }}` template callback; empty falls back to "developer".
QString roleId;
};
QString render(const QString &templateSource, const Bindings &bindings,

View File

@@ -12,7 +12,6 @@
<file>partials/google_part.jinja</file>
<file>partials/ollama_messages.jinja</file>
<file>chat_base.toml</file>
<file>openai_base_chat.toml</file>
<file>openai_responses_base.toml</file>
<file>anthropic_base_chat.toml</file>

View File

@@ -3,16 +3,18 @@ schema_version = 1
name = "Anthropic Base Chat"
description = "Anthropic Messages API request body (/v1/messages). Abstract — extend it and set model."
abstract = true
extends = "Chat Base"
provider_instance = "Claude"
endpoint = "/v1/messages"
enable_tools = true
tags = ["chat", "claude", "anthropic", "cloud"]
system_prompt = """{{ agent_role() }}"""
[body]
max_tokens = 8192
temperature = 1
stream = true
system = """{% if existsIn(ctx, "system_prompt") %}{{ tojson(ctx.system_prompt) }}{% endif %}"""
messages = """
[ {% include "partials/anthropic_messages.jinja" %} ]

View File

@@ -1,16 +0,0 @@
schema_version = 1
name = "Chat Base"
description = "Shared system prompt for coding-chat agents. Abstract — not selectable."
abstract = true
system_prompt = """
You are a helpful coding assistant integrated into Qt Creator.
Answer concisely. Prefer concrete diffs or minimal patches over rewriting
whole files. Use markdown code blocks with language tags.
{% if file_exists("${PROJECT_DIR}/README.md") %}
## Project README.md
{{ read_file("${PROJECT_DIR}/README.md") }}
{% endif %}
"""

View File

@@ -3,12 +3,13 @@ schema_version = 1
name = "Google Base Chat"
description = "Google Gemini generateContent request body. Abstract — extend it and set model/endpoint."
abstract = true
extends = "Chat Base"
provider_instance = "Google AI"
enable_tools = true
tags = ["chat", "gemini", "google", "cloud"]
system_prompt = """{{ agent_role() }}"""
[body]
system_instruction = """{% if existsIn(ctx, "system_prompt") %}{ "parts": [ { "text": {{ tojson(ctx.system_prompt) }} } ] }{% endif %}"""
contents = """

View File

@@ -4,7 +4,7 @@ extends = "Google Base Chat"
name = "Gemini Chat"
description = "Google Gemini 2.5 Flash (generateContent API) — coding chat with thinking."
endpoint = "/models/gemini-2.5-flash:streamGenerateContent?alt=sse"
endpoint = "/models/${MODEL}:streamGenerateContent?alt=sse"
model = "gemini-2.5-flash"
enable_thinking = true

View File

@@ -3,12 +3,13 @@ schema_version = 1
name = "Ollama Base Chat"
description = "Ollama native /api/chat request body. Abstract — extend it and set model/options."
abstract = true
extends = "Chat Base"
provider_instance = "Ollama (Native)"
endpoint = "/api/chat"
tags = ["ollama", "local"]
system_prompt = """{{ agent_role() }}"""
[body]
stream = true
messages = """

View File

@@ -3,16 +3,18 @@ schema_version = 1
name = "OpenAI Base Chat"
description = "OpenAI Chat Completions request body. Abstract — extend it and set provider/endpoint/model."
abstract = true
extends = "Chat Base"
provider_instance = "OpenAI (Chat Completions)"
endpoint = "/chat/completions"
enable_tools = true
tags = ["chat", "openai", "cloud"]
system_prompt = """{{ agent_role() }}"""
[body]
max_tokens = 8192
temperature = 0.7
stream = true
messages = """
[ {% include "partials/openai_messages.jinja" %} ]
"""

View File

@@ -3,16 +3,18 @@ schema_version = 1
name = "OpenAI Responses Base"
description = "OpenAI Responses API request body (/responses). Abstract — extend it and set provider/endpoint/model."
abstract = true
extends = "Chat Base"
provider_instance = "OpenAI (Responses API)"
endpoint = "/responses"
enable_tools = true
tags = ["chat", "openai", "responses", "cloud"]
system_prompt = """{{ agent_role() }}"""
[body]
max_output_tokens = 8192
temperature = 0.7
stream = true
instructions = """{% if existsIn(ctx, "system_prompt") %}{{ tojson(ctx.system_prompt) }}{% endif %}"""
input = """
[ {% include "partials/openai_responses_input.jinja" %} ]