Change message history

This commit is contained in:
Petr Mironychev 2024-10-01 00:20:00 +02:00
parent d7f0cc92e6
commit fbe363689f
2 changed files with 104 additions and 43 deletions

View File

@ -31,6 +31,51 @@
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
int ChatHistory::estimateTokenCount(const QString &text) const
{
return text.length() / 4;
}
void ChatHistory::addMessage(ChatMessage::Role role, const QString &content)
{
int tokenCount = estimateTokenCount(content);
m_messages.append({role, content, tokenCount});
m_totalTokens += tokenCount;
trim();
}
void ChatHistory::clear()
{
m_messages.clear();
m_totalTokens = 0;
}
QVector<ChatMessage> ChatHistory::getMessages() const
{
return m_messages;
}
QString ChatHistory::getSystemPrompt() const
{
return m_systemPrompt;
}
void ChatHistory::setSystemPrompt(const QString &prompt)
{
m_systemPrompt = prompt;
}
void ChatHistory::trim()
{
while (m_messages.size() > MAX_HISTORY_SIZE || m_totalTokens > MAX_TOKENS) {
if (!m_messages.isEmpty()) {
m_totalTokens -= m_messages.first().tokenCount;
m_messages.removeFirst();
} else {
break;
}
}
}
ChatClientInterface::ChatClientInterface(QObject *parent) ChatClientInterface::ChatClientInterface(QObject *parent)
: QObject(parent) : QObject(parent)
, m_requestHandler(new LLMRequestHandler(this)) , m_requestHandler(new LLMRequestHandler(this))
@ -51,15 +96,10 @@ ChatClientInterface::ChatClientInterface(QObject *parent)
} }
}); });
// QJsonObject systemMessage; m_chatHistory.setSystemPrompt("You are a helpful C++ and QML programming assistant.");
// systemMessage["role"] = "system";
// systemMessage["content"] = "You are a helpful C++ and QML programming assistant.";
// m_chatHistory.append(systemMessage);
} }
ChatClientInterface::~ChatClientInterface() ChatClientInterface::~ChatClientInterface() = default;
{
}
void ChatClientInterface::sendMessage(const QString &message) void ChatClientInterface::sendMessage(const QString &message)
{ {
@ -79,14 +119,11 @@ void ChatClientInterface::sendMessage(const QString &message)
QJsonObject providerRequest; QJsonObject providerRequest;
providerRequest["model"] = Settings::generalSettings().chatModelName(); providerRequest["model"] = Settings::generalSettings().chatModelName();
providerRequest["stream"] = true; providerRequest["stream"] = true;
providerRequest["messages"] = prepareMessagesForRequest();
providerRequest["messages"] = m_chatHistory;
chatTemplate->prepareRequest(providerRequest, context); chatTemplate->prepareRequest(providerRequest, context);
chatProvider->prepareRequest(providerRequest); chatProvider->prepareRequest(providerRequest);
m_chatHistory = providerRequest["messages"].toArray();
LLMConfig config; LLMConfig config;
config.requestType = RequestType::Chat; config.requestType = RequestType::Chat;
config.provider = chatProvider; config.provider = chatProvider;
@ -99,18 +136,22 @@ void ChatClientInterface::sendMessage(const QString &message)
request["id"] = QUuid::createUuid().toString(); request["id"] = QUuid::createUuid().toString();
m_accumulatedResponse.clear(); m_accumulatedResponse.clear();
m_pendingMessage = message; m_chatHistory.addMessage(ChatMessage::Role::User, message);
m_requestHandler->sendLLMRequest(config, request); m_requestHandler->sendLLMRequest(config, request);
} }
void ChatClientInterface::clearMessages() void ChatClientInterface::clearMessages()
{ {
m_chatHistory = {}; m_chatHistory.clear();
m_accumulatedResponse.clear(); m_accumulatedResponse.clear();
m_pendingMessage.clear();
logMessage("Chat history cleared"); logMessage("Chat history cleared");
} }
QVector<ChatMessage> ChatClientInterface::getChatHistory() const
{
return m_chatHistory.getMessages();
}
void ChatClientInterface::handleLLMResponse(const QString &response, bool isComplete) void ChatClientInterface::handleLLMResponse(const QString &response, bool isComplete)
{ {
m_accumulatedResponse += response; m_accumulatedResponse += response;
@ -119,42 +160,33 @@ void ChatClientInterface::handleLLMResponse(const QString &response, bool isComp
logMessage("Message completed. Final response: " + m_accumulatedResponse); logMessage("Message completed. Final response: " + m_accumulatedResponse);
emit messageReceived(m_accumulatedResponse.trimmed()); emit messageReceived(m_accumulatedResponse.trimmed());
QJsonObject assistantMessage; m_chatHistory.addMessage(ChatMessage::Role::Assistant, m_accumulatedResponse.trimmed());
assistantMessage["role"] = "assistant";
assistantMessage["content"] = m_accumulatedResponse.trimmed();
m_chatHistory.append(assistantMessage);
m_pendingMessage.clear();
m_accumulatedResponse.clear(); m_accumulatedResponse.clear();
trimChatHistory();
} }
} }
void ChatClientInterface::trimChatHistory() QJsonArray ChatClientInterface::prepareMessagesForRequest() const
{ {
int maxTokens = 4000; QJsonArray messages;
int totalTokens = 0;
QJsonArray newHistory;
if (!m_chatHistory.isEmpty() messages.append(QJsonObject{{"role", "system"}, {"content", m_chatHistory.getSystemPrompt()}});
&& m_chatHistory.first().toObject()["role"].toString() == "system") {
newHistory.append(m_chatHistory.first());
}
for (int i = m_chatHistory.size() - 1; i >= 0; --i) { for (const auto &message : m_chatHistory.getMessages()) {
QJsonObject message = m_chatHistory[i].toObject(); QString role;
int messageTokens = message["content"].toString().length() / 4; switch (message.role) {
case ChatMessage::Role::User:
if (totalTokens + messageTokens > maxTokens) { role = "user";
break; break;
case ChatMessage::Role::Assistant:
role = "assistant";
break;
default:
continue;
}
messages.append(QJsonObject{{"role", role}, {"content", message.content}});
} }
newHistory.prepend(message); return messages;
totalTokens += messageTokens;
}
m_chatHistory = newHistory;
} }
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat

View File

@ -20,12 +20,41 @@
#pragma once #pragma once
#include <QObject> #include <QObject>
#include <QtCore/qjsonarray.h> #include <QString>
#include <QVector>
#include "QodeAssistData.hpp" #include "QodeAssistData.hpp"
#include "core/LLMRequestHandler.hpp" #include "core/LLMRequestHandler.hpp"
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
struct ChatMessage
{
enum class Role { System, User, Assistant };
Role role;
QString content;
int tokenCount;
};
class ChatHistory
{
public:
void addMessage(ChatMessage::Role role, const QString &content);
void clear();
QVector<ChatMessage> getMessages() const;
QString getSystemPrompt() const;
void setSystemPrompt(const QString &prompt);
void trim();
private:
QVector<ChatMessage> m_messages;
QString m_systemPrompt;
int m_totalTokens = 0;
static const int MAX_HISTORY_SIZE = 50;
static const int MAX_TOKENS = 4000;
int estimateTokenCount(const QString &text) const;
};
class ChatClientInterface : public QObject class ChatClientInterface : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -36,6 +65,7 @@ public:
void sendMessage(const QString &message); void sendMessage(const QString &message);
void clearMessages(); void clearMessages();
QVector<ChatMessage> getChatHistory() const;
signals: signals:
void messageReceived(const QString &message); void messageReceived(const QString &message);
@ -43,12 +73,11 @@ signals:
private: private:
void handleLLMResponse(const QString &response, bool isComplete); void handleLLMResponse(const QString &response, bool isComplete);
void trimChatHistory(); QJsonArray prepareMessagesForRequest() const;
LLMRequestHandler *m_requestHandler; LLMRequestHandler *m_requestHandler;
QString m_accumulatedResponse; QString m_accumulatedResponse;
QString m_pendingMessage; ChatHistory m_chatHistory;
QJsonArray m_chatHistory;
}; };
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat