refactor: Restructure project into sources/tests layout and dissolve PluginLLMCore

This commit is contained in:
Petr Mironychev
2026-07-14 02:50:43 +02:00
parent adef7972ed
commit 34d4f2c517
348 changed files with 809 additions and 852 deletions

View File

@@ -0,0 +1,75 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/PromptTemplate.hpp"
#include <QJsonArray>
namespace QodeAssist::Templates {
class Alpaca : public Templates::PromptTemplate
{
public:
QString name() const override { return "Alpaca"; }
Templates::TemplateType type() const override { return Templates::TemplateType::Chat; }
QStringList stopWords() const override
{
return QStringList() << "### Instruction:" << "### Response:";
}
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages;
QString fullContent;
if (context.systemPrompt) {
fullContent += context.systemPrompt.value() + "\n\n";
}
if (context.history) {
for (const auto &msg : context.history.value()) {
if (msg.role == "user") {
fullContent += QString("### Instruction:\n%1\n\n").arg(msg.content);
} else if (msg.role == "assistant") {
fullContent += QString("### Response:\n%1\n\n").arg(msg.content);
}
}
}
messages.append(QJsonObject{{"role", "user"}, {"content", fullContent}});
request["messages"] = messages;
}
QString description() const override
{
return "Template for models using Alpaca instruction format:\n\n"
"{\n"
" \"messages\": [\n"
" {\n"
" \"role\": \"user\",\n"
" \"content\": \"<system prompt>\\n\\n"
"### Instruction:\\n<user message>\\n\\n"
"### Response:\\n<assistant response>\\n\\n\"\n"
" }\n"
" ]\n"
"}\n\n"
"Combines all messages into a single formatted prompt.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case Providers::ProviderID::Ollama:
case Providers::ProviderID::LMStudio:
case Providers::ProviderID::OpenRouter:
case Providers::ProviderID::OpenAICompatible:
case Providers::ProviderID::LlamaCpp:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,76 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class ChatML : public Templates::PromptTemplate
{
public:
QString name() const override { return "ChatML"; }
Templates::TemplateType type() const override { return Templates::TemplateType::Chat; }
QStringList stopWords() const override
{
return QStringList() << "<|im_start|>" << "<|im_end|>";
}
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages;
if (context.systemPrompt) {
messages.append(QJsonObject{
{"role", "system"},
{"content",
QString("<|im_start|>system\n%2\n<|im_end|>").arg(context.systemPrompt.value())}});
}
if (context.history) {
for (const auto &msg : context.history.value()) {
messages.append(QJsonObject{
{"role", msg.role},
{"content",
QString("<|im_start|>%1\n%2\n<|im_end|>").arg(msg.role, msg.content)}});
}
}
request["messages"] = messages;
}
QString description() const override
{
return "Template for models supporting ChatML format:\n\n"
"{\n"
" \"messages\": [\n"
" {\n"
" \"role\": \"system\",\n"
" \"content\": \"<|im_start|>system\\n<system prompt>\\n<|im_end|>\"\n"
" },\n"
" {\n"
" \"role\": \"user\",\n"
" \"content\": \"<|im_start|>user\\n<user message>\\n<|im_end|>\"\n"
" }\n"
" ]\n"
"}\n\n"
"Compatible with multiple providers supporting the ChatML token format.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case Providers::ProviderID::Ollama:
case Providers::ProviderID::LMStudio:
case Providers::ProviderID::OpenRouter:
case Providers::ProviderID::OpenAICompatible:
case Providers::ProviderID::LlamaCpp:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,150 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class Claude : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::Chat; }
QString name() const override { return "Claude"; }
QStringList stopWords() const override { return QStringList(); }
bool supportsToolHistory() const override { return true; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages;
if (context.systemPrompt) {
request["system"] = context.systemPrompt.value();
}
if (context.history) {
int toolResultUserIdx = -1;
for (const auto &msg : context.history.value()) {
if (msg.role == "system") continue;
if (!msg.toolCalls.isEmpty()) {
toolResultUserIdx = -1;
QJsonArray content;
if (!msg.content.isEmpty()) {
content.append(QJsonObject{{"type", "text"}, {"text", msg.content}});
}
for (const auto &call : msg.toolCalls) {
content.append(QJsonObject{
{"type", "tool_use"},
{"id", call.id},
{"name", call.name},
{"input", call.arguments}});
}
messages.append(QJsonObject{{"role", "assistant"}, {"content", content}});
continue;
}
if (msg.role == "tool") {
QJsonObject resultBlock{
{"type", "tool_result"},
{"tool_use_id", msg.toolCallId},
{"content", msg.content}};
if (toolResultUserIdx >= 0) {
QJsonObject userMsg = messages[toolResultUserIdx].toObject();
QJsonArray content = userMsg["content"].toArray();
content.append(resultBlock);
userMsg["content"] = content;
messages[toolResultUserIdx] = userMsg;
} else {
messages.append(QJsonObject{
{"role", "user"}, {"content", QJsonArray{resultBlock}}});
toolResultUserIdx = messages.size() - 1;
}
continue;
}
toolResultUserIdx = -1;
if (msg.isThinking) {
// Claude API requires signature for thinking blocks
if (msg.signature.isEmpty()) {
continue;
}
QJsonArray content;
QJsonObject thinkingBlock;
thinkingBlock["type"] = msg.isRedacted ? "redacted_thinking" : "thinking";
QString thinkingText = msg.content;
int signaturePos = thinkingText.indexOf("\n[Signature: ");
if (signaturePos != -1) {
thinkingText = thinkingText.left(signaturePos);
}
if (!msg.isRedacted) {
thinkingBlock["thinking"] = thinkingText;
}
thinkingBlock["signature"] = msg.signature;
content.append(thinkingBlock);
messages.append(QJsonObject{{"role", "assistant"}, {"content", content}});
} else if (msg.images && !msg.images->isEmpty()) {
QJsonArray content;
if (!msg.content.isEmpty()) {
content.append(QJsonObject{{"type", "text"}, {"text", msg.content}});
}
for (const auto &image : msg.images.value()) {
QJsonObject imageBlock;
imageBlock["type"] = "image";
QJsonObject source;
if (image.isUrl) {
source["type"] = "url";
source["url"] = image.data;
} else {
source["type"] = "base64";
source["media_type"] = image.mediaType;
source["data"] = image.data;
}
imageBlock["source"] = source;
content.append(imageBlock);
}
messages.append(QJsonObject{{"role", msg.role}, {"content", content}});
} else {
messages.append(QJsonObject{{"role", msg.role}, {"content", msg.content}});
}
}
}
request["messages"] = messages;
}
QString description() const override
{
return "Template for Anthropic's Claude models:\n\n"
"{\n"
" \"system\": \"<system prompt>\",\n"
" \"messages\": [\n"
" {\"role\": \"user\", \"content\": \"<user message>\"},\n"
" {\"role\": \"assistant\", \"content\": \"<assistant response>\"}\n"
" ]\n"
"}\n\n"
"Formats content according to Claude API specifications.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case QodeAssist::Providers::ProviderID::Claude:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,47 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class CodeLlamaFim : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::FIM; }
QString name() const override { return "CodeLlama FIM"; }
QString endpoint() const override { return QStringLiteral("/api/generate"); }
QStringList stopWords() const override
{
return QStringList() << "<EOT>" << "<PRE>" << "<SUF" << "<MID>";
}
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
request["prompt"] = QString("<PRE> %1 <SUF>%2 <MID>")
.arg(context.prefix.value_or(""), context.suffix.value_or(""));
request["system"] = context.systemPrompt.value_or("");
}
QString description() const override
{
return "Specialized template for CodeLlama FIM:\n\n"
"{\n"
" \"prompt\": \"<PRE> <code prefix> <SUF><code suffix> <MID>\",\n"
" \"system\": \"<system prompt>\"\n"
"}\n\n"
"Optimized for code completion with CodeLlama models.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case QodeAssist::Providers::ProviderID::Ollama:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,48 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class CodeLlamaQMLFim : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::FIM; }
QString name() const override { return "CodeLlama QML FIM"; }
QString endpoint() const override { return QStringLiteral("/api/generate"); }
QStringList stopWords() const override
{
return QStringList() << "<SUF>" << "<PRE>" << "</PRE>" << "</SUF>" << "< EOT >" << "\\end"
<< "<MID>" << "</MID>" << "##";
}
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
request["prompt"] = QString("<SUF>%1<PRE>%2<MID>")
.arg(context.suffix.value_or(""), context.prefix.value_or(""));
request["system"] = context.systemPrompt.value_or("");
}
QString description() const override
{
return "Specialized template for QML code completion with CodeLlama:\n\n"
"{\n"
" \"prompt\": \"<SUF><code suffix><PRE><code prefix><MID>\",\n"
" \"system\": \"<system prompt>\"\n"
"}\n\n"
"Specifically optimized for QML/JavaScript code completion.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case QodeAssist::Providers::ProviderID::Ollama:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,161 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include <QJsonObject>
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class GoogleAI : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::Chat; }
QString name() const override { return "Google AI"; }
QStringList stopWords() const override { return QStringList(); }
bool supportsToolHistory() const override { return true; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray contents;
if (context.systemPrompt && !context.systemPrompt->isEmpty()) {
request["system_instruction"] = QJsonObject{
{"parts", QJsonObject{{"text", context.systemPrompt.value()}}}};
}
int toolResultIdx = -1;
for (const auto &msg : context.history.value()) {
if (!msg.toolCalls.isEmpty()) {
toolResultIdx = -1;
QJsonArray callParts;
if (!msg.content.isEmpty()) {
callParts.append(QJsonObject{{"text", msg.content}});
}
for (const auto &call : msg.toolCalls) {
callParts.append(QJsonObject{
{"functionCall",
QJsonObject{{"name", call.name}, {"args", call.arguments}}}});
}
contents.append(QJsonObject{{"role", "model"}, {"parts", callParts}});
continue;
}
if (msg.role == "tool") {
QJsonObject responsePart{
{"functionResponse",
QJsonObject{
{"name", msg.toolName},
{"response", QJsonObject{{"result", msg.content}}}}}};
if (toolResultIdx >= 0) {
QJsonObject fnMsg = contents[toolResultIdx].toObject();
QJsonArray fnParts = fnMsg["parts"].toArray();
fnParts.append(responsePart);
fnMsg["parts"] = fnParts;
contents[toolResultIdx] = fnMsg;
} else {
contents.append(
QJsonObject{{"role", "function"}, {"parts", QJsonArray{responsePart}}});
toolResultIdx = contents.size() - 1;
}
continue;
}
toolResultIdx = -1;
QJsonObject content;
QJsonArray parts;
if (msg.isThinking) {
if (!msg.content.isEmpty()) {
QJsonObject thinkingPart;
thinkingPart["text"] = msg.content;
thinkingPart["thought"] = true;
parts.append(thinkingPart);
}
if (!msg.signature.isEmpty()) {
QJsonObject signaturePart;
signaturePart["thoughtSignature"] = msg.signature;
parts.append(signaturePart);
}
if (parts.isEmpty()) {
continue;
}
content["role"] = "model";
} else {
if (!msg.content.isEmpty()) {
parts.append(QJsonObject{{"text", msg.content}});
}
if (msg.images && !msg.images->isEmpty()) {
for (const auto &image : msg.images.value()) {
QJsonObject imagePart;
if (image.isUrl) {
QJsonObject fileData;
fileData["mime_type"] = image.mediaType;
fileData["file_uri"] = image.data;
imagePart["file_data"] = fileData;
} else {
QJsonObject inlineData;
inlineData["mime_type"] = image.mediaType;
inlineData["data"] = image.data;
imagePart["inline_data"] = inlineData;
}
parts.append(imagePart);
}
}
QString role = msg.role;
if (role == "assistant") {
role = "model";
}
content["role"] = role;
}
content["parts"] = parts;
contents.append(content);
}
request["contents"] = contents;
}
QString description() const override
{
return "Template for Google AI models (Gemini):\n\n"
"{\n"
" \"system_instruction\": {\"parts\": {\"text\": \"<system prompt>\"}},\n"
" \"contents\": [\n"
" {\n"
" \"role\": \"user\",\n"
" \"parts\": [{\"text\": \"<user message>\"}]\n"
" },\n"
" {\n"
" \"role\": \"model\",\n"
" \"parts\": [\n"
" {\"text\": \"<thinking>\", \"thought\": true},\n"
" {\"thoughtSignature\": \"<signature>\"},\n"
" {\"text\": \"<assistant response>\"}\n"
" ]\n"
" }\n"
" ]\n"
"}\n\n"
"Supports proper role mapping (model/user roles), images, and thinking blocks.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
return id == QodeAssist::Providers::ProviderID::GoogleAI;
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,24 @@
// Copyright (C) 2025 Povilas Kanapickas <povilas@radix.lt>
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/PromptTemplate.hpp"
#include <QString>
namespace QodeAssist::Templates {
class IPromptProvider
{
public:
virtual ~IPromptProvider() = default;
virtual PromptTemplate *getTemplateByName(const QString &templateName) const = 0;
virtual QStringList templatesNames() const = 0;
virtual QStringList getTemplatesForProvider(Providers::ProviderID id) const = 0;
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,73 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/PromptTemplate.hpp"
#include <QJsonArray>
namespace QodeAssist::Templates {
class Llama2 : public Templates::PromptTemplate
{
public:
QString name() const override { return "Llama 2"; }
Templates::TemplateType type() const override { return Templates::TemplateType::Chat; }
QStringList stopWords() const override { return QStringList() << "[INST]"; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages;
QString fullContent;
if (context.systemPrompt) {
fullContent
+= QString("[INST]<<SYS>>\n%1\n<</SYS>>[/INST]\n").arg(context.systemPrompt.value());
}
if (context.history) {
for (const auto &msg : context.history.value()) {
if (msg.role == "user") {
fullContent += QString("[INST]%1[/INST]\n").arg(msg.content);
} else if (msg.role == "assistant") {
fullContent += msg.content + "\n";
}
}
}
messages.append(QJsonObject{{"role", "user"}, {"content", fullContent}});
request["messages"] = messages;
}
QString description() const override
{
return "Template for Llama 2 models:\n\n"
"{\n"
" \"messages\": [\n"
" {\n"
" \"role\": \"user\",\n"
" \"content\": \"[INST]<<SYS>>\\n<system prompt>\\n<</SYS>>[/INST]\\n"
"<assistant response>\\n"
"[INST]<user message>[/INST]\\n\"\n"
" }\n"
" ]\n"
"}\n\n"
"Compatible with Ollama, LM Studio, and other services for Llama 2.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case Providers::ProviderID::Ollama:
case Providers::ProviderID::LMStudio:
case Providers::ProviderID::OpenRouter:
case Providers::ProviderID::OpenAICompatible:
case Providers::ProviderID::LlamaCpp:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,80 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class Llama3 : public Templates::PromptTemplate
{
public:
QString name() const override { return "Llama 3"; }
Templates::TemplateType type() const override { return Templates::TemplateType::Chat; }
QStringList stopWords() const override
{
return QStringList() << "<|start_header_id|>" << "<|end_header_id|>" << "<|eot_id|>";
}
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages;
if (context.systemPrompt) {
messages.append(QJsonObject{
{"role", "system"},
{"content",
QString("<|start_header_id|>system<|end_header_id|>%2<|eot_id|>")
.arg(context.systemPrompt.value())}});
}
if (context.history) {
for (const auto &msg : context.history.value()) {
messages.append(QJsonObject{
{"role", msg.role},
{"content",
QString("<|start_header_id|>%1<|end_header_id|>%2<|eot_id|>")
.arg(msg.role, msg.content)}});
}
}
request["messages"] = messages;
}
QString description() const override
{
return "Template for Llama 3 models:\n\n"
"{\n"
" \"messages\": [\n"
" {\n"
" \"role\": \"system\",\n"
" \"content\": \"<|start_header_id|>system<|end_header_id|><system "
"prompt><|eot_id|>\"\n"
" },\n"
" {\n"
" \"role\": \"user\",\n"
" \"content\": \"<|start_header_id|>user<|end_header_id|><user "
"message><|eot_id|>\"\n"
" }\n"
" ]\n"
"}\n\n"
"Compatible with Ollama, LM Studio, and OpenAI-compatible services for Llama 3.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case Providers::ProviderID::Ollama:
case Providers::ProviderID::LMStudio:
case Providers::ProviderID::OpenRouter:
case Providers::ProviderID::OpenAICompatible:
case Providers::ProviderID::LlamaCpp:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,55 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class LlamaCppFim : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::FIM; }
QString name() const override { return "llama.cpp FIM"; }
QString endpoint() const override { return QStringLiteral("/infill"); }
QStringList stopWords() const override { return {}; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
request["input_prefix"] = context.prefix.value_or("");
request["input_suffix"] = context.suffix.value_or("");
if (context.filesMetadata && !context.filesMetadata->isEmpty()) {
QJsonArray filesArray;
for (const auto &file : *context.filesMetadata) {
QJsonObject fileObj;
fileObj["filename"] = file.filePath;
fileObj["text"] = file.content;
filesArray.append(fileObj);
}
request["input_extra"] = filesArray;
}
}
QString description() const override
{
return "Default llama.cpp FIM (Fill-in-Middle) /infill template with native format:\n\n"
"{\n"
" \"input_prefix\": \"<code prefix>\",\n"
" \"input_suffix\": \"<code suffix>\",\n"
" \"input_extra\": \"<system prompt>\"\n"
"}\n\n"
"Recommended for models with FIM capability.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
return id == QodeAssist::Providers::ProviderID::LlamaCpp;
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,121 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include "templates/PromptTemplate.hpp"
#include "templates/ToolMessages.hpp"
namespace QodeAssist::Templates {
class MistralAIFim : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::FIM; }
QString name() const override { return "Mistral AI FIM"; }
QString endpoint() const override { return QStringLiteral("/v1/fim/completions"); }
QStringList stopWords() const override { return QStringList(); }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
request["prompt"] = context.prefix.value_or("");
request["suffix"] = context.suffix.value_or("");
}
QString description() const override
{
return "Template for MistralAI models with FIM support:\n\n"
"{\n"
" \"prompt\": \"<code prefix>\",\n"
" \"suffix\": \"<code suffix>\"\n"
"}\n\n"
"Optimized for code completion with MistralAI models.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case QodeAssist::Providers::ProviderID::MistralAI:
return true;
default:
return false;
}
}
};
class MistralAIChat : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::Chat; }
QString name() const override { return "Mistral AI Chat"; }
QStringList stopWords() const override { return QStringList(); }
bool supportsToolHistory() const override { return true; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages;
if (context.systemPrompt) {
messages.append(
QJsonObject{{"role", "system"}, {"content", context.systemPrompt.value()}});
}
if (context.history) {
for (const auto &msg : context.history.value()) {
if (appendOpenAIToolMessage(messages, msg)) {
continue;
}
if (msg.images && !msg.images->isEmpty()) {
QJsonArray content;
if (!msg.content.isEmpty()) {
content.append(QJsonObject{{"type", "text"}, {"text", msg.content}});
}
for (const auto &image : msg.images.value()) {
QJsonObject imageBlock;
imageBlock["type"] = "image_url";
QJsonObject imageUrl;
if (image.isUrl) {
imageUrl["url"] = image.data;
} else {
imageUrl["url"] = QString("data:%1;base64,%2").arg(image.mediaType, image.data);
}
imageBlock["image_url"] = imageUrl;
content.append(imageBlock);
}
messages.append(QJsonObject{{"role", msg.role}, {"content", content}});
} else {
messages.append(QJsonObject{{"role", msg.role}, {"content", msg.content}});
}
}
}
request["messages"] = messages;
}
QString description() const override
{
return "Template for MistralAI chat-capable models:\n\n"
"{\n"
" \"messages\": [\n"
" {\"role\": \"system\", \"content\": \"<system prompt>\"},\n"
" {\"role\": \"user\", \"content\": \"<user message>\"},\n"
" {\"role\": \"assistant\", \"content\": \"<assistant response>\"}\n"
" ]\n"
"}\n\n"
"Supports system messages, conversation history, and images.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case QodeAssist::Providers::ProviderID::MistralAI:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,128 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class OllamaFim : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::FIM; }
QString name() const override { return "Ollama FIM"; }
QString endpoint() const override { return QStringLiteral("/api/generate"); }
QStringList stopWords() const override { return QStringList() << "<EOT>"; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
request["prompt"] = context.prefix.value_or("");
request["suffix"] = context.suffix.value_or("");
request["system"] = context.systemPrompt.value_or("");
}
QString description() const override
{
return "Default Ollama FIM (Fill-in-Middle) template with native format:\n\n"
"{\n"
" \"prompt\": \"<code prefix>\",\n"
" \"suffix\": \"<code suffix>\",\n"
" \"system\": \"<system prompt>\"\n"
"}\n\n"
"Recommended for Ollama models with FIM capability.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case QodeAssist::Providers::ProviderID::Ollama:
return true;
default:
return false;
}
}
};
class OllamaChat : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::Chat; }
QString name() const override { return "Ollama Chat"; }
QStringList stopWords() const override { return QStringList(); }
bool supportsToolHistory() const override { return true; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages;
if (context.systemPrompt) {
messages.append(
QJsonObject{{"role", "system"}, {"content", context.systemPrompt.value()}});
}
if (context.history) {
for (const auto &msg : context.history.value()) {
QJsonObject messageObj;
messageObj["role"] = msg.role;
if (!msg.toolCalls.isEmpty()) {
QJsonArray toolCalls;
for (const auto &call : msg.toolCalls) {
toolCalls.append(QJsonObject{
{"type", "function"},
{"function",
QJsonObject{{"name", call.name}, {"arguments", call.arguments}}}});
}
messageObj["tool_calls"] = toolCalls;
if (!msg.content.isEmpty()) {
messageObj["content"] = msg.content;
}
} else {
messageObj["content"] = msg.content;
// Ollama correlates a tool result to its originating
// call by tool_name; omitting it breaks multi-tool turns.
if (msg.role == QLatin1String("tool") && !msg.toolName.isEmpty()) {
messageObj["tool_name"] = msg.toolName;
}
}
if (msg.images && !msg.images->isEmpty()) {
QJsonArray images;
for (const auto &image : msg.images.value()) {
images.append(image.data);
}
messageObj["images"] = images;
}
messages.append(messageObj);
}
}
request["messages"] = messages;
}
QString description() const override
{
return "Template for Ollama Chat with message array format:\n\n"
"{\n"
" \"messages\": [\n"
" {\"role\": \"system\", \"content\": \"<system prompt>\"},\n"
" {\"role\": \"user\", \"content\": \"<user message>\", \"images\": [\"<base64>\"]},\n"
" {\"role\": \"assistant\", \"content\": \"<assistant response>\"}\n"
" ]\n"
"}\n\n"
"Recommended for Ollama models with chat capability.\n"
"Supports images for multimodal models (e.g., llava).";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case QodeAssist::Providers::ProviderID::Ollama:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,88 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include "templates/PromptTemplate.hpp"
#include "templates/ToolMessages.hpp"
namespace QodeAssist::Templates {
class OpenAI : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::Chat; }
QString name() const override { return "OpenAI"; }
QStringList stopWords() const override { return QStringList(); }
bool supportsToolHistory() const override { return true; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages;
if (context.systemPrompt) {
messages.append(
QJsonObject{{"role", "system"}, {"content", context.systemPrompt.value()}});
}
if (context.history) {
for (const auto &msg : context.history.value()) {
if (appendOpenAIToolMessage(messages, msg)) {
continue;
}
if (msg.images && !msg.images->isEmpty()) {
QJsonArray content;
if (!msg.content.isEmpty()) {
content.append(QJsonObject{{"type", "text"}, {"text", msg.content}});
}
for (const auto &image : msg.images.value()) {
QJsonObject imageBlock;
imageBlock["type"] = "image_url";
QJsonObject imageUrl;
if (image.isUrl) {
imageUrl["url"] = image.data;
} else {
imageUrl["url"] = QString("data:%1;base64,%2").arg(image.mediaType, image.data);
}
imageBlock["image_url"] = imageUrl;
content.append(imageBlock);
}
messages.append(QJsonObject{{"role", msg.role}, {"content", content}});
} else {
messages.append(QJsonObject{{"role", msg.role}, {"content", msg.content}});
}
}
}
request["messages"] = messages;
}
QString description() const override
{
return "Template for OpenAI models (GPT series):\n\n"
"{\n"
" \"messages\": [\n"
" {\"role\": \"system\", \"content\": \"<system prompt>\"},\n"
" {\"role\": \"user\", \"content\": \"<user message>\"},\n"
" {\"role\": \"assistant\", \"content\": \"<assistant response>\"}\n"
" ]\n"
"}\n\n"
"Standard Chat API format for OpenAI.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case QodeAssist::Providers::ProviderID::OpenAI:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,94 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include "templates/PromptTemplate.hpp"
#include "templates/ToolMessages.hpp"
namespace QodeAssist::Templates {
class OpenAICompatible : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::Chat; }
QString name() const override { return "OpenAI Compatible"; }
QStringList stopWords() const override { return QStringList(); }
bool supportsToolHistory() const override { return true; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages;
if (context.systemPrompt) {
messages.append(
QJsonObject{{"role", "system"}, {"content", context.systemPrompt.value()}});
}
if (context.history) {
for (const auto &msg : context.history.value()) {
if (appendOpenAIToolMessage(messages, msg)) {
continue;
}
if (msg.images && !msg.images->isEmpty()) {
QJsonArray content;
if (!msg.content.isEmpty()) {
content.append(QJsonObject{{"type", "text"}, {"text", msg.content}});
}
for (const auto &image : msg.images.value()) {
QJsonObject imageBlock;
imageBlock["type"] = "image_url";
QJsonObject imageUrl;
if (image.isUrl) {
imageUrl["url"] = image.data;
} else {
imageUrl["url"] = QString("data:%1;base64,%2").arg(image.mediaType, image.data);
}
imageBlock["image_url"] = imageUrl;
content.append(imageBlock);
}
messages.append(QJsonObject{{"role", msg.role}, {"content", content}});
} else {
messages.append(QJsonObject{{"role", msg.role}, {"content", msg.content}});
}
}
}
request["messages"] = messages;
}
QString description() const override
{
return "Generic template for OpenAI API-compatible services:\n\n"
"{\n"
" \"messages\": [\n"
" {\"role\": \"system\", \"content\": \"<system prompt>\"},\n"
" {\"role\": \"user\", \"content\": \"<user message>\"},\n"
" {\"role\": \"assistant\", \"content\": \"<assistant response>\"}\n"
" ]\n"
"}\n\n"
"Works with any service implementing the OpenAI Chat API specification.\n"
"Supports images.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case Providers::ProviderID::OpenAICompatible:
case Providers::ProviderID::OpenRouter:
case Providers::ProviderID::LMStudio:
case Providers::ProviderID::LlamaCpp:
case Providers::ProviderID::Qwen:
case Providers::ProviderID::DeepSeek:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,126 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/PromptTemplate.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Templates {
class OpenAIResponses : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const noexcept override
{
return Templates::TemplateType::Chat;
}
QString name() const override { return "OpenAI Responses"; }
QStringList stopWords() const override { return {}; }
bool supportsToolHistory() const override { return true; }
void prepareRequest(
QJsonObject &request, const LLMCore::ContextData &context) const override
{
if (context.systemPrompt) {
request["instructions"] = context.systemPrompt.value();
}
if (!context.history || context.history->isEmpty()) {
return;
}
QJsonArray input;
for (const auto &msg : context.history.value()) {
if (msg.role == "system") {
continue;
}
if (!msg.toolCalls.isEmpty()) {
if (!msg.content.isEmpty()) {
input.append(QJsonObject{{"role", "assistant"}, {"content", msg.content}});
}
for (const auto &call : msg.toolCalls) {
input.append(QJsonObject{
{"type", "function_call"},
{"call_id", call.id},
{"name", call.name},
{"arguments",
QString::fromUtf8(
QJsonDocument(call.arguments).toJson(QJsonDocument::Compact))}});
}
continue;
}
if (msg.role == "tool") {
input.append(QJsonObject{
{"type", "function_call_output"},
{"call_id", msg.toolCallId},
{"output", msg.content}});
continue;
}
QJsonObject message;
message["role"] = msg.role;
const bool hasImages = msg.images && !msg.images->isEmpty();
if (!hasImages) {
message["content"] = msg.content;
} else {
QJsonArray content;
if (!msg.content.isEmpty()) {
content.append(
QJsonObject{{"type", "input_text"}, {"text", msg.content}});
}
for (const auto &image : msg.images.value()) {
QJsonObject imgObj{{"type", "input_image"}, {"detail", "auto"}};
if (image.isUrl) {
imgObj["image_url"] = image.data;
} else {
imgObj["image_url"]
= QString("data:%1;base64,%2").arg(image.mediaType, image.data);
}
content.append(imgObj);
}
message["content"] = content;
}
input.append(message);
}
request["input"] = input;
}
QString description() const override
{
return "Template for OpenAI Responses API:\n\n"
"Simple request:\n"
"{\n"
" \"input\": \"<user message>\"\n"
"}\n\n"
"Multi-turn conversation:\n"
"{\n"
" \"instructions\": \"<system prompt>\",\n"
" \"input\": [\n"
" {\"role\": \"user\", \"content\": \"<message>\"}\n"
" ]\n"
"}";
}
bool isSupportProvider(Providers::ProviderID id) const noexcept override
{
return id == QodeAssist::Providers::ProviderID::OpenAIResponses;
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,38 @@
// Copyright (C) 2025 Povilas Kanapickas <povilas@radix.lt>
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/IPromptProvider.hpp"
#include "templates/PromptTemplate.hpp"
#include "templates/PromptTemplateManager.hpp"
namespace QodeAssist::Templates {
class PromptProviderChat : public IPromptProvider
{
public:
explicit PromptProviderChat(PromptTemplateManager &templateManager)
: m_templateManager(templateManager)
{}
~PromptProviderChat() = default;
PromptTemplate *getTemplateByName(const QString &templateName) const override
{
return m_templateManager.getChatTemplateByName(templateName);
}
QStringList templatesNames() const override { return m_templateManager.chatTemplatesNames(); }
QStringList getTemplatesForProvider(Providers::ProviderID id) const override
{
return m_templateManager.getChatTemplatesForProvider(id);
}
private:
PromptTemplateManager &m_templateManager;
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,37 @@
// Copyright (C) 2025 Povilas Kanapickas <povilas@radix.lt>
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/IPromptProvider.hpp"
#include "templates/PromptTemplateManager.hpp"
namespace QodeAssist::Templates {
class PromptProviderFim : public IPromptProvider
{
public:
explicit PromptProviderFim(PromptTemplateManager &templateManager)
: m_templateManager(templateManager)
{}
~PromptProviderFim() = default;
PromptTemplate *getTemplateByName(const QString &templateName) const override
{
return m_templateManager.getFimTemplateByName(templateName);
}
QStringList templatesNames() const override { return m_templateManager.fimTemplatesNames(); }
QStringList getTemplatesForProvider(Providers::ProviderID id) const override
{
return m_templateManager.getFimTemplatesForProvider(id);
}
private:
PromptTemplateManager &m_templateManager;
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,33 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonObject>
#include <QList>
#include <QString>
#include "llmcore/ContextData.hpp"
#include "providers/ProviderID.hpp"
namespace QodeAssist::Templates {
enum class TemplateType { Chat, FIM, FIMOnChat };
class PromptTemplate
{
public:
virtual ~PromptTemplate() = default;
virtual TemplateType type() const = 0;
virtual QString name() const = 0;
virtual QStringList stopWords() const = 0;
virtual void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const = 0;
virtual QString description() const = 0;
virtual bool isSupportProvider(Providers::ProviderID id) const = 0;
virtual QString endpoint() const { return {}; }
virtual bool supportsToolHistory() const { return false; }
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,84 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#include "templates/PromptTemplateManager.hpp"
#include <QMessageBox>
namespace QodeAssist::Templates {
PromptTemplateManager &PromptTemplateManager::instance()
{
static PromptTemplateManager instance;
return instance;
}
QStringList PromptTemplateManager::fimTemplatesNames() const
{
return m_fimTemplates.keys();
}
QStringList PromptTemplateManager::chatTemplatesNames() const
{
return m_chatTemplates.keys();
}
QStringList PromptTemplateManager::getFimTemplatesForProvider(Providers::ProviderID id)
{
QStringList templateList;
for (const auto tmpl : m_fimTemplates) {
if (tmpl->isSupportProvider(id)) {
templateList.append(tmpl->name());
}
}
return templateList;
}
QStringList PromptTemplateManager::getChatTemplatesForProvider(Providers::ProviderID id)
{
QStringList templateList;
for (const auto tmpl : m_chatTemplates) {
if (tmpl->isSupportProvider(id)) {
templateList.append(tmpl->name());
}
}
return templateList;
}
PromptTemplateManager::~PromptTemplateManager()
{
qDeleteAll(m_fimTemplates);
}
PromptTemplate *PromptTemplateManager::getFimTemplateByName(const QString &templateName)
{
if (!m_fimTemplates.contains(templateName)) {
QMessageBox::warning(
nullptr,
QObject::tr("Template Not Found"),
QObject::tr("Template '%1' was not found or has been updated. Please re-set new one.")
.arg(templateName));
return m_fimTemplates.first();
}
return m_fimTemplates[templateName];
}
PromptTemplate *PromptTemplateManager::getChatTemplateByName(const QString &templateName)
{
if (!m_chatTemplates.contains(templateName)) {
QMessageBox::warning(
nullptr,
QObject::tr("Template Not Found"),
QObject::tr("Template '%1' was not found or has been updated. Please re-set new one.")
.arg(templateName));
return m_chatTemplates.first();
}
return m_chatTemplates[templateName];
}
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,50 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QMap>
#include <QString>
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class PromptTemplateManager
{
public:
static PromptTemplateManager &instance();
~PromptTemplateManager();
template<typename T>
void registerTemplate()
{
static_assert(std::is_base_of<PromptTemplate, T>::value, "T must inherit from PromptTemplate");
T *template_ptr = new T();
QString name = template_ptr->name();
m_fimTemplates[name] = template_ptr;
if (template_ptr->type() == TemplateType::Chat) {
m_chatTemplates[name] = template_ptr;
}
}
PromptTemplate *getFimTemplateByName(const QString &templateName);
PromptTemplate *getChatTemplateByName(const QString &templateName);
QStringList fimTemplatesNames() const;
QStringList chatTemplatesNames() const;
QStringList getFimTemplatesForProvider(Providers::ProviderID id);
QStringList getChatTemplatesForProvider(Providers::ProviderID id);
private:
PromptTemplateManager() = default;
PromptTemplateManager(const PromptTemplateManager &) = delete;
PromptTemplateManager &operator=(const PromptTemplateManager &) = delete;
QMap<QString, PromptTemplate *> m_fimTemplates;
QMap<QString, PromptTemplate *> m_chatTemplates;
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/PromptTemplate.hpp"
#include <QJsonArray>
namespace QodeAssist::Templates {
class Qwen25CoderFIM : public Templates::PromptTemplate
{
public:
QString name() const override { return "Qwen2.5 Coder FIM"; }
Templates::TemplateType type() const override { return Templates::TemplateType::FIM; }
QString endpoint() const override { return QStringLiteral("/api/generate"); }
QStringList stopWords() const override { return QStringList() << "<|endoftext|>" << "<|EOT|>"; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
request["prompt"] = QString("<|fim_prefix|>%1<|fim_suffix|>%2<|fim_middle|>")
.arg(context.prefix.value_or(""), context.suffix.value_or(""));
request["system"] = context.systemPrompt.value_or("");
}
QString description() const override
{
return "Template for Qwen models with FIM support:\n\n"
"{\n"
" \"prompt\": \"<|fim_prefix|><code prefix><|fim_suffix|><code "
"suffix><|fim_middle|>\",\n"
" \"system\": \"<system prompt>\"\n"
"}\n\n"
"Ideal for code completion with Qwen models.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case QodeAssist::Providers::ProviderID::Ollama:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,65 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class Qwen3CoderFIM : public Templates::PromptTemplate
{
public:
QString name() const override { return "Qwen3 Coder FIM"; }
Templates::TemplateType type() const override { return Templates::TemplateType::FIMOnChat; }
QStringList stopWords() const override { return QStringList() << "<|im_end|>"; }
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
QJsonArray messages;
messages.append(
QJsonObject{{"role", "system"}, {"content", context.systemPrompt.value_or("")}});
messages.append(
QJsonObject{
{"role", "user"},
{"content",
QString("<|fim_prefix|>%1<|fim_suffix|>%2<|fim_middle|>")
.arg(context.prefix.value_or(""), context.suffix.value_or(""))}});
request["messages"] = messages;
}
QString description() const override
{
return "Template for supporting Qwen3 Coder FIM format via chat template:\n\n"
"{\n"
" \"messages\": [\n"
" {\n"
" \"role\": \"system\",\n"
" \"content\": \"You are a code completion assistant.\"\n"
" },\n"
" {\n"
" \"role\": \"user\",\n"
" \"content\": \"<|fim_prefix|>code<|fim_suffix|>code<|fim_middle|>\"\n"
" }\n"
" ]\n"
"}\n\n";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case Providers::ProviderID::Ollama:
case Providers::ProviderID::LMStudio:
case Providers::ProviderID::OpenRouter:
case Providers::ProviderID::OpenAICompatible:
case Providers::ProviderID::LlamaCpp:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,48 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class StarCoder2Fim : public Templates::PromptTemplate
{
public:
Templates::TemplateType type() const override { return Templates::TemplateType::FIM; }
QString name() const override { return "StarCoder2 FIM"; }
QString endpoint() const override { return QStringLiteral("/api/generate"); }
QStringList stopWords() const override
{
return QStringList() << "<|endoftext|>" << "<file_sep>" << "<fim_prefix>" << "<fim_suffix>"
<< "<fim_middle>";
}
void prepareRequest(QJsonObject &request, const LLMCore::ContextData &context) const override
{
request["prompt"] = QString("<fim_prefix>%1<fim_suffix>%2<fim_middle>")
.arg(context.prefix.value_or(""), context.suffix.value_or(""));
request["system"] = context.systemPrompt.value_or("");
}
QString description() const override
{
return "Template for StarCoder2 with FIM format:\n\n"
"{\n"
" \"prompt\": \"<fim_prefix><code prefix><fim_suffix><code suffix><fim_middle>\",\n"
" \"system\": \"<system prompt>\"\n"
"}\n\n"
"Includes stop words to prevent token duplication.";
}
bool isSupportProvider(Providers::ProviderID id) const override
{
switch (id) {
case QodeAssist::Providers::ProviderID::Ollama:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,52 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "templates/PromptTemplateManager.hpp"
#include "templates/Alpaca.hpp"
#include "templates/ChatML.hpp"
#include "templates/Claude.hpp"
#include "templates/CodeLlamaFim.hpp"
#include "templates/CodeLlamaQMLFim.hpp"
#include "templates/MistralAI.hpp"
#include "templates/Ollama.hpp"
#include "templates/OpenAI.hpp"
#include "templates/OpenAICompatible.hpp"
#include "templates/OpenAIResponses.hpp"
#include "templates/GoogleAI.hpp"
#include "templates/Llama2.hpp"
#include "templates/Llama3.hpp"
#include "templates/LlamaCppFim.hpp"
#include "templates/Qwen25CoderFIM.hpp"
#include "templates/Qwen3CoderFIM.hpp"
#include "templates/StarCoder2Fim.hpp"
namespace QodeAssist::Templates {
inline void registerTemplates()
{
auto &templateManager = Templates::PromptTemplateManager::instance();
templateManager.registerTemplate<OllamaChat>();
templateManager.registerTemplate<OllamaFim>();
templateManager.registerTemplate<CodeLlamaFim>();
templateManager.registerTemplate<Claude>();
templateManager.registerTemplate<OpenAI>();
templateManager.registerTemplate<OpenAIResponses>();
templateManager.registerTemplate<MistralAIFim>();
templateManager.registerTemplate<MistralAIChat>();
templateManager.registerTemplate<CodeLlamaQMLFim>();
templateManager.registerTemplate<ChatML>();
templateManager.registerTemplate<Llama2>();
templateManager.registerTemplate<Llama3>();
templateManager.registerTemplate<StarCoder2Fim>();
templateManager.registerTemplate<Qwen25CoderFIM>();
templateManager.registerTemplate<Qwen3CoderFIM>();
templateManager.registerTemplate<OpenAICompatible>();
templateManager.registerTemplate<Alpaca>();
templateManager.registerTemplate<GoogleAI>();
templateManager.registerTemplate<LlamaCppFim>();
}
} // namespace QodeAssist::Templates

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include "llmcore/ContextData.hpp"
namespace QodeAssist::Templates {
inline bool appendOpenAIToolMessage(QJsonArray &messages, const LLMCore::Message &msg)
{
if (!msg.toolCalls.isEmpty()) {
QJsonArray toolCalls;
for (const auto &call : msg.toolCalls) {
toolCalls.append(QJsonObject{
{"id", call.id},
{"type", "function"},
{"function",
QJsonObject{
{"name", call.name},
{"arguments",
QString::fromUtf8(
QJsonDocument(call.arguments).toJson(QJsonDocument::Compact))}}}});
}
QJsonObject toolMessage{{"role", "assistant"}, {"tool_calls", toolCalls}};
toolMessage["content"] = msg.content.isEmpty() ? QJsonValue() : QJsonValue(msg.content);
messages.append(toolMessage);
return true;
}
if (msg.role == QLatin1String("tool")) {
messages.append(QJsonObject{
{"role", "tool"}, {"tool_call_id", msg.toolCallId}, {"content", msg.content}});
return true;
}
return false;
}
} // namespace QodeAssist::Templates