vimlm 0.0.3__py3-none-any.whl → 0.0.4__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.
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: vimlm
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: VimLM - LLM-powered Vim assistant
5
5
  Home-page: https://github.com/JosefAlbers/vimlm
6
6
  Author: Josef Albers
7
7
  Author-email: albersj66@gmail.com
8
- Requires-Python: >=3.13.1
8
+ Requires-Python: >=3.12.8
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: nanollama==0.0.3
11
+ Requires-Dist: nanollama==0.0.3b0
12
12
  Requires-Dist: watchfiles==1.0.4
13
13
  Dynamic: author
14
14
  Dynamic: author-email
@@ -21,11 +21,9 @@ Dynamic: summary
21
21
 
22
22
  # VimLM - Vim Language Model Assistant for privacy-conscious developers
23
23
 
24
- ![vimlm](https://github.com/user-attachments/assets/4aa39efe-aa6d-4363-8fe1-cf964d7f849c)
24
+ ![vimlm](https://github.com/user-attachments/assets/67a97048-48df-40c1-9109-53c759e85d96)
25
25
 
26
- VimLM brings AI-powered assistance directly into Vim, keeping your workflow smooth and uninterrupted. Instead of constantly switching between your editor and external tools, VimLM provides real-time, context-aware suggestions, explanations, and code insights—all within Vim.
27
-
28
- Unlike proprietary cloud-based AI models, VimLM runs entirely offline, ensuring complete privacy, data security, and control. You’re not just using a tool—you own it. Fine-tune, modify, or extend it as needed, without reliance on big tech platforms.
26
+ An LLM-powered coding companion for Vim, inspired by GitHub Copilot/Cursor. Integrates contextual code understanding, summarization, and AI assistance directly into your Vim workflow.
29
27
 
30
28
  ## Features
31
29
 
@@ -70,15 +68,15 @@ vimlm your_file.js
70
68
  - Example prompt: "AJAX-ify this app !@#$ ~/scrap/jph00/hypermedia-applications.summ.md"
71
69
 
72
70
  5. **Follow-Up**: After initial response:
73
- - `Ctrl-R`: Continue thread
71
+ - `Ctrl-r`: Continue thread
74
72
  - Example follow-up: "In Manifest V3"
75
73
 
76
74
  ## Key Bindings
77
75
 
78
76
  | Binding | Mode | Action |
79
77
  |------------|---------------|----------------------------------------|
80
- | `Ctrl-L` | Normal/Visual | Send current file + selection to LLM |
81
- | `Ctrl-R` | Normal | Continue conversation |
78
+ | `Ctrl-l` | Normal/Visual | Send current file + selection to LLM |
79
+ | `Ctrl-r` | Normal | Continue conversation |
82
80
  | `Esc` | Prompt | Cancel input |
83
81
 
84
82
 
@@ -0,0 +1,7 @@
1
+ vimlm.py,sha256=kCJphqs6B-ZUGzeikiW3u629Nw5Ih3DlDJV1rsKGXAY,13410
2
+ vimlm-0.0.4.dist-info/LICENSE,sha256=f1xgK8fAXg_intwnbc9nLkHf7ODPLtgpHs7DetQHOro,11343
3
+ vimlm-0.0.4.dist-info/METADATA,sha256=EdZyb9SyLpy0qyK_jILHa7nrd5G8lPxOm6Xfy7NMfiQ,2832
4
+ vimlm-0.0.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
5
+ vimlm-0.0.4.dist-info/entry_points.txt,sha256=mU5V4MYsuIzCc6YB-Ro-6USSHWN5vHw8UDnTEoq0isw,36
6
+ vimlm-0.0.4.dist-info/top_level.txt,sha256=I8GjqoiP--scYsO3AfLhha-6Ax9ci3IvbWvVbPv8g94,6
7
+ vimlm-0.0.4.dist-info/RECORD,,
vimlm.py CHANGED
@@ -26,23 +26,35 @@ import tempfile
26
26
  from pathlib import Path
27
27
  from string import Template
28
28
 
29
- DEBUG = False
29
+ DEBUG = True
30
30
  NUM_TOKEN = 2000
31
31
  SEP_CMD = '!@#$'
32
- CACHE_DIR = os.path.expanduser("~/vimlm")
32
+ VIMLM_DIR = os.path.expanduser("~/vimlm")
33
33
  WATCH_DIR = os.path.expanduser("~/vimlm/watch_dir")
34
+ CFG_FILE = "cfg.json"
34
35
  LOG_FILE = "log.json"
35
- CACHE_FILE = "cache.json"
36
+ LTM_FILE = "cache.json"
36
37
  OUT_FILE = "response.md"
37
- FILES = ["context", "yank", "user", "tree"]
38
- LOG_PATH = os.path.join(CACHE_DIR, LOG_FILE)
39
- CACHE_PATH = os.path.join(CACHE_DIR, CACHE_FILE)
38
+ IN_FILES = ["context", "yank", "user", "tree"]
39
+ CFG_PATH = os.path.join(VIMLM_DIR, CFG_FILE)
40
+ LOG_PATH = os.path.join(VIMLM_DIR, LOG_FILE)
41
+ LTM_PATH = os.path.join(VIMLM_DIR, LTM_FILE)
40
42
  OUT_PATH = os.path.join(WATCH_DIR, OUT_FILE)
41
43
 
42
44
  if os.path.exists(WATCH_DIR):
43
45
  shutil.rmtree(WATCH_DIR)
44
46
  os.makedirs(WATCH_DIR)
45
47
 
48
+ try:
49
+ with open(CFG_PATH, "r") as f:
50
+ cfg = cfg.load(f)
51
+ DEBUG = config.get("DEBUG", DEBUG)
52
+ NUM_TOKEN = config.get("NUM_TOKEN", NUM_TOKEN)
53
+ SEP_CMD = config.get("SEP_CMD", SEP_CMD)
54
+ except:
55
+ with open(CFG_PATH, 'w') as f:
56
+ json.dump(dict(DEBUG=DEBUG, NUM_TOKEN=NUM_TOKEN, SEP_CMD=SEP_CMD), f, indent=2)
57
+
46
58
  def toout(s, key='tovim'):
47
59
  with open(OUT_PATH, 'w', encoding='utf-8') as f:
48
60
  f.write(s)
@@ -102,11 +114,11 @@ def split_str(doc, max_len=2000, get_len=len):
102
114
  chunks.append("".join(current_chunk))
103
115
  return chunks
104
116
 
105
- def get_context(src_path, max_len=2000, get_len=len):
117
+ def retrieve(src_path, max_len=2000, get_len=len):
106
118
  src_path = os.path.expanduser(src_path)
107
119
  result = {}
108
120
  if not os.path.exists(src_path):
109
- tolog(f"The path {src_path} does not exist.", 'get_context')
121
+ tolog(f"The path {src_path} does not exist.", 'retrieve')
110
122
  return result
111
123
  if os.path.isfile(src_path):
112
124
  try:
@@ -114,7 +126,7 @@ def get_context(src_path, max_len=2000, get_len=len):
114
126
  content = f.read()
115
127
  result = {src_path:dict(timestamp=os.path.getmtime(src_path), list_str=split_str(content, max_len=max_len, get_len=get_len))}
116
128
  except Exception as e:
117
- tolog(f'Skipped {filename} due to {e}', 'get_context')
129
+ tolog(f'Skipped {filename} due to {e}', 'retrieve')
118
130
  return result
119
131
  for filename in os.listdir(src_path):
120
132
  try:
@@ -126,7 +138,7 @@ def get_context(src_path, max_len=2000, get_len=len):
126
138
  content = f.read()
127
139
  result[file_path] = dict(timestamp=os.path.getmtime(file_path), list_str=split_str(content, max_len=max_len, get_len=get_len))
128
140
  except Exception as e:
129
- tolog(f'Skipped {filename} due to {e}', 'get_context')
141
+ tolog(f'Skipped {filename} due to {e}', 'retrieve')
130
142
  continue
131
143
  return result
132
144
 
@@ -134,12 +146,12 @@ def get_ntok(s):
134
146
  return len(chat.tokenizer.encode(s)[0])
135
147
 
136
148
  def ingest(src):
137
- def load_cache(cache_path=CACHE_PATH):
149
+ def load_cache(cache_path=LTM_PATH):
138
150
  if os.path.exists(cache_path):
139
151
  with open(cache_path, 'r', encoding='utf-8') as f:
140
152
  return json.load(f)
141
153
  return {}
142
- def dump_cache(new_data, cache_path=CACHE_PATH):
154
+ def dump_cache(new_data, cache_path=LTM_PATH):
143
155
  current_data = load_cache(cache_path)
144
156
  for k, v in new_data.items():
145
157
  if k not in current_data or v['timestamp'] > current_data[k]['timestamp']:
@@ -149,7 +161,7 @@ def ingest(src):
149
161
  toout('Ingesting...')
150
162
  format_ingest = '{volat}{incoming}\n\n---\n\nPlease provide a succint bullet point summary for above:'
151
163
  format_volat = 'Here is a summary of part 1 of **{k}**:\n\n---\n\n{newsum}\n\n---\n\nHere is the next part:\n\n---\n\n'
152
- dict_doc = get_context(src, get_len=get_ntok)
164
+ dict_doc = retrieve(src, get_len=get_ntok)
153
165
  dict_sum = {}
154
166
  cache = load_cache()
155
167
  for k, v in dict_doc.items():
@@ -197,10 +209,10 @@ async def monitor_directory():
197
209
  async for changes in awatch(WATCH_DIR):
198
210
  found_files = {os.path.basename(f) for _, f in changes}
199
211
  tolog(f'{found_files=}') # DEBUG
200
- if FILES[-1] in found_files and set(FILES).issubset(set(os.listdir(WATCH_DIR))):
212
+ if IN_FILES[-1] in found_files and set(IN_FILES).issubset(set(os.listdir(WATCH_DIR))):
201
213
  tolog(f'listdir()={os.listdir(WATCH_DIR)}') # DEBUG
202
214
  data = {}
203
- for file in FILES:
215
+ for file in IN_FILES:
204
216
  path = os.path.join(WATCH_DIR, file)
205
217
  with open(path, 'r', encoding='utf-8') as f:
206
218
  data[file] = f.read().strip()
@@ -241,8 +253,9 @@ async def process_files(data):
241
253
  prompt = str_template.format(**data)
242
254
  tolog(prompt, 'tollm')
243
255
  toout('')
244
- response = chat(prompt, max_new=NUM_TOKEN - get_ntok(prompt), verbose=False, stream=OUT_PATH)[0][:-10].strip()
245
- toout(response)
256
+ response = chat(prompt, max_new=NUM_TOKEN - get_ntok(prompt), verbose=False, stream=OUT_PATH)
257
+ toout(response[0][:-10].strip())
258
+ tolog(response[-1], 'tps')
246
259
 
247
260
  VIMLMSCRIPT = Template(r"""
248
261
  let s:watched_dir = expand('$WATCH_DIR')
@@ -1,7 +0,0 @@
1
- vimlm.py,sha256=ku7NOIrcCNHzRYP9_8qp1NX8w5uCiZAlf0ijMKJXTlw,12996
2
- vimlm-0.0.3.dist-info/LICENSE,sha256=f1xgK8fAXg_intwnbc9nLkHf7ODPLtgpHs7DetQHOro,11343
3
- vimlm-0.0.3.dist-info/METADATA,sha256=i4W8tmpaMH1m86vzEwNpwo9BUfYcLzr3nksEU_iGW6Y,3178
4
- vimlm-0.0.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
5
- vimlm-0.0.3.dist-info/entry_points.txt,sha256=mU5V4MYsuIzCc6YB-Ro-6USSHWN5vHw8UDnTEoq0isw,36
6
- vimlm-0.0.3.dist-info/top_level.txt,sha256=I8GjqoiP--scYsO3AfLhha-6Ax9ci3IvbWvVbPv8g94,6
7
- vimlm-0.0.3.dist-info/RECORD,,
File without changes
File without changes