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

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

View File

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

@@ -0,0 +1,162 @@
// 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)
: Providers::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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
request["temperature"] = Settings::codeCompletionSettings().temperature();
} else if (type == LLMCore::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 != LLMCore::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();
}
Providers::ProviderID ClaudeProvider::providerID() const
{
return Providers::ProviderID::Claude;
}
Providers::ProviderCapabilities ClaudeProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Thinking
| Providers::ProviderCapability::Image
| Providers::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *ClaudeProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

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

View File

@@ -0,0 +1,35 @@
// 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";
}
Providers::ProviderCapabilities CodestralProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Image;
}
} // namespace QodeAssist::Providers

View File

@@ -0,0 +1,22 @@
// 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;
Providers::ProviderCapabilities capabilities() const override;
};
} // namespace QodeAssist::Providers

View File

@@ -0,0 +1,111 @@
// 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)
: Providers::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();
}
Providers::ProviderID DeepSeekProvider::providerID() const
{
return Providers::ProviderID::DeepSeek;
}
Providers::ProviderCapabilities DeepSeekProvider::capabilities() const
{
return Providers::ProviderCapability::Tools
| Providers::ProviderCapability::Thinking
| Providers::ProviderCapability::ModelListing;
}
void DeepSeekProvider::prepareRequest(
QJsonObject &request,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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

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

View File

@@ -0,0 +1,161 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#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)
: Providers::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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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();
}
Providers::ProviderID GoogleAIProvider::providerID() const
{
return Providers::ProviderID::GoogleAI;
}
Providers::ProviderCapabilities GoogleAIProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Thinking
| Providers::ProviderCapability::Image
| Providers::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 Providers::Provider::sendRequest(url, cleaned, endpoint);
}
::LLMQore::BaseClient *GoogleAIProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -0,0 +1,42 @@
// 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/Provider.hpp"
#include <LLMQore/GoogleAIClient.hpp>
namespace QodeAssist::Providers {
class GoogleAIProvider : public Providers::Provider
{
Q_OBJECT
public:
explicit GoogleAIProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
Providers::ProviderID providerID() const override;
Providers::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

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

View File

@@ -0,0 +1,122 @@
// 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)
: Providers::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();
}
Providers::ProviderID LMStudioProvider::providerID() const
{
return Providers::ProviderID::LMStudio;
}
Providers::ProviderCapabilities LMStudioProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Image
| Providers::ProviderCapability::ModelListing
| Providers::ProviderCapability::Thinking;
}
void LMStudioProvider::prepareRequest(
QJsonObject &request,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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 Providers::Provider::sendRequest(
QUrl(ensureOpenAIV1Base(url.toString())), payload, endpoint);
}
::LLMQore::BaseClient *LMStudioProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -0,0 +1,41 @@
// 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 "providers/Provider.hpp"
namespace QodeAssist::Providers {
class LMStudioProvider : public Providers::Provider
{
Q_OBJECT
public:
explicit LMStudioProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
Providers::ProviderID providerID() const override;
Providers::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

@@ -0,0 +1,146 @@
// 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)
: Providers::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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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 Providers::Provider::sendRequest(
QUrl(ensureOpenAIV1Base(url.toString())), payload, endpoint);
}
Providers::ProviderID LMStudioResponsesProvider::providerID() const
{
return Providers::ProviderID::OpenAIResponses;
}
Providers::ProviderCapabilities LMStudioResponsesProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Thinking
| Providers::ProviderCapability::Image
| Providers::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *LMStudioResponsesProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -0,0 +1,41 @@
// 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 "providers/Provider.hpp"
namespace QodeAssist::Providers {
class LMStudioResponsesProvider : public Providers::Provider
{
Q_OBJECT
public:
explicit LMStudioResponsesProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
Providers::ProviderID providerID() const override;
Providers::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

@@ -0,0 +1,127 @@
// 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)
: Providers::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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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();
}
Providers::ProviderID LlamaCppProvider::providerID() const
{
return Providers::ProviderID::LlamaCpp;
}
Providers::ProviderCapabilities LlamaCppProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Thinking
| Providers::ProviderCapability::Image
| Providers::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *LlamaCppProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

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

View File

@@ -0,0 +1,113 @@
// 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)
: Providers::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();
}
Providers::ProviderID MistralAIProvider::providerID() const
{
return Providers::ProviderID::MistralAI;
}
Providers::ProviderCapabilities MistralAIProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Image
| Providers::ProviderCapability::ModelListing
| Providers::ProviderCapability::Thinking;
}
void MistralAIProvider::prepareRequest(
QJsonObject &request,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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

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

View File

@@ -0,0 +1,126 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#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)
: Providers::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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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 Providers::Provider::sendRequest(url, payload, effectiveEndpoint);
}
Providers::ProviderID OllamaCompatProvider::providerID() const
{
return Providers::ProviderID::OpenAICompatible;
}
Providers::ProviderCapabilities OllamaCompatProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Image
| Providers::ProviderCapability::ModelListing
| Providers::ProviderCapability::Thinking;
}
::LLMQore::BaseClient *OllamaCompatProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

@@ -0,0 +1,41 @@
// 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 "providers/Provider.hpp"
namespace QodeAssist::Providers {
class OllamaCompatProvider : public Providers::Provider
{
Q_OBJECT
public:
explicit OllamaCompatProvider(QObject *parent = nullptr);
QString name() const override;
QString url() const override;
void prepareRequest(
QJsonObject &request,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::RequestType type,
bool isToolsEnabled,
bool isThinkingEnabled) override;
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
Providers::ProviderID providerID() const override;
Providers::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

@@ -0,0 +1,139 @@
// 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)
: Providers::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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applySettings(Settings::codeCompletionSettings());
} else if (type == LLMCore::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();
}
Providers::ProviderID OllamaProvider::providerID() const
{
return Providers::ProviderID::Ollama;
}
Providers::ProviderCapabilities OllamaProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Thinking
| Providers::ProviderCapability::Image
| Providers::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *OllamaProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

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

View File

@@ -0,0 +1,111 @@
// 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)
: Providers::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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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>{});
}
Providers::ProviderID OpenAICompatProvider::providerID() const
{
return Providers::ProviderID::OpenAICompatible;
}
Providers::ProviderCapabilities OpenAICompatProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Image
| Providers::ProviderCapability::Thinking;
}
::LLMQore::BaseClient *OpenAICompatProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

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

View File

@@ -0,0 +1,142 @@
// 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)
: Providers::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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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;
});
}
Providers::ProviderID OpenAIProvider::providerID() const
{
return Providers::ProviderID::OpenAI;
}
Providers::ProviderCapabilities OpenAIProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Image
| Providers::ProviderCapability::ModelListing
| Providers::ProviderCapability::Thinking;
}
::LLMQore::BaseClient *OpenAIProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

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

View File

@@ -0,0 +1,149 @@
// 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)
: Providers::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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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;
});
}
Providers::ProviderID OpenAIResponsesProvider::providerID() const
{
return Providers::ProviderID::OpenAIResponses;
}
Providers::ProviderCapabilities OpenAIResponsesProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Thinking
| Providers::ProviderCapability::Image
| Providers::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *OpenAIResponsesProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

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

View File

@@ -0,0 +1,40 @@
// 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";
}
Providers::ProviderID OpenRouterProvider::providerID() const
{
return Providers::ProviderID::OpenRouter;
}
} // namespace QodeAssist::Providers

View File

@@ -0,0 +1,22 @@
// 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;
Providers::ProviderID providerID() const override;
};
} // namespace QodeAssist::Providers

View File

@@ -0,0 +1,50 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#include "providers/Provider.hpp"
#include <LLMQore/BaseClient.hpp>
#include <LLMQore/ToolsManager.hpp>
#include <QJsonDocument>
#include "logger/Logger.hpp"
namespace QodeAssist::Providers {
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::Providers

View File

@@ -0,0 +1,67 @@
// 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 "llmcore/ContextData.hpp"
#include "templates/PromptTemplate.hpp"
#include "LLMQore/BaseClient.hpp"
#include "llmcore/RequestType.hpp"
namespace LLMQore {
class BaseClient;
class ToolsManager;
}
class QJsonObject;
namespace QodeAssist::Providers {
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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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::Providers

View File

@@ -0,0 +1,22 @@
// 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::Providers {
enum class ProviderID {
Any,
Ollama,
LMStudio,
Claude,
OpenAI,
OpenAICompatible,
OpenAIResponses,
MistralAI,
OpenRouter,
GoogleAI,
LlamaCpp,
Qwen,
DeepSeek
};
}

View File

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

@@ -0,0 +1,48 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
#pragma once
#include "providers/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 = Providers::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

@@ -0,0 +1,32 @@
// 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 "providers/ProvidersManager.hpp"
namespace QodeAssist::Providers {
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::Providers

View File

@@ -0,0 +1,41 @@
// 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 "providers/IProviderRegistry.hpp"
#include <QMap>
namespace QodeAssist::Providers {
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::Providers

View File

@@ -0,0 +1,113 @@
// 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)
: Providers::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();
}
Providers::ProviderID QwenProvider::providerID() const
{
return Providers::ProviderID::Qwen;
}
Providers::ProviderCapabilities QwenProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Image
| Providers::ProviderCapability::Thinking
| Providers::ProviderCapability::ModelListing;
}
void QwenProvider::prepareRequest(
QJsonObject &request,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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

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

View File

@@ -0,0 +1,136 @@
// 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)
: Providers::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,
Templates::PromptTemplate *prompt,
LLMCore::ContextData context,
LLMCore::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 == LLMCore::RequestType::CodeCompletion) {
applyModelParams(Settings::codeCompletionSettings());
} else if (type == LLMCore::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();
}
Providers::ProviderID QwenResponsesProvider::providerID() const
{
return Providers::ProviderID::OpenAIResponses;
}
Providers::ProviderCapabilities QwenResponsesProvider::capabilities() const
{
return Providers::ProviderCapability::Tools | Providers::ProviderCapability::Thinking
| Providers::ProviderCapability::Image | Providers::ProviderCapability::ModelListing;
}
::LLMQore::BaseClient *QwenResponsesProvider::client() const
{
return m_client;
}
} // namespace QodeAssist::Providers

View File

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