Skip to content

Commit 66c83a9

Browse files
committed
Do not remove the current pending message when fully received
This way the flicker when the response is received is not present. Also made sure that every started conversation will have a conversation id from the get go, and not after one message has been sent.
1 parent 61b86ff commit 66c83a9

11 files changed

+159
-50
lines changed

llamachateditor.cpp

Lines changed: 91 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -357,14 +357,90 @@ void ChatEditor::refreshMessages(const QVector<Message> &messages, qint64 leafNo
357357
scrollToBottom();
358358
}
359359

360-
void ChatEditor::onMessageAppended(const Message &msg)
360+
void ChatEditor::onMessageAppended(const Message &msg, qint64 pendingId)
361361
{
362-
Conversation c = ChatManager::instance().currentConversation();
363-
ViewingChat chat = ChatManager::instance().getViewingChat(c.id);
364-
refreshMessages(chat.messages, msg.id);
362+
ViewingChat chat = ChatManager::instance().getViewingChat(msg.convId);
363+
if (pendingId < 0) {
364+
refreshMessages(chat.messages, msg.id);
365365

366-
m_input->setIsGenerating(false);
366+
m_input->setIsGenerating(false);
367+
scrollToBottom();
368+
return;
369+
}
370+
371+
// Delete old server‑props widget if it exists
372+
if (m_propsWidget) {
373+
m_propsWidget->deleteLater();
374+
m_propsWidget = nullptr;
375+
}
367376

377+
if (m_followUpWidget) {
378+
m_followUpWidget->deleteLater();
379+
m_followUpWidget = nullptr;
380+
}
381+
382+
QMap<qint64, Message> map;
383+
for (const Message &m : chat.messages)
384+
map.insert(m.id, m);
385+
386+
// Skip root / orphan messages
387+
if (msg.type == "root")
388+
return;
389+
390+
int siblingIdx = 1;
391+
QVector<qint64> siblings;
392+
if (msg.parent >= 0) {
393+
const Message *parent = &map[msg.parent];
394+
for (qint64 cid : parent->children) {
395+
siblings.append(cid);
396+
if (msg.id == cid)
397+
siblingIdx = siblings.size(); // 1‑based index
398+
}
399+
}
400+
401+
// Find the leaf of each sibling
402+
QVector<qint64> leafs;
403+
for (qint64 cid : siblings) {
404+
const Message *cur = &map[cid];
405+
while (!cur->children.isEmpty())
406+
cur = &map[cur->children.back()];
407+
leafs.append(cur->id);
408+
}
409+
410+
ChatMessage *w{nullptr};
411+
auto it = std::find_if(m_messageWidgets.begin(),
412+
m_messageWidgets.end(),
413+
[this, msg, pendingId](ChatMessage *cm) {
414+
return cm->message().id == (pendingId < 0 ? msg.id : pendingId);
415+
});
416+
if (it == m_messageWidgets.end()) {
417+
w = new ChatMessage(msg, leafs, siblingIdx, widget());
418+
connect(w, &ChatMessage::editRequested, this, &ChatEditor::onEditRequested);
419+
connect(w, &ChatMessage::regenerateRequested, this, &ChatEditor::onRegenerateRequested);
420+
connect(w, &ChatMessage::siblingChanged, this, &ChatEditor::onSiblingChanged);
421+
422+
m_messageLayout->addWidget(w);
423+
m_messageWidgets.append(w);
424+
425+
// Speed‑label handling for assistant messages
426+
if (settings().showTokensPerSecond.value() && !w->isUser()) {
427+
m_messageLayout->addWidget(m_speedLabel);
428+
m_speedLabel->setVisible(true);
429+
}
430+
} else {
431+
w = *it;
432+
w->message() = msg;
433+
434+
w->setSiblingIdx(siblingIdx);
435+
w->setSiblingLeafIds(siblings);
436+
437+
w->renderMarkdown(msg.content);
438+
w->messageCompleted(true);
439+
}
440+
441+
updateSpeedLabel(msg);
442+
443+
m_input->setIsGenerating(false);
368444
scrollToBottom();
369445
}
370446

@@ -464,13 +540,19 @@ void ChatEditor::onEditingCancelled()
464540

465541
void ChatEditor::onRegenerateRequested(const Message &msg)
466542
{
543+
Message msgCopy = msg;
467544
ViewingChat chat = ChatManager::instance().getViewingChat(msg.convId);
468545

469-
ChatManager::instance().replaceMessageAndGenerate(chat.conv.id,
470-
msg.parent,
546+
// refreshMessages will invalidate msg, which part of a ChatMessage object
547+
refreshMessages(chat.messages, msgCopy.parent);
548+
549+
ChatManager::instance().replaceMessageAndGenerate(msgCopy.convId,
550+
msgCopy.parent,
471551
QString(),
472-
msg.extra,
473-
[this](qint64 leafId) { scrollToBottom(); });
552+
msgCopy.extra,
553+
[this, msgCopy](qint64 leafId) {
554+
scrollToBottom();
555+
});
474556

475557
m_userInteracted = false;
476558
}

llamachateditor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class ChatEditor : public Core::IEditor
4141
const QStringList &questions);
4242

4343
public slots:
44-
void onMessageAppended(const LlamaCpp::Message &msg);
44+
void onMessageAppended(const LlamaCpp::Message &msg, qint64 pendingId);
4545
void onPendingMessageChanged(const LlamaCpp::Message &pm);
4646
void onSendRequested(const QString &text, const QList<QVariantMap> &extra);
4747
void onStopRequested();

llamachatmanager.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ void ChatManager::sendMessage(const QString &convId,
127127
const QList<QVariantMap> &extra,
128128
std::function<void(qint64)> onChunk)
129129
{
130-
if (isGenerating(convId) || content.trimmed().isEmpty())
130+
if (isGenerating(convId) || content.trimmed().isEmpty() || convId.isEmpty())
131131
return;
132132

133133
Message newMsg;
@@ -141,13 +141,6 @@ void ChatManager::sendMessage(const QString &convId,
141141
newMsg.children.clear();
142142
newMsg.extra = extra; // simple wrapper – see MessageExtra
143143

144-
// create conversation if needed
145-
if (newMsg.convId.isEmpty() || newMsg.convId.isNull()) {
146-
Conversation c = m_storage->createConversation(content.left(256));
147-
newMsg.convId = c.id;
148-
leafNodeId = c.currNode;
149-
}
150-
151144
m_storage->appendMsg(newMsg, leafNodeId);
152145
onChunk(newMsg.id);
153146

@@ -201,7 +194,7 @@ void ChatManager::generateMessage(const QString &convId,
201194

202195
// prepare pending msg
203196
Message pending{};
204-
pending.id = QDateTime::currentMSecsSinceEpoch() + 1; // not to have the same id as the user
197+
pending.id = QDateTime::currentMSecsSinceEpoch();
205198
pending.convId = convId;
206199
pending.type = "text";
207200
pending.timestamp = pending.id;

llamachatmanager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class ChatManager : public QObject
6060
std::function<void(const QStringList &)> onSuccess);
6161
signals:
6262
// emitted when the active conversation changes – UI can react
63-
void messageAppended(const LlamaCpp::Message &msg);
63+
void messageAppended(const LlamaCpp::Message &msg, qint64 pendingId);
6464
void pendingMessageChanged(const LlamaCpp::Message &msg);
6565

6666
void conversationCreated(const QString &convId);

llamachatmessage.cpp

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -181,26 +181,22 @@ void ChatMessage::buildUI()
181181
connect(m_attachedFiles, &QToolButton::clicked, m_attachedFiles, &QToolButton::showMenu);
182182
}
183183

184-
if (m_siblingLeafIds.size() > 1) {
185-
m_prevButton = new QToolButton(this);
186-
m_prevButton->setText("C");
187-
m_prevButton->setEnabled(m_siblingIdx > 1);
188-
m_prevButton->setToolTip(Tr::tr("Go to previous message"));
189-
connect(m_prevButton, &QToolButton::clicked, this, &ChatMessage::onPrevSiblingClicked);
190-
191-
m_siblingLabel = new QLabel(this);
192-
m_siblingLabel->setText(QString("%1/%2").arg(m_siblingIdx).arg(m_siblingLeafIds.size()));
193-
194-
m_nextButton = new QToolButton(this);
195-
m_nextButton->setText("D");
196-
m_nextButton->setEnabled(m_siblingIdx < m_siblingLeafIds.size());
197-
m_nextButton->setToolTip(Tr::tr("Go to next message"));
198-
connect(m_nextButton, &QToolButton::clicked, this, &ChatMessage::onNextSiblingClicked);
199-
200-
actionLayout->addWidget(m_prevButton);
201-
actionLayout->addWidget(m_siblingLabel);
202-
actionLayout->addWidget(m_nextButton);
203-
}
184+
m_prevButton = new QToolButton(this);
185+
m_prevButton->setText("C");
186+
m_prevButton->setToolTip(Tr::tr("Go to previous message"));
187+
connect(m_prevButton, &QToolButton::clicked, this, &ChatMessage::onPrevSiblingClicked);
188+
189+
m_siblingLabel = new QLabel(this);
190+
191+
m_nextButton = new QToolButton(this);
192+
m_nextButton->setText("D");
193+
m_nextButton->setToolTip(Tr::tr("Go to next message"));
194+
connect(m_nextButton, &QToolButton::clicked, this, &ChatMessage::onNextSiblingClicked);
195+
actionLayout->addWidget(m_prevButton);
196+
actionLayout->addWidget(m_siblingLabel);
197+
actionLayout->addWidget(m_nextButton);
198+
199+
updateUI();
204200

205201
if (m_isUser) {
206202
m_editButton = new QToolButton(this);
@@ -257,8 +253,8 @@ void ChatMessage::renderMarkdown(const QString &text)
257253
if (m_thoughtToggle) {
258254
auto [thinking, message] = ThinkingSectionParser::parseThinkingSection(text);
259255
if (m_thoughtToggle->isChecked()) {
260-
m_markdownLabel->setMarkdown(
261-
ThinkingSectionParser::formatThinkingContent(thinking) + "\n\n" + message);
256+
m_markdownLabel->setMarkdown(ThinkingSectionParser::formatThinkingContent(thinking)
257+
+ "\n\n" + message);
262258
} else {
263259
m_markdownLabel->setMarkdown(message);
264260
}
@@ -345,6 +341,17 @@ void ChatMessage::applyStyleSheet()
345341
)"));
346342
}
347343

344+
void ChatMessage::setSiblingIdx(int newSiblingIdx)
345+
{
346+
m_siblingIdx = newSiblingIdx;
347+
}
348+
349+
void ChatMessage::setSiblingLeafIds(const QVector<qint64> &newSiblingLeafIds)
350+
{
351+
m_siblingLeafIds = newSiblingLeafIds;
352+
updateUI();
353+
}
354+
348355
void ChatMessage::onCopyClicked()
349356
{
350357
QGuiApplication::clipboard()->setText(m_msg.content);
@@ -428,4 +435,16 @@ void ChatMessage::onSaveToDisk(const QString &fileName, const QString &verbatimC
428435

429436
sourceFile.writeFileContents(verbatimCode.toUtf8());
430437
}
438+
439+
void ChatMessage::updateUI()
440+
{
441+
m_siblingLabel->setText(QString("%1/%2").arg(m_siblingIdx).arg(m_siblingLeafIds.size()));
442+
m_prevButton->setEnabled(m_siblingIdx > 1);
443+
m_nextButton->setEnabled(m_siblingIdx < m_siblingLeafIds.size());
444+
445+
m_prevButton->setVisible(m_siblingLeafIds.size() > 1);
446+
m_siblingLabel->setVisible(m_siblingLeafIds.size() > 1);
447+
m_nextButton->setVisible(m_siblingLeafIds.size() > 1);
448+
}
449+
431450
} // namespace LlamaCpp

llamachatmessage.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ class ChatMessage : public QWidget
2727

2828
void messageCompleted(bool completed);
2929
bool isUser() const;
30+
void setSiblingLeafIds(const QVector<qint64> &newSiblingLeafIds);
31+
32+
void setSiblingIdx(int newSiblingIdx);
33+
3034
signals:
3135
void regenerateRequested(const Message &msg);
3236
void editRequested(const Message &msg);
@@ -44,6 +48,7 @@ private slots:
4448

4549
private:
4650
void buildUI();
51+
void updateUI();
4752
void resizeEvent(QResizeEvent *ev) override;
4853
bool eventFilter(QObject *obj, QEvent *event) override;
4954

llamaconversationsview.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,11 @@ void ConversationsView::refresh()
186186
void ConversationsView::newConversation()
187187
{
188188
QString title("llama.cpp coversation");
189-
Core::EditorManager::openEditorWithContents(Constants::LLAMACPP_VIEWER_ID, &title);
189+
Conversation c = ChatManager::instance().createConversation(title);
190+
Core::EditorManager::openEditorWithContents(Constants::LLAMACPP_VIEWER_ID,
191+
&title,
192+
c.id.toUtf8(),
193+
c.id);
190194
}
191195

192196
void ConversationsView::showEvent(QShowEvent *)

llamalocatorfilter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ void LocatorFilter::acceptPrompt(const QString &prompt)
136136

137137
Core::EditorManager::openEditorWithContents(Constants::LLAMACPP_VIEWER_ID,
138138
&c.name,
139-
c.id.toUtf8());
139+
c.id.toUtf8(),
140+
c.id);
140141

141142
if (!m_predefined.contains(prompt)) {
142143
m_history.removeAll(prompt);

llamaplugin.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@
3333
#include <QToolButton>
3434
#include <QTranslator>
3535

36-
#include "llamaplugin.h"
3736
#include "llamachateditor.h"
38-
#include "llamaconversationsview.h"
37+
#include "llamachatmanager.h"
3938
#include "llamaconstants.h"
39+
#include "llamaconversationsview.h"
4040
#include "llamaicons.h"
4141
#include "llamalocatorfilter.h"
42+
#include "llamaplugin.h"
4243
#include "llamaprojectpanel.h"
4344
#include "llamasettings.h"
4445
#include "llamatr.h"
4546

46-
4747
using namespace Core;
4848
using namespace TextEditor;
4949
using namespace Utils;
@@ -115,7 +115,11 @@ void LlamaPlugin::initialize()
115115
= ActionManager::registerAction(&m_newConversation, Constants::LLAMACPP_NEW_CONVERSATION);
116116
connect(&m_newConversation, &QAction::triggered, this, [this] {
117117
QString title(Tr::tr("llama.cpp coversation"));
118-
Core::EditorManager::openEditorWithContents(Constants::LLAMACPP_VIEWER_ID, &title);
118+
Conversation c = ChatManager::instance().createConversation(title);
119+
Core::EditorManager::openEditorWithContents(Constants::LLAMACPP_VIEWER_ID,
120+
&title,
121+
c.id.toUtf8(),
122+
c.id);
119123
});
120124
ActionManager::actionContainer(Constants::LLAMACPP_MENU_ID)->addAction(newConversationCmd);
121125

llamastorage.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ void Storage::appendMsg(Message &msg, qint64 parentNodeId)
279279
<< q.lastError();
280280

281281
// Get the auto-generated ID
282+
qint64 pendingId = msg.role == "assistant" ? msg.id : -1;
282283
msg.id = q.lastInsertId().toLongLong();
283284

284285
// update parent children
@@ -313,7 +314,7 @@ void Storage::appendMsg(Message &msg, qint64 parentNodeId)
313314

314315
db.commit();
315316

316-
emit messageAppended(msg);
317+
emit messageAppended(msg, pendingId);
317318
}
318319

319320
QVector<Message> Storage::filterByLeafNodeId(const QVector<Message> &msgs,

0 commit comments

Comments
 (0)