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 <projectexplorer/projectmanager.h>
#include <utils/theme/theme.h> #include <utils/theme/theme.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
#include <coreplugin/editormanager/editormanager.h>
#include "ChatAssistantSettings.hpp" #include "ChatAssistantSettings.hpp"
#include "ChatSerializer.hpp" #include "ChatSerializer.hpp"
@ -45,6 +46,13 @@ ChatRootView::ChatRootView(QQuickItem *parent)
, m_chatModel(new ChatModel(this)) , m_chatModel(new ChatModel(this))
, m_clientInterface(new ClientInterface(m_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(); auto &settings = Settings::generalSettings();
connect(&settings.caModel, connect(&settings.caModel,
@ -52,11 +60,6 @@ ChatRootView::ChatRootView(QQuickItem *parent)
this, this,
&ChatRootView::currentTemplateChanged); &ChatRootView::currentTemplateChanged);
connect(&Settings::chatAssistantSettings().sharingCurrentFile,
&Utils::BaseAspect::changed,
this,
&ChatRootView::isSharingCurrentFileChanged);
connect( connect(
m_clientInterface, m_clientInterface,
&ClientInterface::messageReceivedCompletely, &ClientInterface::messageReceivedCompletely,
@ -76,6 +79,13 @@ ChatRootView::ChatRootView(QQuickItem *parent)
this, &ChatRootView::updateInputTokensCount); this, &ChatRootView::updateInputTokensCount);
connect(&Settings::chatAssistantSettings().systemPrompt, &Utils::BaseAspect::changed, connect(&Settings::chatAssistantSettings().systemPrompt, &Utils::BaseAspect::changed,
this, &ChatRootView::updateInputTokensCount); 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(); updateInputTokensCount();
} }
@ -84,7 +94,7 @@ ChatModel *ChatRootView::chatModel() const
return m_chatModel; return m_chatModel;
} }
void ChatRootView::sendMessage(const QString &message, bool sharingCurrentFile) void ChatRootView::sendMessage(const QString &message)
{ {
if (m_inputTokensCount > m_chatModel->tokensThreshold()) { if (m_inputTokensCount > m_chatModel->tokensThreshold()) {
QMessageBox::StandardButton reply = QMessageBox::question( 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(); clearAttachmentFiles();
} }
@ -158,11 +168,6 @@ QString ChatRootView::currentTemplate() const
return settings.caModel(); return settings.caModel();
} }
bool ChatRootView::isSharingCurrentFile() const
{
return Settings::chatAssistantSettings().sharingCurrentFile();
}
void ChatRootView::saveHistory(const QString &filePath) void ChatRootView::saveHistory(const QString &filePath)
{ {
auto result = ChatSerializer::saveToFile(m_chatModel, filePath); auto result = ChatSerializer::saveToFile(m_chatModel, filePath);
@ -362,6 +367,14 @@ void ChatRootView::calculateMessageTokensCount(const QString &message)
updateInputTokensCount(); updateInputTokensCount();
} }
void ChatRootView::setIsSyncOpenFiles(bool state)
{
if (m_isSyncOpenFiles != state) {
m_isSyncOpenFiles = state;
emit isSyncOpenFilesChanged();
}
}
void ChatRootView::updateInputTokensCount() void ChatRootView::updateInputTokensCount()
{ {
int inputTokens = m_messageTokensCount; int inputTokens = m_messageTokensCount;
@ -396,4 +409,42 @@ int ChatRootView::inputTokensCount() const
return m_inputTokensCount; 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 } // namespace QodeAssist::Chat

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,7 +34,7 @@ public:
// Chat settings // Chat settings
Utils::IntegerAspect chatTokensThreshold{this}; Utils::IntegerAspect chatTokensThreshold{this};
Utils::BoolAspect sharingCurrentFile{this}; Utils::BoolAspect linkOpenFiles{this};
Utils::BoolAspect stream{this}; Utils::BoolAspect stream{this};
Utils::BoolAspect autosave{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 CC_SMART_PROCESS_INSTRUCT_TEXT[] = "QodeAssist.ccSmartProcessInstructText";
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate"; const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
const char CA_TOKENS_THRESHOLD[] = "QodeAssist.caTokensThreshold"; 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_STREAM[] = "QodeAssist.caStream";
const char CA_AUTOSAVE[] = "QodeAssist.caAutosave"; const char CA_AUTOSAVE[] = "QodeAssist.caAutosave";