add markdown parsing + latex + tree-sitter highlighting for codeblocks in llm responses
This commit is contained in:
@@ -1,11 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "segment.hpp"
|
||||
#include "tool.hpp"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QJsonArray>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include <functional>
|
||||
|
||||
class QJsonObject;
|
||||
@@ -15,9 +20,12 @@ namespace ZShell::llm {
|
||||
|
||||
class ChatGeneration;
|
||||
class ChatSession;
|
||||
class LlmSegment;
|
||||
class LlmTool;
|
||||
|
||||
// The only component that talks to the LLM server: owns the network
|
||||
// manager, the in-flight streaming state and the SSE parsing.
|
||||
// manager, the in-flight streaming state, the SSE parsing and the
|
||||
// tool-calling loop.
|
||||
class LlmClient : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -32,6 +40,7 @@ class LlmClient : public QObject {
|
||||
return m_availableModels;
|
||||
}
|
||||
[[nodiscard]] int contextSize() const { return m_contextSize; }
|
||||
[[nodiscard]] ToolRegistry* tools() const { return m_tools; }
|
||||
void setEndpoint(const QString& value);
|
||||
void setModel(const QString& value);
|
||||
void setTemperature(double value);
|
||||
@@ -39,12 +48,16 @@ class LlmClient : public QObject {
|
||||
void probeContextSize();
|
||||
|
||||
[[nodiscard]] bool busy() const { return m_busy; }
|
||||
[[nodiscard]] bool toolsEnabled() const { return m_tools->enabled(); }
|
||||
void setToolsEnabled(bool value) { m_tools->setEnabled(value); }
|
||||
[[nodiscard]] QString streamingChatId() const { return m_streamingChatId; }
|
||||
[[nodiscard]] ChatSession* streamingSession() const { return m_active; }
|
||||
|
||||
// Streams a new assistant reply into `target` (the active generation of
|
||||
// a message of `session`). The context sent to the model is every
|
||||
// Streams a new assistant reply into `target` (the active generation
|
||||
// of a message of `session`). The context sent to the model is every
|
||||
// message of the session that is older than the target message.
|
||||
// Tool calls made by the model are executed and fed back
|
||||
// transparently until the model produces its final answer.
|
||||
void startGeneration(ChatSession* session, ChatGeneration* target);
|
||||
void stop();
|
||||
void endStream();
|
||||
@@ -63,13 +76,39 @@ class LlmClient : public QObject {
|
||||
void modelChanged();
|
||||
void availableModelsChanged();
|
||||
void contextSizeChanged();
|
||||
void toolsEnabledChanged();
|
||||
void streamingChatIdChanged();
|
||||
void errorOccurred(const QString& message);
|
||||
void titleSuggested(ZShell::llm::ChatSession* session, const QString& title);
|
||||
void iconSuggested(ZShell::llm::ChatSession* session, const QString& icon);
|
||||
|
||||
private:
|
||||
void finalize();
|
||||
struct ToolCallBuilder {
|
||||
QString id;
|
||||
QString name;
|
||||
QString arguments;
|
||||
QPointer<LlmSegment> segment;
|
||||
bool seen = false;
|
||||
};
|
||||
|
||||
// Sends one streaming round: context + transcript so far.
|
||||
void sendRound();
|
||||
// The session context for a round, oldest first, ending just before
|
||||
// `stopBeforeRow` (the message of the generation being streamed).
|
||||
QJsonArray buildContextMessages(
|
||||
ChatSession* session, int stopBeforeRow) const;
|
||||
void applyToolCallDelta(const QJsonObject& call);
|
||||
// One round's stream ended; either ends the turn or executes the
|
||||
// requested tool calls and sends the next round. Runs at most once
|
||||
// per round ([DONE] and the reply's finished signal both reach it).
|
||||
void roundFinished();
|
||||
// Dispatches every call of the round; tools run concurrently.
|
||||
void executeAllCalls();
|
||||
// All results in: appends the tool messages (in call order) and
|
||||
// sends the next round.
|
||||
void flushCallResults();
|
||||
// Ends the current turn gracefully and persists the session.
|
||||
void finishTurn();
|
||||
void fail(const QString& message);
|
||||
void handleLine(const QByteArray& line);
|
||||
void drainBuffer();
|
||||
@@ -86,6 +125,7 @@ class LlmClient : public QObject {
|
||||
const QByteArray& body, const QString& fallback);
|
||||
|
||||
QNetworkAccessManager m_manager;
|
||||
ToolRegistry* m_tools = nullptr;
|
||||
QNetworkReply* m_reply = nullptr;
|
||||
QByteArray m_buffer;
|
||||
ChatSession* m_active = nullptr;
|
||||
@@ -98,6 +138,23 @@ class LlmClient : public QObject {
|
||||
QStringList m_availableModels;
|
||||
double m_temperature = 0.7;
|
||||
int m_contextSize = 0;
|
||||
|
||||
// State of the multi-round tool loop of the current turn.
|
||||
QJsonArray m_transcript;
|
||||
QList<ToolCallBuilder> m_callBuilders;
|
||||
struct ToolCallResult {
|
||||
QString content;
|
||||
bool success = false;
|
||||
};
|
||||
QList<ToolCallResult> m_callResults;
|
||||
QString m_finishReason;
|
||||
int m_round = 0;
|
||||
qsizetype m_contentMark = 0;
|
||||
qsizetype m_reasoningMark = 0;
|
||||
bool m_roundDone = false;
|
||||
bool m_toolPhase = false;
|
||||
int m_pendingCalls = 0;
|
||||
static constexpr int kMaxToolRounds = 12;
|
||||
};
|
||||
|
||||
} // namespace ZShell::llm
|
||||
|
||||
Reference in New Issue
Block a user