RAG Pipeline Utils v2.5.0: A Working CLI, Cancellation and Honest Metrics
v2.5.0 is a consolidation release. The CLI now does real work end to end, every pipeline stage can be cancelled, grounding metrics measure what they claim to, and modules that were simulations are now clearly separated in an /experimental entry point. Several of these changes alter scores and defaults, so please read the upgrade notes before you update.
A CLI that does real work
rag-pipeline init, ingest, query and run now work against a persistent local index:
npx rag-pipeline init --template local # or openai | ollama | anthropic
npx rag-pipeline ingest ./docs
npx rag-pipeline query "What is the vacation policy?"
initwrites a.ragrc.jsonof built-in components. In a terminal it runs a setup wizard: it chooses the embedder, LLM, retriever and an optional reranker, checks custom plugin modules before accepting them, reports missing API key environment variables, can run a connection test, and previews the JSON before writing it.- The index is a
FileVectorStore, a JSON-file vector index with atomic writes, embedder/dimension tracking and per-source re-ingestion. It is also exported for use in code. .ragrc.jsonentries of the form{ stage, name, config }resolve to built-in components, custom plugin modules listed underplugins, or plugins registered onpluginRegistry.rag-pipeline serve(andDevServer) exposesGET /healthandPOST /queryon 127.0.0.1: JSON only, 64 KB body limit, no CORS.doctorandvalidatereport real findings (unknown components, missing stages, missing API keys, broken custom plugins) and exit 1 on errors.
Commands and flags that printed success without doing anything have been removed. See the CLI reference.
Plugin install and certification
rag-pipeline hub installinstalls a plugin from a local directory, a.tgz, a single.jsfile,npm:<package>or a registry (sha256-verified). Extraction is confined to.rag/plugins. A static security scan runs first, and high-risk findings block the install unless you pass--allow-risky. The stage contract is checked in a separate process, and the plugin is then registered in.ragrc.json.PluginHub.installPlugin()uses the same installer.rag-pipeline hub certify submit|checkscores a local plugin from its files (contract, security scan, tests, documentation) against the BASIC/VERIFIED/ENTERPRISE automated thresholds. It previously issued certificates from fixed placeholder scores.rag-pipeline hub listlists the plugins registered in the config.- There is no default registry. Registry operations need
--registry <url>or theRAG_PLUGIN_REGISTRYenvironment variable.
rag-pipeline ai commands
The ai commands are rebuilt on the configured pipeline and index:
ai train embeddingrefits thelocalTF-IDF embedder and re-embeds the index.ai benchmarkreports measured p50/p95/mean latency per stage.ai feedback <question> <docId> --rating up|downstores votes in.rag/feedback.json.ai searchapplies them as a capped, explained score adjustment.ai multimodal processreports the type, character count and chunks of a text file. It exits 1 for images, because no built-in connector accepts image input.
The previous federated learning, fine-tuning, ai train status|deploy and ai dashboard commands printed hard-coded or simulated numbers and have been removed.
Cancellation everywhere
createRagPipeline now creates an AbortController per run and passes its signal to every component. The new options.signal lets you cancel a run yourself:
const controller = new AbortController();
const result = await pipeline.run({
query: "Summarize the travel policy",
options: { signal: controller.signal, timeout: 10000 },
});
// on abort: { success: false, error: "Execution aborted" }
// on timeout: { success: false, error: "Execution timeout" }
Components receive the signal as retriever.retrieve({ ..., signal }), embedder.embed/embedQuery(text, { signal }), reranker.rerank(q, docs, { signal }) and llm.generate(q, docs, { signal }). All built-in connectors and rerankers honour it, and AgenticPipeline and IterativeRetriever propagate it. Timeouts now cancel work, and no stage starts after an abort. The API reference describes the contract.
Other pipeline fixes:
- The embedder result was computed and discarded, so vector retrievers never received a
queryVector. That is fixed. - Without an
llm, the pipeline runs retrieval only and returnsanswer: nullinstead of'ok'. - In stream mode with
citationsorevaluate, the stream ends with one{ token: '', done: true, citations, evaluation }chunk.
Metric changes you need to know about
Scores from 2.5.0 are not comparable with earlier releases:
- Grounding (citations, faithfulness, context precision/recall, groundedness,
PostGenerationGuard,SelfCritiqueChecker) now measures the share of a sentence's content words found in a document, optionally IDF-weighted. The old Jaccard similarity against the whole document shrank with document length: an answer copied verbatim from a ~70-word chunk scored 0. Expect noticeably higher scores for grounded answers. - The default per-sentence similarity threshold rose from 0.3 to 0.5. This applies to
CitationTracker.similarityThreshold,PipelineEvaluator.thresholdand the faithfulness, recall and hallucination-detector thresholds. Aggregate thresholds are unchanged. - Context recall needs a reference answer. Pass it as
PipelineEvaluator.evaluate(result, { referenceAnswer })orrun({ options: { evaluate: true, referenceAnswer } }). Without one, the metric is skipped and listed inmetadata.metricsSkipped, and groundedness becomes(0.4 * faithfulness + 0.3 * contextPrecision) / 0.7. - Numbers are checked. A sentence whose numbers (digits, decimals, percentages, number words) are not in the source scores below 0.4 and is not grounded ("20" equals "twenty"). The helpers
checkNumericConsistencyandextractNumbersare exported. - The tokenizer keeps non-ASCII letters and splits on newlines and hyphens. This also affects BM25, BLEU/ROUGE and the scoring reranker.
Grounding is still lexical plus a numeric check. Negation, units, ordinals and claims that combine several documents are not checked. See Evaluation.
Security fixes
- Guardrails: the retrieval guard (ACL, relevance, freshness) now runs before generation, through the new
options.filterResultshook. Removed documents are reported by id only. Strict-mode blocks no longer return the raw answer. PII is redacted in citations and evaluation details too.RetrievalGuardwithenforceACLfails closed whencontext.userPermissionsis missing. - Prompt injection: retrieved documents are no longer placed in the LLM system role. Built-in connectors pass context as delimited, untrusted reference data. Detection normalises homoglyphs, leetspeak and zero-width characters.
detectInjectionis exported, andRetrievalGuardcan scan retrieved documents withdetectInjection: true. AuditLogger:- Tenant, user and action filters never matched, so queries returned every tenant's records. They now filter correctly.
- Encryption uses
createCipherivwith a random IV per field. - The key comes from
encryptionKeyorRAG_AUDIT_ENCRYPTION_KEY;requireEncryption: truemakes a missing key an error. - HMAC signatures are compared in constant time.
InputSanitizer: a regex state bug let command-injection payloads through after a previously blocked input. It is fixed.- Plugin signatures: Ed25519 verification works. A signed
contentDigestbinds plugin code and is always checked when present. The sharedpluginRegistryverifies signatures by default only whenNODE_ENV=production, and the trust store is empty by default. JWTValidator:- New
singleUseTokens(defaulttrue) andmaxTrackedJtis(default 100000) options; the replay store is bounded and expires entries. - An explicit
clockToleranceornotBeforeToleranceof 0 is now honoured.
- New
- Dependencies:
npm audit --omit=devreports no high or critical findings. .secretsis no longer tracked in git and is excluded from Docker builds. Rotate anything it held.
Experimental modules and deprecations
Some classes were simulations or incomplete: random training metrics, SSO that accepted any code outside production, random tenant quotas, GDPR erasure that deleted nothing, a non-functional plugin sandbox, fixed certification scores. They now live in a separate entry point:
import { TenantManager } from "@devilsdev/rag-pipeline-utils/experimental";
This covers ModelTrainingOrchestrator, FederatedLearningCoordinator, MultiModalProcessor, AdaptiveRetrievalEngine, SSOManager, SAMLProvider, OAuth2Provider, ActiveDirectoryProvider, OIDCProvider, TenantManager, ResourceMonitor, DataGovernanceManager / DataGovernance, PluginSandbox and PluginCertification.
The main-entry exports still work, but each emits a one-time DeprecationWarning (RAG_DEP_* codes). They are marked @deprecated in the types and will be removed from the main entry in the next major release. Both entries share the same classes, so instanceof keeps working. The Experimental modules page says what each one really does.
The experimental SSO also got security fixes:
- The development bypass requires
devBypass: trueand a non-productionNODE_ENV. - OIDC id_tokens are validated.
- OAuth2/OIDC use PKCE.
SAMLProviderrefuses to authenticate.
Packaging and TypeScript
require()/importof 2.4.5 failed in every clean install withCannot find module 'isolated-vm'.isolated-vmis now loaded only whenPluginSandboxruns, and it is an optional peer dependency.- A tarball smoke test runs in CI and before publish.
- 52 runtime exports had no TypeScript declarations. Declarations are now generated from JSDoc at build time, and the build fails if any export is undeclared.
Upgrade notes
npm install @devilsdev/rag-pipeline-utils@2.5.0
- Re-baseline evaluation thresholds. Grounding scores run higher, and the per-sentence threshold is now 0.5. Pass a
referenceAnswerif you rely on context recall. - Retrieval-only pipelines now return
answer: null. Results always includeanswerwhen an llm is configured. DevServer.start()now listens on port 3000 and host127.0.0.1by default, so existing callers may seeEADDRINUSE. Passport: 0for a free port.watchPathsnow defaults to[].- ACL filtering fails closed. With
enforceACL, always passcontext.userPermissions. PreRetrievalGuardreturns the query unchanged and reports detections ininjection. Strict mode still blocks.- Answers may differ. Built-in connectors now put retrieved context in the user message.
CostCalculatornow pricesgpt-4o-mini-*andclaude-3-5-sonnet-*correctly. Built-in prices are marked unverified; seePRICING_METADATA.- Plugin registry operations need
--registry <url>orRAG_PLUGIN_REGISTRY. - Removed CLI options:
--trace,--stats,--parallel,--streaming,--max-memory,--preview,--validate,--explain,--abort-timeoutand--quiet. Remove them from your scripts. docker composenow requiresPOSTGRES_PASSWORD,REDIS_PASSWORDandGRAFANA_ADMIN_PASSWORD(see.env.example) and publishes ports on 127.0.0.1 only.- Experimental imports: move them to
@devilsdev/rag-pipeline-utils/experimental.
The full list of changes is in the CHANGELOG.
