@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
|
@@ -1,51 +1,174 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Init command handler
|
|
3
|
-
*
|
|
3
|
+
* Writes a .ragrc.json whose pipeline names built-in components (or custom
|
|
4
|
+
* plugins checked by the setup wizard), so the result works with
|
|
5
|
+
* `ingest` / `query` immediately.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
const fs = require("fs/promises");
|
|
7
|
-
const {
|
|
9
|
+
const { buildInitConfig } = require("../wizard/config-builder.js");
|
|
10
|
+
const { runInitWizard, ask } = require("../wizard/init-wizard.js");
|
|
11
|
+
const {
|
|
12
|
+
buildComponents: defaultBuildComponents,
|
|
13
|
+
} = require("../../config/component-factory");
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Starting points for `init --template <name>`. "local" needs no API keys or
|
|
17
|
+
* services; it has no llm stage, so `query` prints the retrieved passages.
|
|
18
|
+
* In interactive mode the template only sets the wizard's defaults.
|
|
19
|
+
*/
|
|
20
|
+
const TEMPLATES = {
|
|
21
|
+
local: { embedder: "local", llm: null },
|
|
22
|
+
openai: { embedder: "openai", llm: "openai" },
|
|
23
|
+
ollama: { embedder: "ollama", llm: "ollama" },
|
|
24
|
+
anthropic: { embedder: "local", llm: "anthropic" },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* inquirer prompt function. With custom streams it uses a prompt module
|
|
29
|
+
* bound to them (inquirer@8 reads `{input, output}` from createPromptModule,
|
|
30
|
+
* not from prompt()'s second argument, which holds pre-filled answers).
|
|
31
|
+
*/
|
|
32
|
+
function inquirerPrompt({ input, output } = {}) {
|
|
33
|
+
const inquirer = require("inquirer");
|
|
34
|
+
if (!input && !output) return (questions) => inquirer.prompt(questions);
|
|
35
|
+
const promptModule = inquirer.createPromptModule({ input, output });
|
|
36
|
+
return (questions) => promptModule(questions);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Grace period before the CLI forces an exit after a connection test. */
|
|
40
|
+
const EXIT_GRACE_MS = 1000;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Last-resort exit for `rag-pipeline init` after a connection test ran.
|
|
44
|
+
*
|
|
45
|
+
* The test aborts each provider call on timeout (connectors that honour
|
|
46
|
+
* `options.signal` cancel their request), but a custom plugin or a provider
|
|
47
|
+
* SDK may still hold a socket or timer open, which would keep the process
|
|
48
|
+
* alive after "Configuration written". The exit timer is unref'd: when
|
|
49
|
+
* nothing is left running the process ends normally and the timer never
|
|
50
|
+
* fires; otherwise it exits with the current exit code after the grace
|
|
51
|
+
* period. Only the CLI command calls this, never the handler itself.
|
|
52
|
+
*
|
|
53
|
+
* @param {{connectionTested?: boolean}|undefined} result - handleInit result
|
|
54
|
+
* @param {object} [options]
|
|
55
|
+
* @param {number} [options.graceMs=1000]
|
|
56
|
+
* @param {Function} [options.exit=process.exit]
|
|
57
|
+
* @returns {NodeJS.Timeout|null} The scheduled timer, or null
|
|
58
|
+
*/
|
|
59
|
+
function exitAfterConnectionTest(result, options = {}) {
|
|
60
|
+
if (!result || !result.connectionTested) return null;
|
|
61
|
+
const { graceMs = EXIT_GRACE_MS, exit = (code) => process.exit(code) } =
|
|
62
|
+
options;
|
|
63
|
+
const timer = setTimeout(() => exit(process.exitCode ?? 0), graceMs);
|
|
64
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
65
|
+
return timer;
|
|
66
|
+
}
|
|
8
67
|
|
|
9
68
|
/**
|
|
10
69
|
* Handle init command
|
|
11
70
|
* @param {object} globalOptions - Global CLI options from program.opts()
|
|
12
71
|
* @param {object} options - Command-specific options
|
|
13
|
-
* @param {
|
|
72
|
+
* @param {object} [deps] - Injectable dependencies for tests
|
|
73
|
+
* @param {Function} [deps.prompt] - inquirer-style prompt([question]) => answers
|
|
74
|
+
* @param {object} [deps.env] - Environment for the API key check
|
|
75
|
+
* @param {Function} [deps.buildComponents] - Used by the connection test
|
|
76
|
+
* @param {Function} [deps.log] - Wizard output (console.log)
|
|
77
|
+
* @param {number} [deps.timeoutMs] - Connection test timeout
|
|
78
|
+
* @param {import("stream").Readable} [deps.input] - Prompt input (process.stdin)
|
|
79
|
+
* @param {import("stream").Writable} [deps.output] - Prompt output (process.stdout)
|
|
80
|
+
* @returns {Promise<{written: boolean, connectionTested: boolean}>}
|
|
81
|
+
* connectionTested is true when the connection test built a provider
|
|
14
82
|
*/
|
|
15
|
-
async function handleInit(globalOptions, options,
|
|
83
|
+
async function handleInit(globalOptions, options, deps = {}) {
|
|
84
|
+
const outcome = { written: false, connectionTested: false };
|
|
16
85
|
try {
|
|
86
|
+
const templateName = options.template || "local";
|
|
87
|
+
const template = TEMPLATES[templateName];
|
|
88
|
+
if (!template) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Unknown template "${templateName}". Available: ${Object.keys(TEMPLATES).join(", ")}`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// commander sets options.interactive=false for --no-interactive; only
|
|
95
|
+
// prompt when a person is at the terminal.
|
|
96
|
+
const interactive =
|
|
97
|
+
options.interactive !== false &&
|
|
98
|
+
Boolean((deps.input || process.stdin).isTTY);
|
|
99
|
+
|
|
17
100
|
if (globalOptions.dryRun) {
|
|
18
|
-
console.log("🧪 Dry run:
|
|
101
|
+
console.log("🧪 Dry run: would write configuration");
|
|
19
102
|
console.log(`Output file: ${options.output}`);
|
|
20
|
-
console.log(`
|
|
21
|
-
|
|
103
|
+
console.log(`Template: ${templateName}`);
|
|
104
|
+
console.log(`Interactive: ${interactive}`);
|
|
105
|
+
return outcome;
|
|
22
106
|
}
|
|
23
107
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
108
|
+
const prompt =
|
|
109
|
+
deps.prompt || inquirerPrompt({ input: deps.input, output: deps.output });
|
|
110
|
+
const exists = await fs
|
|
111
|
+
.access(options.output)
|
|
112
|
+
.then(() => true)
|
|
113
|
+
.catch(() => false);
|
|
114
|
+
if (exists && !options.force) {
|
|
115
|
+
if (!interactive) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Configuration file already exists: ${options.output}. Use --force to overwrite.`,
|
|
31
118
|
);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
119
|
+
}
|
|
120
|
+
const overwrite = await ask(prompt, {
|
|
121
|
+
type: "confirm",
|
|
122
|
+
name: "overwrite",
|
|
123
|
+
message: `${options.output} already exists. Replace it?`,
|
|
124
|
+
default: false,
|
|
125
|
+
});
|
|
126
|
+
if (!overwrite) {
|
|
127
|
+
console.log("Nothing written.");
|
|
128
|
+
return outcome;
|
|
35
129
|
}
|
|
36
130
|
}
|
|
37
131
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
132
|
+
let config;
|
|
133
|
+
if (interactive) {
|
|
134
|
+
const result = await runInitWizard({
|
|
135
|
+
prompt,
|
|
136
|
+
defaults: template,
|
|
137
|
+
outputPath: options.output,
|
|
138
|
+
overwrite: exists,
|
|
139
|
+
env: deps.env || process.env,
|
|
140
|
+
// Only the connection test builds components; note that it did, so
|
|
141
|
+
// the CLI can make sure no provider request keeps the process alive.
|
|
142
|
+
buildComponents: (...args) => {
|
|
143
|
+
outcome.connectionTested = true;
|
|
144
|
+
return (deps.buildComponents || defaultBuildComponents)(...args);
|
|
145
|
+
},
|
|
146
|
+
log: deps.log || console.log,
|
|
147
|
+
timeoutMs: deps.timeoutMs,
|
|
148
|
+
});
|
|
149
|
+
if (!result) {
|
|
150
|
+
console.log("Nothing written.");
|
|
151
|
+
return outcome;
|
|
152
|
+
}
|
|
153
|
+
config = result.config;
|
|
42
154
|
} else {
|
|
43
|
-
|
|
155
|
+
config = buildInitConfig(template);
|
|
44
156
|
}
|
|
157
|
+
|
|
158
|
+
await fs.writeFile(options.output, JSON.stringify(config, null, 2) + "\n");
|
|
159
|
+
console.log(`✅ Configuration written: ${options.output}`);
|
|
160
|
+
console.log("Next: rag-pipeline ingest <file-or-directory>");
|
|
161
|
+
outcome.written = true;
|
|
45
162
|
} catch (error) {
|
|
46
|
-
|
|
47
|
-
process.
|
|
163
|
+
console.error(`❌ Initialization failed: ${error.message}`);
|
|
164
|
+
process.exitCode = 1;
|
|
48
165
|
}
|
|
166
|
+
return outcome;
|
|
49
167
|
}
|
|
50
168
|
|
|
51
|
-
module.exports = {
|
|
169
|
+
module.exports = {
|
|
170
|
+
handleInit,
|
|
171
|
+
exitAfterConnectionTest,
|
|
172
|
+
buildInitConfig,
|
|
173
|
+
TEMPLATES,
|
|
174
|
+
};
|
|
@@ -1,44 +1,59 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Query command handler
|
|
3
|
-
*
|
|
3
|
+
* Answers a question from the ingested index. Without an llm stage it prints
|
|
4
|
+
* the retrieved passages instead of an answer.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
const {
|
|
7
|
+
const { loadCliConfig } = require("../shared/config-context.js");
|
|
8
|
+
const { query } = require("../services/rag-service.js");
|
|
9
|
+
|
|
10
|
+
function printPassages(results) {
|
|
11
|
+
// A score of 0 means no overlap with the question at all.
|
|
12
|
+
const matches = results.filter((doc) => !(doc.score <= 0));
|
|
13
|
+
if (matches.length === 0) {
|
|
14
|
+
console.log("No matching passages found.");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
matches.forEach((doc, i) => {
|
|
18
|
+
const source = doc.metadata?.source || doc.id;
|
|
19
|
+
const score = typeof doc.score === "number" ? doc.score.toFixed(3) : "-";
|
|
20
|
+
console.log(`\n[${i + 1}] ${source} (score ${score})`);
|
|
21
|
+
console.log(doc.content);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
7
24
|
|
|
8
25
|
/**
|
|
9
|
-
* Handle query command
|
|
10
26
|
* @param {object} globalOptions - Global CLI options from program.opts()
|
|
11
|
-
* @param {string}
|
|
27
|
+
* @param {string} question - Query text
|
|
12
28
|
* @param {object} options - Command-specific options
|
|
13
29
|
*/
|
|
14
|
-
async function handleQuery(globalOptions,
|
|
30
|
+
async function handleQuery(globalOptions, question, options = {}) {
|
|
15
31
|
try {
|
|
32
|
+
const { config, configDir } = loadCliConfig(globalOptions);
|
|
33
|
+
const topK = options.topK || 3;
|
|
34
|
+
|
|
16
35
|
if (globalOptions.dryRun) {
|
|
17
|
-
console.log(
|
|
18
|
-
console.log(
|
|
19
|
-
`Prompt: ${prompt.substring(0, 100)}${prompt.length > 100 ? "..." : ""}`,
|
|
20
|
-
);
|
|
21
|
-
console.log(`Streaming: ${options.stream ? "enabled" : "disabled"}`);
|
|
22
|
-
|
|
23
|
-
if (options.explain) {
|
|
24
|
-
console.log("🔍 Query Explanation:");
|
|
25
|
-
console.log("Processing steps:");
|
|
26
|
-
console.log(" 1. Text embedding generation");
|
|
27
|
-
console.log(" 2. Vector similarity search");
|
|
28
|
-
console.log(" 3. Context retrieval");
|
|
29
|
-
console.log(" 4. Language model generation");
|
|
30
|
-
}
|
|
36
|
+
console.log(`🧪 Dry run: would query the index for: ${question}`);
|
|
31
37
|
return;
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
const result = await query({ config, configDir, question, topK });
|
|
41
|
+
|
|
42
|
+
if (options.json) {
|
|
43
|
+
console.log(JSON.stringify(result, null, 2));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (result.answer === null) {
|
|
48
|
+
console.log("No llm stage configured; showing retrieved passages.");
|
|
49
|
+
} else {
|
|
50
|
+
console.log(result.answer);
|
|
51
|
+
console.log("\nSources:");
|
|
52
|
+
}
|
|
53
|
+
printPassages(result.results);
|
|
39
54
|
} catch (error) {
|
|
40
|
-
|
|
41
|
-
process.
|
|
55
|
+
console.error(`❌ Query failed: ${error.message}`);
|
|
56
|
+
process.exitCode = 1;
|
|
42
57
|
}
|
|
43
58
|
}
|
|
44
59
|
|
|
@@ -1,180 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Run command handler
|
|
3
|
-
*
|
|
3
|
+
* Convenience wrapper: optionally ingest, then optionally query, in one call.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const {
|
|
7
|
-
const {
|
|
6
|
+
const { handleIngest } = require("./ingest-handler.js");
|
|
7
|
+
const { handleQuery } = require("./query-handler.js");
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Handle run command
|
|
11
10
|
* @param {object} globalOptions - Global CLI options from program.opts()
|
|
12
|
-
* @param {object} options -
|
|
11
|
+
* @param {object} options - { ingest?: string, query?: string, topK?: number, json?: boolean }
|
|
13
12
|
*/
|
|
14
|
-
async function handleRun(globalOptions, options) {
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
logger.info(`Streaming: ${options.stream ? "enabled" : "disabled"}`);
|
|
21
|
-
logger.info(`Parallel: ${options.parallel ? "enabled" : "disabled"}`);
|
|
22
|
-
if (options.abortTimeout)
|
|
23
|
-
logger.info(`Abort timeout: ${options.abortTimeout}ms`);
|
|
13
|
+
async function handleRun(globalOptions, options = {}) {
|
|
14
|
+
if (!options.ingest && !options.query) {
|
|
15
|
+
console.error(
|
|
16
|
+
"❌ Nothing to do: pass --ingest <path> and/or --query <text>",
|
|
17
|
+
);
|
|
18
|
+
process.exitCode = 1;
|
|
24
19
|
return;
|
|
25
20
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// Create AbortController for timeout handling
|
|
33
|
-
const abortController = new AbortController();
|
|
34
|
-
let timeoutId;
|
|
35
|
-
|
|
36
|
-
if (options.abortTimeout) {
|
|
37
|
-
timeoutId = setTimeout(() => {
|
|
38
|
-
logger.warn(
|
|
39
|
-
`Operation aborted after ${options.abortTimeout}ms timeout`,
|
|
40
|
-
);
|
|
41
|
-
abortController.abort();
|
|
42
|
-
}, options.abortTimeout);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Setup SIGINT handling for clean shutdown
|
|
46
|
-
const sigintHandler = () => {
|
|
47
|
-
logger.info("Received SIGINT, aborting operation...");
|
|
48
|
-
abortController.abort();
|
|
49
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
50
|
-
};
|
|
51
|
-
process.once("SIGINT", sigintHandler);
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
// Create pipeline with performance options
|
|
55
|
-
const { createRagPipeline } = require("../../core/create-pipeline.js");
|
|
56
|
-
const pipeline = createRagPipeline(config.pipeline[0], {
|
|
57
|
-
useParallelProcessing: options.parallel,
|
|
58
|
-
useStreamingSafeguards: options.stream,
|
|
59
|
-
useLogging: true,
|
|
60
|
-
useRetry: true,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// Execute operation based on options
|
|
64
|
-
if (options.ingest) {
|
|
65
|
-
logger.info(`Starting ingestion of: ${options.ingest}`);
|
|
66
|
-
|
|
67
|
-
if (options.stream) {
|
|
68
|
-
let lastProgressTime = Date.now();
|
|
69
|
-
for await (const progress of pipeline.ingestStream(options.ingest)) {
|
|
70
|
-
if (abortController.signal.aborted) {
|
|
71
|
-
logger.info("Ingestion aborted");
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const now = Date.now();
|
|
76
|
-
if (progress.type === "chunk_processed") {
|
|
77
|
-
// Show progress ticks every 500ms or every 10 chunks
|
|
78
|
-
if (
|
|
79
|
-
now - lastProgressTime > 500 ||
|
|
80
|
-
progress.progress.processed % 10 === 0
|
|
81
|
-
) {
|
|
82
|
-
const percentage = Math.round(
|
|
83
|
-
(progress.progress.processed / progress.progress.total) * 100,
|
|
84
|
-
);
|
|
85
|
-
const progressBar = createProgressBar(percentage, 20);
|
|
86
|
-
process.stdout.write(
|
|
87
|
-
`\r${progressBar} ${percentage}% (${progress.progress.processed}/${progress.progress.total}) `,
|
|
88
|
-
);
|
|
89
|
-
lastProgressTime = now;
|
|
90
|
-
}
|
|
91
|
-
} else if (progress.type === "ingest_complete") {
|
|
92
|
-
process.stdout.write("\n");
|
|
93
|
-
logger.info(
|
|
94
|
-
`✅ Ingestion complete: ${progress.summary.processedChunks}/${progress.summary.totalChunks} chunks processed`,
|
|
95
|
-
);
|
|
96
|
-
if (progress.summary.failedChunks > 0) {
|
|
97
|
-
logger.warn(
|
|
98
|
-
`⚠️ ${progress.summary.failedChunks} chunks failed`,
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
} else if (progress.type === "chunk_failed") {
|
|
102
|
-
process.stdout.write("✗");
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
} else {
|
|
106
|
-
// Non-streaming ingestion with progress callback
|
|
107
|
-
let currentStage = "";
|
|
108
|
-
const progressCallback = (progress) => {
|
|
109
|
-
if (progress.stage !== currentStage) {
|
|
110
|
-
if (currentStage) process.stdout.write("\n");
|
|
111
|
-
currentStage = progress.stage;
|
|
112
|
-
logger.info(
|
|
113
|
-
`${progress.stage}: ${progress.message || "Processing..."}`,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
if (
|
|
117
|
-
progress.completed !== undefined &&
|
|
118
|
-
progress.total !== undefined
|
|
119
|
-
) {
|
|
120
|
-
const percentage = Math.round(
|
|
121
|
-
(progress.completed / progress.total) * 100,
|
|
122
|
-
);
|
|
123
|
-
const progressBar = createProgressBar(percentage, 20);
|
|
124
|
-
process.stdout.write(
|
|
125
|
-
`\r${progressBar} ${percentage}% (${progress.completed}/${progress.total}) `,
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const pipelineWithProgress = createRagPipeline(config.pipeline[0], {
|
|
131
|
-
useParallelProcessing: options.parallel,
|
|
132
|
-
useStreamingSafeguards: false,
|
|
133
|
-
useLogging: true,
|
|
134
|
-
useRetry: true,
|
|
135
|
-
onProgress: progressCallback,
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
await pipelineWithProgress.ingest(options.ingest);
|
|
139
|
-
process.stdout.write("\n");
|
|
140
|
-
logger.info("✅ Ingestion completed successfully");
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (options.query) {
|
|
145
|
-
logger.info(`Executing query: ${options.query.substring(0, 100)}...`);
|
|
146
|
-
|
|
147
|
-
if (options.stream) {
|
|
148
|
-
process.stdout.write("\nResponse: ");
|
|
149
|
-
for await (const token of pipeline.queryStream(options.query)) {
|
|
150
|
-
if (abortController.signal.aborted) {
|
|
151
|
-
logger.info("\nQuery aborted");
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
process.stdout.write(token);
|
|
155
|
-
}
|
|
156
|
-
process.stdout.write("\n\n");
|
|
157
|
-
} else {
|
|
158
|
-
const response = await pipeline.query(options.query);
|
|
159
|
-
logger.info("Query completed successfully");
|
|
160
|
-
console.log("\nResponse:", response);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (!options.ingest && !options.query) {
|
|
165
|
-
logger.error(
|
|
166
|
-
"No operation specified. Use --ingest <file> or --query <prompt>",
|
|
167
|
-
);
|
|
168
|
-
process.exit(1);
|
|
169
|
-
}
|
|
170
|
-
} finally {
|
|
171
|
-
// Cleanup
|
|
172
|
-
process.removeListener("SIGINT", sigintHandler);
|
|
173
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
174
|
-
}
|
|
175
|
-
} catch (error) {
|
|
176
|
-
logger.error("Pipeline operation failed", { error: error.message });
|
|
177
|
-
process.exit(1);
|
|
21
|
+
if (options.ingest) {
|
|
22
|
+
await handleIngest(globalOptions, options.ingest, options);
|
|
23
|
+
if (process.exitCode) return;
|
|
24
|
+
}
|
|
25
|
+
if (options.query) {
|
|
26
|
+
await handleQuery(globalOptions, options.query, options);
|
|
178
27
|
}
|
|
179
28
|
}
|
|
180
29
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serve command handler
|
|
3
|
+
* Runs DevServer: a local HTTP API (GET /health, POST /query) over the
|
|
4
|
+
* pipeline in .ragrc.json, reloading custom plugin modules on change.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { loadCliConfig } = require("../shared/config-context.js");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {object} globalOptions - Global CLI options from program.opts()
|
|
12
|
+
* @param {object} options - { port, host }
|
|
13
|
+
* @returns {Promise<object|undefined>} The started server (for tests)
|
|
14
|
+
*/
|
|
15
|
+
async function handleServe(globalOptions, options = {}) {
|
|
16
|
+
try {
|
|
17
|
+
// Validate the config up front so a broken file fails at startup.
|
|
18
|
+
const { config, configPath, configDir } = loadCliConfig(globalOptions);
|
|
19
|
+
const pluginFiles = Object.values(config.plugins || {})
|
|
20
|
+
.flatMap((group) => Object.values(group || {}))
|
|
21
|
+
.filter((p) => typeof p === "string")
|
|
22
|
+
.map((p) => path.resolve(configDir, p));
|
|
23
|
+
|
|
24
|
+
const { DevServer } = require("../../dev/dev-server.js");
|
|
25
|
+
const server = new DevServer({
|
|
26
|
+
port: options.port,
|
|
27
|
+
host: options.host,
|
|
28
|
+
configPath,
|
|
29
|
+
watchPaths: pluginFiles,
|
|
30
|
+
verbose: false,
|
|
31
|
+
});
|
|
32
|
+
await server.start();
|
|
33
|
+
|
|
34
|
+
console.log(`🚀 Serving ${path.basename(configPath)} at ${server.url}`);
|
|
35
|
+
console.log(` GET ${server.url}/health`);
|
|
36
|
+
console.log(` POST ${server.url}/query {"question": "...", "topK": 3}`);
|
|
37
|
+
if (
|
|
38
|
+
!["127.0.0.1", "localhost", "::1"].includes(options.host || "127.0.0.1")
|
|
39
|
+
) {
|
|
40
|
+
console.warn(
|
|
41
|
+
`⚠️ Listening on ${options.host}: the API has no authentication and anyone who can reach this address can query your index.`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
console.log("Press Ctrl+C to stop.");
|
|
45
|
+
|
|
46
|
+
const shutdown = async () => {
|
|
47
|
+
await server.stop();
|
|
48
|
+
process.exit(0);
|
|
49
|
+
};
|
|
50
|
+
process.once("SIGINT", shutdown);
|
|
51
|
+
process.once("SIGTERM", shutdown);
|
|
52
|
+
return server;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(`❌ Server failed to start: ${error.message}`);
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = { handleServe };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Summary statistics for latency samples measured by `rag-pipeline ai
|
|
5
|
+
* benchmark`. Pure functions so the math can be unit-tested.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Nearest-rank percentile: the smallest sample such that at least p% of the
|
|
10
|
+
* samples are less than or equal to it. Always returns an observed value.
|
|
11
|
+
*
|
|
12
|
+
* @param {number[]} samples - Unsorted samples (not modified)
|
|
13
|
+
* @param {number} p - Percentile in (0, 100]
|
|
14
|
+
* @returns {number}
|
|
15
|
+
*/
|
|
16
|
+
function percentile(samples, p) {
|
|
17
|
+
if (!Array.isArray(samples) || samples.length === 0) {
|
|
18
|
+
throw new Error("percentile() needs at least one sample");
|
|
19
|
+
}
|
|
20
|
+
if (!(p > 0 && p <= 100)) {
|
|
21
|
+
throw new Error(`Percentile must be in (0, 100], got ${p}`);
|
|
22
|
+
}
|
|
23
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
24
|
+
const rank = Math.ceil((p / 100) * sorted.length);
|
|
25
|
+
return sorted[rank - 1];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {number[]} samples
|
|
30
|
+
* @returns {number} Arithmetic mean
|
|
31
|
+
*/
|
|
32
|
+
function mean(samples) {
|
|
33
|
+
if (!Array.isArray(samples) || samples.length === 0) {
|
|
34
|
+
throw new Error("mean() needs at least one sample");
|
|
35
|
+
}
|
|
36
|
+
return samples.reduce((sum, x) => sum + x, 0) / samples.length;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {number[]} samples - Latencies in milliseconds
|
|
41
|
+
* @returns {{count: number, mean: number, p50: number, p95: number, min: number, max: number}}
|
|
42
|
+
*/
|
|
43
|
+
function summarize(samples) {
|
|
44
|
+
return {
|
|
45
|
+
count: samples.length,
|
|
46
|
+
mean: mean(samples),
|
|
47
|
+
p50: percentile(samples, 50),
|
|
48
|
+
p95: percentile(samples, 95),
|
|
49
|
+
min: Math.min(...samples),
|
|
50
|
+
max: Math.max(...samples),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { percentile, mean, summarize };
|