mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-07-05 20:49:13 -04:00
fix: Found and fix review mistakes
This commit is contained in:
@@ -33,12 +33,12 @@ namespace {
|
||||
|
||||
enum class PillKind {
|
||||
Template,
|
||||
On, // capability on (thinking/tools)
|
||||
Off, // capability off ("plain")
|
||||
User, // user-defined agent
|
||||
Active, // ✓ active
|
||||
Match, // matched-this-row chip background
|
||||
Tag, // free-form discoverability tag from AgentConfig::tags
|
||||
On,
|
||||
Off,
|
||||
User,
|
||||
Active,
|
||||
Match,
|
||||
Tag,
|
||||
Neutral,
|
||||
};
|
||||
|
||||
@@ -192,16 +192,17 @@ class AgentRosterRow : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AgentRosterRow(int index,
|
||||
const QString &name,
|
||||
const AgentConfig *cfg,
|
||||
const QString &model,
|
||||
bool active,
|
||||
bool first,
|
||||
bool last,
|
||||
bool orderable,
|
||||
const Theme &theme,
|
||||
QWidget *parent = nullptr);
|
||||
AgentRosterRow(
|
||||
int index,
|
||||
const QString &name,
|
||||
const AgentConfig *cfg,
|
||||
const QString &model,
|
||||
bool active,
|
||||
bool first,
|
||||
bool last,
|
||||
bool orderable,
|
||||
const Theme &theme,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void moveUpRequested(int index);
|
||||
@@ -221,17 +222,19 @@ private:
|
||||
int m_index;
|
||||
};
|
||||
|
||||
AgentRosterRow::AgentRosterRow(int index,
|
||||
const QString &name,
|
||||
const AgentConfig *cfg,
|
||||
const QString &model,
|
||||
bool active,
|
||||
bool first,
|
||||
bool last,
|
||||
bool orderable,
|
||||
const Theme &theme,
|
||||
QWidget *parent)
|
||||
: QFrame(parent), m_index(index)
|
||||
AgentRosterRow::AgentRosterRow(
|
||||
int index,
|
||||
const QString &name,
|
||||
const AgentConfig *cfg,
|
||||
const QString &model,
|
||||
bool active,
|
||||
bool first,
|
||||
bool last,
|
||||
bool orderable,
|
||||
const Theme &theme,
|
||||
QWidget *parent)
|
||||
: QFrame(parent)
|
||||
, m_index(index)
|
||||
{
|
||||
setAutoFillBackground(true);
|
||||
QPalette pal = palette();
|
||||
@@ -334,8 +337,8 @@ QWidget *AgentRosterRow::buildIdentityLine(const QString &displayName,
|
||||
return w;
|
||||
}
|
||||
|
||||
QWidget *AgentRosterRow::buildMetaLine(const AgentConfig *cfg, bool active, bool showMatch,
|
||||
const Theme &t)
|
||||
QWidget *AgentRosterRow::buildMetaLine(
|
||||
const AgentConfig *cfg, bool active, bool showMatch, const Theme &t)
|
||||
{
|
||||
auto *w = new QWidget(this);
|
||||
auto *line = new QHBoxLayout(w);
|
||||
@@ -352,14 +355,16 @@ QWidget *AgentRosterRow::buildMetaLine(const AgentConfig *cfg, bool active, bool
|
||||
QString chipText = QStringLiteral(
|
||||
"<span style='opacity:0.7'>%1</span> "
|
||||
"<span style='color:%2'>%3:</span> %4")
|
||||
.arg(sm.icon,
|
||||
hex(active ? t.pill(PillKind::Match).fg : t.textFaint),
|
||||
sm.kind,
|
||||
sm.value.toHtmlEscaped());
|
||||
.arg(
|
||||
sm.icon,
|
||||
hex(active ? t.pill(PillKind::Match).fg : t.textFaint),
|
||||
sm.kind,
|
||||
sm.value.toHtmlEscaped());
|
||||
chip->setTextFormat(Qt::RichText);
|
||||
chip->setText(chipText);
|
||||
chip->setStyleSheet(QStringLiteral("background:%1; color:%2; border:1px solid %3;"
|
||||
"padding:1px 6px; border-radius:3px; font-size:11px;")
|
||||
chip->setStyleSheet(QStringLiteral(
|
||||
"background:%1; color:%2; border:1px solid %3;"
|
||||
"padding:1px 6px; border-radius:3px; font-size:11px;")
|
||||
.arg(hex(bg), hex(fg), hex(bd)));
|
||||
chip->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
line->addWidget(chip);
|
||||
@@ -457,14 +462,14 @@ AgentRosterWidget::AgentRosterWidget(QWidget *parent)
|
||||
m_rowsFrame = new QFrame(this);
|
||||
m_rowsFrame->setObjectName(QStringLiteral("rosterCard"));
|
||||
m_rowsFrame->setStyleSheet(
|
||||
QStringLiteral("QFrame#rosterCard { background:%1; border:1px solid %2; border-radius:4px; }")
|
||||
QStringLiteral(
|
||||
"QFrame#rosterCard { background:%1; border:1px solid %2; border-radius:4px; }")
|
||||
.arg(hex(t.cardBg), hex(t.cardBorder)));
|
||||
m_rowsLayout = new QVBoxLayout(m_rowsFrame);
|
||||
m_rowsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_rowsLayout->setSpacing(0);
|
||||
|
||||
m_emptyHint = new QLabel(tr("No agent selected yet — use \"Add agent…\" below."),
|
||||
m_rowsFrame);
|
||||
m_emptyHint = new QLabel(tr("No agent selected yet — use \"Add agent…\" below."), m_rowsFrame);
|
||||
m_emptyHint->setAlignment(Qt::AlignCenter);
|
||||
m_emptyHint->setContentsMargins(10, 12, 10, 12);
|
||||
QFont eh = m_emptyHint->font();
|
||||
@@ -497,9 +502,7 @@ AgentRosterWidget::AgentRosterWidget(QWidget *parent)
|
||||
}
|
||||
|
||||
void AgentRosterWidget::setSlot(
|
||||
const QString &title,
|
||||
const QString &hint,
|
||||
const QStringList &presetTags)
|
||||
const QString &title, const QString &hint, const QStringList &presetTags)
|
||||
{
|
||||
m_presetTags = presetTags;
|
||||
m_titleLabel->setText(title);
|
||||
@@ -514,13 +517,14 @@ void AgentRosterWidget::setRoster(const QStringList &names, AgentFactory *factor
|
||||
QObject::disconnect(m_modelConn);
|
||||
m_factory = factory;
|
||||
if (m_factory) {
|
||||
m_factoryConn = connect(m_factory, &AgentFactory::agentsChanged, this,
|
||||
[this] { rebuildRows(); });
|
||||
m_modelConn = connect(m_factory, &AgentFactory::agentModelChanged, this,
|
||||
[this](const QString &name) {
|
||||
if (m_names.contains(name))
|
||||
rebuildRows();
|
||||
});
|
||||
m_factoryConn = connect(m_factory, &AgentFactory::agentsChanged, this, [this] {
|
||||
rebuildRows();
|
||||
});
|
||||
m_modelConn = connect(
|
||||
m_factory, &AgentFactory::agentModelChanged, this, [this](const QString &name) {
|
||||
if (m_names.contains(name))
|
||||
rebuildRows();
|
||||
});
|
||||
}
|
||||
}
|
||||
m_names = names;
|
||||
@@ -558,6 +562,13 @@ void AgentRosterWidget::applyMode()
|
||||
m_footerHint->setVisible(!footer.isEmpty());
|
||||
}
|
||||
|
||||
void AgentRosterWidget::setRoutingContext(const AgentRouter::Context &ctx)
|
||||
{
|
||||
m_routingCtx = ctx;
|
||||
if (!m_names.isEmpty())
|
||||
rebuildRows();
|
||||
}
|
||||
|
||||
void AgentRosterWidget::recomputeActive()
|
||||
{
|
||||
if (!m_orderable || !m_factory || m_names.isEmpty()
|
||||
@@ -571,7 +582,6 @@ void AgentRosterWidget::recomputeActive()
|
||||
|
||||
void AgentRosterWidget::rebuildRows()
|
||||
{
|
||||
// Tear down existing row widgets (keep the empty hint).
|
||||
while (m_rowsLayout->count() > 0) {
|
||||
QLayoutItem *it = m_rowsLayout->takeAt(0);
|
||||
if (auto *w = it->widget()) {
|
||||
@@ -598,16 +608,17 @@ void AgentRosterWidget::rebuildRows()
|
||||
const QString &name = m_names.at(i);
|
||||
const AgentConfig *cfg = m_factory ? m_factory->configByName(name) : nullptr;
|
||||
const QString model = cfg ? cfg->model : QString();
|
||||
auto *row = new AgentRosterRow(i,
|
||||
name,
|
||||
cfg,
|
||||
model,
|
||||
i == m_activeIndex,
|
||||
/*first*/ i == 0,
|
||||
/*last*/ i == m_names.size() - 1,
|
||||
m_orderable,
|
||||
t,
|
||||
m_rowsFrame);
|
||||
auto *row = new AgentRosterRow(
|
||||
i,
|
||||
name,
|
||||
cfg,
|
||||
model,
|
||||
i == m_activeIndex,
|
||||
/*first*/ i == 0,
|
||||
/*last*/ i == m_names.size() - 1,
|
||||
m_orderable,
|
||||
t,
|
||||
m_rowsFrame);
|
||||
connect(row, &AgentRosterRow::moveUpRequested, this, &AgentRosterWidget::onRowMoveUp);
|
||||
connect(row, &AgentRosterRow::moveDownRequested, this, &AgentRosterWidget::onRowMoveDown);
|
||||
connect(row, &AgentRosterRow::editRequested, this, &AgentRosterWidget::onRowEdit);
|
||||
@@ -621,11 +632,12 @@ void AgentRosterWidget::onAddClicked()
|
||||
if (!m_factory)
|
||||
return;
|
||||
|
||||
AgentSelectionDialog dialog(m_factory->configs(),
|
||||
/*currentName*/ m_single ? m_names.value(0) : QString{},
|
||||
m_factory.data(),
|
||||
m_presetTags,
|
||||
Core::ICore::dialogParent());
|
||||
AgentSelectionDialog dialog(
|
||||
m_factory->configs(),
|
||||
/*currentName*/ m_single ? m_names.value(0) : QString{},
|
||||
m_factory.data(),
|
||||
m_presetTags,
|
||||
Core::ICore::dialogParent());
|
||||
if (dialog.exec() != QDialog::Accepted)
|
||||
return;
|
||||
const QString picked = dialog.selectedName();
|
||||
|
||||
@@ -31,20 +31,12 @@ class AgentRosterWidget : public QWidget
|
||||
public:
|
||||
explicit AgentRosterWidget(QWidget *parent = nullptr);
|
||||
|
||||
void setSlot(
|
||||
const QString &title,
|
||||
const QString &hint,
|
||||
const QStringList &presetTags = {});
|
||||
void setSlot(const QString &title, const QString &hint, const QStringList &presetTags = {});
|
||||
void setRoster(const QStringList &names, AgentFactory *factory);
|
||||
void setRoutingContext(const AgentRouter::Context &ctx);
|
||||
|
||||
// When false, the list is an unordered set: no move arrows, no position
|
||||
// numbers, no "first matching" routing hint. Used by pipelines where the
|
||||
// user — not a router — chooses the agent (e.g. the chat picker).
|
||||
void setOrderable(bool orderable);
|
||||
|
||||
// When true, the card holds at most one agent: "Add" becomes "Change",
|
||||
// selecting replaces the current entry, and the routing footer is hidden.
|
||||
// Implies a non-orderable list. Used by single-agent pipelines.
|
||||
void setSingle(bool single);
|
||||
|
||||
[[nodiscard]] QStringList roster() const { return m_names; }
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#include "AgentSelectionDialog.hpp"
|
||||
|
||||
#include "PipelinesConfig.hpp"
|
||||
#include "Pill.hpp"
|
||||
#include "PipelinesConfig.hpp"
|
||||
#include "SettingsTr.hpp"
|
||||
#include "TagFilterStrip.hpp"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <AgentFactory.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QEnterEvent>
|
||||
#include <QEvent>
|
||||
@@ -27,12 +28,9 @@
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <algorithm>
|
||||
|
||||
namespace QodeAssist::Settings {
|
||||
|
||||
// -- ListRowCard -------------------------------------------------------
|
||||
|
||||
ListRowCard::ListRowCard(QWidget *parent)
|
||||
: QFrame(parent)
|
||||
{
|
||||
@@ -128,16 +126,13 @@ void ListRowCard::applyTheme()
|
||||
.arg(bg, bd));
|
||||
}
|
||||
|
||||
// -- AgentRowCard ------------------------------------------------------
|
||||
|
||||
AgentRowCard::AgentRowCard(const AgentConfig &cfg, QWidget *parent)
|
||||
: ListRowCard(parent)
|
||||
{
|
||||
setItemName(cfg.name);
|
||||
setItemTags(cfg.tags);
|
||||
QStringList haystack{cfg.name, cfg.providerInstance, cfg.model,
|
||||
cfg.description, cfg.systemPrompt,
|
||||
cfg.endpoint};
|
||||
QStringList haystack{
|
||||
cfg.name, cfg.providerInstance, cfg.model, cfg.description, cfg.systemPrompt, cfg.endpoint};
|
||||
haystack += cfg.tags;
|
||||
buildSearchHaystack(haystack);
|
||||
|
||||
@@ -249,8 +244,6 @@ AgentRowCard::AgentRowCard(const AgentConfig &cfg, QWidget *parent)
|
||||
setToolTip(tooltip.trimmed());
|
||||
}
|
||||
|
||||
// -- ProviderSection ---------------------------------------------------
|
||||
|
||||
ProviderSection::ProviderSection(const QString &name, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
@@ -337,8 +330,6 @@ bool ProviderSection::eventFilter(QObject *watched, QEvent *event)
|
||||
return QWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
// -- AgentSelectionDialog ----------------------------------------------
|
||||
|
||||
AgentSelectionDialog::AgentSelectionDialog(
|
||||
const std::vector<AgentConfig> &configs,
|
||||
const QString ¤tName,
|
||||
@@ -380,10 +371,10 @@ AgentSelectionDialog::AgentSelectionDialog(
|
||||
m_resultCount->setPalette(rp);
|
||||
}
|
||||
|
||||
auto *expandAll = new QLabel(
|
||||
QStringLiteral("<a href=\"#\">%1</a>").arg(Tr::tr("Expand all")), this);
|
||||
auto *collapseAll = new QLabel(
|
||||
QStringLiteral("<a href=\"#\">%1</a>").arg(Tr::tr("Collapse all")), this);
|
||||
auto *expandAll
|
||||
= new QLabel(QStringLiteral("<a href=\"#\">%1</a>").arg(Tr::tr("Expand all")), this);
|
||||
auto *collapseAll
|
||||
= new QLabel(QStringLiteral("<a href=\"#\">%1</a>").arg(Tr::tr("Collapse all")), this);
|
||||
|
||||
auto *controlsRow = new QHBoxLayout;
|
||||
controlsRow->setContentsMargins(2, 0, 2, 0);
|
||||
@@ -425,8 +416,9 @@ AgentSelectionDialog::AgentSelectionDialog(
|
||||
preset.insert(tag);
|
||||
m_tagStrip->setAvailableTags(tagCounts, preset);
|
||||
|
||||
connect(m_tagStrip, &TagFilterStrip::activeTagsChanged, this,
|
||||
[this](const QSet<QString> &) { applyFilters(); });
|
||||
connect(m_tagStrip, &TagFilterStrip::activeTagsChanged, this, [this](const QSet<QString> &) {
|
||||
applyFilters();
|
||||
});
|
||||
connect(expandAll, &QLabel::linkActivated, this, [this](const QString &) {
|
||||
setAllExpanded(true);
|
||||
});
|
||||
@@ -436,8 +428,7 @@ AgentSelectionDialog::AgentSelectionDialog(
|
||||
|
||||
rebuild(currentName);
|
||||
|
||||
connect(m_filter, &QLineEdit::textChanged, this,
|
||||
[this](const QString &) { applyFilters(); });
|
||||
connect(m_filter, &QLineEdit::textChanged, this, [this](const QString &) { applyFilters(); });
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
@@ -486,7 +477,8 @@ void AgentSelectionDialog::rebuild(const QString ¤tName)
|
||||
|
||||
QMap<QString, std::vector<const AgentConfig *>> byProvider;
|
||||
for (const auto &cfg : configs) {
|
||||
if (cfg.hidden) continue; // hidden profiles stay loaded but don't surface in the picker
|
||||
if (cfg.hidden)
|
||||
continue;
|
||||
const QString key = cfg.providerInstance.isEmpty()
|
||||
? Tr::tr("(Unknown provider instance)")
|
||||
: cfg.providerInstance;
|
||||
@@ -565,8 +557,7 @@ void AgentSelectionDialog::applyFilters()
|
||||
if (m_emptyLabel)
|
||||
m_emptyLabel->setVisible(total == 0);
|
||||
if (m_resultCount)
|
||||
m_resultCount->setText(total == 0 ? tr("No matches")
|
||||
: tr("%n agent(s)", nullptr, total));
|
||||
m_resultCount->setText(total == 0 ? tr("No matches") : tr("%n agent(s)", nullptr, total));
|
||||
|
||||
if (m_tagStrip) {
|
||||
QMap<QString, int> liveCounts;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <QDialog>
|
||||
#include <QFont>
|
||||
#include <QFrame>
|
||||
@@ -11,7 +12,6 @@
|
||||
#include <QPalette>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
#include <vector>
|
||||
|
||||
#include <AgentConfig.hpp>
|
||||
|
||||
@@ -142,7 +142,6 @@ public:
|
||||
void setExpanded(bool expanded);
|
||||
const QList<ListRowCard *> &cards() const { return m_cards; }
|
||||
|
||||
// returns number of visible cards
|
||||
int applyFilter(const QString &needle, const QSet<QString> &activeTags);
|
||||
|
||||
protected:
|
||||
@@ -189,7 +188,7 @@ private:
|
||||
QString m_selectedName;
|
||||
|
||||
AgentFactory *m_agentFactory = nullptr;
|
||||
std::vector<AgentConfig> m_localConfigs; // fallback when no factory
|
||||
std::vector<AgentConfig> m_localConfigs;
|
||||
QStringList m_presetTags;
|
||||
};
|
||||
|
||||
|
||||
@@ -87,9 +87,10 @@ void Pill::applyTheme()
|
||||
QScopedValueRollback<bool> guard(m_inApplyTheme, true);
|
||||
|
||||
const PillTone t = toneFor(m_kind);
|
||||
setStyleSheet(QStringLiteral("QLabel { background-color:%1; color:%2;"
|
||||
" border:1px solid %3; border-radius:3px;"
|
||||
" padding:1px 7px; font-size:11px; }")
|
||||
setStyleSheet(QStringLiteral(
|
||||
"QLabel { background-color:%1; color:%2;"
|
||||
" border:1px solid %3; border-radius:3px;"
|
||||
" padding:1px 7px; font-size:11px; }")
|
||||
.arg(cssColor(t.bg), cssColor(t.fg), cssColor(t.border)));
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
|
||||
namespace QodeAssist::Settings {
|
||||
|
||||
// Small rounded chip. Theme-aware: recolors itself on palette/style changes.
|
||||
// All colors derive from the active Qt Creator theme (Utils::Theme).
|
||||
class Pill : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "Logger.hpp"
|
||||
#include "TomlWriter.hpp"
|
||||
#include <AgentFactory.hpp>
|
||||
|
||||
namespace QodeAssist::Settings {
|
||||
|
||||
@@ -45,8 +44,6 @@ QString toSingleString(const toml::node *node, const QString &slotKey, bool *sch
|
||||
return {};
|
||||
if (const auto *s = node->as_string())
|
||||
return trimAndCap(QString::fromStdString(s->get()));
|
||||
// Backward compatibility: older pipelines.toml stored these slots as an
|
||||
// ordered array. Collapse to the first usable name.
|
||||
if (const auto *arr = node->as_array()) {
|
||||
for (size_t i = 0; i < arr->size(); ++i) {
|
||||
if (const auto *s = (*arr)[i].as_string()) {
|
||||
@@ -115,19 +112,53 @@ void fillMissingFromDefaults(PipelineRosters &r, const toml::table §ion)
|
||||
r.quickRefactor = defs.quickRefactor;
|
||||
}
|
||||
|
||||
struct CacheState
|
||||
{
|
||||
PipelinesLoadResult result;
|
||||
QDateTime mtime;
|
||||
qint64 size = -1;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
CacheState &cacheState()
|
||||
{
|
||||
static CacheState s;
|
||||
return s;
|
||||
}
|
||||
|
||||
QString &filePathOverride()
|
||||
{
|
||||
static QString p;
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PipelineRosters PipelineRosters::defaults()
|
||||
{
|
||||
return PipelineRosters{};
|
||||
return PipelineRosters{
|
||||
{QStringLiteral("Ollama Completion — FIM")},
|
||||
{QStringLiteral("Ollama Chat — Simple"),
|
||||
QStringLiteral("Ollama Chat — Thinking"),
|
||||
QStringLiteral("Ollama Chat — Gemma 4")},
|
||||
QStringLiteral("Ollama Compression — 8 GB"),
|
||||
QStringLiteral("Ollama Quick Refactor — Simple")};
|
||||
}
|
||||
|
||||
QString PipelinesConfig::filePath()
|
||||
{
|
||||
if (!filePathOverride().isEmpty())
|
||||
return filePathOverride();
|
||||
return Core::ICore::userResourcePath(QStringLiteral("qodeassist/config/pipelines.toml"))
|
||||
.toFSPathString();
|
||||
}
|
||||
|
||||
void PipelinesConfig::setFilePathForTests(const QString &path)
|
||||
{
|
||||
filePathOverride() = path;
|
||||
cacheState() = {};
|
||||
}
|
||||
|
||||
PipelinesLoadResult PipelinesConfig::load()
|
||||
{
|
||||
PipelinesLoadResult result;
|
||||
@@ -206,19 +237,18 @@ PipelinesLoadResult PipelinesConfig::load()
|
||||
|
||||
PipelinesLoadResult PipelinesConfig::loadCached()
|
||||
{
|
||||
static PipelinesLoadResult cached;
|
||||
static QDateTime cachedMTime;
|
||||
static bool valid = false;
|
||||
|
||||
auto &cache = cacheState();
|
||||
const QFileInfo info(filePath());
|
||||
const QDateTime mtime = info.exists() ? info.lastModified() : QDateTime();
|
||||
if (valid && mtime == cachedMTime)
|
||||
return cached;
|
||||
const qint64 size = info.exists() ? info.size() : -1;
|
||||
if (cache.valid && mtime == cache.mtime && size == cache.size)
|
||||
return cache.result;
|
||||
|
||||
cached = load();
|
||||
cachedMTime = mtime;
|
||||
valid = true;
|
||||
return cached;
|
||||
cache.result = load();
|
||||
cache.mtime = mtime;
|
||||
cache.size = size;
|
||||
cache.valid = true;
|
||||
return cache.result;
|
||||
}
|
||||
|
||||
bool PipelinesConfig::save(const PipelineRosters &rosters, QString *errorOut)
|
||||
@@ -236,15 +266,14 @@ bool PipelinesConfig::save(const PipelineRosters &rosters, QString *errorOut)
|
||||
|
||||
TomlSerializer::TomlWriter w;
|
||||
w.writeComment(QStringLiteral("QodeAssist pipelines — which agent each feature uses."));
|
||||
w.writeComment(QStringLiteral(
|
||||
"code_completion: ordered list; the router walks it top-down and uses"));
|
||||
w.writeComment(QStringLiteral(
|
||||
" the first agent whose match rules fit the current file/project."));
|
||||
w.writeComment(QStringLiteral(
|
||||
"chat_assistant: agents offered in the chat picker (order irrelevant —"));
|
||||
w.writeComment(
|
||||
QStringLiteral("code_completion: ordered list; the router walks it top-down and uses"));
|
||||
w.writeComment(
|
||||
QStringLiteral(" the first agent whose match rules fit the current file/project."));
|
||||
w.writeComment(
|
||||
QStringLiteral("chat_assistant: agents offered in the chat picker (order irrelevant —"));
|
||||
w.writeComment(QStringLiteral(" you choose one in the UI)."));
|
||||
w.writeComment(QStringLiteral(
|
||||
"chat_compression / quick_refactor: a single agent name."));
|
||||
w.writeComment(QStringLiteral("chat_compression / quick_refactor: a single agent name."));
|
||||
w.writeBlankLine();
|
||||
w.writeTableHeader(QString::fromUtf8(kSection));
|
||||
w.writeStringArray(QString::fromUtf8(kCodeCompletion), rosters.codeCompletion);
|
||||
@@ -276,56 +305,8 @@ bool PipelinesConfig::save(const PipelineRosters &rosters, QString *errorOut)
|
||||
*errorOut = out.errorString();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PipelinesConfig::validate(const QodeAssist::AgentFactory &factory, QString *errorOut)
|
||||
{
|
||||
PipelinesLoadResult lr = load();
|
||||
PipelineRosters &r = lr.rosters;
|
||||
bool changed = false;
|
||||
|
||||
auto fix = [&](QStringList ¤t) {
|
||||
QStringList kept;
|
||||
kept.reserve(current.size());
|
||||
for (const QString &raw : current) {
|
||||
const QString name = trimAndCap(raw);
|
||||
if (name.isEmpty() || kept.contains(name))
|
||||
continue;
|
||||
if (factory.configByName(name))
|
||||
kept.append(name);
|
||||
}
|
||||
if (kept != current) {
|
||||
current = std::move(kept);
|
||||
changed = true;
|
||||
}
|
||||
};
|
||||
|
||||
auto fixOne = [&](QString ¤t) {
|
||||
const QString name = trimAndCap(current);
|
||||
const QString next = (!name.isEmpty() && factory.configByName(name)) ? name : QString();
|
||||
if (next != current) {
|
||||
current = next;
|
||||
changed = true;
|
||||
}
|
||||
};
|
||||
|
||||
fix(r.codeCompletion);
|
||||
fix(r.chatAssistant);
|
||||
fixOne(r.chatCompression);
|
||||
fixOne(r.quickRefactor);
|
||||
|
||||
if (!changed && lr.status == PipelinesLoadStatus::Ok)
|
||||
return true;
|
||||
|
||||
QString saveErr;
|
||||
if (!save(r, &saveErr)) {
|
||||
const QString msg = QStringLiteral("failed to persist after validation: %1").arg(saveErr);
|
||||
LOG_MESSAGE(QStringLiteral("[Pipelines] %1").arg(msg));
|
||||
if (errorOut)
|
||||
*errorOut = msg;
|
||||
return false;
|
||||
}
|
||||
cacheState() = {};
|
||||
PipelinesNotifier::instance()->notifyChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,24 +4,35 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
namespace QodeAssist {
|
||||
class AgentFactory;
|
||||
}
|
||||
|
||||
namespace QodeAssist::Settings {
|
||||
|
||||
class PipelinesNotifier : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static PipelinesNotifier *instance()
|
||||
{
|
||||
static PipelinesNotifier notifier;
|
||||
return ¬ifier;
|
||||
}
|
||||
|
||||
void notifyChanged() { emit pipelinesChanged(); }
|
||||
|
||||
signals:
|
||||
void pipelinesChanged();
|
||||
|
||||
private:
|
||||
PipelinesNotifier() = default;
|
||||
};
|
||||
|
||||
struct PipelineRosters
|
||||
{
|
||||
// Code completion is auto-routed: the router walks this ordered list at
|
||||
// request time and uses the first agent whose match rules fit the file.
|
||||
QStringList codeCompletion;
|
||||
// Chat is user-driven: this is an unordered allow-list of the agents
|
||||
// offered in the chat picker. The user picks; no routing happens.
|
||||
QStringList chatAssistant;
|
||||
// Compression and quick refactor each use a single fixed agent.
|
||||
QString chatCompression;
|
||||
QString quickRefactor;
|
||||
|
||||
@@ -48,8 +59,7 @@ public:
|
||||
|
||||
[[nodiscard]] static bool save(const PipelineRosters &rosters, QString *errorOut = nullptr);
|
||||
|
||||
[[nodiscard]] static bool validate(
|
||||
const QodeAssist::AgentFactory &factory, QString *errorOut = nullptr);
|
||||
static void setFilePathForTests(const QString &path);
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Settings
|
||||
|
||||
Reference in New Issue
Block a user