rlmgrep 0.1.4__py3-none-any.whl → 0.1.6__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.
rlmgrep/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.1.4"
2
+ __version__ = "0.1.6"
rlmgrep/cli.py CHANGED
@@ -73,8 +73,6 @@ def _parse_args(argv: list[str]) -> argparse.Namespace:
73
73
  parser.add_argument("paths", nargs="*", help="Files or directories")
74
74
 
75
75
  parser.add_argument("-n", dest="line_numbers", action="store_true", help="Show line numbers (default)")
76
- parser.add_argument("--no-line-number", dest="line_numbers", action="store_false", help="Hide line numbers")
77
- parser.add_argument("-H", dest="with_filename", action="store_true", help="Always show filenames (default)")
78
76
  parser.add_argument("-r", dest="recursive", action="store_true", help="Recursive (directories are searched recursively by default)")
79
77
  parser.add_argument("--no-recursive", dest="recursive", action="store_false", help="Do not recurse directories")
80
78
  parser.set_defaults(recursive=True, line_numbers=True)
@@ -86,10 +84,22 @@ def _parse_args(argv: list[str]) -> argparse.Namespace:
86
84
  parser.add_argument("-a", "--text", dest="binary_as_text", action="store_true", help="Search binary files as text")
87
85
  parser.add_argument("--answer", action="store_true", help="Print a narrative answer before grep output")
88
86
  parser.add_argument("-y", "--yes", action="store_true", help="Skip file count confirmation")
87
+ parser.add_argument(
88
+ "--files-from-stdin",
89
+ action="store_true",
90
+ help="Treat stdin as newline-delimited file paths (e.g., rg -l)",
91
+ )
92
+ parser.add_argument(
93
+ "--files-from-rg",
94
+ dest="stdin_files",
95
+ action="store_true",
96
+ help="Alias for --files-from-stdin",
97
+ )
89
98
  parser.add_argument(
90
99
  "--stdin-files",
100
+ dest="stdin_files",
91
101
  action="store_true",
92
- help="Treat stdin as newline-delimited file paths",
102
+ help="Deprecated: use --files-from-stdin",
93
103
  )
94
104
 
95
105
  parser.add_argument("-g", "--glob", dest="globs", action="append", default=[], help="Include files matching glob (may repeat)")
@@ -240,6 +250,33 @@ def _build_markitdown(config: dict, warnings: list[str]):
240
250
  kwargs["base_url"] = api_base
241
251
  return OpenAI(**kwargs)
242
252
 
253
+ class _LiteLLMClient:
254
+ def __init__(self, api_key: str | None, api_base: str | None):
255
+ try:
256
+ import litellm # type: ignore
257
+ except Exception as exc:
258
+ raise RuntimeError("litellm not available") from exc
259
+ self._litellm = litellm
260
+ self._api_key = api_key
261
+ self._api_base = api_base
262
+ self.chat = self._Chat(self)
263
+
264
+ class _Chat:
265
+ def __init__(self, parent):
266
+ self.completions = parent._Completions(parent)
267
+
268
+ class _Completions:
269
+ def __init__(self, parent):
270
+ self._parent = parent
271
+
272
+ def create(self, model: str, messages):
273
+ kwargs = {"model": model, "messages": messages}
274
+ if self._parent._api_key:
275
+ kwargs["api_key"] = self._parent._api_key
276
+ if self._parent._api_base:
277
+ kwargs["api_base"] = self._parent._api_base
278
+ return self._parent._litellm.completion(**kwargs)
279
+
243
280
  llm_client = None
244
281
  if enable_images:
245
282
  if not llm_model:
@@ -247,18 +284,27 @@ def _build_markitdown(config: dict, warnings: list[str]):
247
284
  "markitdown_enable_images set but markitdown_image_llm_model missing; skipping images"
248
285
  )
249
286
  enable_images = False
250
- elif llm_provider != "openai":
251
- warnings.append(
252
- f"markitdown image LLM provider '{llm_provider}' not supported; skipping images"
253
- )
254
- enable_images = False
255
287
  else:
256
- llm_client = _openai_client(
257
- llm_api_key,
258
- llm_api_base,
259
- "openai package missing; skipping image conversion",
260
- )
261
- if llm_client is None:
288
+ if llm_provider == "openai":
289
+ llm_client = _openai_client(
290
+ llm_api_key,
291
+ llm_api_base,
292
+ "openai package missing; skipping image conversion",
293
+ )
294
+ if llm_client is None:
295
+ enable_images = False
296
+ elif llm_provider in {"gemini", "anthropic"}:
297
+ try:
298
+ llm_client = _LiteLLMClient(llm_api_key, llm_api_base)
299
+ except RuntimeError:
300
+ warnings.append(
301
+ "litellm not available; skipping image conversion"
302
+ )
303
+ enable_images = False
304
+ else:
305
+ warnings.append(
306
+ f"markitdown image LLM provider '{llm_provider}' not supported; skipping images"
307
+ )
262
308
  enable_images = False
263
309
 
264
310
  md_kwargs: dict[str, object] = {"enable_plugins": False}
rlmgrep/config.py CHANGED
@@ -25,7 +25,7 @@ DEFAULT_CONFIG_TEXT = "\n".join(
25
25
  "# markitdown_image_llm_api_base = \"\"",
26
26
  "# markitdown_image_llm_prompt = \"\"",
27
27
  "# markitdown_enable_audio = false",
28
- "# markitdown_audio_model = \"gpt-4o-mini-transcribe\"",
28
+ "# markitdown_audio_model = \"gpt-4o-mini-transcribe-2025-12-15\"",
29
29
  "# markitdown_audio_provider = \"openai\"",
30
30
  "# markitdown_audio_api_key = \"\"",
31
31
  "# markitdown_audio_api_base = \"\"",
rlmgrep/render.py CHANGED
@@ -28,11 +28,11 @@ def _format_line(
28
28
  heading: bool,
29
29
  ) -> str:
30
30
  delim = ":" if is_match else "-"
31
- if show_line_numbers:
32
- prefix = _colorize(str(line_no), COLOR_LINE_NO, use_color)
33
- sep = "\t" if heading else ""
34
- return f"{prefix}{delim}{sep}{text}"
35
- return text
31
+ if not show_line_numbers:
32
+ return text
33
+ prefix = _colorize(str(line_no), COLOR_LINE_NO, use_color)
34
+ sep = "\t" if heading else ""
35
+ return f"{prefix}{delim}{sep}{text}"
36
36
 
37
37
 
38
38
  def _merge_ranges(ranges: list[tuple[int, int]]) -> list[tuple[int, int]]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rlmgrep
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: Grep-shaped CLI search powered by DSPy RLM
5
5
  Author: rlmgrep
6
6
  License: MIT
@@ -61,9 +61,6 @@ rlmgrep [options] "query" [paths...]
61
61
 
62
62
  Common options:
63
63
 
64
- - `-n` show line numbers (default)
65
- - `--no-line-number` hide line numbers
66
- - `-H` always show filenames (default)
67
64
  - `-C N` context lines before/after (grep-style)
68
65
  - `-A N` context lines after
69
66
  - `-B N` context lines before
@@ -83,19 +80,19 @@ Examples:
83
80
 
84
81
  ```sh
85
82
  # Natural-language query over a repo
86
- rlmgrep -n -C 2 "token parsing" rlmgrep/
83
+ rlmgrep -n -C 2 "Where is retry/backoff configured and what are the defaults?" .
87
84
 
88
85
  # Restrict to Python files
89
- rlmgrep "where config is read" --type py rlmgrep/
86
+ rlmgrep "Where do we parse JWTs and enforce expiration?" --type py .
90
87
 
91
88
  # Glob filters (repeatable or comma-separated)
92
- rlmgrep "error handling" -g "**/*.py" -g "**/*.md" .
89
+ rlmgrep "How do we map external API errors into internal error codes?" -g "**/*.py" -g "**/*.ts" .
93
90
 
94
- # Read from stdin (only when no paths are provided)
95
- cat README.md | rlmgrep "install"
91
+ # Single-file semantic question (when you already have the file)
92
+ cat README.md | rlmgrep --answer "What is this tool for and how is it used?"
96
93
 
97
94
  # Use rg/grep to find candidate files, then rlmgrep over that list
98
- rg -l "token" . | rlmgrep --stdin-files --answer "what does this token control?"
95
+ rg -l "token" . | rlmgrep --files-from-stdin --answer "What does this token control and where is it validated?"
99
96
  ```
100
97
 
101
98
  ## Input selection
@@ -120,7 +117,6 @@ rg -l "token" . | rlmgrep --stdin-files --answer "what does this token control?"
120
117
  - `1` = no matches
121
118
  - `2` = usage/config/error
122
119
 
123
- Agent tip: use `-n -H` and no context for parse-friendly output, then key off exit codes.
124
120
 
125
121
  ## Regex-style queries (best effort)
126
122
 
@@ -160,7 +156,7 @@ file_hard_max = 1000
160
156
  # markitdown_image_llm_api_base = ""
161
157
  # markitdown_image_llm_prompt = ""
162
158
  # markitdown_enable_audio = false
163
- # markitdown_audio_model = "gpt-4o-mini-transcribe"
159
+ # markitdown_audio_model = "gpt-4o-mini-transcribe-2025-12-15"
164
160
  # markitdown_audio_provider = "openai"
165
161
  # markitdown_audio_api_key = ""
166
162
  # markitdown_audio_api_base = ""
@@ -180,22 +176,12 @@ If more than one provider key is set and the model does not make the provider ob
180
176
  ## Non-text files (PDF, images, audio)
181
177
 
182
178
  - PDF files are parsed with `pypdf`. Each page gets a marker line `===== Page N =====`, and output lines include a `page=N` suffix.
183
- - Images and audio are converted via `markitdown` when enabled in config. For image/audio conversion, an `openai` Python client is required.
179
+ - Images and audio are converted via `markitdown` when enabled in config. Image conversion supports `openai`, `anthropic`, and `gemini` providers; audio conversion currently supports `openai` only.
184
180
  - Converted image/audio text is cached in sidecar files named `<original>.<ext>.md` next to the original file and reused on subsequent runs.
185
181
  - Use `-a/--text` to force binary files to be read as text (UTF-8 with replacement).
186
182
 
187
- ## Agent usage notes
188
-
189
- - Prefer narrow corpora (globs/types) to reduce token usage.
190
- - Use `--max-llm-calls` to cap costs; combine with small `--max-iterations` for safety.
191
- - For reproducible parsing, use `-n -H` and avoid context (`-C/-A/-B`).
192
-
193
183
  ## Development
194
184
 
195
185
  - Install locally: `pip install -e .` or `uv tool install .`
196
186
  - Run: `rlmgrep "query" .`
197
187
  - No test suite is configured yet.
198
-
199
- ## Security
200
-
201
- Do not commit API keys. Use environment variables or `~/.rlmgrep/config.toml`.
@@ -0,0 +1,14 @@
1
+ rlmgrep/__init__.py,sha256=uEY-o5UpO0zjCdZ1SCWaClP-xXaDZDIXmwIfxAtFffI,48
2
+ rlmgrep/__main__.py,sha256=MHKZ_ae3fSLGTLUUMOx15fWdeOnJSHhq-zslRP5F5Lc,79
3
+ rlmgrep/cli.py,sha256=eUdM9aC0znZFzjqj-4YmT0_ymisSc_lUwDVjRi4ZU-s,20589
4
+ rlmgrep/config.py,sha256=u1iz-nI8dj-dZETbpIki3RQefHJEyi5oE5zE4_IR8kg,2399
5
+ rlmgrep/file_map.py,sha256=x2Ri1wzK8_87GUorsAV01K_nYLZcv30yIquDeTCcdEw,876
6
+ rlmgrep/ingest.py,sha256=uCz2el9B-RIT9umFo-gFEdAsmWPP1IJOArFFQY0D_1A,9127
7
+ rlmgrep/interpreter.py,sha256=s_nMRxLlAU9C0JmUzUBW5NbVbuH67doVWF54K54STlA,2478
8
+ rlmgrep/render.py,sha256=OYZy7BuJJe-KsDhEGAz6JA5RGd65ZInPWf9wLDJE0ag,3554
9
+ rlmgrep/rlm.py,sha256=i3rCTp8OABByF60Un5gO7265gaW4spwU0OFKIz4surg,5750
10
+ rlmgrep-0.1.6.dist-info/METADATA,sha256=g9zfDLY_x-QCcMzB0pcyXWZ6FoXr_c67mLiVRrOcHyI,6318
11
+ rlmgrep-0.1.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
+ rlmgrep-0.1.6.dist-info/entry_points.txt,sha256=UV6QkEbkwBO1JJ53mm84_n35tVyOczPvOQ14ga7vrCI,45
13
+ rlmgrep-0.1.6.dist-info/top_level.txt,sha256=gTujSRsO58c80eN7aRH2cfe51FHxx8LJ1w1Y2YlHti0,8
14
+ rlmgrep-0.1.6.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- rlmgrep/__init__.py,sha256=V2k5h8cZ-5Dt0onxqKP5wn7Rn2cmXmsSVcJMQZ9X1sY,48
2
- rlmgrep/__main__.py,sha256=MHKZ_ae3fSLGTLUUMOx15fWdeOnJSHhq-zslRP5F5Lc,79
3
- rlmgrep/cli.py,sha256=4qwR5cdQV0H40XZhLXdAO9tzlubeQCgA2sepWrXzKWI,18996
4
- rlmgrep/config.py,sha256=A6VLuuXSgQ1vM207CP0G92Mg3et93dGSmkkLQ0IOfwk,2388
5
- rlmgrep/file_map.py,sha256=x2Ri1wzK8_87GUorsAV01K_nYLZcv30yIquDeTCcdEw,876
6
- rlmgrep/ingest.py,sha256=uCz2el9B-RIT9umFo-gFEdAsmWPP1IJOArFFQY0D_1A,9127
7
- rlmgrep/interpreter.py,sha256=s_nMRxLlAU9C0JmUzUBW5NbVbuH67doVWF54K54STlA,2478
8
- rlmgrep/render.py,sha256=mxU7ByarJ6FU8ERt7biPVIOmpZQdKRrdyoZqlngploQ,3558
9
- rlmgrep/rlm.py,sha256=i3rCTp8OABByF60Un5gO7265gaW4spwU0OFKIz4surg,5750
10
- rlmgrep-0.1.4.dist-info/METADATA,sha256=s8iJbqKgLZSiUv7q4dkun4f40uDTQOYom3qjE1OMaks,6600
11
- rlmgrep-0.1.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
- rlmgrep-0.1.4.dist-info/entry_points.txt,sha256=UV6QkEbkwBO1JJ53mm84_n35tVyOczPvOQ14ga7vrCI,45
13
- rlmgrep-0.1.4.dist-info/top_level.txt,sha256=gTujSRsO58c80eN7aRH2cfe51FHxx8LJ1w1Y2YlHti0,8
14
- rlmgrep-0.1.4.dist-info/RECORD,,