feat: Add auto sync open files with model context

This commit is contained in:
Petr Mironychev 2025-01-24 00:22:44 +01:00
parent 9add61c805
commit bf3c0b3aa0
9 changed files with 93 additions and 41 deletions

View File

@ -29,6 +29,7 @@
#include <projectexplorer/projectmanager.h>
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
#include <coreplugin/editormanager/editormanager.h>
#include "ChatAssistantSettings.hpp"
#include "ChatSerializer.hpp"
@ -45,6 +46,13 @@ ChatRootView::ChatRootView(QQuickItem *parent)
, m_chatModel(new ChatModel(this))
, m_clientInterface(new ClientInterface(m_chatModel, this))
{
m_isSyncOpenFiles = Settings::chatAssistantSettings().linkOpenFiles();
connect(&Settings::chatAssistantSettings().linkOpenFiles, &Utils::BaseAspect::changed,
this,
[this](){
setIsSyncOpenFiles(Settings::chatAssistantSettings().linkOpenFiles());
});
auto &settings = Settings::generalSettings();
connect(&settings.caModel,
@ -52,11 +60,6 @@ ChatRootView::ChatRootView(QQuickItem *parent)
this,
&ChatRootView::currentTemplateChanged);
connect(&Settings::chatAssistantSettings().sharingCurrentFile,
&Utils::BaseAspect::changed,
this,
&ChatRootView::isSharingCurrentFileChanged);
connect(
m_clientInterface,
&ClientInterface::messageReceivedCompletely,
@ -76,6 +79,13 @@ ChatRootView::ChatRootView(QQuickItem *parent)
this, &ChatRootView::updateInputTokensCount);
connect(&Settings::chatAssistantSettings().systemPrompt, &Utils::BaseAspect::changed,
this, &ChatRootView::updateInputTokensCount);
auto editors = Core::EditorManager::instance();
connect(editors, &Core::EditorManager::editorOpened, this, &ChatRootView::onEditorOpened);
connect(editors, &Core::EditorManager::editorAboutToClose, this, &ChatRootView::onEditorAboutToClose);
connect(editors, &Core::EditorManager::editorsClosed, this, &ChatRootView::onEditorsClosed);
updateInputTokensCount();
}
@ -84,7 +94,7 @@ ChatModel *ChatRootView::chatModel() const
return m_chatModel;
}
void ChatRootView::sendMessage(const QString &message, bool sharingCurrentFile)
void ChatRootView::sendMessage(const QString &message)
{
if (m_inputTokensCount > m_chatModel->tokensThreshold()) {
QMessageBox::StandardButton reply = QMessageBox::question(
@ -102,7 +112,7 @@ void ChatRootView::sendMessage(const QString &message, bool sharingCurrentFile)
}
}
m_clientInterface->sendMessage(message, m_attachmentFiles, m_linkedFiles, sharingCurrentFile);
m_clientInterface->sendMessage(message, m_attachmentFiles, m_linkedFiles);
clearAttachmentFiles();
}
@ -158,11 +168,6 @@ QString ChatRootView::currentTemplate() const
return settings.caModel();
}
bool ChatRootView::isSharingCurrentFile() const
{
return Settings::chatAssistantSettings().sharingCurrentFile();
}
void ChatRootView::saveHistory(const QString &filePath)
{
auto result = ChatSerializer::saveToFile(m_chatModel, filePath);
@ -362,6 +367,14 @@ void ChatRootView::calculateMessageTokensCount(const QString &message)
updateInputTokensCount();
}
void ChatRootView::setIsSyncOpenFiles(bool state)
{
if (m_isSyncOpenFiles != state) {
m_isSyncOpenFiles = state;
emit isSyncOpenFilesChanged();
}
}
void ChatRootView::updateInputTokensCount()
{
int inputTokens = m_messageTokensCount;
@ -396,4 +409,42 @@ int ChatRootView::inputTokensCount() const
return m_inputTokensCount;
}
bool ChatRootView::isSyncOpenFiles() const
{
return m_isSyncOpenFiles;
}
void ChatRootView::onEditorOpened(Core::IEditor *editor)
{
if (auto document = editor->document(); document && isSyncOpenFiles()) {
QString filePath = document->filePath().toString();
if (!m_linkedFiles.contains(filePath)) {
m_linkedFiles.append(filePath);
emit linkedFilesChanged();
}
}
}
void ChatRootView::onEditorAboutToClose(Core::IEditor *editor)
{
if (auto document = editor->document(); document && isSyncOpenFiles()) {
QString filePath = document->filePath().toString();
m_linkedFiles.removeOne(filePath);
emit linkedFilesChanged();
}
}
void ChatRootView::onEditorsClosed(QList<Core::IEditor *> editors)
{
if (isSyncOpenFiles()) {
for (Core::IEditor *editor : editors) {
if (auto document = editor->document()) {
QString filePath = document->filePath().toString();
m_linkedFiles.removeOne(filePath);
}
}
emit linkedFilesChanged();
}
}
} // namespace QodeAssist::Chat

View File

@ -23,6 +23,7 @@
#include "ChatModel.hpp"
#include "ClientInterface.hpp"
#include <coreplugin/editormanager/editormanager.h>
namespace QodeAssist::Chat {
@ -31,8 +32,7 @@ class ChatRootView : public QQuickItem
Q_OBJECT
Q_PROPERTY(QodeAssist::Chat::ChatModel *chatModel READ chatModel NOTIFY chatModelChanged FINAL)
Q_PROPERTY(QString currentTemplate READ currentTemplate NOTIFY currentTemplateChanged FINAL)
Q_PROPERTY(bool isSharingCurrentFile READ isSharingCurrentFile NOTIFY
isSharingCurrentFileChanged FINAL)
Q_PROPERTY(bool isSyncOpenFiles READ isSyncOpenFiles NOTIFY isSyncOpenFilesChanged FINAL)
Q_PROPERTY(QStringList attachmentFiles READ attachmentFiles NOTIFY attachmentFilesChanged FINAL)
Q_PROPERTY(QStringList linkedFiles READ linkedFiles NOTIFY linkedFilesChanged FINAL)
Q_PROPERTY(int inputTokensCount READ inputTokensCount NOTIFY inputTokensCountChanged FINAL)
@ -45,8 +45,6 @@ public:
ChatModel *chatModel() const;
QString currentTemplate() const;
bool isSharingCurrentFile() const;
void saveHistory(const QString &filePath);
void loadHistory(const QString &filePath);
@ -64,12 +62,19 @@ public:
Q_INVOKABLE void showLinkFilesDialog();
Q_INVOKABLE void removeFileFromLinkList(int index);
Q_INVOKABLE void calculateMessageTokensCount(const QString &message);
Q_INVOKABLE void setIsSyncOpenFiles(bool state);
void updateInputTokensCount();
int inputTokensCount() const;
bool isSyncOpenFiles() const;
void onEditorOpened(Core::IEditor *editor);
void onEditorAboutToClose(Core::IEditor *editor);
void onEditorsClosed(QList<Core::IEditor *> editors);
public slots:
void sendMessage(const QString &message, bool sharingCurrentFile = false);
void sendMessage(const QString &message);
void copyToClipboard(const QString &text);
void cancelRequest();
void clearAttachmentFiles();
@ -78,10 +83,10 @@ public slots:
signals:
void chatModelChanged();
void currentTemplateChanged();
void isSharingCurrentFileChanged();
void attachmentFilesChanged();
void linkedFilesChanged();
void inputTokensCountChanged();
void isSyncOpenFilesChanged();
private:
QString getChatsHistoryDir() const;
@ -95,6 +100,7 @@ private:
QStringList m_linkedFiles;
int m_messageTokensCount{0};
int m_inputTokensCount{0};
bool m_isSyncOpenFiles;
};
} // namespace QodeAssist::Chat

View File

@ -66,7 +66,7 @@ ClientInterface::ClientInterface(ChatModel *chatModel, QObject *parent)
ClientInterface::~ClientInterface() = default;
void ClientInterface::sendMessage(
const QString &message, const QList<QString> &attachments, const QList<QString> &linkedFiles, bool includeCurrentFile)
const QString &message, const QList<QString> &attachments, const QList<QString> &linkedFiles)
{
cancelRequest();
@ -100,13 +100,6 @@ void ClientInterface::sendMessage(
if (chatAssistantSettings.useSystemPrompt())
systemPrompt = chatAssistantSettings.systemPrompt();
if (includeCurrentFile) {
QString fileContext = getCurrentFileContext();
if (!fileContext.isEmpty()) {
systemPrompt = systemPrompt.append(fileContext);
}
}
if (!linkedFiles.isEmpty()) {
systemPrompt = getSystemPromptWithLinkedFiles(systemPrompt, linkedFiles);
}

View File

@ -39,8 +39,7 @@ public:
void sendMessage(
const QString &message,
const QList<QString> &attachments = {},
const QList<QString> &linkedFiles = {},
bool includeCurrentFile = false);
const QList<QString> &linkedFiles = {});
void clearMessages();
void cancelRequest();

View File

@ -188,7 +188,10 @@ ChatRootView {
sendButton.onClicked: root.sendChatMessage()
stopButton.onClicked: root.cancelRequest()
sharingCurrentFile.checked: root.isSharingCurrentFile
syncOpenFiles {
checked: root.isSyncOpenFiles
onCheckedChanged: root.setIsSyncOpenFiles(bottomBar.syncOpenFiles.checked)
}
attachFiles.onClicked: root.showAttachFilesDialog()
linkFiles.onClicked: root.showLinkFilesDialog()
}
@ -197,7 +200,6 @@ ChatRootView {
function clearChat() {
root.chatModel.clear()
root.clearAttachmentFiles()
root.clearLinkedFiles()
}
function scrollToBottom() {
@ -205,7 +207,7 @@ ChatRootView {
}
function sendChatMessage() {
root.sendMessage(messageInput.text, bottomBar.sharingCurrentFile.checked)
root.sendMessage(messageInput.text)
messageInput.text = ""
scrollToBottom()
}

View File

@ -27,7 +27,7 @@ Rectangle {
property alias sendButton: sendButtonId
property alias stopButton: stopButtonId
property alias sharingCurrentFile: sharingCurrentFileId
property alias syncOpenFiles: syncOpenFilesId
property alias attachFiles: attachFilesId
property alias linkFiles: linkFilesId
@ -61,9 +61,9 @@ Rectangle {
}
CheckBox {
id: sharingCurrentFileId
id: syncOpenFilesId
text: qsTr("Share current file with models")
text: qsTr("Sync open files with model context")
}
QoAButton {

View File

@ -44,15 +44,15 @@ ChatAssistantSettings::ChatAssistantSettings()
// Chat Settings
chatTokensThreshold.setSettingsKey(Constants::CA_TOKENS_THRESHOLD);
chatTokensThreshold.setLabelText(Tr::tr("Chat History Token Limit:"));
chatTokensThreshold.setLabelText(Tr::tr("Chat history token limit:"));
chatTokensThreshold.setToolTip(Tr::tr("Maximum number of tokens in chat history. When "
"exceeded, oldest messages will be removed."));
chatTokensThreshold.setRange(1, 900000);
chatTokensThreshold.setDefaultValue(8000);
sharingCurrentFile.setSettingsKey(Constants::CA_SHARING_CURRENT_FILE);
sharingCurrentFile.setLabelText(Tr::tr("Share Current File With Assistant by Default"));
sharingCurrentFile.setDefaultValue(true);
linkOpenFiles.setSettingsKey(Constants::CA_LINK_OPEN_FILES);
linkOpenFiles.setLabelText(Tr::tr("Sync open files with assistant by default"));
linkOpenFiles.setDefaultValue(false);
stream.setSettingsKey(Constants::CA_STREAM);
stream.setDefaultValue(true);
@ -171,7 +171,7 @@ ChatAssistantSettings::ChatAssistantSettings()
Space{8},
Group{
title(Tr::tr("Chat Settings")),
Column{Row{chatTokensThreshold, Stretch{1}}, sharingCurrentFile, stream, autosave}},
Column{Row{chatTokensThreshold, Stretch{1}}, linkOpenFiles, stream, autosave}},
Space{8},
Group{
title(Tr::tr("General Parameters")),
@ -227,6 +227,7 @@ void ChatAssistantSettings::resetSettingsToDefaults()
resetAspect(systemPrompt);
resetAspect(ollamaLivetime);
resetAspect(contextWindow);
resetAspect(linkOpenFiles);
}
}

View File

@ -34,7 +34,7 @@ public:
// Chat settings
Utils::IntegerAspect chatTokensThreshold{this};
Utils::BoolAspect sharingCurrentFile{this};
Utils::BoolAspect linkOpenFiles{this};
Utils::BoolAspect stream{this};
Utils::BoolAspect autosave{this};

View File

@ -62,7 +62,7 @@ const char CC_STREAM[] = "QodeAssist.ccStream";
const char CC_SMART_PROCESS_INSTRUCT_TEXT[] = "QodeAssist.ccSmartProcessInstructText";
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
const char CA_TOKENS_THRESHOLD[] = "QodeAssist.caTokensThreshold";
const char CA_SHARING_CURRENT_FILE[] = "QodeAssist.caSharingCurrentFile";
const char CA_LINK_OPEN_FILES[] = "QodeAssist.caLinkOpenFiles";
const char CA_STREAM[] = "QodeAssist.caStream";
const char CA_AUTOSAVE[] = "QodeAssist.caAutosave";