mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-06-14 02:09:22 -04:00
fix: Code completion via session
This commit is contained in:
@@ -13,12 +13,12 @@
|
|||||||
#include <projectexplorer/projectmanager.h>
|
#include <projectexplorer/projectmanager.h>
|
||||||
#include <utils/filepath.h>
|
#include <utils/filepath.h>
|
||||||
|
|
||||||
|
#include <Agent.hpp>
|
||||||
#include <AgentConfig.hpp>
|
#include <AgentConfig.hpp>
|
||||||
#include <AgentFactory.hpp>
|
#include <AgentFactory.hpp>
|
||||||
#include <AgentRouter.hpp>
|
#include <AgentRouter.hpp>
|
||||||
#include <ResponseEvent.hpp>
|
#include <ConversationHistory.hpp>
|
||||||
#include <Session.hpp>
|
#include <Session.hpp>
|
||||||
#include <SessionManager.hpp>
|
|
||||||
#include "sources/common/ContextData.hpp"
|
#include "sources/common/ContextData.hpp"
|
||||||
|
|
||||||
#include "CodeHandler.hpp"
|
#include "CodeHandler.hpp"
|
||||||
@@ -34,13 +34,11 @@ namespace QodeAssist {
|
|||||||
LLMClientInterface::LLMClientInterface(
|
LLMClientInterface::LLMClientInterface(
|
||||||
const Settings::GeneralSettings &generalSettings,
|
const Settings::GeneralSettings &generalSettings,
|
||||||
const Settings::CodeCompletionSettings &completeSettings,
|
const Settings::CodeCompletionSettings &completeSettings,
|
||||||
SessionManager &sessionManager,
|
|
||||||
AgentFactory &agentFactory,
|
AgentFactory &agentFactory,
|
||||||
Context::IDocumentReader &documentReader,
|
Context::IDocumentReader &documentReader,
|
||||||
IRequestPerformanceLogger &performanceLogger)
|
IRequestPerformanceLogger &performanceLogger)
|
||||||
: m_generalSettings(generalSettings)
|
: m_generalSettings(generalSettings)
|
||||||
, m_completeSettings(completeSettings)
|
, m_completeSettings(completeSettings)
|
||||||
, m_sessionManager(sessionManager)
|
|
||||||
, m_agentFactory(agentFactory)
|
, m_agentFactory(agentFactory)
|
||||||
, m_documentReader(documentReader)
|
, m_documentReader(documentReader)
|
||||||
, m_performanceLogger(performanceLogger)
|
, m_performanceLogger(performanceLogger)
|
||||||
@@ -63,32 +61,24 @@ void LLMClientInterface::startImpl()
|
|||||||
emit started();
|
emit started();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::onSessionEvent(const QString &requestId, const ResponseEvent &event)
|
void LLMClientInterface::onCompletionFinished(const QString &requestId)
|
||||||
{
|
{
|
||||||
auto it = m_activeRequests.find(requestId);
|
auto it = m_activeRequests.find(requestId);
|
||||||
if (it == m_activeRequests.end())
|
if (it == m_activeRequests.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (event.kind() == ResponseEvent::Kind::TextDelta) {
|
QString fullText;
|
||||||
if (const auto *delta = event.as<ResponseEvents::TextDelta>())
|
if (Session *session = it.value().session) {
|
||||||
it.value().accumulated += delta->text;
|
if (auto *history = session->history(); history && !history->isEmpty())
|
||||||
|
fullText = history->messages().back().text();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void LLMClientInterface::onSessionFinished(const QString &requestId)
|
|
||||||
{
|
|
||||||
auto it = m_activeRequests.find(requestId);
|
|
||||||
if (it == m_activeRequests.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const QString fullText = it.value().accumulated;
|
|
||||||
const QJsonObject originalRequest = it.value().originalRequest;
|
const QJsonObject originalRequest = it.value().originalRequest;
|
||||||
|
|
||||||
sendCompletionToClient(fullText, originalRequest, true);
|
sendCompletionToClient(fullText, originalRequest, true);
|
||||||
finishRequest(requestId);
|
finishRequest(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::onSessionFailed(const QString &requestId, const QString &error)
|
void LLMClientInterface::onCompletionFailed(const QString &requestId, const QString &error)
|
||||||
{
|
{
|
||||||
auto it = m_activeRequests.find(requestId);
|
auto it = m_activeRequests.find(requestId);
|
||||||
if (it == m_activeRequests.end())
|
if (it == m_activeRequests.end())
|
||||||
@@ -120,7 +110,7 @@ void LLMClientInterface::finishRequest(const QString &requestId)
|
|||||||
m_performanceLogger.endTimeMeasurement(requestId);
|
m_performanceLogger.endTimeMeasurement(requestId);
|
||||||
|
|
||||||
if (session)
|
if (session)
|
||||||
m_sessionManager.removeSession(session);
|
session->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::sendData(const QByteArray &data)
|
void LLMClientInterface::sendData(const QByteArray &data)
|
||||||
@@ -158,8 +148,10 @@ void LLMClientInterface::handleCancelRequest()
|
|||||||
|
|
||||||
for (auto it = requests.begin(); it != requests.end(); ++it) {
|
for (auto it = requests.begin(); it != requests.end(); ++it) {
|
||||||
m_performanceLogger.endTimeMeasurement(it.key());
|
m_performanceLogger.endTimeMeasurement(it.key());
|
||||||
if (it.value().session)
|
if (Session *session = it.value().session) {
|
||||||
m_sessionManager.removeSession(it.value().session);
|
session->cancel();
|
||||||
|
session->deleteLater();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_MESSAGE("All requests cancelled and state cleared");
|
LOG_MESSAGE("All requests cancelled and state cleared");
|
||||||
@@ -252,11 +244,20 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString sessionError;
|
QString agentError;
|
||||||
Session *session = m_sessionManager.createSession(agentName, &sessionError);
|
Agent *agent = m_agentFactory.create(agentName, /*parent=*/nullptr, &agentError);
|
||||||
if (!session) {
|
if (!agent) {
|
||||||
LOG_MESSAGE(sessionError);
|
LOG_MESSAGE(agentError);
|
||||||
sendErrorResponse(request, sessionError);
|
sendErrorResponse(request, agentError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *session = new Session(agent, this);
|
||||||
|
if (!session->isValid()) {
|
||||||
|
const QString error = session->invalidReason();
|
||||||
|
delete session;
|
||||||
|
LOG_MESSAGE(error);
|
||||||
|
sendErrorResponse(request, error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,14 +273,11 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
|||||||
if (!editorContext.isEmpty())
|
if (!editorContext.isEmpty())
|
||||||
context.systemPrompt = editorContext;
|
context.systemPrompt = editorContext;
|
||||||
|
|
||||||
connect(session, &Session::event, this, [this, session](const ResponseEvent &event) {
|
|
||||||
onSessionEvent(requestIdForSession(session), event);
|
|
||||||
});
|
|
||||||
connect(session, &Session::finished, this, [this, session](const LLMQore::RequestID &, const QString &) {
|
connect(session, &Session::finished, this, [this, session](const LLMQore::RequestID &, const QString &) {
|
||||||
onSessionFinished(requestIdForSession(session));
|
onCompletionFinished(requestIdForSession(session));
|
||||||
});
|
});
|
||||||
connect(session, &Session::failed, this, [this, session](const LLMQore::RequestID &, const QString &error) {
|
connect(session, &Session::failed, this, [this, session](const LLMQore::RequestID &, const QString &error) {
|
||||||
onSessionFailed(requestIdForSession(session), error);
|
onCompletionFailed(requestIdForSession(session), error);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (auto *client = session->client())
|
if (auto *client = session->client())
|
||||||
@@ -288,14 +286,14 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
|||||||
|
|
||||||
const LLMQore::RequestID requestId = session->sendCompletion(std::move(context));
|
const LLMQore::RequestID requestId = session->sendCompletion(std::move(context));
|
||||||
if (requestId.isEmpty()) {
|
if (requestId.isEmpty()) {
|
||||||
m_sessionManager.removeSession(session);
|
session->deleteLater();
|
||||||
QString error = QString("Failed to start completion request for agent: %1").arg(agentName);
|
QString error = QString("Failed to start completion request for agent: %1").arg(agentName);
|
||||||
LOG_MESSAGE(error);
|
LOG_MESSAGE(error);
|
||||||
sendErrorResponse(request, error);
|
sendErrorResponse(request, error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_activeRequests[requestId] = {request, session, QString()};
|
m_activeRequests[requestId] = {request, session};
|
||||||
m_performanceLogger.startTimeMeasurement(requestId);
|
m_performanceLogger.startTimeMeasurement(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,8 @@ class QNetworkAccessManager;
|
|||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
class SessionManager;
|
|
||||||
class AgentFactory;
|
class AgentFactory;
|
||||||
class Session;
|
class Session;
|
||||||
class ResponseEvent;
|
|
||||||
|
|
||||||
namespace Templates {
|
namespace Templates {
|
||||||
struct ContextData;
|
struct ContextData;
|
||||||
@@ -39,7 +37,6 @@ public:
|
|||||||
LLMClientInterface(
|
LLMClientInterface(
|
||||||
const Settings::GeneralSettings &generalSettings,
|
const Settings::GeneralSettings &generalSettings,
|
||||||
const Settings::CodeCompletionSettings &completeSettings,
|
const Settings::CodeCompletionSettings &completeSettings,
|
||||||
SessionManager &sessionManager,
|
|
||||||
AgentFactory &agentFactory,
|
AgentFactory &agentFactory,
|
||||||
Context::IDocumentReader &documentReader,
|
Context::IDocumentReader &documentReader,
|
||||||
IRequestPerformanceLogger &performanceLogger);
|
IRequestPerformanceLogger &performanceLogger);
|
||||||
@@ -69,9 +66,8 @@ private:
|
|||||||
void handleCancelRequest();
|
void handleCancelRequest();
|
||||||
void sendErrorResponse(const QJsonObject &request, const QString &errorMessage);
|
void sendErrorResponse(const QJsonObject &request, const QString &errorMessage);
|
||||||
|
|
||||||
void onSessionEvent(const QString &requestId, const ResponseEvent &event);
|
void onCompletionFinished(const QString &requestId);
|
||||||
void onSessionFinished(const QString &requestId);
|
void onCompletionFailed(const QString &requestId, const QString &error);
|
||||||
void onSessionFailed(const QString &requestId, const QString &error);
|
|
||||||
void finishRequest(const QString &requestId);
|
void finishRequest(const QString &requestId);
|
||||||
QString requestIdForSession(Session *session) const;
|
QString requestIdForSession(Session *session) const;
|
||||||
|
|
||||||
@@ -79,7 +75,6 @@ private:
|
|||||||
{
|
{
|
||||||
QJsonObject originalRequest;
|
QJsonObject originalRequest;
|
||||||
QPointer<Session> session;
|
QPointer<Session> session;
|
||||||
QString accumulated;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Templates::ContextData prepareContext(
|
Templates::ContextData prepareContext(
|
||||||
@@ -89,7 +84,6 @@ private:
|
|||||||
|
|
||||||
const Settings::CodeCompletionSettings &m_completeSettings;
|
const Settings::CodeCompletionSettings &m_completeSettings;
|
||||||
const Settings::GeneralSettings &m_generalSettings;
|
const Settings::GeneralSettings &m_generalSettings;
|
||||||
SessionManager &m_sessionManager;
|
|
||||||
AgentFactory &m_agentFactory;
|
AgentFactory &m_agentFactory;
|
||||||
Context::IDocumentReader &m_documentReader;
|
Context::IDocumentReader &m_documentReader;
|
||||||
IRequestPerformanceLogger &m_performanceLogger;
|
IRequestPerformanceLogger &m_performanceLogger;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <context/Utils.hpp>
|
#include <context/Utils.hpp>
|
||||||
#include <logger/Logger.hpp>
|
#include <logger/Logger.hpp>
|
||||||
#include <sources/common/ResponseCleaner.hpp>
|
#include <sources/common/ResponseCleaner.hpp>
|
||||||
|
#include <settings/GeneralSettings.hpp>
|
||||||
#include <settings/QuickRefactorSettings.hpp>
|
#include <settings/QuickRefactorSettings.hpp>
|
||||||
#include <settings/ToolsSettings.hpp>
|
#include <settings/ToolsSettings.hpp>
|
||||||
|
|
||||||
@@ -177,7 +178,7 @@ void QuickRefactorHandler::prepareAndSendRequest(
|
|||||||
session->systemPrompt()->setLayer(
|
session->systemPrompt()->setLayer(
|
||||||
QStringLiteral("refactor"), buildSystemPrompt(editor, range));
|
QStringLiteral("refactor"), buildSystemPrompt(editor, range));
|
||||||
|
|
||||||
provider->client()->setTransferTimeout(
|
client->setTransferTimeout(
|
||||||
static_cast<int>(Settings::generalSettings().requestTimeout() * 1000));
|
static_cast<int>(Settings::generalSettings().requestTimeout() * 1000));
|
||||||
|
|
||||||
m_isRefactoringInProgress = true;
|
m_isRefactoringInProgress = true;
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
#include <ProviderSecretsStore.hpp>
|
#include <ProviderSecretsStore.hpp>
|
||||||
#include <ResponseEvent.hpp>
|
#include <ResponseEvent.hpp>
|
||||||
#include <Session.hpp>
|
#include <Session.hpp>
|
||||||
#include <SessionManager.hpp>
|
|
||||||
|
|
||||||
using namespace QodeAssist;
|
using namespace QodeAssist;
|
||||||
|
|
||||||
@@ -259,7 +258,6 @@ int main(int argc, char *argv[])
|
|||||||
auto *instances = new Providers::ProviderInstanceFactory(&app);
|
auto *instances = new Providers::ProviderInstanceFactory(&app);
|
||||||
auto *secrets = new Providers::ProviderSecretsStore(&app);
|
auto *secrets = new Providers::ProviderSecretsStore(&app);
|
||||||
auto *agentFactory = new AgentFactory(instances, secrets, &app);
|
auto *agentFactory = new AgentFactory(instances, secrets, &app);
|
||||||
auto *sessions = new SessionManager(agentFactory, &app);
|
|
||||||
|
|
||||||
if (parser.isSet(listOpt)) {
|
if (parser.isSet(listOpt)) {
|
||||||
const QStringList names = agentFactory->configNames();
|
const QStringList names = agentFactory->configNames();
|
||||||
@@ -271,21 +269,26 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString error;
|
QString error;
|
||||||
Session *session = nullptr;
|
Agent *agent = nullptr;
|
||||||
if (parser.isSet(fileOpt)) {
|
if (parser.isSet(fileOpt)) {
|
||||||
Agent *agent = agentFactory->createFromFile(parser.value(fileOpt), &app, &error);
|
agent = agentFactory->createFromFile(parser.value(fileOpt), &app, &error);
|
||||||
if (agent)
|
|
||||||
session = new Session(agent, &app);
|
|
||||||
} else if (parser.isSet(agentOpt)) {
|
} else if (parser.isSet(agentOpt)) {
|
||||||
session = sessions->createSession(parser.value(agentOpt), &error);
|
agent = agentFactory->create(parser.value(agentOpt), &app, &error);
|
||||||
} else {
|
} else {
|
||||||
err() << "Specify an agent with --agent <name> or --file <path>, or use --list.\n";
|
err() << "Specify an agent with --agent <name> or --file <path>, or use --list.\n";
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!session || !session->isValid()) {
|
if (!agent) {
|
||||||
err() << "Failed to create session: "
|
err() << "Failed to create agent: " << error << "\n";
|
||||||
<< (session ? session->invalidReason() : error) << "\n";
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool fimMode = parser.isSet(fimOpt);
|
||||||
|
|
||||||
|
Session *session = new Session(agent, &app);
|
||||||
|
if (!session->isValid()) {
|
||||||
|
err() << "Failed to create session: " << session->invalidReason() << "\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,14 +306,14 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
QString key = parser.value(apiKeyOpt);
|
QString key = parser.value(apiKeyOpt);
|
||||||
if (key.isEmpty()) {
|
if (key.isEmpty()) {
|
||||||
const AgentConfig &cfg = session->agent()->config();
|
const AgentConfig &cfg = agent->config();
|
||||||
const Providers::ProviderInstance *inst
|
const Providers::ProviderInstance *inst
|
||||||
= instances->instanceByName(cfg.providerInstance);
|
= instances->instanceByName(cfg.providerInstance);
|
||||||
if (inst)
|
if (inst)
|
||||||
key = resolveApiKey(envFile, inst->clientApi, inst->apiKeyRef);
|
key = resolveApiKey(envFile, inst->clientApi, inst->apiKeyRef);
|
||||||
}
|
}
|
||||||
if (!key.isEmpty() && session->agent()->provider())
|
if (!key.isEmpty() && agent->provider())
|
||||||
session->agent()->provider()->setApiKey(key);
|
agent->provider()->setApiKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -340,23 +343,26 @@ int main(int argc, char *argv[])
|
|||||||
const bool showThinking = !parser.isSet(noThinkingOpt);
|
const bool showThinking = !parser.isSet(noThinkingOpt);
|
||||||
int exitCode = 0;
|
int exitCode = 0;
|
||||||
|
|
||||||
QObject::connect(session, &Session::event, &app, [showThinking](const ResponseEvent &ev) {
|
|
||||||
printEvent(ev, showThinking);
|
|
||||||
});
|
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
session, &Session::finished, &app, [&](const LLMQore::RequestID &, const QString &reason) {
|
session, &Session::event, &app, [showThinking](const ResponseEvent &ev) {
|
||||||
|
printEvent(ev, showThinking);
|
||||||
|
});
|
||||||
|
QObject::connect(
|
||||||
|
session, &Session::finished, &app,
|
||||||
|
[&](const LLMQore::RequestID &, const QString &reason) {
|
||||||
err() << "\n[done] stopReason=" << (reason.isEmpty() ? "<none>" : reason) << "\n";
|
err() << "\n[done] stopReason=" << (reason.isEmpty() ? "<none>" : reason) << "\n";
|
||||||
QCoreApplication::quit();
|
QCoreApplication::quit();
|
||||||
});
|
});
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
session, &Session::failed, &app, [&](const LLMQore::RequestID &, const QString &msg) {
|
session, &Session::failed, &app,
|
||||||
|
[&](const LLMQore::RequestID &, const QString &msg) {
|
||||||
err() << "\n[failed] " << msg << "\n";
|
err() << "\n[failed] " << msg << "\n";
|
||||||
exitCode = 1;
|
exitCode = 1;
|
||||||
QCoreApplication::quit();
|
QCoreApplication::quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
auto dispatch = [&] {
|
auto dispatch = [&] {
|
||||||
if (parser.isSet(fimOpt)) {
|
if (fimMode) {
|
||||||
Templates::ContextData ctx;
|
Templates::ContextData ctx;
|
||||||
ctx.prefix = prompt;
|
ctx.prefix = prompt;
|
||||||
if (parser.isSet(suffixOpt))
|
if (parser.isSet(suffixOpt))
|
||||||
|
|||||||
@@ -225,9 +225,8 @@ public:
|
|||||||
m_agentsOptionsPage = Settings::createAgentsSettingsPage(
|
m_agentsOptionsPage = Settings::createAgentsSettingsPage(
|
||||||
m_agentFactory, m_agentsPageNavigator);
|
m_agentFactory, m_agentsPageNavigator);
|
||||||
|
|
||||||
m_agentPipelinesPageNavigator = new Settings::AgentPipelinesPageNavigator(this);
|
Settings::generalSettings().setAgentPipelinesContext(
|
||||||
m_agentPipelinesOptionsPage = Settings::createAgentPipelinesSettingsPage(
|
m_agentFactory, m_agentsPageNavigator);
|
||||||
m_agentFactory, m_agentPipelinesPageNavigator, m_agentsPageNavigator);
|
|
||||||
|
|
||||||
m_mcpServerManager = new Mcp::McpServerManager(this);
|
m_mcpServerManager = new Mcp::McpServerManager(this);
|
||||||
m_mcpServerManager->init();
|
m_mcpServerManager->init();
|
||||||
@@ -347,7 +346,6 @@ public:
|
|||||||
m_qodeAssistClient = new QodeAssistClient(new LLMClientInterface(
|
m_qodeAssistClient = new QodeAssistClient(new LLMClientInterface(
|
||||||
Settings::generalSettings(),
|
Settings::generalSettings(),
|
||||||
Settings::codeCompletionSettings(),
|
Settings::codeCompletionSettings(),
|
||||||
*m_sessionManager,
|
|
||||||
*m_agentFactory,
|
*m_agentFactory,
|
||||||
m_documentReader,
|
m_documentReader,
|
||||||
m_performanceLogger));
|
m_performanceLogger));
|
||||||
@@ -533,8 +531,6 @@ private:
|
|||||||
QPointer<SessionManager> m_sessionManager;
|
QPointer<SessionManager> m_sessionManager;
|
||||||
QPointer<Settings::AgentsPageNavigator> m_agentsPageNavigator;
|
QPointer<Settings::AgentsPageNavigator> m_agentsPageNavigator;
|
||||||
std::unique_ptr<Core::IOptionsPage> m_agentsOptionsPage;
|
std::unique_ptr<Core::IOptionsPage> m_agentsOptionsPage;
|
||||||
QPointer<Settings::AgentPipelinesPageNavigator> m_agentPipelinesPageNavigator;
|
|
||||||
std::unique_ptr<Core::IOptionsPage> m_agentPipelinesOptionsPage;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Internal
|
} // namespace QodeAssist::Internal
|
||||||
|
|||||||
@@ -6,16 +6,14 @@
|
|||||||
|
|
||||||
#include <coreplugin/dialogs/ioptionspage.h>
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
#include <utils/infolabel.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
#include <QColor>
|
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPointer>
|
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QScrollArea>
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
@@ -39,6 +37,192 @@ GeneralSettings &generalSettings()
|
|||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int kSaveDebounceMs = 300;
|
||||||
|
|
||||||
|
struct SlotMeta
|
||||||
|
{
|
||||||
|
const char *title;
|
||||||
|
const char *hint;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SlotMeta kSlotMeta[] = {
|
||||||
|
{TrConstants::CODE_COMPLETION, TrConstants::SLOT_HINT_CODE_COMPLETION},
|
||||||
|
{TrConstants::CHAT_ASSISTANT, TrConstants::SLOT_HINT_CHAT_ASSISTANT},
|
||||||
|
{TrConstants::CHAT_COMPRESSION, TrConstants::SLOT_HINT_CHAT_COMPRESSION},
|
||||||
|
{TrConstants::QUICK_REFACTOR, TrConstants::SLOT_HINT_QUICK_REFACTOR},
|
||||||
|
};
|
||||||
|
|
||||||
|
class AgentPipelinesWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY_MOVE(AgentPipelinesWidget)
|
||||||
|
public:
|
||||||
|
AgentPipelinesWidget(
|
||||||
|
const QPointer<AgentFactory> &agentFactory,
|
||||||
|
const QPointer<AgentsPageNavigator> &agentsNavigator,
|
||||||
|
QWidget *parent = nullptr)
|
||||||
|
: QWidget(parent)
|
||||||
|
, m_agentFactory(agentFactory)
|
||||||
|
, m_agentsNavigator(agentsNavigator)
|
||||||
|
{
|
||||||
|
m_titleLabel = new QLabel(Tr::tr(TrConstants::AGENT_PIPELINES), this);
|
||||||
|
QFont tf = m_titleLabel->font();
|
||||||
|
tf.setBold(true);
|
||||||
|
tf.setPixelSize(13);
|
||||||
|
m_titleLabel->setFont(tf);
|
||||||
|
|
||||||
|
m_resetBtn = new QPushButton(Tr::tr(TrConstants::RESET_TO_DEFAULTS), this);
|
||||||
|
|
||||||
|
auto *headerRow = new QHBoxLayout;
|
||||||
|
headerRow->setContentsMargins(0, 0, 0, 0);
|
||||||
|
headerRow->setSpacing(8);
|
||||||
|
headerRow->addWidget(m_titleLabel);
|
||||||
|
headerRow->addStretch(1);
|
||||||
|
headerRow->addWidget(m_resetBtn);
|
||||||
|
|
||||||
|
auto *headerSep = new QFrame(this);
|
||||||
|
headerSep->setFrameShape(QFrame::HLine);
|
||||||
|
headerSep->setFrameShadow(QFrame::Sunken);
|
||||||
|
|
||||||
|
m_loadWarning = new Utils::InfoLabel({}, Utils::InfoLabel::Warning, this);
|
||||||
|
m_loadWarning->setElideMode(Qt::ElideNone);
|
||||||
|
m_loadWarning->setWordWrap(true);
|
||||||
|
m_loadWarning->setVisible(false);
|
||||||
|
|
||||||
|
m_rosters[0] = new AgentRosterWidget(this);
|
||||||
|
m_rosters[1] = new AgentRosterWidget(this);
|
||||||
|
m_rosters[2] = new AgentRosterWidget(this);
|
||||||
|
m_rosters[3] = new AgentRosterWidget(this);
|
||||||
|
|
||||||
|
for (int i = 0; i < kRosterCount; ++i)
|
||||||
|
m_rosters[i]->setSlot(Tr::tr(kSlotMeta[i].title), Tr::tr(kSlotMeta[i].hint), {});
|
||||||
|
|
||||||
|
auto *root = new QVBoxLayout(this);
|
||||||
|
root->setContentsMargins(0, 0, 0, 0);
|
||||||
|
root->setSpacing(12);
|
||||||
|
root->addLayout(headerRow);
|
||||||
|
root->addWidget(headerSep);
|
||||||
|
root->addWidget(m_loadWarning);
|
||||||
|
for (int i = 0; i < kRosterCount; ++i)
|
||||||
|
root->addWidget(m_rosters[i]);
|
||||||
|
|
||||||
|
m_saveDebounce = new QTimer(this);
|
||||||
|
m_saveDebounce->setSingleShot(true);
|
||||||
|
m_saveDebounce->setInterval(kSaveDebounceMs);
|
||||||
|
connect(m_saveDebounce, &QTimer::timeout, this, [this]() { persistRosters(); });
|
||||||
|
|
||||||
|
loadFromSettings();
|
||||||
|
|
||||||
|
connect(m_resetBtn, &QPushButton::clicked, this, &AgentPipelinesWidget::onReset);
|
||||||
|
|
||||||
|
for (int i = 0; i < kRosterCount; ++i) {
|
||||||
|
connect(m_rosters[i], &AgentRosterWidget::editAgentRequested, this,
|
||||||
|
&AgentPipelinesWidget::onEditAgent);
|
||||||
|
connect(m_rosters[i], &AgentRosterWidget::rosterChanged, this,
|
||||||
|
[this](const QStringList &) { m_saveDebounce->start(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~AgentPipelinesWidget() override
|
||||||
|
{
|
||||||
|
if (m_saveDebounce && m_saveDebounce->isActive()) {
|
||||||
|
m_saveDebounce->stop();
|
||||||
|
persistRosters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int kRosterCount = 4;
|
||||||
|
|
||||||
|
void persistRosters()
|
||||||
|
{
|
||||||
|
PipelineRosters rosters;
|
||||||
|
rosters.codeCompletion = m_rosters[0]->roster();
|
||||||
|
rosters.chatAssistant = m_rosters[1]->roster();
|
||||||
|
rosters.chatCompression = m_rosters[2]->roster();
|
||||||
|
rosters.quickRefactor = m_rosters[3]->roster();
|
||||||
|
QString err;
|
||||||
|
if (!PipelinesConfig::save(rosters, &err)) {
|
||||||
|
LOG_MESSAGE(QStringLiteral("[Pipelines] save failed (%1): %2")
|
||||||
|
.arg(PipelinesConfig::filePath(), err));
|
||||||
|
if (!m_saveErrorShown) {
|
||||||
|
m_saveErrorShown = true;
|
||||||
|
QMessageBox::warning(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
Tr::tr(TrConstants::AGENT_PIPELINES),
|
||||||
|
tr("Failed to save pipelines.toml:\n%1\n\n"
|
||||||
|
"Further save failures will only be logged.")
|
||||||
|
.arg(err));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_saveErrorShown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onReset()
|
||||||
|
{
|
||||||
|
const auto reply = QMessageBox::question(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
Tr::tr(TrConstants::RESET_SETTINGS),
|
||||||
|
Tr::tr(TrConstants::CONFIRMATION),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (reply != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString err;
|
||||||
|
if (!PipelinesConfig::save(PipelineRosters::defaults(), &err))
|
||||||
|
LOG_MESSAGE(QStringLiteral("[Pipelines] failed to reset rosters: %1").arg(err));
|
||||||
|
|
||||||
|
m_saveErrorShown = false;
|
||||||
|
loadFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onEditAgent(const QString &name)
|
||||||
|
{
|
||||||
|
if (m_agentsNavigator)
|
||||||
|
m_agentsNavigator->requestSelectAgent(name);
|
||||||
|
|
||||||
|
showSettings(Constants::QODE_ASSIST_AGENTS_SETTINGS_PAGE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFromSettings()
|
||||||
|
{
|
||||||
|
const PipelinesLoadResult lr = PipelinesConfig::load();
|
||||||
|
const bool broken = lr.status == PipelinesLoadStatus::ParseError
|
||||||
|
|| lr.status == PipelinesLoadStatus::SchemaError;
|
||||||
|
if (broken) {
|
||||||
|
m_loadWarning->setText(
|
||||||
|
tr("pipelines.toml has issues — using defaults for affected entries:\n%1\n"
|
||||||
|
"Changes you make here will overwrite the file.")
|
||||||
|
.arg(lr.message));
|
||||||
|
}
|
||||||
|
m_loadWarning->setVisible(broken);
|
||||||
|
|
||||||
|
AgentFactory *factory = m_agentFactory.data();
|
||||||
|
m_rosters[0]->setRoster(lr.rosters.codeCompletion, factory);
|
||||||
|
m_rosters[1]->setRoster(lr.rosters.chatAssistant, factory);
|
||||||
|
m_rosters[2]->setRoster(lr.rosters.chatCompression, factory);
|
||||||
|
m_rosters[3]->setRoster(lr.rosters.quickRefactor, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointer<AgentFactory> m_agentFactory;
|
||||||
|
QPointer<AgentsPageNavigator> m_agentsNavigator;
|
||||||
|
|
||||||
|
QLabel *m_titleLabel = nullptr;
|
||||||
|
QPushButton *m_resetBtn = nullptr;
|
||||||
|
Utils::InfoLabel *m_loadWarning = nullptr;
|
||||||
|
|
||||||
|
AgentRosterWidget *m_rosters[kRosterCount] = {};
|
||||||
|
|
||||||
|
QTimer *m_saveDebounce = nullptr;
|
||||||
|
bool m_saveErrorShown = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
GeneralSettings::GeneralSettings()
|
GeneralSettings::GeneralSettings()
|
||||||
{
|
{
|
||||||
setAutoApply(false);
|
setAutoApply(false);
|
||||||
@@ -99,6 +283,8 @@ GeneralSettings::GeneralSettings()
|
|||||||
supportLinks->setOpenExternalLinks(true);
|
supportLinks->setOpenExternalLinks(true);
|
||||||
supportLinks->setTextFormat(Qt::RichText);
|
supportLinks->setTextFormat(Qt::RichText);
|
||||||
|
|
||||||
|
auto *pipelines = new AgentPipelinesWidget(m_agentFactory, m_agentsNavigator);
|
||||||
|
|
||||||
return Column{
|
return Column{
|
||||||
Row{supportLabel, supportLinks, Stretch{1}},
|
Row{supportLabel, supportLinks, Stretch{1}},
|
||||||
Space{8},
|
Space{8},
|
||||||
@@ -107,10 +293,19 @@ GeneralSettings::GeneralSettings()
|
|||||||
Row{enableCheckUpdate, Stretch{1}},
|
Row{enableCheckUpdate, Stretch{1}},
|
||||||
Space{8},
|
Space{8},
|
||||||
networkGroup,
|
networkGroup,
|
||||||
|
Space{12},
|
||||||
|
pipelines,
|
||||||
Stretch{1}};
|
Stretch{1}};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::setAgentPipelinesContext(
|
||||||
|
AgentFactory *agentFactory, AgentsPageNavigator *agentsNavigator)
|
||||||
|
{
|
||||||
|
m_agentFactory = agentFactory;
|
||||||
|
m_agentsNavigator = agentsNavigator;
|
||||||
|
}
|
||||||
|
|
||||||
void GeneralSettings::setupConnections()
|
void GeneralSettings::setupConnections()
|
||||||
{
|
{
|
||||||
connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
connect(&enableLogging, &Utils::BoolAspect::volatileValueChanged, this, [this]() {
|
||||||
@@ -183,244 +378,6 @@ void showSettings(const Utils::Id page, Utils::Id item)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
AgentPipelinesPageNavigator::AgentPipelinesPageNavigator(QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
{}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr int kSaveDebounceMs = 300;
|
|
||||||
|
|
||||||
struct SlotMeta
|
|
||||||
{
|
|
||||||
const char *title;
|
|
||||||
const char *hint;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SlotMeta kSlotMeta[] = {
|
|
||||||
{TrConstants::CODE_COMPLETION, TrConstants::SLOT_HINT_CODE_COMPLETION},
|
|
||||||
{TrConstants::CHAT_ASSISTANT, TrConstants::SLOT_HINT_CHAT_ASSISTANT},
|
|
||||||
{TrConstants::CHAT_COMPRESSION, TrConstants::SLOT_HINT_CHAT_COMPRESSION},
|
|
||||||
{TrConstants::QUICK_REFACTOR, TrConstants::SLOT_HINT_QUICK_REFACTOR},
|
|
||||||
};
|
|
||||||
|
|
||||||
class AgentPipelinesPageWidget : public Core::IOptionsPageWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY_MOVE(AgentPipelinesPageWidget)
|
|
||||||
public:
|
|
||||||
AgentPipelinesPageWidget(
|
|
||||||
const QPointer<AgentFactory> &agentFactory,
|
|
||||||
const QPointer<AgentPipelinesPageNavigator> &navigator,
|
|
||||||
const QPointer<AgentsPageNavigator> &agentsNavigator)
|
|
||||||
: m_agentFactory(agentFactory)
|
|
||||||
, m_navigator(navigator)
|
|
||||||
, m_agentsNavigator(agentsNavigator)
|
|
||||||
{
|
|
||||||
m_titleLabel = new QLabel(Tr::tr(TrConstants::AGENT_PIPELINES), this);
|
|
||||||
QFont tf = m_titleLabel->font();
|
|
||||||
tf.setBold(true);
|
|
||||||
tf.setPixelSize(13);
|
|
||||||
m_titleLabel->setFont(tf);
|
|
||||||
|
|
||||||
m_resetBtn = new QPushButton(Tr::tr(TrConstants::RESET_TO_DEFAULTS), this);
|
|
||||||
|
|
||||||
auto *headerRow = new QHBoxLayout;
|
|
||||||
headerRow->setContentsMargins(0, 0, 0, 0);
|
|
||||||
headerRow->setSpacing(8);
|
|
||||||
headerRow->addWidget(m_titleLabel);
|
|
||||||
headerRow->addStretch(1);
|
|
||||||
headerRow->addWidget(m_resetBtn);
|
|
||||||
|
|
||||||
auto *headerSep = new QFrame(this);
|
|
||||||
headerSep->setFrameShape(QFrame::HLine);
|
|
||||||
headerSep->setFrameShadow(QFrame::Sunken);
|
|
||||||
|
|
||||||
m_rosters[0] = new AgentRosterWidget(this);
|
|
||||||
m_rosters[1] = new AgentRosterWidget(this);
|
|
||||||
m_rosters[2] = new AgentRosterWidget(this);
|
|
||||||
m_rosters[3] = new AgentRosterWidget(this);
|
|
||||||
|
|
||||||
for (int i = 0; i < kRosterCount; ++i)
|
|
||||||
m_rosters[i]->setSlot(Tr::tr(kSlotMeta[i].title), Tr::tr(kSlotMeta[i].hint), {});
|
|
||||||
|
|
||||||
auto *content = new QWidget(this);
|
|
||||||
auto *contentLay = new QVBoxLayout(content);
|
|
||||||
contentLay->setContentsMargins(0, 0, 0, 0);
|
|
||||||
contentLay->setSpacing(12);
|
|
||||||
for (int i = 0; i < kRosterCount; ++i)
|
|
||||||
contentLay->addWidget(m_rosters[i]);
|
|
||||||
contentLay->addStretch(1);
|
|
||||||
|
|
||||||
auto *scroll = new QScrollArea(this);
|
|
||||||
scroll->setWidgetResizable(true);
|
|
||||||
scroll->setFrameShape(QFrame::NoFrame);
|
|
||||||
scroll->setWidget(content);
|
|
||||||
|
|
||||||
auto *root = new QVBoxLayout(this);
|
|
||||||
root->setContentsMargins(8, 8, 8, 8);
|
|
||||||
root->setSpacing(6);
|
|
||||||
root->addLayout(headerRow);
|
|
||||||
root->addWidget(headerSep);
|
|
||||||
root->addWidget(scroll, 1);
|
|
||||||
|
|
||||||
m_saveDebounce = new QTimer(this);
|
|
||||||
m_saveDebounce->setSingleShot(true);
|
|
||||||
m_saveDebounce->setInterval(kSaveDebounceMs);
|
|
||||||
connect(m_saveDebounce, &QTimer::timeout, this, [this]() { persistRosters(); });
|
|
||||||
|
|
||||||
loadFromSettings();
|
|
||||||
|
|
||||||
connect(m_resetBtn, &QPushButton::clicked, this, &AgentPipelinesPageWidget::onReset);
|
|
||||||
|
|
||||||
for (int i = 0; i < kRosterCount; ++i) {
|
|
||||||
connect(m_rosters[i], &AgentRosterWidget::editAgentRequested, this,
|
|
||||||
&AgentPipelinesPageWidget::onEditAgent);
|
|
||||||
connect(m_rosters[i], &AgentRosterWidget::rosterChanged, this,
|
|
||||||
[this](const QStringList &) { m_saveDebounce->start(); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~AgentPipelinesPageWidget() override
|
|
||||||
{
|
|
||||||
if (m_saveDebounce && m_saveDebounce->isActive()) {
|
|
||||||
m_saveDebounce->stop();
|
|
||||||
persistRosters();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply() final
|
|
||||||
{
|
|
||||||
if (m_saveDebounce && m_saveDebounce->isActive())
|
|
||||||
m_saveDebounce->stop();
|
|
||||||
persistRosters();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr int kRosterCount = 4;
|
|
||||||
|
|
||||||
void persistRosters()
|
|
||||||
{
|
|
||||||
PipelineRosters rosters;
|
|
||||||
rosters.codeCompletion = m_rosters[0]->roster();
|
|
||||||
rosters.chatAssistant = m_rosters[1]->roster();
|
|
||||||
rosters.chatCompression = m_rosters[2]->roster();
|
|
||||||
rosters.quickRefactor = m_rosters[3]->roster();
|
|
||||||
QString err;
|
|
||||||
if (!PipelinesConfig::save(rosters, &err)) {
|
|
||||||
LOG_MESSAGE(QStringLiteral("[Pipelines] save failed (%1): %2")
|
|
||||||
.arg(PipelinesConfig::filePath(), err));
|
|
||||||
if (!m_saveErrorShown) {
|
|
||||||
m_saveErrorShown = true;
|
|
||||||
QMessageBox::warning(
|
|
||||||
Core::ICore::dialogParent(),
|
|
||||||
Tr::tr(TrConstants::AGENT_PIPELINES),
|
|
||||||
tr("Failed to save pipelines.toml:\n%1\n\n"
|
|
||||||
"Further save failures will only be logged.")
|
|
||||||
.arg(err));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_saveErrorShown = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onReset()
|
|
||||||
{
|
|
||||||
const auto reply = QMessageBox::question(
|
|
||||||
Core::ICore::dialogParent(),
|
|
||||||
Tr::tr(TrConstants::RESET_SETTINGS),
|
|
||||||
Tr::tr(TrConstants::CONFIRMATION),
|
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
|
||||||
|
|
||||||
if (reply != QMessageBox::Yes)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString err;
|
|
||||||
if (!PipelinesConfig::save(PipelineRosters::defaults(), &err))
|
|
||||||
LOG_MESSAGE(QStringLiteral("[Pipelines] failed to reset rosters: %1").arg(err));
|
|
||||||
|
|
||||||
m_saveErrorShown = false;
|
|
||||||
loadFromSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onEditAgent(const QString &name)
|
|
||||||
{
|
|
||||||
if (m_agentsNavigator)
|
|
||||||
m_agentsNavigator->requestSelectAgent(name);
|
|
||||||
if (m_navigator)
|
|
||||||
emit m_navigator->editAgentRequested(name);
|
|
||||||
|
|
||||||
#if QODEASSIST_QT_CREATOR_VERSION >= QT_VERSION_CHECK(18, 0, 83)
|
|
||||||
Core::ICore::showSettings(Constants::QODE_ASSIST_AGENTS_SETTINGS_PAGE_ID);
|
|
||||||
#else
|
|
||||||
Core::ICore::showOptionsDialog(Constants::QODE_ASSIST_AGENTS_SETTINGS_PAGE_ID);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadFromSettings()
|
|
||||||
{
|
|
||||||
const PipelinesLoadResult lr = PipelinesConfig::load();
|
|
||||||
if (lr.status == PipelinesLoadStatus::ParseError
|
|
||||||
|| lr.status == PipelinesLoadStatus::SchemaError) {
|
|
||||||
QMessageBox::warning(
|
|
||||||
Core::ICore::dialogParent(),
|
|
||||||
Tr::tr(TrConstants::AGENT_PIPELINES),
|
|
||||||
tr("pipelines.toml has issues — using defaults for affected entries:\n%1\n\n"
|
|
||||||
"Click OK to continue. Changes you make here will overwrite the file.")
|
|
||||||
.arg(lr.message));
|
|
||||||
}
|
|
||||||
|
|
||||||
AgentFactory *factory = m_agentFactory.data();
|
|
||||||
m_rosters[0]->setRoster(lr.rosters.codeCompletion, factory);
|
|
||||||
m_rosters[1]->setRoster(lr.rosters.chatAssistant, factory);
|
|
||||||
m_rosters[2]->setRoster(lr.rosters.chatCompression, factory);
|
|
||||||
m_rosters[3]->setRoster(lr.rosters.quickRefactor, factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointer<AgentFactory> m_agentFactory;
|
|
||||||
QPointer<AgentPipelinesPageNavigator> m_navigator;
|
|
||||||
QPointer<AgentsPageNavigator> m_agentsNavigator;
|
|
||||||
|
|
||||||
QLabel *m_titleLabel = nullptr;
|
|
||||||
QPushButton *m_resetBtn = nullptr;
|
|
||||||
|
|
||||||
AgentRosterWidget *m_rosters[kRosterCount] = {};
|
|
||||||
|
|
||||||
QTimer *m_saveDebounce = nullptr;
|
|
||||||
bool m_saveErrorShown = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AgentPipelinesOptionsPage final : public Core::IOptionsPage
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AgentPipelinesOptionsPage(
|
|
||||||
AgentFactory *agentFactory,
|
|
||||||
AgentPipelinesPageNavigator *navigator,
|
|
||||||
AgentsPageNavigator *agentsNavigator)
|
|
||||||
{
|
|
||||||
setId(Constants::QODE_ASSIST_AGENT_PIPELINES_PAGE_ID);
|
|
||||||
setDisplayName(Tr::tr(TrConstants::AGENT_PIPELINES));
|
|
||||||
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
|
|
||||||
const QPointer<AgentFactory> factoryPtr(agentFactory);
|
|
||||||
const QPointer<AgentPipelinesPageNavigator> navPtr(navigator);
|
|
||||||
const QPointer<AgentsPageNavigator> agentsNavPtr(agentsNavigator);
|
|
||||||
setWidgetCreator([factoryPtr, navPtr, agentsNavPtr] {
|
|
||||||
return new AgentPipelinesPageWidget(factoryPtr, navPtr, agentsNavPtr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
std::unique_ptr<Core::IOptionsPage> createAgentPipelinesSettingsPage(
|
|
||||||
AgentFactory *agentFactory,
|
|
||||||
AgentPipelinesPageNavigator *navigator,
|
|
||||||
AgentsPageNavigator *agentsNavigator)
|
|
||||||
{
|
|
||||||
return std::make_unique<AgentPipelinesOptionsPage>(
|
|
||||||
agentFactory, navigator, agentsNavigator);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist::Settings
|
} // namespace QodeAssist::Settings
|
||||||
|
|
||||||
#include "GeneralSettings.moc"
|
#include "GeneralSettings.moc"
|
||||||
|
|||||||
@@ -4,30 +4,28 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <QPointer>
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
#include <utils/aspects.h>
|
#include <utils/aspects.h>
|
||||||
|
|
||||||
#include "ButtonAspect.hpp"
|
#include "ButtonAspect.hpp"
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
class IOptionsPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
class AgentFactory;
|
class AgentFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace QodeAssist::Settings {
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
class AgentsPageNavigator;
|
||||||
|
|
||||||
class GeneralSettings : public Utils::AspectContainer
|
class GeneralSettings : public Utils::AspectContainer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GeneralSettings();
|
GeneralSettings();
|
||||||
|
|
||||||
|
void setAgentPipelinesContext(
|
||||||
|
AgentFactory *agentFactory, AgentsPageNavigator *agentsNavigator);
|
||||||
|
|
||||||
Utils::BoolAspect enableQodeAssist{this};
|
Utils::BoolAspect enableQodeAssist{this};
|
||||||
Utils::BoolAspect enableLogging{this};
|
Utils::BoolAspect enableLogging{this};
|
||||||
Utils::BoolAspect enableCheckUpdate{this};
|
Utils::BoolAspect enableCheckUpdate{this};
|
||||||
@@ -40,6 +38,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
void setupConnections();
|
void setupConnections();
|
||||||
void resetPageToDefaults();
|
void resetPageToDefaults();
|
||||||
|
|
||||||
|
QPointer<AgentFactory> m_agentFactory;
|
||||||
|
QPointer<AgentsPageNavigator> m_agentsNavigator;
|
||||||
};
|
};
|
||||||
|
|
||||||
GeneralSettings &generalSettings();
|
GeneralSettings &generalSettings();
|
||||||
@@ -47,22 +48,4 @@ GeneralSettings &generalSettings();
|
|||||||
void showSettings(const Utils::Id page);
|
void showSettings(const Utils::Id page);
|
||||||
void showSettings(const Utils::Id page, Utils::Id item);
|
void showSettings(const Utils::Id page, Utils::Id item);
|
||||||
|
|
||||||
class AgentsPageNavigator;
|
|
||||||
|
|
||||||
class AgentPipelinesPageNavigator : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY_MOVE(AgentPipelinesPageNavigator)
|
|
||||||
public:
|
|
||||||
explicit AgentPipelinesPageNavigator(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void editAgentRequested(const QString &agentName);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<Core::IOptionsPage> createAgentPipelinesSettingsPage(
|
|
||||||
AgentFactory *agentFactory,
|
|
||||||
AgentPipelinesPageNavigator *navigator,
|
|
||||||
AgentsPageNavigator *agentsNavigator);
|
|
||||||
|
|
||||||
} // namespace QodeAssist::Settings
|
} // namespace QodeAssist::Settings
|
||||||
|
|||||||
@@ -143,9 +143,6 @@ const char QODE_ASSIST_PROVIDER_SETTINGS_PAGE_ID[] = "QodeAssist.7ProviderSettin
|
|||||||
// Agents Settings Page ID
|
// Agents Settings Page ID
|
||||||
const char QODE_ASSIST_AGENTS_SETTINGS_PAGE_ID[] = "QodeAssist.8AgentsSettingsPageId";
|
const char QODE_ASSIST_AGENTS_SETTINGS_PAGE_ID[] = "QodeAssist.8AgentsSettingsPageId";
|
||||||
|
|
||||||
// Agent Pipelines (experimental) settings
|
|
||||||
const char QODE_ASSIST_AGENT_PIPELINES_PAGE_ID[] = "QodeAssist.9AgentPipelinesPageId";
|
|
||||||
|
|
||||||
// Provider API Keys
|
// Provider API Keys
|
||||||
const char OPEN_ROUTER_API_KEY[] = "QodeAssist.openRouterApiKey";
|
const char OPEN_ROUTER_API_KEY[] = "QodeAssist.openRouterApiKey";
|
||||||
const char OPEN_ROUTER_API_KEY_HISTORY[] = "QodeAssist.openRouterApiKeyHistory";
|
const char OPEN_ROUTER_API_KEY_HISTORY[] = "QodeAssist.openRouterApiKeyHistory";
|
||||||
|
|||||||
@@ -150,6 +150,15 @@ LLMQore::RequestID Session::sendText(const QString &text)
|
|||||||
return send(std::move(blocks));
|
return send(std::move(blocks));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLMQore::RequestID Session::sendCompletion(Templates::ContextData ctx)
|
||||||
|
{
|
||||||
|
if (!isValid())
|
||||||
|
return {};
|
||||||
|
if (isInFlight())
|
||||||
|
cancel();
|
||||||
|
return dispatchContext(std::move(ctx), /*tools=*/false, /*thinking=*/false);
|
||||||
|
}
|
||||||
|
|
||||||
LLMQore::RequestID Session::send(
|
LLMQore::RequestID Session::send(
|
||||||
std::vector<std::unique_ptr<LLMQore::ContentBlock>> userBlocks,
|
std::vector<std::unique_ptr<LLMQore::ContentBlock>> userBlocks,
|
||||||
std::optional<bool> toolsOverride,
|
std::optional<bool> toolsOverride,
|
||||||
@@ -185,49 +194,9 @@ void Session::cancel()
|
|||||||
emit failed(id, QStringLiteral("Cancelled by user"));
|
emit failed(id, QStringLiteral("Cancelled by user"));
|
||||||
}
|
}
|
||||||
|
|
||||||
LLMQore::RequestID Session::sendCompletion(Templates::ContextData ctx)
|
|
||||||
{
|
|
||||||
if (!isValid())
|
|
||||||
return {};
|
|
||||||
if (isInFlight())
|
|
||||||
cancel();
|
|
||||||
|
|
||||||
if (m_history)
|
|
||||||
m_history->clear();
|
|
||||||
|
|
||||||
auto *provider = m_agent->provider();
|
|
||||||
auto *tmpl = m_agent->promptTemplate();
|
|
||||||
const auto &cfg = m_agent->config();
|
|
||||||
|
|
||||||
const QString rolePrompt = m_systemPrompt ? m_systemPrompt->compose() : QString();
|
|
||||||
if (!rolePrompt.isEmpty()) {
|
|
||||||
ctx.systemPrompt = (ctx.systemPrompt && !ctx.systemPrompt->isEmpty())
|
|
||||||
? rolePrompt + QStringLiteral("\n\n") + *ctx.systemPrompt
|
|
||||||
: rolePrompt;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject payload{{QStringLiteral("model"), cfg.model}};
|
|
||||||
if (!provider->prepareRequest(payload, tmpl, ctx, /*tools=*/false, /*thinking=*/false))
|
|
||||||
return {};
|
|
||||||
|
|
||||||
QString endpoint = cfg.endpoint;
|
|
||||||
endpoint.replace(QStringLiteral("${MODEL}"), cfg.model);
|
|
||||||
const auto id = provider->sendRequest(QUrl(provider->url()), payload, endpoint);
|
|
||||||
if (id.isEmpty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
m_inFlight = id;
|
|
||||||
if (m_router)
|
|
||||||
m_router->beginRequest(id);
|
|
||||||
emit started(id);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
LLMQore::RequestID Session::dispatch(
|
LLMQore::RequestID Session::dispatch(
|
||||||
std::optional<bool> toolsOverride, std::optional<bool> thinkingOverride)
|
std::optional<bool> toolsOverride, std::optional<bool> thinkingOverride)
|
||||||
{
|
{
|
||||||
auto *provider = m_agent->provider();
|
|
||||||
auto *tmpl = m_agent->promptTemplate();
|
|
||||||
const auto &cfg = m_agent->config();
|
const auto &cfg = m_agent->config();
|
||||||
|
|
||||||
const QString renderedContext = renderAgentContext();
|
const QString renderedContext = renderAgentContext();
|
||||||
@@ -236,11 +205,19 @@ LLMQore::RequestID Session::dispatch(
|
|||||||
else
|
else
|
||||||
m_systemPrompt->setLayer(QStringLiteral("agent.system"), renderedContext);
|
m_systemPrompt->setLayer(QStringLiteral("agent.system"), renderedContext);
|
||||||
|
|
||||||
Templates::ContextData ctx = toLegacyContext();
|
|
||||||
QJsonObject payload{{QStringLiteral("model"), cfg.model}};
|
|
||||||
|
|
||||||
const bool tools = toolsOverride.value_or(cfg.enableTools);
|
const bool tools = toolsOverride.value_or(cfg.enableTools);
|
||||||
const bool thinking = thinkingOverride.value_or(cfg.enableThinking);
|
const bool thinking = thinkingOverride.value_or(cfg.enableThinking);
|
||||||
|
return dispatchContext(toLegacyContext(), tools, thinking);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLMQore::RequestID Session::dispatchContext(
|
||||||
|
Templates::ContextData ctx, bool tools, bool thinking)
|
||||||
|
{
|
||||||
|
auto *provider = m_agent->provider();
|
||||||
|
auto *tmpl = m_agent->promptTemplate();
|
||||||
|
const auto &cfg = m_agent->config();
|
||||||
|
|
||||||
|
QJsonObject payload{{QStringLiteral("model"), cfg.model}};
|
||||||
if (!provider->prepareRequest(payload, tmpl, ctx, tools, thinking))
|
if (!provider->prepareRequest(payload, tmpl, ctx, tools, thinking))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public:
|
|||||||
LLMQore::RequestID sendText(const QString &text);
|
LLMQore::RequestID sendText(const QString &text);
|
||||||
|
|
||||||
LLMQore::RequestID sendCompletion(Templates::ContextData ctx);
|
LLMQore::RequestID sendCompletion(Templates::ContextData ctx);
|
||||||
|
|
||||||
void cancel();
|
void cancel();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -87,6 +87,7 @@ private:
|
|||||||
LLMQore::RequestID dispatch(
|
LLMQore::RequestID dispatch(
|
||||||
std::optional<bool> toolsOverride = std::nullopt,
|
std::optional<bool> toolsOverride = std::nullopt,
|
||||||
std::optional<bool> thinkingOverride = std::nullopt);
|
std::optional<bool> thinkingOverride = std::nullopt);
|
||||||
|
LLMQore::RequestID dispatchContext(Templates::ContextData ctx, bool tools, bool thinking);
|
||||||
Templates::ContextData toLegacyContext() const;
|
Templates::ContextData toLegacyContext() const;
|
||||||
|
|
||||||
Agent *m_agent = nullptr; // child if non-null
|
Agent *m_agent = nullptr; // child if non-null
|
||||||
|
|||||||
Reference in New Issue
Block a user