chore: restructure llm plugin to be more coherent & fix various bugs in llm ui
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m35s
Rust / fmt (pull_request) Successful in 36s
Rust / clippy (pull_request) Successful in 54s
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m35s
Rust / fmt (pull_request) Successful in 36s
Rust / clippy (pull_request) Successful in 54s
This commit is contained in:
+104
-74
@@ -11,16 +11,12 @@ namespace ZShell::llm {
|
||||
|
||||
namespace {
|
||||
constexpr int kMarkdownRefreshMs = 150;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
LlmSegment::LlmSegment(Type type, qint64 timestamp, QObject* parent)
|
||||
: QObject(parent), m_type(type), m_timestamp(timestamp) {
|
||||
m_markdownTimer.setInterval(kMarkdownRefreshMs);
|
||||
connect(&m_markdownTimer, &QTimer::timeout, this, [this]() {
|
||||
if (!m_markdownDirty) return;
|
||||
if (parseMarkdown()) m_markdownDirty = false;
|
||||
});
|
||||
}
|
||||
: QObject(parent), m_type(type), m_timestamp(timestamp) {}
|
||||
|
||||
LlmSegment::~LlmSegment() = default;
|
||||
|
||||
qint64 LlmSegment::elapsedMs() const {
|
||||
if (m_startedAt <= 0) return 0;
|
||||
@@ -45,60 +41,7 @@ void LlmSegment::close() {
|
||||
Q_EMIT runningChanged();
|
||||
Q_EMIT elapsedMsChanged();
|
||||
}
|
||||
if (m_type == Type::Content && m_markdownDirty) {
|
||||
m_markdownTimer.stop();
|
||||
if (parseMarkdown()) m_markdownDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void LlmSegment::appendText(const QString& piece) {
|
||||
if (piece.isEmpty()) return;
|
||||
m_text += piece;
|
||||
Q_EMIT textChanged();
|
||||
scheduleMarkdown();
|
||||
}
|
||||
|
||||
void LlmSegment::setText(const QString& value) {
|
||||
if (m_text == value) return;
|
||||
m_text = value;
|
||||
Q_EMIT textChanged();
|
||||
scheduleMarkdown();
|
||||
}
|
||||
|
||||
void LlmSegment::setName(const QString& value) {
|
||||
if (m_name == value) return;
|
||||
m_name = value;
|
||||
Q_EMIT nameChanged();
|
||||
}
|
||||
|
||||
void LlmSegment::setToolCallId(const QString& value) {
|
||||
if (m_toolCallId == value) return;
|
||||
m_toolCallId = value;
|
||||
Q_EMIT toolCallIdChanged();
|
||||
}
|
||||
|
||||
void LlmSegment::appendArguments(const QString& piece) {
|
||||
if (piece.isEmpty()) return;
|
||||
m_arguments += piece;
|
||||
Q_EMIT argumentsChanged();
|
||||
}
|
||||
|
||||
void LlmSegment::setResult(const QString& value) {
|
||||
if (m_result == value) return;
|
||||
m_result = value;
|
||||
Q_EMIT resultChanged();
|
||||
}
|
||||
|
||||
void LlmSegment::setStatus(Status value) {
|
||||
if (m_status == value) return;
|
||||
m_status = value;
|
||||
Q_EMIT statusChanged();
|
||||
}
|
||||
|
||||
void LlmSegment::finishTool(const QString& resultText, bool success) {
|
||||
setResult(resultText);
|
||||
setStatus(success ? Status::Success : Status::Error);
|
||||
close();
|
||||
closed();
|
||||
}
|
||||
|
||||
void LlmSegment::restore(qint64 elapsedMs) {
|
||||
@@ -106,32 +49,119 @@ void LlmSegment::restore(qint64 elapsedMs) {
|
||||
m_endedAt = m_timestamp + qMax<qint64>(0, elapsedMs);
|
||||
}
|
||||
|
||||
void LlmSegment::scheduleMarkdown() {
|
||||
if (m_type != Type::Content) return;
|
||||
LlmTextSegment::LlmTextSegment(Type type, qint64 timestamp, QObject* parent)
|
||||
: LlmSegment(type, timestamp, parent) {
|
||||
m_markdownTimer.setInterval(kMarkdownRefreshMs);
|
||||
connect(&m_markdownTimer, &QTimer::timeout, this, [this]() {
|
||||
if (!m_markdownDirty) return;
|
||||
if (parseMarkdown()) m_markdownDirty = false;
|
||||
});
|
||||
}
|
||||
|
||||
void LlmTextSegment::setText(const QString& value) {
|
||||
if (m_text == value) return;
|
||||
m_text = value;
|
||||
Q_EMIT textChanged();
|
||||
scheduleMarkdown();
|
||||
}
|
||||
|
||||
void LlmTextSegment::appendText(const QString& piece) {
|
||||
if (piece.isEmpty()) return;
|
||||
m_text += piece;
|
||||
Q_EMIT textChanged();
|
||||
scheduleMarkdown();
|
||||
}
|
||||
|
||||
void LlmTextSegment::closed() {
|
||||
if (type() != Type::Content) return;
|
||||
if (m_markdownDirty) {
|
||||
m_markdownTimer.stop();
|
||||
if (parseMarkdown()) m_markdownDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void LlmTextSegment::scheduleMarkdown() {
|
||||
if (type() != Type::Content) return;
|
||||
m_markdownDirty = true;
|
||||
if (!m_markdownTimer.isActive()) m_markdownTimer.start();
|
||||
}
|
||||
|
||||
bool LlmSegment::parseMarkdown() {
|
||||
bool LlmTextSegment::parseMarkdown() {
|
||||
if (m_parseInFlight) return false;
|
||||
m_parseInFlight = true;
|
||||
const QString text = m_text;
|
||||
QThreadPool::globalInstance()->start([this, text]() {
|
||||
const QVariantList blocks = MarkdownParser::parse(text);
|
||||
QPointer<LlmSegment> guard(this);
|
||||
const QVector<MarkdownBlockData> parsed = MarkdownParser::parse(text);
|
||||
QPointer<LlmTextSegment> guard(this);
|
||||
QMetaObject::invokeMethod(
|
||||
QCoreApplication::instance(),
|
||||
[guard, blocks]() {
|
||||
LlmSegment* seg = guard;
|
||||
if (!seg) return;
|
||||
seg->m_parseInFlight = false;
|
||||
seg->m_markdown = blocks;
|
||||
Q_EMIT seg->markdownChanged();
|
||||
if (seg->m_markdownDirty) seg->parseMarkdown();
|
||||
[guard, parsed]() {
|
||||
LlmTextSegment* segment = guard;
|
||||
if (!segment) return;
|
||||
segment->m_parseInFlight = false;
|
||||
|
||||
QList<MarkdownBlock*> blocks;
|
||||
blocks.reserve(static_cast<int>(parsed.size()));
|
||||
for (int i = 0; i < static_cast<int>(parsed.size()); ++i) {
|
||||
const MarkdownBlockData& data = parsed.at(i);
|
||||
const QString id = QStringLiteral("%1:%2").arg(i).arg(
|
||||
static_cast<int>(data.type));
|
||||
blocks.append(new MarkdownBlock(
|
||||
data.type,
|
||||
id,
|
||||
data.text,
|
||||
data.latex,
|
||||
data.code,
|
||||
data.language,
|
||||
data.level,
|
||||
segment));
|
||||
}
|
||||
segment->m_blocks = blocks;
|
||||
Q_EMIT segment->markdownChanged();
|
||||
if (segment->m_markdownDirty) segment->parseMarkdown();
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
LlmToolCall::LlmToolCall(qint64 timestamp, QObject* parent)
|
||||
: LlmSegment(Type::ToolCall, timestamp, parent) {}
|
||||
|
||||
void LlmToolCall::setName(const QString& value) {
|
||||
if (m_name == value) return;
|
||||
m_name = value;
|
||||
Q_EMIT nameChanged();
|
||||
}
|
||||
|
||||
void LlmToolCall::setToolCallId(const QString& value) {
|
||||
if (m_toolCallId == value) return;
|
||||
m_toolCallId = value;
|
||||
Q_EMIT toolCallIdChanged();
|
||||
}
|
||||
|
||||
void LlmToolCall::appendArguments(const QString& piece) {
|
||||
if (piece.isEmpty()) return;
|
||||
m_arguments += piece;
|
||||
Q_EMIT argumentsChanged();
|
||||
}
|
||||
|
||||
void LlmToolCall::setResult(const QString& value) {
|
||||
if (m_result == value) return;
|
||||
m_result = value;
|
||||
Q_EMIT resultChanged();
|
||||
}
|
||||
|
||||
void LlmToolCall::setStatus(Status value) {
|
||||
if (m_status == value) return;
|
||||
m_status = value;
|
||||
Q_EMIT statusChanged();
|
||||
}
|
||||
|
||||
void LlmToolCall::finish(const QString& resultText, bool success) {
|
||||
setResult(resultText);
|
||||
setStatus(success ? Status::Success : Status::Error);
|
||||
close();
|
||||
}
|
||||
|
||||
} // namespace ZShell::llm
|
||||
|
||||
Reference in New Issue
Block a user