propagate response IDs through chat models to reactTo

This commit is contained in:
Levi Neely 2026-07-27 19:12:12 +02:00
parent 16364e52b0
commit e2592d821f
6 changed files with 23 additions and 7 deletions

View File

@ -706,7 +706,7 @@ Item {
font.pixelSize: 14
implicitWidth: 28
implicitHeight: 28
onClicked: dbus.react(paneRoot.sessionId, modelData.emoji)
onClicked: dbus.reactTo(paneRoot.sessionId, block.responseId, modelData.emoji)
ToolTip.text: modelData.tip
ToolTip.visible: hovered
}

View File

@ -64,6 +64,8 @@ QVariant ChatBlockModel::data(const QModelIndex &index, int role) const
return block.name;
case ContentRole:
return block.content;
case ResponseIdRole:
return block.responseId;
case SubBlocksRole:
if (block.cachedContent != block.content) {
block.cachedSubBlocks = parseSubBlocks(block.content);
@ -102,6 +104,7 @@ QHash<int, QByteArray> ChatBlockModel::roleNames() const
{ContentRole, "blockContent"},
{SubBlocksRole, "subBlocks"},
{FormattedContentRole, "formattedContent"},
{ResponseIdRole, "responseId"},
};
}
@ -174,7 +177,8 @@ void ChatBlockModel::updateVisibleWindow()
for (int i = 0; i < commonCount; ++i) {
if (m_blocks[i].content != newBlocks[i].content ||
m_blocks[i].type != newBlocks[i].type ||
m_blocks[i].name != newBlocks[i].name) {
m_blocks[i].name != newBlocks[i].name ||
m_blocks[i].responseId != newBlocks[i].responseId) {
firstDiff = i;
break;
}
@ -228,7 +232,7 @@ QList<ChatBlock> ChatBlockModel::parseBlocks(const QString &text) const
QList<ChatBlock> blocks;
if (text.isEmpty()) return blocks;
// Block headers: [user], [assistant], [reasoning], [call:name], [tool:name], [error], [info] msg
// Block headers: [user], [assistant:id], [reasoning], [call:name], etc.
static const QRegularExpression headerRe(
R"(^\[(user|assistant|reasoning|call|tool|error|info)(?::([^\]]*))?\](.*)$)",
QRegularExpression::MultilineOption);
@ -280,7 +284,10 @@ QList<ChatBlock> ChatBlockModel::parseBlocks(const QString &text) const
else if (typeStr == "error") b.type = ChatBlock::Error;
else if (typeStr == "info") b.type = ChatBlock::Info;
b.name = name;
if (b.type == ChatBlock::Assistant)
b.responseId = name;
else
b.name = name;
b.content = content;
blocks.append(b);
}

View File

@ -18,8 +18,9 @@ struct ChatBlock {
};
Type type;
QString name; // tool/call name, empty for others
QString content; // raw text content
QString name; // tool/call name, empty for others
QString responseId; // stable assistant response ID
QString content; // raw text content
mutable QVariantList cachedSubBlocks; // cached parseSubBlocks result
mutable QString cachedContent; // content when subBlocks were cached
};
@ -51,6 +52,7 @@ public:
ContentRole,
SubBlocksRole, // QVariantList of sub-block maps
FormattedContentRole, // pre-formatted HTML for call/tool expanded view
ResponseIdRole,
};
explicit ChatBlockModel(QObject *parent = nullptr);

View File

@ -1531,7 +1531,7 @@ ApplicationWindow {
font.pixelSize: 14
implicitWidth: 28
implicitHeight: 28
onClicked: dbus.react(paneRoot.sessionId, modelData.emoji)
onClicked: dbus.reactTo(paneRoot.sessionId, block.responseId, modelData.emoji)
ToolTip.text: modelData.tip
ToolTip.visible: hovered
}

View File

@ -463,6 +463,12 @@ bool OllieDBusClient::react(const QString &sessionId, const QString &emoji)
return true;
}
bool OllieDBusClient::reactTo(const QString &sessionId, const QString &responseId, const QString &emoji)
{
m_iface->asyncCall("ReactTo", sessionId, responseId, emoji);
return true;
}
bool OllieDBusClient::interrupt(const QString &sessionId)
{
m_iface->asyncCall("Interrupt", sessionId);

View File

@ -54,6 +54,7 @@ public:
Q_INVOKABLE bool setConfig(const QString &sessionId, const QString &key, const QString &value);
Q_INVOKABLE bool submit(const QString &sessionId, const QString &prompt);
Q_INVOKABLE bool react(const QString &sessionId, const QString &emoji);
Q_INVOKABLE bool reactTo(const QString &sessionId, const QString &responseId, const QString &emoji);
Q_INVOKABLE bool interrupt(const QString &sessionId);
Q_INVOKABLE QStringList listBackends();
Q_INVOKABLE QStringList listModels(const QString &sessionId);