ragx-cli 0.4.0__tar.gz → 0.5.0__tar.gz

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.
Files changed (100) hide show
  1. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/CHANGELOG.md +27 -0
  2. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/CLAUDE.md +16 -4
  3. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/PKG-INFO +63 -4
  4. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/README.md +62 -3
  5. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/pyproject.toml +1 -1
  6. ragx_cli-0.5.0/research/lm-studio-model-download-api-and-curated-embedding-reranker-catalog-for-ragx-models-command.md +119 -0
  7. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/skills/ragx-cli/SKILL.md +6 -0
  8. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/cli/app.py +10 -1
  9. ragx_cli-0.5.0/src/ragx/cli/models_cmd.py +230 -0
  10. ragx_cli-0.5.0/src/ragx/core/catalog.py +138 -0
  11. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/config.py +11 -2
  12. ragx_cli-0.5.0/src/ragx/core/lmstudio.py +106 -0
  13. ragx_cli-0.5.0/src/ragx/providers/llama_embedder.py +53 -0
  14. ragx_cli-0.5.0/src/ragx/providers/llama_process.py +108 -0
  15. ragx_cli-0.5.0/src/ragx/providers/llama_server.py +45 -0
  16. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/providers/registry.py +28 -0
  17. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_cli.py +4 -2
  18. ragx_cli-0.5.0/tests/test_llama_server.py +135 -0
  19. ragx_cli-0.5.0/tests/test_models.py +312 -0
  20. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/SKILL.md +0 -0
  21. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/api-cheatsheet.md +0 -0
  22. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/collection-management/python.md +0 -0
  23. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/collection-management/typescript.md +0 -0
  24. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/configuration.md +0 -0
  25. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/data-model.md +0 -0
  26. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/data-operations/python.md +0 -0
  27. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/data-operations/typescript.md +0 -0
  28. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/embedding.md +0 -0
  29. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/hybrid-search/python.md +0 -0
  30. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/hybrid-search/typescript.md +0 -0
  31. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/multimodal-search/python.md +0 -0
  32. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/multimodal-search/typescript.md +0 -0
  33. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/quick-start/python.md +0 -0
  34. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/quick-start/typescript.md +0 -0
  35. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/rag-system/python.md +0 -0
  36. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/rag-system/typescript.md +0 -0
  37. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/reranker.md +0 -0
  38. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/troubleshooting.md +0 -0
  39. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/vector-search/python.md +0 -0
  40. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.agents/skills/zvec/vector-search/typescript.md +0 -0
  41. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.github/workflows/publish.yml +0 -0
  42. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/.gitignore +0 -0
  43. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/AGENTS.md +0 -0
  44. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/CONTRACTS-PHASE23.md +0 -0
  45. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/CONTRACTS.md +0 -0
  46. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/LICENSE +0 -0
  47. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/docs/feature-temporal-weighting.md +0 -0
  48. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/ragx-cli-plan.md +0 -0
  49. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/research/bge-m3-colbert-multi-vector-late-interaction-for-ragx-rerank-alternative-and-token-level-graph-edges.md +0 -0
  50. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/research/bge-m3-dense-embeddings-as-ragx-provider-multilingual-quality-and-threshold-calibration.md +0 -0
  51. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/research/bge-m3-dense-q8-vs-nomic-q4-benchmark-2026-07-12-worktree-eval-results.md +0 -0
  52. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/research/bge-m3-sparse-lexical-weights-hybrid-retrieval-leg-for-ragx-vs-sqlite-fts5-bm25.md +0 -0
  53. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/research/bge-m3-subchunk-edge-ablation-2026-07-13-ties-again-and-beta-zero-shortlist-mechanism.md +0 -0
  54. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/research/fine-grained-sub-chunk-edges-with-coarse-chunk-nodes-multi-granularity-graph-rag-literature-validation.md +0 -0
  55. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/research/subchunk-edge-ablation-2026-07-12-two-round-experiment-protocol-results-and-verdict.md +0 -0
  56. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/skills-lock.json +0 -0
  57. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/__init__.py +0 -0
  58. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/cli/__init__.py +0 -0
  59. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/cli/detect.py +0 -0
  60. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/cli/eval_cmd.py +0 -0
  61. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/cli/init_prompt.py +0 -0
  62. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/cli/inspect_cmd.py +0 -0
  63. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/cli/output.py +0 -0
  64. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/cli/pipeline.py +0 -0
  65. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/__init__.py +0 -0
  66. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/chunking.py +0 -0
  67. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/communities.py +0 -0
  68. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/discovery.py +0 -0
  69. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/errors.py +0 -0
  70. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/eval.py +0 -0
  71. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/expansion.py +0 -0
  72. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/fusion.py +0 -0
  73. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/graph.py +0 -0
  74. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/indexer.py +0 -0
  75. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/models.py +0 -0
  76. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/query.py +0 -0
  77. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/scoring.py +0 -0
  78. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/store.py +0 -0
  79. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/traversal.py +0 -0
  80. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/core/vectors.py +0 -0
  81. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/providers/__init__.py +0 -0
  82. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/providers/base.py +0 -0
  83. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/providers/openai_compat.py +0 -0
  84. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/src/ragx/providers/st_reranker.py +0 -0
  85. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/__init__.py +0 -0
  86. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/conftest.py +0 -0
  87. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_chunking.py +0 -0
  88. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_communities.py +0 -0
  89. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_config_rc.py +0 -0
  90. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_discovery.py +0 -0
  91. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_eval.py +0 -0
  92. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_expansion.py +0 -0
  93. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_fusion.py +0 -0
  94. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_graph.py +0 -0
  95. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_inspect.py +0 -0
  96. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_integration.py +0 -0
  97. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_providers.py +0 -0
  98. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_store.py +0 -0
  99. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_traversal.py +0 -0
  100. {ragx_cli-0.4.0 → ragx_cli-0.5.0}/tests/test_vectors.py +0 -0
@@ -4,6 +4,33 @@ All notable changes to `ragx-cli` are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.5.0] — 2026-07-15
8
+
9
+ ### Added
10
+
11
+ - **`ragx-cli models`**: recommends an embedding + reranker combo (quality tier,
12
+ RAM/macOS detection), downloads models through LM Studio (`lms get --yes`), and writes
13
+ the config to `ragx.toml` (with an `index --full` warning when the embedding model
14
+ changed). Scriptable: `--quality fast|balanced|best|jina-nano`, `--embed-engine`,
15
+ `--rerank-engine`, `--yes`, `--dry-run`, `--json` (schema `ragx.models.v1`).
16
+ Interactive `init` offers the flow as its final step. Embedding catalog:
17
+ EmbeddingGemma-300M / BGE-M3 / Qwen3-Embedding-0.6B / Jina-v5-nano (CC-BY-NC,
18
+ llama-server engine only); reranker: bge-reranker-v2-m3 (multilingual).
19
+ - **`llama-server` rerank engine** (`rerank.provider = "llama-server"`): reranking via
20
+ llama.cpp's `/v1/rerank` on a GGUF (`rerank.gguf`), auto-spawning `llama-server`
21
+ (`rerank.server_bin`, `rerank.base_url`) when nothing is listening and terminating it
22
+ at exit. Reranker GGUFs download through LM Studio (`ragx-cli models --rerank-engine
23
+ llama-server`), so reranking needs **no direct huggingface.co access** and no torch/
24
+ sentence-transformers install. Q8_0 GGUFs validated against the safetensors originals
25
+ (scores within ~0.5 logit, identical ordering).
26
+ - **`llama-server` embedding engine** (`embeddings.provider = "llama-server"`): embeddings
27
+ served by an auto-spawned `llama-server --embedding` on `embeddings.gguf` (same managed
28
+ lifecycle as the rerank engine; default port 9813). `ragx-cli models --embed-engine
29
+ llama-server` wires it up. With both engines on llama-server, LM Studio is purely a
30
+ model downloader — nothing but ragx-managed processes run at query time. Also unblocks
31
+ EuroBERT models (e.g. `jina-embeddings-v5-text-nano-retrieval-GGUF`) that LM Studio
32
+ downloads but cannot serve.
33
+
7
34
  ## [0.4.0] — 2026-07-15
8
35
 
9
36
  ### Changed (BREAKING)
@@ -22,6 +22,9 @@ API contracts for every core module. Deferred feature specs live in `docs/`.
22
22
  hashing), pathspec (gitignore globs).
23
23
  - Providers speak **OpenAI-compatible HTTP**. Local default: LM Studio at
24
24
  `http://localhost:1234/v1`. `provider="ollama"` auto-switches to `:11434/v1`.
25
+ `embeddings.provider="llama-server"` instead auto-spawns `llama-server --embedding` on
26
+ `embeddings.gguf` (port 9813; shared lifecycle code in `providers/llama_process.py`) —
27
+ with the llama-server rerank engine, LM Studio is downloader-only at query time.
25
28
  Env fallbacks: `OPENAI_BASE_URL` (only while base_url is still the default) and
26
29
  `OPENAI_API_KEY` (only when `api_key_env` is unset); explicit config always wins.
27
30
  - Machine-level provider settings live in `~/.ragxrc` (TOML; ONLY the embeddings/
@@ -29,8 +32,11 @@ API contracts for every core module. Deferred feature specs live in `docs/`.
29
32
  ragx.toml < ~/.ragxrc — the rc OVERRIDES corpus values and logs a stderr warning
30
33
  per overridden key. Write via `ragx-cli config set --global <key> <value>`.
31
34
  `Config.save` never bakes rc values into the corpus file.
32
- - Reranker is always local: sentence-transformers CrossEncoder `BAAI/bge-reranker-v2-m3`
33
- (optional extra `ragx-cli[rerank]`). LM Studio has NO /v1/rerank endpoint — verified; don't try.
35
+ - Reranker is always local, two engines: sentence-transformers CrossEncoder (default,
36
+ `BAAI/bge-reranker-v2-m3`, optional extra `ragx-cli[rerank]`) or `rerank.provider=
37
+ "llama-server"` (llama.cpp `/v1/rerank` on a GGUF, auto-spawned from `rerank.gguf`;
38
+ GGUFs download via LM Studio — validated ≈ safetensors scores). LM Studio itself has
39
+ NO /v1/rerank endpoint — verified; don't try.
34
40
 
35
41
  ## Layout (core/CLI split is the one architectural rule — MCP server later rides on it)
36
42
 
@@ -57,10 +63,16 @@ src/ragx/
57
63
  indexer.py # run_index: discover->hash->chunk->embed->HNSW+store->kNN edges
58
64
  query.py # run_query: expansion->fan-out->RRF->traversal->rerank->combine; JSON serializers
59
65
  eval.py # recall@5/@10 + MRR over file-level ranking, injected query_fn
66
+ catalog.py # curated embedding/reranker catalog (tiers, prefixes) + spec detect
67
+ lmstudio.py # `lms` CLI wrapper: find/download (`lms get --yes`)/`lms ls --json`;
68
+ # LM Studio can't download plain safetensors — rerankers stay on HF
60
69
  providers/ # base.py protocols (Embedder/Generator/Reranker); openai_compat.py;
61
- # st_reranker.py; registry.py factories (env-var + api_key_env resolution)
70
+ # st_reranker.py; llama_process.py (managed llama-server lifecycle) +
71
+ # llama_server.py (rerank) + llama_embedder.py (embeddings);
72
+ # registry.py factories (env-var + api_key_env resolution)
62
73
  cli/ # thin shells only: app.py (init/status/config + registration),
63
- # pipeline.py (index/query), inspect_cmd.py, eval_cmd.py, output.py
74
+ # pipeline.py (index/query), inspect_cmd.py, eval_cmd.py, models_cmd.py
75
+ # (recommend/download/configure models), output.py
64
76
  tests/ # 177 tests; mocked HTTP (respx), FakeEmbedder integration tests, no live network
65
77
  ```
66
78
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ragx-cli
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Similarity-graph RAG CLI: index a corpus into a chunk-level embedding graph and query it
5
5
  Project-URL: Repository, https://github.com/alber70g/ragx-cli
6
6
  Project-URL: Issues, https://github.com/alber70g/ragx-cli/issues
@@ -52,6 +52,7 @@ ragx-cli init # create ragx.toml next to your corpus (interacti
52
52
  ragx-cli index # chunk -> embed -> HNSW + kNN similarity graph (incremental after the first run)
53
53
  ragx-cli query "why did we switch build tools?" --json --files-only
54
54
  ragx-cli index --full # full rebuild: re-chunk + re-embed everything
55
+ ragx-cli models # recommend + download an embedding/reranker combo via LM Studio
55
56
  ```
56
57
 
57
58
  Runs against any OpenAI-compatible embedding endpoint (LM Studio, Ollama, OpenAI). Reranking
@@ -319,6 +320,53 @@ Full write-ups: `research/bge-m3-dense-q8-vs-nomic-q4-benchmark-2026-07-12-workt
319
320
 
320
321
  ---
321
322
 
323
+ ## Picking models: `ragx-cli models`
324
+
325
+ `ragx-cli models` recommends an embedding + reranker combo, downloads the embedding model
326
+ **through LM Studio** (`lms get` — one download path, plays well with restricted networks),
327
+ pre-fetches the reranker from Hugging Face, and writes both to `ragx.toml`. Interactive on a
328
+ TTY (quality tier + engine questions, RAM auto-detected); scriptable via flags:
329
+
330
+ ```bash
331
+ ragx-cli models # interactive
332
+ ragx-cli models --quality balanced --yes --json # agents
333
+ ragx-cli models --quality fast --dry-run # recommend only, change nothing
334
+ ```
335
+
336
+ The curated catalog (every entry verified working end-to-end):
337
+
338
+ | tier | embedding model | notes |
339
+ |---|---|---|
340
+ | `fast` | EmbeddingGemma 300M | 308M, 100+ languages, 2048-token context |
341
+ | `balanced` | BGE-M3 | ragx's benchmarked production model (~100 languages) |
342
+ | `best` | Qwen3-Embedding-0.6B | 100+ languages, 32K context |
343
+ | `jina-nano` | Jina embeddings v5 nano | 239M, best-in-class sub-500M; **CC-BY-NC (non-commercial)**; llama-server engine only — LM Studio downloads it but can't serve EuroBERT |
344
+
345
+ **Serving engines.** Embeddings run through `--embed-engine`: **`llama-server`** (default when
346
+ llama.cpp is installed — ragx auto-spawns `llama-server --embedding` on the downloaded GGUF and
347
+ terminates it at exit; LM Studio is only the downloader) or **`lm-studio`** (LM Studio serves
348
+ `/v1/embeddings`; zero extra processes, LM Studio must be running). The reranker —
349
+ `BAAI/bge-reranker-v2-m3` (multilingual) — runs through `--rerank-engine`:
350
+
351
+ - **`llama-server`** (default when llama.cpp is installed): the reranker's Q8_0 GGUF is also
352
+ downloaded via LM Studio, and ragx auto-spawns/terminates a `llama-server --rerank` process
353
+ at query time (`rerank.provider="llama-server"`, `rerank.gguf`, `rerank.base_url`,
354
+ `rerank.server_bin`). **No huggingface.co access needed anywhere.** GGUF scores are validated
355
+ within ~0.5 logit of the safetensors originals.
356
+ - **`sentence-transformers`**: the classic CrossEncoder path; model pre-fetched from Hugging
357
+ Face (needs the `ragx-cli[rerank]` extra).
358
+
359
+ With both engines on `llama-server` the whole stack is ragx-managed: LM Studio downloads the
360
+ GGUFs, ragx spawns and reaps the servers, and neither LM Studio nor huggingface.co is needed
361
+ at query time. Under 8 GB RAM the embedding tier drops to `fast`; on macOS
362
+ `lms get --yes` picks MLX variants where LM Studio's catalog offers them. Switching the
363
+ embedding model invalidates the index — the command tells you to run `ragx-cli index --full`.
364
+ `init` offers this flow as its final step; rerun `ragx-cli models` anytime to switch.
365
+
366
+ Why not `jina-embeddings-v5-text-nano`? Excellent model, but its EuroBERT architecture needs
367
+ Jina's llama.cpp fork — stock LM Studio can't load it (and the license is CC-BY-NC). See
368
+ `research/lm-studio-model-download-api-and-curated-embedding-reranker-catalog-for-ragx-models-command.md`.
369
+
322
370
  ## Agent-first conventions
323
371
 
324
372
  - `--json` emits exactly one JSON document on stdout (versioned schemas: `ragx.query.v1`,
@@ -409,9 +457,19 @@ detects the mismatch and asks you to run a full `ragx-cli index`.
409
457
 
410
458
  ### Reranker on restricted networks (no huggingface.co)
411
459
 
412
- The reranker model (`BAAI/bge-reranker-v2-m3`, ~2.3 GB) is downloaded from huggingface.co on
413
- first use. If your network blocks huggingface.co, ragx-cli degrades gracefully — queries run
414
- without reranking and a warning explains why — but you have three ways to get reranking working:
460
+ With the default `sentence-transformers` engine, the reranker model
461
+ (`BAAI/bge-reranker-v2-m3`, ~2.3 GB) is downloaded from huggingface.co on first use. If your
462
+ network blocks huggingface.co, ragx-cli degrades gracefully — queries run without reranking
463
+ and a warning explains why — and the easiest fix is to skip huggingface.co entirely:
464
+
465
+ **Option 0 — the `llama-server` engine (recommended).** `ragx-cli models --rerank-engine
466
+ llama-server` downloads a validated Q8_0 GGUF of the reranker **through LM Studio** (same
467
+ download path as the embedding model) and reranks via llama.cpp's `llama-server --rerank`,
468
+ which ragx starts and stops automatically. Requires a recent llama.cpp
469
+ (`brew install llama.cpp`); no torch, no sentence-transformers, no huggingface.co. The GGUFs
470
+ score within ~0.5 logit of the safetensors originals with identical ordering.
471
+
472
+ Prefer the sentence-transformers engine? Three more ways:
415
473
 
416
474
  **Option A — copy the model and point `rerank.model` at the directory** (recommended).
417
475
  On any machine with access:
@@ -516,6 +574,7 @@ unchecked ones are next up. Release history: [CHANGELOG.md](CHANGELOG.md).
516
574
  - [x] **Offline-friendly rerank**: graceful degradation + pre-seeding docs when huggingface.co is unreachable
517
575
  - [x] **Sub-chunk edges** (opt-in `graph.edge_source="subchunk"`): edge weight = max cosine over sentence-aligned sub-chunk pairs, for corpora with long multi-concept chunks
518
576
  - [x] **Incremental by default** (0.4.0): `index` hash-diffs, `--full` rebuilds, `status` reports corpus drift, chunking params are manifest-guarded
577
+ - [x] **`ragx-cli models`**: curated embedding/reranker recommendation (quality tier + RAM detection), downloads via LM Studio's `lms get`, serving via llama-server or LM Studio
519
578
  - [ ] **Community labels**: name the Leiden communities so they're browsable without reading member chunks
520
579
  - [ ] **query --global** for corpus-level questions (answer from community summaries, not individual chunks)
521
580
  - [ ] **MCP server**: a second thin shell over `ragx.core` (the core/CLI split it needs is already enforced)
@@ -24,6 +24,7 @@ ragx-cli init # create ragx.toml next to your corpus (interacti
24
24
  ragx-cli index # chunk -> embed -> HNSW + kNN similarity graph (incremental after the first run)
25
25
  ragx-cli query "why did we switch build tools?" --json --files-only
26
26
  ragx-cli index --full # full rebuild: re-chunk + re-embed everything
27
+ ragx-cli models # recommend + download an embedding/reranker combo via LM Studio
27
28
  ```
28
29
 
29
30
  Runs against any OpenAI-compatible embedding endpoint (LM Studio, Ollama, OpenAI). Reranking
@@ -291,6 +292,53 @@ Full write-ups: `research/bge-m3-dense-q8-vs-nomic-q4-benchmark-2026-07-12-workt
291
292
 
292
293
  ---
293
294
 
295
+ ## Picking models: `ragx-cli models`
296
+
297
+ `ragx-cli models` recommends an embedding + reranker combo, downloads the embedding model
298
+ **through LM Studio** (`lms get` — one download path, plays well with restricted networks),
299
+ pre-fetches the reranker from Hugging Face, and writes both to `ragx.toml`. Interactive on a
300
+ TTY (quality tier + engine questions, RAM auto-detected); scriptable via flags:
301
+
302
+ ```bash
303
+ ragx-cli models # interactive
304
+ ragx-cli models --quality balanced --yes --json # agents
305
+ ragx-cli models --quality fast --dry-run # recommend only, change nothing
306
+ ```
307
+
308
+ The curated catalog (every entry verified working end-to-end):
309
+
310
+ | tier | embedding model | notes |
311
+ |---|---|---|
312
+ | `fast` | EmbeddingGemma 300M | 308M, 100+ languages, 2048-token context |
313
+ | `balanced` | BGE-M3 | ragx's benchmarked production model (~100 languages) |
314
+ | `best` | Qwen3-Embedding-0.6B | 100+ languages, 32K context |
315
+ | `jina-nano` | Jina embeddings v5 nano | 239M, best-in-class sub-500M; **CC-BY-NC (non-commercial)**; llama-server engine only — LM Studio downloads it but can't serve EuroBERT |
316
+
317
+ **Serving engines.** Embeddings run through `--embed-engine`: **`llama-server`** (default when
318
+ llama.cpp is installed — ragx auto-spawns `llama-server --embedding` on the downloaded GGUF and
319
+ terminates it at exit; LM Studio is only the downloader) or **`lm-studio`** (LM Studio serves
320
+ `/v1/embeddings`; zero extra processes, LM Studio must be running). The reranker —
321
+ `BAAI/bge-reranker-v2-m3` (multilingual) — runs through `--rerank-engine`:
322
+
323
+ - **`llama-server`** (default when llama.cpp is installed): the reranker's Q8_0 GGUF is also
324
+ downloaded via LM Studio, and ragx auto-spawns/terminates a `llama-server --rerank` process
325
+ at query time (`rerank.provider="llama-server"`, `rerank.gguf`, `rerank.base_url`,
326
+ `rerank.server_bin`). **No huggingface.co access needed anywhere.** GGUF scores are validated
327
+ within ~0.5 logit of the safetensors originals.
328
+ - **`sentence-transformers`**: the classic CrossEncoder path; model pre-fetched from Hugging
329
+ Face (needs the `ragx-cli[rerank]` extra).
330
+
331
+ With both engines on `llama-server` the whole stack is ragx-managed: LM Studio downloads the
332
+ GGUFs, ragx spawns and reaps the servers, and neither LM Studio nor huggingface.co is needed
333
+ at query time. Under 8 GB RAM the embedding tier drops to `fast`; on macOS
334
+ `lms get --yes` picks MLX variants where LM Studio's catalog offers them. Switching the
335
+ embedding model invalidates the index — the command tells you to run `ragx-cli index --full`.
336
+ `init` offers this flow as its final step; rerun `ragx-cli models` anytime to switch.
337
+
338
+ Why not `jina-embeddings-v5-text-nano`? Excellent model, but its EuroBERT architecture needs
339
+ Jina's llama.cpp fork — stock LM Studio can't load it (and the license is CC-BY-NC). See
340
+ `research/lm-studio-model-download-api-and-curated-embedding-reranker-catalog-for-ragx-models-command.md`.
341
+
294
342
  ## Agent-first conventions
295
343
 
296
344
  - `--json` emits exactly one JSON document on stdout (versioned schemas: `ragx.query.v1`,
@@ -381,9 +429,19 @@ detects the mismatch and asks you to run a full `ragx-cli index`.
381
429
 
382
430
  ### Reranker on restricted networks (no huggingface.co)
383
431
 
384
- The reranker model (`BAAI/bge-reranker-v2-m3`, ~2.3 GB) is downloaded from huggingface.co on
385
- first use. If your network blocks huggingface.co, ragx-cli degrades gracefully — queries run
386
- without reranking and a warning explains why — but you have three ways to get reranking working:
432
+ With the default `sentence-transformers` engine, the reranker model
433
+ (`BAAI/bge-reranker-v2-m3`, ~2.3 GB) is downloaded from huggingface.co on first use. If your
434
+ network blocks huggingface.co, ragx-cli degrades gracefully — queries run without reranking
435
+ and a warning explains why — and the easiest fix is to skip huggingface.co entirely:
436
+
437
+ **Option 0 — the `llama-server` engine (recommended).** `ragx-cli models --rerank-engine
438
+ llama-server` downloads a validated Q8_0 GGUF of the reranker **through LM Studio** (same
439
+ download path as the embedding model) and reranks via llama.cpp's `llama-server --rerank`,
440
+ which ragx starts and stops automatically. Requires a recent llama.cpp
441
+ (`brew install llama.cpp`); no torch, no sentence-transformers, no huggingface.co. The GGUFs
442
+ score within ~0.5 logit of the safetensors originals with identical ordering.
443
+
444
+ Prefer the sentence-transformers engine? Three more ways:
387
445
 
388
446
  **Option A — copy the model and point `rerank.model` at the directory** (recommended).
389
447
  On any machine with access:
@@ -488,6 +546,7 @@ unchecked ones are next up. Release history: [CHANGELOG.md](CHANGELOG.md).
488
546
  - [x] **Offline-friendly rerank**: graceful degradation + pre-seeding docs when huggingface.co is unreachable
489
547
  - [x] **Sub-chunk edges** (opt-in `graph.edge_source="subchunk"`): edge weight = max cosine over sentence-aligned sub-chunk pairs, for corpora with long multi-concept chunks
490
548
  - [x] **Incremental by default** (0.4.0): `index` hash-diffs, `--full` rebuilds, `status` reports corpus drift, chunking params are manifest-guarded
549
+ - [x] **`ragx-cli models`**: curated embedding/reranker recommendation (quality tier + RAM detection), downloads via LM Studio's `lms get`, serving via llama-server or LM Studio
491
550
  - [ ] **Community labels**: name the Leiden communities so they're browsable without reading member chunks
492
551
  - [ ] **query --global** for corpus-level questions (answer from community summaries, not individual chunks)
493
552
  - [ ] **MCP server**: a second thin shell over `ragx.core` (the core/CLI split it needs is already enforced)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ragx-cli"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Similarity-graph RAG CLI: index a corpus into a chunk-level embedding graph and query it"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -0,0 +1,119 @@
1
+ # LM Studio model downloads + curated embedding/reranker catalog for `ragx-cli models`
2
+
3
+ Date: 2026-07-15. Sources: lmstudio.ai official docs, lmstudio-ai/lms + lmstudio-python source,
4
+ Hugging Face model cards, live tests on LM Studio 0.4.12 (this machine). Full agent reports
5
+ summarized here; doc URLs at the bottom.
6
+
7
+ ## 1. How to trigger LM Studio downloads programmatically
8
+
9
+ Three mechanisms exist; capabilities verified live on 0.4.12:
10
+
11
+ | Mechanism | Unattended | Needs server up | Notes |
12
+ |---|---|---|---|
13
+ | `lms get <ref> --yes` | yes (exit 0/1, ANSI noise on stdout) | **no** | `<ref>` = catalog id (`google/embedding-gemma-300m`), search term, or full HF URL; `@quant` suffix; `--gguf`/`--mlx` filters. `--yes` auto-picks the hardware-preferred variant. |
14
+ | REST `POST /api/v1/models/download` + poll `GET /api/v1/models/download/status/:job_id` | yes (clean JSON) | yes, ≥0.4.0 | structured errors (`model_not_found`); optional bearer auth off by default. |
15
+ | Python SDK `lmstudio` (`client.repository.search_models → get_download_options → download`) | yes (blocking + progress callback) | yes | extra dependency; nicest API. |
16
+
17
+ - `lms` ships with the app (linked at `~/.lmstudio/bin/lms` after first app run); detect via
18
+ `shutil.which("lms")` + that fallback path. `lms version` prints no semver (use the app plist
19
+ on macOS if ever needed).
20
+ - Verify success + resolve the served model key via `lms ls --json`: entries carry `modelKey`,
21
+ `type` (`"embedding"`/`"llm"`), `format` (`gguf`/`mlx`), `path` (relative to models root),
22
+ `sizeBytes`, `architecture`.
23
+ - Downloads land in `<lmstudio-home>/models/{publisher}/{repo}/…`; home found via
24
+ `~/.lmstudio-home-pointer` (internal, not contractual).
25
+ - REST has **no** catalog-search endpoint; SDK does.
26
+
27
+ **Hard limitation (verified live, twice):** LM Studio only downloads GGUF or MLX-converted
28
+ artifacts. Plain transformers safetensors repos are rejected at resolution time —
29
+ `lms get https://huggingface.co/Alibaba-NLP/gte-reranker-modernbert-base --yes` → exit 1,
30
+ "No download options available". A GGUF conversion of that reranker exists
31
+ (`jolleyboy/gte-reranker-modernbert-base-GGUF`) and downloads fine, but sentence-transformers
32
+ cannot load GGUF, and LM Studio has no /v1/rerank endpoint to serve it. **Conclusion: the
33
+ reranker cannot ride the LM Studio download path in any usable form; it stays on the
34
+ HF/sentence-transformers path** (huggingface_hub `snapshot_download` for pre-fetching).
35
+
36
+ ## 2. Requested models — verdicts
37
+
38
+ ### jina-embeddings-v5-text-nano (`jinaai/jina-embeddings-v5-text-nano`)
39
+ Real (released 2026-02-18): 239M EuroBERT-based, 768 dims (Matryoshka 32–768), 8192 ctx,
40
+ 71.0 MTEB-en — best-in-class sub-500M. BUT:
41
+ - **EuroBERT is not supported by stock llama.cpp** — Jina's cards say to use *their fork*;
42
+ LM Studio ships stock llama.cpp, so the GGUF repos (`…-nano-retrieval[-GGUF]`) won't load.
43
+ - The MLX ports (`…-nano-mlx`) are custom-code (own `model.py`), not LM Studio-loadable.
44
+ - License **CC-BY-NC-4.0** (non-commercial) — bad default for a recommendation catalog.
45
+ → Excluded from the catalog until upstream llama.cpp support lands. Revisit: watch
46
+ ggml-org/llama.cpp for EuroBERT and ollama/ollama#14641.
47
+
48
+ ### gte-reranker-modernbert-base (`Alibaba-NLP/gte-reranker-modernbert-base`)
49
+ 149M ModernBERT cross-encoder, 8192 ctx, Apache-2.0, loads in `CrossEncoder(...)` natively
50
+ (transformers ≥4.48, no trust_remote_code). BEIR 56.73; third-party Feb-2026 benchmark: tied
51
+ best Hit@1 at 8× smaller than a 1.2B competitor, ahead of bge-reranker-v2-m3 — on **English**.
52
+ It is English-only; ragx's eval corpus is EN+NL where bge-reranker-v2-m3's multilingual
53
+ training is the reason the Dutch recall win exists. → Catalog **English tier** reranker;
54
+ bge-reranker-v2-m3 stays the multilingual tier/default.
55
+
56
+ ## 3. Curated catalog (all embedding entries verified LM Studio-loadable)
57
+
58
+ | Tier | Model | Why | Prefixes |
59
+ |---|---|---|---|
60
+ | fast | `lmstudio-community/embeddinggemma-300m-qat-GGUF` (curated catalog id `google/embedding-gemma-300m`) | 308M, 100+ langs, in LM Studio's own catalog; 2048 ctx / 768 dims cap quality | query `"task: search result \| query: "`, doc `"title: none \| text: "` |
61
+ | balanced | BGE-M3 GGUF (`gaianet/bge-m3-GGUF`) | current ragx production model; ~100 langs, 1024 dims, 8192 ctx, MIT; benchmarked best on the ragx eval corpus | none |
62
+ | best | `Qwen/Qwen3-Embedding-0.6B-GGUF` | Apache-2.0, 100+ langs, 32K ctx, MRL 1024 dims; top multilingual MTEB family; runs on upstream llama.cpp (early 0.3.16/0.3.17 LM Studio bugs since fixed) | query instruct prefix, doc none |
63
+ | reranker (multilingual, default) | `BAAI/bge-reranker-v2-m3` | current default; only clean multilingual CrossEncoder without trust_remote_code | – |
64
+ | reranker (English) | `Alibaba-NLP/gte-reranker-modernbert-base` | 149M, faster, beats bge-v2-m3 on EN benchmarks | – |
65
+
66
+ Rejected alternatives: `gte-multilingual-reranker-base` (306M, good numbers, but needs
67
+ trust_remote_code — security/UX friction), `jina-reranker-v2-base-multilingual` (NC license,
68
+ trust_remote_code, 1024-tok window), `Qwen3-Reranker-0.6B` (causal-LM yes/no scoring, doesn't
69
+ fit the CrossEncoder wrapper).
70
+
71
+ All three embedding tiers are multilingual, so the "multilingual?" question only steers the
72
+ reranker (EN → gte, otherwise bge-v2-m3). Spec detection: total RAM (<8 GB → force fast tier);
73
+ macOS → let `lms get --yes` hardware-preselect MLX where LM Studio's catalog offers it
74
+ (pinned GGUF URLs stay GGUF — they run on Metal fine; that's how bge-m3 Q8 runs today).
75
+
76
+ ## 4. Design consequences for `ragx-cli models`
77
+
78
+ - Prefer `lms` CLI subprocess over REST/SDK: works with the server down, zero new deps.
79
+ Absent `lms` → graceful message pointing at lmstudio.ai.
80
+ - After `lms get` exit 0: re-run `lms ls --json`, match the new entry by repo fragment, use its
81
+ `modelKey` as `embeddings.model`, and write the tier's doc/query prefixes to config.
82
+ - Reranker pre-fetch: `huggingface_hub.snapshot_download` (ships with the `rerank` extra);
83
+ degrade to a printed hint when the extra isn't installed.
84
+ - Changing `embeddings.model` invalidates the index (manifest guard) → command must end with a
85
+ loud `ragx-cli index --full` pointer, never auto-reindex.
86
+
87
+ ## Decision outcomes (2026-07-15, reviewed by Albert — folded from the session's DECISIONS.md)
88
+
89
+ 1. **jina-embeddings-v5-text-nano**: first excluded (stock LM Studio couldn't load EuroBERT),
90
+ then EuroBERT landed in upstream llama.cpp and the retrieval GGUF proved out end-to-end via
91
+ `llama-server --embedding` → **included as the 4th catalog option** (`--quality jina-nano`),
92
+ llama-server engine only, CC-BY-NC warning attached. LM Studio still downloads-but-misclassifies
93
+ it (`type: "llm"` → its /v1/embeddings refuses; candidate LM Studio bug report).
94
+ 2. **Reranker delivery**: HF-only at first (LM Studio rejects safetensors; ST can't read GGUF);
95
+ superseded the same day by `rerank.provider="llama-server"` — GGUF via LM Studio, served by
96
+ an auto-spawned `llama-server --rerank`. Q8_0 GGUFs validated ≈ safetensors (within ~0.5
97
+ logit, identical ordering — the all-negative bge logits on toy Dutch pairs match safetensors
98
+ exactly; model behavior, not conversion loss). Verified on brew llama.cpp 9960.
99
+ 3. **gte-reranker-modernbert-base (English-only) dropped entirely** per Albert — one multilingual
100
+ reranker (bge-reranker-v2-m3) keeps the measured Dutch-recall win; the multilingual question
101
+ left the CLI with it.
102
+ 4. **Downloads**: `lms get --yes` subprocess (works with the server down, zero deps) over REST/SDK.
103
+ 5. **MLX**: no hard pinning; `lms get --yes` hardware-preselects; explicit `gguf_ref`s exist per
104
+ entry because the llama-server engine can't load MLX.
105
+ 6. **Embedding tiers**: embeddinggemma-300m / bge-m3 / qwen3-embedding-0.6b confirmed by Albert.
106
+ 7. **Single-engine mode** (Albert's direction): `embeddings.provider="llama-server"` mirrors the
107
+ rerank engine (shared `providers/llama_process.py`, ports 9813/9814, atexit reaping); both
108
+ engine prompts default to llama-server when the binary exists — LM Studio becomes
109
+ downloader-only at query time. Trade-off: ~1–2 s cold-spawn per engine per process lifetime.
110
+ 8. **No auto-reindex on model switch** — loud `ragx-cli index --full` pointer instead; rebuild
111
+ spend stays explicit.
112
+
113
+ ## Doc URLs
114
+
115
+ lmstudio.ai/docs/cli · /docs/cli/get · /docs/developer/rest · /docs/developer/rest/download ·
116
+ /docs/developer/rest/download-status · /docs/python · github.com/lmstudio-ai/lms ·
117
+ github.com/lmstudio-ai/lmstudio-python · huggingface.co/jinaai/jina-embeddings-v5-text-nano ·
118
+ huggingface.co/Alibaba-NLP/gte-reranker-modernbert-base · huggingface.co/Qwen/Qwen3-Embedding-0.6B-GGUF ·
119
+ lmstudio.ai/models/google/embedding-gemma-300m · huggingface.co/BAAI/bge-reranker-v2-m3
@@ -55,6 +55,12 @@ Other query flags: `--no-graph`, `--hops N`, `--explain`.
55
55
  - Changing `embeddings.model`, `chunking.*`, or `graph.edge_source` invalidates the index:
56
56
  commands fail loud on the manifest mismatch, and the fix is `ragx-cli index --full` —
57
57
  re-embeds everything, so confirm before running on a large corpus.
58
+ - `ragx-cli models` picks + downloads an embedding/reranker combo. All files download via
59
+ LM Studio; serving is per engine — `--embed-engine llama-server|lm-studio` and
60
+ `--rerank-engine llama-server|sentence-transformers`. With both on `llama-server`
61
+ (the default when llama.cpp is installed), ragx auto-spawns/reaps the servers and
62
+ needs neither LM Studio nor huggingface.co at query time. Other flags: `--quality
63
+ fast|balanced|best|jina-nano`, `--yes`, `--dry-run`, `--json`.
58
64
 
59
65
  ## Why did/didn't a result appear?
60
66
 
@@ -27,11 +27,12 @@ app = typer.Typer(add_completion=False, no_args_is_help=True)
27
27
  config_app = typer.Typer(add_completion=False, no_args_is_help=True)
28
28
  app.add_typer(config_app, name="config")
29
29
 
30
- from ragx.cli import eval_cmd, inspect_cmd, pipeline # noqa: E402 (needs `app` defined above)
30
+ from ragx.cli import eval_cmd, inspect_cmd, models_cmd, pipeline # noqa: E402 (needs `app` defined above)
31
31
 
32
32
  pipeline.register(app)
33
33
  inspect_cmd.register(app)
34
34
  eval_cmd.register(app)
35
+ models_cmd.register(app)
35
36
 
36
37
 
37
38
  def _require_root() -> Path:
@@ -77,6 +78,14 @@ def init(
77
78
  else:
78
79
  cfg_path = write_default_config(root)
79
80
  typer.echo(f"created {cfg_path}")
81
+ if ask and typer.confirm(
82
+ "recommend + download models via LM Studio now? (rerun anytime: `ragx-cli models`)",
83
+ default=False,
84
+ ):
85
+ try:
86
+ models_cmd.run_flow(root)
87
+ except RagxError as exc:
88
+ fail(str(exc))
80
89
 
81
90
 
82
91
  def _counts(root: Path) -> tuple[int, int, int, int]: