ragx-cli 0.6.0__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.6.0 → ragx_cli-0.7.0}/CHANGELOG.md +34 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/CLAUDE.md +16 -4
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/PKG-INFO +57 -5
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/README.md +55 -3
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/pyproject.toml +2 -2
- {ragx_cli-0.6.0 → 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.6.0 → ragx_cli-0.7.0}/src/ragx/cli/pipeline.py +14 -1
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/catalog.py +2 -2
- {ragx_cli-0.6.0 → 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.6.0 → ragx_cli-0.7.0}/src/ragx/core/lmstudio.py +49 -13
- {ragx_cli-0.6.0 → 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.6.0 → 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.6.0 → 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.6.0 → ragx_cli-0.7.0}/tests/test_models.py +5 -5
- ragx_cli-0.6.0/tests/conftest.py +0 -13
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/SKILL.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/api-cheatsheet.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/collection-management/python.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/collection-management/typescript.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/configuration.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/data-model.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/data-operations/python.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/data-operations/typescript.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/embedding.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/hybrid-search/python.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/hybrid-search/typescript.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/multimodal-search/python.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/multimodal-search/typescript.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/quick-start/python.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/quick-start/typescript.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/rag-system/python.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/rag-system/typescript.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/reranker.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/troubleshooting.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/vector-search/python.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.agents/skills/zvec/vector-search/typescript.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.github/workflows/publish.yml +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/.gitignore +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/AGENTS.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/CONTRACTS-PHASE23.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/CONTRACTS.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/LICENSE +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/docs/feature-temporal-weighting.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/docs/proposal-models-command-interactive-flow.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/ragx-cli-plan.md +0 -0
- {ragx_cli-0.6.0 → 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.6.0 → ragx_cli-0.7.0}/research/bge-m3-dense-embeddings-as-ragx-provider-multilingual-quality-and-threshold-calibration.md +0 -0
- {ragx_cli-0.6.0 → 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.6.0 → 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.6.0 → 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.6.0 → 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.6.0 → 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.6.0 → 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.6.0 → ragx_cli-0.7.0}/skills/ragx-cli/SKILL.md +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/skills-lock.json +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/__init__.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/cli/__init__.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/cli/detect.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/cli/eval_cmd.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/cli/init_prompt.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/cli/inspect_cmd.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/cli/models_cmd.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/cli/models_ui.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/cli/output.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/__init__.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/chunking.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/communities.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/discovery.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/errors.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/eval.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/expansion.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/fusion.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/graph.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/indexer.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/models.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/query.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/scoring.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/store.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/traversal.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/core/vectors.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/providers/__init__.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/providers/base.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/providers/llama_embedder.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/providers/llama_server.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/providers/openai_compat.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/src/ragx/providers/st_reranker.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/__init__.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_chunking.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_cli.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_communities.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_config_rc.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_discovery.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_eval.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_expansion.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_fusion.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_graph.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_inspect.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_integration.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_providers.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_store.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_traversal.py +0 -0
- {ragx_cli-0.6.0 → ragx_cli-0.7.0}/tests/test_vectors.py +0 -0
|
@@ -4,6 +4,40 @@ 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
|
+
|
|
7
41
|
## [0.6.0] — 2026-07-15
|
|
8
42
|
|
|
9
43
|
### Changed
|
|
@@ -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)
|
|
@@ -381,16 +383,66 @@ Why not `jina-embeddings-v5-text-nano`? Excellent model, but its EuroBERT archit
|
|
|
381
383
|
Jina's llama.cpp fork — stock LM Studio can't load it (and the license is CC-BY-NC). See
|
|
382
384
|
`research/lm-studio-model-download-api-and-curated-embedding-reranker-catalog-for-ragx-models-command.md`.
|
|
383
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
|
+
|
|
384
433
|
## Agent-first conventions
|
|
385
434
|
|
|
386
435
|
- `--json` emits exactly one JSON document on stdout (versioned schemas: `ragx.query.v1`,
|
|
387
|
-
`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.
|
|
388
438
|
- Exit codes: `0` results, `1` success-but-empty, `2` error.
|
|
389
439
|
- Every chunk carries `file`, `line_start/line_end`, `byte_start/byte_end` — agents jump to the
|
|
390
440
|
exact source location and read the full text themselves (JSON chunk text is truncated).
|
|
391
441
|
- `--files-only` aggregates chunk scores per file (sum of top-3) — the mode coding agents use most.
|
|
392
442
|
- `ragx-cli status` includes a `drift` object (`new`/`changed`/`deleted` file counts vs the index) —
|
|
393
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.
|
|
394
446
|
- `ragx-cli query -` reads the query from stdin; `ragx-cli inspect chunk|file|neighbors|communities|community`
|
|
395
447
|
debugs the graph.
|
|
396
448
|
|
|
@@ -569,8 +621,8 @@ below — Enter accepts them all. With piped stdin (agents), `--yes`, or
|
|
|
569
621
|
| `[communities]` | `resolution=1.0`, `seed=42` — recomputed every index run; changing these never invalidates the index |
|
|
570
622
|
| `[fusion]` | `rrf_k=60`, `per_query_top=20` |
|
|
571
623
|
| `[scoring]` | `alpha_rerank=0.6`, `beta_heat=0.25`, `gamma_vector=0.15` |
|
|
572
|
-
| `[embeddings]` | `provider="openai"`, `base_url="http://localhost:1234/v1"`, prefixes for nomic-style models, `api_key_env=""` |
|
|
573
|
-
| `[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` |
|
|
574
626
|
| `[rerank]` | `BAAI/bge-reranker-v2-m3` via sentence-transformers (`uv tool install 'ragx-cli[rerank]'`) |
|
|
575
627
|
|
|
576
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)
|
|
@@ -353,16 +355,66 @@ Why not `jina-embeddings-v5-text-nano`? Excellent model, but its EuroBERT archit
|
|
|
353
355
|
Jina's llama.cpp fork — stock LM Studio can't load it (and the license is CC-BY-NC). See
|
|
354
356
|
`research/lm-studio-model-download-api-and-curated-embedding-reranker-catalog-for-ragx-models-command.md`.
|
|
355
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
|
+
|
|
356
405
|
## Agent-first conventions
|
|
357
406
|
|
|
358
407
|
- `--json` emits exactly one JSON document on stdout (versioned schemas: `ragx.query.v1`,
|
|
359
|
-
`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.
|
|
360
410
|
- Exit codes: `0` results, `1` success-but-empty, `2` error.
|
|
361
411
|
- Every chunk carries `file`, `line_start/line_end`, `byte_start/byte_end` — agents jump to the
|
|
362
412
|
exact source location and read the full text themselves (JSON chunk text is truncated).
|
|
363
413
|
- `--files-only` aggregates chunk scores per file (sum of top-3) — the mode coding agents use most.
|
|
364
414
|
- `ragx-cli status` includes a `drift` object (`new`/`changed`/`deleted` file counts vs the index) —
|
|
365
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.
|
|
366
418
|
- `ragx-cli query -` reads the query from stdin; `ragx-cli inspect chunk|file|neighbors|communities|community`
|
|
367
419
|
debugs the graph.
|
|
368
420
|
|
|
@@ -541,8 +593,8 @@ below — Enter accepts them all. With piped stdin (agents), `--yes`, or
|
|
|
541
593
|
| `[communities]` | `resolution=1.0`, `seed=42` — recomputed every index run; changing these never invalidates the index |
|
|
542
594
|
| `[fusion]` | `rrf_k=60`, `per_query_top=20` |
|
|
543
595
|
| `[scoring]` | `alpha_rerank=0.6`, `beta_heat=0.25`, `gamma_vector=0.15` |
|
|
544
|
-
| `[embeddings]` | `provider="openai"`, `base_url="http://localhost:1234/v1"`, prefixes for nomic-style models, `api_key_env=""` |
|
|
545
|
-
| `[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` |
|
|
546
598
|
| `[rerank]` | `BAAI/bge-reranker-v2-m3` via sentence-transformers (`uv tool install 'ragx-cli[rerank]'`) |
|
|
547
599
|
|
|
548
600
|
## Features & roadmap
|
|
@@ -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")
|
|
@@ -12,6 +12,7 @@ from ragx.core.config import Config, require_root
|
|
|
12
12
|
from ragx.core.errors import RagxError
|
|
13
13
|
from ragx.core.indexer import run_index
|
|
14
14
|
from ragx.core.query import QueryOptions, run_query, to_files_json, to_query_json
|
|
15
|
+
from ragx.providers import lmstudio_process
|
|
15
16
|
from ragx.providers.registry import make_embedder, make_generator, make_reranker
|
|
16
17
|
|
|
17
18
|
|
|
@@ -36,6 +37,7 @@ def index(
|
|
|
36
37
|
try:
|
|
37
38
|
root = require_root(path)
|
|
38
39
|
cfg = Config.load(root, confirm=migrate_confirm())
|
|
40
|
+
lmstudio_process.ensure_for_section(cfg, "embeddings")
|
|
39
41
|
stats = run_index(root, cfg, make_embedder(cfg), full=full)
|
|
40
42
|
except RagxError as exc:
|
|
41
43
|
fail(str(exc))
|
|
@@ -50,6 +52,16 @@ def index(
|
|
|
50
52
|
)
|
|
51
53
|
|
|
52
54
|
|
|
55
|
+
def _generator(cfg: Config):
|
|
56
|
+
"""Expansion is optional: a backend that won't come up must not sink the query —
|
|
57
|
+
expand_query degrades to a no-op on any failure."""
|
|
58
|
+
try:
|
|
59
|
+
lmstudio_process.ensure_for_section(cfg, "expansion")
|
|
60
|
+
except RagxError as exc:
|
|
61
|
+
print(f"warning: query expansion backend unavailable: {exc}", file=sys.stderr)
|
|
62
|
+
return make_generator(cfg)
|
|
63
|
+
|
|
64
|
+
|
|
53
65
|
def query(
|
|
54
66
|
text: str = typer.Argument(..., help="query text, or '-' to read from stdin"),
|
|
55
67
|
top: int | None = typer.Option(None, "--top"),
|
|
@@ -67,6 +79,7 @@ def query(
|
|
|
67
79
|
try:
|
|
68
80
|
root = require_root()
|
|
69
81
|
cfg = Config.load(root, confirm=migrate_confirm())
|
|
82
|
+
lmstudio_process.ensure_for_section(cfg, "embeddings")
|
|
70
83
|
opts = QueryOptions(
|
|
71
84
|
top=top if top is not None else cfg.get("query.top"),
|
|
72
85
|
files_only=files_only,
|
|
@@ -78,7 +91,7 @@ def query(
|
|
|
78
91
|
)
|
|
79
92
|
out = run_query(
|
|
80
93
|
root, cfg, make_embedder(cfg), text, opts,
|
|
81
|
-
generator=
|
|
94
|
+
generator=_generator(cfg) if opts.expand else None,
|
|
82
95
|
reranker=make_reranker(cfg) if opts.rerank else None,
|
|
83
96
|
)
|
|
84
97
|
except RagxError as exc:
|
|
@@ -75,7 +75,7 @@ EMBEDDINGS: dict[str, EmbeddingChoice] = {
|
|
|
75
75
|
"balanced": EmbeddingChoice(
|
|
76
76
|
tier="balanced",
|
|
77
77
|
label="BGE-M3",
|
|
78
|
-
ref="https://huggingface.co/
|
|
78
|
+
ref="https://huggingface.co/gpustack/bge-m3-GGUF@Q8_0",
|
|
79
79
|
repo_fragment="bge-m3",
|
|
80
80
|
doc_prefix="",
|
|
81
81
|
query_prefix="",
|
|
@@ -84,7 +84,7 @@ EMBEDDINGS: dict[str, EmbeddingChoice] = {
|
|
|
84
84
|
context="8K",
|
|
85
85
|
download_mb=600,
|
|
86
86
|
notes="ragx's benchmarked production model",
|
|
87
|
-
gguf_ref="https://huggingface.co/
|
|
87
|
+
gguf_ref="https://huggingface.co/gpustack/bge-m3-GGUF@Q8_0",
|
|
88
88
|
gguf_fragment="bge-m3",
|
|
89
89
|
),
|
|
90
90
|
"best": EmbeddingChoice(
|
|
@@ -5,6 +5,7 @@ The rc overrides corpus values — with a warning."""
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
|
+
import os
|
|
8
9
|
import tomllib
|
|
9
10
|
from collections.abc import Callable
|
|
10
11
|
from pathlib import Path
|
|
@@ -46,6 +47,7 @@ DEFAULTS: dict[str, dict[str, Any]] = {
|
|
|
46
47
|
"query_prefix": "search_query: ",
|
|
47
48
|
"batch_size": 32,
|
|
48
49
|
"api_key_env": "", # NAME of an env var holding the API key; empty = no auth header
|
|
50
|
+
"autostart": True, # provider="openai" + local base_url: start LM Studio and load the model
|
|
49
51
|
"gguf": "", # llama-server only: path to the embedding GGUF (LM Studio dir works)
|
|
50
52
|
"server_bin": "llama-server", # llama-server only: binary name or path
|
|
51
53
|
},
|
|
@@ -57,6 +59,7 @@ DEFAULTS: dict[str, dict[str, Any]] = {
|
|
|
57
59
|
"variants": 3,
|
|
58
60
|
"hyde": True,
|
|
59
61
|
"api_key_env": "",
|
|
62
|
+
"autostart": True, # see embeddings.autostart
|
|
60
63
|
},
|
|
61
64
|
"rerank": {
|
|
62
65
|
"enabled": True,
|
|
@@ -143,6 +146,20 @@ def _coerce(section: str, key: str, value: Any) -> Any:
|
|
|
143
146
|
return value
|
|
144
147
|
|
|
145
148
|
|
|
149
|
+
DEFAULT_OPENAI_BASE_URL = DEFAULTS["embeddings"]["base_url"] # the LM Studio default
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def effective_base_url(cfg: "Config", section: str) -> str:
|
|
153
|
+
"""The URL a provider section actually talks to: `<section>.base_url`, unless it is
|
|
154
|
+
still the built-in default and OPENAI_BASE_URL is set — an explicit `config set`
|
|
155
|
+
always wins. Providers and the LM Studio autostart must agree on this."""
|
|
156
|
+
base_url = cfg.get(f"{section}.base_url")
|
|
157
|
+
env_url = os.environ.get("OPENAI_BASE_URL")
|
|
158
|
+
if env_url and base_url == DEFAULT_OPENAI_BASE_URL:
|
|
159
|
+
return env_url.rstrip("/")
|
|
160
|
+
return base_url
|
|
161
|
+
|
|
162
|
+
|
|
146
163
|
def load_rc(rc: Path | None = None) -> dict[str, dict[str, Any]]:
|
|
147
164
|
"""Parse ~/.ragxrc. Only provider sections with known keys are allowed — fails loud."""
|
|
148
165
|
path = rc if rc is not None else rc_path()
|