@devilsdev/rag-pipeline-utils 2.4.5 → 2.5.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 +345 -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 +4992 -1242
- package/dist/index.d.ts +80 -2
- package/dist/index.mjs +1 -1
- 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/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 +92 -0
- package/dist/types/connectors/base-connector.d.ts +87 -0
- package/dist/types/connectors/cohere-connector.d.ts +89 -0
- package/dist/types/connectors/connector-utils.d.ts +110 -0
- package/dist/types/connectors/local-embedder.d.ts +86 -0
- package/dist/types/connectors/memory-retriever.d.ts +91 -0
- package/dist/types/connectors/ollama-connector.d.ts +110 -0
- package/dist/types/connectors/openai-connector.d.ts +114 -0
- package/dist/types/connectors/rag-prompt.d.ts +40 -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/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/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 +87 -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/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 +53 -29
- 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/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 +101 -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 +96 -36
- 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 +72 -217
- 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 +41 -26
- 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/rag-service.js +322 -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 +617 -0
- package/src/config/component-factory.js +125 -0
- package/src/config/load-config.js +29 -10
- package/src/config/load-plugin-config.js +46 -2
- package/src/connectors/anthropic-connector.js +93 -55
- package/src/connectors/base-connector.js +74 -19
- package/src/connectors/cohere-connector.js +88 -32
- package/src/connectors/connector-utils.js +302 -0
- package/src/connectors/local-embedder.js +43 -5
- package/src/connectors/memory-retriever.js +108 -15
- package/src/connectors/ollama-connector.js +83 -49
- package/src/connectors/openai-connector.js +108 -44
- package/src/connectors/rag-prompt.js +88 -0
- package/src/core/create-pipeline.js +209 -60
- package/src/core/plugin-registry.js +87 -11
- package/src/cost/cost-calculator.js +205 -32
- package/src/dev/dev-server.js +166 -11
- package/src/dev/hot-reload.js +40 -1
- 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 +715 -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/experimental.js +71 -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 +36 -1
- package/src/loader/document-reader.js +91 -0
- 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 +212 -0
- package/src/retrieval/hybrid-retriever.js +22 -19
- package/src/security/jwt-config.js +13 -2
- package/src/security/jwt-validator.js +134 -30
- package/src/security/plugin-sandbox.js +18 -1
- package/src/security/plugin-signature-verifier.js +245 -70
- 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,364 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handlers for `rag-pipeline ai ...`. Everything printed here is computed from
|
|
5
|
+
* the configured pipeline, the index or the given files; failures set
|
|
6
|
+
* process.exitCode = 1 and explain why on stderr.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
const path = require("path");
|
|
11
|
+
const fg = require("fast-glob");
|
|
12
|
+
|
|
13
|
+
const { loadCliConfig } = require("../shared/config-context.js");
|
|
14
|
+
const {
|
|
15
|
+
retrain,
|
|
16
|
+
retrieve,
|
|
17
|
+
loadComponents,
|
|
18
|
+
} = require("../services/rag-service.js");
|
|
19
|
+
const { runBenchmark } = require("../services/benchmark.js");
|
|
20
|
+
const {
|
|
21
|
+
feedbackPath,
|
|
22
|
+
loadFeedback,
|
|
23
|
+
addFeedback,
|
|
24
|
+
applyFeedback,
|
|
25
|
+
} = require("../services/feedback.js");
|
|
26
|
+
const { readDocuments } = require("../../loader/document-reader.js");
|
|
27
|
+
|
|
28
|
+
function fail(prefix, error) {
|
|
29
|
+
console.error(`${prefix}: ${error.message}`);
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const fmt = (ms) => `${ms.toFixed(2)} ms`;
|
|
34
|
+
const signed = (x) => `${x >= 0 ? "+" : "-"}${Math.abs(x).toFixed(3)}`;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* `ai train embedding [path]`
|
|
38
|
+
*/
|
|
39
|
+
async function handleTrainEmbedding(globalOptions, inputPath) {
|
|
40
|
+
try {
|
|
41
|
+
const { config, configDir } = loadCliConfig(globalOptions);
|
|
42
|
+
if (globalOptions.dryRun) {
|
|
43
|
+
console.log(
|
|
44
|
+
`Dry run: would fit the local embedder on ${inputPath || "the indexed chunks"} ` +
|
|
45
|
+
"and re-embed the index.",
|
|
46
|
+
);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const r = await retrain({ config, configDir, inputPath });
|
|
50
|
+
const source =
|
|
51
|
+
r.trainedOn === "index"
|
|
52
|
+
? `${r.trainingDocuments} indexed chunk(s)`
|
|
53
|
+
: `${r.trainingDocuments} chunk(s) read from ${r.trainedOn}`;
|
|
54
|
+
console.log(`Trained the local TF-IDF embedder on ${source}.`);
|
|
55
|
+
console.log(
|
|
56
|
+
`Vocabulary: ${r.vocabularySize} term(s)` +
|
|
57
|
+
(r.previousVocabularySize !== null
|
|
58
|
+
? ` (previously ${r.previousVocabularySize})`
|
|
59
|
+
: ""),
|
|
60
|
+
);
|
|
61
|
+
console.log(
|
|
62
|
+
`Re-embedded ${r.reembedded} indexed chunk(s) with the new model.`,
|
|
63
|
+
);
|
|
64
|
+
if (r.zeroVectors > 0) {
|
|
65
|
+
console.log(
|
|
66
|
+
`Warning: ${r.zeroVectors} indexed chunk(s) share no term with the new ` +
|
|
67
|
+
"vocabulary and can no longer be retrieved.",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (r.trainedOn !== "index") {
|
|
71
|
+
console.log(
|
|
72
|
+
"Note: the next `ingest` refits the embedder on the indexed chunks.",
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
fail("Training failed", error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* `ai benchmark`
|
|
82
|
+
*/
|
|
83
|
+
async function handleBenchmark(globalOptions, options = {}) {
|
|
84
|
+
try {
|
|
85
|
+
const { config, configDir } = loadCliConfig(globalOptions);
|
|
86
|
+
const report = await runBenchmark({
|
|
87
|
+
config,
|
|
88
|
+
configDir,
|
|
89
|
+
queriesFile: options.queries,
|
|
90
|
+
runs: options.runs || 3,
|
|
91
|
+
topK: options.topK || 3,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (options.json) {
|
|
95
|
+
console.log(JSON.stringify(report, null, 2));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const env = report.environment;
|
|
100
|
+
const n = report.queries.length;
|
|
101
|
+
console.log(
|
|
102
|
+
`Benchmark: ${n} quer${n === 1 ? "y" : "ies"} (${report.queriesSource}) ` +
|
|
103
|
+
`x ${report.runs} run(s), top-k ${report.topK}`,
|
|
104
|
+
);
|
|
105
|
+
console.log(
|
|
106
|
+
`Environment: Node ${env.node} (${env.platform}), ${env.chunks} indexed chunk(s), ` +
|
|
107
|
+
`embedder ${env.embedder}` +
|
|
108
|
+
(env.dimensions ? ` (${env.dimensions} dims)` : "") +
|
|
109
|
+
`, retriever ${env.retriever}, reranker ${env.reranker || "none"}, ` +
|
|
110
|
+
`llm ${env.llm || "none"}`,
|
|
111
|
+
);
|
|
112
|
+
console.log(`Setup (load config and index): ${fmt(report.setupMs)}`);
|
|
113
|
+
console.log(`Warm-up query (excluded): ${fmt(report.warmupMs)}`);
|
|
114
|
+
console.log("");
|
|
115
|
+
const labels = {
|
|
116
|
+
embed: "embed query",
|
|
117
|
+
retrieve: "retrieve",
|
|
118
|
+
rerank: "rerank",
|
|
119
|
+
endToEnd: report.includesGeneration
|
|
120
|
+
? "end-to-end (incl. llm)"
|
|
121
|
+
: "end-to-end",
|
|
122
|
+
};
|
|
123
|
+
const header = ["stage", "samples", "p50", "p95", "mean", "max"];
|
|
124
|
+
const rows = Object.entries(report.stages).map(([stage, s]) => [
|
|
125
|
+
labels[stage],
|
|
126
|
+
String(s.count),
|
|
127
|
+
fmt(s.p50),
|
|
128
|
+
fmt(s.p95),
|
|
129
|
+
fmt(s.mean),
|
|
130
|
+
fmt(s.max),
|
|
131
|
+
]);
|
|
132
|
+
const widths = header.map((h, i) =>
|
|
133
|
+
Math.max(h.length, ...rows.map((r) => r[i].length)),
|
|
134
|
+
);
|
|
135
|
+
const line = (cells) =>
|
|
136
|
+
cells
|
|
137
|
+
.map((c, i) => c.padEnd(widths[i]))
|
|
138
|
+
.join(" ")
|
|
139
|
+
.trimEnd();
|
|
140
|
+
console.log(line(header));
|
|
141
|
+
rows.forEach((r) => console.log(line(r)));
|
|
142
|
+
console.log("");
|
|
143
|
+
console.log(
|
|
144
|
+
`Results per query: ${report.results.min === report.results.max ? report.results.min : `${report.results.min}-${report.results.max}`}`,
|
|
145
|
+
);
|
|
146
|
+
console.log("Percentiles are nearest-rank over the measured samples.");
|
|
147
|
+
} catch (error) {
|
|
148
|
+
fail("Benchmark failed", error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* `ai feedback <question> <docId> --rating up|down`
|
|
154
|
+
*/
|
|
155
|
+
async function handleFeedback(globalOptions, question, docId, options = {}) {
|
|
156
|
+
try {
|
|
157
|
+
const { config, configDir } = loadCliConfig(globalOptions);
|
|
158
|
+
const components = loadComponents({ config, configDir });
|
|
159
|
+
const { retriever } = components;
|
|
160
|
+
if (typeof retriever.list !== "function") {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Retriever "${components.names.retriever}" cannot list documents. Use "file-store".`,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (!retriever.list().some((doc) => doc.id === docId)) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`No indexed chunk with id "${docId}". Ids are shown by \`rag-pipeline ai search\`.`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
const file = feedbackPath(retriever, configDir);
|
|
171
|
+
if (globalOptions.dryRun) {
|
|
172
|
+
console.log(
|
|
173
|
+
`Dry run: would record a "${options.rating}" vote for ${docId} in ${file}`,
|
|
174
|
+
);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const { total } = addFeedback(file, {
|
|
178
|
+
question,
|
|
179
|
+
docId,
|
|
180
|
+
rating: options.rating,
|
|
181
|
+
});
|
|
182
|
+
console.log(
|
|
183
|
+
`Recorded "${options.rating}" for ${docId} on "${question}" ` +
|
|
184
|
+
`(${total} vote(s) in ${path.relative(process.cwd(), file) || file}).`,
|
|
185
|
+
);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
fail("Feedback failed", error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* `ai search <question>`: retrieval re-scored with stored feedback.
|
|
193
|
+
*/
|
|
194
|
+
async function handleSearch(globalOptions, question, options = {}) {
|
|
195
|
+
try {
|
|
196
|
+
const { config, configDir } = loadCliConfig(globalOptions);
|
|
197
|
+
const topK = options.topK || 3;
|
|
198
|
+
const components = loadComponents({ config, configDir });
|
|
199
|
+
const { retriever } = components;
|
|
200
|
+
const size = retriever.size ? retriever.size() : topK;
|
|
201
|
+
// Re-score a larger pool so a boosted chunk can move into the top-k.
|
|
202
|
+
const pool = Math.min(size, Math.max(topK * 4, topK + 10));
|
|
203
|
+
const results = await retrieve({ components, question, topK: pool });
|
|
204
|
+
const file = feedbackPath(retriever, configDir);
|
|
205
|
+
const { entries } = loadFeedback(file);
|
|
206
|
+
const ranked = applyFeedback(results, entries, question)
|
|
207
|
+
.filter((r) => r.baseScore > 0)
|
|
208
|
+
.slice(0, topK);
|
|
209
|
+
|
|
210
|
+
if (options.json) {
|
|
211
|
+
console.log(JSON.stringify({ question, results: ranked }, null, 2));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
console.log(
|
|
215
|
+
`${entries.length} stored feedback vote(s); retrieval only (no llm).`,
|
|
216
|
+
);
|
|
217
|
+
if (ranked.length === 0) {
|
|
218
|
+
console.log("No matching passages found.");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
ranked.forEach((r, i) => {
|
|
222
|
+
console.log(
|
|
223
|
+
`\n[${i + 1}] ${r.id} score ${r.finalScore.toFixed(3)} = ` +
|
|
224
|
+
`${r.baseScore.toFixed(3)} retrieval ${signed(r.adjustment)} feedback`,
|
|
225
|
+
);
|
|
226
|
+
for (const reason of r.reasons) {
|
|
227
|
+
console.log(
|
|
228
|
+
` ${reason.rating} vote on "${reason.question}" ` +
|
|
229
|
+
`(term overlap ${reason.similarity.toFixed(2)}): ${signed(reason.contribution)}`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
console.log(r.content);
|
|
233
|
+
});
|
|
234
|
+
} catch (error) {
|
|
235
|
+
fail("Search failed", error);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const TEXT_TYPES = {
|
|
240
|
+
".md": "markdown",
|
|
241
|
+
".markdown": "markdown",
|
|
242
|
+
".html": "html",
|
|
243
|
+
".htm": "html",
|
|
244
|
+
".csv": "csv",
|
|
245
|
+
".txt": "text",
|
|
246
|
+
};
|
|
247
|
+
const IMAGE_EXTENSIONS = new Set([
|
|
248
|
+
".png",
|
|
249
|
+
".jpg",
|
|
250
|
+
".jpeg",
|
|
251
|
+
".gif",
|
|
252
|
+
".webp",
|
|
253
|
+
".bmp",
|
|
254
|
+
".tif",
|
|
255
|
+
".tiff",
|
|
256
|
+
]);
|
|
257
|
+
|
|
258
|
+
function configuredLlm(globalOptions) {
|
|
259
|
+
try {
|
|
260
|
+
const { config } = loadCliConfig(globalOptions);
|
|
261
|
+
const entry = (config.pipeline || []).find((e) => e.stage === "llm");
|
|
262
|
+
return entry ? entry.name : "none";
|
|
263
|
+
} catch {
|
|
264
|
+
return "none (no readable config)";
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function imageUnsupportedMessage(globalOptions) {
|
|
269
|
+
return (
|
|
270
|
+
"Image processing needs a vision-capable model, and none is available: the " +
|
|
271
|
+
"built-in llm connectors (openai, anthropic, ollama) only send text prompts. " +
|
|
272
|
+
`Configured llm: ${configuredLlm(globalOptions)}.`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async function describeTextFile(file, baseDir) {
|
|
277
|
+
const [doc] = await readDocuments(file, { baseDir });
|
|
278
|
+
return {
|
|
279
|
+
source: doc.source,
|
|
280
|
+
type: TEXT_TYPES[path.extname(file).toLowerCase()],
|
|
281
|
+
characters: fs.readFileSync(file, "utf8").length,
|
|
282
|
+
extractedCharacters: doc.chunks.reduce((n, c) => n + c.length, 0),
|
|
283
|
+
chunks: doc.chunks.length,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* `ai multimodal process <path>`: report what the loaders extract. Does not
|
|
289
|
+
* modify the index.
|
|
290
|
+
*/
|
|
291
|
+
async function handleMultimodalProcess(globalOptions, inputPath, options = {}) {
|
|
292
|
+
try {
|
|
293
|
+
const absolute = path.resolve(inputPath);
|
|
294
|
+
if (!fs.existsSync(absolute))
|
|
295
|
+
throw new Error(`Path not found: ${inputPath}`);
|
|
296
|
+
|
|
297
|
+
const files = fs.statSync(absolute).isDirectory()
|
|
298
|
+
? (
|
|
299
|
+
await fg("**/*", {
|
|
300
|
+
cwd: absolute,
|
|
301
|
+
absolute: true,
|
|
302
|
+
ignore: ["**/node_modules/**", "**/.git/**"],
|
|
303
|
+
})
|
|
304
|
+
).sort()
|
|
305
|
+
: [absolute];
|
|
306
|
+
|
|
307
|
+
const processed = [];
|
|
308
|
+
const skipped = [];
|
|
309
|
+
for (const file of files) {
|
|
310
|
+
const ext = path.extname(file).toLowerCase();
|
|
311
|
+
const rel = path.relative(process.cwd(), file).split(path.sep).join("/");
|
|
312
|
+
if (TEXT_TYPES[ext]) {
|
|
313
|
+
processed.push(await describeTextFile(file, process.cwd()));
|
|
314
|
+
} else if (IMAGE_EXTENSIONS.has(ext)) {
|
|
315
|
+
if (files.length === 1)
|
|
316
|
+
throw new Error(imageUnsupportedMessage(globalOptions));
|
|
317
|
+
skipped.push({
|
|
318
|
+
source: rel,
|
|
319
|
+
reason: "image (needs a vision-capable model)",
|
|
320
|
+
});
|
|
321
|
+
} else {
|
|
322
|
+
if (files.length === 1) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
`Unsupported file type "${ext}". Supported text types: ${Object.keys(TEXT_TYPES).join(", ")}`,
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
skipped.push({ source: rel, reason: `unsupported type "${ext}"` });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (options.json) {
|
|
332
|
+
console.log(JSON.stringify({ processed, skipped }, null, 2));
|
|
333
|
+
} else {
|
|
334
|
+
for (const p of processed) {
|
|
335
|
+
console.log(
|
|
336
|
+
`${p.source}: ${p.type}, ${p.characters} characters, ` +
|
|
337
|
+
`${p.chunks} chunk(s) (${p.extractedCharacters} characters extracted)`,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
for (const s of skipped) console.log(`skipped ${s.source}: ${s.reason}`);
|
|
341
|
+
console.log(
|
|
342
|
+
`Processed ${processed.length} file(s), skipped ${skipped.length}. ` +
|
|
343
|
+
"The index was not modified; use `rag-pipeline ingest` to add files.",
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
if (processed.length === 0) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
skipped.some((s) => s.reason.startsWith("image"))
|
|
349
|
+
? imageUnsupportedMessage(globalOptions)
|
|
350
|
+
: "No supported text files found.",
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
} catch (error) {
|
|
354
|
+
fail("Processing failed", error);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
module.exports = {
|
|
359
|
+
handleTrainEmbedding,
|
|
360
|
+
handleBenchmark,
|
|
361
|
+
handleFeedback,
|
|
362
|
+
handleSearch,
|
|
363
|
+
handleMultimodalProcess,
|
|
364
|
+
};
|
|
@@ -6,66 +6,82 @@
|
|
|
6
6
|
const fs = require("fs/promises");
|
|
7
7
|
const { logger } = require("../../utils/logger.js");
|
|
8
8
|
|
|
9
|
+
const SEVERITY_ICONS = { error: "❌", warning: "⚠️ ", info: "ℹ️ " };
|
|
10
|
+
|
|
11
|
+
function printIssues(issues) {
|
|
12
|
+
for (const issue of issues) {
|
|
13
|
+
console.log(
|
|
14
|
+
`${SEVERITY_ICONS[issue.severity] || "-"} [${issue.code}] ${issue.message}`,
|
|
15
|
+
);
|
|
16
|
+
if (issue.fix) console.log(` Fix: ${issue.fix}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
/**
|
|
10
|
-
* Handle doctor command
|
|
21
|
+
* Handle doctor command: run the diagnostic checkers and print the issues.
|
|
22
|
+
* Exits 1 when any error-level issue is found.
|
|
23
|
+
*
|
|
11
24
|
* @param {object} globalOptions - Global CLI options from program.opts()
|
|
12
25
|
* @param {object} options - Command-specific options
|
|
13
|
-
* @param {Function} pipelineDoctorFn -
|
|
26
|
+
* @param {Function} [pipelineDoctorFn] - Injectable for tests
|
|
14
27
|
*/
|
|
15
28
|
async function handleDoctor(globalOptions, options, pipelineDoctorFn) {
|
|
16
29
|
try {
|
|
17
|
-
const
|
|
18
|
-
|
|
30
|
+
const runDoctor =
|
|
31
|
+
pipelineDoctorFn || require("../doctor-command.js").runPipelineDoctor;
|
|
32
|
+
const report = await runDoctor({
|
|
33
|
+
configPath: globalOptions.config || ".ragrc.json",
|
|
19
34
|
verbose: globalOptions.verbose,
|
|
20
35
|
autoFix: options.autoFix,
|
|
21
|
-
categories: options.category,
|
|
22
|
-
};
|
|
36
|
+
categories: options.category || ["all"],
|
|
37
|
+
});
|
|
23
38
|
|
|
24
|
-
|
|
39
|
+
console.log("🏥 Pipeline diagnostics\n");
|
|
40
|
+
if (report.issues.length === 0) {
|
|
41
|
+
console.log("✅ No issues found");
|
|
42
|
+
} else {
|
|
43
|
+
printIssues(report.issues);
|
|
44
|
+
}
|
|
45
|
+
const { errors, warnings, info } = report.summary;
|
|
46
|
+
console.log(`\n${errors} error(s), ${warnings} warning(s), ${info} info`);
|
|
25
47
|
|
|
26
48
|
if (options.report) {
|
|
27
49
|
await fs.writeFile(options.report, JSON.stringify(report, null, 2));
|
|
28
50
|
console.log(`📋 Diagnostic report saved to: ${options.report}`);
|
|
29
51
|
}
|
|
52
|
+
if (errors > 0) process.exitCode = 1;
|
|
30
53
|
} catch (error) {
|
|
31
|
-
|
|
32
|
-
process.
|
|
54
|
+
console.error(`❌ Doctor command failed: ${error.message}`);
|
|
55
|
+
process.exitCode = 1;
|
|
33
56
|
}
|
|
34
57
|
}
|
|
35
58
|
|
|
36
59
|
/**
|
|
37
|
-
* Handle validate command
|
|
60
|
+
* Handle validate command: schema validation plus resolving every pipeline
|
|
61
|
+
* component. Exits 1 when the configuration has errors.
|
|
62
|
+
*
|
|
38
63
|
* @param {object} globalOptions - Global CLI options from program.opts()
|
|
39
|
-
* @param {object} options - Command-specific options
|
|
40
|
-
* @param {Function} validateSchemaFn - Schema validation function
|
|
41
64
|
*/
|
|
42
|
-
async function handleValidate(globalOptions
|
|
65
|
+
async function handleValidate(globalOptions) {
|
|
43
66
|
try {
|
|
44
|
-
const configPath = globalOptions.config;
|
|
45
|
-
|
|
67
|
+
const configPath = globalOptions.config || ".ragrc.json";
|
|
46
68
|
console.log(`🔍 Validating configuration: ${configPath}`);
|
|
47
69
|
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
const
|
|
70
|
+
const { checkConfiguration } = require("../diagnostics/config-checker.js");
|
|
71
|
+
const issues = await checkConfiguration({ configPath });
|
|
72
|
+
const errors = issues.filter((issue) => issue.severity === "error");
|
|
51
73
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (validation.valid) {
|
|
74
|
+
if (errors.length === 0) {
|
|
55
75
|
console.log("✅ Configuration is valid");
|
|
56
|
-
|
|
57
|
-
if (validation.legacy) {
|
|
58
|
-
console.log("⚠️ Using legacy format - consider upgrading");
|
|
59
|
-
}
|
|
76
|
+
printIssues(issues);
|
|
60
77
|
} else {
|
|
61
78
|
console.log("❌ Configuration validation failed:");
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
79
|
+
printIssues(issues);
|
|
80
|
+
process.exitCode = 1;
|
|
65
81
|
}
|
|
66
82
|
} catch (error) {
|
|
67
|
-
|
|
68
|
-
process.
|
|
83
|
+
console.error(`❌ Validation failed: ${error.message}`);
|
|
84
|
+
process.exitCode = 1;
|
|
69
85
|
}
|
|
70
86
|
}
|
|
71
87
|
|
|
@@ -97,13 +113,44 @@ async function handleInfo(globalOptions, options) {
|
|
|
97
113
|
);
|
|
98
114
|
console.log("Configuration:");
|
|
99
115
|
console.log(` File: ${globalOptions.config}`);
|
|
100
|
-
|
|
116
|
+
for (const entry of Array.isArray(config.pipeline)
|
|
117
|
+
? config.pipeline
|
|
118
|
+
: []) {
|
|
119
|
+
console.log(` ${entry.stage}: ${entry.name}`);
|
|
120
|
+
}
|
|
101
121
|
console.log("");
|
|
102
122
|
} catch (error) {
|
|
103
123
|
console.log("Configuration: Not found or invalid");
|
|
104
124
|
console.log("");
|
|
105
125
|
}
|
|
106
126
|
}
|
|
127
|
+
|
|
128
|
+
// Available components: built-ins plus custom plugins from the config
|
|
129
|
+
if ((!options.system && !options.config) || options.plugins) {
|
|
130
|
+
const { BUILT_INS } = require("../../config/component-factory.js");
|
|
131
|
+
console.log("Built-in components:");
|
|
132
|
+
for (const [stage, names] of Object.entries(BUILT_INS)) {
|
|
133
|
+
console.log(` ${stage}: ${Object.keys(names).join(", ")}`);
|
|
134
|
+
}
|
|
135
|
+
let custom = {};
|
|
136
|
+
try {
|
|
137
|
+
custom =
|
|
138
|
+
JSON.parse(await fs.readFile(globalOptions.config, "utf-8"))
|
|
139
|
+
.plugins || {};
|
|
140
|
+
} catch {
|
|
141
|
+
// no or invalid config: built-ins only
|
|
142
|
+
}
|
|
143
|
+
const entries = Object.entries(custom).flatMap(([stage, plugins]) =>
|
|
144
|
+
Object.entries(plugins || {}).map(
|
|
145
|
+
([name, modulePath]) => ` ${stage}/${name}: ${modulePath}`,
|
|
146
|
+
),
|
|
147
|
+
);
|
|
148
|
+
if (entries.length > 0) {
|
|
149
|
+
console.log("Custom plugins:");
|
|
150
|
+
entries.forEach((line) => console.log(line));
|
|
151
|
+
}
|
|
152
|
+
console.log("");
|
|
153
|
+
}
|
|
107
154
|
} catch (error) {
|
|
108
155
|
logger.error("❌ Failed to get system information:", error.message);
|
|
109
156
|
process.exit(1);
|
|
@@ -1,41 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ingest command handler
|
|
3
|
-
*
|
|
3
|
+
* Loads documents, embeds their chunks and stores them in the configured
|
|
4
|
+
* retriever (file-store persists them for later `query` runs).
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
const {
|
|
7
|
+
const { loadCliConfig } = require("../shared/config-context.js");
|
|
8
|
+
const { ingest } = require("../services/rag-service.js");
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
|
-
* Handle ingest command
|
|
10
11
|
* @param {object} globalOptions - Global CLI options from program.opts()
|
|
11
|
-
* @param {string}
|
|
12
|
+
* @param {string} inputPath - File or directory to ingest
|
|
12
13
|
* @param {object} options - Command-specific options
|
|
13
14
|
*/
|
|
14
|
-
async function handleIngest(globalOptions,
|
|
15
|
+
async function handleIngest(globalOptions, inputPath, options = {}) {
|
|
15
16
|
try {
|
|
16
|
-
|
|
17
|
-
console.log("🧪 Dry run: Would ingest document");
|
|
18
|
-
console.log(`File: ${file}`);
|
|
19
|
-
console.log(
|
|
20
|
-
`Parallel processing: ${options.parallel ? "enabled" : "disabled"}`,
|
|
21
|
-
);
|
|
22
|
-
console.log(`Streaming: ${options.streaming ? "enabled" : "disabled"}`);
|
|
23
|
-
console.log(`Tracing: ${options.trace ? "enabled" : "disabled"}`);
|
|
17
|
+
const { config, configDir } = loadCliConfig(globalOptions);
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
if (globalOptions.dryRun) {
|
|
20
|
+
const { readDocuments } = require("../../loader/document-reader.js");
|
|
21
|
+
const documents = await readDocuments(inputPath, { baseDir: configDir });
|
|
22
|
+
console.log(`🧪 Dry run: would ingest ${documents.length} file(s)`);
|
|
23
|
+
for (const doc of documents) {
|
|
24
|
+
console.log(` ${doc.source} (${doc.chunks.length} chunks)`);
|
|
30
25
|
}
|
|
31
26
|
return;
|
|
32
27
|
}
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
const summary = await ingest({
|
|
30
|
+
config,
|
|
31
|
+
configDir,
|
|
32
|
+
inputPath,
|
|
33
|
+
rebuild: Boolean(options.rebuild),
|
|
34
|
+
});
|
|
35
|
+
console.log(
|
|
36
|
+
`✅ Ingested ${summary.files} file(s), ${summary.chunks} chunk(s). ` +
|
|
37
|
+
`Index now holds ${summary.totalChunks} chunk(s).`,
|
|
38
|
+
);
|
|
39
|
+
if (globalOptions.verbose) {
|
|
40
|
+
for (const source of summary.sources) console.log(` ${source}`);
|
|
41
|
+
}
|
|
36
42
|
} catch (error) {
|
|
37
|
-
|
|
38
|
-
process.
|
|
43
|
+
console.error(`❌ Ingestion failed: ${error.message}`);
|
|
44
|
+
process.exitCode = 1;
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
|