@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.
- package/.ragrc.schema.json +83 -192
- package/CHANGELOG.md +431 -0
- package/README.md +418 -117
- package/SECURITY.md +5 -5
- package/dist/experimental.cjs +2 -0
- package/dist/experimental.d.ts +1 -0
- package/dist/experimental.mjs +5 -0
- package/dist/index.cjs +6442 -1355
- package/dist/index.d.ts +129 -2
- package/dist/index.mjs +1 -1
- package/dist/testing.cjs +2 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.mjs +5 -0
- package/dist/types/adapters/ai-sdk.d.ts +53 -0
- package/dist/types/agentic/agentic-pipeline.d.ts +103 -0
- package/dist/types/agentic/iterative-retriever.d.ts +85 -0
- package/dist/types/agentic/query-planner.d.ts +145 -0
- package/dist/types/agentic/self-critique.d.ts +45 -0
- package/dist/types/ai/adaptive-retrieval.d.ts +44 -0
- package/dist/types/ai/federation/federated-learning-coordinator.d.ts +62 -0
- package/dist/types/ai/multimodal-processing.d.ts +32 -0
- package/dist/types/ai/multimodal.d.ts +39 -0
- package/dist/types/ai/retrieval-engine.d.ts +37 -0
- package/dist/types/ai/training/model-training-orchestrator.d.ts +31 -0
- package/dist/types/cache/component-keys.d.ts +7 -0
- package/dist/types/cache/memory-cache.d.ts +54 -0
- package/dist/types/cache/pipeline-cache.d.ts +66 -0
- package/dist/types/cache/redis-cache.d.ts +46 -0
- package/dist/types/chunking/chunking-engine.d.ts +70 -0
- package/dist/types/chunking/strategies/fixed-size.d.ts +17 -0
- package/dist/types/chunking/strategies/recursive.d.ts +20 -0
- package/dist/types/chunking/strategies/semantic.d.ts +19 -0
- package/dist/types/chunking/strategies/sentence.d.ts +13 -0
- package/dist/types/chunking/strategies/structure-aware.d.ts +19 -0
- package/dist/types/citation/citation-tracker.d.ts +64 -0
- package/dist/types/citation/hallucination-detector.d.ts +40 -0
- package/dist/types/citation/source-mapper.d.ts +37 -0
- package/dist/types/connectors/anthropic-connector.d.ts +97 -0
- package/dist/types/connectors/base-connector.d.ts +87 -0
- package/dist/types/connectors/cohere-connector.d.ts +94 -0
- package/dist/types/connectors/connector-utils.d.ts +110 -0
- package/dist/types/connectors/local-embedder.d.ts +95 -0
- package/dist/types/connectors/memory-retriever.d.ts +99 -0
- package/dist/types/connectors/ollama-connector.d.ts +115 -0
- package/dist/types/connectors/openai-connector.d.ts +129 -0
- package/dist/types/connectors/rag-prompt.d.ts +40 -0
- package/dist/types/core/create-pipeline.d.ts +8 -0
- package/dist/types/core/plugin-registry.d.ts +136 -0
- package/dist/types/core/stage-contracts.d.ts +36 -0
- package/dist/types/core/validate-plugin.d.ts +45 -0
- package/dist/types/cost/cost-calculator.d.ts +152 -0
- package/dist/types/cost/cost-tracker.d.ts +126 -0
- package/dist/types/cost/token-budget.d.ts +110 -0
- package/dist/types/dag/core/error-context.d.ts +239 -0
- package/dist/types/dag/core/execution-scheduler.d.ts +191 -0
- package/dist/types/dag/core/topology-validation.d.ts +226 -0
- package/dist/types/dag/dag-engine.d.ts +156 -0
- package/dist/types/dag/dag-node.d.ts +37 -0
- package/dist/types/dashboard/dashboard-generator.d.ts +127 -0
- package/dist/types/dashboard/metrics-aggregator.d.ts +218 -0
- package/dist/types/debug/execution-tracer.d.ts +57 -0
- package/dist/types/debug/trace-inspector.d.ts +31 -0
- package/dist/types/ecosystem/plugin-certification.d.ts +169 -0
- package/dist/types/enterprise/data-governance.d.ts +203 -0
- package/dist/types/enterprise/multi-tenancy.d.ts +137 -0
- package/dist/types/enterprise/sso-integration.d.ts +420 -0
- package/dist/types/evaluate/context-metrics.d.ts +55 -0
- package/dist/types/evaluate/faithfulness.d.ts +41 -0
- package/dist/types/evaluate/groundedness.d.ts +60 -0
- package/dist/types/evaluate/pipeline-evaluator.d.ts +88 -0
- package/dist/types/evaluate/relevance.d.ts +21 -0
- package/dist/types/evaluate/scoring.d.ts +123 -0
- package/dist/types/evaluate/semantic-similarity.d.ts +14 -0
- package/dist/types/experimental.d.ts +15 -0
- package/dist/types/graph/entity-extractor.d.ts +84 -0
- package/dist/types/graph/graph-index.d.ts +49 -0
- package/dist/types/graph/graph-retriever.d.ts +81 -0
- package/dist/types/graph/knowledge-graph.d.ts +129 -0
- package/dist/types/grounding/check-answer.d.ts +101 -0
- package/dist/types/guardrails/guardrails-pipeline.d.ts +63 -0
- package/dist/types/guardrails/injection-detector.d.ts +47 -0
- package/dist/types/guardrails/post-generation-guard.d.ts +61 -0
- package/dist/types/guardrails/pre-retrieval-guard.d.ts +64 -0
- package/dist/types/guardrails/retrieval-guard.d.ts +57 -0
- package/dist/types/mcp/mcp-server.d.ts +85 -0
- package/dist/types/mcp/mcp-tool-builder.d.ts +55 -0
- package/dist/types/reranker/cascade-reranker.d.ts +40 -0
- package/dist/types/reranker/embedding-reranker.d.ts +77 -0
- package/dist/types/reranker/scoring-reranker.d.ts +41 -0
- package/dist/types/retrieval/bm25-search.d.ts +43 -0
- package/dist/types/retrieval/file-vector-store.d.ts +99 -0
- package/dist/types/retrieval/hybrid-retriever.d.ts +35 -0
- package/dist/types/retrieval/rank-fusion.d.ts +10 -0
- package/dist/types/security/jwt-config.d.ts +80 -0
- package/dist/types/security/jwt-validator.d.ts +195 -0
- package/dist/types/security/plugin-sandbox.d.ts +158 -0
- package/dist/types/security/plugin-signature-verifier.d.ts +139 -0
- package/dist/types/security/validator.d.ts +5 -0
- package/dist/types/streaming/embedding-stream.d.ts +65 -0
- package/dist/types/streaming/sse-adapter.d.ts +59 -0
- package/dist/types/streaming/stream-router.d.ts +88 -0
- package/dist/types/streaming/streaming-embedder.d.ts +99 -0
- package/dist/types/streaming/websocket-adapter.d.ts +71 -0
- package/dist/types/templates/scaffold.d.ts +55 -0
- package/dist/types/templates/template-registry.d.ts +63 -0
- package/dist/types/testing.d.ts +168 -0
- package/dist/types/utils/embed-query.d.ts +20 -0
- package/dist/types/utils/logger.d.ts +50 -0
- package/dist/types/utils/rate-limiter.d.ts +66 -0
- package/dist/types/utils/retry.d.ts +27 -0
- package/dist/types/utils/run-abort.d.ts +34 -0
- package/dist/types/utils/secure-logging.d.ts +51 -0
- package/dist/types/utils/structured-logger.d.ts +126 -0
- package/package.json +72 -33
- package/src/adapters/ai-sdk.js +215 -0
- package/src/agentic/agentic-pipeline.js +75 -7
- package/src/agentic/iterative-retriever.js +47 -10
- package/src/agentic/query-planner.js +329 -78
- package/src/cache/component-keys.js +38 -0
- package/src/cache/memory-cache.js +87 -0
- package/src/cache/pipeline-cache.js +260 -0
- package/src/cache/redis-cache.js +83 -0
- package/src/citation/citation-tracker.js +6 -4
- package/src/citation/hallucination-detector.js +2 -2
- package/src/citation/source-mapper.js +15 -28
- package/src/cli/commands/ai.js +106 -0
- package/src/cli/commands/plugin-hub/event-handlers.js +6 -0
- package/src/cli/commands/plugin-hub/hub-commands.js +0 -74
- package/src/cli/commands/plugin-hub/local-commands.js +143 -0
- package/src/cli/commands/plugin-hub.js +114 -49
- package/src/cli/diagnostics/config-checker.js +76 -63
- package/src/cli/diagnostics/environment-checker.js +62 -31
- package/src/cli/diagnostics/plugin-checker.js +36 -89
- package/src/cli/doctor-command.js +39 -5
- package/src/cli/enhanced-cli-commands.js +104 -233
- package/src/cli/handlers/ai-handler.js +364 -0
- package/src/cli/handlers/diagnostics-handler.js +78 -31
- package/src/cli/handlers/ingest-handler.js +28 -22
- package/src/cli/handlers/init-handler.js +148 -25
- package/src/cli/handlers/query-handler.js +203 -25
- package/src/cli/handlers/run-handler.js +16 -167
- package/src/cli/handlers/serve-handler.js +60 -0
- package/src/cli/services/bench-stats.js +54 -0
- package/src/cli/services/benchmark.js +161 -0
- package/src/cli/services/feedback.js +155 -0
- package/src/cli/services/instrument.js +83 -0
- package/src/cli/services/rag-service.js +361 -0
- package/src/cli/shared/cli-utils.js +24 -7
- package/src/cli/shared/config-context.js +21 -0
- package/src/cli/wizard/config-builder.js +81 -0
- package/src/cli/wizard/init-wizard.js +602 -0
- package/src/config/component-factory.js +125 -0
- package/src/config/enhanced-ragrc-schema.js +0 -2
- package/src/config/load-config.js +29 -10
- package/src/config/load-plugin-config.js +46 -2
- package/src/config/validate-schema.js +4 -1
- package/src/connectors/anthropic-connector.js +115 -57
- package/src/connectors/base-connector.js +74 -19
- package/src/connectors/cohere-connector.js +108 -34
- package/src/connectors/connector-utils.js +302 -0
- package/src/connectors/local-embedder.js +63 -5
- package/src/connectors/memory-retriever.js +125 -14
- package/src/connectors/ollama-connector.js +112 -51
- package/src/connectors/openai-connector.js +153 -44
- package/src/connectors/rag-prompt.js +88 -0
- package/src/core/create-pipeline.js +307 -61
- package/src/core/performance/parallel-processor.js +62 -1
- package/src/core/plugin-registry.js +125 -26
- package/src/core/stage-contracts.js +36 -0
- package/src/core/validate-plugin.js +85 -0
- package/src/cost/cost-calculator.js +205 -32
- package/src/debug/execution-tracer.js +14 -4
- package/src/dev/dev-server.js +173 -11
- package/src/dev/hot-reload.js +44 -4
- package/src/ecosystem/plugin-checks.js +181 -0
- package/src/ecosystem/plugin-hub.js +50 -125
- package/src/ecosystem/plugin-installer.js +298 -0
- package/src/ecosystem/plugin-probe.js +127 -0
- package/src/ecosystem/plugin-scanner.js +185 -0
- package/src/ecosystem/tar-extract.js +114 -0
- package/src/enterprise/audit-logging.js +158 -29
- package/src/enterprise/sso-integration.js +716 -228
- package/src/evaluate/context-metrics.js +28 -16
- package/src/evaluate/evaluator.js +3 -3
- package/src/evaluate/faithfulness.js +18 -14
- package/src/evaluate/groundedness.js +43 -12
- package/src/evaluate/pipeline-evaluator.js +35 -8
- package/src/evaluate/scoring.js +251 -3
- package/src/evaluate/semantic-similarity.js +79 -0
- package/src/experimental.js +71 -0
- package/src/grounding/check-answer.js +173 -0
- package/src/guardrails/guardrails-pipeline.js +69 -7
- package/src/guardrails/injection-detector.js +283 -0
- package/src/guardrails/post-generation-guard.js +19 -0
- package/src/guardrails/pre-retrieval-guard.js +36 -36
- package/src/guardrails/retrieval-guard.js +58 -8
- package/src/index.js +58 -1
- package/src/loader/csv-loader.js +3 -2
- package/src/loader/document-reader.js +91 -0
- package/src/loader/html-loader.js +3 -1
- package/src/loader/markdown-loader.js +3 -2
- package/src/reranker/cascade-reranker.js +3 -0
- package/src/reranker/embedding-reranker.js +136 -21
- package/src/reranker/llm-reranker.js +79 -12
- package/src/reranker/scoring-reranker.js +3 -0
- package/src/retrieval/bm25-search.js +12 -6
- package/src/retrieval/file-vector-store.js +241 -0
- package/src/retrieval/hybrid-retriever.js +22 -19
- package/src/security/jwt-config.js +13 -2
- package/src/security/jwt-validator.js +139 -34
- package/src/security/plugin-sandbox.js +18 -1
- package/src/security/plugin-signature-verifier.js +245 -70
- package/src/testing.js +254 -0
- package/src/utils/deprecate.js +69 -0
- package/src/utils/embed-query.js +33 -0
- package/src/utils/input-sanitizer.js +19 -4
- package/src/utils/logger.js +34 -4
- package/src/utils/run-abort.js +102 -0
- package/src/ai/federated-learning.js +0 -211
- package/src/ai/federation.js +0 -71
- package/src/ai/index.js +0 -22
- package/src/ai/model-trainer.js +0 -217
- package/src/ai/model-training.js +0 -202
- package/src/ai/multimodal/multi-modal-processor.js +0 -345
- package/src/ai/orchestrator.js +0 -65
- package/src/ai/retrieval/adaptive-retrieval-engine.js +0 -398
- package/src/cli/commands/ai-ml/adaptive-commands.js +0 -178
- package/src/cli/commands/ai-ml/federated-commands.js +0 -207
- package/src/cli/commands/ai-ml/index.js +0 -17
- package/src/cli/commands/ai-ml/multimodal-commands.js +0 -187
- package/src/cli/commands/ai-ml/shared.js +0 -51
- package/src/cli/commands/ai-ml/training-commands.js +0 -228
- package/src/cli/commands/ai-ml.js +0 -386
- package/src/cli/commands/docs.js +0 -228
- package/src/cli/commands/dx.js +0 -538
- package/src/cli/commands/wizard.js +0 -627
- package/src/cli/input-sanitization-middleware.js +0 -356
- package/src/cli/interactive-wizard.js +0 -373
- package/src/cli/templates/development.template.json +0 -64
- package/src/cli/templates/minimal.template.json +0 -33
- package/src/cli/templates/production.template.json +0 -104
- package/src/cli/templates/testing.template.json +0 -45
- package/src/cli/wizard/configuration.js +0 -148
- package/src/cli/wizard/observability.js +0 -79
- package/src/cli/wizard/performance.js +0 -76
- package/src/cli/wizard/plugin-selection.js +0 -297
- package/src/cli/wizard/preview-save.js +0 -172
- package/src/cli/wizard/project-setup.js +0 -66
- package/src/core/pipeline-factory.js +0 -59
- package/src/dag/enhanced-dag-engine.js +0 -894
- package/src/dx/index.js +0 -40
- package/src/dx/integration-templates.js +0 -662
- package/src/dx/performance-profiler.js +0 -968
- package/src/dx/realtime-debugger.js +0 -755
- package/src/dx/visual-pipeline-builder.js +0 -578
- package/src/ingest.js +0 -59
- package/src/pipeline/pipeline.js +0 -18
- 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 "
|
|
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:(
|
|
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 '
|
|
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
|
|
75
|
-
rag-pipeline
|
|
76
|
-
rag-pipeline
|
|
77
|
-
rag-pipeline config show
|
|
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 };
|