ragx-cli 0.5.1__tar.gz → 0.7.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.
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/CHANGELOG.md +50 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/CLAUDE.md +16 -4
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/PKG-INFO +79 -14
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/README.md +77 -12
- ragx_cli-0.7.0/docs/proposal-models-command-interactive-flow.md +131 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/pyproject.toml +2 -2
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/cli/app.py +3 -1
- ragx_cli-0.7.0/src/ragx/cli/doctor_cmd.py +62 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/cli/models_cmd.py +72 -40
- ragx_cli-0.7.0/src/ragx/cli/models_ui.py +117 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/cli/pipeline.py +14 -1
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/catalog.py +54 -12
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/config.py +17 -0
- ragx_cli-0.7.0/src/ragx/core/doctor.py +204 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/lmstudio.py +49 -13
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/providers/llama_process.py +12 -1
- ragx_cli-0.7.0/src/ragx/providers/lmstudio_process.py +120 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/providers/registry.py +11 -17
- ragx_cli-0.7.0/tests/conftest.py +24 -0
- ragx_cli-0.7.0/tests/test_doctor.py +231 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_llama_server.py +43 -0
- ragx_cli-0.7.0/tests/test_lmstudio_autostart.py +142 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_models.py +127 -4
- ragx_cli-0.5.1/tests/conftest.py +0 -13
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/SKILL.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/api-cheatsheet.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/collection-management/python.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/collection-management/typescript.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/configuration.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/data-model.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/data-operations/python.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/data-operations/typescript.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/embedding.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/hybrid-search/python.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/hybrid-search/typescript.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/multimodal-search/python.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/multimodal-search/typescript.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/quick-start/python.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/quick-start/typescript.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/rag-system/python.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/rag-system/typescript.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/reranker.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/troubleshooting.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/vector-search/python.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.agents/skills/zvec/vector-search/typescript.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.github/workflows/publish.yml +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/.gitignore +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/AGENTS.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/CONTRACTS-PHASE23.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/CONTRACTS.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/LICENSE +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/docs/feature-temporal-weighting.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/ragx-cli-plan.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/research/bge-m3-colbert-multi-vector-late-interaction-for-ragx-rerank-alternative-and-token-level-graph-edges.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/research/bge-m3-dense-embeddings-as-ragx-provider-multilingual-quality-and-threshold-calibration.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/research/bge-m3-dense-q8-vs-nomic-q4-benchmark-2026-07-12-worktree-eval-results.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/research/bge-m3-sparse-lexical-weights-hybrid-retrieval-leg-for-ragx-vs-sqlite-fts5-bm25.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/research/bge-m3-subchunk-edge-ablation-2026-07-13-ties-again-and-beta-zero-shortlist-mechanism.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/research/fine-grained-sub-chunk-edges-with-coarse-chunk-nodes-multi-granularity-graph-rag-literature-validation.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/research/lm-studio-model-download-api-and-curated-embedding-reranker-catalog-for-ragx-models-command.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/research/subchunk-edge-ablation-2026-07-12-two-round-experiment-protocol-results-and-verdict.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/skills/ragx-cli/SKILL.md +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/skills-lock.json +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/__init__.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/cli/__init__.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/cli/detect.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/cli/eval_cmd.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/cli/init_prompt.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/cli/inspect_cmd.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/cli/output.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/__init__.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/chunking.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/communities.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/discovery.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/errors.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/eval.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/expansion.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/fusion.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/graph.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/indexer.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/models.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/query.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/scoring.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/store.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/traversal.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/core/vectors.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/providers/__init__.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/providers/base.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/providers/llama_embedder.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/providers/llama_server.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/providers/openai_compat.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/src/ragx/providers/st_reranker.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/__init__.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_chunking.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_cli.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_communities.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_config_rc.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_discovery.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_eval.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_expansion.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_fusion.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_graph.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_inspect.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_integration.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_providers.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_store.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_traversal.py +0 -0
- {ragx_cli-0.5.1 → ragx_cli-0.7.0}/tests/test_vectors.py +0 -0
|
@@ -4,6 +4,56 @@ 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.7.0] — 2026-09-17
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`ragx-cli doctor`**: runs the configured providers for real — embeds a probe string,
|
|
12
|
+
scores a probe pair with the reranker, and checks the index manifest and corpus drift —
|
|
13
|
+
then reports every stage with an actionable hint. Starts whatever ragx manages, exactly
|
|
14
|
+
as `index`/`query` would. Exit `0` healthy, `1` a check failed, `2` doctor could not run
|
|
15
|
+
(no corpus, unreadable config); warnings (no index yet, drift, a model the server does
|
|
16
|
+
not list but may JIT-load) do not fail the run. `--json` emits `ragx.doctor.v1` with a
|
|
17
|
+
`status`/`detail`/`hint`/`elapsed_ms` entry per check.
|
|
18
|
+
- **LM Studio autostart**: with `provider = "openai"` and a local `base_url`, `index`,
|
|
19
|
+
`query`, and `doctor` now run `lms server start` and `lms load <model>` before their
|
|
20
|
+
first request, so a cold machine needs no manual setup. ragx already spawned the
|
|
21
|
+
`llama-server` engines; LM Studio is an app ragx hands off to, so it is started but
|
|
22
|
+
never stopped. Only local URLs are touched (a cloud endpoint, including one set via
|
|
23
|
+
`OPENAI_BASE_URL`, is never started), an already-running server is left alone, and a
|
|
24
|
+
model that is not downloaded fails loud naming the models the machine does have.
|
|
25
|
+
Opt out with `embeddings.autostart` / `expansion.autostart` (default `true`, settable
|
|
26
|
+
machine-wide via `ragx-cli config set --global`).
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **`llama-server` returned 500 on every real query.** The server was spawned without
|
|
31
|
+
`-b`/`-ub`/`-c`, leaving llama.cpp's default 512-token physical batch in place; a rerank
|
|
32
|
+
or embedding input is non-causal and cannot be split across ubatches, so ragx's
|
|
33
|
+
~800-token chunks overflowed it ("input is too large to process"). Two-document smoke
|
|
34
|
+
tests passed, which is why it went unnoticed. Now spawned with
|
|
35
|
+
`--parallel 1 -c 0 -b 8192 -ub 8192` — one slot with the model's full trained context.
|
|
36
|
+
Fixes the `llama-server` embedding engine at default chunk size as well as rerank.
|
|
37
|
+
- **`ragx-cli models --quality balanced` could not download BGE-M3.** `gaianet/bge-m3-GGUF`
|
|
38
|
+
now answers 401 from the Hugging Face API; both refs point at `gpustack/bge-m3-GGUF@Q8_0`
|
|
39
|
+
(same model, already the catalog's reranker source, pinned to the benchmarked quant).
|
|
40
|
+
|
|
41
|
+
## [0.6.0] — 2026-07-15
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- **`ragx-cli models` interactive flow redesigned** around context → choice → consequence →
|
|
46
|
+
commit: a machine header (RAM, platform, `llama-server`/`lms` detection) prints first;
|
|
47
|
+
the quality tier is picked from a numbered comparison table (model, size, languages,
|
|
48
|
+
context, license) and the serving engines from numbered menus with one-line tradeoffs —
|
|
49
|
+
recommended defaults marked `»`, invalid input re-prompts; a plan step (download sizes,
|
|
50
|
+
`already downloaded ✓`, config sections, re-index consequence) shows before the confirm.
|
|
51
|
+
Behavior changes: on <8 GB RAM machines an interactive pick of `balanced`/`best` is
|
|
52
|
+
confirmed and then honored instead of silently downgraded (non-interactive runs keep the
|
|
53
|
+
auto-downgrade); `--quality jina-nano` without `--embed-engine` now locks the embedding
|
|
54
|
+
engine to `llama-server` instead of erroring (an explicit `--embed-engine lm-studio`
|
|
55
|
+
still fails loud). Flags, `--json` output, and the non-interactive contract are unchanged.
|
|
56
|
+
|
|
7
57
|
## [0.5.0] — 2026-07-15
|
|
8
58
|
|
|
9
59
|
### Added
|
|
@@ -27,6 +27,11 @@ API contracts for every core module. Deferred feature specs live in `docs/`.
|
|
|
27
27
|
with the llama-server rerank engine, LM Studio is downloader-only at query time.
|
|
28
28
|
Env fallbacks: `OPENAI_BASE_URL` (only while base_url is still the default) and
|
|
29
29
|
`OPENAI_API_KEY` (only when `api_key_env` is unset); explicit config always wins.
|
|
30
|
+
With `provider="openai"` and a LOCAL base_url, `index`/`query`/`doctor` autostart LM
|
|
31
|
+
Studio first (`providers/lmstudio_process.py`: `lms server start` + `lms load` when the
|
|
32
|
+
model isn't in `lms ps`; never stops it — it's an app, not our child). Remote URLs and
|
|
33
|
+
`<section>.autostart=false` skip it. Tests are disarmed by an autouse conftest fixture
|
|
34
|
+
that stubs `ensure_for_section` — never let a test reach the real `lms`.
|
|
30
35
|
- Machine-level provider settings live in `~/.ragxrc` (TOML; ONLY the embeddings/
|
|
31
36
|
expansion/rerank sections, unknown keys fail loud). Precedence: DEFAULTS < corpus
|
|
32
37
|
ragx.toml < ~/.ragxrc — the rc OVERRIDES corpus values and logs a stderr warning
|
|
@@ -63,17 +68,20 @@ src/ragx/
|
|
|
63
68
|
indexer.py # run_index: discover->hash->chunk->embed->HNSW+store->kNN edges
|
|
64
69
|
query.py # run_query: expansion->fan-out->RRF->traversal->rerank->combine; JSON serializers
|
|
65
70
|
eval.py # recall@5/@10 + MRR over file-level ranking, injected query_fn
|
|
71
|
+
doctor.py # run_doctor: config/embeddings/expansion/rerank/index health checks
|
|
72
|
+
# (injected provider factories; backends get started here too)
|
|
66
73
|
catalog.py # curated embedding/reranker catalog (tiers, prefixes) + spec detect
|
|
67
74
|
lmstudio.py # `lms` CLI wrapper: find/download (`lms get --yes`)/`lms ls --json`;
|
|
68
75
|
# LM Studio can't download plain safetensors — rerankers stay on HF
|
|
69
76
|
providers/ # base.py protocols (Embedder/Generator/Reranker); openai_compat.py;
|
|
70
|
-
# st_reranker.py;
|
|
77
|
+
# st_reranker.py; lmstudio_process.py (LM Studio autostart);
|
|
78
|
+
# llama_process.py (managed llama-server lifecycle) +
|
|
71
79
|
# llama_server.py (rerank) + llama_embedder.py (embeddings);
|
|
72
80
|
# registry.py factories (env-var + api_key_env resolution)
|
|
73
81
|
cli/ # thin shells only: app.py (init/status/config + registration),
|
|
74
82
|
# pipeline.py (index/query), inspect_cmd.py, eval_cmd.py, models_cmd.py
|
|
75
|
-
# (recommend/download/configure models), output.py
|
|
76
|
-
tests/ #
|
|
83
|
+
# (recommend/download/configure models), doctor_cmd.py, output.py
|
|
84
|
+
tests/ # 244 tests; mocked HTTP (respx), FakeEmbedder integration tests, no live network
|
|
77
85
|
```
|
|
78
86
|
|
|
79
87
|
## Flows
|
|
@@ -132,6 +140,10 @@ mostly on Dutch/multilingual queries. Details: `research/bge-m3-dense-q8-vs-nomi
|
|
|
132
140
|
|
|
133
141
|
## Gotchas
|
|
134
142
|
|
|
143
|
+
- llama-server spawns MUST pass `--parallel 1 -c 0 -b/-ub 8192` (`llama_process.py`):
|
|
144
|
+
llama.cpp's default 512-token physical batch cannot hold a ~800-token chunk, and a
|
|
145
|
+
non-causal rerank/embedding input can't split across ubatches — without them every real
|
|
146
|
+
query 500s ("input is too large to process") while a 2-doc smoke test passes.
|
|
135
147
|
- Reasoning models as expansion LLM: answers land in `content` only after thinking;
|
|
136
148
|
expansion uses max_tokens=4096 to leave room for that. Don't lower it.
|
|
137
149
|
- Changing `embeddings.model` invalidates the index; run_query fails loud on manifest
|
|
@@ -158,7 +170,7 @@ mostly on Dutch/multilingual queries. Details: `research/bge-m3-dense-q8-vs-nomi
|
|
|
158
170
|
|
|
159
171
|
## Dev loop
|
|
160
172
|
|
|
161
|
-
`uv sync --group dev --extra rerank` · `uv run pytest -q` (
|
|
173
|
+
`uv sync --group dev --extra rerank` · `uv run pytest -q` (244 pass, ~5 s) ·
|
|
162
174
|
`uv run ruff check src tests` · file soft cap ~150 lines · expected failures raise `RagxError`
|
|
163
175
|
(CLI maps to exit 2). Live smoke: LM Studio must be running with the configured embedding model.
|
|
164
176
|
Changes that impact usage (CLI flags, config keys/precedence, output schemas, install steps)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: ragx-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.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
|
|
@@ -53,6 +53,7 @@ ragx-cli index # chunk -> embed -> HNSW + kNN similarity graph (
|
|
|
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
55
|
ragx-cli models # recommend + download an embedding/reranker combo via LM Studio
|
|
56
|
+
ragx-cli doctor # check the configured providers start and answer, and the index matches
|
|
56
57
|
ragx-cli --version # version, repo, and the effective models (corpus config + ~/.ragxrc overrides)
|
|
57
58
|
```
|
|
58
59
|
|
|
@@ -70,6 +71,7 @@ your files (gitignore it) — like `.git/`, delete `.ragx/` and the corpus is un
|
|
|
70
71
|
- [Does it actually help? (benchmarks)](#does-it-actually-help-benchmarks)
|
|
71
72
|
- [Parameter study: what each knob actually does (2026-07)](#parameter-study-what-each-knob-actually-does-2026-07)
|
|
72
73
|
- [BGE-M3 embedding study (2026-07-12)](#bge-m3-embedding-study-2026-07-12)
|
|
74
|
+
- [Checking your setup: `ragx-cli doctor`](#checking-your-setup-ragx-cli-doctor)
|
|
73
75
|
- [Agent-first conventions](#agent-first-conventions)
|
|
74
76
|
- [Using ragx-cli from a coding agent (CLAUDE.md / AGENTS.md)](#using-ragx-cli-from-a-coding-agent-claudemd--agentsmd)
|
|
75
77
|
- [Pointing ragx-cli at your LLM — local or online](#pointing-ragx-cli-at-your-llm--local-or-online)
|
|
@@ -325,8 +327,19 @@ Full write-ups: `research/bge-m3-dense-q8-vs-nomic-q4-benchmark-2026-07-12-workt
|
|
|
325
327
|
|
|
326
328
|
`ragx-cli models` recommends an embedding + reranker combo, downloads the embedding model
|
|
327
329
|
**through LM Studio** (`lms get` — one download path, plays well with restricted networks),
|
|
328
|
-
pre-fetches the reranker from Hugging Face, and writes both to `ragx.toml`.
|
|
329
|
-
|
|
330
|
+
pre-fetches the reranker from Hugging Face, and writes both to `ragx.toml`. On a TTY it walks
|
|
331
|
+
three steps — context first, consequences before anything is written:
|
|
332
|
+
|
|
333
|
+
1. **machine header** — detected RAM, platform, whether `llama-server` is on PATH and the
|
|
334
|
+
`lms` CLI was found (this is what drives the recommended defaults);
|
|
335
|
+
2. **numbered menus** — the embedding tier table (model, size, languages, context, license)
|
|
336
|
+
and one menu per serving engine with a one-line tradeoff each; the recommended default is
|
|
337
|
+
marked `»` (Enter accepts), invalid input re-prompts;
|
|
338
|
+
3. **plan** — what will be downloaded (or `already downloaded ✓`) with approximate sizes,
|
|
339
|
+
which config sections change, and whether a re-index is required — all shown *before*
|
|
340
|
+
the final confirm.
|
|
341
|
+
|
|
342
|
+
Scriptable via flags:
|
|
330
343
|
|
|
331
344
|
```bash
|
|
332
345
|
ragx-cli models # interactive
|
|
@@ -336,12 +349,12 @@ ragx-cli models --quality fast --dry-run # recommend only, change
|
|
|
336
349
|
|
|
337
350
|
The curated catalog (every entry verified working end-to-end):
|
|
338
351
|
|
|
339
|
-
| tier | embedding model | notes |
|
|
340
|
-
|
|
341
|
-
| `fast` | EmbeddingGemma 300M | 308M
|
|
342
|
-
| `balanced` | BGE-M3 | ragx's benchmarked production model
|
|
343
|
-
| `best` | Qwen3-Embedding-0.6B | 100+
|
|
344
|
-
| `jina-nano` | Jina embeddings v5 nano | 239M
|
|
352
|
+
| tier | embedding model | size | langs | ctx | notes |
|
|
353
|
+
|---|---|---|---|---|---|
|
|
354
|
+
| `fast` | EmbeddingGemma 300M | 308M | 100+ | 2K | lightest; small corpora, low RAM |
|
|
355
|
+
| `balanced` | BGE-M3 | 568M | ~100 | 8K | ragx's benchmarked production model |
|
|
356
|
+
| `best` | Qwen3-Embedding-0.6B | 0.6B | 100+ | 32K | highest quality; slower indexing |
|
|
357
|
+
| `jina-nano` | Jina embeddings v5 nano | 239M | multi | 8K | best sub-500M; **CC-BY-NC (non-commercial)**; llama-server engine only — LM Studio downloads it but can't serve EuroBERT (picking it locks the embedding engine to llama-server automatically; an explicit `--embed-engine lm-studio` still fails loud) |
|
|
345
358
|
|
|
346
359
|
**Serving engines.** Embeddings run through `--embed-engine`: **`llama-server`** (default when
|
|
347
360
|
llama.cpp is installed — ragx auto-spawns `llama-server --embedding` on the downloaded GGUF and
|
|
@@ -359,7 +372,9 @@ terminates it at exit; LM Studio is only the downloader) or **`lm-studio`** (LM
|
|
|
359
372
|
|
|
360
373
|
With both engines on `llama-server` the whole stack is ragx-managed: LM Studio downloads the
|
|
361
374
|
GGUFs, ragx spawns and reaps the servers, and neither LM Studio nor huggingface.co is needed
|
|
362
|
-
at query time. Under 8 GB RAM the
|
|
375
|
+
at query time. Under 8 GB RAM the recommended default moves to `fast`: interactively, picking
|
|
376
|
+
a heavier tier asks for confirmation and then **honors your choice**; non-interactive runs
|
|
377
|
+
(`--yes`/piped) auto-downgrade to `fast` with a note, since there is nobody to ask. On macOS
|
|
363
378
|
`lms get --yes` picks MLX variants where LM Studio's catalog offers them. Switching the
|
|
364
379
|
embedding model invalidates the index — the command tells you to run `ragx-cli index --full`.
|
|
365
380
|
`init` offers this flow as its final step; rerun `ragx-cli models` anytime to switch.
|
|
@@ -368,16 +383,66 @@ Why not `jina-embeddings-v5-text-nano`? Excellent model, but its EuroBERT archit
|
|
|
368
383
|
Jina's llama.cpp fork — stock LM Studio can't load it (and the license is CC-BY-NC). See
|
|
369
384
|
`research/lm-studio-model-download-api-and-curated-embedding-reranker-catalog-for-ragx-models-command.md`.
|
|
370
385
|
|
|
386
|
+
## Checking your setup: `ragx-cli doctor`
|
|
387
|
+
|
|
388
|
+
`ragx-cli doctor` runs the configured pipeline's providers for real — it embeds a probe string,
|
|
389
|
+
scores a probe pair with the reranker, and checks the index on disk against the config — then
|
|
390
|
+
reports each stage:
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
✓ config /corpus/ragx.toml — embeddings=openai/text-embedding-bge-m3 · expansion=openai/qwen3.5-9b · rerank=sentence-transformers/BAAI/bge-reranker-v2-m3
|
|
394
|
+
✗ embeddings request to http://localhost:1234/v1/embeddings failed after 3 attempts: [Errno 61] Connection refused
|
|
395
|
+
→ ragx does not start this server — launch it at http://localhost:1234/v1 (LM Studio: `lms server start`),
|
|
396
|
+
or hand the process to ragx with `ragx-cli models --embed-engine llama-server`
|
|
397
|
+
✗ expansion no server answered at http://localhost:1234/v1/models
|
|
398
|
+
→ turn it off with `ragx-cli config set expansion.enabled false`
|
|
399
|
+
✓ rerank sentence-transformers/BAAI/bge-reranker-v2-m3 scored a probe pair (-8.14, 4.72) 3.1s
|
|
400
|
+
✓ index 634 files, 1247 chunks, built with 'text-embedding-bge-m3', no drift
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
Exit codes: `0` healthy, `1` at least one check failed, `2` doctor itself could not run (no
|
|
404
|
+
corpus, unreadable config). Warnings (no index yet, corpus drift, a model the server does not
|
|
405
|
+
list but may JIT-load) do not fail the run. `--json` emits a `ragx.doctor.v1` document with a
|
|
406
|
+
`status`/`detail`/`hint`/`elapsed_ms` entry per check.
|
|
407
|
+
|
|
408
|
+
**Which servers does ragx start?** All of the local ones:
|
|
409
|
+
|
|
410
|
+
| engine (`ragx-cli models`) | config | lifecycle |
|
|
411
|
+
| --- | --- | --- |
|
|
412
|
+
| `llama-server` | `provider="llama-server"` (embeddings and/or rerank) | spawned on demand from the configured GGUF and **killed at exit** — ragx owns the process |
|
|
413
|
+
| `lm-studio` | `embeddings.provider="openai"` + a local `base_url` | `lms server start` + `lms load <model>` when nothing answers — LM Studio **keeps running** afterwards (it is an app, not ragx's child) |
|
|
414
|
+
| `sentence-transformers` | `rerank.provider="sentence-transformers"` | in-process, no server |
|
|
415
|
+
|
|
416
|
+
`index`, `query`, and `doctor` all do this before their first request, so a cold machine needs
|
|
417
|
+
no manual setup. What was started is logged to stderr and shown in doctor's report:
|
|
418
|
+
|
|
419
|
+
```
|
|
420
|
+
✓ embeddings started the LM Studio server on port 1234; loaded 'text-embedding-nomic-embed-text-v1.5' into LM Studio — openai/text-embedding-nomic-embed-text-v1.5 answered, dim=768 14.8s
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Rules it follows: only a **local** `base_url` is ever touched (a cloud endpoint, including one
|
|
424
|
+
set via `OPENAI_BASE_URL`, is never ragx's to start); an already-running server is left alone,
|
|
425
|
+
and if LM Studio isn't installed but something else answers on that port, ragx just uses it; a
|
|
426
|
+
model that isn't downloaded fails loud naming the models the machine does have. Turn it off per
|
|
427
|
+
section with `ragx-cli config set embeddings.autostart false` (or machine-wide via
|
|
428
|
+
`--global`) — then ragx only ever talks to servers you started.
|
|
429
|
+
|
|
430
|
+
`ragx-cli models` still only downloads models and writes `ragx.toml`; it never launches
|
|
431
|
+
anything. If a run dies with "connection refused", `doctor` names the stage and the fix.
|
|
432
|
+
|
|
371
433
|
## Agent-first conventions
|
|
372
434
|
|
|
373
435
|
- `--json` emits exactly one JSON document on stdout (versioned schemas: `ragx.query.v1`,
|
|
374
|
-
`ragx.files.v1`, `ragx.status.v1`, `ragx.eval.v1`, `ragx.inspect.*.v1`); logs go
|
|
436
|
+
`ragx.files.v1`, `ragx.status.v1`, `ragx.doctor.v1`, `ragx.eval.v1`, `ragx.inspect.*.v1`); logs go
|
|
437
|
+
to stderr.
|
|
375
438
|
- Exit codes: `0` results, `1` success-but-empty, `2` error.
|
|
376
439
|
- Every chunk carries `file`, `line_start/line_end`, `byte_start/byte_end` — agents jump to the
|
|
377
440
|
exact source location and read the full text themselves (JSON chunk text is truncated).
|
|
378
441
|
- `--files-only` aggregates chunk scores per file (sum of top-3) — the mode coding agents use most.
|
|
379
442
|
- `ragx-cli status` includes a `drift` object (`new`/`changed`/`deleted` file counts vs the index) —
|
|
380
443
|
agents check it to decide whether to run `ragx-cli index` before querying.
|
|
444
|
+
- `ragx-cli doctor --json` verifies the providers answer before a batch of queries (`ok: false`
|
|
445
|
+
plus a per-check `hint` when something is down); exit `1` = a check failed.
|
|
381
446
|
- `ragx-cli query -` reads the query from stdin; `ragx-cli inspect chunk|file|neighbors|communities|community`
|
|
382
447
|
debugs the graph.
|
|
383
448
|
|
|
@@ -556,8 +621,8 @@ below — Enter accepts them all. With piped stdin (agents), `--yes`, or
|
|
|
556
621
|
| `[communities]` | `resolution=1.0`, `seed=42` — recomputed every index run; changing these never invalidates the index |
|
|
557
622
|
| `[fusion]` | `rrf_k=60`, `per_query_top=20` |
|
|
558
623
|
| `[scoring]` | `alpha_rerank=0.6`, `beta_heat=0.25`, `gamma_vector=0.15` |
|
|
559
|
-
| `[embeddings]` | `provider="openai"`, `base_url="http://localhost:1234/v1"`, prefixes for nomic-style models, `api_key_env=""` |
|
|
560
|
-
| `[expansion]` | optional LLM for multi-query/HyDE; reasoning models supported (4096-token budget); `api_key_env=""` |
|
|
624
|
+
| `[embeddings]` | `provider="openai"`, `base_url="http://localhost:1234/v1"`, prefixes for nomic-style models, `api_key_env=""`, `autostart=true` (start LM Studio + load the model when the URL is local) |
|
|
625
|
+
| `[expansion]` | optional LLM for multi-query/HyDE; reasoning models supported (4096-token budget); `api_key_env=""`, `autostart=true` |
|
|
561
626
|
| `[rerank]` | `BAAI/bge-reranker-v2-m3` via sentence-transformers (`uv tool install 'ragx-cli[rerank]'`) |
|
|
562
627
|
|
|
563
628
|
## Features & roadmap
|
|
@@ -25,6 +25,7 @@ ragx-cli index # chunk -> embed -> HNSW + kNN similarity graph (
|
|
|
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
27
|
ragx-cli models # recommend + download an embedding/reranker combo via LM Studio
|
|
28
|
+
ragx-cli doctor # check the configured providers start and answer, and the index matches
|
|
28
29
|
ragx-cli --version # version, repo, and the effective models (corpus config + ~/.ragxrc overrides)
|
|
29
30
|
```
|
|
30
31
|
|
|
@@ -42,6 +43,7 @@ your files (gitignore it) — like `.git/`, delete `.ragx/` and the corpus is un
|
|
|
42
43
|
- [Does it actually help? (benchmarks)](#does-it-actually-help-benchmarks)
|
|
43
44
|
- [Parameter study: what each knob actually does (2026-07)](#parameter-study-what-each-knob-actually-does-2026-07)
|
|
44
45
|
- [BGE-M3 embedding study (2026-07-12)](#bge-m3-embedding-study-2026-07-12)
|
|
46
|
+
- [Checking your setup: `ragx-cli doctor`](#checking-your-setup-ragx-cli-doctor)
|
|
45
47
|
- [Agent-first conventions](#agent-first-conventions)
|
|
46
48
|
- [Using ragx-cli from a coding agent (CLAUDE.md / AGENTS.md)](#using-ragx-cli-from-a-coding-agent-claudemd--agentsmd)
|
|
47
49
|
- [Pointing ragx-cli at your LLM — local or online](#pointing-ragx-cli-at-your-llm--local-or-online)
|
|
@@ -297,8 +299,19 @@ Full write-ups: `research/bge-m3-dense-q8-vs-nomic-q4-benchmark-2026-07-12-workt
|
|
|
297
299
|
|
|
298
300
|
`ragx-cli models` recommends an embedding + reranker combo, downloads the embedding model
|
|
299
301
|
**through LM Studio** (`lms get` — one download path, plays well with restricted networks),
|
|
300
|
-
pre-fetches the reranker from Hugging Face, and writes both to `ragx.toml`.
|
|
301
|
-
|
|
302
|
+
pre-fetches the reranker from Hugging Face, and writes both to `ragx.toml`. On a TTY it walks
|
|
303
|
+
three steps — context first, consequences before anything is written:
|
|
304
|
+
|
|
305
|
+
1. **machine header** — detected RAM, platform, whether `llama-server` is on PATH and the
|
|
306
|
+
`lms` CLI was found (this is what drives the recommended defaults);
|
|
307
|
+
2. **numbered menus** — the embedding tier table (model, size, languages, context, license)
|
|
308
|
+
and one menu per serving engine with a one-line tradeoff each; the recommended default is
|
|
309
|
+
marked `»` (Enter accepts), invalid input re-prompts;
|
|
310
|
+
3. **plan** — what will be downloaded (or `already downloaded ✓`) with approximate sizes,
|
|
311
|
+
which config sections change, and whether a re-index is required — all shown *before*
|
|
312
|
+
the final confirm.
|
|
313
|
+
|
|
314
|
+
Scriptable via flags:
|
|
302
315
|
|
|
303
316
|
```bash
|
|
304
317
|
ragx-cli models # interactive
|
|
@@ -308,12 +321,12 @@ ragx-cli models --quality fast --dry-run # recommend only, change
|
|
|
308
321
|
|
|
309
322
|
The curated catalog (every entry verified working end-to-end):
|
|
310
323
|
|
|
311
|
-
| tier | embedding model | notes |
|
|
312
|
-
|
|
313
|
-
| `fast` | EmbeddingGemma 300M | 308M
|
|
314
|
-
| `balanced` | BGE-M3 | ragx's benchmarked production model
|
|
315
|
-
| `best` | Qwen3-Embedding-0.6B | 100+
|
|
316
|
-
| `jina-nano` | Jina embeddings v5 nano | 239M
|
|
324
|
+
| tier | embedding model | size | langs | ctx | notes |
|
|
325
|
+
|---|---|---|---|---|---|
|
|
326
|
+
| `fast` | EmbeddingGemma 300M | 308M | 100+ | 2K | lightest; small corpora, low RAM |
|
|
327
|
+
| `balanced` | BGE-M3 | 568M | ~100 | 8K | ragx's benchmarked production model |
|
|
328
|
+
| `best` | Qwen3-Embedding-0.6B | 0.6B | 100+ | 32K | highest quality; slower indexing |
|
|
329
|
+
| `jina-nano` | Jina embeddings v5 nano | 239M | multi | 8K | best sub-500M; **CC-BY-NC (non-commercial)**; llama-server engine only — LM Studio downloads it but can't serve EuroBERT (picking it locks the embedding engine to llama-server automatically; an explicit `--embed-engine lm-studio` still fails loud) |
|
|
317
330
|
|
|
318
331
|
**Serving engines.** Embeddings run through `--embed-engine`: **`llama-server`** (default when
|
|
319
332
|
llama.cpp is installed — ragx auto-spawns `llama-server --embedding` on the downloaded GGUF and
|
|
@@ -331,7 +344,9 @@ terminates it at exit; LM Studio is only the downloader) or **`lm-studio`** (LM
|
|
|
331
344
|
|
|
332
345
|
With both engines on `llama-server` the whole stack is ragx-managed: LM Studio downloads the
|
|
333
346
|
GGUFs, ragx spawns and reaps the servers, and neither LM Studio nor huggingface.co is needed
|
|
334
|
-
at query time. Under 8 GB RAM the
|
|
347
|
+
at query time. Under 8 GB RAM the recommended default moves to `fast`: interactively, picking
|
|
348
|
+
a heavier tier asks for confirmation and then **honors your choice**; non-interactive runs
|
|
349
|
+
(`--yes`/piped) auto-downgrade to `fast` with a note, since there is nobody to ask. On macOS
|
|
335
350
|
`lms get --yes` picks MLX variants where LM Studio's catalog offers them. Switching the
|
|
336
351
|
embedding model invalidates the index — the command tells you to run `ragx-cli index --full`.
|
|
337
352
|
`init` offers this flow as its final step; rerun `ragx-cli models` anytime to switch.
|
|
@@ -340,16 +355,66 @@ Why not `jina-embeddings-v5-text-nano`? Excellent model, but its EuroBERT archit
|
|
|
340
355
|
Jina's llama.cpp fork — stock LM Studio can't load it (and the license is CC-BY-NC). See
|
|
341
356
|
`research/lm-studio-model-download-api-and-curated-embedding-reranker-catalog-for-ragx-models-command.md`.
|
|
342
357
|
|
|
358
|
+
## Checking your setup: `ragx-cli doctor`
|
|
359
|
+
|
|
360
|
+
`ragx-cli doctor` runs the configured pipeline's providers for real — it embeds a probe string,
|
|
361
|
+
scores a probe pair with the reranker, and checks the index on disk against the config — then
|
|
362
|
+
reports each stage:
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
✓ config /corpus/ragx.toml — embeddings=openai/text-embedding-bge-m3 · expansion=openai/qwen3.5-9b · rerank=sentence-transformers/BAAI/bge-reranker-v2-m3
|
|
366
|
+
✗ embeddings request to http://localhost:1234/v1/embeddings failed after 3 attempts: [Errno 61] Connection refused
|
|
367
|
+
→ ragx does not start this server — launch it at http://localhost:1234/v1 (LM Studio: `lms server start`),
|
|
368
|
+
or hand the process to ragx with `ragx-cli models --embed-engine llama-server`
|
|
369
|
+
✗ expansion no server answered at http://localhost:1234/v1/models
|
|
370
|
+
→ turn it off with `ragx-cli config set expansion.enabled false`
|
|
371
|
+
✓ rerank sentence-transformers/BAAI/bge-reranker-v2-m3 scored a probe pair (-8.14, 4.72) 3.1s
|
|
372
|
+
✓ index 634 files, 1247 chunks, built with 'text-embedding-bge-m3', no drift
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Exit codes: `0` healthy, `1` at least one check failed, `2` doctor itself could not run (no
|
|
376
|
+
corpus, unreadable config). Warnings (no index yet, corpus drift, a model the server does not
|
|
377
|
+
list but may JIT-load) do not fail the run. `--json` emits a `ragx.doctor.v1` document with a
|
|
378
|
+
`status`/`detail`/`hint`/`elapsed_ms` entry per check.
|
|
379
|
+
|
|
380
|
+
**Which servers does ragx start?** All of the local ones:
|
|
381
|
+
|
|
382
|
+
| engine (`ragx-cli models`) | config | lifecycle |
|
|
383
|
+
| --- | --- | --- |
|
|
384
|
+
| `llama-server` | `provider="llama-server"` (embeddings and/or rerank) | spawned on demand from the configured GGUF and **killed at exit** — ragx owns the process |
|
|
385
|
+
| `lm-studio` | `embeddings.provider="openai"` + a local `base_url` | `lms server start` + `lms load <model>` when nothing answers — LM Studio **keeps running** afterwards (it is an app, not ragx's child) |
|
|
386
|
+
| `sentence-transformers` | `rerank.provider="sentence-transformers"` | in-process, no server |
|
|
387
|
+
|
|
388
|
+
`index`, `query`, and `doctor` all do this before their first request, so a cold machine needs
|
|
389
|
+
no manual setup. What was started is logged to stderr and shown in doctor's report:
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
✓ embeddings started the LM Studio server on port 1234; loaded 'text-embedding-nomic-embed-text-v1.5' into LM Studio — openai/text-embedding-nomic-embed-text-v1.5 answered, dim=768 14.8s
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Rules it follows: only a **local** `base_url` is ever touched (a cloud endpoint, including one
|
|
396
|
+
set via `OPENAI_BASE_URL`, is never ragx's to start); an already-running server is left alone,
|
|
397
|
+
and if LM Studio isn't installed but something else answers on that port, ragx just uses it; a
|
|
398
|
+
model that isn't downloaded fails loud naming the models the machine does have. Turn it off per
|
|
399
|
+
section with `ragx-cli config set embeddings.autostart false` (or machine-wide via
|
|
400
|
+
`--global`) — then ragx only ever talks to servers you started.
|
|
401
|
+
|
|
402
|
+
`ragx-cli models` still only downloads models and writes `ragx.toml`; it never launches
|
|
403
|
+
anything. If a run dies with "connection refused", `doctor` names the stage and the fix.
|
|
404
|
+
|
|
343
405
|
## Agent-first conventions
|
|
344
406
|
|
|
345
407
|
- `--json` emits exactly one JSON document on stdout (versioned schemas: `ragx.query.v1`,
|
|
346
|
-
`ragx.files.v1`, `ragx.status.v1`, `ragx.eval.v1`, `ragx.inspect.*.v1`); logs go
|
|
408
|
+
`ragx.files.v1`, `ragx.status.v1`, `ragx.doctor.v1`, `ragx.eval.v1`, `ragx.inspect.*.v1`); logs go
|
|
409
|
+
to stderr.
|
|
347
410
|
- Exit codes: `0` results, `1` success-but-empty, `2` error.
|
|
348
411
|
- Every chunk carries `file`, `line_start/line_end`, `byte_start/byte_end` — agents jump to the
|
|
349
412
|
exact source location and read the full text themselves (JSON chunk text is truncated).
|
|
350
413
|
- `--files-only` aggregates chunk scores per file (sum of top-3) — the mode coding agents use most.
|
|
351
414
|
- `ragx-cli status` includes a `drift` object (`new`/`changed`/`deleted` file counts vs the index) —
|
|
352
415
|
agents check it to decide whether to run `ragx-cli index` before querying.
|
|
416
|
+
- `ragx-cli doctor --json` verifies the providers answer before a batch of queries (`ok: false`
|
|
417
|
+
plus a per-check `hint` when something is down); exit `1` = a check failed.
|
|
353
418
|
- `ragx-cli query -` reads the query from stdin; `ragx-cli inspect chunk|file|neighbors|communities|community`
|
|
354
419
|
debugs the graph.
|
|
355
420
|
|
|
@@ -528,8 +593,8 @@ below — Enter accepts them all. With piped stdin (agents), `--yes`, or
|
|
|
528
593
|
| `[communities]` | `resolution=1.0`, `seed=42` — recomputed every index run; changing these never invalidates the index |
|
|
529
594
|
| `[fusion]` | `rrf_k=60`, `per_query_top=20` |
|
|
530
595
|
| `[scoring]` | `alpha_rerank=0.6`, `beta_heat=0.25`, `gamma_vector=0.15` |
|
|
531
|
-
| `[embeddings]` | `provider="openai"`, `base_url="http://localhost:1234/v1"`, prefixes for nomic-style models, `api_key_env=""` |
|
|
532
|
-
| `[expansion]` | optional LLM for multi-query/HyDE; reasoning models supported (4096-token budget); `api_key_env=""` |
|
|
596
|
+
| `[embeddings]` | `provider="openai"`, `base_url="http://localhost:1234/v1"`, prefixes for nomic-style models, `api_key_env=""`, `autostart=true` (start LM Studio + load the model when the URL is local) |
|
|
597
|
+
| `[expansion]` | optional LLM for multi-query/HyDE; reasoning models supported (4096-token budget); `api_key_env=""`, `autostart=true` |
|
|
533
598
|
| `[rerank]` | `BAAI/bge-reranker-v2-m3` via sentence-transformers (`uv tool install 'ragx-cli[rerank]'`) |
|
|
534
599
|
|
|
535
600
|
## Features & roadmap
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Proposal: `ragx-cli models` interactive flow redesign
|
|
2
|
+
|
|
3
|
+
**Status:** implemented 2026-07-15 (`cli/models_ui.py` + `cli/models_cmd.py`); kept as the
|
|
4
|
+
design rationale. The open decision below was resolved as proposed: warn-and-honor
|
|
5
|
+
interactively, auto-downgrade non-interactively.
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
The current interactive flow (`src/ragx/cli/models_cmd.py:62-104`) asks all three
|
|
10
|
+
questions **before** showing any of the information needed to answer them:
|
|
11
|
+
|
|
12
|
+
1. **Blind prompts.** `quality (fast/balanced/best/jina-nano) [balanced]:` offers four
|
|
13
|
+
opaque words. The catalog knows each tier's model name, parameter count, languages,
|
|
14
|
+
context length, and license — none of it is shown at prompt time. Machine specs print
|
|
15
|
+
*after* the answers.
|
|
16
|
+
2. **Engine prompts don't state tradeoffs.** "llama-server vs sentence-transformers"
|
|
17
|
+
means nothing unless you already know one avoids huggingface.co entirely and the
|
|
18
|
+
other needs the `[rerank]` extra. That context lives only in `--help` flag text.
|
|
19
|
+
3. **Silent downgrade.** Pick `best` on a <8 GB machine and `recommend()`
|
|
20
|
+
(`catalog.py:133-135`) substitutes `fast`, telling you only afterwards. No way to
|
|
21
|
+
override or reconsider.
|
|
22
|
+
4. **The jina-nano trap.** Choosing `jina-nano` with the default `lm-studio` embed
|
|
23
|
+
engine hard-errors at the end ("rerun with `--embed-engine llama-server`") — even
|
|
24
|
+
though the catalog already encodes the constraint (`requires_llama_server`) and
|
|
25
|
+
could drive the default.
|
|
26
|
+
5. **Fragile input.** A typo in the free-text tier name raises `RagxError`, exit 2,
|
|
27
|
+
start over.
|
|
28
|
+
6. **Consequences surface too late.** Download sizes are never shown; "your index is
|
|
29
|
+
now invalid, run `index --full`" prints *after* `ragx.toml` is written.
|
|
30
|
+
|
|
31
|
+
## Proposed flow
|
|
32
|
+
|
|
33
|
+
Principle: **context → choice → consequence → commit.** Detect everything first, show
|
|
34
|
+
it, ask with numbered menus, and present a plan before touching anything.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
$ ragx-cli models
|
|
38
|
+
|
|
39
|
+
machine: 32 GB RAM · macOS · llama-server: on PATH · LM Studio (lms): found
|
|
40
|
+
|
|
41
|
+
── Step 1/3 · embedding model ─────────────────────────────────────────
|
|
42
|
+
Determines retrieval quality. Changing it later invalidates the
|
|
43
|
+
index (full re-index required).
|
|
44
|
+
|
|
45
|
+
# tier model size langs ctx notes
|
|
46
|
+
1 fast EmbeddingGemma 300M 308M 100+ 2K lightest; small corpora, low RAM
|
|
47
|
+
» 2 balanced BGE-M3 568M ~100 8K ragx's benchmarked production model
|
|
48
|
+
3 best Qwen3-Embedding-0.6B 0.6B 100+ 32K highest quality; slower indexing
|
|
49
|
+
4 jina-nano Jina v5 nano 239M multi 8K best <500M quality · CC-BY-NC
|
|
50
|
+
(non-commercial) · llama-server only
|
|
51
|
+
select 1-4 [2]:
|
|
52
|
+
|
|
53
|
+
── Step 2/3 · engines ─────────────────────────────────────────────────
|
|
54
|
+
embedding serving:
|
|
55
|
+
» 1 llama-server ragx auto-spawns llama.cpp; LM Studio only
|
|
56
|
+
downloads (detected on PATH)
|
|
57
|
+
2 lm-studio LM Studio app must be running to serve
|
|
58
|
+
select 1-2 [1]:
|
|
59
|
+
|
|
60
|
+
reranker:
|
|
61
|
+
» 1 llama-server GGUF via LM Studio — no huggingface.co needed
|
|
62
|
+
2 sentence-transformers downloads from Hugging Face; needs the
|
|
63
|
+
ragx-cli[rerank] extra
|
|
64
|
+
select 1-2 [1]:
|
|
65
|
+
|
|
66
|
+
── Step 3/3 · plan ────────────────────────────────────────────────────
|
|
67
|
+
embedding BGE-M3 Q8 GGUF (~600 MB) already downloaded ✓
|
|
68
|
+
reranker bge-reranker-v2-m3 Q8 (~600 MB) will download via LM Studio
|
|
69
|
+
config ragx.toml → embeddings.*, rerank.* (7 keys)
|
|
70
|
+
re-index REQUIRED — embedding model changes; run `ragx-cli index --full` after
|
|
71
|
+
|
|
72
|
+
proceed? [Y/n]:
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### How each problem maps to a fix
|
|
76
|
+
|
|
77
|
+
- **Specs shown first**, including whether `llama-server` was found on PATH — the
|
|
78
|
+
engine defaults stop being invisible magic.
|
|
79
|
+
- **Numbered menus with a re-prompt loop** instead of one-shot free text. `»` marks
|
|
80
|
+
the recommended default; Enter accepts it. Invalid input re-prompts instead of
|
|
81
|
+
exiting.
|
|
82
|
+
- **RAM guard becomes visible, not substituting.** On a <8 GB machine the `»` marker
|
|
83
|
+
moves to `fast` and heavy rows get a `⚠ needs ≥8 GB` annotation. If the user still
|
|
84
|
+
picks `best`, honor it after a one-line confirm — a balanced choice means respecting
|
|
85
|
+
an informed override. Non-interactive `--quality best` keeps today's auto-downgrade
|
|
86
|
+
+ note (an agent can't be asked); this interactive/non-interactive asymmetry should
|
|
87
|
+
be stated in the README.
|
|
88
|
+
- **jina-nano stops being a trap:** picking it prints
|
|
89
|
+
`→ embedding engine locked to llama-server (LM Studio cannot serve this model)` and
|
|
90
|
+
skips that question instead of erroring at the end.
|
|
91
|
+
- **The plan step** shows download sizes, marks what is already downloaded (the
|
|
92
|
+
`_ensure_downloaded` check moves before the confirm), lists which config keys
|
|
93
|
+
change, and states the re-index consequence *before* the write.
|
|
94
|
+
|
|
95
|
+
## Implementation sketch
|
|
96
|
+
|
|
97
|
+
Small and contained — no behavior change for flags / `--json` / non-interactive:
|
|
98
|
+
|
|
99
|
+
1. **`core/catalog.py`** — add structured fields to the choices (`params`, `context`,
|
|
100
|
+
`languages`, `download_mb`) instead of parsing prose out of `notes`.
|
|
101
|
+
Verify: existing tests pass; table renders from fields.
|
|
102
|
+
2. **`cli/models_cmd.py`** — a `_pick_numbered(title, rows, default)` helper
|
|
103
|
+
(validation loop, `»` marker) replacing `typer.prompt`/`_pick_engine` in
|
|
104
|
+
interactive mode; reorder so specs + `lms`/`llama-server` detection and the
|
|
105
|
+
already-downloaded check happen before any question.
|
|
106
|
+
Verify: interactive smoke run against a scratch corpus.
|
|
107
|
+
3. **`recommend()`** — split recommendation from enforcement: return annotations
|
|
108
|
+
(RAM warning, license note, engine lock) rather than silently swapping tiers in
|
|
109
|
+
interactive mode.
|
|
110
|
+
Verify: unit tests for low-RAM interactive vs non-interactive paths.
|
|
111
|
+
4. **README** — update the `models` section in the same change (CLAUDE.md rule:
|
|
112
|
+
usage-visible changes ship with their docs).
|
|
113
|
+
|
|
114
|
+
Estimated diff: ~150 lines, centered on `models_cmd.py`.
|
|
115
|
+
|
|
116
|
+
### Rendering choice
|
|
117
|
+
|
|
118
|
+
Plain f-string column alignment is enough. `rich` is already available (transitive
|
|
119
|
+
dependency of typer ≥0.12) if fancier tables are ever wanted, but no new dependency
|
|
120
|
+
and no speculative widgets now.
|
|
121
|
+
|
|
122
|
+
### Deliberate non-goal
|
|
123
|
+
|
|
124
|
+
No full TUI (arrow-key selection à la questionary). Numbered menus keep the code
|
|
125
|
+
testable with piped stdin and keep the agent-first stdin/stdout contract intact.
|
|
126
|
+
|
|
127
|
+
## Open decision
|
|
128
|
+
|
|
129
|
+
Interactive RAM-override behavior: warn-and-honor (proposed) vs keep the current
|
|
130
|
+
auto-downgrade everywhere. Proposal argues warn-and-honor interactively, downgrade
|
|
131
|
+
non-interactively.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ragx-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.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"
|
|
@@ -44,7 +44,7 @@ build-backend = "hatchling.build"
|
|
|
44
44
|
packages = ["src/ragx"]
|
|
45
45
|
|
|
46
46
|
[dependency-groups]
|
|
47
|
-
dev = ["pytest>=8", "ruff>=0.
|
|
47
|
+
dev = ["pytest>=8", "ruff>=0.15,<0.16", "respx>=0.21"]
|
|
48
48
|
|
|
49
49
|
[tool.ruff]
|
|
50
50
|
line-length = 100
|
|
@@ -32,12 +32,13 @@ app = typer.Typer(add_completion=False, no_args_is_help=True)
|
|
|
32
32
|
config_app = typer.Typer(add_completion=False, no_args_is_help=True)
|
|
33
33
|
app.add_typer(config_app, name="config")
|
|
34
34
|
|
|
35
|
-
from ragx.cli import eval_cmd, inspect_cmd, models_cmd, pipeline # noqa: E402 (needs `app` defined above)
|
|
35
|
+
from ragx.cli import doctor_cmd, eval_cmd, inspect_cmd, models_cmd, pipeline # noqa: E402 (needs `app` defined above)
|
|
36
36
|
|
|
37
37
|
pipeline.register(app)
|
|
38
38
|
inspect_cmd.register(app)
|
|
39
39
|
eval_cmd.register(app)
|
|
40
40
|
models_cmd.register(app)
|
|
41
|
+
doctor_cmd.register(app)
|
|
41
42
|
|
|
42
43
|
|
|
43
44
|
def _print_version() -> None:
|
|
@@ -225,4 +226,5 @@ def config_set(
|
|
|
225
226
|
|
|
226
227
|
def main() -> None:
|
|
227
228
|
logging.basicConfig(level=logging.INFO, stream=sys.stderr, format="%(levelname)s: %(message)s")
|
|
229
|
+
logging.getLogger("httpx").setLevel(logging.WARNING) # one INFO line per embedding batch
|
|
228
230
|
app()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""`doctor`: run the config health checks and render them. Registered by app.py."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from ragx.cli.output import emit_json, fail, migrate_confirm
|
|
10
|
+
from ragx.core.config import Config, require_root
|
|
11
|
+
from ragx.core.doctor import FAIL, OK, SKIP, WARN, DoctorReport, run_doctor, to_doctor_json
|
|
12
|
+
from ragx.core.errors import RagxError
|
|
13
|
+
from ragx.providers.registry import make_embedder, make_generator, make_reranker
|
|
14
|
+
|
|
15
|
+
MARKS = {OK: "✓", WARN: "!", FAIL: "✗", SKIP: "-"}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def register(app: typer.Typer) -> None:
|
|
19
|
+
app.command()(doctor)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def doctor(
|
|
23
|
+
path: Path | None = typer.Argument(None),
|
|
24
|
+
json_out: bool = typer.Option(False, "--json"),
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Check that the configured providers start and answer, and that the index matches.
|
|
27
|
+
|
|
28
|
+
Starts whatever ragx itself manages (the llama-server engines), exactly as `index`
|
|
29
|
+
and `query` would. Exit 0 = healthy, 1 = a check failed, 2 = doctor could not run.
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
root = require_root(path)
|
|
33
|
+
cfg = Config.load(root, confirm=migrate_confirm())
|
|
34
|
+
except RagxError as exc:
|
|
35
|
+
fail(str(exc))
|
|
36
|
+
report = run_doctor(
|
|
37
|
+
root,
|
|
38
|
+
cfg,
|
|
39
|
+
embedder_factory=make_embedder,
|
|
40
|
+
generator_factory=make_generator,
|
|
41
|
+
reranker_factory=lambda c: make_reranker(c, strict=True),
|
|
42
|
+
)
|
|
43
|
+
if json_out:
|
|
44
|
+
emit_json(to_doctor_json(report))
|
|
45
|
+
else:
|
|
46
|
+
_render(report)
|
|
47
|
+
if not report.ok:
|
|
48
|
+
raise typer.Exit(code=1)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _render(report: DoctorReport) -> None:
|
|
52
|
+
for check in report.checks:
|
|
53
|
+
secs = f" {check.elapsed_ms / 1000:.1f}s" if check.elapsed_ms >= 100 else ""
|
|
54
|
+
typer.echo(f"{MARKS[check.status]} {check.name:<11} {check.detail}{secs}")
|
|
55
|
+
if check.hint:
|
|
56
|
+
typer.echo(f" → {check.hint}")
|
|
57
|
+
failed = sum(1 for c in report.checks if c.status == FAIL)
|
|
58
|
+
warned = sum(1 for c in report.checks if c.status == WARN)
|
|
59
|
+
if failed or warned:
|
|
60
|
+
typer.echo(f"\n{failed} failed, {warned} warning(s)")
|
|
61
|
+
else:
|
|
62
|
+
typer.echo("\nall checks passed")
|