@devilsdev/rag-pipeline-utils 2.4.5 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/.ragrc.schema.json +83 -192
  2. package/CHANGELOG.md +431 -0
  3. package/README.md +418 -117
  4. package/SECURITY.md +5 -5
  5. package/dist/experimental.cjs +2 -0
  6. package/dist/experimental.d.ts +1 -0
  7. package/dist/experimental.mjs +5 -0
  8. package/dist/index.cjs +6442 -1355
  9. package/dist/index.d.ts +129 -2
  10. package/dist/index.mjs +1 -1
  11. package/dist/testing.cjs +2 -0
  12. package/dist/testing.d.ts +1 -0
  13. package/dist/testing.mjs +5 -0
  14. package/dist/types/adapters/ai-sdk.d.ts +53 -0
  15. package/dist/types/agentic/agentic-pipeline.d.ts +103 -0
  16. package/dist/types/agentic/iterative-retriever.d.ts +85 -0
  17. package/dist/types/agentic/query-planner.d.ts +145 -0
  18. package/dist/types/agentic/self-critique.d.ts +45 -0
  19. package/dist/types/ai/adaptive-retrieval.d.ts +44 -0
  20. package/dist/types/ai/federation/federated-learning-coordinator.d.ts +62 -0
  21. package/dist/types/ai/multimodal-processing.d.ts +32 -0
  22. package/dist/types/ai/multimodal.d.ts +39 -0
  23. package/dist/types/ai/retrieval-engine.d.ts +37 -0
  24. package/dist/types/ai/training/model-training-orchestrator.d.ts +31 -0
  25. package/dist/types/cache/component-keys.d.ts +7 -0
  26. package/dist/types/cache/memory-cache.d.ts +54 -0
  27. package/dist/types/cache/pipeline-cache.d.ts +66 -0
  28. package/dist/types/cache/redis-cache.d.ts +46 -0
  29. package/dist/types/chunking/chunking-engine.d.ts +70 -0
  30. package/dist/types/chunking/strategies/fixed-size.d.ts +17 -0
  31. package/dist/types/chunking/strategies/recursive.d.ts +20 -0
  32. package/dist/types/chunking/strategies/semantic.d.ts +19 -0
  33. package/dist/types/chunking/strategies/sentence.d.ts +13 -0
  34. package/dist/types/chunking/strategies/structure-aware.d.ts +19 -0
  35. package/dist/types/citation/citation-tracker.d.ts +64 -0
  36. package/dist/types/citation/hallucination-detector.d.ts +40 -0
  37. package/dist/types/citation/source-mapper.d.ts +37 -0
  38. package/dist/types/connectors/anthropic-connector.d.ts +97 -0
  39. package/dist/types/connectors/base-connector.d.ts +87 -0
  40. package/dist/types/connectors/cohere-connector.d.ts +94 -0
  41. package/dist/types/connectors/connector-utils.d.ts +110 -0
  42. package/dist/types/connectors/local-embedder.d.ts +95 -0
  43. package/dist/types/connectors/memory-retriever.d.ts +99 -0
  44. package/dist/types/connectors/ollama-connector.d.ts +115 -0
  45. package/dist/types/connectors/openai-connector.d.ts +129 -0
  46. package/dist/types/connectors/rag-prompt.d.ts +40 -0
  47. package/dist/types/core/create-pipeline.d.ts +8 -0
  48. package/dist/types/core/plugin-registry.d.ts +136 -0
  49. package/dist/types/core/stage-contracts.d.ts +36 -0
  50. package/dist/types/core/validate-plugin.d.ts +45 -0
  51. package/dist/types/cost/cost-calculator.d.ts +152 -0
  52. package/dist/types/cost/cost-tracker.d.ts +126 -0
  53. package/dist/types/cost/token-budget.d.ts +110 -0
  54. package/dist/types/dag/core/error-context.d.ts +239 -0
  55. package/dist/types/dag/core/execution-scheduler.d.ts +191 -0
  56. package/dist/types/dag/core/topology-validation.d.ts +226 -0
  57. package/dist/types/dag/dag-engine.d.ts +156 -0
  58. package/dist/types/dag/dag-node.d.ts +37 -0
  59. package/dist/types/dashboard/dashboard-generator.d.ts +127 -0
  60. package/dist/types/dashboard/metrics-aggregator.d.ts +218 -0
  61. package/dist/types/debug/execution-tracer.d.ts +57 -0
  62. package/dist/types/debug/trace-inspector.d.ts +31 -0
  63. package/dist/types/ecosystem/plugin-certification.d.ts +169 -0
  64. package/dist/types/enterprise/data-governance.d.ts +203 -0
  65. package/dist/types/enterprise/multi-tenancy.d.ts +137 -0
  66. package/dist/types/enterprise/sso-integration.d.ts +420 -0
  67. package/dist/types/evaluate/context-metrics.d.ts +55 -0
  68. package/dist/types/evaluate/faithfulness.d.ts +41 -0
  69. package/dist/types/evaluate/groundedness.d.ts +60 -0
  70. package/dist/types/evaluate/pipeline-evaluator.d.ts +88 -0
  71. package/dist/types/evaluate/relevance.d.ts +21 -0
  72. package/dist/types/evaluate/scoring.d.ts +123 -0
  73. package/dist/types/evaluate/semantic-similarity.d.ts +14 -0
  74. package/dist/types/experimental.d.ts +15 -0
  75. package/dist/types/graph/entity-extractor.d.ts +84 -0
  76. package/dist/types/graph/graph-index.d.ts +49 -0
  77. package/dist/types/graph/graph-retriever.d.ts +81 -0
  78. package/dist/types/graph/knowledge-graph.d.ts +129 -0
  79. package/dist/types/grounding/check-answer.d.ts +101 -0
  80. package/dist/types/guardrails/guardrails-pipeline.d.ts +63 -0
  81. package/dist/types/guardrails/injection-detector.d.ts +47 -0
  82. package/dist/types/guardrails/post-generation-guard.d.ts +61 -0
  83. package/dist/types/guardrails/pre-retrieval-guard.d.ts +64 -0
  84. package/dist/types/guardrails/retrieval-guard.d.ts +57 -0
  85. package/dist/types/mcp/mcp-server.d.ts +85 -0
  86. package/dist/types/mcp/mcp-tool-builder.d.ts +55 -0
  87. package/dist/types/reranker/cascade-reranker.d.ts +40 -0
  88. package/dist/types/reranker/embedding-reranker.d.ts +77 -0
  89. package/dist/types/reranker/scoring-reranker.d.ts +41 -0
  90. package/dist/types/retrieval/bm25-search.d.ts +43 -0
  91. package/dist/types/retrieval/file-vector-store.d.ts +99 -0
  92. package/dist/types/retrieval/hybrid-retriever.d.ts +35 -0
  93. package/dist/types/retrieval/rank-fusion.d.ts +10 -0
  94. package/dist/types/security/jwt-config.d.ts +80 -0
  95. package/dist/types/security/jwt-validator.d.ts +195 -0
  96. package/dist/types/security/plugin-sandbox.d.ts +158 -0
  97. package/dist/types/security/plugin-signature-verifier.d.ts +139 -0
  98. package/dist/types/security/validator.d.ts +5 -0
  99. package/dist/types/streaming/embedding-stream.d.ts +65 -0
  100. package/dist/types/streaming/sse-adapter.d.ts +59 -0
  101. package/dist/types/streaming/stream-router.d.ts +88 -0
  102. package/dist/types/streaming/streaming-embedder.d.ts +99 -0
  103. package/dist/types/streaming/websocket-adapter.d.ts +71 -0
  104. package/dist/types/templates/scaffold.d.ts +55 -0
  105. package/dist/types/templates/template-registry.d.ts +63 -0
  106. package/dist/types/testing.d.ts +168 -0
  107. package/dist/types/utils/embed-query.d.ts +20 -0
  108. package/dist/types/utils/logger.d.ts +50 -0
  109. package/dist/types/utils/rate-limiter.d.ts +66 -0
  110. package/dist/types/utils/retry.d.ts +27 -0
  111. package/dist/types/utils/run-abort.d.ts +34 -0
  112. package/dist/types/utils/secure-logging.d.ts +51 -0
  113. package/dist/types/utils/structured-logger.d.ts +126 -0
  114. package/package.json +72 -33
  115. package/src/adapters/ai-sdk.js +215 -0
  116. package/src/agentic/agentic-pipeline.js +75 -7
  117. package/src/agentic/iterative-retriever.js +47 -10
  118. package/src/agentic/query-planner.js +329 -78
  119. package/src/cache/component-keys.js +38 -0
  120. package/src/cache/memory-cache.js +87 -0
  121. package/src/cache/pipeline-cache.js +260 -0
  122. package/src/cache/redis-cache.js +83 -0
  123. package/src/citation/citation-tracker.js +6 -4
  124. package/src/citation/hallucination-detector.js +2 -2
  125. package/src/citation/source-mapper.js +15 -28
  126. package/src/cli/commands/ai.js +106 -0
  127. package/src/cli/commands/plugin-hub/event-handlers.js +6 -0
  128. package/src/cli/commands/plugin-hub/hub-commands.js +0 -74
  129. package/src/cli/commands/plugin-hub/local-commands.js +143 -0
  130. package/src/cli/commands/plugin-hub.js +114 -49
  131. package/src/cli/diagnostics/config-checker.js +76 -63
  132. package/src/cli/diagnostics/environment-checker.js +62 -31
  133. package/src/cli/diagnostics/plugin-checker.js +36 -89
  134. package/src/cli/doctor-command.js +39 -5
  135. package/src/cli/enhanced-cli-commands.js +104 -233
  136. package/src/cli/handlers/ai-handler.js +364 -0
  137. package/src/cli/handlers/diagnostics-handler.js +78 -31
  138. package/src/cli/handlers/ingest-handler.js +28 -22
  139. package/src/cli/handlers/init-handler.js +148 -25
  140. package/src/cli/handlers/query-handler.js +203 -25
  141. package/src/cli/handlers/run-handler.js +16 -167
  142. package/src/cli/handlers/serve-handler.js +60 -0
  143. package/src/cli/services/bench-stats.js +54 -0
  144. package/src/cli/services/benchmark.js +161 -0
  145. package/src/cli/services/feedback.js +155 -0
  146. package/src/cli/services/instrument.js +83 -0
  147. package/src/cli/services/rag-service.js +361 -0
  148. package/src/cli/shared/cli-utils.js +24 -7
  149. package/src/cli/shared/config-context.js +21 -0
  150. package/src/cli/wizard/config-builder.js +81 -0
  151. package/src/cli/wizard/init-wizard.js +602 -0
  152. package/src/config/component-factory.js +125 -0
  153. package/src/config/enhanced-ragrc-schema.js +0 -2
  154. package/src/config/load-config.js +29 -10
  155. package/src/config/load-plugin-config.js +46 -2
  156. package/src/config/validate-schema.js +4 -1
  157. package/src/connectors/anthropic-connector.js +115 -57
  158. package/src/connectors/base-connector.js +74 -19
  159. package/src/connectors/cohere-connector.js +108 -34
  160. package/src/connectors/connector-utils.js +302 -0
  161. package/src/connectors/local-embedder.js +63 -5
  162. package/src/connectors/memory-retriever.js +125 -14
  163. package/src/connectors/ollama-connector.js +112 -51
  164. package/src/connectors/openai-connector.js +153 -44
  165. package/src/connectors/rag-prompt.js +88 -0
  166. package/src/core/create-pipeline.js +307 -61
  167. package/src/core/performance/parallel-processor.js +62 -1
  168. package/src/core/plugin-registry.js +125 -26
  169. package/src/core/stage-contracts.js +36 -0
  170. package/src/core/validate-plugin.js +85 -0
  171. package/src/cost/cost-calculator.js +205 -32
  172. package/src/debug/execution-tracer.js +14 -4
  173. package/src/dev/dev-server.js +173 -11
  174. package/src/dev/hot-reload.js +44 -4
  175. package/src/ecosystem/plugin-checks.js +181 -0
  176. package/src/ecosystem/plugin-hub.js +50 -125
  177. package/src/ecosystem/plugin-installer.js +298 -0
  178. package/src/ecosystem/plugin-probe.js +127 -0
  179. package/src/ecosystem/plugin-scanner.js +185 -0
  180. package/src/ecosystem/tar-extract.js +114 -0
  181. package/src/enterprise/audit-logging.js +158 -29
  182. package/src/enterprise/sso-integration.js +716 -228
  183. package/src/evaluate/context-metrics.js +28 -16
  184. package/src/evaluate/evaluator.js +3 -3
  185. package/src/evaluate/faithfulness.js +18 -14
  186. package/src/evaluate/groundedness.js +43 -12
  187. package/src/evaluate/pipeline-evaluator.js +35 -8
  188. package/src/evaluate/scoring.js +251 -3
  189. package/src/evaluate/semantic-similarity.js +79 -0
  190. package/src/experimental.js +71 -0
  191. package/src/grounding/check-answer.js +173 -0
  192. package/src/guardrails/guardrails-pipeline.js +69 -7
  193. package/src/guardrails/injection-detector.js +283 -0
  194. package/src/guardrails/post-generation-guard.js +19 -0
  195. package/src/guardrails/pre-retrieval-guard.js +36 -36
  196. package/src/guardrails/retrieval-guard.js +58 -8
  197. package/src/index.js +58 -1
  198. package/src/loader/csv-loader.js +3 -2
  199. package/src/loader/document-reader.js +91 -0
  200. package/src/loader/html-loader.js +3 -1
  201. package/src/loader/markdown-loader.js +3 -2
  202. package/src/reranker/cascade-reranker.js +3 -0
  203. package/src/reranker/embedding-reranker.js +136 -21
  204. package/src/reranker/llm-reranker.js +79 -12
  205. package/src/reranker/scoring-reranker.js +3 -0
  206. package/src/retrieval/bm25-search.js +12 -6
  207. package/src/retrieval/file-vector-store.js +241 -0
  208. package/src/retrieval/hybrid-retriever.js +22 -19
  209. package/src/security/jwt-config.js +13 -2
  210. package/src/security/jwt-validator.js +139 -34
  211. package/src/security/plugin-sandbox.js +18 -1
  212. package/src/security/plugin-signature-verifier.js +245 -70
  213. package/src/testing.js +254 -0
  214. package/src/utils/deprecate.js +69 -0
  215. package/src/utils/embed-query.js +33 -0
  216. package/src/utils/input-sanitizer.js +19 -4
  217. package/src/utils/logger.js +34 -4
  218. package/src/utils/run-abort.js +102 -0
  219. package/src/ai/federated-learning.js +0 -211
  220. package/src/ai/federation.js +0 -71
  221. package/src/ai/index.js +0 -22
  222. package/src/ai/model-trainer.js +0 -217
  223. package/src/ai/model-training.js +0 -202
  224. package/src/ai/multimodal/multi-modal-processor.js +0 -345
  225. package/src/ai/orchestrator.js +0 -65
  226. package/src/ai/retrieval/adaptive-retrieval-engine.js +0 -398
  227. package/src/cli/commands/ai-ml/adaptive-commands.js +0 -178
  228. package/src/cli/commands/ai-ml/federated-commands.js +0 -207
  229. package/src/cli/commands/ai-ml/index.js +0 -17
  230. package/src/cli/commands/ai-ml/multimodal-commands.js +0 -187
  231. package/src/cli/commands/ai-ml/shared.js +0 -51
  232. package/src/cli/commands/ai-ml/training-commands.js +0 -228
  233. package/src/cli/commands/ai-ml.js +0 -386
  234. package/src/cli/commands/docs.js +0 -228
  235. package/src/cli/commands/dx.js +0 -538
  236. package/src/cli/commands/wizard.js +0 -627
  237. package/src/cli/input-sanitization-middleware.js +0 -356
  238. package/src/cli/interactive-wizard.js +0 -373
  239. package/src/cli/templates/development.template.json +0 -64
  240. package/src/cli/templates/minimal.template.json +0 -33
  241. package/src/cli/templates/production.template.json +0 -104
  242. package/src/cli/templates/testing.template.json +0 -45
  243. package/src/cli/wizard/configuration.js +0 -148
  244. package/src/cli/wizard/observability.js +0 -79
  245. package/src/cli/wizard/performance.js +0 -76
  246. package/src/cli/wizard/plugin-selection.js +0 -297
  247. package/src/cli/wizard/preview-save.js +0 -172
  248. package/src/cli/wizard/project-setup.js +0 -66
  249. package/src/core/pipeline-factory.js +0 -59
  250. package/src/dag/enhanced-dag-engine.js +0 -894
  251. package/src/dx/index.js +0 -40
  252. package/src/dx/integration-templates.js +0 -662
  253. package/src/dx/performance-profiler.js +0 -968
  254. package/src/dx/realtime-debugger.js +0 -755
  255. package/src/dx/visual-pipeline-builder.js +0 -578
  256. package/src/ingest.js +0 -59
  257. package/src/pipeline/pipeline.js +0 -18
  258. package/src/query.js +0 -117
@@ -0,0 +1,283 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Prompt-injection detection shared by PreRetrievalGuard (user queries) and
5
+ * RetrievalGuard (retrieved documents, opt-in).
6
+ *
7
+ * Text is normalised before matching so trivial obfuscation does not bypass
8
+ * the patterns: Unicode NFKC, removal of zero-width and format characters,
9
+ * control characters turned into spaces, diacritics stripped, common
10
+ * Cyrillic/Greek homoglyphs and leetspeak folded to ASCII letters, and
11
+ * whitespace collapsed. Detection never rewrites the caller's text.
12
+ */
13
+
14
+ /**
15
+ * Build a character class from code points / [start, end] ranges, so the
16
+ * source stays ASCII-only.
17
+ * @param {Array<number|[number, number]>} items
18
+ * @returns {string}
19
+ */
20
+ function charClass(items) {
21
+ const hex = (cp) => '\\u' + cp.toString(16).padStart(4, '0');
22
+ return items
23
+ .map((it) => (Array.isArray(it) ? `${hex(it[0])}-${hex(it[1])}` : hex(it)))
24
+ .join('');
25
+ }
26
+
27
+ /** Zero-width / invisible format characters removed before matching. */
28
+ const INVISIBLE_RE = new RegExp(
29
+ `[${charClass([
30
+ 0x00ad, // soft hyphen
31
+ 0x034f, // combining grapheme joiner
32
+ 0x061c, // arabic letter mark
33
+ 0x115f,
34
+ 0x1160, // hangul fillers
35
+ 0x17b4,
36
+ 0x17b5,
37
+ [0x180b, 0x180f], // mongolian variation selectors / vowel separator
38
+ [0x200b, 0x200f], // zero-width space/joiners, LRM/RLM
39
+ [0x202a, 0x202e], // bidi embeddings/overrides
40
+ [0x2060, 0x206f], // word joiner, invisible operators, bidi isolates
41
+ 0x3164, // hangul filler
42
+ [0xfe00, 0xfe0f], // variation selectors
43
+ 0xfeff, // BOM / zero-width no-break space
44
+ 0xffa0, // halfwidth hangul filler
45
+ ])}]`,
46
+ 'g',
47
+ );
48
+
49
+ /** C0/C1 control characters (other than whitespace), replaced by a space. */
50
+ const CONTROL_RE = new RegExp(
51
+ `[${charClass([[0x00, 0x08], 0x0b, 0x0c, [0x0e, 0x1f], [0x7f, 0x9f]])}]`,
52
+ 'g',
53
+ );
54
+
55
+ /** Cyrillic and Greek letters that look like Latin letters (code point -> letter). */
56
+ const HOMOGLYPH_CODEPOINTS = [
57
+ // Cyrillic
58
+ [0x0430, 'a'],
59
+ [0x0432, 'b'],
60
+ [0x0435, 'e'],
61
+ [0x0451, 'e'],
62
+ [0x043a, 'k'],
63
+ [0x043c, 'm'],
64
+ [0x043d, 'h'],
65
+ [0x043e, 'o'],
66
+ [0x0440, 'p'],
67
+ [0x0441, 'c'],
68
+ [0x0442, 't'],
69
+ [0x0443, 'y'],
70
+ [0x0445, 'x'],
71
+ [0x0455, 's'],
72
+ [0x0456, 'i'],
73
+ [0x0457, 'i'],
74
+ [0x0458, 'j'],
75
+ [0x0501, 'd'],
76
+ [0x051b, 'q'],
77
+ [0x051d, 'w'],
78
+ [0x04cf, 'l'],
79
+ [0x0261, 'g'],
80
+ // Greek
81
+ [0x03b1, 'a'],
82
+ [0x03b2, 'b'],
83
+ [0x03b5, 'e'],
84
+ [0x03b7, 'n'],
85
+ [0x03b9, 'i'],
86
+ [0x03ba, 'k'],
87
+ [0x03bd, 'v'],
88
+ [0x03bf, 'o'],
89
+ [0x03c1, 'p'],
90
+ [0x03c4, 't'],
91
+ [0x03c5, 'u'],
92
+ [0x03c7, 'x'],
93
+ [0x03c9, 'w'],
94
+ [0x03f2, 'c'],
95
+ ];
96
+ const HOMOGLYPHS = Object.fromEntries(
97
+ HOMOGLYPH_CODEPOINTS.map(([cp, letter]) => [
98
+ String.fromCodePoint(cp),
99
+ letter,
100
+ ]),
101
+ );
102
+
103
+ /** Leetspeak substitutions folded to letters (only in the folded copy). */
104
+ const LEET = {
105
+ 0: 'o',
106
+ 1: 'i',
107
+ 3: 'e',
108
+ 4: 'a',
109
+ 5: 's',
110
+ 7: 't',
111
+ 8: 'b',
112
+ '@': 'a',
113
+ $: 's',
114
+ '!': 'i',
115
+ '|': 'l',
116
+ };
117
+
118
+ /**
119
+ * Normalise text for matching, without leetspeak/homoglyph folding.
120
+ * Case is preserved so case-sensitive custom patterns still work.
121
+ * @param {string} text
122
+ * @returns {string}
123
+ */
124
+ function normalizeText(text) {
125
+ return String(text)
126
+ .normalize('NFKC')
127
+ .replace(INVISIBLE_RE, '')
128
+ .replace(CONTROL_RE, ' ')
129
+ .normalize('NFD')
130
+ .replace(/\p{Mn}+/gu, '')
131
+ .normalize('NFC')
132
+ .replace(/[^\S\n]+/g, ' ')
133
+ .replace(/ ?\n\s*/g, '\n')
134
+ .trim();
135
+ }
136
+
137
+ /**
138
+ * Fully folded, lower-case form used by the built-in patterns.
139
+ * @param {string} text
140
+ * @returns {string}
141
+ */
142
+ function foldText(text) {
143
+ const lower = normalizeText(text).toLowerCase();
144
+ let out = '';
145
+ for (const ch of lower) {
146
+ out += HOMOGLYPHS[ch] || LEET[ch] || ch;
147
+ }
148
+ // Separator tricks such as "i.g.n.o.r.e" or "i-g-n-o-r-e": join single
149
+ // letters separated by one punctuation/space character.
150
+ return out.replace(/\b(?:[a-z][.\-_* ]){3,}[a-z]\b/g, (m) =>
151
+ m.replace(/[.\-_* ]/g, ''),
152
+ );
153
+ }
154
+
155
+ /**
156
+ * Built-in injection patterns. They run against the folded, lower-case text,
157
+ * so they are written in lower case and must stay narrow enough not to flag
158
+ * ordinary questions (e.g. "How does Redis act as a cache?").
159
+ * @type {Array<{ id: string, pattern: RegExp }>}
160
+ */
161
+ const DEFAULT_RULES = [
162
+ {
163
+ id: 'ignore-instructions',
164
+ pattern:
165
+ /\b(?:ignore|disregard|forget|skip|override|bypass)\s+(?:all\s+|any\s+|the\s+|your\s+|my\s+|of\s+)*(?:previous|prior|above|preceding|earlier|former|original|system|initial)\s+(?:instructions?|prompts?|directions?|directives?|rules|guidelines|context|messages?)\b/,
166
+ },
167
+ {
168
+ id: 'forget-everything',
169
+ pattern:
170
+ /\bforget\s+(?:everything|all)\s+(?:you(?:'ve|\s+have|\s+were|\s+know|\s+learned)|(?:previous|prior|above|before|that\s+came))/,
171
+ },
172
+ {
173
+ id: 'role-override',
174
+ pattern:
175
+ /\byou\s+are\s+now\s+(?:an?\s+|my\s+|the\s+)?(?:[a-z'-]+\s+){0,2}(?:ai|assistant|bot|chatbot|model|llm|dan|hacker|unrestricted|unfiltered|uncensored|jailbroken|evil|persona|character)\b|\byou\s+are\s+now\s+(?:in\s+)?(?:developer|dan|god|admin|jailbreak|unrestricted)\s+mode\b|\byou\s+are\s+no\s+longer\s+(?:an?\s+)?(?:ai|assistant|bound|restricted|limited|chatgpt|claude)\b|\byou\s+are\s+dan\b/,
176
+ },
177
+ {
178
+ // "act as" only in imperative position: start of text/sentence, or after
179
+ // please/now/you must/will/should. "Redis acts as a cache" is not flagged.
180
+ id: 'act-as',
181
+ pattern:
182
+ /(?:^|[.!?:;\n]\s*|\b(?:please|now|you\s+(?:must|will|should|shall|can|are\s+to)|i\s+want\s+you\s+to)\s+)act\s+as\s+(?:if\s+you\b|an?\s+|my\s+|the\s+|though\b)/,
183
+ },
184
+ {
185
+ id: 'pretend',
186
+ pattern:
187
+ /\bpretend\s+(?:that\s+)?(?:you\s+are|you're|youre|you\s+have\s+no|to\s+be\s+(?:an?\s+)?(?:\w+\s+)?(?:ai|assistant|model|bot|chatbot|dan|unrestricted|unfiltered|uncensored|evil|hacker|jailbroken))\b/,
188
+ },
189
+ {
190
+ // Chat-template role markers: "system:" as a line prefix, [system],
191
+ // <|im_start|>system, [INST], <<SYS>>, "### system".
192
+ id: 'role-marker',
193
+ pattern:
194
+ /(?:^|\n)\s*(?:system|assistant|developer)\s*:|\[\s*(?:system|inst)\s*\]|<\|?\s*(?:im_start|system)\s*\|?>|<<\s*sys\s*>>|#{2,}\s*(?:system|instruction)s?\b/,
195
+ },
196
+ {
197
+ id: 'dan',
198
+ pattern: /\bdan\s+(?:mode|prompt|jailbreak)\b|\bdo\s+anything\s+now\b/,
199
+ },
200
+ {
201
+ id: 'bypass-restrictions',
202
+ pattern:
203
+ /\b(?:bypass|circumvent|evade)\s+(?:all\s+|any\s+|your\s+|the\s+|of\s+)*(?:ai\s+|safety\s+|content\s+|ethical\s+)?(?:restrictions|guardrails|safeguards|safety\s+(?:rules|measures|guidelines|filters?)|content\s+(?:polic(?:y|ies)|filters?)|moderation)\b|\b(?:disable|ignore|remove|turn\s+off|drop)\s+(?:all\s+(?:of\s+)?)?your\s+(?:safety\s+|content\s+|ethical\s+)?(?:restrictions|filters?|guardrails|safeguards|safety|rules|guidelines|programming)\b/,
204
+ },
205
+ {
206
+ id: 'override-safety',
207
+ pattern:
208
+ /\boverride\s+(?:your\s+|the\s+|all\s+)?(?:safety|content\s+(?:filters?|polic(?:y|ies))|system\s+(?:prompt|instructions?))\b/,
209
+ },
210
+ {
211
+ id: 'jailbreak',
212
+ pattern:
213
+ /\bjailbr(?:ea|o)k(?:ing|en)?\s+(?:the\s+|this\s+|your\s+|an?\s+)?(?:model|ai|assistant|llm|chatbot|gpt|claude|system|prompt|you)\b|\bjailbreak\s+(?:mode|prompt)\b|\byou\s+are\s+jailbroken\b/,
214
+ },
215
+ {
216
+ id: 'prompt-exfiltration',
217
+ pattern:
218
+ /\b(?:reveal|show|print|repeat|output|leak|display|tell\s+me)\s+(?:me\s+)?(?:your|the)\s+(?:system\s+prompt|hidden\s+(?:prompt|instructions)|initial\s+(?:prompt|instructions)|original\s+(?:prompt|instructions)|instructions\s+above)\b/,
219
+ },
220
+ ];
221
+
222
+ /**
223
+ * Prepare a user-supplied pattern for repeated .test() calls: a global or
224
+ * sticky flag would keep lastIndex state between calls.
225
+ * @param {RegExp|string} pattern
226
+ * @returns {RegExp}
227
+ */
228
+ function toStatelessRegExp(pattern) {
229
+ if (pattern instanceof RegExp) {
230
+ if (!pattern.global && !pattern.sticky) return pattern;
231
+ return new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, ''));
232
+ }
233
+ return new RegExp(String(pattern), 'i');
234
+ }
235
+
236
+ /**
237
+ * Detect prompt-injection attempts in text.
238
+ *
239
+ * Built-in rules run on the folded text. Custom patterns run on the
240
+ * normalised text (case preserved) and on the folded text.
241
+ *
242
+ * @param {string} text
243
+ * @param {object} [opts]
244
+ * @param {Array<RegExp|string>} [opts.patterns=[]] - Extra patterns
245
+ * @param {boolean} [opts.useDefaults=true] - Include the built-in rules
246
+ * @returns {{ detected: boolean, matches: Array<{ id: string, pattern: string, match: string }>, normalized: string }}
247
+ */
248
+ function detectInjection(text, opts = {}) {
249
+ const normalized = normalizeText(text == null ? '' : text);
250
+ const lower = normalized.toLowerCase();
251
+ const folded = foldText(normalized);
252
+ const matches = [];
253
+
254
+ if (opts.useDefaults !== false) {
255
+ for (const rule of DEFAULT_RULES) {
256
+ const m = rule.pattern.exec(lower) || rule.pattern.exec(folded);
257
+ if (m) {
258
+ matches.push({
259
+ id: rule.id,
260
+ pattern: String(rule.pattern),
261
+ match: m[0].trim(),
262
+ });
263
+ }
264
+ }
265
+ }
266
+
267
+ for (const raw of opts.patterns || []) {
268
+ const re = toStatelessRegExp(raw);
269
+ const m = re.exec(normalized) || re.exec(folded);
270
+ if (m) {
271
+ matches.push({ id: 'custom', pattern: String(raw), match: m[0].trim() });
272
+ }
273
+ }
274
+
275
+ return { detected: matches.length > 0, matches, normalized };
276
+ }
277
+
278
+ module.exports = {
279
+ detectInjection,
280
+ normalizeText,
281
+ foldText,
282
+ DEFAULT_RULES,
283
+ };
@@ -165,6 +165,25 @@ class PostGenerationGuard extends EventEmitter {
165
165
  this.emit('checked', result);
166
166
  return result;
167
167
  }
168
+
169
+ /**
170
+ * Replace PII matches in arbitrary text with [REDACTED], using the same
171
+ * patterns as check(). Used to scrub text derived from the answer, such as
172
+ * citation sentences.
173
+ *
174
+ * @param {string} text
175
+ * @returns {string}
176
+ */
177
+ redact(text) {
178
+ if (typeof text !== 'string' || !this.config.enablePIIDetection) {
179
+ return text;
180
+ }
181
+ let redacted = text;
182
+ for (const pattern of Object.values(this.config.piiPatterns)) {
183
+ redacted = redacted.replace(pattern, '[REDACTED]');
184
+ }
185
+ return redacted;
186
+ }
168
187
  }
169
188
 
170
189
  module.exports = { PostGenerationGuard, DEFAULT_CONFIG };
@@ -3,6 +3,7 @@
3
3
  const { EventEmitter } = require('events');
4
4
  const { validateInput } = require('../security/validator');
5
5
  const { tokenize } = require('../evaluate/scoring');
6
+ const { detectInjection, DEFAULT_RULES } = require('./injection-detector');
6
7
 
7
8
  /**
8
9
  * Default configuration for the PreRetrievalGuard.
@@ -14,18 +15,10 @@ const DEFAULT_CONFIG = {
14
15
  enableTopicFiltering: false,
15
16
  blockedTopics: [],
16
17
  allowedTopics: [],
17
- injectionPatterns: [
18
- /ignore\s+(all\s+)?previous\s+instructions/i,
19
- /you\s+are\s+now/i,
20
- /forget\s+everything/i,
21
- /act\s+as\s+(a|an)?\s*/i,
22
- /pretend\s+(you\s+are|to\s+be)/i,
23
- /system\s*:/i,
24
- /\bDAN\b/,
25
- /bypass\s+(all\s+)?restrictions/i,
26
- /override\s+(safety|content|system)/i,
27
- /jailbreak/i,
28
- ],
18
+ // Built-in rules (see ./injection-detector). They match against normalised
19
+ // text (NFKC, zero-width/control characters removed, homoglyph and
20
+ // leetspeak folding). options.injectionPatterns adds extra patterns.
21
+ injectionPatterns: DEFAULT_RULES.map((rule) => rule.pattern),
29
22
  };
30
23
 
31
24
  /**
@@ -48,6 +41,10 @@ class PreRetrievalGuard extends EventEmitter {
48
41
  constructor(options = {}) {
49
42
  super();
50
43
  this.config = { ...DEFAULT_CONFIG, ...options };
44
+ /** @private Extra patterns supplied by the caller */
45
+ this._customPatterns = options.injectionPatterns
46
+ ? [...options.injectionPatterns]
47
+ : [];
51
48
  if (options.injectionPatterns) {
52
49
  this.config.injectionPatterns = [
53
50
  ...DEFAULT_CONFIG.injectionPatterns,
@@ -63,8 +60,13 @@ class PreRetrievalGuard extends EventEmitter {
63
60
  * @returns {{
64
61
  * safe: boolean,
65
62
  * issues: string[],
66
- * sanitizedQuery: string
63
+ * sanitizedQuery: string,
64
+ * injection: { detected: boolean, matches: Array<{id: string, pattern: string, match: string}> }
67
65
  * }}
66
+ * `sanitizedQuery` is the query truncated to maxQueryLength; it is never
67
+ * rewritten. Injection findings are reported in `issues` and `injection`
68
+ * and make `safe` false; blocking is up to the caller (GuardrailsPipeline
69
+ * blocks in strict mode).
68
70
  * @fires PreRetrievalGuard#checked
69
71
  */
70
72
  check(query) {
@@ -77,7 +79,12 @@ class PreRetrievalGuard extends EventEmitter {
77
79
  query.trim().length === 0
78
80
  ) {
79
81
  issues.push('Query must be a non-empty string');
80
- const result = { safe: false, issues, sanitizedQuery: '' };
82
+ const result = {
83
+ safe: false,
84
+ issues,
85
+ sanitizedQuery: '',
86
+ injection: { detected: false, matches: [] },
87
+ };
81
88
  this.emit('checked', result);
82
89
  return result;
83
90
  }
@@ -92,16 +99,22 @@ class PreRetrievalGuard extends EventEmitter {
92
99
  }
93
100
  }
94
101
 
95
- let sanitizedQuery = validation.sanitized;
102
+ // The query is passed on unchanged (apart from length truncation).
103
+ // Deleting only the matched phrase used to leave the rest of an attack
104
+ // payload in place, so detections are reported instead.
105
+ const sanitizedQuery = validation.sanitized;
96
106
 
97
- // 3. Injection detection
98
- const matchedPatterns = [];
107
+ // 3. Injection detection on a normalised copy of the query
108
+ let injection = { detected: false, matches: [] };
99
109
  if (this.config.enableInjectionDetection) {
100
- for (const pattern of this.config.injectionPatterns) {
101
- if (pattern.test(sanitizedQuery)) {
102
- matchedPatterns.push(pattern);
103
- issues.push(`Potential prompt injection detected: ${pattern}`);
104
- }
110
+ const found = detectInjection(sanitizedQuery, {
111
+ patterns: this._customPatterns,
112
+ });
113
+ injection = { detected: found.detected, matches: found.matches };
114
+ for (const m of found.matches) {
115
+ issues.push(
116
+ `Potential prompt injection detected: ${m.id} (${JSON.stringify(m.match)})`,
117
+ );
105
118
  }
106
119
  }
107
120
 
@@ -133,24 +146,11 @@ class PreRetrievalGuard extends EventEmitter {
133
146
  }
134
147
  }
135
148
 
136
- // 5. Sanitize: strip matched injection patterns from query
137
- if (matchedPatterns.length > 0) {
138
- for (const pattern of matchedPatterns) {
139
- // Create a global version of the pattern for replacement
140
- const flags = pattern.flags.includes('g')
141
- ? pattern.flags
142
- : pattern.flags + 'g';
143
- const globalPattern = new RegExp(pattern.source, flags);
144
- sanitizedQuery = sanitizedQuery.replace(globalPattern, '').trim();
145
- }
146
- // Collapse multiple spaces left after stripping
147
- sanitizedQuery = sanitizedQuery.replace(/\s{2,}/g, ' ').trim();
148
- }
149
-
150
149
  const result = {
151
150
  safe: issues.length === 0,
152
151
  issues,
153
152
  sanitizedQuery,
153
+ injection,
154
154
  };
155
155
 
156
156
  this.emit('checked', result);
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { EventEmitter } = require('events');
4
+ const { detectInjection } = require('./injection-detector');
4
5
 
5
6
  /**
6
7
  * Default configuration for the RetrievalGuard.
@@ -10,6 +11,12 @@ const DEFAULT_CONFIG = {
10
11
  minRelevanceScore: 0,
11
12
  maxAgeDays: 0, // 0 = disabled
12
13
  enforceACL: false,
14
+ // Opt-in scan of retrieved document text for prompt injection (indirect
15
+ // injection). 'remove' drops flagged documents, 'flag' keeps them and
16
+ // marks them with `injection` details.
17
+ detectInjection: false,
18
+ injectionAction: 'remove',
19
+ injectionPatterns: [],
13
20
  };
14
21
 
15
22
  /**
@@ -25,6 +32,9 @@ class RetrievalGuard extends EventEmitter {
25
32
  * @param {number} [options.minRelevanceScore=0] - Minimum relevance score threshold
26
33
  * @param {number} [options.maxAgeDays=0] - Maximum document age in days (0 = disabled)
27
34
  * @param {boolean} [options.enforceACL=false] - Whether to enforce access control
35
+ * @param {boolean} [options.detectInjection=false] - Scan document text for prompt injection
36
+ * @param {'remove'|'flag'} [options.injectionAction='remove'] - Remove flagged documents or keep and mark them
37
+ * @param {RegExp[]} [options.injectionPatterns=[]] - Extra injection patterns for the scan
28
38
  */
29
39
  constructor(options = {}) {
30
40
  super();
@@ -86,17 +96,19 @@ class RetrievalGuard extends EventEmitter {
86
96
  }
87
97
  }
88
98
 
89
- // 3. ACL filter
90
- if (!dominated && this.config.enforceACL && context.userPermissions) {
91
- const requiredRoles = doc.requiredRole
92
- ? [].concat(doc.requiredRole)
93
- : doc.acl
94
- ? [].concat(doc.acl)
95
- : null;
99
+ // 3. ACL filter. Missing userPermissions fails closed: the user is
100
+ // treated as having no roles, so restricted documents are removed.
101
+ if (!dominated && this.config.enforceACL) {
102
+ const userPermissions = context.userPermissions || [];
103
+ // Read the restriction from the result itself or its metadata, so a
104
+ // top-level `acl` is not ignored when the result also has metadata.
105
+ const restriction =
106
+ result.requiredRole ?? result.acl ?? doc.requiredRole ?? doc.acl;
107
+ const requiredRoles = restriction ? [].concat(restriction) : null;
96
108
 
97
109
  if (requiredRoles && requiredRoles.length > 0) {
98
110
  const hasAccess = requiredRoles.some((role) =>
99
- context.userPermissions.includes(role),
111
+ userPermissions.includes(role),
100
112
  );
101
113
  if (!hasAccess) {
102
114
  dominated = true;
@@ -105,6 +117,25 @@ class RetrievalGuard extends EventEmitter {
105
117
  }
106
118
  }
107
119
 
120
+ // 4. Indirect prompt injection in document text (opt-in)
121
+ if (!dominated && this.config.detectInjection) {
122
+ const text = documentText(result);
123
+ const found = detectInjection(text, {
124
+ patterns: this.config.injectionPatterns,
125
+ });
126
+ if (found.detected) {
127
+ reasons.injection = (reasons.injection || 0) + 1;
128
+ if (this.config.injectionAction === 'flag') {
129
+ kept.push({
130
+ ...result,
131
+ injection: { detected: true, matches: found.matches },
132
+ });
133
+ continue;
134
+ }
135
+ dominated = true;
136
+ }
137
+ }
138
+
108
139
  if (dominated) {
109
140
  removed.push(result);
110
141
  } else {
@@ -123,4 +154,23 @@ class RetrievalGuard extends EventEmitter {
123
154
  }
124
155
  }
125
156
 
157
+ /**
158
+ * Text of a retrieval result for injection scanning.
159
+ * @param {object|string} result
160
+ * @returns {string}
161
+ */
162
+ function documentText(result) {
163
+ if (typeof result === 'string') return result;
164
+ if (!result || typeof result !== 'object') return '';
165
+ const doc = result.doc && typeof result.doc === 'object' ? result.doc : {};
166
+ return String(
167
+ result.content ||
168
+ result.text ||
169
+ result.pageContent ||
170
+ doc.content ||
171
+ doc.text ||
172
+ '',
173
+ );
174
+ }
175
+
126
176
  module.exports = { RetrievalGuard, DEFAULT_CONFIG };
package/src/index.js CHANGED
@@ -13,6 +13,7 @@ const { normalizeConfig } = require('./config/normalize-config');
13
13
 
14
14
  // Plugin system
15
15
  const pluginRegistry = require('./core/plugin-registry');
16
+ const { validatePlugin } = require('./core/validate-plugin');
16
17
 
17
18
  // Utilities
18
19
  const logger = require('./utils/logger');
@@ -49,6 +50,8 @@ const {
49
50
  scoreAnswer,
50
51
  computeBLEU,
51
52
  computeROUGE,
53
+ checkNumericConsistency,
54
+ extractNumbers,
52
55
  } = require('./evaluate/scoring');
53
56
  const { computeFaithfulness } = require('./evaluate/faithfulness');
54
57
  const { computeAnswerRelevance } = require('./evaluate/relevance');
@@ -57,6 +60,7 @@ const {
57
60
  computeContextRecall,
58
61
  } = require('./evaluate/context-metrics');
59
62
  const { computeGroundedness } = require('./evaluate/groundedness');
63
+ const { computeSemanticSimilarity } = require('./evaluate/semantic-similarity');
60
64
  const { PipelineEvaluator } = require('./evaluate/pipeline-evaluator');
61
65
 
62
66
  // Chunking
@@ -64,6 +68,10 @@ const { ChunkingEngine } = require('./chunking/chunking-engine');
64
68
 
65
69
  // Citation & Grounding
66
70
  const { CitationTracker } = require('./citation/citation-tracker');
71
+ const { checkAnswer, normalizeSources } = require('./grounding/check-answer');
72
+ const { createAiSdkAdapter } = require('./adapters/ai-sdk');
73
+ const { MemoryCache } = require('./cache/memory-cache');
74
+ const { RedisCache } = require('./cache/redis-cache');
67
75
  const {
68
76
  mapSentenceToSources,
69
77
  buildIDFWeights,
@@ -74,9 +82,11 @@ const { detectHallucinations } = require('./citation/hallucination-detector');
74
82
  const { BM25Search } = require('./retrieval/bm25-search');
75
83
  const { reciprocalRankFusion } = require('./retrieval/rank-fusion');
76
84
  const { HybridRetriever } = require('./retrieval/hybrid-retriever');
85
+ const { FileVectorStore } = require('./retrieval/file-vector-store');
77
86
 
78
87
  // Guardrails
79
88
  const { PreRetrievalGuard } = require('./guardrails/pre-retrieval-guard');
89
+ const { detectInjection } = require('./guardrails/injection-detector');
80
90
  const { RetrievalGuard } = require('./guardrails/retrieval-guard');
81
91
  const { PostGenerationGuard } = require('./guardrails/post-generation-guard');
82
92
  const { GuardrailsPipeline } = require('./guardrails/guardrails-pipeline');
@@ -88,7 +98,7 @@ const { SelfCritiqueChecker } = require('./agentic/self-critique');
88
98
  const { AgenticPipeline } = require('./agentic/agentic-pipeline');
89
99
 
90
100
  // Cost Management
91
- const { CostCalculator } = require('./cost/cost-calculator');
101
+ const { CostCalculator, PRICING_METADATA } = require('./cost/cost-calculator');
92
102
  const { CostTracker } = require('./cost/cost-tracker');
93
103
  const { TokenBudget } = require('./cost/token-budget');
94
104
 
@@ -184,6 +194,7 @@ module.exports = {
184
194
 
185
195
  // Plugin system
186
196
  pluginRegistry,
197
+ validatePlugin,
187
198
 
188
199
  // Utilities
189
200
  logger,
@@ -218,7 +229,10 @@ module.exports = {
218
229
  scoreAnswer,
219
230
  computeBLEU,
220
231
  computeROUGE,
232
+ checkNumericConsistency,
233
+ extractNumbers,
221
234
  computeFaithfulness,
235
+ computeSemanticSimilarity,
222
236
  computeAnswerRelevance,
223
237
  computeContextPrecision,
224
238
  computeContextRecall,
@@ -230,6 +244,11 @@ module.exports = {
230
244
 
231
245
  // Citation & Grounding
232
246
  CitationTracker,
247
+ checkAnswer,
248
+ normalizeSources,
249
+ createAiSdkAdapter,
250
+ MemoryCache,
251
+ RedisCache,
233
252
  mapSentenceToSources,
234
253
  buildIDFWeights,
235
254
  detectHallucinations,
@@ -238,9 +257,11 @@ module.exports = {
238
257
  BM25Search,
239
258
  reciprocalRankFusion,
240
259
  HybridRetriever,
260
+ FileVectorStore,
241
261
 
242
262
  // Guardrails
243
263
  PreRetrievalGuard,
264
+ detectInjection,
244
265
  RetrievalGuard,
245
266
  PostGenerationGuard,
246
267
  GuardrailsPipeline,
@@ -253,6 +274,7 @@ module.exports = {
253
274
 
254
275
  // Cost Management
255
276
  CostCalculator,
277
+ PRICING_METADATA,
256
278
  CostTracker,
257
279
  TokenBudget,
258
280
 
@@ -330,3 +352,38 @@ module.exports = {
330
352
  DevServer,
331
353
  createDevServer,
332
354
  };
355
+
356
+ // Simulated or incomplete modules now live in the ./experimental entry
357
+ // (src/experimental.js). Their main-entry exports keep working but emit a
358
+ // one-time DeprecationWarning when used, and will be removed from the main
359
+ // entry in the next major release (SEMVER.md, "Deprecation process").
360
+ const experimental = require('./experimental');
361
+ const { deprecateClass } = require('./utils/deprecate');
362
+
363
+ for (const name of Object.keys(experimental)) {
364
+ if (!(name in module.exports)) continue;
365
+ module.exports[name] = deprecateClass(experimental[name], {
366
+ name,
367
+ message:
368
+ `${name} is experimental (simulated or incomplete) and is deprecated in ` +
369
+ `the main entry. Import it from "@devilsdev/rag-pipeline-utils/experimental"; ` +
370
+ 'it will be removed from the main entry in the next major release.',
371
+ });
372
+ }
373
+
374
+ // The ./testing build entry reads its module from here, so it shares the
375
+ // main bundle's classes (MemoryRetriever, createRagPipeline). A getter, so
376
+ // importing the package does not load the testing helpers.
377
+ Object.defineProperty(
378
+ module.exports,
379
+ Symbol.for('@devilsdev/rag-pipeline-utils/testing'),
380
+ { get: () => require('./testing'), enumerable: false }, // eslint-disable-line global-require
381
+ );
382
+
383
+ // The ./experimental build entry reads the unwrapped classes from here, so
384
+ // both entries share one copy of each class (non-enumerable: not a named export).
385
+ Object.defineProperty(
386
+ module.exports,
387
+ Symbol.for('@devilsdev/rag-pipeline-utils/experimental'),
388
+ { value: experimental, enumerable: false },
389
+ );