mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-07-21 10:40:57 -04:00
refactor: Restructure project into sources/tests layout and dissolve PluginLLMCore
This commit is contained in:
75
sources/templates/Alpaca.hpp
Normal file
75
sources/templates/Alpaca.hpp
Normal 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
|
||||
76
sources/templates/ChatML.hpp
Normal file
76
sources/templates/ChatML.hpp
Normal 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
|
||||
150
sources/templates/Claude.hpp
Normal file
150
sources/templates/Claude.hpp
Normal 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
|
||||
47
sources/templates/CodeLlamaFim.hpp
Normal file
47
sources/templates/CodeLlamaFim.hpp
Normal 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
|
||||
48
sources/templates/CodeLlamaQMLFim.hpp
Normal file
48
sources/templates/CodeLlamaQMLFim.hpp
Normal 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
|
||||
161
sources/templates/GoogleAI.hpp
Normal file
161
sources/templates/GoogleAI.hpp
Normal 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
|
||||
24
sources/templates/IPromptProvider.hpp
Normal file
24
sources/templates/IPromptProvider.hpp
Normal 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
|
||||
73
sources/templates/Llama2.hpp
Normal file
73
sources/templates/Llama2.hpp
Normal 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
|
||||
80
sources/templates/Llama3.hpp
Normal file
80
sources/templates/Llama3.hpp
Normal 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
|
||||
55
sources/templates/LlamaCppFim.hpp
Normal file
55
sources/templates/LlamaCppFim.hpp
Normal 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
|
||||
121
sources/templates/MistralAI.hpp
Normal file
121
sources/templates/MistralAI.hpp
Normal 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
|
||||
128
sources/templates/Ollama.hpp
Normal file
128
sources/templates/Ollama.hpp
Normal 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
|
||||
88
sources/templates/OpenAI.hpp
Normal file
88
sources/templates/OpenAI.hpp
Normal 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
|
||||
94
sources/templates/OpenAICompatible.hpp
Normal file
94
sources/templates/OpenAICompatible.hpp
Normal 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
|
||||
126
sources/templates/OpenAIResponses.hpp
Normal file
126
sources/templates/OpenAIResponses.hpp
Normal 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
|
||||
38
sources/templates/PromptProviderChat.hpp
Normal file
38
sources/templates/PromptProviderChat.hpp
Normal 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
|
||||
37
sources/templates/PromptProviderFim.hpp
Normal file
37
sources/templates/PromptProviderFim.hpp
Normal 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
|
||||
33
sources/templates/PromptTemplate.hpp
Normal file
33
sources/templates/PromptTemplate.hpp
Normal 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
|
||||
84
sources/templates/PromptTemplateManager.cpp
Normal file
84
sources/templates/PromptTemplateManager.cpp
Normal 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
|
||||
50
sources/templates/PromptTemplateManager.hpp
Normal file
50
sources/templates/PromptTemplateManager.hpp
Normal 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
|
||||
46
sources/templates/Qwen25CoderFIM.hpp
Normal file
46
sources/templates/Qwen25CoderFIM.hpp
Normal 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
|
||||
65
sources/templates/Qwen3CoderFIM.hpp
Normal file
65
sources/templates/Qwen3CoderFIM.hpp
Normal 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
|
||||
48
sources/templates/StarCoder2Fim.hpp
Normal file
48
sources/templates/StarCoder2Fim.hpp
Normal 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
|
||||
52
sources/templates/Templates.hpp
Normal file
52
sources/templates/Templates.hpp
Normal 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
|
||||
46
sources/templates/ToolMessages.hpp
Normal file
46
sources/templates/ToolMessages.hpp
Normal 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
|
||||
Reference in New Issue
Block a user