mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-06-15 02:39:19 -04:00
refactor: add to template agent roles
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
list(APPEND CMAKE_PREFIX_PATH "/Users/palm1r/Qt/Qt Creator.sdk/lib/cmake/QtCreator")
|
||||||
|
|
||||||
project(QodeAssist)
|
project(QodeAssist)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
@@ -38,7 +40,6 @@ add_definitions(
|
|||||||
|
|
||||||
add_subdirectory(sources)
|
add_subdirectory(sources)
|
||||||
add_subdirectory(logger)
|
add_subdirectory(logger)
|
||||||
add_subdirectory(pluginllmcore)
|
|
||||||
add_subdirectory(settings)
|
add_subdirectory(settings)
|
||||||
add_subdirectory(UIControls)
|
add_subdirectory(UIControls)
|
||||||
add_subdirectory(ChatView)
|
add_subdirectory(ChatView)
|
||||||
@@ -70,7 +71,6 @@ add_qtc_plugin(QodeAssist
|
|||||||
QtCreator::Utils
|
QtCreator::Utils
|
||||||
QtCreator::CPlusPlus
|
QtCreator::CPlusPlus
|
||||||
LLMQore
|
LLMQore
|
||||||
PluginLLMCore
|
|
||||||
ProvidersConfig
|
ProvidersConfig
|
||||||
Agents
|
Agents
|
||||||
Skills
|
Skills
|
||||||
@@ -84,42 +84,6 @@ add_qtc_plugin(QodeAssist
|
|||||||
QodeAssisttr.h
|
QodeAssisttr.h
|
||||||
LLMClientInterface.hpp LLMClientInterface.cpp
|
LLMClientInterface.hpp LLMClientInterface.cpp
|
||||||
RefactorContextHelper.hpp
|
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
|
QodeAssist.qrc
|
||||||
LSPCompletion.hpp
|
LSPCompletion.hpp
|
||||||
LLMSuggestion.hpp LLMSuggestion.cpp
|
LLMSuggestion.hpp LLMSuggestion.cpp
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <SessionManager.hpp>
|
#include <SessionManager.hpp>
|
||||||
|
|
||||||
#include "ChatAgentController.hpp"
|
#include "ChatAgentController.hpp"
|
||||||
|
#include "AgentRole.hpp"
|
||||||
#include "ChatAssistantSettings.hpp"
|
#include "ChatAssistantSettings.hpp"
|
||||||
#include "ChatCompressor.hpp"
|
#include "ChatCompressor.hpp"
|
||||||
#include "ChatHistoryStore.hpp"
|
#include "ChatHistoryStore.hpp"
|
||||||
@@ -393,6 +394,43 @@ void ChatRootView::setCurrentChatAgent(const QString &name)
|
|||||||
m_agentController->setCurrentAgent(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 ChatRootView::searchSkills(const QString &query) const
|
||||||
{
|
{
|
||||||
QVariantList results;
|
QVariantList results;
|
||||||
@@ -501,6 +539,7 @@ void ChatRootView::dispatchSend(
|
|||||||
m_clientInterface->setSkillsManager(skillsManager());
|
m_clientInterface->setSkillsManager(skillsManager());
|
||||||
m_clientInterface->setSessionManager(sessionManager());
|
m_clientInterface->setSessionManager(sessionManager());
|
||||||
m_clientInterface->setActiveAgent(currentChatAgent());
|
m_clientInterface->setActiveAgent(currentChatAgent());
|
||||||
|
m_clientInterface->setActiveRole(currentRole());
|
||||||
m_clientInterface->sendMessage(message, attachments, linkedFiles);
|
m_clientInterface->sendMessage(message, attachments, linkedFiles);
|
||||||
|
|
||||||
m_fileManager->clearIntermediateStorage();
|
m_fileManager->clearIntermediateStorage();
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ class ChatRootView : public QQuickItem
|
|||||||
Q_PROPERTY(bool isThinkingSupport READ isThinkingSupport NOTIFY isThinkingSupportChanged FINAL)
|
Q_PROPERTY(bool isThinkingSupport READ isThinkingSupport NOTIFY isThinkingSupportChanged FINAL)
|
||||||
Q_PROPERTY(QStringList availableChatAgents READ availableChatAgents NOTIFY availableChatAgentsChanged FINAL)
|
Q_PROPERTY(QStringList availableChatAgents READ availableChatAgents NOTIFY availableChatAgentsChanged FINAL)
|
||||||
Q_PROPERTY(QString currentChatAgent READ currentChatAgent WRITE setCurrentChatAgent NOTIFY currentChatAgentChanged 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 isCompressing READ isCompressing NOTIFY isCompressingChanged FINAL)
|
||||||
Q_PROPERTY(bool isInEditor READ isInEditor NOTIFY isInEditorChanged FINAL)
|
Q_PROPERTY(bool isInEditor READ isInEditor NOTIFY isInEditorChanged FINAL)
|
||||||
Q_PROPERTY(QString chatTitle READ chatTitle NOTIFY chatTitleChanged FINAL)
|
Q_PROPERTY(QString chatTitle READ chatTitle NOTIFY chatTitleChanged FINAL)
|
||||||
@@ -155,6 +157,11 @@ public:
|
|||||||
QString currentChatAgent() const;
|
QString currentChatAgent() const;
|
||||||
void setCurrentChatAgent(const QString &name);
|
void setCurrentChatAgent(const QString &name);
|
||||||
|
|
||||||
|
Q_INVOKABLE void loadAvailableRoles();
|
||||||
|
QStringList availableRoles() const;
|
||||||
|
QString currentRole() const;
|
||||||
|
void setCurrentRole(const QString &roleId);
|
||||||
|
|
||||||
int currentMessageTotalEdits() const;
|
int currentMessageTotalEdits() const;
|
||||||
int currentMessageAppliedEdits() const;
|
int currentMessageAppliedEdits() const;
|
||||||
int currentMessagePendingEdits() const;
|
int currentMessagePendingEdits() const;
|
||||||
@@ -208,6 +215,8 @@ signals:
|
|||||||
|
|
||||||
void availableChatAgentsChanged();
|
void availableChatAgentsChanged();
|
||||||
void currentChatAgentChanged();
|
void currentChatAgentChanged();
|
||||||
|
void availableRolesChanged();
|
||||||
|
void currentRoleChanged();
|
||||||
|
|
||||||
void isCompressingChanged();
|
void isCompressingChanged();
|
||||||
void compressionCompleted(const QString &compressedChatPath);
|
void compressionCompleted(const QString &compressedChatPath);
|
||||||
@@ -262,6 +271,9 @@ private:
|
|||||||
|
|
||||||
QString m_lastInfoMessage;
|
QString m_lastInfoMessage;
|
||||||
|
|
||||||
|
QString m_currentRole = QStringLiteral("developer");
|
||||||
|
QStringList m_availableRoles;
|
||||||
|
|
||||||
ChatCompressor *m_chatCompressor;
|
ChatCompressor *m_chatCompressor;
|
||||||
ChatAgentController *m_agentController;
|
ChatAgentController *m_agentController;
|
||||||
FileEditController *m_fileEditController;
|
FileEditController *m_fileEditController;
|
||||||
|
|||||||
@@ -30,7 +30,10 @@
|
|||||||
|
|
||||||
#include <ConversationHistory.hpp>
|
#include <ConversationHistory.hpp>
|
||||||
#include <Message.hpp>
|
#include <Message.hpp>
|
||||||
|
#include <ContextRenderer.hpp>
|
||||||
#include <Session.hpp>
|
#include <Session.hpp>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
#include <SessionManager.hpp>
|
#include <SessionManager.hpp>
|
||||||
#include <SystemPromptBuilder.hpp>
|
#include <SystemPromptBuilder.hpp>
|
||||||
|
|
||||||
@@ -75,6 +78,11 @@ void ClientInterface::setActiveAgent(const QString &agentName)
|
|||||||
m_activeAgent = agentName;
|
m_activeAgent = agentName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientInterface::setActiveRole(const QString &roleId)
|
||||||
|
{
|
||||||
|
m_activeRoleId = roleId;
|
||||||
|
}
|
||||||
|
|
||||||
void ClientInterface::sendMessage(
|
void ClientInterface::sendMessage(
|
||||||
const QString &message,
|
const QString &message,
|
||||||
const QList<QString> &attachments,
|
const QList<QString> &attachments,
|
||||||
@@ -175,9 +183,15 @@ void ClientInterface::sendMessage(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools::registerQodeAssistTools(client->tools());
|
auto *project = ProjectExplorer::ProjectManager::startupProject();
|
||||||
if (m_skillsManager)
|
Templates::ContextRenderer::Bindings bindings;
|
||||||
Tools::registerSkillTool(client->tools(), m_skillsManager);
|
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->setMaxToolContinuations(Settings::toolsSettings().maxToolContinuations());
|
||||||
client->setTransferTimeout(
|
client->setTransferTimeout(
|
||||||
static_cast<int>(Settings::generalSettings().requestTimeout() * 1000));
|
static_cast<int>(Settings::generalSettings().requestTimeout() * 1000));
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public:
|
|||||||
void setSkillsManager(Skills::SkillsManager *skillsManager);
|
void setSkillsManager(Skills::SkillsManager *skillsManager);
|
||||||
void setSessionManager(SessionManager *sessionManager);
|
void setSessionManager(SessionManager *sessionManager);
|
||||||
void setActiveAgent(const QString &agentName);
|
void setActiveAgent(const QString &agentName);
|
||||||
|
void setActiveRole(const QString &roleId);
|
||||||
|
|
||||||
void sendMessage(
|
void sendMessage(
|
||||||
const QString &message,
|
const QString &message,
|
||||||
@@ -98,6 +99,7 @@ private:
|
|||||||
Skills::SkillsManager *m_skillsManager = nullptr;
|
Skills::SkillsManager *m_skillsManager = nullptr;
|
||||||
QPointer<SessionManager> m_sessionManager;
|
QPointer<SessionManager> m_sessionManager;
|
||||||
QString m_activeAgent;
|
QString m_activeAgent;
|
||||||
|
QString m_activeRoleId;
|
||||||
QString m_chatFilePath;
|
QString m_chatFilePath;
|
||||||
|
|
||||||
QHash<QString, RequestContext> m_activeRequests;
|
QHash<QString, RequestContext> m_activeRequests;
|
||||||
|
|||||||
@@ -152,6 +152,19 @@ ChatRootView {
|
|||||||
root.loadAvailableChatAgents()
|
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 {
|
RowLayout {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ Rectangle {
|
|||||||
property alias contextButton: contextButtonId
|
property alias contextButton: contextButtonId
|
||||||
property alias settingsButton: settingsButtonId
|
property alias settingsButton: settingsButtonId
|
||||||
property alias agentSelector: agentSelectorId
|
property alias agentSelector: agentSelectorId
|
||||||
|
property alias roleSelector: roleSelectorId
|
||||||
property alias relocateTooltip: relocateTooltipId
|
property alias relocateTooltip: relocateTooltipId
|
||||||
|
|
||||||
color: palette.window.hslLightness > 0.5 ?
|
color: palette.window.hslLightness > 0.5 ?
|
||||||
@@ -141,7 +142,22 @@ Rectangle {
|
|||||||
QoAToolTip {
|
QoAToolTip {
|
||||||
visible: agentSelectorId.hovered
|
visible: agentSelectorId.hovered
|
||||||
delay: 250
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,14 +334,7 @@ Templates::ContextData LLMClientInterface::prepareContext(
|
|||||||
|
|
||||||
Context::DocumentContextReader
|
Context::DocumentContextReader
|
||||||
reader(documentInfo.document, documentInfo.mimeType, documentInfo.filePath);
|
reader(documentInfo.document, documentInfo.mimeType, documentInfo.filePath);
|
||||||
const PluginLLMCore::ContextData legacy
|
return reader.prepareContext(lineNumber, cursorPosition, m_completeSettings);
|
||||||
= reader.prepareContext(lineNumber, cursorPosition, m_completeSettings);
|
|
||||||
|
|
||||||
Templates::ContextData context;
|
|
||||||
context.prefix = legacy.prefix;
|
|
||||||
context.suffix = legacy.suffix;
|
|
||||||
context.fileContext = legacy.fileContext;
|
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::ContextManager *LLMClientInterface::contextManager() const
|
Context::ContextManager *LLMClientInterface::contextManager() const
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
#include "widgets/EditorChatButtonHandler.hpp"
|
#include "widgets/EditorChatButtonHandler.hpp"
|
||||||
#include "widgets/RefactorWidgetHandler.hpp"
|
#include "widgets/RefactorWidgetHandler.hpp"
|
||||||
#include <languageclient/client.h>
|
#include <languageclient/client.h>
|
||||||
#include <pluginllmcore/IPromptProvider.hpp>
|
|
||||||
#include <pluginllmcore/IProviderRegistry.hpp>
|
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#include <context/DocumentReaderQtCreator.hpp>
|
#include <context/DocumentReaderQtCreator.hpp>
|
||||||
#include <context/Utils.hpp>
|
#include <context/Utils.hpp>
|
||||||
#include <logger/Logger.hpp>
|
#include <logger/Logger.hpp>
|
||||||
#include <pluginllmcore/ResponseCleaner.hpp>
|
#include <sources/common/ResponseCleaner.hpp>
|
||||||
#include <settings/QuickRefactorSettings.hpp>
|
#include <settings/QuickRefactorSettings.hpp>
|
||||||
#include <settings/ToolsSettings.hpp>
|
#include <settings/ToolsSettings.hpp>
|
||||||
|
|
||||||
@@ -378,7 +378,7 @@ void QuickRefactorHandler::handleLLMResponse(
|
|||||||
|
|
||||||
if (isComplete) {
|
if (isComplete) {
|
||||||
m_isRefactoringInProgress = false;
|
m_isRefactoringInProgress = false;
|
||||||
QString cleanedResponse = PluginLLMCore::ResponseCleaner::clean(response);
|
QString cleanedResponse = ResponseCleaner::clean(response);
|
||||||
|
|
||||||
RefactorResult result;
|
RefactorResult result;
|
||||||
result.newText = cleanedResponse;
|
result.newText = cleanedResponse;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ target_link_libraries(Context
|
|||||||
QtCreator::Utils
|
QtCreator::Utils
|
||||||
QtCreator::ProjectExplorer
|
QtCreator::ProjectExplorer
|
||||||
PRIVATE
|
PRIVATE
|
||||||
PluginLLMCore
|
Common
|
||||||
QodeAssistSettings
|
QodeAssistSettings
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ CopyrightInfo DocumentContextReader::copyrightInfo() const
|
|||||||
return m_copyrightInfo;
|
return m_copyrightInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginLLMCore::ContextData DocumentContextReader::prepareContext(
|
Templates::ContextData DocumentContextReader::prepareContext(
|
||||||
int lineNumber, int cursorPosition, const Settings::CodeCompletionSettings &settings) const
|
int lineNumber, int cursorPosition, const Settings::CodeCompletionSettings &settings) const
|
||||||
{
|
{
|
||||||
QString contextBefore;
|
QString contextBefore;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <texteditor/textdocument.h>
|
#include <texteditor/textdocument.h>
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
|
|
||||||
#include <pluginllmcore/ContextData.hpp>
|
#include <sources/common/ContextData.hpp>
|
||||||
#include <settings/CodeCompletionSettings.hpp>
|
#include <settings/CodeCompletionSettings.hpp>
|
||||||
|
|
||||||
namespace QodeAssist::Context {
|
namespace QodeAssist::Context {
|
||||||
@@ -58,7 +58,7 @@ public:
|
|||||||
|
|
||||||
CopyrightInfo copyrightInfo() const;
|
CopyrightInfo copyrightInfo() const;
|
||||||
|
|
||||||
PluginLLMCore::ContextData prepareContext(
|
Templates::ContextData prepareContext(
|
||||||
int lineNumber, int cursorPosition, const Settings::CodeCompletionSettings &settings) const;
|
int lineNumber, int cursorPosition, const Settings::CodeCompletionSettings &settings) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -43,18 +43,26 @@ exactly like the curl body.
|
|||||||
|
|
||||||
No runtime toggles: thinking / tools / streaming are **fixed per agent**. A thinking
|
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
|
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
|
file. There is no `{% if thinking %}` in the body. `system` uses
|
||||||
rendering. `system` uses `{% if existsIn(ctx, "system_prompt") %}` only because that
|
`{% if existsIn(ctx, "system_prompt") %}` only because that is about *presence of
|
||||||
is about *presence of data*, not a mode toggle.
|
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:
|
Outside the body:
|
||||||
- `model` — supplied by the **client** from its own settings; never in the profile.
|
- `model` — the TOML `model` is the **default**; a per-agent override chosen in
|
||||||
Google embeds the model in the URL, so its `endpoint` uses a `${MODEL}` placeholder
|
QodeAssist settings wins. Overrides are stored in `agent_models.json`
|
||||||
the client resolves (same substitution style as `${PROJECT_DIR}` / `${HOME}`).
|
(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
|
- `tools` — injected by the **provider** when `enable_tools` is set (tool
|
||||||
definitions are dynamic, from `ToolsManager`; they can't be authored in TOML).
|
definitions are dynamic, from `ToolsManager`; they can't be authored in TOML).
|
||||||
- `stream` — always on. Literal `"stream": true` in the body for OpenAI / Claude /
|
- `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
|
### 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
|
### 3. `extends` shares config down a hierarchy
|
||||||
|
|
||||||
`extends` already exists (`resolveExtends` + `deepMerge` + `abstract`/`hidden`); it
|
`extends` already exists (`resolveExtends` + `deepMerge` + `abstract`/`hidden`); it
|
||||||
keeps doing what it does, now over the structured `[body]` too. Typical 2–3 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) → system_prompt + provider/endpoint/enable_tools + [body]
|
||||||
├─ openai_base (abstract) → provider/endpoint/enable_tools + [body]
|
├─ openai_chat → name
|
||||||
│ ├─ openai_chat → name
|
├─ mistral_chat → name, provider, endpoint
|
||||||
│ ├─ mistral_chat → name, provider, endpoint
|
└─ mistral_reasoning → + enable_thinking
|
||||||
│ └─ mistral_reasoning → + [body].reasoning_effort
|
anthropic_base (abstract) → system_prompt + provider/endpoint + [body]
|
||||||
├─ anthropic_base (abstract) → provider/endpoint/thinking + [body]
|
├─ claude_chat → name
|
||||||
│ ├─ claude_chat → name
|
└─ claude_sonnet → + [body] thinking / output_config
|
||||||
│ └─ claude_sonnet → + [body.output_config].effort
|
google_base (abstract) → system_prompt + provider + [body]
|
||||||
└─ google_base (abstract) → provider/endpoint + [body]
|
└─ gemini_chat → endpoint (${MODEL}) + [body.generationConfig] thinkingConfig
|
||||||
└─ gemini_chat → name
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
@@ -98,21 +107,33 @@ Notes:
|
|||||||
by `model` become one agent (the client picks the model). A separate file is only
|
by `model` become one agent (the client picks the model). A separate file is only
|
||||||
needed when the body genuinely differs (effort, no-thinking, …).
|
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
|
The old `role` (static text) and `context` (jinja) layers collapse into one
|
||||||
same system prompt (`SystemPromptBuilder` layers `agent.role` / `agent.context`).
|
`agent.system` layer in `Session`, rendered through `ContextRenderer`. The agent's
|
||||||
Merge into one `system_prompt` field, always rendered through `ContextRenderer`
|
`system_prompt` field IS that template, and the user edits it (in the profile) to
|
||||||
(static text passes through; dynamic parts use `{% %}`), e.g. README via
|
compose the prompt from building-block callbacks:
|
||||||
`file_exists` instead of the `set readme / if length` dance. `Session` collapses the
|
|
||||||
two layers into one rendered layer.
|
- `{{ 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
|
## Worked examples
|
||||||
|
|
||||||
OpenAI base:
|
OpenAI base:
|
||||||
```toml
|
```toml
|
||||||
extends = "Chat Base"
|
|
||||||
abstract = true
|
abstract = true
|
||||||
|
system_prompt = """{{ agent_role("developer") }}"""
|
||||||
provider_instance = "OpenAI (Chat Completions)"
|
provider_instance = "OpenAI (Chat Completions)"
|
||||||
endpoint = "/chat/completions"
|
endpoint = "/chat/completions"
|
||||||
enable_tools = true
|
enable_tools = true
|
||||||
@@ -140,8 +161,8 @@ reasoning_effort = "medium"
|
|||||||
|
|
||||||
Claude base (literally the curl body):
|
Claude base (literally the curl body):
|
||||||
```toml
|
```toml
|
||||||
extends = "Chat Base"
|
|
||||||
abstract = true
|
abstract = true
|
||||||
|
system_prompt = """{{ agent_role("developer") }}"""
|
||||||
provider_instance = "Claude"
|
provider_instance = "Claude"
|
||||||
endpoint = "/v1/messages"
|
endpoint = "/v1/messages"
|
||||||
enable_thinking = true
|
enable_thinking = true
|
||||||
@@ -170,8 +191,8 @@ effort = "medium"
|
|||||||
|
|
||||||
Google base (`${MODEL}` in endpoint; streaming in the URL):
|
Google base (`${MODEL}` in endpoint; streaming in the URL):
|
||||||
```toml
|
```toml
|
||||||
extends = "Chat Base"
|
|
||||||
abstract = true
|
abstract = true
|
||||||
|
system_prompt = """{{ agent_role("developer") }}"""
|
||||||
provider_instance = "Google AI"
|
provider_instance = "Google AI"
|
||||||
endpoint = "/models/${MODEL}:streamGenerateContent?alt=sse"
|
endpoint = "/models/${MODEL}:streamGenerateContent?alt=sse"
|
||||||
enable_thinking = true
|
enable_thinking = true
|
||||||
@@ -337,10 +358,16 @@ In `AgentConfig` / `AgentLoader`:
|
|||||||
as JSON (render once against a synthetic context with tool_use / tool_result /
|
as JSON (render once against a synthetic context with tool_use / tool_result /
|
||||||
image). Catches breakage at startup, not mid-conversation.
|
image). Catches breakage at startup, not mid-conversation.
|
||||||
|
|
||||||
In the client / provider layer:
|
Model selection (per-agent override):
|
||||||
- The client sets `model` from its settings (and resolves `${MODEL}` in the
|
- `AgentFactory` owns an agentName → model map loaded from `agent_models.json`
|
||||||
endpoint); `Session` no longer seeds the payload with `cfg.model`.
|
(`loadModelOverrides`/`saveModelOverrides`). `create()`/`createFromFile()` apply the
|
||||||
- The provider keeps injecting `tools` when `enable_tools` is set.
|
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`:
|
In `Session`:
|
||||||
- Collapse the `agent.role` + `agent.context` system-prompt layers into one rendered
|
- 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
|
2. `[body]`-table model in `JsonPromptTemplate` + loader; delete
|
||||||
sampling/thinking/`applySampling`; drop `thinkingEnabled`.
|
sampling/thinking/`applySampling`; drop `thinkingEnabled`.
|
||||||
3. `system_prompt` merge in loader + `Session`.
|
3. `system_prompt` merge in loader + `Session`.
|
||||||
4. `model` from client (+ `${MODEL}` endpoint substitution); convert bundled agents
|
4. Per-agent model override in `AgentFactory` (`agent_models.json`) + `${MODEL}`
|
||||||
to the base/partials/`extends` layout.
|
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).
|
5. Load-time validation (partial resolves, body parses).
|
||||||
|
|||||||
@@ -15,9 +15,8 @@
|
|||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <LLMQore/ToolsManager.hpp>
|
||||||
#include <logger/Logger.hpp>
|
#include <logger/Logger.hpp>
|
||||||
#include <pluginllmcore/Provider.hpp>
|
|
||||||
#include <pluginllmcore/ProvidersManager.hpp>
|
|
||||||
#include <settings/McpSettings.hpp>
|
#include <settings/McpSettings.hpp>
|
||||||
|
|
||||||
namespace QodeAssist::Mcp {
|
namespace QodeAssist::Mcp {
|
||||||
@@ -176,18 +175,14 @@ QList<McpServerConnection *> McpClientsManager::connections() const
|
|||||||
return m_connections;
|
return m_connections;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<PluginLLMCore::Provider *> McpClientsManager::toolsCapableProviders() const
|
void McpClientsManager::registerToolsOn(::LLMQore::ToolsManager *tools) const
|
||||||
{
|
{
|
||||||
QList<PluginLLMCore::Provider *> out;
|
if (!tools)
|
||||||
auto &pm = PluginLLMCore::ProvidersManager::instance();
|
return;
|
||||||
for (const QString &name : pm.providersNames()) {
|
for (auto *c : m_connections) {
|
||||||
auto *p = pm.getProviderByName(name);
|
if (c)
|
||||||
if (!p)
|
c->registerToolsOn(tools);
|
||||||
continue;
|
|
||||||
if (p->capabilities().testFlag(PluginLLMCore::ProviderCapability::Tools))
|
|
||||||
out.append(p);
|
|
||||||
}
|
}
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject McpClientsManager::builtinServers()
|
QJsonObject McpClientsManager::builtinServers()
|
||||||
@@ -319,8 +314,6 @@ void McpClientsManager::loadFromDisk()
|
|||||||
newConfigs.append(McpServerConfig::fromJson(it.key(), it.value().toObject()));
|
newConfigs.append(McpServerConfig::fromJson(it.key(), it.value().toObject()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto providers = toolsCapableProviders();
|
|
||||||
|
|
||||||
const bool masterEnabled = Settings::mcpSettings().enableMcpClients();
|
const bool masterEnabled = Settings::mcpSettings().enableMcpClients();
|
||||||
|
|
||||||
QList<McpServerConnection *> keep;
|
QList<McpServerConnection *> keep;
|
||||||
@@ -350,7 +343,6 @@ void McpClientsManager::loadFromDisk()
|
|||||||
existing->deleteLater();
|
existing->deleteLater();
|
||||||
}
|
}
|
||||||
c = new McpServerConnection(cfg, this);
|
c = new McpServerConnection(cfg, this);
|
||||||
c->setProviders(providers);
|
|
||||||
connect(
|
connect(
|
||||||
c,
|
c,
|
||||||
&McpServerConnection::stateChanged,
|
&McpServerConnection::stateChanged,
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public:
|
|||||||
bool removeServer(const QString &name);
|
bool removeServer(const QString &name);
|
||||||
void reload();
|
void reload();
|
||||||
|
|
||||||
|
void registerToolsOn(::LLMQore::ToolsManager *tools) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void serversChanged();
|
void serversChanged();
|
||||||
void writeFailed(const QString &reason);
|
void writeFailed(const QString &reason);
|
||||||
@@ -50,7 +52,6 @@ private:
|
|||||||
void setupWatcher();
|
void setupWatcher();
|
||||||
void updateWatchedPaths();
|
void updateWatchedPaths();
|
||||||
|
|
||||||
QList<PluginLLMCore::Provider *> toolsCapableProviders() const;
|
|
||||||
static QJsonObject builtinServers();
|
static QJsonObject builtinServers();
|
||||||
QJsonObject readRoot() const;
|
QJsonObject readRoot() const;
|
||||||
bool writeRoot(const QJsonObject &root);
|
bool writeRoot(const QJsonObject &root);
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#include <logger/Logger.hpp>
|
#include <logger/Logger.hpp>
|
||||||
#include <pluginllmcore/Provider.hpp>
|
|
||||||
#include <settings/McpSettings.hpp>
|
#include <settings/McpSettings.hpp>
|
||||||
|
|
||||||
namespace QodeAssist::Mcp {
|
namespace QodeAssist::Mcp {
|
||||||
@@ -35,13 +34,6 @@ QString transportToString(McpTransportKind k)
|
|||||||
return k == McpTransportKind::Http ? QStringLiteral("http") : QStringLiteral("stdio");
|
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
|
} // namespace
|
||||||
|
|
||||||
McpServerConfig McpServerConfig::fromJson(const QString &name, const QJsonObject &obj)
|
McpServerConfig McpServerConfig::fromJson(const QString &name, const QJsonObject &obj)
|
||||||
@@ -133,15 +125,6 @@ McpServerConnection::~McpServerConnection()
|
|||||||
disconnectFromServer();
|
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()
|
::LLMQore::Mcp::McpTransport *McpServerConnection::createTransport()
|
||||||
{
|
{
|
||||||
if (m_config.transport == McpTransportKind::Http) {
|
if (m_config.transport == McpTransportKind::Http) {
|
||||||
@@ -293,40 +276,20 @@ void McpServerConnection::fetchAndRegisterTools()
|
|||||||
[this](const QList<::LLMQore::Mcp::ToolInfo> &tools) {
|
[this](const QList<::LLMQore::Mcp::ToolInfo> &tools) {
|
||||||
if (m_listToolsWatchdog)
|
if (m_listToolsWatchdog)
|
||||||
m_listToolsWatchdog->stop();
|
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) {
|
for (const auto &info : tools) {
|
||||||
if (info.name.isEmpty())
|
if (info.name.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
m_toolIds.append(info.name);
|
m_tools.append(info);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(m_config.name)
|
||||||
.arg(tools.size())
|
.arg(m_tools.size()));
|
||||||
.arg(m_providers.size()));
|
|
||||||
setState(
|
setState(
|
||||||
McpConnectionState::Connected,
|
McpConnectionState::Connected,
|
||||||
QStringLiteral("Connected (%1 tools)").arg(tools.size()));
|
QStringLiteral("Connected (%1 tools)").arg(m_tools.size()));
|
||||||
})
|
})
|
||||||
.onFailed(this, [this](const std::exception &e) {
|
.onFailed(this, [this](const std::exception &e) {
|
||||||
if (m_listToolsWatchdog)
|
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()
|
void McpServerConnection::unregisterTools()
|
||||||
{
|
{
|
||||||
if (m_toolIds.isEmpty())
|
m_tools.clear();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void McpServerConnection::disconnectFromServer()
|
void McpServerConnection::disconnectFromServer()
|
||||||
|
|||||||
@@ -14,15 +14,17 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include <LLMQore/McpTypes.hpp>
|
||||||
|
|
||||||
|
namespace LLMQore {
|
||||||
|
class ToolsManager;
|
||||||
|
}
|
||||||
|
|
||||||
namespace LLMQore::Mcp {
|
namespace LLMQore::Mcp {
|
||||||
class McpClient;
|
class McpClient;
|
||||||
class McpTransport;
|
class McpTransport;
|
||||||
} // namespace LLMQore::Mcp
|
} // namespace LLMQore::Mcp
|
||||||
|
|
||||||
namespace QodeAssist::PluginLLMCore {
|
|
||||||
class Provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace QodeAssist::Mcp {
|
namespace QodeAssist::Mcp {
|
||||||
|
|
||||||
enum class McpTransportKind { Http, Stdio };
|
enum class McpTransportKind { Http, Stdio };
|
||||||
@@ -61,10 +63,17 @@ public:
|
|||||||
const McpServerConfig &config() const { return m_config; }
|
const McpServerConfig &config() const { return m_config; }
|
||||||
McpConnectionState state() const { return m_state; }
|
McpConnectionState state() const { return m_state; }
|
||||||
QString statusText() const { return m_statusText; }
|
QString statusText() const { return m_statusText; }
|
||||||
int toolCount() const { return m_toolIds.size(); }
|
int toolCount() const { return m_tools.size(); }
|
||||||
QStringList toolNames() const { return m_toolIds; }
|
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 connectToServer();
|
||||||
void disconnectFromServer();
|
void disconnectFromServer();
|
||||||
@@ -75,7 +84,6 @@ signals:
|
|||||||
private:
|
private:
|
||||||
void setState(McpConnectionState state, const QString &text = {});
|
void setState(McpConnectionState state, const QString &text = {});
|
||||||
void fetchAndRegisterTools();
|
void fetchAndRegisterTools();
|
||||||
void registerTools(const QList<::LLMQore::Mcp::McpClient *> & /*unused*/);
|
|
||||||
void unregisterTools();
|
void unregisterTools();
|
||||||
::LLMQore::Mcp::McpTransport *createTransport();
|
::LLMQore::Mcp::McpTransport *createTransport();
|
||||||
|
|
||||||
@@ -87,8 +95,7 @@ private:
|
|||||||
QPointer<::LLMQore::Mcp::McpTransport> m_transport;
|
QPointer<::LLMQore::Mcp::McpTransport> m_transport;
|
||||||
QPointer<QTimer> m_listToolsWatchdog;
|
QPointer<QTimer> m_listToolsWatchdog;
|
||||||
|
|
||||||
QList<QPointer<PluginLLMCore::Provider>> m_providers;
|
QList<::LLMQore::Mcp::ToolInfo> m_tools;
|
||||||
QStringList m_toolIds;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Mcp
|
} // namespace QodeAssist::Mcp
|
||||||
|
|||||||
@@ -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})
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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 };
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -42,13 +42,12 @@
|
|||||||
#include "chat/ChatOutputPane.h"
|
#include "chat/ChatOutputPane.h"
|
||||||
#include "chat/NavigationPanel.hpp"
|
#include "chat/NavigationPanel.hpp"
|
||||||
#include "context/DocumentReaderQtCreator.hpp"
|
#include "context/DocumentReaderQtCreator.hpp"
|
||||||
#include "pluginllmcore/ProvidersManager.hpp"
|
|
||||||
#include "logger/RequestPerformanceLogger.hpp"
|
#include "logger/RequestPerformanceLogger.hpp"
|
||||||
#include "mcp/McpClientsManager.hpp"
|
#include "mcp/McpClientsManager.hpp"
|
||||||
#include "mcp/McpServerManager.hpp"
|
#include "mcp/McpServerManager.hpp"
|
||||||
#include "sources/skills/SkillsManager.hpp"
|
#include "sources/skills/SkillsManager.hpp"
|
||||||
#include "tools/ToolsRegistration.hpp"
|
#include "tools/ToolsRegistration.hpp"
|
||||||
#include "providers/Providers.hpp"
|
#include "settings/AgentRole.hpp"
|
||||||
#include "settings/ChatAssistantSettings.hpp"
|
#include "settings/ChatAssistantSettings.hpp"
|
||||||
#include "settings/GeneralSettings.hpp"
|
#include "settings/GeneralSettings.hpp"
|
||||||
#include "settings/ProjectSettingsPanel.hpp"
|
#include "settings/ProjectSettingsPanel.hpp"
|
||||||
@@ -64,7 +63,6 @@
|
|||||||
#include <AgentFactory.hpp>
|
#include <AgentFactory.hpp>
|
||||||
#include <GenericProvider.hpp>
|
#include <GenericProvider.hpp>
|
||||||
#include <SessionManager.hpp>
|
#include <SessionManager.hpp>
|
||||||
#include "templates/Templates.hpp"
|
|
||||||
#include "widgets/CustomInstructionsManager.hpp"
|
#include "widgets/CustomInstructionsManager.hpp"
|
||||||
#include "widgets/QuickRefactorDialog.hpp"
|
#include "widgets/QuickRefactorDialog.hpp"
|
||||||
#include <ChatView/ChatView.hpp>
|
#include <ChatView/ChatView.hpp>
|
||||||
@@ -141,9 +139,6 @@ public:
|
|||||||
|
|
||||||
loadTranslations();
|
loadTranslations();
|
||||||
|
|
||||||
Providers::registerProviders();
|
|
||||||
Templates::registerTemplates();
|
|
||||||
|
|
||||||
CustomInstructionsManager::instance().loadInstructions();
|
CustomInstructionsManager::instance().loadInstructions();
|
||||||
|
|
||||||
Utils::Icon QCODEASSIST_ICON(
|
Utils::Icon QCODEASSIST_ICON(
|
||||||
@@ -180,16 +175,6 @@ public:
|
|||||||
m_sessionFileRegistry = new Chat::SessionFileRegistry{this};
|
m_sessionFileRegistry = new Chat::SessionFileRegistry{this};
|
||||||
m_skillsManager = new Skills::SkillsManager{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()) {
|
if (Settings::chatAssistantSettings().enableChatInBottomToolBar()) {
|
||||||
m_chatOutputPane = new Chat::ChatOutputPane{
|
m_chatOutputPane = new Chat::ChatOutputPane{
|
||||||
m_engine, m_sessionFileRegistry, m_skillsManager};
|
m_engine, m_sessionFileRegistry, m_skillsManager};
|
||||||
@@ -214,8 +199,26 @@ public:
|
|||||||
m_providerLauncher,
|
m_providerLauncher,
|
||||||
m_providersPageNavigator);
|
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_agentFactory = new AgentFactory(m_providerInstanceFactory, m_providerSecretsStore, this);
|
||||||
m_sessionManager = new SessionManager(m_agentFactory, 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("agentFactory", m_agentFactory);
|
||||||
m_engine->rootContext()->setContextProperty("sessionManager", m_sessionManager);
|
m_engine->rootContext()->setContextProperty("sessionManager", m_sessionManager);
|
||||||
m_agentsPageNavigator = new Settings::AgentsPageNavigator(this);
|
m_agentsPageNavigator = new Settings::AgentsPageNavigator(this);
|
||||||
|
|||||||
@@ -171,7 +171,10 @@ void AgentListPane::rebuildList()
|
|||||||
const QSet<QString> &activeTags = m_tagStrip->activeTags();
|
const QSet<QString> &activeTags = m_tagStrip->activeTags();
|
||||||
auto addAgents = [&](const std::vector<const AgentConfig *> &agents) {
|
auto addAgents = [&](const std::vector<const AgentConfig *> &agents) {
|
||||||
for (const AgentConfig *cfg : 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->setSelected(cfg->name == m_currentName);
|
||||||
item->setActiveTags(activeTags);
|
item->setActiveTags(activeTags);
|
||||||
connect(item, &AgentListItem::clicked, this, &AgentListPane::onRowClicked);
|
connect(item, &AgentListItem::clicked, this, &AgentListPane::onRowClicked);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ add_library(Session STATIC
|
|||||||
Session.hpp Session.cpp
|
Session.hpp Session.cpp
|
||||||
SessionManager.hpp SessionManager.cpp
|
SessionManager.hpp SessionManager.cpp
|
||||||
SystemPromptBuilder.hpp SystemPromptBuilder.cpp
|
SystemPromptBuilder.hpp SystemPromptBuilder.cpp
|
||||||
|
ToolContributorRegistry.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(Session
|
target_link_libraries(Session
|
||||||
|
|||||||
@@ -210,7 +210,9 @@ LLMQore::RequestID Session::sendCompletion(Templates::ContextData ctx)
|
|||||||
if (!provider->prepareRequest(payload, tmpl, ctx, /*tools=*/false, /*thinking=*/false))
|
if (!provider->prepareRequest(payload, tmpl, ctx, /*tools=*/false, /*thinking=*/false))
|
||||||
return {};
|
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())
|
if (id.isEmpty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@@ -242,7 +244,9 @@ LLMQore::RequestID Session::dispatch(
|
|||||||
if (!provider->prepareRequest(payload, tmpl, ctx, tools, thinking))
|
if (!provider->prepareRequest(payload, tmpl, ctx, tools, thinking))
|
||||||
return {};
|
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())
|
if (id.isEmpty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "ToolContributorRegistry.hpp"
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
class AgentFactory;
|
class AgentFactory;
|
||||||
@@ -41,6 +43,9 @@ public:
|
|||||||
|
|
||||||
void cancelAll();
|
void cancelAll();
|
||||||
|
|
||||||
|
ToolContributorRegistry &toolContributors() noexcept { return m_toolContributors; }
|
||||||
|
const ToolContributorRegistry &toolContributors() const noexcept { return m_toolContributors; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sessionCreated(Session *session);
|
void sessionCreated(Session *session);
|
||||||
void sessionRemoved(Session *session);
|
void sessionRemoved(Session *session);
|
||||||
@@ -48,6 +53,7 @@ signals:
|
|||||||
private:
|
private:
|
||||||
QPointer<AgentFactory> m_agentFactory;
|
QPointer<AgentFactory> m_agentFactory;
|
||||||
QList<QPointer<Session>> m_sessions;
|
QList<QPointer<Session>> m_sessions;
|
||||||
|
ToolContributorRegistry m_toolContributors;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
41
sources/Session/ToolContributorRegistry.hpp
Normal file
41
sources/Session/ToolContributorRegistry.hpp
Normal 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
|
||||||
@@ -248,6 +248,15 @@ void AgentFactory::setModelOverride(const QString &agentName, const QString &mod
|
|||||||
saveModelOverrides();
|
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 {
|
namespace {
|
||||||
QString modelOverridesPath()
|
QString modelOverridesPath()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ public:
|
|||||||
[[nodiscard]] QString modelOverride(const QString &agentName) const;
|
[[nodiscard]] QString modelOverride(const QString &agentName) const;
|
||||||
void setModelOverride(const QString &agentName, const QString &model);
|
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::ProviderInstanceFactory *instanceFactory() const noexcept;
|
||||||
[[nodiscard]] Providers::ProviderSecretsStore *secretsStore() const noexcept;
|
[[nodiscard]] Providers::ProviderSecretsStore *secretsStore() const noexcept;
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,12 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#include <inja/inja.hpp>
|
#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)
|
void registerSandbox(inja::Environment &env)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -183,6 +226,7 @@ QString render(const QString &templateSource, const Bindings &bindings, QString
|
|||||||
registerFileExists(env, bindings);
|
registerFileExists(env, bindings);
|
||||||
registerReadDir(env, bindings);
|
registerReadDir(env, bindings);
|
||||||
registerStringHelpers(env);
|
registerStringHelpers(env);
|
||||||
|
registerAgentRole(env, bindings);
|
||||||
|
|
||||||
inja::Template tpl;
|
inja::Template tpl;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ struct Bindings
|
|||||||
{
|
{
|
||||||
QString projectDir;
|
QString projectDir;
|
||||||
QString homeDir;
|
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,
|
QString render(const QString &templateSource, const Bindings &bindings,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
<file>partials/google_part.jinja</file>
|
<file>partials/google_part.jinja</file>
|
||||||
<file>partials/ollama_messages.jinja</file>
|
<file>partials/ollama_messages.jinja</file>
|
||||||
|
|
||||||
<file>chat_base.toml</file>
|
|
||||||
<file>openai_base_chat.toml</file>
|
<file>openai_base_chat.toml</file>
|
||||||
<file>openai_responses_base.toml</file>
|
<file>openai_responses_base.toml</file>
|
||||||
<file>anthropic_base_chat.toml</file>
|
<file>anthropic_base_chat.toml</file>
|
||||||
|
|||||||
@@ -3,16 +3,18 @@ schema_version = 1
|
|||||||
name = "Anthropic Base Chat"
|
name = "Anthropic Base Chat"
|
||||||
description = "Anthropic Messages API request body (/v1/messages). Abstract — extend it and set model."
|
description = "Anthropic Messages API request body (/v1/messages). Abstract — extend it and set model."
|
||||||
abstract = true
|
abstract = true
|
||||||
extends = "Chat Base"
|
|
||||||
|
|
||||||
provider_instance = "Claude"
|
provider_instance = "Claude"
|
||||||
endpoint = "/v1/messages"
|
endpoint = "/v1/messages"
|
||||||
enable_tools = true
|
enable_tools = true
|
||||||
tags = ["chat", "claude", "anthropic", "cloud"]
|
tags = ["chat", "claude", "anthropic", "cloud"]
|
||||||
|
|
||||||
|
system_prompt = """{{ agent_role() }}"""
|
||||||
|
|
||||||
[body]
|
[body]
|
||||||
max_tokens = 8192
|
max_tokens = 8192
|
||||||
temperature = 1
|
temperature = 1
|
||||||
|
stream = true
|
||||||
system = """{% if existsIn(ctx, "system_prompt") %}{{ tojson(ctx.system_prompt) }}{% endif %}"""
|
system = """{% if existsIn(ctx, "system_prompt") %}{{ tojson(ctx.system_prompt) }}{% endif %}"""
|
||||||
messages = """
|
messages = """
|
||||||
[ {% include "partials/anthropic_messages.jinja" %} ]
|
[ {% include "partials/anthropic_messages.jinja" %} ]
|
||||||
|
|||||||
@@ -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 %}
|
|
||||||
"""
|
|
||||||
@@ -3,12 +3,13 @@ schema_version = 1
|
|||||||
name = "Google Base Chat"
|
name = "Google Base Chat"
|
||||||
description = "Google Gemini generateContent request body. Abstract — extend it and set model/endpoint."
|
description = "Google Gemini generateContent request body. Abstract — extend it and set model/endpoint."
|
||||||
abstract = true
|
abstract = true
|
||||||
extends = "Chat Base"
|
|
||||||
|
|
||||||
provider_instance = "Google AI"
|
provider_instance = "Google AI"
|
||||||
enable_tools = true
|
enable_tools = true
|
||||||
tags = ["chat", "gemini", "google", "cloud"]
|
tags = ["chat", "gemini", "google", "cloud"]
|
||||||
|
|
||||||
|
system_prompt = """{{ agent_role() }}"""
|
||||||
|
|
||||||
[body]
|
[body]
|
||||||
system_instruction = """{% if existsIn(ctx, "system_prompt") %}{ "parts": [ { "text": {{ tojson(ctx.system_prompt) }} } ] }{% endif %}"""
|
system_instruction = """{% if existsIn(ctx, "system_prompt") %}{ "parts": [ { "text": {{ tojson(ctx.system_prompt) }} } ] }{% endif %}"""
|
||||||
contents = """
|
contents = """
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ extends = "Google Base Chat"
|
|||||||
name = "Gemini Chat"
|
name = "Gemini Chat"
|
||||||
description = "Google Gemini 2.5 Flash (generateContent API) — coding chat with thinking."
|
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"
|
model = "gemini-2.5-flash"
|
||||||
|
|
||||||
enable_thinking = true
|
enable_thinking = true
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ schema_version = 1
|
|||||||
name = "Ollama Base Chat"
|
name = "Ollama Base Chat"
|
||||||
description = "Ollama native /api/chat request body. Abstract — extend it and set model/options."
|
description = "Ollama native /api/chat request body. Abstract — extend it and set model/options."
|
||||||
abstract = true
|
abstract = true
|
||||||
extends = "Chat Base"
|
|
||||||
|
|
||||||
provider_instance = "Ollama (Native)"
|
provider_instance = "Ollama (Native)"
|
||||||
endpoint = "/api/chat"
|
endpoint = "/api/chat"
|
||||||
tags = ["ollama", "local"]
|
tags = ["ollama", "local"]
|
||||||
|
|
||||||
|
system_prompt = """{{ agent_role() }}"""
|
||||||
|
|
||||||
[body]
|
[body]
|
||||||
stream = true
|
stream = true
|
||||||
messages = """
|
messages = """
|
||||||
|
|||||||
@@ -3,16 +3,18 @@ schema_version = 1
|
|||||||
name = "OpenAI Base Chat"
|
name = "OpenAI Base Chat"
|
||||||
description = "OpenAI Chat Completions request body. Abstract — extend it and set provider/endpoint/model."
|
description = "OpenAI Chat Completions request body. Abstract — extend it and set provider/endpoint/model."
|
||||||
abstract = true
|
abstract = true
|
||||||
extends = "Chat Base"
|
|
||||||
|
|
||||||
provider_instance = "OpenAI (Chat Completions)"
|
provider_instance = "OpenAI (Chat Completions)"
|
||||||
endpoint = "/chat/completions"
|
endpoint = "/chat/completions"
|
||||||
enable_tools = true
|
enable_tools = true
|
||||||
tags = ["chat", "openai", "cloud"]
|
tags = ["chat", "openai", "cloud"]
|
||||||
|
|
||||||
|
system_prompt = """{{ agent_role() }}"""
|
||||||
|
|
||||||
[body]
|
[body]
|
||||||
max_tokens = 8192
|
max_tokens = 8192
|
||||||
temperature = 0.7
|
temperature = 0.7
|
||||||
|
stream = true
|
||||||
messages = """
|
messages = """
|
||||||
[ {% include "partials/openai_messages.jinja" %} ]
|
[ {% include "partials/openai_messages.jinja" %} ]
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -3,16 +3,18 @@ schema_version = 1
|
|||||||
name = "OpenAI Responses Base"
|
name = "OpenAI Responses Base"
|
||||||
description = "OpenAI Responses API request body (/responses). Abstract — extend it and set provider/endpoint/model."
|
description = "OpenAI Responses API request body (/responses). Abstract — extend it and set provider/endpoint/model."
|
||||||
abstract = true
|
abstract = true
|
||||||
extends = "Chat Base"
|
|
||||||
|
|
||||||
provider_instance = "OpenAI (Responses API)"
|
provider_instance = "OpenAI (Responses API)"
|
||||||
endpoint = "/responses"
|
endpoint = "/responses"
|
||||||
enable_tools = true
|
enable_tools = true
|
||||||
tags = ["chat", "openai", "responses", "cloud"]
|
tags = ["chat", "openai", "responses", "cloud"]
|
||||||
|
|
||||||
|
system_prompt = """{{ agent_role() }}"""
|
||||||
|
|
||||||
[body]
|
[body]
|
||||||
max_output_tokens = 8192
|
max_output_tokens = 8192
|
||||||
temperature = 0.7
|
temperature = 0.7
|
||||||
|
stream = true
|
||||||
instructions = """{% if existsIn(ctx, "system_prompt") %}{{ tojson(ctx.system_prompt) }}{% endif %}"""
|
instructions = """{% if existsIn(ctx, "system_prompt") %}{{ tojson(ctx.system_prompt) }}{% endif %}"""
|
||||||
input = """
|
input = """
|
||||||
[ {% include "partials/openai_responses_input.jinja" %} ]
|
[ {% include "partials/openai_responses_input.jinja" %} ]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ add_library(Common INTERFACE)
|
|||||||
|
|
||||||
target_sources(Common INTERFACE
|
target_sources(Common INTERFACE
|
||||||
ContextData.hpp
|
ContextData.hpp
|
||||||
|
ResponseCleaner.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(Common INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(Common INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
namespace QodeAssist::PluginLLMCore {
|
namespace QodeAssist {
|
||||||
|
|
||||||
class ResponseCleaner
|
class ResponseCleaner
|
||||||
{
|
{
|
||||||
@@ -100,5 +100,4 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::PluginLLMCore
|
} // namespace QodeAssist
|
||||||
|
|
||||||
@@ -199,6 +199,7 @@ public:
|
|||||||
AgentRosterRow(int index,
|
AgentRosterRow(int index,
|
||||||
const QString &name,
|
const QString &name,
|
||||||
const AgentConfig *cfg,
|
const AgentConfig *cfg,
|
||||||
|
const QString &effectiveModel,
|
||||||
bool active,
|
bool active,
|
||||||
bool first,
|
bool first,
|
||||||
bool last,
|
bool last,
|
||||||
@@ -226,6 +227,7 @@ private:
|
|||||||
AgentRosterRow::AgentRosterRow(int index,
|
AgentRosterRow::AgentRosterRow(int index,
|
||||||
const QString &name,
|
const QString &name,
|
||||||
const AgentConfig *cfg,
|
const AgentConfig *cfg,
|
||||||
|
const QString &effectiveModel,
|
||||||
bool active,
|
bool active,
|
||||||
bool first,
|
bool first,
|
||||||
bool last,
|
bool last,
|
||||||
@@ -282,7 +284,7 @@ AgentRosterRow::AgentRosterRow(int index,
|
|||||||
body->setSpacing(2);
|
body->setSpacing(2);
|
||||||
|
|
||||||
const QString displayName = cfg ? cfg->name : tr("%1 (missing)").arg(name);
|
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();
|
const bool isUser = cfg && cfg->isUserSource();
|
||||||
|
|
||||||
body->addWidget(buildIdentityLine(displayName, model, active, isUser, theme));
|
body->addWidget(buildIdentityLine(displayName, model, active, isUser, theme));
|
||||||
@@ -562,9 +564,11 @@ void AgentRosterWidget::rebuildRows()
|
|||||||
for (int i = 0; i < m_names.size(); ++i) {
|
for (int i = 0; i < m_names.size(); ++i) {
|
||||||
const QString &name = m_names.at(i);
|
const QString &name = m_names.at(i);
|
||||||
const AgentConfig *cfg = m_factory ? m_factory->configByName(name) : nullptr;
|
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,
|
auto *row = new AgentRosterRow(i,
|
||||||
name,
|
name,
|
||||||
cfg,
|
cfg,
|
||||||
|
effModel,
|
||||||
i == m_activeIndex,
|
i == m_activeIndex,
|
||||||
/*first*/ i == 0,
|
/*first*/ i == 0,
|
||||||
/*last*/ i == m_names.size() - 1,
|
/*last*/ i == m_names.size() - 1,
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
Reference in New Issue
Block a user