vimlm 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vimlm/__init__.py +0 -0
- vimlm/main.py +72 -0
- vimlm/watcher.py +59 -0
- vimlm-0.0.1.dist-info/METADATA +55 -0
- vimlm-0.0.1.dist-info/RECORD +8 -0
- vimlm-0.0.1.dist-info/WHEEL +5 -0
- vimlm-0.0.1.dist-info/entry_points.txt +2 -0
- vimlm-0.0.1.dist-info/top_level.txt +1 -0
vimlm/__init__.py
ADDED
File without changes
|
vimlm/main.py
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
import argparse
|
2
|
+
import subprocess
|
3
|
+
import tempfile
|
4
|
+
import os
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
VIM_CONFIG = """
|
8
|
+
let s:watched_dir = expand('~/watched_dir')
|
9
|
+
|
10
|
+
function! Monitor()
|
11
|
+
write
|
12
|
+
let response_path = s:watched_dir . '/response.md'
|
13
|
+
rightbelow vsplit | execute 'view ' . response_path
|
14
|
+
setlocal autoread
|
15
|
+
setlocal readonly
|
16
|
+
setlocal nobuflisted
|
17
|
+
filetype detect
|
18
|
+
syntax on
|
19
|
+
wincmd h
|
20
|
+
let s:monitor_timer = timer_start(1000, 'CheckForUpdates', {'repeat': -1})
|
21
|
+
endfunction
|
22
|
+
|
23
|
+
function! CheckForUpdates(timer)
|
24
|
+
let bufnum = bufnr(s:watched_dir . '/response.md')
|
25
|
+
if bufnum == -1
|
26
|
+
call timer_stop(s:monitor_timer)
|
27
|
+
return
|
28
|
+
endif
|
29
|
+
silent! checktime
|
30
|
+
endfunction
|
31
|
+
|
32
|
+
function! SaveUserInput()
|
33
|
+
let user_input = input('Ask LLM: ')
|
34
|
+
let user_file = s:watched_dir . '/user'
|
35
|
+
call writefile([user_input], user_file, 'w')
|
36
|
+
let current_file = expand('%:t')
|
37
|
+
let tree_file = s:watched_dir . '/tree'
|
38
|
+
call writefile([current_file], tree_file, 'w')
|
39
|
+
endfunction
|
40
|
+
|
41
|
+
vnoremap <c-l> :w! ~/watched_dir/yank<CR>:w! ~/watched_dir/context<CR>:call SaveUserInput()<CR>
|
42
|
+
nnoremap <c-l> V:w! ~/watched_dir/yank<CR>:w! ~/watched_dir/context<CR>:call SaveUserInput()<CR>
|
43
|
+
|
44
|
+
call Monitor()
|
45
|
+
"""
|
46
|
+
|
47
|
+
def main():
|
48
|
+
parser = argparse.ArgumentParser(description="VimLM - LLM-powered Vim assistant")
|
49
|
+
parser.add_argument("vim_args", nargs=argparse.REMAINDER, help="Additional Vim arguments")
|
50
|
+
args = parser.parse_args()
|
51
|
+
watch_dir = Path.home() / "watched_dir"
|
52
|
+
watch_dir.mkdir(exist_ok=True)
|
53
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.vim', delete=False) as f:
|
54
|
+
f.write(VIM_CONFIG)
|
55
|
+
vim_script = f.name
|
56
|
+
try:
|
57
|
+
watcher = subprocess.Popen(
|
58
|
+
["python", "-m", "vimlm.watcher"],
|
59
|
+
stdout=(watch_dir / "response.md").open("w"),
|
60
|
+
stderr=subprocess.STDOUT,
|
61
|
+
)
|
62
|
+
vim_command = ["vim", "-c", f"source {vim_script}"]
|
63
|
+
if args.vim_args:
|
64
|
+
vim_command.extend(args.vim_args)
|
65
|
+
subprocess.run(vim_command)
|
66
|
+
finally:
|
67
|
+
watcher.terminate()
|
68
|
+
watcher.wait()
|
69
|
+
os.unlink(vim_script)
|
70
|
+
|
71
|
+
if __name__ == "__main__":
|
72
|
+
main()
|
vimlm/watcher.py
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
import asyncio
|
2
|
+
import subprocess
|
3
|
+
import json
|
4
|
+
import os
|
5
|
+
from watchfiles import awatch
|
6
|
+
from nanollama32 import Chat
|
7
|
+
|
8
|
+
DEBUG = False
|
9
|
+
NUM_TOKEN = 1000
|
10
|
+
DIRECTORY = os.path.expanduser("~/watched_dir")
|
11
|
+
OUT_FILE = "response.md"
|
12
|
+
LOG_FILE = "log.json"
|
13
|
+
FILES = ["context", "yank", "user", "tree"]
|
14
|
+
|
15
|
+
out_path = os.path.join(DIRECTORY, OUT_FILE)
|
16
|
+
log_path = os.path.join(DIRECTORY, LOG_FILE)
|
17
|
+
os.makedirs(DIRECTORY, exist_ok=True)
|
18
|
+
chat = Chat(variant='uncn_llama_32_3b_it')
|
19
|
+
|
20
|
+
with open(out_path, "w", encoding="utf-8") as f:
|
21
|
+
f.write('LLM is ready')
|
22
|
+
|
23
|
+
async def monitor_directory():
|
24
|
+
async for changes in awatch(DIRECTORY):
|
25
|
+
found_files = {os.path.basename(f) for _, f in changes}
|
26
|
+
if FILES[-1] in found_files and set(FILES).issubset(set(os.listdir(DIRECTORY))):
|
27
|
+
await process_files()
|
28
|
+
|
29
|
+
async def process_files():
|
30
|
+
data = {}
|
31
|
+
for file in FILES:
|
32
|
+
path = os.path.join(DIRECTORY, file)
|
33
|
+
with open(path, "r", encoding="utf-8") as f:
|
34
|
+
data[file] = f.read().strip()
|
35
|
+
os.remove(path)
|
36
|
+
data['ext'] = data['tree'].split('.')[-1]
|
37
|
+
if len(data['yank']) > 0:
|
38
|
+
str_template = "**{tree}**\n```{ext}\n{context}\n```\n\n```{ext}\n{yank}\n```\n\n{user}" if '\n' in data['yank'] else "**{tree}**\n```{ext}\n{context}\n```\n\n`{yank}` {user}"
|
39
|
+
else:
|
40
|
+
str_template = "**{tree}**\n```{ext}\n{context}\n\n```\n\n{user}"
|
41
|
+
prompt = str_template.format(**data)
|
42
|
+
with open(out_path, "w", encoding="utf-8") as f:
|
43
|
+
f.write('')
|
44
|
+
response = chat(prompt, max_new=NUM_TOKEN, verbose=DEBUG, stream=out_path)[0][:-10].strip()
|
45
|
+
with open(out_path, "w", encoding="utf-8") as f:
|
46
|
+
f.write(response)
|
47
|
+
chat.reset()
|
48
|
+
if DEBUG:
|
49
|
+
if os.path.exists(log_path):
|
50
|
+
with open(log_path, "r", encoding="utf-8") as log_f:
|
51
|
+
logs = json.load(log_f)
|
52
|
+
else:
|
53
|
+
logs = []
|
54
|
+
logs.append({'prompt':prompt, 'respone':response})
|
55
|
+
with open(log_path, "w", encoding="utf-8") as log_f:
|
56
|
+
json.dump(logs, log_f, indent=2)
|
57
|
+
|
58
|
+
asyncio.run(monitor_directory())
|
59
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: vimlm
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: VimLM - LLM-powered Vim assistant
|
5
|
+
Home-page: https://github.com/JosefAlbers/vimlm
|
6
|
+
Author: Josef Albers
|
7
|
+
Author-email: albersj66@gmail.com
|
8
|
+
Requires-Python: >=3.13.1
|
9
|
+
Description-Content-Type: text/markdown
|
10
|
+
Requires-Dist: nanollama==0.0.3
|
11
|
+
Requires-Dist: watchfiles==1.0.4
|
12
|
+
Dynamic: author
|
13
|
+
Dynamic: author-email
|
14
|
+
Dynamic: description
|
15
|
+
Dynamic: description-content-type
|
16
|
+
Dynamic: home-page
|
17
|
+
Dynamic: requires-dist
|
18
|
+
Dynamic: requires-python
|
19
|
+
Dynamic: summary
|
20
|
+
|
21
|
+
# VimLM - LLM-powered Vim assistant
|
22
|
+
|
23
|
+
## Features
|
24
|
+
|
25
|
+
- Real-time code assistance using local LLMs
|
26
|
+
- Context-aware suggestions based on your current file
|
27
|
+
- Split-window interface showing LLM responses
|
28
|
+
- Simple keybinding integration with Vim
|
29
|
+
- Works completely offline with local models
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
```zsh
|
34
|
+
pip install vimlm
|
35
|
+
```
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
1. Start Vim with VimLM:
|
40
|
+
|
41
|
+
```zsh
|
42
|
+
vimlm your_file.js
|
43
|
+
```
|
44
|
+
|
45
|
+
2. Use the key bindings in Vim:
|
46
|
+
- `Ctrl-L` in normal mode: Get suggestions for current line
|
47
|
+
- `Ctrl-L` in visual mode: Get suggestions for selected code
|
48
|
+
|
49
|
+
The LLM response will appear in a split window on the right side of your Vim interface.
|
50
|
+
|
51
|
+
## Demo
|
52
|
+
|
53
|
+

|
54
|
+
|
55
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
vimlm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
vimlm/main.py,sha256=oquliLkQSdPgPBf0sUBBSu7ZZAeerLMkms3H8kXBmIw,2164
|
3
|
+
vimlm/watcher.py,sha256=4xI6LE-IhtgvLU8ho_pXCBL5vx8xRkkgQq9Iwr8-jeQ,2075
|
4
|
+
vimlm-0.0.1.dist-info/METADATA,sha256=-4HQ3JOHYnfnCmVJ1Yej_7DKGOVzYrwJeQ9vjkV3Dzw,1246
|
5
|
+
vimlm-0.0.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
6
|
+
vimlm-0.0.1.dist-info/entry_points.txt,sha256=s1QPno-YPS74cjm8JI_FeZqYpKO-QrTThGXUwl9shqk,42
|
7
|
+
vimlm-0.0.1.dist-info/top_level.txt,sha256=I8GjqoiP--scYsO3AfLhha-6Ax9ci3IvbWvVbPv8g94,6
|
8
|
+
vimlm-0.0.1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
vimlm
|