gui: incremental chat parsing during streaming
reparseIncremental() only re-parses from the last block boundary instead of the entire chat text. Fixes UI lag and typing blockage during long streaming responses. Also extracted updateVisibleWindow() for reuse.
This commit is contained in:
parent
e021724c52
commit
7ccce95445
|
|
@ -107,17 +107,16 @@ void ChatBlockModel::setRawText(const QString &text)
|
|||
{
|
||||
if (text == m_rawText) return;
|
||||
|
||||
// If text is just appended (common during streaming), only reparse the last block
|
||||
// If text is just appended (common during streaming), only reparse from the last block
|
||||
bool isAppend = text.startsWith(m_rawText) && !m_rawText.isEmpty();
|
||||
m_rawText = text;
|
||||
|
||||
if (isAppend && !m_allBlocks.isEmpty()) {
|
||||
// Re-parse only: find where the last block started in the text and reparse from there
|
||||
// Simpler: just reparse the full text but it's cached via incremental diff
|
||||
reparse();
|
||||
reparseIncremental();
|
||||
} else {
|
||||
// Full reparse (session switch, initial load)
|
||||
m_allBlocks.clear();
|
||||
m_lastBlockTextOffset = 0;
|
||||
beginResetModel();
|
||||
m_blocks.clear();
|
||||
m_visibleStart = 0;
|
||||
|
|
@ -130,10 +129,58 @@ void ChatBlockModel::setRawText(const QString &text)
|
|||
static constexpr int INITIAL_BLOCKS = 20;
|
||||
static constexpr int LOAD_MORE_COUNT = 30;
|
||||
|
||||
void ChatBlockModel::reparseIncremental()
|
||||
{
|
||||
// Only re-parse from where the last block started — O(tail) not O(full text)
|
||||
QString tail = m_rawText.mid(m_lastBlockTextOffset);
|
||||
QList<ChatBlock> tailBlocks = parseBlocks(tail);
|
||||
|
||||
// Replace the last block (which may have grown) and append any new blocks
|
||||
if (!m_allBlocks.isEmpty())
|
||||
m_allBlocks.removeLast();
|
||||
m_allBlocks.append(tailBlocks);
|
||||
|
||||
// Update offset: last block starts at the last header in the tail
|
||||
if (tailBlocks.size() > 1) {
|
||||
// Multiple blocks parsed from tail — advance offset to where the last one starts
|
||||
// Find the last header boundary in m_rawText
|
||||
static const QRegularExpression headerRe(
|
||||
R"(^\[(user|assistant|reasoning|call|tool|error|info)(?::([^\]]*))\])",
|
||||
QRegularExpression::MultilineOption);
|
||||
auto it = headerRe.globalMatch(m_rawText);
|
||||
int lastPos = 0;
|
||||
while (it.hasNext()) {
|
||||
auto match = it.next();
|
||||
lastPos = match.capturedStart();
|
||||
}
|
||||
m_lastBlockTextOffset = lastPos;
|
||||
}
|
||||
|
||||
updateVisibleWindow();
|
||||
}
|
||||
|
||||
void ChatBlockModel::reparse()
|
||||
{
|
||||
m_allBlocks = parseBlocks(m_rawText);
|
||||
// Track where the last block starts for future incremental parses
|
||||
if (!m_allBlocks.isEmpty()) {
|
||||
static const QRegularExpression headerRe(
|
||||
R"(^\[(user|assistant|reasoning|call|tool|error|info)(?::([^\]]*))\])",
|
||||
QRegularExpression::MultilineOption);
|
||||
auto it = headerRe.globalMatch(m_rawText);
|
||||
int lastPos = 0;
|
||||
while (it.hasNext()) {
|
||||
auto match = it.next();
|
||||
lastPos = match.capturedStart();
|
||||
}
|
||||
m_lastBlockTextOffset = lastPos;
|
||||
}
|
||||
|
||||
updateVisibleWindow();
|
||||
}
|
||||
|
||||
void ChatBlockModel::updateVisibleWindow()
|
||||
{
|
||||
// Determine visible window: last INITIAL_BLOCKS (or expand if already showing more)
|
||||
int desiredStart;
|
||||
if (m_blocks.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ signals:
|
|||
|
||||
private:
|
||||
void reparse();
|
||||
void reparseIncremental();
|
||||
void updateVisibleWindow();
|
||||
QList<ChatBlock> parseBlocks(const QString &text) const;
|
||||
QVariantList parseSubBlocks(const QString &content) const;
|
||||
|
||||
|
|
@ -81,6 +83,7 @@ private:
|
|||
QList<ChatBlock> m_allBlocks; // all parsed blocks
|
||||
QList<ChatBlock> m_blocks; // visible window (tail of m_allBlocks)
|
||||
int m_visibleStart = 0; // index into m_allBlocks where visible window starts
|
||||
int m_lastBlockTextOffset = 0; // char offset in m_rawText where last block starts
|
||||
mutable QHash<QString, QString> m_highlightCache;
|
||||
};
|
||||
|
||||
|
|
|
|||
Reference in New Issue