refactor: Finalize agent template

This commit is contained in:
Petr Mironychev
2026-06-03 17:28:50 +02:00
parent 98a618cf87
commit c151c5030b
57 changed files with 1737 additions and 393 deletions

View File

@@ -34,9 +34,8 @@ QString AgentConfig::validate(const AgentConfig &config)
return QStringLiteral("Agent config '%1' has no model").arg(config.name);
if (config.endpoint.isEmpty())
return QStringLiteral("Agent config '%1' has no endpoint").arg(config.name);
if (config.messageFormat.isEmpty()) {
return QStringLiteral("Agent config '%1' has no [template].message_format")
.arg(config.name);
if (config.body.isEmpty()) {
return QStringLiteral("Agent config '%1' has no [body]").arg(config.name);
}
return {};
}

View File

@@ -19,7 +19,7 @@ struct AgentConfig
QString providerInstance;
QString model;
QString endpoint;
QString role;
QString systemPrompt;
QStringList tags;
struct Match
@@ -40,10 +40,7 @@ struct AgentConfig
bool enableThinking = false;
bool enableTools = false;
QString messageFormat;
QJsonObject sampling;
QJsonObject thinking;
QString context;
QJsonObject body;
QString extendsName;
bool abstract = false;
bool hidden = false;

View File

@@ -4,6 +4,11 @@
#include "AgentFactory.hpp"
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLoggingCategory>
#include <QThread>
@@ -37,6 +42,7 @@ AgentFactory::AgentFactory(
, m_secrets(secrets)
{
::initAgentsResource();
loadModelOverrides();
reload();
}
@@ -169,7 +175,11 @@ Agent *AgentFactory::create(const QString &name, QObject *parent, QString *error
*cfg, m_instanceFactory.data(), m_secrets.data(), errorOut);
if (!provider)
return nullptr;
auto agent = std::make_unique<Agent>(*cfg, provider, /*parent=*/nullptr);
AgentConfig resolved = *cfg;
const QString modelOv = m_modelOverrides.value(resolved.name);
if (!modelOv.isEmpty())
resolved.model = modelOv;
auto agent = std::make_unique<Agent>(resolved, provider, /*parent=*/nullptr);
if (!agent->isValid()) {
if (errorOut)
*errorOut = agent->invalidReason();
@@ -193,6 +203,9 @@ Agent *AgentFactory::createFromFile(
*cfgOpt, m_instanceFactory.data(), m_secrets.data(), errorOut);
if (!provider)
return nullptr;
const QString modelOv = m_modelOverrides.value(cfgOpt->name);
if (!modelOv.isEmpty())
cfgOpt->model = modelOv;
auto agent = std::make_unique<Agent>(std::move(*cfgOpt), provider, /*parent=*/nullptr);
if (!agent->isValid()) {
if (errorOut) *errorOut = agent->invalidReason();
@@ -221,4 +234,55 @@ Providers::ProviderSecretsStore *AgentFactory::secretsStore() const noexcept
return m_secrets.data();
}
QString AgentFactory::modelOverride(const QString &agentName) const
{
return m_modelOverrides.value(agentName);
}
void AgentFactory::setModelOverride(const QString &agentName, const QString &model)
{
if (model.isEmpty())
m_modelOverrides.remove(agentName);
else
m_modelOverrides.insert(agentName, model);
saveModelOverrides();
}
namespace {
QString modelOverridesPath()
{
return Core::ICore::userResourcePath(QStringLiteral("qodeassist/config/agent_models.json"))
.toFSPathString();
}
} // namespace
void AgentFactory::loadModelOverrides()
{
m_modelOverrides.clear();
QFile f(modelOverridesPath());
if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
return;
const QJsonObject obj = QJsonDocument::fromJson(f.readAll()).object();
for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) {
const QString model = it.value().toString();
if (!model.isEmpty())
m_modelOverrides.insert(it.key(), model);
}
}
void AgentFactory::saveModelOverrides() const
{
const QString path = modelOverridesPath();
QDir().mkpath(QFileInfo(path).absolutePath());
QJsonObject obj;
for (auto it = m_modelOverrides.constBegin(); it != m_modelOverrides.constEnd(); ++it)
obj.insert(it.key(), it.value());
QFile f(path);
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
LOG_MESSAGE(QStringLiteral("[Agents] cannot write model overrides: %1").arg(path));
return;
}
f.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
}
} // namespace QodeAssist

View File

@@ -53,12 +53,22 @@ public:
void registerConfig(AgentConfig config);
void clear();
// Per-agent model chosen in QodeAssist settings. The agent TOML's `model`
// is only the default; an override here (keyed by agent name) wins and is
// applied when the agent is built. Empty model clears the override.
[[nodiscard]] QString modelOverride(const QString &agentName) const;
void setModelOverride(const QString &agentName, const QString &model);
[[nodiscard]] Providers::ProviderInstanceFactory *instanceFactory() const noexcept;
[[nodiscard]] Providers::ProviderSecretsStore *secretsStore() const noexcept;
private:
void loadModelOverrides();
void saveModelOverrides() const;
std::vector<AgentConfig> m_configs;
QHash<QString, qsizetype> m_indexByName;
QHash<QString, QString> m_modelOverrides;
QStringList m_errors;
QStringList m_warnings;
QPointer<Providers::ProviderInstanceFactory> m_instanceFactory;

View File

@@ -120,8 +120,7 @@ AgentConfig configFromMerged(const QJsonObject &obj)
cfg.providerInstance = obj.value("provider_instance").toString();
cfg.model = obj.value("model").toString();
cfg.endpoint = obj.value("endpoint").toString();
cfg.role = obj.value("role").toString();
cfg.context = obj.value("context").toString();
cfg.systemPrompt = obj.value("system_prompt").toString();
cfg.enableThinking = obj.value("enable_thinking").toBool(false);
cfg.enableTools = obj.value("enable_tools").toBool(false);
cfg.tags = stringArray(obj.value("tags"));
@@ -135,10 +134,7 @@ AgentConfig configFromMerged(const QJsonObject &obj)
cfg.abstract = obj.value("abstract").toBool(false);
cfg.hidden = obj.value("hidden").toBool(false);
const QJsonObject tpl = obj.value("template").toObject();
cfg.messageFormat = tpl.value("message_format").toString();
cfg.sampling = tpl.value("sampling").toObject();
cfg.thinking = tpl.value("thinking").toObject();
cfg.body = obj.value("body").toObject();
return cfg;
}

View File

@@ -1,11 +1,46 @@
<RCC>
<qresource prefix="/agents">
<file>partials/openai_messages.jinja</file>
<file>partials/openai_assistant.jinja</file>
<file>partials/openai_tool_results.jinja</file>
<file>partials/openai_user.jinja</file>
<file>partials/openai_image_content.jinja</file>
<file>partials/openai_responses_input.jinja</file>
<file>partials/anthropic_messages.jinja</file>
<file>partials/anthropic_image.jinja</file>
<file>partials/google_contents.jinja</file>
<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>
<file>google_base_chat.toml</file>
<file>ollama_base_chat.toml</file>
<file>ollama_base_fim.toml</file>
<file>openai_chat.toml</file>
<file>openai_compatible_chat.toml</file>
<file>openai_responses.toml</file>
<file>mistral_chat.toml</file>
<file>mistral_medium_chat.toml</file>
<file>mistral_reasoning_chat.toml</file>
<file>codestral_chat.toml</file>
<file>codestral_fim.toml</file>
<file>llamacpp_chat.toml</file>
<file>lmstudio_chat.toml</file>
<file>lmstudio_responses.toml</file>
<file>openrouter_chat.toml</file>
<file>ollama_openai_chat.toml</file>
<file>ollama_gemma4_e4b_chat.toml</file>
<file>ollama_codellama_7b_code_fim.toml</file>
<file>ollama_codellama_13b_qml_fim.toml</file>
<file>claude_sonnet_chat.toml</file>
<file>claude_sonnet46_chat.toml</file>
<file>claude_haiku45_chat.toml</file>
<file>claude_opus_max.toml</file>
<file>google_gemini_chat.toml</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,19 @@
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"]
[body]
max_tokens = 8192
temperature = 1
system = """{% if existsIn(ctx, "system_prompt") %}{{ tojson(ctx.system_prompt) }}{% endif %}"""
messages = """
[ {% include "partials/anthropic_messages.jinja" %} ]
"""

View File

@@ -0,0 +1,16 @@
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

@@ -0,0 +1,14 @@
schema_version = 1
extends = "Anthropic Base Chat"
name = "Claude Haiku 4.5 Chat"
description = "Anthropic Claude Haiku 4.5 — fastest model with near-frontier intelligence; extended thinking (manual budget)."
model = "claude-haiku-4-5-20251001"
enable_thinking = true
tags = ["chat", "claude", "anthropic", "cloud", "haiku", "fast"]
[body]
max_tokens = 16000
thinking = { type = "enabled", budget_tokens = 4096 }

View File

@@ -0,0 +1,15 @@
schema_version = 1
extends = "Anthropic Base Chat"
name = "Claude Opus 4.8 Max"
description = "Anthropic Claude Opus 4.8 at maximum capability — adaptive thinking at max effort, 128k max output."
model = "claude-opus-4-8"
enable_thinking = true
tags = ["chat", "claude", "anthropic", "cloud", "opus", "max"]
[body]
max_tokens = 128000
thinking = { type = "adaptive", display = "summarized" }
output_config = { effort = "max" }

View File

@@ -0,0 +1,15 @@
schema_version = 1
extends = "Anthropic Base Chat"
name = "Claude Sonnet 4.6 Chat"
description = "Anthropic Claude Sonnet 4.6 — fast, capable coding chat with adaptive thinking."
model = "claude-sonnet-4-6"
enable_thinking = true
tags = ["chat", "claude", "anthropic", "cloud", "sonnet"]
[body]
max_tokens = 16000
thinking = { type = "adaptive", display = "summarized" }
output_config = { effort = "high" }

View File

@@ -1,77 +1,14 @@
schema_version = 1
extends = "Anthropic Base Chat"
name = "Claude Sonnet Chat"
description = "Anthropic Claude (Messages API) — coding chat assistant via the hosted Claude provider."
provider_instance = "Claude"
endpoint = "/v1/messages"
description = "Anthropic Claude Sonnet 4.6 (Messages API) — coding chat assistant with thinking."
model = "claude-sonnet-4-6"
role = """
You are a helpful coding assistant integrated into Qt Creator.
Answer concisely. When the user shares code, prefer concrete diffs or
minimal patches over rewriting whole files. Use markdown code blocks
with language tags so the IDE can render them.
"""
enable_thinking = true
enable_tools = true
tags = ["chat", "claude", "anthropic", "cloud"]
context = """
{%- set readme = read_file("${PROJECT_DIR}/README.md") -%}
{%- if length(readme) > 0 %}
## Project README.md
{{ readme }}
{%- endif %}
"""
[template]
message_format = """
{
{%- if existsIn(ctx, "system_prompt") %}
"system": {{ tojson(ctx.system_prompt) }},
{%- endif %}
"messages": [
{%- for msg in ctx.history %}
{
"role": {{ tojson(msg.role) }},
"content": [
{%- for b in msg.content_blocks %}
{%- if b.type == "image" %}
{
"type": "image",
"source": {
{%- if b.is_url %}
"type": "url",
"url": {{ tojson(b.data) }}
{%- else %}
"type": "base64",
"media_type": {{ tojson(b.media_type) }},
"data": {{ tojson(b.data) }}
{%- endif %}
}
}{% if not loop.is_last %},{% endif %}
{%- else %}
{{ tojson(b) }}{% if not loop.is_last %},{% endif %}
{%- endif %}
{%- endfor %}
]
}{% if not loop.is_last %},{% endif %}
{%- endfor %}
]
}
"""
[template.sampling]
max_tokens = 8192
temperature = 1
[template.thinking.overrides]
temperature = 1
[template.thinking.request_block.thinking]
type = "enabled"
budget_tokens = 4096
[body]
max_tokens = 8192
thinking = { type = "enabled", budget_tokens = 4096 }

View File

@@ -0,0 +1,11 @@
schema_version = 1
extends = "OpenAI Base Chat"
name = "Codestral Chat"
description = "Mistral Codestral (Chat Completions API) — coding chat assistant."
provider_instance = "Codestral"
endpoint = "/v1/chat/completions"
model = "codestral-latest"
tags = ["chat", "codestral", "mistral", "cloud"]

View File

@@ -0,0 +1,19 @@
schema_version = 1
name = "Codestral FIM"
description = "Mistral Codestral fill-in-the-middle code completion (/v1/fim/completions)."
provider_instance = "Mistral AI"
endpoint = "/v1/fim/completions"
model = "codestral-latest"
enable_thinking = false
enable_tools = false
tags = ["fim", "codestral", "mistral", "cloud", "completion"]
[body]
max_tokens = 256
temperature = 0.2
stream = true
prompt = """{{ tojson(ctx.prefix) }}"""
suffix = """{% if existsIn(ctx, "suffix") %}{{ tojson(ctx.suffix) }}{% endif %}"""

View File

@@ -0,0 +1,20 @@
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"]
[body]
system_instruction = """{% if existsIn(ctx, "system_prompt") %}{ "parts": [ { "text": {{ tojson(ctx.system_prompt) }} } ] }{% endif %}"""
contents = """
[ {% include "partials/google_contents.jinja" %} ]
"""
[body.generationConfig]
maxOutputTokens = 8192
temperature = 1

View File

@@ -1,79 +1,15 @@
schema_version = 1
extends = "Google Base Chat"
name = "Gemini Chat"
description = "Google Gemini (generateContent API) — coding chat assistant via the hosted Google AI provider."
description = "Google Gemini 2.5 Flash (generateContent API) — coding chat with thinking."
provider_instance = "Google AI"
endpoint = "/models/gemini-2.5-flash:streamGenerateContent?alt=sse"
model = "gemini-2.5-flash"
role = """
You are a helpful coding assistant integrated into Qt Creator.
Answer concisely. When the user shares code, prefer concrete diffs or
minimal patches over rewriting whole files. Use markdown code blocks
with language tags so the IDE can render them.
"""
endpoint = "/models/gemini-2.5-flash:streamGenerateContent?alt=sse"
model = "gemini-2.5-flash"
enable_thinking = true
enable_tools = true
tags = ["chat", "gemini", "google", "cloud"]
context = """
{%- set readme = read_file("${PROJECT_DIR}/README.md") -%}
{%- if length(readme) > 0 %}
## Project README.md
{{ readme }}
{%- endif %}
"""
[template]
message_format = """
{
{%- if existsIn(ctx, "system_prompt") %}
"system_instruction": { "parts": [{ "text": {{ tojson(ctx.system_prompt) }} }] },
{%- endif %}
"contents": [
{%- for msg in ctx.history %}
{
"role": {% if msg.role == "assistant" %}"model"{% else %}"user"{% endif %},
"parts": [
{%- for b in msg.content_blocks %}
{%- if b.type == "text" %}
{ "text": {{ tojson(b.text) }} }
{%- else if b.type == "thinking" %}
{ "text": {{ tojson(b.thinking) }}, "thought": true, "thoughtSignature": {{ tojson(b.signature) }} }
{%- else if b.type == "tool_use" %}
{ "functionCall": { "name": {{ tojson(b.name) }}, "args": {{ tojson(b.input) }} } }
{%- else if b.type == "tool_result" %}
{ "functionResponse": { "name": {{ tojson(b.name) }}, "response": { "result": {{ tojson(b.content) }} } } }
{%- else if b.type == "image" %}
{%- if b.is_url %}
{ "file_data": { "mime_type": {{ tojson(b.media_type) }}, "file_uri": {{ tojson(b.data) }} } }
{%- else %}
{ "inline_data": { "mime_type": {{ tojson(b.media_type) }}, "data": {{ tojson(b.data) }} } }
{%- endif %}
{%- else %}
{ "text": "" }
{%- endif %}
{% if not loop.is_last %},{% endif %}
{%- endfor %}
]
}{% if not loop.is_last %},{% endif %}
{%- endfor %}
]
}
"""
[template.sampling.generationConfig]
maxOutputTokens = 8192
temperature = 1
[template.thinking.request_block.generationConfig]
temperature = 1
[body.generationConfig]
maxOutputTokens = 16000
[template.thinking.request_block.generationConfig.thinkingConfig]
includeThoughts = true
thinkingBudget = 8192
thinkingConfig = { includeThoughts = true, thinkingBudget = 8192 }

View File

@@ -0,0 +1,11 @@
schema_version = 1
extends = "OpenAI Base Chat"
name = "llama.cpp Chat"
description = "llama.cpp server (OpenAI-compatible Chat Completions) — local coding chat assistant."
provider_instance = "llama.cpp"
endpoint = "/v1/chat/completions"
model = "llama"
tags = ["chat", "llamacpp", "local"]

View File

@@ -0,0 +1,11 @@
schema_version = 1
extends = "OpenAI Base Chat"
name = "LM Studio Chat"
description = "LM Studio (Chat Completions API) — local coding chat assistant."
provider_instance = "LM Studio (Chat Completions)"
endpoint = "/v1/chat/completions"
model = "local-model"
tags = ["chat", "lmstudio", "local"]

View File

@@ -0,0 +1,11 @@
schema_version = 1
extends = "OpenAI Responses Base"
name = "LM Studio Responses"
description = "LM Studio (Responses API) — local coding chat assistant."
provider_instance = "LM Studio (Responses API)"
endpoint = "/v1/responses"
model = "local-model"
tags = ["chat", "lmstudio", "responses", "local"]

View File

@@ -0,0 +1,11 @@
schema_version = 1
extends = "OpenAI Base Chat"
name = "Mistral Chat"
description = "Mistral Large (Chat Completions API) — coding chat assistant."
provider_instance = "Mistral AI"
endpoint = "/v1/chat/completions"
model = "mistral-large-latest"
tags = ["chat", "mistral", "cloud"]

View File

@@ -0,0 +1,11 @@
schema_version = 1
extends = "OpenAI Base Chat"
name = "Mistral Medium Chat"
description = "Mistral Medium 3.5 (Chat Completions API) — frontier coding/agentic chat."
provider_instance = "Mistral AI"
endpoint = "/v1/chat/completions"
model = "mistral-medium-latest"
tags = ["chat", "mistral", "medium", "cloud"]

View File

@@ -0,0 +1,12 @@
schema_version = 1
extends = "OpenAI Base Chat"
name = "Mistral Reasoning Chat"
description = "Mistral Magistral Medium — native chain-of-thought reasoning model."
provider_instance = "Mistral AI"
endpoint = "/v1/chat/completions"
model = "magistral-medium-latest"
enable_thinking = true
tags = ["chat", "mistral", "reasoning", "cloud"]

View File

@@ -1,44 +1,21 @@
schema_version = 1
name = "Ollama Base Chat"
description = "Shared base for Ollama /api/chat profiles."
abstract = true
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"]
[template]
message_format = """
{
"messages": [
{%- if existsIn(ctx, "system_prompt") %}
{
"role": "system",
"content": {{ tojson(ctx.system_prompt) }}
}{% if length(ctx.history) > 0 %},{% endif %}
{%- 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) }}{% if not loop.is_last %},{% endif %}
{%- endfor %}
]{% endif %}
}{% if not loop.is_last %},{% endif %}
{%- endfor %}
]
}
[body]
stream = true
messages = """
[ {% include "partials/ollama_messages.jinja" %} ]
"""
[template.sampling]
stream = true
[template.sampling.options]
[body.options]
num_predict = 2048
temperature = 0.7
keep_alive = "5m"

View File

@@ -1,30 +1,18 @@
schema_version = 1
name = "Ollama FIM Base"
description = "Shared base for Ollama native FIM (/api/generate) profiles."
abstract = true
description = "Ollama native /api/generate FIM request body. Abstract — extend it and set model/prompt."
abstract = true
provider_instance = "Ollama (Native)"
endpoint = "/api/generate"
tags = ["ollama", "local", "fim"]
[template]
message_format = """
{
"prompt": {{ tojson(ctx.prefix) }},
"suffix": {{ tojson(ctx.suffix) }}
{%- if existsIn(ctx, "system_prompt") %},
"system": {{ tojson(ctx.system_prompt) }}
{%- endif %}
}
"""
[template.sampling]
[body]
stream = true
system = """{% if existsIn(ctx, "system_prompt") %}{{ tojson(ctx.system_prompt) }}{% endif %}"""
[template.sampling.options]
[body.options]
num_predict = 512
temperature = 0.2
top_p = 0.9

View File

@@ -1,11 +1,9 @@
schema_version = 1
extends = "Ollama FIM Base"
name = "Qt CodeLlama 13B QML FIM"
description = "Local Qt-Company-tuned CodeLlama 13B for QML FIM completion."
provider_instance = "Ollama (Native)"
endpoint = "/api/generate"
model = "theqtcompany/codellama-13b-qml:latest"
tags = ["fim", "ollama", "local", "codellama", "qml", "qt"]
@@ -13,28 +11,12 @@ tags = ["fim", "ollama", "local", "codellama", "qml", "qt"]
[match]
file_patterns = ["*.qml"]
[template]
message_format = """
{
"prompt": {%- if existsIn(ctx, "suffix") and length(ctx.suffix) > 0 -%}
{{ tojson("<SUF>" + ctx.suffix + "<PRE>" + ctx.prefix + "<MID>") }}
{%- else -%}
{{ tojson("<PRE>" + ctx.prefix + "<MID>") }}
{%- endif %}
{%- if existsIn(ctx, "system_prompt") %},
"system": {{ tojson(ctx.system_prompt) }}
{%- endif %}
}
"""
[body]
prompt = """{% if existsIn(ctx, "suffix") and length(ctx.suffix) > 0 %}{{ tojson("<SUF>" + ctx.suffix + "<PRE>" + ctx.prefix + "<MID>") }}{% else %}{{ tojson("<PRE>" + ctx.prefix + "<MID>") }}{% endif %}"""
[template.sampling]
stream = true
[template.sampling.options]
[body.options]
num_predict = 500
temperature = 0
top_p = 1
repeat_penalty = 1.05
keep_alive = "5m"
stop = ["<SUF>", "<PRE>", "</PRE>", "</SUF>", "< EOT >", "\\end", "<MID>", "</MID>", "##"]

View File

@@ -1,11 +1,9 @@
schema_version = 1
extends = "Ollama FIM Base"
name = "CodeLlama 7B Code FIM"
description = "Local CodeLlama 7B (code variant) on Ollama, FIM completion via PRE/SUF/MID markers."
provider_instance = "Ollama (Native)"
endpoint = "/api/generate"
model = "codellama:7b-code"
tags = ["fim", "ollama", "local", "codellama"]
@@ -13,22 +11,8 @@ tags = ["fim", "ollama", "local", "codellama"]
[match]
file_patterns = ["*.cpp", "*.cc", "*.cxx", "*.c", "*.h", "*.hpp", "*.hxx", "*.inl"]
[template]
message_format = """
{
"prompt": {{ tojson("<PRE> " + ctx.prefix + " <SUF>" + ctx.suffix + " <MID>") }}
{%- if existsIn(ctx, "system_prompt") %},
"system": {{ tojson(ctx.system_prompt) }}
{%- endif %}
}
"""
[body]
prompt = """{{ tojson("<PRE> " + ctx.prefix + " <SUF>" + ctx.suffix + " <MID>") }}"""
[template.sampling]
stream = true
[template.sampling.options]
num_predict = 512
temperature = 0.2
top_p = 0.9
keep_alive = "5m"
[body.options]
stop = ["<EOT>", "<PRE>", "<SUF>", "<MID>"]

View File

@@ -1,34 +1,16 @@
schema_version = 1
name = "Ollama gemma4:e4b Chat"
extends = "Ollama Base Chat"
extends = "Ollama Base Chat"
name = "Ollama gemma4:e4b Chat"
description = "Local Gemma 4 E4B on Ollama /api/chat — coding chat assistant."
model = "gemma4:e4b"
role = """
You are a helpful coding assistant integrated into Qt Creator.
Answer concisely. When the user shares code, prefer concrete diffs or
minimal patches over rewriting whole files. Use markdown code blocks
with language tags so the IDE can render them.
"""
enable_thinking = true
enable_tools = true
tags = ["chat", "ollama", "local", "gemma"]
context = """
{%- set readme = read_file("${PROJECT_DIR}/README.md") -%}
{%- if length(readme) > 0 %}
## Project README.md
{{ readme }}
{%- endif %}
"""
[template.sampling.options]
[body.options]
num_predict = 4096
temperature = 1
top_k = 64

View File

@@ -0,0 +1,11 @@
schema_version = 1
extends = "OpenAI Base Chat"
name = "Ollama (OpenAI-compatible) Chat"
description = "Ollama via its OpenAI-compatible Chat Completions endpoint — local coding chat assistant."
provider_instance = "Ollama (OpenAI-compatible)"
endpoint = "/v1/chat/completions"
model = "qwen2.5-coder"
tags = ["chat", "ollama", "local"]

View File

@@ -0,0 +1,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"]
[body]
max_tokens = 8192
temperature = 0.7
messages = """
[ {% include "partials/openai_messages.jinja" %} ]
"""

View File

@@ -0,0 +1,7 @@
schema_version = 1
extends = "OpenAI Base Chat"
name = "OpenAI Chat"
description = "OpenAI Chat Completions — coding chat assistant (GPT-4o)."
model = "gpt-4o"

View File

@@ -0,0 +1,10 @@
schema_version = 1
extends = "OpenAI Base Chat"
name = "OpenAI Compatible Chat"
description = "Any OpenAI-compatible Chat Completions endpoint — set the model to match your server."
provider_instance = "OpenAI Compatible"
model = "default"
tags = ["chat", "openai", "compatible"]

View File

@@ -0,0 +1,7 @@
schema_version = 1
extends = "OpenAI Responses Base"
name = "OpenAI Responses"
description = "OpenAI Responses API (/responses) — coding chat assistant."
model = "gpt-4o"

View File

@@ -0,0 +1,19 @@
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"]
[body]
max_output_tokens = 8192
temperature = 0.7
instructions = """{% if existsIn(ctx, "system_prompt") %}{{ tojson(ctx.system_prompt) }}{% endif %}"""
input = """
[ {% include "partials/openai_responses_input.jinja" %} ]
"""

View File

@@ -0,0 +1,11 @@
schema_version = 1
extends = "OpenAI Base Chat"
name = "OpenRouter Chat"
description = "OpenRouter (OpenAI-compatible Chat Completions) — coding chat assistant."
provider_instance = "OpenRouter"
endpoint = "/chat/completions"
model = "openai/gpt-4o"
tags = ["chat", "openrouter", "cloud"]

View File

@@ -0,0 +1,9 @@
{
"type": "image",
"source":
{% if b.is_url %}
{ "type": "url", "url": {{ tojson(b.data) }} }
{% else %}
{ "type": "base64", "media_type": {{ tojson(b.media_type) }}, "data": {{ tojson(b.data) }} }
{% endif %}
},

View File

@@ -0,0 +1,12 @@
{% for msg in ctx.history %}
{
"role": {{ tojson(msg.role) }},
"content": [
{% for b in msg.content_blocks %}
{% if b.type == "image" %}{% include "partials/anthropic_image.jinja" %}
{% else %}{{ tojson(b) }},
{% endif %}
{% endfor %}
]
},
{% endfor %}

View File

@@ -0,0 +1,6 @@
{% for msg in ctx.history %}
{
"role": {% if msg.role == "assistant" %}"model"{% else %}"user"{% endif %},
"parts": [ {% for b in msg.content_blocks %}{% include "partials/google_part.jinja" %}{% endfor %} ]
},
{% endfor %}

View File

@@ -0,0 +1,17 @@
{% if b.type == "text" %}
{ "text": {{ tojson(b.text) }} },
{% else if b.type == "thinking" %}
{ "text": {{ tojson(b.thinking) }}, "thought": true, "thoughtSignature": {{ tojson(b.signature) }} },
{% else if b.type == "tool_use" %}
{ "functionCall": { "name": {{ tojson(b.name) }}, "args": {{ tojson(b.input) }} } },
{% else if b.type == "tool_result" %}
{ "functionResponse": { "name": {{ tojson(b.name) }}, "response": { "result": {{ tojson(b.content) }} } } },
{% else if b.type == "image" %}
{% if b.is_url %}
{ "file_data": { "mime_type": {{ tojson(b.media_type) }}, "file_uri": {{ tojson(b.data) }} } },
{% else %}
{ "inline_data": { "mime_type": {{ tojson(b.media_type) }}, "data": {{ tojson(b.data) }} } },
{% endif %}
{% else %}
{ "text": "" },
{% endif %}

View File

@@ -0,0 +1,16 @@
{% if existsIn(ctx, "system_prompt") %}
{ "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) }},
{% endfor %}
]
{% endif %}
},
{% endfor %}

View File

@@ -0,0 +1,19 @@
{% set tcalls = filter_by_type(msg.content_blocks, "tool_use") %}
{
"role": "assistant",
"content": {{ tojson(msg.content) }}
{% if length(tcalls) > 0 %}
, "tool_calls": [
{% for b in tcalls %}
{
"id": {{ tojson(b.id) }},
"type": "function",
"function": {
"name": {{ tojson(b.name) }},
"arguments": {{ tojson(tojson(b.input)) }}
}
},
{% endfor %}
]
{% endif %}
},

View File

@@ -0,0 +1,11 @@
[
{ "type": "text", "text": {{ tojson(msg.content) }} }
{% for img in msg.images %}
,
{% if img.is_url %}
{ "type": "image_url", "image_url": { "url": {{ tojson(img.data) }} } }
{% else %}
{ "type": "image_url", "image_url": { "url": "data:{{ img.media_type }};base64,{{ img.data }}" } }
{% endif %}
{% endfor %}
]

View File

@@ -0,0 +1,9 @@
{% if existsIn(ctx, "system_prompt") %}
{ "role": "system", "content": {{ tojson(ctx.system_prompt) }} },
{% endif %}
{% for msg in ctx.history %}
{% if msg.role == "assistant" %}{% include "partials/openai_assistant.jinja" %}
{% else if length(filter_by_type(msg.content_blocks, "tool_result")) > 0 %}{% include "partials/openai_tool_results.jinja" %}
{% else %}{% include "partials/openai_user.jinja" %}
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,30 @@
{% for msg in ctx.history %}
{% if msg.role == "assistant" %}
{% if msg.content != "" %}
{ "role": "assistant", "content": {{ tojson(msg.content) }} },
{% endif %}
{% for b in filter_by_type(msg.content_blocks, "tool_use") %}
{ "type": "function_call", "call_id": {{ tojson(b.id) }}, "name": {{ tojson(b.name) }}, "arguments": {{ tojson(tojson(b.input)) }} },
{% endfor %}
{% else if length(filter_by_type(msg.content_blocks, "tool_result")) > 0 %}
{% for b in filter_by_type(msg.content_blocks, "tool_result") %}
{ "type": "function_call_output", "call_id": {{ tojson(b.tool_use_id) }}, "output": {{ tojson(b.content) }} },
{% endfor %}
{% else %}
{% if existsIn(msg, "images") %}
{ "role": "user", "content": [
{ "type": "input_text", "text": {{ tojson(msg.content) }} }
{% for img in msg.images %}
,
{% if img.is_url %}
{ "type": "input_image", "detail": "auto", "image_url": {{ tojson(img.data) }} }
{% else %}
{ "type": "input_image", "detail": "auto", "image_url": "data:{{ img.media_type }};base64,{{ img.data }}" }
{% endif %}
{% endfor %}
] },
{% else %}
{ "role": "user", "content": {{ tojson(msg.content) }} },
{% endif %}
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,3 @@
{% for b in filter_by_type(msg.content_blocks, "tool_result") %}
{ "role": "tool", "tool_call_id": {{ tojson(b.tool_use_id) }}, "content": {{ tojson(b.content) }} },
{% endfor %}

View File

@@ -0,0 +1,5 @@
{% if existsIn(msg, "images") %}
{ "role": "user", "content": {% include "partials/openai_image_content.jinja" %} },
{% else %}
{ "role": "user", "content": {{ tojson(msg.content) }} },
{% endif %}