refactor: add to template agent roles

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

View File

@@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.16)
list(APPEND CMAKE_PREFIX_PATH "/Users/palm1r/Qt/Qt Creator.sdk/lib/cmake/QtCreator")
project(QodeAssist)
set(CMAKE_AUTOMOC ON)
@@ -38,7 +40,6 @@ add_definitions(
add_subdirectory(sources)
add_subdirectory(logger)
add_subdirectory(pluginllmcore)
add_subdirectory(settings)
add_subdirectory(UIControls)
add_subdirectory(ChatView)
@@ -70,7 +71,6 @@ add_qtc_plugin(QodeAssist
QtCreator::Utils
QtCreator::CPlusPlus
LLMQore
PluginLLMCore
ProvidersConfig
Agents
Skills
@@ -84,42 +84,6 @@ add_qtc_plugin(QodeAssist
QodeAssisttr.h
LLMClientInterface.hpp LLMClientInterface.cpp
RefactorContextHelper.hpp
templates/Templates.hpp
templates/CodeLlamaFim.hpp
templates/Ollama.hpp
templates/Claude.hpp
templates/OpenAI.hpp
templates/MistralAI.hpp
templates/StarCoder2Fim.hpp
templates/Qwen25CoderFIM.hpp
templates/OpenAICompatible.hpp
templates/Llama3.hpp
templates/ChatML.hpp
templates/Alpaca.hpp
templates/Llama2.hpp
templates/CodeLlamaQMLFim.hpp
templates/GoogleAI.hpp
templates/LlamaCppFim.hpp
templates/Qwen3CoderFIM.hpp
templates/OpenAIResponses.hpp
providers/Providers.hpp
providers/ProviderUrlUtils.hpp
providers/OllamaProvider.hpp providers/OllamaProvider.cpp
providers/OllamaCompatProvider.hpp providers/OllamaCompatProvider.cpp
providers/ClaudeProvider.hpp providers/ClaudeProvider.cpp
providers/OpenAIProvider.hpp providers/OpenAIProvider.cpp
providers/MistralAIProvider.hpp providers/MistralAIProvider.cpp
providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp
providers/LMStudioResponsesProvider.hpp providers/LMStudioResponsesProvider.cpp
providers/OpenAICompatProvider.hpp providers/OpenAICompatProvider.cpp
providers/OpenRouterAIProvider.hpp providers/OpenRouterAIProvider.cpp
providers/GoogleAIProvider.hpp providers/GoogleAIProvider.cpp
providers/LlamaCppProvider.hpp providers/LlamaCppProvider.cpp
providers/CodestralProvider.hpp providers/CodestralProvider.cpp
providers/OpenAIResponsesProvider.hpp providers/OpenAIResponsesProvider.cpp
providers/QwenProvider.hpp providers/QwenProvider.cpp
providers/QwenResponsesProvider.hpp providers/QwenResponsesProvider.cpp
providers/DeepSeekProvider.hpp providers/DeepSeekProvider.cpp
QodeAssist.qrc
LSPCompletion.hpp
LLMSuggestion.hpp LLMSuggestion.cpp

View File

@@ -32,6 +32,7 @@
#include <SessionManager.hpp>
#include "ChatAgentController.hpp"
#include "AgentRole.hpp"
#include "ChatAssistantSettings.hpp"
#include "ChatCompressor.hpp"
#include "ChatHistoryStore.hpp"
@@ -393,6 +394,43 @@ void ChatRootView::setCurrentChatAgent(const QString &name)
m_agentController->setCurrentAgent(name);
}
QStringList ChatRootView::availableRoles() const
{
return m_availableRoles;
}
QString ChatRootView::currentRole() const
{
return m_currentRole;
}
void ChatRootView::setCurrentRole(const QString &roleId)
{
if (m_currentRole == roleId)
return;
m_currentRole = roleId;
emit currentRoleChanged();
}
void ChatRootView::loadAvailableRoles()
{
QStringList ids;
const QList<Settings::AgentRole> roles = Settings::AgentRolesManager::loadAllRoles();
ids.reserve(roles.size());
for (const auto &r : roles)
ids << r.id;
if (ids != m_availableRoles) {
m_availableRoles = ids;
emit availableRolesChanged();
}
if (!m_availableRoles.isEmpty() && !m_availableRoles.contains(m_currentRole))
setCurrentRole(m_availableRoles.contains(QStringLiteral("developer"))
? QStringLiteral("developer")
: m_availableRoles.first());
}
QVariantList ChatRootView::searchSkills(const QString &query) const
{
QVariantList results;
@@ -501,6 +539,7 @@ void ChatRootView::dispatchSend(
m_clientInterface->setSkillsManager(skillsManager());
m_clientInterface->setSessionManager(sessionManager());
m_clientInterface->setActiveAgent(currentChatAgent());
m_clientInterface->setActiveRole(currentRole());
m_clientInterface->sendMessage(message, attachments, linkedFiles);
m_fileManager->clearIntermediateStorage();

View File

@@ -59,6 +59,8 @@ class ChatRootView : public QQuickItem
Q_PROPERTY(bool isThinkingSupport READ isThinkingSupport NOTIFY isThinkingSupportChanged FINAL)
Q_PROPERTY(QStringList availableChatAgents READ availableChatAgents NOTIFY availableChatAgentsChanged FINAL)
Q_PROPERTY(QString currentChatAgent READ currentChatAgent WRITE setCurrentChatAgent NOTIFY currentChatAgentChanged FINAL)
Q_PROPERTY(QStringList availableRoles READ availableRoles NOTIFY availableRolesChanged FINAL)
Q_PROPERTY(QString currentRole READ currentRole WRITE setCurrentRole NOTIFY currentRoleChanged FINAL)
Q_PROPERTY(bool isCompressing READ isCompressing NOTIFY isCompressingChanged FINAL)
Q_PROPERTY(bool isInEditor READ isInEditor NOTIFY isInEditorChanged FINAL)
Q_PROPERTY(QString chatTitle READ chatTitle NOTIFY chatTitleChanged FINAL)
@@ -155,6 +157,11 @@ public:
QString currentChatAgent() const;
void setCurrentChatAgent(const QString &name);
Q_INVOKABLE void loadAvailableRoles();
QStringList availableRoles() const;
QString currentRole() const;
void setCurrentRole(const QString &roleId);
int currentMessageTotalEdits() const;
int currentMessageAppliedEdits() const;
int currentMessagePendingEdits() const;
@@ -208,6 +215,8 @@ signals:
void availableChatAgentsChanged();
void currentChatAgentChanged();
void availableRolesChanged();
void currentRoleChanged();
void isCompressingChanged();
void compressionCompleted(const QString &compressedChatPath);
@@ -262,6 +271,9 @@ private:
QString m_lastInfoMessage;
QString m_currentRole = QStringLiteral("developer");
QStringList m_availableRoles;
ChatCompressor *m_chatCompressor;
ChatAgentController *m_agentController;
FileEditController *m_fileEditController;

View File

@@ -30,7 +30,10 @@
#include <ConversationHistory.hpp>
#include <Message.hpp>
#include <ContextRenderer.hpp>
#include <Session.hpp>
#include <QDir>
#include <SessionManager.hpp>
#include <SystemPromptBuilder.hpp>
@@ -75,6 +78,11 @@ void ClientInterface::setActiveAgent(const QString &agentName)
m_activeAgent = agentName;
}
void ClientInterface::setActiveRole(const QString &roleId)
{
m_activeRoleId = roleId;
}
void ClientInterface::sendMessage(
const QString &message,
const QList<QString> &attachments,
@@ -175,9 +183,15 @@ void ClientInterface::sendMessage(
return;
}
Tools::registerQodeAssistTools(client->tools());
if (m_skillsManager)
Tools::registerSkillTool(client->tools(), m_skillsManager);
auto *project = ProjectExplorer::ProjectManager::startupProject();
Templates::ContextRenderer::Bindings bindings;
bindings.projectDir = project ? project->projectDirectory().toFSPathString() : QString();
bindings.homeDir = QDir::homePath();
bindings.roleId = m_activeRoleId;
session->setContextBindings(bindings);
if (m_sessionManager)
m_sessionManager->toolContributors().contribute(client->tools());
client->setMaxToolContinuations(Settings::toolsSettings().maxToolContinuations());
client->setTransferTimeout(
static_cast<int>(Settings::generalSettings().requestTimeout() * 1000));

View File

@@ -37,6 +37,7 @@ public:
void setSkillsManager(Skills::SkillsManager *skillsManager);
void setSessionManager(SessionManager *sessionManager);
void setActiveAgent(const QString &agentName);
void setActiveRole(const QString &roleId);
void sendMessage(
const QString &message,
@@ -98,6 +99,7 @@ private:
Skills::SkillsManager *m_skillsManager = nullptr;
QPointer<SessionManager> m_sessionManager;
QString m_activeAgent;
QString m_activeRoleId;
QString m_chatFilePath;
QHash<QString, RequestContext> m_activeRequests;

View File

@@ -152,6 +152,19 @@ ChatRootView {
root.loadAvailableChatAgents()
}
}
roleSelector {
model: root.availableRoles
displayText: root.currentRole
onActivated: function(index) {
root.currentRole = root.availableRoles[index]
}
Component.onCompleted: root.loadAvailableRoles()
popup.onAboutToShow: {
root.loadAvailableRoles()
}
}
}
RowLayout {

View File

@@ -25,6 +25,7 @@ Rectangle {
property alias contextButton: contextButtonId
property alias settingsButton: settingsButtonId
property alias agentSelector: agentSelectorId
property alias roleSelector: roleSelectorId
property alias relocateTooltip: relocateTooltipId
color: palette.window.hslLightness > 0.5 ?
@@ -141,7 +142,22 @@ Rectangle {
QoAToolTip {
visible: agentSelectorId.hovered
delay: 250
text: qsTr("Select chat agent (provider, model and role come from the agent)")
text: qsTr("Select chat agent (provider and model come from the agent)")
}
}
QoAComboBox {
id: roleSelectorId
implicitHeight: 25
model: []
currentIndex: 0
QoAToolTip {
visible: roleSelectorId.hovered
delay: 250
text: qsTr("Select the role (system prompt) for the chat")
}
}
}

View File

@@ -334,14 +334,7 @@ Templates::ContextData LLMClientInterface::prepareContext(
Context::DocumentContextReader
reader(documentInfo.document, documentInfo.mimeType, documentInfo.filePath);
const PluginLLMCore::ContextData legacy
= reader.prepareContext(lineNumber, cursorPosition, m_completeSettings);
Templates::ContextData context;
context.prefix = legacy.prefix;
context.suffix = legacy.suffix;
context.fileContext = legacy.fileContext;
return context;
return reader.prepareContext(lineNumber, cursorPosition, m_completeSettings);
}
Context::ContextManager *LLMClientInterface::contextManager() const

View File

@@ -17,8 +17,6 @@
#include "widgets/EditorChatButtonHandler.hpp"
#include "widgets/RefactorWidgetHandler.hpp"
#include <languageclient/client.h>
#include <pluginllmcore/IPromptProvider.hpp>
#include <pluginllmcore/IProviderRegistry.hpp>
namespace QodeAssist {

View File

@@ -21,7 +21,7 @@
#include <context/DocumentReaderQtCreator.hpp>
#include <context/Utils.hpp>
#include <logger/Logger.hpp>
#include <pluginllmcore/ResponseCleaner.hpp>
#include <sources/common/ResponseCleaner.hpp>
#include <settings/QuickRefactorSettings.hpp>
#include <settings/ToolsSettings.hpp>
@@ -378,7 +378,7 @@ void QuickRefactorHandler::handleLLMResponse(
if (isComplete) {
m_isRefactoringInProgress = false;
QString cleanedResponse = PluginLLMCore::ResponseCleaner::clean(response);
QString cleanedResponse = ResponseCleaner::clean(response);
RefactorResult result;
result.newText = cleanedResponse;

View File

@@ -21,7 +21,7 @@ target_link_libraries(Context
QtCreator::Utils
QtCreator::ProjectExplorer
PRIVATE
PluginLLMCore
Common
QodeAssistSettings
)

View File

@@ -254,7 +254,7 @@ CopyrightInfo DocumentContextReader::copyrightInfo() const
return m_copyrightInfo;
}
PluginLLMCore::ContextData DocumentContextReader::prepareContext(
Templates::ContextData DocumentContextReader::prepareContext(
int lineNumber, int cursorPosition, const Settings::CodeCompletionSettings &settings) const
{
QString contextBefore;

View File

@@ -7,7 +7,7 @@
#include <texteditor/textdocument.h>
#include <QTextDocument>
#include <pluginllmcore/ContextData.hpp>
#include <sources/common/ContextData.hpp>
#include <settings/CodeCompletionSettings.hpp>
namespace QodeAssist::Context {
@@ -58,7 +58,7 @@ public:
CopyrightInfo copyrightInfo() const;
PluginLLMCore::ContextData prepareContext(
Templates::ContextData prepareContext(
int lineNumber, int cursorPosition, const Settings::CodeCompletionSettings &settings) const;
private:

View File

@@ -43,18 +43,26 @@ exactly like the curl body.
No runtime toggles: thinking / tools / streaming are **fixed per agent**. A thinking
agent literally carries the `thinking` fields; a non-thinking variant is a separate
file. There is no `{% if thinking %}` and no `thinkingEnabled` flag threaded into
rendering. `system` uses `{% if existsIn(ctx, "system_prompt") %}` only because that
is about *presence of data*, not a mode toggle.
file. There is no `{% if thinking %}` in the body. `system` uses
`{% if existsIn(ctx, "system_prompt") %}` only because that is about *presence of
data*, not a mode toggle. `enable_thinking` / `enable_tools` are **capability hints**
(used for UI badges and to decide tool-definition injection) — the body is the source
of truth for what is actually sent, so a thinking agent's body must carry the thinking
fields regardless of the flag.
Outside the body:
- `model`supplied by the **client** from its own settings; never in the profile.
Google embeds the model in the URL, so its `endpoint` uses a `${MODEL}` placeholder
the client resolves (same substitution style as `${PROJECT_DIR}` / `${HOME}`).
- `model`the TOML `model` is the **default**; a per-agent override chosen in
QodeAssist settings wins. Overrides are stored in `agent_models.json`
(agentName → model) and applied by `AgentFactory` when it builds the agent
(`AgentFactory::effectiveModel`/`setModelOverride`); `Session` still seeds the
payload `model` from the resolved `cfg.model`. URL-model providers (Google) put a
`${MODEL}` placeholder in `endpoint`; `Session` substitutes the resolved model into
the endpoint before sending (same substitution style as `${PROJECT_DIR}`/`${HOME}`),
so the override drives the URL too.
- `tools` — injected by the **provider** when `enable_tools` is set (tool
definitions are dynamic, from `ToolsManager`; they can't be authored in TOML).
- `stream` — always on. Literal `"stream": true` in the body for OpenAI / Claude /
Mistral; encoded in the `endpoint` URL for Google.
Mistral / Responses / Ollama; encoded in the `endpoint` URL for Google.
### 2. `include` re-enabled as whitelisted partials
@@ -71,19 +79,20 @@ A missing/typo'd partial is a **load-time** error.
### 3. `extends` shares config down a hierarchy
`extends` already exists (`resolveExtends` + `deepMerge` + `abstract`/`hidden`); it
keeps doing what it does, now over the structured `[body]` too. Typical 23 levels:
keeps doing what it does, now over the structured `[body]` too. A flat 2 levels — each
provider base sets `system_prompt = """{{ agent_role("developer") }}"""` (the role text
comes from the role JSON via the `agent_role` callback; see below). No shared root base:
```
chat_base (abstract) → system_prompt (shared by all)
├─ openai_base (abstract) → provider/endpoint/enable_tools + [body]
│ ├─ openai_chat → name
│ ├─ mistral_chat → name, provider, endpoint
│ └─ mistral_reasoning → + [body].reasoning_effort
├─ anthropic_base (abstract) → provider/endpoint/thinking + [body]
│ ├─ claude_chat name
│ └─ claude_sonnet → + [body.output_config].effort
└─ google_base (abstract) → provider/endpoint + [body]
└─ gemini_chat → name
openai_base (abstract) → system_prompt + provider/endpoint/enable_tools + [body]
├─ openai_chat → name
├─ mistral_chat → name, provider, endpoint
─ mistral_reasoning → + enable_thinking
anthropic_base (abstract) → system_prompt + provider/endpoint + [body]
├─ claude_chat → name
─ claude_sonnet+ [body] thinking / output_config
google_base (abstract) → system_prompt + provider + [body]
└─ gemini_chat → endpoint (${MODEL}) + [body.generationConfig] thinkingConfig
```
Notes:
@@ -98,21 +107,33 @@ Notes:
by `model` become one agent (the client picks the model). A separate file is only
needed when the body genuinely differs (effort, no-thinking, …).
### `role` + `context` merged into `system_prompt`
### System prompt — a composable template with building blocks
The old `role` (static) and `context` (jinja, reads files) are two layers of the
same system prompt (`SystemPromptBuilder` layers `agent.role` / `agent.context`).
Merge into one `system_prompt` field, always rendered through `ContextRenderer`
(static text passes through; dynamic parts use `{% %}`), e.g. README via
`file_exists` instead of the `set readme / if length` dance. `Session` collapses the
two layers into one rendered layer.
The old `role` (static text) and `context` (jinja) layers collapse into one
`agent.system` layer in `Session`, rendered through `ContextRenderer`. The agent's
`system_prompt` field IS that template, and the user edits it (in the profile) to
compose the prompt from building-block callbacks:
- `{{ agent_role("<id>") }}` — insert a role's text (Developer/Reviewer/Researcher…).
Implemented as a `ContextRenderer` callback (`registerAgentRole`) that reads
`userResourcePath("qodeassist/agent_roles/<id>.json")["systemPrompt"]`. Returns "" if
missing. Lives in `sources/agents` (no dependency on `settings/`), so it works in the
plugin and bench. The role text lives once in the role JSON (managed by the settings
Roles UI); the chat bases just carry `system_prompt = """{{ agent_role("developer") }}"""`.
- `{{ read_file("...") }}` / `file_exists` / `${PROJECT_DIR}` / `${HOME}` — existing
`ContextRenderer` helpers, composable in the same template.
So a profile can do `system_prompt = """{{ agent_role("developer") }}\n\n{{ read_file("…") }}"""`.
`qodeassist.cpp` calls `AgentRolesManager::ensureDefaultRoles()` at startup so the default
role JSONs exist before agents load. There is NO per-agent settings override — the edit
point is the profile's `system_prompt`. Code-completion/FIM agents set no `system_prompt`.
## Worked examples
OpenAI base:
```toml
extends = "Chat Base"
abstract = true
system_prompt = """{{ agent_role("developer") }}"""
provider_instance = "OpenAI (Chat Completions)"
endpoint = "/chat/completions"
enable_tools = true
@@ -140,8 +161,8 @@ reasoning_effort = "medium"
Claude base (literally the curl body):
```toml
extends = "Chat Base"
abstract = true
system_prompt = """{{ agent_role("developer") }}"""
provider_instance = "Claude"
endpoint = "/v1/messages"
enable_thinking = true
@@ -170,8 +191,8 @@ effort = "medium"
Google base (`${MODEL}` in endpoint; streaming in the URL):
```toml
extends = "Chat Base"
abstract = true
system_prompt = """{{ agent_role("developer") }}"""
provider_instance = "Google AI"
endpoint = "/models/${MODEL}:streamGenerateContent?alt=sse"
enable_thinking = true
@@ -337,10 +358,16 @@ In `AgentConfig` / `AgentLoader`:
as JSON (render once against a synthetic context with tool_use / tool_result /
image). Catches breakage at startup, not mid-conversation.
In the client / provider layer:
- The client sets `model` from its settings (and resolves `${MODEL}` in the
endpoint); `Session` no longer seeds the payload with `cfg.model`.
- The provider keeps injecting `tools` when `enable_tools` is set.
Model selection (per-agent override):
- `AgentFactory` owns an agentName → model map loaded from `agent_models.json`
(`loadModelOverrides`/`saveModelOverrides`). `create()`/`createFromFile()` apply the
override into the built `AgentConfig`; `effectiveModel()` exposes the resolved value;
`setModelOverride()` persists. The settings UI (`AgentDetailPane`) edits it via an
editable Model field; list/roster widgets display `effectiveModel`.
- `Session` substitutes `${MODEL}` in `cfg.endpoint` with the resolved model before
`sendRequest` (covers Google, whose model lives in the URL), and still seeds the
payload `model` from `cfg.model`. The provider keeps injecting `tools` when
`enable_tools` is set.
In `Session`:
- Collapse the `agent.role` + `agent.context` system-prompt layers into one rendered
@@ -353,6 +380,7 @@ In `Session`:
2. `[body]`-table model in `JsonPromptTemplate` + loader; delete
sampling/thinking/`applySampling`; drop `thinkingEnabled`.
3. `system_prompt` merge in loader + `Session`.
4. `model` from client (+ `${MODEL}` endpoint substitution); convert bundled agents
to the base/partials/`extends` layout.
4. Per-agent model override in `AgentFactory` (`agent_models.json`) + `${MODEL}`
endpoint substitution in `Session`; editable Model field in settings; convert
bundled agents to the base/partials/`extends` layout.
5. Load-time validation (partial resolves, body parses).

View File

@@ -15,9 +15,8 @@
#include <QSaveFile>
#include <QTimer>
#include <LLMQore/ToolsManager.hpp>
#include <logger/Logger.hpp>
#include <pluginllmcore/Provider.hpp>
#include <pluginllmcore/ProvidersManager.hpp>
#include <settings/McpSettings.hpp>
namespace QodeAssist::Mcp {
@@ -176,18 +175,14 @@ QList<McpServerConnection *> McpClientsManager::connections() const
return m_connections;
}
QList<PluginLLMCore::Provider *> McpClientsManager::toolsCapableProviders() const
void McpClientsManager::registerToolsOn(::LLMQore::ToolsManager *tools) const
{
QList<PluginLLMCore::Provider *> out;
auto &pm = PluginLLMCore::ProvidersManager::instance();
for (const QString &name : pm.providersNames()) {
auto *p = pm.getProviderByName(name);
if (!p)
continue;
if (p->capabilities().testFlag(PluginLLMCore::ProviderCapability::Tools))
out.append(p);
if (!tools)
return;
for (auto *c : m_connections) {
if (c)
c->registerToolsOn(tools);
}
return out;
}
QJsonObject McpClientsManager::builtinServers()
@@ -319,8 +314,6 @@ void McpClientsManager::loadFromDisk()
newConfigs.append(McpServerConfig::fromJson(it.key(), it.value().toObject()));
}
const auto providers = toolsCapableProviders();
const bool masterEnabled = Settings::mcpSettings().enableMcpClients();
QList<McpServerConnection *> keep;
@@ -350,7 +343,6 @@ void McpClientsManager::loadFromDisk()
existing->deleteLater();
}
c = new McpServerConnection(cfg, this);
c->setProviders(providers);
connect(
c,
&McpServerConnection::stateChanged,

View File

@@ -35,6 +35,8 @@ public:
bool removeServer(const QString &name);
void reload();
void registerToolsOn(::LLMQore::ToolsManager *tools) const;
signals:
void serversChanged();
void writeFailed(const QString &reason);
@@ -50,7 +52,6 @@ private:
void setupWatcher();
void updateWatchedPaths();
QList<PluginLLMCore::Provider *> toolsCapableProviders() const;
static QJsonObject builtinServers();
QJsonObject readRoot() const;
bool writeRoot(const QJsonObject &root);

View File

@@ -23,7 +23,6 @@
#include <QStandardPaths>
#include <logger/Logger.hpp>
#include <pluginllmcore/Provider.hpp>
#include <settings/McpSettings.hpp>
namespace QodeAssist::Mcp {
@@ -35,13 +34,6 @@ QString transportToString(McpTransportKind k)
return k == McpTransportKind::Http ? QStringLiteral("http") : QStringLiteral("stdio");
}
bool providerSupportsTools(PluginLLMCore::Provider *p)
{
if (!p)
return false;
return p->capabilities().testFlag(PluginLLMCore::ProviderCapability::Tools);
}
} // namespace
McpServerConfig McpServerConfig::fromJson(const QString &name, const QJsonObject &obj)
@@ -133,15 +125,6 @@ McpServerConnection::~McpServerConnection()
disconnectFromServer();
}
void McpServerConnection::setProviders(const QList<PluginLLMCore::Provider *> &providers)
{
m_providers.clear();
for (auto *p : providers) {
if (providerSupportsTools(p))
m_providers.append(p);
}
}
::LLMQore::Mcp::McpTransport *McpServerConnection::createTransport()
{
if (m_config.transport == McpTransportKind::Http) {
@@ -293,40 +276,20 @@ void McpServerConnection::fetchAndRegisterTools()
[this](const QList<::LLMQore::Mcp::ToolInfo> &tools) {
if (m_listToolsWatchdog)
m_listToolsWatchdog->stop();
if (m_providers.isEmpty()) {
LOG_MESSAGE(QString("MCP client [%1]: no tools-capable providers to "
"register %2 tools into")
.arg(m_config.name)
.arg(tools.size()));
setState(
McpConnectionState::Connected,
QStringLiteral("Connected (%1 tools)").arg(tools.size()));
return;
}
m_tools.clear();
for (const auto &info : tools) {
if (info.name.isEmpty())
continue;
m_toolIds.append(info.name);
for (const auto &p : m_providers) {
if (!p)
continue;
auto *tm = p->toolsManager();
if (!tm)
continue;
auto *remote = new ::LLMQore::Mcp::McpRemoteTool(
m_client.data(), info, tm);
tm->addTool(remote);
}
m_tools.append(info);
}
LOG_MESSAGE(QString("MCP client [%1]: registered %2 tools across %3 providers")
LOG_MESSAGE(QString("MCP client [%1]: discovered %2 tools")
.arg(m_config.name)
.arg(tools.size())
.arg(m_providers.size()));
.arg(m_tools.size()));
setState(
McpConnectionState::Connected,
QStringLiteral("Connected (%1 tools)").arg(tools.size()));
QStringLiteral("Connected (%1 tools)").arg(m_tools.size()));
})
.onFailed(this, [this](const std::exception &e) {
if (m_listToolsWatchdog)
@@ -337,21 +300,19 @@ void McpServerConnection::fetchAndRegisterTools()
});
}
void McpServerConnection::registerToolsOn(::LLMQore::ToolsManager *tools)
{
if (!tools || !m_client || m_state != McpConnectionState::Connected)
return;
for (const auto &info : m_tools) {
auto *remote = new ::LLMQore::Mcp::McpRemoteTool(m_client.data(), info, tools);
tools->addTool(remote);
}
}
void McpServerConnection::unregisterTools()
{
if (m_toolIds.isEmpty())
return;
for (const auto &p : m_providers) {
if (!p)
continue;
auto *tm = p->toolsManager();
if (!tm)
continue;
for (const QString &id : m_toolIds)
tm->removeTool(id);
}
m_toolIds.clear();
m_tools.clear();
}
void McpServerConnection::disconnectFromServer()

View File

@@ -14,15 +14,17 @@
#include <QTimer>
#include <QUrl>
#include <LLMQore/McpTypes.hpp>
namespace LLMQore {
class ToolsManager;
}
namespace LLMQore::Mcp {
class McpClient;
class McpTransport;
} // namespace LLMQore::Mcp
namespace QodeAssist::PluginLLMCore {
class Provider;
}
namespace QodeAssist::Mcp {
enum class McpTransportKind { Http, Stdio };
@@ -61,10 +63,17 @@ public:
const McpServerConfig &config() const { return m_config; }
McpConnectionState state() const { return m_state; }
QString statusText() const { return m_statusText; }
int toolCount() const { return m_toolIds.size(); }
QStringList toolNames() const { return m_toolIds; }
int toolCount() const { return m_tools.size(); }
QStringList toolNames() const
{
QStringList names;
names.reserve(m_tools.size());
for (const auto &tool : m_tools)
names << tool.name;
return names;
}
void setProviders(const QList<PluginLLMCore::Provider *> &providers);
void registerToolsOn(::LLMQore::ToolsManager *tools);
void connectToServer();
void disconnectFromServer();
@@ -75,7 +84,6 @@ signals:
private:
void setState(McpConnectionState state, const QString &text = {});
void fetchAndRegisterTools();
void registerTools(const QList<::LLMQore::Mcp::McpClient *> & /*unused*/);
void unregisterTools();
::LLMQore::Mcp::McpTransport *createTransport();
@@ -87,8 +95,7 @@ private:
QPointer<::LLMQore::Mcp::McpTransport> m_transport;
QPointer<QTimer> m_listToolsWatchdog;
QList<QPointer<PluginLLMCore::Provider>> m_providers;
QStringList m_toolIds;
QList<::LLMQore::Mcp::ToolInfo> m_tools;
};
} // namespace QodeAssist::Mcp

View File

@@ -1,28 +0,0 @@
add_library(PluginLLMCore STATIC
RequestType.hpp
Provider.hpp Provider.cpp
ProvidersManager.hpp ProvidersManager.cpp
ContextData.hpp
IPromptProvider.hpp
IProviderRegistry.hpp
PromptProviderChat.hpp
PromptProviderFim.hpp
PromptTemplate.hpp
PromptTemplateManager.hpp PromptTemplateManager.cpp
ProviderID.hpp
ResponseCleaner.hpp
)
target_link_libraries(PluginLLMCore
PUBLIC
Qt::Core
Qt::Network
QtCreator::Core
QtCreator::Utils
QtCreator::ExtensionSystem
LLMQore
PRIVATE
QodeAssistLogger
)
target_include_directories(PluginLLMCore PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -1,68 +0,0 @@
// 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 <QString>
#include <QVector>
namespace QodeAssist::PluginLLMCore {
struct ImageAttachment
{
QString data; // Base64 encoded data or URL
QString mediaType; // e.g., "image/png", "image/jpeg", "image/webp", "image/gif"
bool isUrl = false; // true if data is URL, false if base64
bool operator==(const ImageAttachment &) const = default;
};
struct ToolCall
{
QString id;
QString name;
QJsonObject arguments;
bool operator==(const ToolCall &) const = default;
};
struct Message
{
QString role;
QString content;
QString signature;
bool isThinking = false;
bool isRedacted = false;
std::optional<QVector<ImageAttachment>> images;
QVector<ToolCall> toolCalls;
QString toolCallId;
QString toolName;
// clang-format off
bool operator==(const Message&) const = default;
// clang-format on
};
struct FileMetadata
{
QString filePath;
QString content;
bool operator==(const FileMetadata &) const = default;
};
struct ContextData
{
std::optional<QString> systemPrompt;
std::optional<QString> prefix;
std::optional<QString> suffix;
std::optional<QString> fileContext;
std::optional<QVector<Message>> history;
std::optional<QList<FileMetadata>> filesMetadata;
bool operator==(const ContextData &) const = default;
};
} // namespace QodeAssist::PluginLLMCore

View File

@@ -1,24 +0,0 @@
// 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 "PromptTemplate.hpp"
#include <QString>
namespace QodeAssist::PluginLLMCore {
class IPromptProvider
{
public:
virtual ~IPromptProvider() = default;
virtual PromptTemplate *getTemplateByName(const QString &templateName) const = 0;
virtual QStringList templatesNames() const = 0;
virtual QStringList getTemplatesForProvider(ProviderID id) const = 0;
};
} // namespace QodeAssist::PluginLLMCore

View File

@@ -1,21 +0,0 @@
// 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 "Provider.hpp"
namespace QodeAssist::PluginLLMCore {
class IProviderRegistry
{
public:
virtual ~IProviderRegistry() = default;
virtual Provider *getProviderByName(const QString &providerName) = 0;
virtual QStringList providersNames() const = 0;
};
} // namespace QodeAssist::PluginLLMCore

View File

@@ -1,38 +0,0 @@
// 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 "IPromptProvider.hpp"
#include "PromptTemplate.hpp"
#include "PromptTemplateManager.hpp"
namespace QodeAssist::PluginLLMCore {
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(ProviderID id) const override
{
return m_templateManager.getChatTemplatesForProvider(id);
}
private:
PromptTemplateManager &m_templateManager;
};
} // namespace QodeAssist::PluginLLMCore

View File

@@ -1,37 +0,0 @@
// 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 "IPromptProvider.hpp"
#include "PromptTemplateManager.hpp"
namespace QodeAssist::PluginLLMCore {
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(ProviderID id) const override
{
return m_templateManager.getFimTemplatesForProvider(id);
}
private:
PromptTemplateManager &m_templateManager;
};
} // namespace QodeAssist::PluginLLMCore

View File

@@ -1,33 +0,0 @@
// 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 "ContextData.hpp"
#include "ProviderID.hpp"
namespace QodeAssist::PluginLLMCore {
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 ContextData &context) const = 0;
virtual QString description() const = 0;
virtual bool isSupportProvider(ProviderID id) const = 0;
virtual QString endpoint() const { return {}; }
virtual bool supportsToolHistory() const { return false; }
};
} // namespace QodeAssist::PluginLLMCore

View File

@@ -1,84 +0,0 @@
// 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 "PromptTemplateManager.hpp"
#include <QMessageBox>
namespace QodeAssist::PluginLLMCore {
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(ProviderID id)
{
QStringList templateList;
for (const auto tmpl : m_fimTemplates) {
if (tmpl->isSupportProvider(id)) {
templateList.append(tmpl->name());
}
}
return templateList;
}
QStringList PromptTemplateManager::getChatTemplatesForProvider(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::PluginLLMCore

View File

@@ -1,50 +0,0 @@
// 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 "PromptTemplate.hpp"
namespace QodeAssist::PluginLLMCore {
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(ProviderID id);
QStringList getChatTemplatesForProvider(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::PluginLLMCore

View File

@@ -1,50 +0,0 @@
// 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 "Provider.hpp"
#include <LLMQore/BaseClient.hpp>
#include <LLMQore/ToolsManager.hpp>
#include <QJsonDocument>
#include <Logger.hpp>
namespace QodeAssist::PluginLLMCore {
Provider::Provider(QObject *parent)
: QObject(parent)
{}
LLMQore::RequestID Provider::sendRequest(
const QUrl &url, const QJsonObject &payload, const QString &endpoint)
{
auto *c = client();
c->setUrl(url.toString());
c->setApiKey(apiKey());
auto requestId = c->sendMessage(payload, endpoint);
LOG_MESSAGE(
QString("%1: Sending request %2 to %3%4").arg(name(), requestId, url.toString(), endpoint));
LOG_MESSAGE(
QString("%1: Payload:\n%2")
.arg(name(), QString::fromUtf8(QJsonDocument(payload).toJson(QJsonDocument::Indented))));
return requestId;
}
void Provider::cancelRequest(const LLMQore::RequestID &requestId)
{
LOG_MESSAGE(QString("%1: Cancelling request %2").arg(name(), requestId));
client()->cancelRequest(requestId);
}
::LLMQore::ToolsManager *Provider::toolsManager() const
{
return client()->tools();
}
} // namespace QodeAssist::PluginLLMCore

View File

@@ -1,67 +0,0 @@
// 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 <QFlags>
#include <QFuture>
#include <QObject>
#include <QString>
#include <utils/environment.h>
#include "ContextData.hpp"
#include "PromptTemplate.hpp"
#include "LLMQore/BaseClient.hpp"
#include "RequestType.hpp"
namespace LLMQore {
class BaseClient;
class ToolsManager;
}
class QJsonObject;
namespace QodeAssist::PluginLLMCore {
enum class ProviderCapability {
Tools = 0x1,
Thinking = 0x2,
Image = 0x4,
ModelListing = 0x8,
};
Q_DECLARE_FLAGS(ProviderCapabilities, ProviderCapability)
Q_DECLARE_OPERATORS_FOR_FLAGS(ProviderCapabilities)
class Provider : public QObject
{
Q_OBJECT
public:
explicit Provider(QObject *parent = nullptr);
virtual ~Provider() = default;
virtual QString name() const = 0;
virtual QString url() const = 0;
virtual void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
= 0;
virtual QFuture<QList<QString>> getInstalledModels(const QString &url) = 0;
virtual ProviderID providerID() const = 0;
virtual ProviderCapabilities capabilities() const { return {}; }
virtual ::LLMQore::BaseClient *client() const = 0;
virtual QString apiKey() const = 0;
virtual LLMQore::RequestID sendRequest(
const QUrl &url, const QJsonObject &payload, const QString &endpoint);
void cancelRequest(const LLMQore::RequestID &requestId);
::LLMQore::ToolsManager *toolsManager() const;
};
} // namespace QodeAssist::PluginLLMCore

View File

@@ -1,22 +0,0 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
namespace QodeAssist::PluginLLMCore {
enum class ProviderID {
Any,
Ollama,
LMStudio,
Claude,
OpenAI,
OpenAICompatible,
OpenAIResponses,
MistralAI,
OpenRouter,
GoogleAI,
LlamaCpp,
Qwen,
DeepSeek
};
}

View File

@@ -1,32 +0,0 @@
// 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 "ProvidersManager.hpp"
namespace QodeAssist::PluginLLMCore {
ProvidersManager &ProvidersManager::instance()
{
static ProvidersManager instance;
return instance;
}
QStringList ProvidersManager::providersNames() const
{
return m_providers.keys();
}
ProvidersManager::~ProvidersManager()
{
qDeleteAll(m_providers);
}
Provider *ProvidersManager::getProviderByName(const QString &providerName)
{
if (!m_providers.contains(providerName))
return m_providers.first();
return m_providers[providerName];
}
} // namespace QodeAssist::PluginLLMCore

View File

@@ -1,41 +0,0 @@
// 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 <QString>
#include "IProviderRegistry.hpp"
#include <QMap>
namespace QodeAssist::PluginLLMCore {
class ProvidersManager : public IProviderRegistry
{
public:
static ProvidersManager &instance();
~ProvidersManager();
template<typename T>
void registerProvider()
{
static_assert(std::is_base_of<Provider, T>::value, "T must inherit from Provider");
T *provider = new T();
QString name = provider->name();
m_providers[name] = provider;
}
Provider *getProviderByName(const QString &providerName) override;
QStringList providersNames() const override;
private:
ProvidersManager() = default;
ProvidersManager(const ProvidersManager &) = delete;
ProvidersManager &operator=(const ProvidersManager &) = delete;
QMap<QString, Provider *> m_providers;
};
} // namespace QodeAssist::PluginLLMCore

View File

@@ -1,13 +0,0 @@
// 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 <QString>
#pragma once
namespace QodeAssist::PluginLLMCore {
enum RequestType { CodeCompletion, Chat, Embedding, QuickRefactoring };
}

View File

@@ -1,90 +0,0 @@
// 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 <QJsonValue>
#include <QString>
namespace QodeAssist::Providers::ClaudeCacheControl {
inline QJsonObject buildBreakpoint(bool extendedTtl)
{
QJsonObject cacheControl{{"type", "ephemeral"}};
if (extendedTtl)
cacheControl["ttl"] = "1h";
return cacheControl;
}
inline void markLastBlock(QJsonArray &blocks, const QJsonObject &cacheControl)
{
if (blocks.isEmpty())
return;
QJsonObject last = blocks.last().toObject();
last["cache_control"] = cacheControl;
blocks.replace(blocks.size() - 1, last);
}
inline void applyToSystem(QJsonObject &request, const QJsonObject &cacheControl)
{
if (!request.contains("system"))
return;
const QJsonValue sys = request.value("system");
if (sys.isString()) {
const QString text = sys.toString();
if (!text.isEmpty()) {
request["system"] = QJsonArray{QJsonObject{
{"type", "text"}, {"text", text}, {"cache_control", cacheControl}}};
}
} else if (sys.isArray()) {
QJsonArray blocks = sys.toArray();
markLastBlock(blocks, cacheControl);
request["system"] = blocks;
}
}
inline void applyToTools(QJsonObject &request, const QJsonObject &cacheControl)
{
if (!request.contains("tools"))
return;
QJsonArray tools = request.value("tools").toArray();
markLastBlock(tools, cacheControl);
request["tools"] = tools;
}
inline void applyToHistory(QJsonObject &request, const QJsonObject &cacheControl)
{
if (!request.contains("messages"))
return;
QJsonArray messages = request.value("messages").toArray();
if (messages.size() < 2)
return;
const int idx = messages.size() - 2;
QJsonObject msg = messages[idx].toObject();
const QJsonValue content = msg.value("content");
if (content.isString()) {
msg["content"] = QJsonArray{QJsonObject{
{"type", "text"}, {"text", content.toString()}, {"cache_control", cacheControl}}};
} else if (content.isArray()) {
QJsonArray blocks = content.toArray();
markLastBlock(blocks, cacheControl);
msg["content"] = blocks;
}
messages.replace(idx, msg);
request["messages"] = messages;
}
inline void apply(QJsonObject &request, bool extendedTtl)
{
const QJsonObject cacheControl = buildBreakpoint(extendedTtl);
applyToSystem(request, cacheControl);
applyToTools(request, cacheControl);
applyToHistory(request, cacheControl);
}
} // namespace QodeAssist::Providers::ClaudeCacheControl

View File

@@ -1,159 +0,0 @@
// 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 "ClaudeProvider.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <LLMQore/ToolsManager.hpp>
#include "ClaudeCacheControl.hpp"
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include "tools/ToolsRegistration.hpp"
namespace QodeAssist::Providers {
ClaudeProvider::ClaudeProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::ClaudeClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString ClaudeProvider::name() const
{
return "Claude";
}
QString ClaudeProvider::apiKey() const
{
return Settings::providerSettings().claudeApiKey();
}
QString ClaudeProvider::url() const
{
return "https://api.anthropic.com";
}
void ClaudeProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_tokens"] = settings.maxTokens();
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useTopK())
request["top_k"] = settings.topK();
request["stream"] = true;
};
auto applyThinkingMode = [&request](const auto &settings) {
const QString model = request.value("model").toString().toLower();
const bool useAdaptiveThinking = model.contains("opus-4-8") || model.contains("opus-4-7")
|| model.contains("opus-4-6") || model.contains("sonnet-4-6");
QJsonObject thinkingObj;
if (useAdaptiveThinking) {
thinkingObj["type"] = "adaptive";
const int budget = settings.thinkingBudgetTokens();
QString effort = "high";
if (budget < 8000)
effort = "low";
else if (budget < 24000)
effort = "medium";
QJsonObject outputConfig;
outputConfig["effort"] = effort;
request["output_config"] = outputConfig;
} else {
thinkingObj["type"] = "enabled";
thinkingObj["budget_tokens"] = settings.thinkingBudgetTokens();
}
request["thinking"] = thinkingObj;
request["max_tokens"] = settings.thinkingMaxTokens();
request["temperature"] = 1.0;
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
const auto &qrSettings = Settings::quickRefactorSettings();
applyModelParams(qrSettings);
if (isThinkingEnabled) {
applyThinkingMode(qrSettings);
} else {
request["temperature"] = qrSettings.temperature();
}
} else {
const auto &chatSettings = Settings::chatAssistantSettings();
applyModelParams(chatSettings);
if (isThinkingEnabled) {
applyThinkingMode(chatSettings);
} else {
request["temperature"] = chatSettings.temperature();
}
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(QString("Added %1 tools to Claude request").arg(toolsDefinitions.size()));
}
}
const auto &ps = Settings::providerSettings();
const bool cachingOn = ps.claudeEnablePromptCaching()
&& type != PluginLLMCore::RequestType::CodeCompletion;
m_client->setUseExtendedCacheTTL(cachingOn && ps.claudeUseExtendedCacheTTL());
if (cachingOn) {
ClaudeCacheControl::apply(request, ps.claudeUseExtendedCacheTTL());
}
}
QFuture<QList<QString>> ClaudeProvider::getInstalledModels(const QString &baseUrl)
{
m_client->setUrl(baseUrl);
m_client->setApiKey(apiKey());
return m_client->listModels();
}
PluginLLMCore::ProviderID ClaudeProvider::providerID() const
{
return PluginLLMCore::ProviderID::Claude;
}
PluginLLMCore::ProviderCapabilities ClaudeProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Thinking
| PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *ClaudeProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,39 +0,0 @@
// 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 <pluginllmcore/Provider.hpp>
#include <LLMQore/ClaudeClient.hpp>
namespace QodeAssist::Providers {
class ClaudeProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit ClaudeProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::ClaudeClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,35 +0,0 @@
// Copyright (C) 2025-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#include "CodestralProvider.hpp"
#include "settings/ProviderSettings.hpp"
namespace QodeAssist::Providers {
CodestralProvider::CodestralProvider(QObject *parent)
: MistralAIProvider(parent)
{}
QString CodestralProvider::name() const
{
return "Codestral";
}
QString CodestralProvider::apiKey() const
{
return Settings::providerSettings().codestralApiKey();
}
QString CodestralProvider::url() const
{
return "https://codestral.mistral.ai";
}
PluginLLMCore::ProviderCapabilities CodestralProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image;
}
} // namespace QodeAssist::Providers

View File

@@ -1,22 +0,0 @@
// Copyright (C) 2025-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "MistralAIProvider.hpp"
namespace QodeAssist::Providers {
class CodestralProvider : public MistralAIProvider
{
public:
explicit CodestralProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
QString apiKey() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
};
} // namespace QodeAssist::Providers

View File

@@ -1,109 +0,0 @@
// 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 "DeepSeekProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "tools/ToolsRegistration.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
DeepSeekProvider::DeepSeekProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString DeepSeekProvider::name() const
{
return "DeepSeek";
}
QString DeepSeekProvider::apiKey() const
{
return Settings::providerSettings().deepSeekApiKey();
}
QString DeepSeekProvider::url() const
{
return "https://api.deepseek.com";
}
QFuture<QList<QString>> DeepSeekProvider::getInstalledModels(const QString &url)
{
m_client->setUrl(url);
m_client->setApiKey(apiKey());
return m_client->listModels();
}
PluginLLMCore::ProviderID DeepSeekProvider::providerID() const
{
return PluginLLMCore::ProviderID::DeepSeek;
}
PluginLLMCore::ProviderCapabilities DeepSeekProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools
| PluginLLMCore::ProviderCapability::Thinking
| PluginLLMCore::ProviderCapability::ModelListing;
}
void DeepSeekProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings.temperature();
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
request["presence_penalty"] = settings.presencePenalty();
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
applyModelParams(Settings::quickRefactorSettings());
} else {
applyModelParams(Settings::chatAssistantSettings());
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(QString("Added %1 tools to DeepSeek request").arg(toolsDefinitions.size()));
}
}
}
::LLMQore::BaseClient *DeepSeekProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,38 +0,0 @@
// 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 <LLMQore/OpenAIClient.hpp>
#include <pluginllmcore/Provider.hpp>
namespace QodeAssist::Providers {
class DeepSeekProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit DeepSeekProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::OpenAIClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,159 +0,0 @@
// 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 "GoogleAIProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include <QJsonArray>
#include "tools/ToolsRegistration.hpp"
#include <QJsonDocument>
#include <QJsonObject>
#include <QtCore/qurlquery.h>
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
namespace QodeAssist::Providers {
GoogleAIProvider::GoogleAIProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::GoogleAIClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString GoogleAIProvider::name() const
{
return "Google AI";
}
QString GoogleAIProvider::apiKey() const
{
return Settings::providerSettings().googleAiApiKey();
}
QString GoogleAIProvider::url() const
{
return "https://generativelanguage.googleapis.com/v1beta";
}
void GoogleAIProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
QJsonObject generationConfig;
generationConfig["maxOutputTokens"] = settings.maxTokens();
generationConfig["temperature"] = settings.temperature();
if (settings.useTopP())
generationConfig["topP"] = settings.topP();
if (settings.useTopK())
generationConfig["topK"] = settings.topK();
request["generationConfig"] = generationConfig;
};
auto applyThinkingMode = [&request](const auto &settings) {
QJsonObject generationConfig;
generationConfig["maxOutputTokens"] = settings.thinkingMaxTokens();
if (settings.useTopP())
generationConfig["topP"] = settings.topP();
if (settings.useTopK())
generationConfig["topK"] = settings.topK();
generationConfig["temperature"] = 1.0;
QJsonObject thinkingConfig;
thinkingConfig["includeThoughts"] = true;
int budgetTokens = settings.thinkingBudgetTokens();
if (budgetTokens != -1) {
thinkingConfig["thinkingBudget"] = budgetTokens;
}
generationConfig["thinkingConfig"] = thinkingConfig;
request["generationConfig"] = generationConfig;
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
const auto &qrSettings = Settings::quickRefactorSettings();
if (isThinkingEnabled) {
applyThinkingMode(qrSettings);
} else {
applyModelParams(qrSettings);
}
} else {
const auto &chatSettings = Settings::chatAssistantSettings();
if (isThinkingEnabled) {
applyThinkingMode(chatSettings);
} else {
applyModelParams(chatSettings);
}
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(QString("Added %1 tools to Google AI request").arg(toolsDefinitions.size()));
}
}
}
QFuture<QList<QString>> GoogleAIProvider::getInstalledModels(const QString &baseUrl)
{
m_client->setUrl(baseUrl);
m_client->setApiKey(apiKey());
return m_client->listModels();
}
PluginLLMCore::ProviderID GoogleAIProvider::providerID() const
{
return PluginLLMCore::ProviderID::GoogleAI;
}
PluginLLMCore::ProviderCapabilities GoogleAIProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Thinking
| PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::ModelListing;
}
LLMQore::RequestID GoogleAIProvider::sendRequest(
const QUrl &url, const QJsonObject &payload, const QString &endpoint)
{
QJsonObject cleaned = payload;
if (cleaned.contains("model")) {
m_client->setModel(cleaned["model"].toString());
cleaned.remove("model");
}
cleaned.remove("stream");
return PluginLLMCore::Provider::sendRequest(url, cleaned, endpoint);
}
::LLMQore::BaseClient *GoogleAIProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,42 +0,0 @@
// 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 <pluginllmcore/Provider.hpp>
#include <LLMQore/GoogleAIClient.hpp>
namespace QodeAssist::Providers {
class GoogleAIProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit GoogleAIProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
LLMQore::RequestID sendRequest(
const QUrl &url, const QJsonObject &payload, const QString &endpoint) override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::GoogleAIClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,120 +0,0 @@
// 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 "LMStudioProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "providers/ProviderUrlUtils.hpp"
#include "tools/ToolsRegistration.hpp"
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
LMStudioProvider::LMStudioProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString LMStudioProvider::name() const
{
return "LM Studio (Chat Completions)";
}
QString LMStudioProvider::apiKey() const
{
return {};
}
QString LMStudioProvider::url() const
{
return "http://localhost:1234";
}
QFuture<QList<QString>> LMStudioProvider::getInstalledModels(const QString &url)
{
m_client->setUrl(ensureOpenAIV1Base(url));
m_client->setApiKey(apiKey());
return m_client->listModels();
}
PluginLLMCore::ProviderID LMStudioProvider::providerID() const
{
return PluginLLMCore::ProviderID::LMStudio;
}
PluginLLMCore::ProviderCapabilities LMStudioProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::ModelListing
| PluginLLMCore::ProviderCapability::Thinking;
}
void LMStudioProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings.temperature();
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useTopK())
request["top_k"] = settings.topK();
if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
request["presence_penalty"] = settings.presencePenalty();
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
applyModelParams(Settings::quickRefactorSettings());
} else {
applyModelParams(Settings::chatAssistantSettings());
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(QString("Added %1 tools to LMStudio request").arg(toolsDefinitions.size()));
}
}
}
LLMQore::RequestID LMStudioProvider::sendRequest(
const QUrl &url, const QJsonObject &payload, const QString &endpoint)
{
return PluginLLMCore::Provider::sendRequest(
QUrl(ensureOpenAIV1Base(url.toString())), payload, endpoint);
}
::LLMQore::BaseClient *LMStudioProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,41 +0,0 @@
// 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 <LLMQore/OpenAIClient.hpp>
#include <pluginllmcore/Provider.hpp>
namespace QodeAssist::Providers {
class LMStudioProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit LMStudioProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
LLMQore::RequestID sendRequest(
const QUrl &url, const QJsonObject &payload, const QString &endpoint) override;
private:
::LLMQore::OpenAIClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,144 +0,0 @@
// 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 "LMStudioResponsesProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "logger/Logger.hpp"
#include "providers/ProviderUrlUtils.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "tools/ToolsRegistration.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
LMStudioResponsesProvider::LMStudioResponsesProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::OpenAIResponsesClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString LMStudioResponsesProvider::name() const
{
return "LM Studio (Responses API)";
}
QString LMStudioResponsesProvider::apiKey() const
{
return {};
}
QString LMStudioResponsesProvider::url() const
{
return "http://localhost:1234";
}
void LMStudioResponsesProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_output_tokens"] = settings.maxTokens();
if (settings.useTopP()) {
request["top_p"] = settings.topP();
}
};
auto applyThinkingMode = [&request](const auto &settings) {
QString effortStr = settings.openAIResponsesReasoningEffort.stringValue().toLower();
if (effortStr.isEmpty()) {
effortStr = "medium";
}
QJsonObject reasoning;
reasoning["effort"] = effortStr;
request["reasoning"] = reasoning;
request["max_output_tokens"] = settings.thinkingMaxTokens();
request["store"] = true;
QJsonArray include;
include.append("reasoning.encrypted_content");
request["include"] = include;
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
const auto &qrSettings = Settings::quickRefactorSettings();
applyModelParams(qrSettings);
if (isThinkingEnabled) {
applyThinkingMode(qrSettings);
}
} else {
const auto &chatSettings = Settings::chatAssistantSettings();
applyModelParams(chatSettings);
if (isThinkingEnabled) {
applyThinkingMode(chatSettings);
}
}
if (isToolsEnabled) {
const auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(QString("Added %1 tools to LM Studio Responses request")
.arg(toolsDefinitions.size()));
}
}
request["stream"] = true;
}
QFuture<QList<QString>> LMStudioResponsesProvider::getInstalledModels(const QString &baseUrl)
{
m_client->setUrl(ensureOpenAIV1Base(baseUrl));
m_client->setApiKey(apiKey());
return m_client->listModels();
}
LLMQore::RequestID LMStudioResponsesProvider::sendRequest(
const QUrl &url, const QJsonObject &payload, const QString &endpoint)
{
return PluginLLMCore::Provider::sendRequest(
QUrl(ensureOpenAIV1Base(url.toString())), payload, endpoint);
}
PluginLLMCore::ProviderID LMStudioResponsesProvider::providerID() const
{
return PluginLLMCore::ProviderID::OpenAIResponses;
}
PluginLLMCore::ProviderCapabilities LMStudioResponsesProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Thinking
| PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *LMStudioResponsesProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,41 +0,0 @@
// 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 <LLMQore/OpenAIResponsesClient.hpp>
#include <pluginllmcore/Provider.hpp>
namespace QodeAssist::Providers {
class LMStudioResponsesProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit LMStudioResponsesProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
LLMQore::RequestID sendRequest(
const QUrl &url, const QJsonObject &payload, const QString &endpoint) override;
private:
::LLMQore::OpenAIResponsesClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,125 +0,0 @@
// 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 "LlamaCppProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include "tools/ToolsRegistration.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
LlamaCppProvider::LlamaCppProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::LlamaCppClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString LlamaCppProvider::name() const
{
return "llama.cpp";
}
QString LlamaCppProvider::apiKey() const
{
return Settings::providerSettings().llamaCppApiKey();
}
QString LlamaCppProvider::url() const
{
return "http://localhost:8080";
}
void LlamaCppProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings.temperature();
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useTopK())
request["top_k"] = settings.topK();
if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
request["presence_penalty"] = settings.presencePenalty();
};
auto applyThinkingMode = [&request]() {
QJsonObject chatTemplateKwargs = request["chat_template_kwargs"].toObject();
chatTemplateKwargs["enable_thinking"] = true;
request["chat_template_kwargs"] = chatTemplateKwargs;
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
applyModelParams(Settings::quickRefactorSettings());
if (isThinkingEnabled) {
applyThinkingMode();
LOG_MESSAGE(QString("LlamaCppProvider: Thinking mode enabled for QuickRefactoring"));
}
} else {
applyModelParams(Settings::chatAssistantSettings());
if (isThinkingEnabled) {
applyThinkingMode();
LOG_MESSAGE(QString("LlamaCppProvider: Thinking mode enabled for Chat"));
}
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(QString("Added %1 tools to llama.cpp request").arg(toolsDefinitions.size()));
}
}
}
QFuture<QList<QString>> LlamaCppProvider::getInstalledModels(const QString &baseUrl)
{
m_client->setUrl(baseUrl);
m_client->setApiKey(Settings::providerSettings().llamaCppApiKey());
return m_client->listModels();
}
PluginLLMCore::ProviderID LlamaCppProvider::providerID() const
{
return PluginLLMCore::ProviderID::LlamaCpp;
}
PluginLLMCore::ProviderCapabilities LlamaCppProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Thinking
| PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *LlamaCppProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,39 +0,0 @@
// 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 <pluginllmcore/Provider.hpp>
#include <LLMQore/LlamaCppClient.hpp>
namespace QodeAssist::Providers {
class LlamaCppProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit LlamaCppProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::LlamaCppClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,111 +0,0 @@
// 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 "MistralAIProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include "tools/ToolsRegistration.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
MistralAIProvider::MistralAIProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::MistralClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString MistralAIProvider::name() const
{
return "Mistral AI";
}
QString MistralAIProvider::apiKey() const
{
return Settings::providerSettings().mistralAiApiKey();
}
QString MistralAIProvider::url() const
{
return "https://api.mistral.ai";
}
QFuture<QList<QString>> MistralAIProvider::getInstalledModels(const QString &url)
{
m_client->setUrl(url);
m_client->setApiKey(apiKey());
return m_client->listModels();
}
PluginLLMCore::ProviderID MistralAIProvider::providerID() const
{
return PluginLLMCore::ProviderID::MistralAI;
}
PluginLLMCore::ProviderCapabilities MistralAIProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::ModelListing
| PluginLLMCore::ProviderCapability::Thinking;
}
void MistralAIProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings.temperature();
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useTopK())
request["top_k"] = settings.topK();
if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
request["presence_penalty"] = settings.presencePenalty();
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
applyModelParams(Settings::quickRefactorSettings());
} else {
applyModelParams(Settings::chatAssistantSettings());
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(QString("Added %1 tools to Mistral request").arg(toolsDefinitions.size()));
}
}
}
::LLMQore::BaseClient *MistralAIProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,38 +0,0 @@
// 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 <LLMQore/MistralClient.hpp>
#include <pluginllmcore/Provider.hpp>
namespace QodeAssist::Providers {
class MistralAIProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit MistralAIProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::MistralClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,124 +0,0 @@
// 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 "OllamaCompatProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "tools/ToolsRegistration.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
OllamaCompatProvider::OllamaCompatProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString OllamaCompatProvider::name() const
{
return "Ollama (OpenAI-compatible)";
}
QString OllamaCompatProvider::apiKey() const
{
return Settings::providerSettings().ollamaBasicAuthApiKey();
}
QString OllamaCompatProvider::url() const
{
return "http://localhost:11434";
}
void OllamaCompatProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings.temperature();
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useTopK())
request["top_k"] = settings.topK();
if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
request["presence_penalty"] = settings.presencePenalty();
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
applyModelParams(Settings::quickRefactorSettings());
} else {
applyModelParams(Settings::chatAssistantSettings());
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(
QString("Added %1 tools to OllamaCompat request").arg(toolsDefinitions.size()));
}
}
}
QFuture<QList<QString>> OllamaCompatProvider::getInstalledModels(const QString &baseUrl)
{
QString url = baseUrl;
if (!url.endsWith(QStringLiteral("/v1")))
url += QStringLiteral("/v1");
m_client->setUrl(url);
m_client->setApiKey(apiKey());
return m_client->listModels();
}
LLMQore::RequestID OllamaCompatProvider::sendRequest(
const QUrl &url, const QJsonObject &payload, const QString &endpoint)
{
const QString effectiveEndpoint
= endpoint.isEmpty() ? QStringLiteral("/v1/chat/completions") : endpoint;
return PluginLLMCore::Provider::sendRequest(url, payload, effectiveEndpoint);
}
PluginLLMCore::ProviderID OllamaCompatProvider::providerID() const
{
return PluginLLMCore::ProviderID::OpenAICompatible;
}
PluginLLMCore::ProviderCapabilities OllamaCompatProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::ModelListing
| PluginLLMCore::ProviderCapability::Thinking;
}
::LLMQore::BaseClient *OllamaCompatProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,41 +0,0 @@
// 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 <LLMQore/OpenAIClient.hpp>
#include <pluginllmcore/Provider.hpp>
namespace QodeAssist::Providers {
class OllamaCompatProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit OllamaCompatProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
LLMQore::RequestID sendRequest(
const QUrl &url, const QJsonObject &payload, const QString &endpoint) override;
private:
::LLMQore::OpenAIClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,137 +0,0 @@
// 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 "OllamaProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include "tools/ToolsRegistration.hpp"
namespace QodeAssist::Providers {
OllamaProvider::OllamaProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::OllamaClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString OllamaProvider::name() const
{
return "Ollama (Native)";
}
QString OllamaProvider::apiKey() const
{
return Settings::providerSettings().ollamaBasicAuthApiKey();
}
QString OllamaProvider::url() const
{
return "http://localhost:11434";
}
void OllamaProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applySettings = [&request](const auto &settings) {
QJsonObject options;
options["num_predict"] = settings.maxTokens();
options["temperature"] = settings.temperature();
options["stop"] = request.take("stop");
if (settings.useTopP())
options["top_p"] = settings.topP();
if (settings.useTopK())
options["top_k"] = settings.topK();
if (settings.useFrequencyPenalty())
options["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
options["presence_penalty"] = settings.presencePenalty();
request["options"] = options;
request["keep_alive"] = settings.ollamaLivetime();
};
auto applyThinkingMode = [&request]() {
request["enable_thinking"] = true;
QJsonObject options = request["options"].toObject();
options["temperature"] = 1.0;
request["options"] = options;
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
const auto &qrSettings = Settings::quickRefactorSettings();
applySettings(qrSettings);
if (isThinkingEnabled) {
applyThinkingMode();
LOG_MESSAGE(QString("OllamaProvider: Thinking mode enabled for QuickRefactoring"));
}
} else {
const auto &chatSettings = Settings::chatAssistantSettings();
applySettings(chatSettings);
if (isThinkingEnabled) {
applyThinkingMode();
LOG_MESSAGE(QString("OllamaProvider: Thinking mode enabled for Chat"));
}
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(
QString("OllamaProvider: Added %1 tools to request").arg(toolsDefinitions.size()));
}
}
}
QFuture<QList<QString>> OllamaProvider::getInstalledModels(const QString &baseUrl)
{
m_client->setUrl(baseUrl);
m_client->setApiKey(Settings::providerSettings().ollamaBasicAuthApiKey());
return m_client->listModels();
}
PluginLLMCore::ProviderID OllamaProvider::providerID() const
{
return PluginLLMCore::ProviderID::Ollama;
}
PluginLLMCore::ProviderCapabilities OllamaProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Thinking
| PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *OllamaProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,39 +0,0 @@
// 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 <pluginllmcore/Provider.hpp>
#include <LLMQore/OllamaClient.hpp>
namespace QodeAssist::Providers {
class OllamaProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit OllamaProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::OllamaClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,109 +0,0 @@
// 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 "OpenAICompatProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "tools/ToolsRegistration.hpp"
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
OpenAICompatProvider::OpenAICompatProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString OpenAICompatProvider::name() const
{
return "OpenAI Compatible";
}
QString OpenAICompatProvider::apiKey() const
{
return Settings::providerSettings().openAiCompatApiKey();
}
QString OpenAICompatProvider::url() const
{
return "http://localhost:1234/v1";
}
void OpenAICompatProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings.temperature();
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useTopK())
request["top_k"] = settings.topK();
if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
request["presence_penalty"] = settings.presencePenalty();
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
applyModelParams(Settings::quickRefactorSettings());
} else {
applyModelParams(Settings::chatAssistantSettings());
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(
QString("Added %1 tools to OpenAICompat request").arg(toolsDefinitions.size()));
}
}
}
QFuture<QList<QString>> OpenAICompatProvider::getInstalledModels(const QString &)
{
return QtFuture::makeReadyFuture(QList<QString>{});
}
PluginLLMCore::ProviderID OpenAICompatProvider::providerID() const
{
return PluginLLMCore::ProviderID::OpenAICompatible;
}
PluginLLMCore::ProviderCapabilities OpenAICompatProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::Thinking;
}
::LLMQore::BaseClient *OpenAICompatProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,38 +0,0 @@
// 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 <LLMQore/OpenAIClient.hpp>
#include <pluginllmcore/Provider.hpp>
namespace QodeAssist::Providers {
class OpenAICompatProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit OpenAICompatProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::OpenAIClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,140 +0,0 @@
// 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 "OpenAIProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "tools/ToolsRegistration.hpp"
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
OpenAIProvider::OpenAIProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString OpenAIProvider::name() const
{
return "OpenAI (Chat Completions)";
}
QString OpenAIProvider::apiKey() const
{
return Settings::providerSettings().openAiApiKey();
}
QString OpenAIProvider::url() const
{
return "https://api.openai.com/v1";
}
void OpenAIProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
QString model = request.value("model").toString().toLower();
bool useNewParameter = model.contains("gpt-4o") || model.contains("gpt-4-turbo")
|| model.contains("o1-") || model.contains("gpt-5")
|| model.startsWith("o1") || model.contains("o3");
bool isReasoningModel = model.contains("o1-") || model.contains("gpt-5")
|| model.startsWith("o1") || model.contains("o3");
if (useNewParameter) {
request["max_completion_tokens"] = settings.maxTokens();
} else {
request["max_tokens"] = settings.maxTokens();
}
if (!isReasoningModel) {
request["temperature"] = settings.temperature();
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useTopK())
request["top_k"] = settings.topK();
} else {
request["temperature"] = 1.0;
}
if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
request["presence_penalty"] = settings.presencePenalty();
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
applyModelParams(Settings::quickRefactorSettings());
} else {
applyModelParams(Settings::chatAssistantSettings());
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(QString("Added %1 tools to OpenAI request").arg(toolsDefinitions.size()));
}
}
}
QFuture<QList<QString>> OpenAIProvider::getInstalledModels(const QString &baseUrl)
{
m_client->setUrl(baseUrl);
m_client->setApiKey(apiKey());
return m_client->listModels().then([](const QList<QString> &allModels) {
QList<QString> filtered;
for (const QString &modelId : allModels) {
if (!modelId.contains("dall-e") && !modelId.contains("whisper")
&& !modelId.contains("tts") && !modelId.contains("davinci")
&& !modelId.contains("babbage") && !modelId.contains("omni")) {
filtered.append(modelId);
}
}
return filtered;
});
}
PluginLLMCore::ProviderID OpenAIProvider::providerID() const
{
return PluginLLMCore::ProviderID::OpenAI;
}
PluginLLMCore::ProviderCapabilities OpenAIProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::ModelListing
| PluginLLMCore::ProviderCapability::Thinking;
}
::LLMQore::BaseClient *OpenAIProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,38 +0,0 @@
// 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 <LLMQore/OpenAIClient.hpp>
#include <pluginllmcore/Provider.hpp>
namespace QodeAssist::Providers {
class OpenAIProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit OpenAIProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::OpenAIClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,147 +0,0 @@
// 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 "OpenAIResponsesProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "tools/ToolsRegistration.hpp"
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
OpenAIResponsesProvider::OpenAIResponsesProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::OpenAIResponsesClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString OpenAIResponsesProvider::name() const
{
return "OpenAI (Responses API)";
}
QString OpenAIResponsesProvider::apiKey() const
{
return Settings::providerSettings().openAiApiKey();
}
QString OpenAIResponsesProvider::url() const
{
return "https://api.openai.com/v1";
}
void OpenAIResponsesProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_output_tokens"] = settings.maxTokens();
if (settings.useTopP()) {
request["top_p"] = settings.topP();
}
};
auto applyThinkingMode = [&request](const auto &settings) {
QString effortStr = settings.openAIResponsesReasoningEffort.stringValue().toLower();
if (effortStr.isEmpty()) {
effortStr = "medium";
}
QJsonObject reasoning;
reasoning["effort"] = effortStr;
request["reasoning"] = reasoning;
request["max_output_tokens"] = settings.thinkingMaxTokens();
request["store"] = true;
QJsonArray include;
include.append("reasoning.encrypted_content");
request["include"] = include;
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
const auto &qrSettings = Settings::quickRefactorSettings();
applyModelParams(qrSettings);
if (isThinkingEnabled) {
applyThinkingMode(qrSettings);
}
} else {
const auto &chatSettings = Settings::chatAssistantSettings();
applyModelParams(chatSettings);
if (isThinkingEnabled) {
applyThinkingMode(chatSettings);
}
}
if (isToolsEnabled) {
const auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(QString("Added %1 tools to OpenAI Responses request")
.arg(toolsDefinitions.size()));
}
}
request["stream"] = true;
}
QFuture<QList<QString>> OpenAIResponsesProvider::getInstalledModels(const QString &baseUrl)
{
m_client->setUrl(baseUrl);
m_client->setApiKey(apiKey());
return m_client->listModels().then([](const QList<QString> &models) {
QList<QString> filtered;
static const QStringList modelPrefixes = {"gpt-5", "o1", "o2", "o3", "o4"};
for (const QString &modelId : models) {
for (const QString &prefix : modelPrefixes) {
if (modelId.contains(prefix)) {
filtered.append(modelId);
break;
}
}
}
return filtered;
});
}
PluginLLMCore::ProviderID OpenAIResponsesProvider::providerID() const
{
return PluginLLMCore::ProviderID::OpenAIResponses;
}
PluginLLMCore::ProviderCapabilities OpenAIResponsesProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Thinking
| PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *OpenAIResponsesProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,38 +0,0 @@
// 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 <LLMQore/OpenAIResponsesClient.hpp>
#include <pluginllmcore/Provider.hpp>
namespace QodeAssist::Providers {
class OpenAIResponsesProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit OpenAIResponsesProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::OpenAIResponsesClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,40 +0,0 @@
// 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 "OpenRouterAIProvider.hpp"
#include "settings/ProviderSettings.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
namespace QodeAssist::Providers {
OpenRouterProvider::OpenRouterProvider(QObject *parent)
: OpenAICompatProvider(parent)
{}
QString OpenRouterProvider::name() const
{
return "OpenRouter";
}
QString OpenRouterProvider::apiKey() const
{
return Settings::providerSettings().openRouterApiKey();
}
QString OpenRouterProvider::url() const
{
return "https://openrouter.ai/api";
}
PluginLLMCore::ProviderID OpenRouterProvider::providerID() const
{
return PluginLLMCore::ProviderID::OpenRouter;
}
} // namespace QodeAssist::Providers

View File

@@ -1,22 +0,0 @@
// 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 "providers/OpenAICompatProvider.hpp"
namespace QodeAssist::Providers {
class OpenRouterProvider : public OpenAICompatProvider
{
public:
explicit OpenRouterProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
QString apiKey() const override;
PluginLLMCore::ProviderID providerID() const override;
};
} // namespace QodeAssist::Providers

View File

@@ -1,24 +0,0 @@
// 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 <QString>
namespace QodeAssist::Providers {
// LM Studio presents its OpenAI-compatible API as <host>/v1/..., while the
// OpenAI-style clients expect the base URL to already include /v1. Accept the
// configured URL either with or without the /v1 suffix and return it normalized.
inline QString ensureOpenAIV1Base(const QString &url)
{
QString base = url.trimmed();
while (base.endsWith(QLatin1Char('/')))
base.chop(1);
if (!base.endsWith(QStringLiteral("/v1")))
base += QStringLiteral("/v1");
return base;
}
} // namespace QodeAssist::Providers

View File

@@ -1,48 +0,0 @@
// 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 "pluginllmcore/ProvidersManager.hpp"
#include "providers/ClaudeProvider.hpp"
#include "providers/DeepSeekProvider.hpp"
#include "providers/CodestralProvider.hpp"
#include "providers/GoogleAIProvider.hpp"
#include "providers/LMStudioProvider.hpp"
#include "providers/LMStudioResponsesProvider.hpp"
#include "providers/LlamaCppProvider.hpp"
#include "providers/MistralAIProvider.hpp"
#include "providers/OllamaCompatProvider.hpp"
#include "providers/OllamaProvider.hpp"
#include "providers/OpenAICompatProvider.hpp"
#include "providers/OpenAIProvider.hpp"
#include "providers/OpenAIResponsesProvider.hpp"
#include "providers/OpenRouterAIProvider.hpp"
#include "providers/QwenProvider.hpp"
#include "providers/QwenResponsesProvider.hpp"
namespace QodeAssist::Providers {
inline void registerProviders()
{
auto &providerManager = PluginLLMCore::ProvidersManager::instance();
providerManager.registerProvider<OllamaProvider>();
providerManager.registerProvider<OllamaCompatProvider>();
providerManager.registerProvider<ClaudeProvider>();
providerManager.registerProvider<OpenAIProvider>();
providerManager.registerProvider<OpenAIResponsesProvider>();
providerManager.registerProvider<OpenAICompatProvider>();
providerManager.registerProvider<LMStudioProvider>();
providerManager.registerProvider<LMStudioResponsesProvider>();
providerManager.registerProvider<OpenRouterProvider>();
providerManager.registerProvider<MistralAIProvider>();
providerManager.registerProvider<GoogleAIProvider>();
providerManager.registerProvider<LlamaCppProvider>();
providerManager.registerProvider<CodestralProvider>();
providerManager.registerProvider<QwenProvider>();
providerManager.registerProvider<QwenResponsesProvider>();
providerManager.registerProvider<DeepSeekProvider>();
}
} // namespace QodeAssist::Providers

View File

@@ -1,111 +0,0 @@
// 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 "QwenProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "tools/ToolsRegistration.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
QwenProvider::QwenProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString QwenProvider::name() const
{
return "Qwen (OpenAI)";
}
QString QwenProvider::apiKey() const
{
return Settings::providerSettings().qwenApiKey();
}
QString QwenProvider::url() const
{
return "https://dashscope-intl.aliyuncs.com/compatible-mode/v1";
}
QFuture<QList<QString>> QwenProvider::getInstalledModels(const QString &url)
{
m_client->setUrl(url);
m_client->setApiKey(apiKey());
return m_client->listModels();
}
PluginLLMCore::ProviderID QwenProvider::providerID() const
{
return PluginLLMCore::ProviderID::Qwen;
}
PluginLLMCore::ProviderCapabilities QwenProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image
| PluginLLMCore::ProviderCapability::Thinking
| PluginLLMCore::ProviderCapability::ModelListing;
}
void QwenProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_tokens"] = settings.maxTokens();
request["temperature"] = settings.temperature();
if (settings.useTopP())
request["top_p"] = settings.topP();
if (settings.useTopK())
request["top_k"] = settings.topK();
if (settings.useFrequencyPenalty())
request["frequency_penalty"] = settings.frequencyPenalty();
if (settings.usePresencePenalty())
request["presence_penalty"] = settings.presencePenalty();
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
applyModelParams(Settings::quickRefactorSettings());
} else {
applyModelParams(Settings::chatAssistantSettings());
}
if (isToolsEnabled) {
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(QString("Added %1 tools to Qwen request").arg(toolsDefinitions.size()));
}
}
}
::LLMQore::BaseClient *QwenProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,38 +0,0 @@
// 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 <LLMQore/OpenAIClient.hpp>
#include <pluginllmcore/Provider.hpp>
namespace QodeAssist::Providers {
class QwenProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit QwenProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::OpenAIClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -1,134 +0,0 @@
// 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 "QwenResponsesProvider.hpp"
#include <LLMQore/ToolsManager.hpp>
#include "logger/Logger.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/CodeCompletionSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProviderSettings.hpp"
#include "settings/QuickRefactorSettings.hpp"
#include "tools/ToolsRegistration.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace QodeAssist::Providers {
QwenResponsesProvider::QwenResponsesProvider(QObject *parent)
: PluginLLMCore::Provider(parent)
, m_client(new ::LLMQore::OpenAIResponsesClient(QString(), QString(), QString(), this))
{
Tools::registerQodeAssistTools(m_client->tools());
}
QString QwenResponsesProvider::name() const
{
return "Qwen (OpenAI Response)";
}
QString QwenResponsesProvider::apiKey() const
{
return Settings::providerSettings().qwenApiKey();
}
QString QwenResponsesProvider::url() const
{
return "https://dashscope-intl.aliyuncs.com/compatible-mode/v1";
}
void QwenResponsesProvider::prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled)
{
if (!prompt->isSupportProvider(providerID())) {
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
}
prompt->prepareRequest(request, context);
auto applyModelParams = [&request](const auto &settings) {
request["max_output_tokens"] = settings.maxTokens();
if (settings.useTopP()) {
request["top_p"] = settings.topP();
}
};
auto applyThinkingMode = [&request](const auto &settings) {
QString effortStr = settings.openAIResponsesReasoningEffort.stringValue().toLower();
if (effortStr.isEmpty()) {
effortStr = "medium";
}
QJsonObject reasoning;
reasoning["effort"] = effortStr;
request["reasoning"] = reasoning;
request["max_output_tokens"] = settings.thinkingMaxTokens();
request["store"] = true;
QJsonArray include;
include.append("reasoning.encrypted_content");
request["include"] = include;
};
if (type == PluginLLMCore::RequestType::QuickRefactoring) {
const auto &qrSettings = Settings::quickRefactorSettings();
applyModelParams(qrSettings);
if (isThinkingEnabled) {
applyThinkingMode(qrSettings);
}
} else {
const auto &chatSettings = Settings::chatAssistantSettings();
applyModelParams(chatSettings);
if (isThinkingEnabled) {
applyThinkingMode(chatSettings);
}
}
if (isToolsEnabled) {
const auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
if (!toolsDefinitions.isEmpty()) {
request["tools"] = toolsDefinitions;
LOG_MESSAGE(
QString("Added %1 tools to Qwen Responses request").arg(toolsDefinitions.size()));
}
}
request["stream"] = true;
}
QFuture<QList<QString>> QwenResponsesProvider::getInstalledModels(const QString &url)
{
m_client->setUrl(url);
m_client->setApiKey(apiKey());
return m_client->listModels();
}
PluginLLMCore::ProviderID QwenResponsesProvider::providerID() const
{
return PluginLLMCore::ProviderID::OpenAIResponses;
}
PluginLLMCore::ProviderCapabilities QwenResponsesProvider::capabilities() const
{
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Thinking
| PluginLLMCore::ProviderCapability::Image | PluginLLMCore::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *QwenResponsesProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -1,38 +0,0 @@
// 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 <LLMQore/OpenAIResponsesClient.hpp>
#include <pluginllmcore/Provider.hpp>
namespace QodeAssist::Providers {
class QwenResponsesProvider : public PluginLLMCore::Provider
{
Q_OBJECT
public:
explicit QwenResponsesProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
PluginLLMCore::PromptTemplate *prompt,
PluginLLMCore::ContextData context,
PluginLLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
PluginLLMCore::ProviderID providerID() const override;
PluginLLMCore::ProviderCapabilities capabilities() const override;
::LLMQore::BaseClient *client() const override;
QString apiKey() const override;
private:
::LLMQore::OpenAIResponsesClient *m_client;
};
} // namespace QodeAssist::Providers

View File

@@ -42,13 +42,12 @@
#include "chat/ChatOutputPane.h"
#include "chat/NavigationPanel.hpp"
#include "context/DocumentReaderQtCreator.hpp"
#include "pluginllmcore/ProvidersManager.hpp"
#include "logger/RequestPerformanceLogger.hpp"
#include "mcp/McpClientsManager.hpp"
#include "mcp/McpServerManager.hpp"
#include "sources/skills/SkillsManager.hpp"
#include "tools/ToolsRegistration.hpp"
#include "providers/Providers.hpp"
#include "settings/AgentRole.hpp"
#include "settings/ChatAssistantSettings.hpp"
#include "settings/GeneralSettings.hpp"
#include "settings/ProjectSettingsPanel.hpp"
@@ -64,7 +63,6 @@
#include <AgentFactory.hpp>
#include <GenericProvider.hpp>
#include <SessionManager.hpp>
#include "templates/Templates.hpp"
#include "widgets/CustomInstructionsManager.hpp"
#include "widgets/QuickRefactorDialog.hpp"
#include <ChatView/ChatView.hpp>
@@ -141,9 +139,6 @@ public:
loadTranslations();
Providers::registerProviders();
Templates::registerTemplates();
CustomInstructionsManager::instance().loadInstructions();
Utils::Icon QCODEASSIST_ICON(
@@ -180,16 +175,6 @@ public:
m_sessionFileRegistry = new Chat::SessionFileRegistry{this};
m_skillsManager = new Skills::SkillsManager{this};
{
auto &providers = PluginLLMCore::ProvidersManager::instance();
for (const QString &providerName : providers.providersNames()) {
if (auto *provider = providers.getProviderByName(providerName)) {
if (auto *toolsManager = provider->toolsManager())
Tools::registerSkillTool(toolsManager, m_skillsManager);
}
}
}
if (Settings::chatAssistantSettings().enableChatInBottomToolBar()) {
m_chatOutputPane = new Chat::ChatOutputPane{
m_engine, m_sessionFileRegistry, m_skillsManager};
@@ -214,8 +199,26 @@ public:
m_providerLauncher,
m_providersPageNavigator);
// Ensure the default agent roles exist on disk before agents load, so a
// chat agent's `role = "<id>"` resolves to a system prompt even on a fresh
// install where the Roles settings page was never opened.
Settings::AgentRolesManager::ensureDefaultRoles();
m_agentFactory = new AgentFactory(m_providerInstanceFactory, m_providerSecretsStore, this);
m_sessionManager = new SessionManager(m_agentFactory, this);
{
auto &contributors = m_sessionManager->toolContributors();
contributors.add([](::LLMQore::ToolsManager *tools) {
Tools::registerQodeAssistTools(tools);
});
contributors.add([skills = m_skillsManager](::LLMQore::ToolsManager *tools) {
if (skills)
Tools::registerSkillTool(tools, skills);
});
contributors.add([](::LLMQore::ToolsManager *tools) {
Mcp::McpClientsManager::instance().registerToolsOn(tools);
});
}
m_engine->rootContext()->setContextProperty("agentFactory", m_agentFactory);
m_engine->rootContext()->setContextProperty("sessionManager", m_sessionManager);
m_agentsPageNavigator = new Settings::AgentsPageNavigator(this);

View File

@@ -171,7 +171,10 @@ void AgentListPane::rebuildList()
const QSet<QString> &activeTags = m_tagStrip->activeTags();
auto addAgents = [&](const std::vector<const AgentConfig *> &agents) {
for (const AgentConfig *cfg : agents) {
auto *item = new AgentListItem(*cfg, content);
AgentConfig display = *cfg;
if (m_factory)
display.model = m_factory->effectiveModel(cfg->name);
auto *item = new AgentListItem(display, content);
item->setSelected(cfg->name == m_currentName);
item->setActiveTags(activeTags);
connect(item, &AgentListItem::clicked, this, &AgentListPane::onRowClicked);

View File

@@ -9,6 +9,7 @@ add_library(Session STATIC
Session.hpp Session.cpp
SessionManager.hpp SessionManager.cpp
SystemPromptBuilder.hpp SystemPromptBuilder.cpp
ToolContributorRegistry.hpp
)
target_link_libraries(Session

View File

@@ -210,7 +210,9 @@ LLMQore::RequestID Session::sendCompletion(Templates::ContextData ctx)
if (!provider->prepareRequest(payload, tmpl, ctx, /*tools=*/false, /*thinking=*/false))
return {};
const auto id = provider->sendRequest(QUrl(provider->url()), payload, cfg.endpoint);
QString endpoint = cfg.endpoint;
endpoint.replace(QStringLiteral("${MODEL}"), cfg.model);
const auto id = provider->sendRequest(QUrl(provider->url()), payload, endpoint);
if (id.isEmpty())
return {};
@@ -242,7 +244,9 @@ LLMQore::RequestID Session::dispatch(
if (!provider->prepareRequest(payload, tmpl, ctx, tools, thinking))
return {};
const auto id = provider->sendRequest(QUrl(provider->url()), payload, cfg.endpoint);
QString endpoint = cfg.endpoint;
endpoint.replace(QStringLiteral("${MODEL}"), cfg.model);
const auto id = provider->sendRequest(QUrl(provider->url()), payload, endpoint);
if (id.isEmpty())
return {};

View File

@@ -9,6 +9,8 @@
#include <QPointer>
#include <QString>
#include "ToolContributorRegistry.hpp"
namespace QodeAssist {
class AgentFactory;
@@ -41,6 +43,9 @@ public:
void cancelAll();
ToolContributorRegistry &toolContributors() noexcept { return m_toolContributors; }
const ToolContributorRegistry &toolContributors() const noexcept { return m_toolContributors; }
signals:
void sessionCreated(Session *session);
void sessionRemoved(Session *session);
@@ -48,6 +53,7 @@ signals:
private:
QPointer<AgentFactory> m_agentFactory;
QList<QPointer<Session>> m_sessions;
ToolContributorRegistry m_toolContributors;
};
} // namespace QodeAssist

View File

@@ -0,0 +1,41 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <functional>
#include <utility>
#include <vector>
namespace LLMQore {
class ToolsManager;
}
namespace QodeAssist {
class ToolContributorRegistry
{
public:
using Contributor = std::function<void(LLMQore::ToolsManager *)>;
void add(Contributor contributor)
{
if (contributor)
m_contributors.push_back(std::move(contributor));
}
void contribute(LLMQore::ToolsManager *tools) const
{
if (!tools)
return;
for (const auto &contributor : m_contributors)
contributor(tools);
}
void clear() { m_contributors.clear(); }
private:
std::vector<Contributor> m_contributors;
};
} // namespace QodeAssist

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,6 +2,7 @@ add_library(Common INTERFACE)
target_sources(Common INTERFACE
ContextData.hpp
ResponseCleaner.hpp
)
target_include_directories(Common INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -8,7 +8,7 @@
#include <QStringList>
#include <QRegularExpression>
namespace QodeAssist::PluginLLMCore {
namespace QodeAssist {
class ResponseCleaner
{
@@ -100,5 +100,4 @@ private:
}
};
} // namespace QodeAssist::PluginLLMCore
} // namespace QodeAssist

View File

@@ -199,6 +199,7 @@ public:
AgentRosterRow(int index,
const QString &name,
const AgentConfig *cfg,
const QString &effectiveModel,
bool active,
bool first,
bool last,
@@ -226,6 +227,7 @@ private:
AgentRosterRow::AgentRosterRow(int index,
const QString &name,
const AgentConfig *cfg,
const QString &effectiveModel,
bool active,
bool first,
bool last,
@@ -282,7 +284,7 @@ AgentRosterRow::AgentRosterRow(int index,
body->setSpacing(2);
const QString displayName = cfg ? cfg->name : tr("%1 (missing)").arg(name);
const QString model = cfg ? cfg->model : QString();
const QString model = effectiveModel;
const bool isUser = cfg && cfg->isUserSource();
body->addWidget(buildIdentityLine(displayName, model, active, isUser, theme));
@@ -562,9 +564,11 @@ void AgentRosterWidget::rebuildRows()
for (int i = 0; i < m_names.size(); ++i) {
const QString &name = m_names.at(i);
const AgentConfig *cfg = m_factory ? m_factory->configByName(name) : nullptr;
const QString effModel = m_factory ? m_factory->effectiveModel(name) : QString();
auto *row = new AgentRosterRow(i,
name,
cfg,
effModel,
i == m_activeIndex,
/*first*/ i == 0,
/*last*/ i == m_names.size() - 1,

View File

@@ -1,75 +0,0 @@
// 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 "pluginllmcore/PromptTemplate.hpp"
#include <QJsonArray>
namespace QodeAssist::Templates {
class Alpaca : public PluginLLMCore::PromptTemplate
{
public:
QString name() const override { return "Alpaca"; }
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::Chat; }
QStringList stopWords() const override
{
return QStringList() << "### Instruction:" << "### Response:";
}
void prepareRequest(QJsonObject &request, const PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
switch (id) {
case PluginLLMCore::ProviderID::Ollama:
case PluginLLMCore::ProviderID::LMStudio:
case PluginLLMCore::ProviderID::OpenRouter:
case PluginLLMCore::ProviderID::OpenAICompatible:
case PluginLLMCore::ProviderID::LlamaCpp:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -1,76 +0,0 @@
// 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 "pluginllmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class ChatML : public PluginLLMCore::PromptTemplate
{
public:
QString name() const override { return "ChatML"; }
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::Chat; }
QStringList stopWords() const override
{
return QStringList() << "<|im_start|>" << "<|im_end|>";
}
void prepareRequest(QJsonObject &request, const PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
switch (id) {
case PluginLLMCore::ProviderID::Ollama:
case PluginLLMCore::ProviderID::LMStudio:
case PluginLLMCore::ProviderID::OpenRouter:
case PluginLLMCore::ProviderID::OpenAICompatible:
case PluginLLMCore::ProviderID::LlamaCpp:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -1,150 +0,0 @@
// 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 "pluginllmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class Claude : public PluginLLMCore::PromptTemplate
{
public:
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::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 PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
switch (id) {
case QodeAssist::PluginLLMCore::ProviderID::Claude:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -1,47 +0,0 @@
// 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 "pluginllmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class CodeLlamaFim : public PluginLLMCore::PromptTemplate
{
public:
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::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 PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
switch (id) {
case QodeAssist::PluginLLMCore::ProviderID::Ollama:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -1,48 +0,0 @@
// 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 "pluginllmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class CodeLlamaQMLFim : public PluginLLMCore::PromptTemplate
{
public:
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::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 PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
switch (id) {
case QodeAssist::PluginLLMCore::ProviderID::Ollama:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -1,33 +0,0 @@
// 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 "llmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class DeepSeekCoderFim : public LLMQore::PromptTemplate
{
public:
LLMQore::TemplateType type() const override { return LLMQore::TemplateType::Fim; }
QString name() const override { return "DeepSeekCoder FIM"; }
QString promptTemplate() const override
{
return "<fim▁begin>%1<fim▁hole>%2<fim▁end>";
}
QStringList stopWords() const override { return QStringList(); }
void prepareRequest(QJsonObject &request, const LLMQore::ContextData &context) const override
{
QString formattedPrompt = promptTemplate().arg(context.prefix, context.suffix);
request["prompt"] = formattedPrompt;
}
QString description() const override
{
return "The message will contain the following tokens: "
"<fim▁begin>%1<fim▁hole>%2<fim▁end>";
}
};
} // namespace QodeAssist::Templates

View File

@@ -1,161 +0,0 @@
// 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 "pluginllmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class GoogleAI : public PluginLLMCore::PromptTemplate
{
public:
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::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 PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
return id == QodeAssist::PluginLLMCore::ProviderID::GoogleAI;
}
};
} // namespace QodeAssist::Templates

View File

@@ -1,73 +0,0 @@
// 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 "pluginllmcore/PromptTemplate.hpp"
#include <QJsonArray>
namespace QodeAssist::Templates {
class Llama2 : public PluginLLMCore::PromptTemplate
{
public:
QString name() const override { return "Llama 2"; }
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::Chat; }
QStringList stopWords() const override { return QStringList() << "[INST]"; }
void prepareRequest(QJsonObject &request, const PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
switch (id) {
case PluginLLMCore::ProviderID::Ollama:
case PluginLLMCore::ProviderID::LMStudio:
case PluginLLMCore::ProviderID::OpenRouter:
case PluginLLMCore::ProviderID::OpenAICompatible:
case PluginLLMCore::ProviderID::LlamaCpp:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -1,80 +0,0 @@
// 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 "pluginllmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class Llama3 : public PluginLLMCore::PromptTemplate
{
public:
QString name() const override { return "Llama 3"; }
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::TemplateType::Chat; }
QStringList stopWords() const override
{
return QStringList() << "<|start_header_id|>" << "<|end_header_id|>" << "<|eot_id|>";
}
void prepareRequest(QJsonObject &request, const PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
switch (id) {
case PluginLLMCore::ProviderID::Ollama:
case PluginLLMCore::ProviderID::LMStudio:
case PluginLLMCore::ProviderID::OpenRouter:
case PluginLLMCore::ProviderID::OpenAICompatible:
case PluginLLMCore::ProviderID::LlamaCpp:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

View File

@@ -1,55 +0,0 @@
// 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 "pluginllmcore/PromptTemplate.hpp"
namespace QodeAssist::Templates {
class LlamaCppFim : public PluginLLMCore::PromptTemplate
{
public:
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::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 PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
return id == QodeAssist::PluginLLMCore::ProviderID::LlamaCpp;
}
};
} // namespace QodeAssist::Templates

View File

@@ -1,121 +0,0 @@
// 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 "pluginllmcore/PromptTemplate.hpp"
#include "templates/ToolMessages.hpp"
namespace QodeAssist::Templates {
class MistralAIFim : public PluginLLMCore::PromptTemplate
{
public:
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::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 PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
switch (id) {
case QodeAssist::PluginLLMCore::ProviderID::MistralAI:
return true;
default:
return false;
}
}
};
class MistralAIChat : public PluginLLMCore::PromptTemplate
{
public:
PluginLLMCore::TemplateType type() const override { return PluginLLMCore::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 PluginLLMCore::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(PluginLLMCore::ProviderID id) const override
{
switch (id) {
case QodeAssist::PluginLLMCore::ProviderID::MistralAI:
return true;
default:
return false;
}
}
};
} // namespace QodeAssist::Templates

Some files were not shown because too many files have changed in this diff Show More