@devilsdev/rag-pipeline-utils 2.4.5 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/.ragrc.schema.json +83 -192
  2. package/CHANGELOG.md +431 -0
  3. package/README.md +418 -117
  4. package/SECURITY.md +5 -5
  5. package/dist/experimental.cjs +2 -0
  6. package/dist/experimental.d.ts +1 -0
  7. package/dist/experimental.mjs +5 -0
  8. package/dist/index.cjs +6442 -1355
  9. package/dist/index.d.ts +129 -2
  10. package/dist/index.mjs +1 -1
  11. package/dist/testing.cjs +2 -0
  12. package/dist/testing.d.ts +1 -0
  13. package/dist/testing.mjs +5 -0
  14. package/dist/types/adapters/ai-sdk.d.ts +53 -0
  15. package/dist/types/agentic/agentic-pipeline.d.ts +103 -0
  16. package/dist/types/agentic/iterative-retriever.d.ts +85 -0
  17. package/dist/types/agentic/query-planner.d.ts +145 -0
  18. package/dist/types/agentic/self-critique.d.ts +45 -0
  19. package/dist/types/ai/adaptive-retrieval.d.ts +44 -0
  20. package/dist/types/ai/federation/federated-learning-coordinator.d.ts +62 -0
  21. package/dist/types/ai/multimodal-processing.d.ts +32 -0
  22. package/dist/types/ai/multimodal.d.ts +39 -0
  23. package/dist/types/ai/retrieval-engine.d.ts +37 -0
  24. package/dist/types/ai/training/model-training-orchestrator.d.ts +31 -0
  25. package/dist/types/cache/component-keys.d.ts +7 -0
  26. package/dist/types/cache/memory-cache.d.ts +54 -0
  27. package/dist/types/cache/pipeline-cache.d.ts +66 -0
  28. package/dist/types/cache/redis-cache.d.ts +46 -0
  29. package/dist/types/chunking/chunking-engine.d.ts +70 -0
  30. package/dist/types/chunking/strategies/fixed-size.d.ts +17 -0
  31. package/dist/types/chunking/strategies/recursive.d.ts +20 -0
  32. package/dist/types/chunking/strategies/semantic.d.ts +19 -0
  33. package/dist/types/chunking/strategies/sentence.d.ts +13 -0
  34. package/dist/types/chunking/strategies/structure-aware.d.ts +19 -0
  35. package/dist/types/citation/citation-tracker.d.ts +64 -0
  36. package/dist/types/citation/hallucination-detector.d.ts +40 -0
  37. package/dist/types/citation/source-mapper.d.ts +37 -0
  38. package/dist/types/connectors/anthropic-connector.d.ts +97 -0
  39. package/dist/types/connectors/base-connector.d.ts +87 -0
  40. package/dist/types/connectors/cohere-connector.d.ts +94 -0
  41. package/dist/types/connectors/connector-utils.d.ts +110 -0
  42. package/dist/types/connectors/local-embedder.d.ts +95 -0
  43. package/dist/types/connectors/memory-retriever.d.ts +99 -0
  44. package/dist/types/connectors/ollama-connector.d.ts +115 -0
  45. package/dist/types/connectors/openai-connector.d.ts +129 -0
  46. package/dist/types/connectors/rag-prompt.d.ts +40 -0
  47. package/dist/types/core/create-pipeline.d.ts +8 -0
  48. package/dist/types/core/plugin-registry.d.ts +136 -0
  49. package/dist/types/core/stage-contracts.d.ts +36 -0
  50. package/dist/types/core/validate-plugin.d.ts +45 -0
  51. package/dist/types/cost/cost-calculator.d.ts +152 -0
  52. package/dist/types/cost/cost-tracker.d.ts +126 -0
  53. package/dist/types/cost/token-budget.d.ts +110 -0
  54. package/dist/types/dag/core/error-context.d.ts +239 -0
  55. package/dist/types/dag/core/execution-scheduler.d.ts +191 -0
  56. package/dist/types/dag/core/topology-validation.d.ts +226 -0
  57. package/dist/types/dag/dag-engine.d.ts +156 -0
  58. package/dist/types/dag/dag-node.d.ts +37 -0
  59. package/dist/types/dashboard/dashboard-generator.d.ts +127 -0
  60. package/dist/types/dashboard/metrics-aggregator.d.ts +218 -0
  61. package/dist/types/debug/execution-tracer.d.ts +57 -0
  62. package/dist/types/debug/trace-inspector.d.ts +31 -0
  63. package/dist/types/ecosystem/plugin-certification.d.ts +169 -0
  64. package/dist/types/enterprise/data-governance.d.ts +203 -0
  65. package/dist/types/enterprise/multi-tenancy.d.ts +137 -0
  66. package/dist/types/enterprise/sso-integration.d.ts +420 -0
  67. package/dist/types/evaluate/context-metrics.d.ts +55 -0
  68. package/dist/types/evaluate/faithfulness.d.ts +41 -0
  69. package/dist/types/evaluate/groundedness.d.ts +60 -0
  70. package/dist/types/evaluate/pipeline-evaluator.d.ts +88 -0
  71. package/dist/types/evaluate/relevance.d.ts +21 -0
  72. package/dist/types/evaluate/scoring.d.ts +123 -0
  73. package/dist/types/evaluate/semantic-similarity.d.ts +14 -0
  74. package/dist/types/experimental.d.ts +15 -0
  75. package/dist/types/graph/entity-extractor.d.ts +84 -0
  76. package/dist/types/graph/graph-index.d.ts +49 -0
  77. package/dist/types/graph/graph-retriever.d.ts +81 -0
  78. package/dist/types/graph/knowledge-graph.d.ts +129 -0
  79. package/dist/types/grounding/check-answer.d.ts +101 -0
  80. package/dist/types/guardrails/guardrails-pipeline.d.ts +63 -0
  81. package/dist/types/guardrails/injection-detector.d.ts +47 -0
  82. package/dist/types/guardrails/post-generation-guard.d.ts +61 -0
  83. package/dist/types/guardrails/pre-retrieval-guard.d.ts +64 -0
  84. package/dist/types/guardrails/retrieval-guard.d.ts +57 -0
  85. package/dist/types/mcp/mcp-server.d.ts +85 -0
  86. package/dist/types/mcp/mcp-tool-builder.d.ts +55 -0
  87. package/dist/types/reranker/cascade-reranker.d.ts +40 -0
  88. package/dist/types/reranker/embedding-reranker.d.ts +77 -0
  89. package/dist/types/reranker/scoring-reranker.d.ts +41 -0
  90. package/dist/types/retrieval/bm25-search.d.ts +43 -0
  91. package/dist/types/retrieval/file-vector-store.d.ts +99 -0
  92. package/dist/types/retrieval/hybrid-retriever.d.ts +35 -0
  93. package/dist/types/retrieval/rank-fusion.d.ts +10 -0
  94. package/dist/types/security/jwt-config.d.ts +80 -0
  95. package/dist/types/security/jwt-validator.d.ts +195 -0
  96. package/dist/types/security/plugin-sandbox.d.ts +158 -0
  97. package/dist/types/security/plugin-signature-verifier.d.ts +139 -0
  98. package/dist/types/security/validator.d.ts +5 -0
  99. package/dist/types/streaming/embedding-stream.d.ts +65 -0
  100. package/dist/types/streaming/sse-adapter.d.ts +59 -0
  101. package/dist/types/streaming/stream-router.d.ts +88 -0
  102. package/dist/types/streaming/streaming-embedder.d.ts +99 -0
  103. package/dist/types/streaming/websocket-adapter.d.ts +71 -0
  104. package/dist/types/templates/scaffold.d.ts +55 -0
  105. package/dist/types/templates/template-registry.d.ts +63 -0
  106. package/dist/types/testing.d.ts +168 -0
  107. package/dist/types/utils/embed-query.d.ts +20 -0
  108. package/dist/types/utils/logger.d.ts +50 -0
  109. package/dist/types/utils/rate-limiter.d.ts +66 -0
  110. package/dist/types/utils/retry.d.ts +27 -0
  111. package/dist/types/utils/run-abort.d.ts +34 -0
  112. package/dist/types/utils/secure-logging.d.ts +51 -0
  113. package/dist/types/utils/structured-logger.d.ts +126 -0
  114. package/package.json +72 -33
  115. package/src/adapters/ai-sdk.js +215 -0
  116. package/src/agentic/agentic-pipeline.js +75 -7
  117. package/src/agentic/iterative-retriever.js +47 -10
  118. package/src/agentic/query-planner.js +329 -78
  119. package/src/cache/component-keys.js +38 -0
  120. package/src/cache/memory-cache.js +87 -0
  121. package/src/cache/pipeline-cache.js +260 -0
  122. package/src/cache/redis-cache.js +83 -0
  123. package/src/citation/citation-tracker.js +6 -4
  124. package/src/citation/hallucination-detector.js +2 -2
  125. package/src/citation/source-mapper.js +15 -28
  126. package/src/cli/commands/ai.js +106 -0
  127. package/src/cli/commands/plugin-hub/event-handlers.js +6 -0
  128. package/src/cli/commands/plugin-hub/hub-commands.js +0 -74
  129. package/src/cli/commands/plugin-hub/local-commands.js +143 -0
  130. package/src/cli/commands/plugin-hub.js +114 -49
  131. package/src/cli/diagnostics/config-checker.js +76 -63
  132. package/src/cli/diagnostics/environment-checker.js +62 -31
  133. package/src/cli/diagnostics/plugin-checker.js +36 -89
  134. package/src/cli/doctor-command.js +39 -5
  135. package/src/cli/enhanced-cli-commands.js +104 -233
  136. package/src/cli/handlers/ai-handler.js +364 -0
  137. package/src/cli/handlers/diagnostics-handler.js +78 -31
  138. package/src/cli/handlers/ingest-handler.js +28 -22
  139. package/src/cli/handlers/init-handler.js +148 -25
  140. package/src/cli/handlers/query-handler.js +203 -25
  141. package/src/cli/handlers/run-handler.js +16 -167
  142. package/src/cli/handlers/serve-handler.js +60 -0
  143. package/src/cli/services/bench-stats.js +54 -0
  144. package/src/cli/services/benchmark.js +161 -0
  145. package/src/cli/services/feedback.js +155 -0
  146. package/src/cli/services/instrument.js +83 -0
  147. package/src/cli/services/rag-service.js +361 -0
  148. package/src/cli/shared/cli-utils.js +24 -7
  149. package/src/cli/shared/config-context.js +21 -0
  150. package/src/cli/wizard/config-builder.js +81 -0
  151. package/src/cli/wizard/init-wizard.js +602 -0
  152. package/src/config/component-factory.js +125 -0
  153. package/src/config/enhanced-ragrc-schema.js +0 -2
  154. package/src/config/load-config.js +29 -10
  155. package/src/config/load-plugin-config.js +46 -2
  156. package/src/config/validate-schema.js +4 -1
  157. package/src/connectors/anthropic-connector.js +115 -57
  158. package/src/connectors/base-connector.js +74 -19
  159. package/src/connectors/cohere-connector.js +108 -34
  160. package/src/connectors/connector-utils.js +302 -0
  161. package/src/connectors/local-embedder.js +63 -5
  162. package/src/connectors/memory-retriever.js +125 -14
  163. package/src/connectors/ollama-connector.js +112 -51
  164. package/src/connectors/openai-connector.js +153 -44
  165. package/src/connectors/rag-prompt.js +88 -0
  166. package/src/core/create-pipeline.js +307 -61
  167. package/src/core/performance/parallel-processor.js +62 -1
  168. package/src/core/plugin-registry.js +125 -26
  169. package/src/core/stage-contracts.js +36 -0
  170. package/src/core/validate-plugin.js +85 -0
  171. package/src/cost/cost-calculator.js +205 -32
  172. package/src/debug/execution-tracer.js +14 -4
  173. package/src/dev/dev-server.js +173 -11
  174. package/src/dev/hot-reload.js +44 -4
  175. package/src/ecosystem/plugin-checks.js +181 -0
  176. package/src/ecosystem/plugin-hub.js +50 -125
  177. package/src/ecosystem/plugin-installer.js +298 -0
  178. package/src/ecosystem/plugin-probe.js +127 -0
  179. package/src/ecosystem/plugin-scanner.js +185 -0
  180. package/src/ecosystem/tar-extract.js +114 -0
  181. package/src/enterprise/audit-logging.js +158 -29
  182. package/src/enterprise/sso-integration.js +716 -228
  183. package/src/evaluate/context-metrics.js +28 -16
  184. package/src/evaluate/evaluator.js +3 -3
  185. package/src/evaluate/faithfulness.js +18 -14
  186. package/src/evaluate/groundedness.js +43 -12
  187. package/src/evaluate/pipeline-evaluator.js +35 -8
  188. package/src/evaluate/scoring.js +251 -3
  189. package/src/evaluate/semantic-similarity.js +79 -0
  190. package/src/experimental.js +71 -0
  191. package/src/grounding/check-answer.js +173 -0
  192. package/src/guardrails/guardrails-pipeline.js +69 -7
  193. package/src/guardrails/injection-detector.js +283 -0
  194. package/src/guardrails/post-generation-guard.js +19 -0
  195. package/src/guardrails/pre-retrieval-guard.js +36 -36
  196. package/src/guardrails/retrieval-guard.js +58 -8
  197. package/src/index.js +58 -1
  198. package/src/loader/csv-loader.js +3 -2
  199. package/src/loader/document-reader.js +91 -0
  200. package/src/loader/html-loader.js +3 -1
  201. package/src/loader/markdown-loader.js +3 -2
  202. package/src/reranker/cascade-reranker.js +3 -0
  203. package/src/reranker/embedding-reranker.js +136 -21
  204. package/src/reranker/llm-reranker.js +79 -12
  205. package/src/reranker/scoring-reranker.js +3 -0
  206. package/src/retrieval/bm25-search.js +12 -6
  207. package/src/retrieval/file-vector-store.js +241 -0
  208. package/src/retrieval/hybrid-retriever.js +22 -19
  209. package/src/security/jwt-config.js +13 -2
  210. package/src/security/jwt-validator.js +139 -34
  211. package/src/security/plugin-sandbox.js +18 -1
  212. package/src/security/plugin-signature-verifier.js +245 -70
  213. package/src/testing.js +254 -0
  214. package/src/utils/deprecate.js +69 -0
  215. package/src/utils/embed-query.js +33 -0
  216. package/src/utils/input-sanitizer.js +19 -4
  217. package/src/utils/logger.js +34 -4
  218. package/src/utils/run-abort.js +102 -0
  219. package/src/ai/federated-learning.js +0 -211
  220. package/src/ai/federation.js +0 -71
  221. package/src/ai/index.js +0 -22
  222. package/src/ai/model-trainer.js +0 -217
  223. package/src/ai/model-training.js +0 -202
  224. package/src/ai/multimodal/multi-modal-processor.js +0 -345
  225. package/src/ai/orchestrator.js +0 -65
  226. package/src/ai/retrieval/adaptive-retrieval-engine.js +0 -398
  227. package/src/cli/commands/ai-ml/adaptive-commands.js +0 -178
  228. package/src/cli/commands/ai-ml/federated-commands.js +0 -207
  229. package/src/cli/commands/ai-ml/index.js +0 -17
  230. package/src/cli/commands/ai-ml/multimodal-commands.js +0 -187
  231. package/src/cli/commands/ai-ml/shared.js +0 -51
  232. package/src/cli/commands/ai-ml/training-commands.js +0 -228
  233. package/src/cli/commands/ai-ml.js +0 -386
  234. package/src/cli/commands/docs.js +0 -228
  235. package/src/cli/commands/dx.js +0 -538
  236. package/src/cli/commands/wizard.js +0 -627
  237. package/src/cli/input-sanitization-middleware.js +0 -356
  238. package/src/cli/interactive-wizard.js +0 -373
  239. package/src/cli/templates/development.template.json +0 -64
  240. package/src/cli/templates/minimal.template.json +0 -33
  241. package/src/cli/templates/production.template.json +0 -104
  242. package/src/cli/templates/testing.template.json +0 -45
  243. package/src/cli/wizard/configuration.js +0 -148
  244. package/src/cli/wizard/observability.js +0 -79
  245. package/src/cli/wizard/performance.js +0 -76
  246. package/src/cli/wizard/plugin-selection.js +0 -297
  247. package/src/cli/wizard/preview-save.js +0 -172
  248. package/src/cli/wizard/project-setup.js +0 -66
  249. package/src/core/pipeline-factory.js +0 -59
  250. package/src/dag/enhanced-dag-engine.js +0 -894
  251. package/src/dx/index.js +0 -40
  252. package/src/dx/integration-templates.js +0 -662
  253. package/src/dx/performance-profiler.js +0 -968
  254. package/src/dx/realtime-debugger.js +0 -755
  255. package/src/dx/visual-pipeline-builder.js +0 -578
  256. package/src/ingest.js +0 -59
  257. package/src/pipeline/pipeline.js +0 -18
  258. package/src/query.js +0 -117
@@ -0,0 +1,361 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Ingest and query operations shared by the `ingest`, `query` and `run` CLI
5
+ * commands (and the dev server). Components come from the .ragrc.json
6
+ * pipeline via buildComponents(); the retriever must persist between
7
+ * processes for ingest and query to be separate commands (file-store does).
8
+ */
9
+
10
+ const { buildComponents } = require("../../config/component-factory");
11
+ const { readDocuments } = require("../../loader/document-reader");
12
+ const { embedQuery } = require("../../utils/embed-query");
13
+ const { createRagPipeline } = require("../../core/create-pipeline");
14
+ const { performance } = require("perf_hooks");
15
+ const { instrumentComponents } = require("./instrument");
16
+
17
+ function requireStage(components, stage) {
18
+ if (!components[stage]) {
19
+ throw new Error(
20
+ `No ${stage} configured. Add a { "stage": "${stage}", ... } entry to the pipeline in .ragrc.json.`,
21
+ );
22
+ }
23
+ return components[stage];
24
+ }
25
+
26
+ /** Embed document text with embed() (not embedQuery), normalizing the shape. */
27
+ function embedDocument(embedder, text) {
28
+ return embedQuery({ embed: (t) => embedder.embed(t) }, text);
29
+ }
30
+
31
+ /**
32
+ * Restore embedder state saved with the index (e.g. LocalEmbedder's
33
+ * vocabulary), so queries embed into the same vector space as ingestion.
34
+ */
35
+ function restoreEmbedder(embedder, retriever) {
36
+ const info = retriever.getEmbedderInfo?.();
37
+ if (info?.state && typeof embedder.setState === "function") {
38
+ embedder.setState(info.state);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Embed entries with the (already trained/restored) embedder, store them and
44
+ * record the embedder metadata and state with the index.
45
+ *
46
+ * @returns {Promise<Array<{id: string, vector: number[]}>>} The stored vectors
47
+ */
48
+ async function embedAndStore({
49
+ embedder,
50
+ embedderName,
51
+ store,
52
+ entries,
53
+ previous,
54
+ }) {
55
+ const vectors = [];
56
+ for (const entry of entries) {
57
+ vectors.push({
58
+ ...entry,
59
+ vector: await embedDocument(embedder, entry.content),
60
+ });
61
+ }
62
+ const dimensions = vectors[0]?.vector?.length ?? previous?.dimensions ?? 0;
63
+ if (vectors.some((v) => v.vector.length !== dimensions)) {
64
+ throw new Error("Embedder returned vectors of inconsistent dimensions");
65
+ }
66
+
67
+ await store.store(vectors);
68
+ store.setEmbedderInfo?.({
69
+ name: embedderName,
70
+ dimensions,
71
+ ...(typeof embedder.getState === "function" && {
72
+ state: embedder.getState(),
73
+ }),
74
+ });
75
+ store.save?.();
76
+ return vectors;
77
+ }
78
+
79
+ /**
80
+ * Load documents from a file or directory, embed their chunks and store them.
81
+ * Re-ingesting a source replaces its previous chunks.
82
+ *
83
+ * @param {object} params
84
+ * @param {object} params.config - Normalized config
85
+ * @param {string} params.configDir - Directory of the config file
86
+ * @param {string} params.inputPath - File or directory to ingest
87
+ * @param {boolean} [params.rebuild=false] - Discard the existing index first
88
+ * @returns {Promise<{files: number, chunks: number, totalChunks: number, sources: string[]}>}
89
+ */
90
+ async function ingest({ config, configDir, inputPath, rebuild = false }) {
91
+ const components = buildComponents(config, { configDir });
92
+ const embedder = requireStage(components, "embedder");
93
+ const store = requireStage(components, "retriever");
94
+ if (typeof store.store !== "function") {
95
+ throw new Error(
96
+ `Retriever "${components.names.retriever}" cannot store documents. Use "file-store".`,
97
+ );
98
+ }
99
+
100
+ const embedderName = components.names.embedder;
101
+ if (rebuild) store.clear?.();
102
+ const previous = store.getEmbedderInfo?.();
103
+ if (previous && previous.name !== embedderName && store.size() > 0) {
104
+ throw new Error(
105
+ `The index was built with embedder "${previous.name}" but the config now uses ` +
106
+ `"${embedderName}". Re-run with --rebuild to re-index everything.`,
107
+ );
108
+ }
109
+
110
+ const documents = await readDocuments(inputPath, { baseDir: configDir });
111
+ for (const { source } of documents) store.removeBySource?.(source);
112
+
113
+ let entries = documents.flatMap(({ source, chunks }) =>
114
+ chunks.map((content, i) => ({
115
+ id: `${source}#${i}`,
116
+ content,
117
+ metadata: { source, chunk: i },
118
+ })),
119
+ );
120
+
121
+ // Corpus-fitted embedders (LocalEmbedder) must be retrained on everything
122
+ // stored, and every stored chunk re-embedded into the new vector space.
123
+ if (typeof embedder.train === "function") {
124
+ entries = [...(store.list?.() || []), ...entries];
125
+ embedder.train(entries.map((e) => e.content));
126
+ } else {
127
+ restoreEmbedder(embedder, store);
128
+ }
129
+
130
+ const vectors = await embedAndStore({
131
+ embedder,
132
+ embedderName,
133
+ store,
134
+ entries,
135
+ previous,
136
+ });
137
+
138
+ return {
139
+ files: documents.length,
140
+ chunks: documents.reduce((n, d) => n + d.chunks.length, 0),
141
+ totalChunks: store.size?.() ?? vectors.length,
142
+ sources: documents.map((d) => d.source),
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Refit a corpus-trained embedder (only the built-in `local` TF-IDF embedder)
148
+ * and re-embed every stored chunk with the new model.
149
+ *
150
+ * Without `inputPath` the embedder is fitted on the indexed chunks. With it,
151
+ * the embedder is fitted on the documents read from that path only (they are
152
+ * not added to the index); indexed chunks that share no term with the new
153
+ * vocabulary embed to all-zero vectors and are counted in `zeroVectors`.
154
+ *
155
+ * @param {object} params
156
+ * @param {object} params.config
157
+ * @param {string} params.configDir
158
+ * @param {string} [params.inputPath] - Optional training corpus
159
+ * @returns {Promise<{trainedOn: string, trainingDocuments: number, vocabularySize: number,
160
+ * previousVocabularySize: number|null, reembedded: number, zeroVectors: number}>}
161
+ */
162
+ async function retrain({ config, configDir, inputPath }) {
163
+ // Check the configured name before building components, so a hosted
164
+ // embedder without credentials still gets this explanation.
165
+ const entry = (config.pipeline || []).find((e) => e.stage === "embedder");
166
+ if (!entry) {
167
+ throw new Error(
168
+ 'No embedder configured. Add { "stage": "embedder", "name": "local" } to the pipeline in .ragrc.json.',
169
+ );
170
+ }
171
+ if (entry.name !== "local") {
172
+ throw new Error(
173
+ `Training is only supported for the built-in "local" (TF-IDF) embedder; ` +
174
+ `this pipeline uses "${entry.name}". Hosted embedders (openai, cohere, ollama) ` +
175
+ "use fixed pre-trained models and cannot be fitted to your corpus.",
176
+ );
177
+ }
178
+
179
+ const components = buildComponents(config, { configDir });
180
+ const embedderName = components.names.embedder;
181
+ const embedder = requireStage(components, "embedder");
182
+ const store = requireStage(components, "retriever");
183
+ if (typeof store.list !== "function" || typeof store.save !== "function") {
184
+ throw new Error(
185
+ `Retriever "${components.names.retriever}" does not persist an index. Use "file-store".`,
186
+ );
187
+ }
188
+
189
+ const stored = store.list();
190
+ if (stored.length === 0) {
191
+ throw new Error(
192
+ "The index is empty. Run `rag-pipeline ingest <path>` first.",
193
+ );
194
+ }
195
+ const previous = store.getEmbedderInfo?.();
196
+ if (previous && previous.name !== embedderName) {
197
+ throw new Error(
198
+ `The index was built with embedder "${previous.name}". ` +
199
+ "Re-run `rag-pipeline ingest <path> --rebuild` with the local embedder first.",
200
+ );
201
+ }
202
+
203
+ let corpus = stored.map((e) => e.content);
204
+ let trainedOn = "index";
205
+ if (inputPath) {
206
+ const documents = await readDocuments(inputPath, { baseDir: configDir });
207
+ corpus = documents.flatMap((d) => d.chunks);
208
+ if (corpus.length === 0) {
209
+ throw new Error(`No text found to train on in ${inputPath}`);
210
+ }
211
+ trainedOn = inputPath;
212
+ }
213
+
214
+ embedder.train(corpus);
215
+ const vectors = await embedAndStore({
216
+ embedder,
217
+ embedderName,
218
+ store,
219
+ entries: stored,
220
+ previous,
221
+ });
222
+
223
+ return {
224
+ trainedOn,
225
+ trainingDocuments: corpus.length,
226
+ vocabularySize: embedder.getVocabularySize?.() ?? embedder.dimensions,
227
+ previousVocabularySize: previous?.dimensions ?? null,
228
+ reembedded: vectors.length,
229
+ zeroVectors: vectors.filter((v) => v.vector.every((x) => x === 0)).length,
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Build the configured components once and restore the embedder state saved
235
+ * with the index, so they can be reused across several retrieve()/query()
236
+ * calls (e.g. by `ai benchmark`).
237
+ *
238
+ * @returns {object} Components as returned by buildComponents()
239
+ */
240
+ function loadComponents({ config, configDir }) {
241
+ const components = buildComponents(config, { configDir });
242
+ const embedder = requireStage(components, "embedder");
243
+ const retriever = requireStage(components, "retriever");
244
+ if (retriever.size && retriever.size() === 0) {
245
+ throw new Error(
246
+ "The index is empty. Run `rag-pipeline ingest <path>` first.",
247
+ );
248
+ }
249
+ restoreEmbedder(embedder, retriever);
250
+ return components;
251
+ }
252
+
253
+ /**
254
+ * Retrieval only (no llm): embed the question, retrieve and, if configured,
255
+ * rerank.
256
+ *
257
+ * @param {object} params
258
+ * @param {object} [params.components] - Prebuilt components (loadComponents)
259
+ * @param {object} [params.config]
260
+ * @param {string} [params.configDir]
261
+ * @param {string} params.question
262
+ * @param {number} [params.topK=3]
263
+ * @returns {Promise<Array<{id: string, score: number, content: string, metadata: object}>>}
264
+ */
265
+ async function retrieve({ components, config, configDir, question, topK = 3 }) {
266
+ const c = components || loadComponents({ config, configDir });
267
+ const queryVector = await embedQuery(c.embedder, question);
268
+ let results = await c.retriever.retrieve({
269
+ query: question,
270
+ queryVector,
271
+ topK,
272
+ });
273
+ if (c.reranker) {
274
+ results = await c.reranker.rerank(question, results, { topK });
275
+ }
276
+ return results;
277
+ }
278
+
279
+ /**
280
+ * Answer a question from the index. Without an llm stage this returns the
281
+ * retrieved passages only (answer: null) instead of a placeholder answer.
282
+ *
283
+ * @param {object} params
284
+ * @param {object} params.config
285
+ * @param {string} params.configDir
286
+ * @param {string} params.question
287
+ * @param {number} [params.topK=3]
288
+ * @param {object} [params.components] - Prebuilt components (loadComponents)
289
+ * @param {number} [params.timeout] - Abort the run after this many ms
290
+ * @param {function(string): void} [params.onToken] - Stream the answer: called
291
+ * with each token (only with an llm stage)
292
+ * @returns {Promise<{answer: string|null, results: Array, citations?: object,
293
+ * timings: Array<{stage: string, ms: number}>, durationMs: number}>}
294
+ */
295
+ async function query({
296
+ config,
297
+ configDir,
298
+ question,
299
+ topK = 3,
300
+ components,
301
+ timeout,
302
+ onToken,
303
+ }) {
304
+ const loaded = components || loadComponents({ config, configDir });
305
+ const { components: c, timings, captured } = instrumentComponents(loaded);
306
+ const started = performance.now();
307
+
308
+ const pipeline = createRagPipeline({
309
+ embedder: c.embedder,
310
+ retriever: c.retriever,
311
+ llm: c.llm,
312
+ reranker: c.reranker,
313
+ });
314
+ const stream = Boolean(onToken && c.llm);
315
+ const result = await pipeline.run({
316
+ query: question,
317
+ options: {
318
+ topK,
319
+ citations: Boolean(c.llm),
320
+ ...(timeout && { timeout }),
321
+ ...(stream && { stream: true }),
322
+ },
323
+ });
324
+ if (result && result.success === false) throw new Error(result.error);
325
+
326
+ if (stream) {
327
+ let answer = "";
328
+ let citations;
329
+ for await (const chunk of result) {
330
+ if (chunk.token) {
331
+ answer += chunk.token;
332
+ onToken(chunk.token);
333
+ }
334
+ if (chunk.done) citations = chunk.citations;
335
+ }
336
+ return {
337
+ answer,
338
+ results: captured.results || [],
339
+ citations,
340
+ timings,
341
+ durationMs: performance.now() - started,
342
+ };
343
+ }
344
+
345
+ return {
346
+ answer: c.llm ? result.answer : null,
347
+ results: result.results,
348
+ ...(result.citations && { citations: result.citations }),
349
+ timings,
350
+ durationMs: performance.now() - started,
351
+ };
352
+ }
353
+
354
+ module.exports = {
355
+ ingest,
356
+ retrain,
357
+ query,
358
+ retrieve,
359
+ loadComponents,
360
+ restoreEmbedder,
361
+ };
@@ -28,6 +28,23 @@ function parsePositiveInteger(value) {
28
28
  return parsed;
29
29
  }
30
30
 
31
+ // Top-level commands offered by shell completion
32
+ const COMMANDS = [
33
+ "init",
34
+ "ingest",
35
+ "query",
36
+ "run",
37
+ "serve",
38
+ "doctor",
39
+ "validate",
40
+ "hub",
41
+ "ai",
42
+ "plugin",
43
+ "config",
44
+ "info",
45
+ "completion",
46
+ ].join(" ");
47
+
31
48
  /**
32
49
  * Generate shell completion script
33
50
  */
@@ -36,7 +53,7 @@ function generateCompletionScript(shell) {
36
53
  case "bash":
37
54
  return `# RAG Pipeline bash completion
38
55
  _rag_pipeline_completions() {
39
- COMPREPLY=($(compgen -W "init doctor ingest query plugin config info validate completion" -- "\${COMP_WORDS[COMP_CWORD]}"))
56
+ COMPREPLY=($(compgen -W "${COMMANDS}" -- "\${COMP_WORDS[COMP_CWORD]}"))
40
57
  }
41
58
  complete -F _rag_pipeline_completions rag-pipeline`;
42
59
 
@@ -45,13 +62,13 @@ complete -F _rag_pipeline_completions rag-pipeline`;
45
62
  #compdef rag-pipeline
46
63
  _rag_pipeline() {
47
64
  _arguments \\
48
- '1:command:(init doctor ingest query plugin config info validate completion)'
65
+ '1:command:(${COMMANDS})'
49
66
  }
50
67
  _rag_pipeline "$@"`;
51
68
 
52
69
  case "fish":
53
70
  return `# RAG Pipeline fish completion
54
- complete -c rag-pipeline -n '__fish_use_subcommand' -a 'init doctor ingest query plugin config info validate completion'`;
71
+ complete -c rag-pipeline -n '__fish_use_subcommand' -a '${COMMANDS}'`;
55
72
 
56
73
  default:
57
74
  return `# Completion not available for ${shell}`;
@@ -71,10 +88,10 @@ function getVersion() {
71
88
  function getExtendedHelp() {
72
89
  return `
73
90
  Examples:
74
- rag-pipeline init --interactive Initialize with wizard
75
- rag-pipeline doctor --auto-fix Diagnose and fix issues
76
- rag-pipeline plugin search openai Search for plugins
77
- rag-pipeline config show plugins Show plugin configuration
91
+ rag-pipeline init Create .ragrc.json (prompts in a terminal)
92
+ rag-pipeline ingest ./docs Index a file or directory
93
+ rag-pipeline query "What is X?" Ask a question about the indexed documents
94
+ rag-pipeline config show Show the current configuration
78
95
 
79
96
  For more information, visit: https://github.com/DevilsDev/rag-pipeline-utils
80
97
  `;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolve and load the config file named by the global --config option.
3
+ */
4
+
5
+ const path = require("path");
6
+ const {
7
+ loadRagConfig,
8
+ resolveConfigPath,
9
+ } = require("../../config/load-config.js");
10
+
11
+ /**
12
+ * @param {object} globalOptions - program.opts(); uses globalOptions.config
13
+ * @returns {{config: object, configPath: string, configDir: string}}
14
+ */
15
+ function loadCliConfig(globalOptions = {}) {
16
+ const configPath = resolveConfigPath(globalOptions.config || ".ragrc.json");
17
+ const config = loadRagConfig(configPath);
18
+ return { config, configPath, configDir: path.dirname(configPath) };
19
+ }
20
+
21
+ module.exports = { loadCliConfig };
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Builds the .ragrc.json written by `rag-pipeline init` (both the
5
+ * non-interactive template path and the setup wizard).
6
+ */
7
+
8
+ const { validateRagrc } = require("../../config/enhanced-ragrc-schema.js");
9
+
10
+ const DEFAULT_INDEX_PATH = ".rag/index.json";
11
+
12
+ /**
13
+ * A stage selection is either a built-in name (string) or
14
+ * `{ name, config?, module? }`, where `module` is a custom plugin path
15
+ * (relative to the config file) written to `plugins.<stage>.<name>`.
16
+ */
17
+ function toSelection(value) {
18
+ if (value === null || value === undefined) return null;
19
+ if (typeof value === "string") return { name: value };
20
+ return value;
21
+ }
22
+
23
+ /**
24
+ * @param {object} choice
25
+ * @param {string|object} choice.embedder - Embedder selection
26
+ * @param {string|object|null} [choice.llm] - LLM selection (null = none)
27
+ * @param {string|object} [choice.retriever='file-store'] - Retriever selection
28
+ * @param {string|object|null} [choice.reranker] - Reranker selection
29
+ * @param {string} [choice.indexPath='.rag/index.json'] - file-store path when
30
+ * the retriever selection carries no config of its own
31
+ * @returns {object} Normalized, validated configuration
32
+ */
33
+ function buildInitConfig({
34
+ embedder,
35
+ llm = null,
36
+ retriever = "file-store",
37
+ reranker = null,
38
+ indexPath = DEFAULT_INDEX_PATH,
39
+ }) {
40
+ const retrieverSel = toSelection(retriever);
41
+ if (retrieverSel.name === "file-store" && !retrieverSel.module) {
42
+ retrieverSel.config = { path: indexPath, ...(retrieverSel.config || {}) };
43
+ }
44
+
45
+ const stages = [
46
+ ["loader", { name: "auto" }],
47
+ ["embedder", toSelection(embedder)],
48
+ ["retriever", retrieverSel],
49
+ ["reranker", toSelection(reranker)],
50
+ ["llm", toSelection(llm)],
51
+ ];
52
+
53
+ const pipeline = [];
54
+ const plugins = {};
55
+ for (const [stage, sel] of stages) {
56
+ if (!sel) continue;
57
+ const entry = { stage, name: sel.name };
58
+ if (sel.config && Object.keys(sel.config).length > 0) {
59
+ entry.config = sel.config;
60
+ }
61
+ pipeline.push(entry);
62
+ if (sel.module) {
63
+ plugins[stage] = { ...(plugins[stage] || {}), [sel.name]: sel.module };
64
+ }
65
+ }
66
+
67
+ const result = validateRagrc({
68
+ namespace: "default",
69
+ metadata: { name: "rag-pipeline", createdAt: new Date().toISOString() },
70
+ pipeline,
71
+ ...(Object.keys(plugins).length > 0 && { plugins }),
72
+ });
73
+ if (!result.valid) {
74
+ throw new Error(
75
+ `Generated configuration is invalid: ${JSON.stringify(result.errors)}`,
76
+ );
77
+ }
78
+ return result.normalized;
79
+ }
80
+
81
+ module.exports = { buildInitConfig, DEFAULT_INDEX_PATH };