From 37a34b4f152f4c8a6e836974728a492e9ad2a8ff Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 16 Jun 2025 15:11:03 +0200 Subject: [PATCH] ProjectExplorer: Create AI task handlers dynamically Change-Id: I7495bf7f4001031e84fc29eb852ddc9160d9bd94 Reviewed-by: hjk --- .../coreplugin/customlanguagemodels.cpp | 7 ++- src/plugins/coreplugin/customlanguagemodels.h | 5 +- src/plugins/projectexplorer/taskhandlers.cpp | 52 +++++++++++++++---- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/plugins/coreplugin/customlanguagemodels.cpp b/src/plugins/coreplugin/customlanguagemodels.cpp index b77417f9aca..c6c1bd8a3f0 100644 --- a/src/plugins/coreplugin/customlanguagemodels.cpp +++ b/src/plugins/coreplugin/customlanguagemodels.cpp @@ -244,7 +244,7 @@ CustomLanguageModels::CustomLanguageModels() } // namespace -QStringList availableLanguageModels() +const QStringList availableLanguageModels() { return Utils::transform( customLanguageModels().models.items(), [](const std::shared_ptr &aspect) { @@ -261,6 +261,11 @@ CommandLine commandLineForLanguageModel(const QString &model) return {}; } +BaseAspect &customLanguageModelsContext() +{ + return customLanguageModels(); +} + namespace Internal { void setupCustomLanguageModels() diff --git a/src/plugins/coreplugin/customlanguagemodels.h b/src/plugins/coreplugin/customlanguagemodels.h index 55100eeba04..c13d6fe6262 100644 --- a/src/plugins/coreplugin/customlanguagemodels.h +++ b/src/plugins/coreplugin/customlanguagemodels.h @@ -6,10 +6,13 @@ #include +namespace Utils { class BaseAspect; } + namespace Core { -CORE_EXPORT QStringList availableLanguageModels(); +CORE_EXPORT const QStringList availableLanguageModels(); CORE_EXPORT Utils::CommandLine commandLineForLanguageModel(const QString &model); +CORE_EXPORT Utils::BaseAspect &customLanguageModelsContext(); namespace Internal { diff --git a/src/plugins/projectexplorer/taskhandlers.cpp b/src/plugins/projectexplorer/taskhandlers.cpp index 4479d59e6bc..743a3dabb8a 100644 --- a/src/plugins/projectexplorer/taskhandlers.cpp +++ b/src/plugins/projectexplorer/taskhandlers.cpp @@ -18,18 +18,22 @@ #include #include #include +#include #include #include #include #include +#include +#include + using namespace Core; using namespace Utils; namespace ProjectExplorer { namespace { -static QObject *g_actionParent = nullptr; +static QPointer g_actionParent; static Core::Context g_cmdContext; static Internal::RegisterHandlerAction g_onCreateAction; static Internal::GetHandlerTasks g_getTasks; @@ -248,11 +252,13 @@ private: } }; -// FIXME: There should be one handler per LLM class ExplainWithAiHandler : public ITaskHandler { public: - ExplainWithAiHandler() : ITaskHandler(createAction()) {} + ExplainWithAiHandler(const QString &model) + : ITaskHandler(createAction(model)) + , m_model(model) + {} private: bool canHandle(const Task &task) const override @@ -263,8 +269,8 @@ private: void handle(const Task &task) override { - const QStringList llms = availableLanguageModels(); - QTC_ASSERT(!llms.isEmpty(), return); + const CommandLine cmdLine = commandLineForLanguageModel(m_model); + QTC_ASSERT(!cmdLine.isEmpty(), return); QString prompt; if (task.origin.isEmpty()) @@ -288,7 +294,7 @@ private: } } const auto process = new Process; - process->setCommand(commandLineForLanguageModel(llms.first())); + process->setCommand(cmdLine); process->setProcessMode(ProcessMode::Writer); process->setTextChannelMode(Channel::Output, TextChannelMode::MultiLine); process->setTextChannelMode(Channel::Error, TextChannelMode::MultiLine); @@ -305,16 +311,40 @@ private: process->closeWriteChannel(); }); QTimer::singleShot(60000, process, [process] { process->kill(); }); - MessageManager::writeDisrupting(Tr::tr("Querying LLM...")); + MessageManager::writeDisrupting(Tr::tr("Querying %1...").arg(m_model)); process->start(); } - QAction *createAction() const + QAction *createAction(const QString &model) const { - const auto action = new QAction(Tr::tr("Get help from AI")); - action->setToolTip(Tr::tr("Ask an AI to help with this issue.")); + const auto action = new QAction(Tr::tr("Get help from %1").arg(model)); + action->setToolTip(Tr::tr("Ask the %1 LLM to help with this issue.").arg(model)); return action; } + + const QString m_model; +}; + +class AiHandlersManager +{ +public: + AiHandlersManager() + { + QObject::connect(&customLanguageModelsContext(), &BaseAspect::changed, + g_actionParent, [this] { reset(); }); + reset(); + } + +private: + void reset() + { + m_aiTaskHandlers.clear(); + for (const QString &model : availableLanguageModels()) + m_aiTaskHandlers.emplace_back(std::make_unique(model)); + updateTaskHandlerActionsState(); + } + + std::vector> m_aiTaskHandlers; }; } // namespace @@ -336,7 +366,7 @@ void setupTaskHandlers( static const RemoveTaskHandler removeTaskHandler; static const ShowInEditorTaskHandler showInEditorTaskHandler; static const VcsAnnotateTaskHandler vcsAnnotateTaskHandler; - static const ExplainWithAiHandler explainWithAiHandler; + static const AiHandlersManager aiHandlersManager; registerQueuedTaskHandlers(); }