-
-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(chat): Adding buffer watcher #610
base: main
Are you sure you want to change the base?
Conversation
I am very excited about this. No pressure at all though. Appreciate this will require a tonne of work. I would very much like this to replace pins in the future...watcher feels more intuitive as well. One thing to think about as you're testing this is a prompt playbook that we can use to test against various models. I expect this will be fine with an gpt-4 or claude-sonnet but could be more challenging for self-hosted LLMs. I'm thinking out loud here but maybe for some models, you can specify to just continue to send the whole buffer with every response (after all, token consumption doesn't matter too much for those users). Awesome job so far and I'll enjoy checking it out later in the week. |
I added this at the weekend btw. |
Thank you for the nice words! indeed, especially for the tracking mechanism.
Yes, with Sonnet 3.5 I've had no issues at all. It's smart enough to keep track of line numbers.
I've tested with some 32B models on huggingface. It worked when I tell the LLM to track the line numbers with an exact example. I think some prompts after this will be changed a bit to include some examples when we finalize the tracking mechanism so the LLM can know exactly what to expect. I'm building a test to send the same prompts automatically to multiple LLMs since I usually work with Hugging Face models, and they have all kinds of LLMs that can work locally. If we succeed without sending the whole buffer again and again, we can benefit from not eating up the context window of the LLM, especially when smaller local LLMs have smaller context windows. |
Ah, I hadn’t noticed it since I started working from the local branch the other two days. Thanks a bunch! One todo item is gone. |
timestamp = vim.loop.now(), | ||
reported = false, | ||
}) | ||
log:debug("Recording change in buffer %d: lines %d-%d: %s", buf, start_row + 1, end_row + 1, vim.inspect(lines)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth changing this line to log:trace
as it will be writing to the log all the time. I ask users to turn logging to debug
in their config when reporting an issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted, I’ve added extensive debug logging to monitor the process and changes. However, in the final version, it will be reduced to log:trace.
Btw, great move to use |
Regarding the latter issue, is it a storage issue? I.e Neovim is only sharing the last change versus all of the changes since a point in time? |
I don't think that's the case, The consolidation logic is too aggressive on purpose. I’m considering a different approach to keep watching lines only and then capture all modified lines that changed when FocusLost event on the buffer, or maybe a less aggressive consolidation mechanism. |
Current State of the PR: This PR is still incomplete, but the basic structure seems solid. Next steps include refining and modifying the prompts, cleaning up the code, and conducting thorough testing (both with written tests and LLM testing). |
Awesome. Plan on testing this properly at the weekend. |
Btw, wondered whether oil.nvim is a useful reference for keeping track of buffer changes? |
When I first saw your comment, I was commuting and started wondering why Honestly, I’m glad I built the buffer watcher without knowing At first, I struggled with Last night, I decided to simplify things and ditched Today, I wrote a lot of tests, including some edge cases I thought of, 13 tests in total spanning about 370 lines of code. Despite that, the core mechanism of the watcher remains simple, at around 130 lines. After all those tests, it’s proven to handle all those cases. I’m sure it’s not perfect, and users will likely uncover new edge cases, but that’s okay. I’m planning to add more edge cases to the tests as well. |
I took a quick look at the oil.nvim implementation, the codebase is large and complex. From what I observed, it uses a different approach and incorporates several advanced features, such as:
Overall, the oil.nvim implementation is significantly more complex, working closely with system events and serving a different purpose. Personally, I tend to favor simpler, easier-to-maintain solutions unless there’s a substantial benefit to a more intricate approach. That said, if you think the oil.nvim implementation would better suit this purpose, I can revisit the oil.nvim code to fully understand the mechanism. It might take some time to go through, as it’s a bit complex. For context, I’m a big fan of Steven’s work and regularly use some of his other plugins like quicker, overseer, and conform. If we encounter edge cases that this watcher doesn’t handle, we can consider integrating some of those ideas (e.g., involving vim.bo.modified or certain Neovim events). |
|
I have not reviewed any of the code. On a trip with the family. But I reviewed the conversation above. Thought maybe my 2cents in the form of a potentially "dumb" question may help as it is the first thing that came to mind when trying to think of a "simple" solution. What if when a buffer is added to a chat it stores the last modified timestamp of that file? I know this may be an issue with handling temp buffers that are not tied to a timestamp but let's forget about that case for now. Then you simply check each time you send a message to LLM to see if the last modified timestamp of the file has changed. If so, grab the new content, compare / diff the changes and send those changes. Thoughts? |
|
||
local allowed_pins = { | ||
"<buf>", | ||
"<file>", | ||
} | ||
|
||
local allowed_watches = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think we should rename this to allowed_watchers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -8,12 +8,17 @@ local config = require("codecompanion.config") | |||
local api = vim.api | |||
local user_role = config.strategies.chat.roles.user | |||
local pinned_icon = config.display.chat.icons.pinned_buffer | |||
local watched_icon = config.display.chat.icons.watched_buffer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add these icons into a table then we can iterate over them to remove them on line 253
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Just played around with user: #buffer What does this file do?
llm: This Lua file defines three functions: ...
user: [watches buffer] I've just changed the file. Can you tell what I've done?
llm: You have deleted the `hello_oli` function
user: @editor can you add it back?
llm: [Runs editor tool correctly]
user: Now what have I done?
llm: You have added a new function
user: Can you share with me what you think the file looks like now?
llm: [Shares correct output]
user: @editor can we change hello_oliver to hello_oli?
llm: [Does so correctly] |
@GitMurf, well basically this final solution is doing something similar in the idea, so It maintains |
@olimorris, if there are no further comments, I believe this is ready to merge. Regarding the documentation, I was working with an older version of the README, so I wasn’t fully aware of the updated structure and the docs site, congratulations on that, by the way; it’s fantastic! As for potential future improvements that could be addressed in separate PRs:
|
Description
This PR introduces a buffer watching mechanism to improve how CodeCompanion handles code context in conversations. Instead of resending entire pinned buffers with each message (which is token-intensive), the watcher tracks and reports only the changes made to referenced buffers.
This is a work in progress PR that aims to optimize token usage and provide better context management in the chat stack.
If the idea of this PR is good and can be merged then I can continue working on it.
TODO
Modify the prompts to prepare the llm for watcher. (smaller llm needs example of what to receive). it needs more thinking and suitable separate PR.Known Issues
Change detection becomes inconsistent (just sometimes) after multiple message exchanges (3rd/4th message). Need to verify if the change tracking logic inchat/init.lua
is properly placed and triggered.issue that the reference gets removed from the message after like the third response.watcher now is better, but stillregistersend the last changes only every time, leaving some changes unreported.When adding a buffer watcher for the first time or stopping the watcher for a buffer, the buffer is printed in the chat as a duplicate. I’m unsure why this happens. Need help here (see video).Occasionally, after a long conversation, I receive a warning message stating, “No messages to submit.” I’m not certain why this occurs or if it’s even related to this PR.IT LOOKS like it doesn't happen any more.Related Issue(s)
#575
Currently, pinned buffers, while useful for maintaining context, are inefficient in terms of API token usage as they resend the same code repeatedly. This PR addresses this by implementing a change-tracking system that only sends buffer modifications to the LLM, significantly reducing token consumption while maintaining context awareness.
Technical Details
watcher.lua
Screenshots
Screen.Recording.2025-01-07.at.12.00.52.PM.mov
Checklist
make docs
command