@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,215 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Adapter for the Vercel AI SDK (`ai` package): use an AI SDK embedding
5
+ * model and language model as this library's `embedder` and `llm`.
6
+ *
7
+ * The AI SDK is not a dependency. You pass in the functions you already
8
+ * import from it, so any AI SDK version from 5 on works, from ESM or
9
+ * CommonJS, and this package keeps supporting Node 18 (AI SDK 7 needs Node
10
+ * 22 and is ESM-only).
11
+ */
12
+
13
+ const { buildRagPrompt } = require('../connectors/rag-prompt');
14
+ const { componentCacheKey } = require('../cache/component-keys');
15
+
16
+ /** Identity of an AI SDK model object (provider and model id). */
17
+ const modelId = (model) =>
18
+ `${model.provider || 'unknown'}:${model.modelId || model.modelName || 'unknown'}`;
19
+
20
+ /**
21
+ * @typedef {Object} AiSdkFunctions
22
+ * @property {Function} [embed] - `embed` from 'ai' (needed for an embedder)
23
+ * @property {Function} [embedMany] - `embedMany` from 'ai' (batch embedding)
24
+ * @property {Function} [generateText] - `generateText` from 'ai' (needed for an llm)
25
+ * @property {Function} [streamText] - `streamText` from 'ai' (streaming)
26
+ */
27
+
28
+ function requireFunction(ai, name, purpose) {
29
+ if (typeof ai[name] !== 'function') {
30
+ throw new Error(
31
+ `createAiSdkAdapter: ${purpose} needs ai.${name}. Pass the AI SDK functions, e.g. import { ${name} } from "ai".`,
32
+ );
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Options for every generateText / streamText call. The RAG system prompt
38
+ * (retrieved text is untrusted reference data, not instructions) is passed
39
+ * as both `instructions` (AI SDK 7) and `system` (AI SDK 5 and 6; 7 reads
40
+ * `instructions ?? system`), so it reaches the model on every version.
41
+ */
42
+ function textCallOptions(model, query, docs, settings, signal, systemPrompt) {
43
+ const { system, user } = buildRagPrompt(query, docs, { systemPrompt });
44
+ return {
45
+ ...settings,
46
+ model,
47
+ ...(system && { system, instructions: system }),
48
+ prompt: user,
49
+ ...(signal && { abortSignal: signal }),
50
+ };
51
+ }
52
+
53
+ /** Per-call settings the pipeline may pass through to the model. */
54
+ const CALL_SETTINGS = [
55
+ 'temperature',
56
+ 'maxOutputTokens',
57
+ 'topP',
58
+ 'topK',
59
+ 'presencePenalty',
60
+ 'frequencyPenalty',
61
+ 'seed',
62
+ 'stopSequences',
63
+ 'maxRetries',
64
+ ];
65
+
66
+ function pickSettings(options) {
67
+ const out = {};
68
+ for (const key of CALL_SETTINGS) {
69
+ if (options[key] !== undefined) out[key] = options[key];
70
+ }
71
+ return out;
72
+ }
73
+
74
+ /**
75
+ * Wrap AI SDK models as an embedder and an llm for createRagPipeline().
76
+ *
77
+ * @example
78
+ * import { embed, embedMany, generateText, streamText } from "ai";
79
+ * import { openai } from "@ai-sdk/openai";
80
+ * import { createAiSdkAdapter, createRagPipeline } from "@devilsdev/rag-pipeline-utils";
81
+ *
82
+ * const { embedder, llm } = createAiSdkAdapter({
83
+ * ai: { embed, embedMany, generateText, streamText },
84
+ * embeddingModel: openai.embedding("text-embedding-3-small"),
85
+ * languageModel: openai("gpt-4o-mini"),
86
+ * });
87
+ * const pipeline = createRagPipeline({ embedder, retriever, llm });
88
+ *
89
+ * @param {Object} options
90
+ * @param {AiSdkFunctions} options.ai - Functions imported from 'ai'
91
+ * @param {Object} [options.embeddingModel] - AI SDK embedding model
92
+ * @param {Object} [options.languageModel] - AI SDK language model
93
+ * @param {Object} [options.settings] - Defaults for every text call, e.g.
94
+ * `{ temperature: 0, maxOutputTokens: 512 }`
95
+ * @param {string} [options.systemPrompt] - Replaces the built-in RAG system
96
+ * prompt
97
+ * @returns {{ embedder: Object|null, llm: Object|null }}
98
+ */
99
+ function createAiSdkAdapter(options = {}) {
100
+ const {
101
+ ai,
102
+ embeddingModel,
103
+ languageModel,
104
+ settings = {},
105
+ systemPrompt,
106
+ } = options;
107
+ if (!ai || typeof ai !== 'object') {
108
+ throw new Error(
109
+ 'createAiSdkAdapter: pass `ai`, the AI SDK functions, e.g. { embed, embedMany, generateText, streamText } from "ai".',
110
+ );
111
+ }
112
+ if (!embeddingModel && !languageModel) {
113
+ throw new Error(
114
+ 'createAiSdkAdapter: pass an embeddingModel, a languageModel, or both.',
115
+ );
116
+ }
117
+
118
+ let embedder = null;
119
+ if (embeddingModel) {
120
+ requireFunction(ai, 'embed', 'an embeddingModel');
121
+ const embedOne = async (text, { signal } = {}) => {
122
+ const { embedding } = await ai.embed({
123
+ model: embeddingModel,
124
+ value: String(text),
125
+ ...(signal && { abortSignal: signal }),
126
+ });
127
+ return embedding;
128
+ };
129
+ embedder = {
130
+ metadata: { name: 'ai-sdk-embedder', version: '1.0.0', type: 'embedder' },
131
+ getCacheKey: (role) =>
132
+ role === 'embedder'
133
+ ? componentCacheKey('ai-sdk-embed', {
134
+ model: modelId(embeddingModel),
135
+ })
136
+ : null,
137
+ embed: embedOne,
138
+ embedQuery: embedOne,
139
+ /** One vector per text; uses embedMany when it was passed in. */
140
+ async embedBatch(texts, { signal } = {}) {
141
+ if (typeof ai.embedMany !== 'function') {
142
+ const vectors = [];
143
+ for (const text of texts)
144
+ vectors.push(await embedOne(text, { signal }));
145
+ return vectors;
146
+ }
147
+ const { embeddings } = await ai.embedMany({
148
+ model: embeddingModel,
149
+ values: texts.map(String),
150
+ ...(signal && { abortSignal: signal }),
151
+ });
152
+ return embeddings;
153
+ },
154
+ };
155
+ }
156
+
157
+ let llm = null;
158
+ if (languageModel) {
159
+ requireFunction(ai, 'generateText', 'a languageModel');
160
+ llm = {
161
+ metadata: { name: 'ai-sdk-llm', version: '1.0.0', type: 'llm' },
162
+ // The AI SDK leaves an unset temperature to the provider, whose default
163
+ // is usually 1, so unset counts as non-deterministic: generation
164
+ // caching needs `settings.temperature: 0`.
165
+ config: { temperature: settings.temperature ?? 1 },
166
+ getCacheKey: (role) =>
167
+ role === 'llm'
168
+ ? componentCacheKey('ai-sdk', {
169
+ model: modelId(languageModel),
170
+ settings: JSON.stringify(settings),
171
+ systemPrompt,
172
+ })
173
+ : null,
174
+ async generate(query, docs = [], callOptions = {}) {
175
+ const { text } = await ai.generateText(
176
+ textCallOptions(
177
+ languageModel,
178
+ query,
179
+ docs,
180
+ { ...settings, ...pickSettings(callOptions) },
181
+ callOptions.signal,
182
+ systemPrompt,
183
+ ),
184
+ );
185
+ return text;
186
+ },
187
+ };
188
+ if (typeof ai.streamText === 'function') {
189
+ llm.generateStream = async function* generateStream(
190
+ query,
191
+ docs = [],
192
+ callOptions = {},
193
+ ) {
194
+ const result = ai.streamText(
195
+ textCallOptions(
196
+ languageModel,
197
+ query,
198
+ docs,
199
+ { ...settings, ...pickSettings(callOptions) },
200
+ callOptions.signal,
201
+ systemPrompt,
202
+ ),
203
+ );
204
+ for await (const token of result.textStream) {
205
+ yield { token, done: false };
206
+ }
207
+ yield { token: '', done: true };
208
+ };
209
+ }
210
+ }
211
+
212
+ return { embedder, llm };
213
+ }
214
+
215
+ module.exports = { createAiSdkAdapter };
@@ -5,6 +5,8 @@ const { DAG } = require('../dag/dag-engine');
5
5
  const { QueryPlanner } = require('./query-planner');
6
6
  const { IterativeRetriever } = require('./iterative-retriever');
7
7
  const { SelfCritiqueChecker } = require('./self-critique');
8
+ const { embedQuery, canEmbed } = require('../utils/embed-query');
9
+ const { createRunAbort } = require('../utils/run-abort');
8
10
 
9
11
  /**
10
12
  * Default configuration for the AgenticPipeline.
@@ -14,6 +16,7 @@ const DEFAULT_CONFIG = {
14
16
  maxConcurrency: 3,
15
17
  timeout: 60000,
16
18
  enablePlanning: true,
19
+ enableLlmPlanning: false,
17
20
  enableCritique: true,
18
21
  enableIterativeRetrieval: false,
19
22
  maxIterations: 3,
@@ -33,6 +36,9 @@ class AgenticPipeline extends EventEmitter {
33
36
  * @param {number} [options.maxConcurrency=3] - Max concurrent DAG node execution
34
37
  * @param {number} [options.timeout=60000] - Global timeout in ms
35
38
  * @param {boolean} [options.enablePlanning=true] - Whether to decompose queries
39
+ * @param {boolean} [options.enableLlmPlanning=false] - Ask the llm for the
40
+ * sub-queries (strictly validated, heuristic fallback); costs one extra
41
+ * llm.generate() call per run
36
42
  * @param {boolean} [options.enableCritique=true] - Whether to self-critique answers
37
43
  * @param {boolean} [options.enableIterativeRetrieval=false] - Whether to use iterative retrieval
38
44
  * @param {number} [options.maxIterations=3] - Max iterations for iterative retrieval
@@ -62,7 +68,16 @@ class AgenticPipeline extends EventEmitter {
62
68
  * @param {object} params.retriever - Retriever instance with .retrieve() method
63
69
  * @param {object} params.llm - LLM instance with .generate() method
64
70
  * @param {Array<number>} [params.queryVector] - Optional pre-computed query vector
71
+ * @param {object} [params.embedder] - Optional embedder; when given, each
72
+ * sub-query is embedded on its own instead of reusing queryVector
65
73
  * @param {object} [params.options={}] - Extra options passed to llm.generate()
74
+ * @param {AbortSignal} [params.options.signal] - Cancels the run. The run
75
+ * has its own AbortController (also aborted after `timeout`); its signal
76
+ * is passed to retriever.retrieve({ ..., signal }), embedder.embedQuery /
77
+ * embed(text, { signal }) and llm.generate(query, docs, { ...options,
78
+ * signal }), and no later stage starts once it aborts. The returned
79
+ * promise then rejects with Error('Execution timeout') or
80
+ * Error('Execution aborted').
66
81
  * @returns {Promise<{
67
82
  * success: boolean,
68
83
  * query: string,
@@ -74,7 +89,7 @@ class AgenticPipeline extends EventEmitter {
74
89
  * }>}
75
90
  * @throws {Error} If retriever or llm is missing
76
91
  */
77
- async run({ query, retriever, llm, queryVector, options = {} }) {
92
+ async run({ query, retriever, llm, queryVector, embedder, options = {} }) {
78
93
  // 1. Validate inputs
79
94
  if (!retriever || typeof retriever.retrieve !== 'function') {
80
95
  throw new Error(
@@ -92,10 +107,44 @@ class AgenticPipeline extends EventEmitter {
92
107
  );
93
108
  }
94
109
 
110
+ // One AbortController per run: aborted on timeout or when the caller's
111
+ // options.signal aborts. Its signal goes to every component call.
112
+ const runAbort = createRunAbort({
113
+ timeout: this.config.timeout,
114
+ signal: options && options.signal,
115
+ });
116
+ try {
117
+ return await runAbort.race(
118
+ this._execute(
119
+ { query, retriever, llm, queryVector, embedder, options },
120
+ runAbort,
121
+ ),
122
+ );
123
+ } finally {
124
+ runAbort.cleanup();
125
+ }
126
+ }
127
+
128
+ /**
129
+ * @param {object} params - Validated run() parameters
130
+ * @param {object} runAbort - Per-run cancellation (see utils/run-abort)
131
+ * @returns {Promise<object>}
132
+ * @private
133
+ */
134
+ async _execute(
135
+ { query, retriever, llm, queryVector, embedder, options },
136
+ runAbort,
137
+ ) {
138
+ const { signal } = runAbort;
139
+ const llmOptions = { ...(options || {}), signal };
140
+
95
141
  // Phase 1: Plan
96
142
  let subQueries;
97
143
  if (this.config.enablePlanning) {
98
- const plan = this.planner.plan(query);
144
+ const plan = this.config.enableLlmPlanning
145
+ ? await this.planner.plan(query, { llm, signal })
146
+ : this.planner.plan(query);
147
+ runAbort.throwIfAborted();
99
148
  subQueries = plan.subQueries;
100
149
  this.emit('planned', plan);
101
150
  } else {
@@ -111,19 +160,31 @@ class AgenticPipeline extends EventEmitter {
111
160
  dag.addNode(
112
161
  `retrieve-${i}`,
113
162
  async () => {
163
+ runAbort.throwIfAborted();
164
+ // queryVector belongs to the original query; a decomposed
165
+ // sub-query needs its own embedding when an embedder is available.
166
+ const needsOwnVector =
167
+ canEmbed(embedder) && (sq.subQuery !== query || !queryVector);
168
+ const subQueryVector = needsOwnVector
169
+ ? await embedQuery(embedder, sq.subQuery, { signal })
170
+ : queryVector;
171
+ runAbort.throwIfAborted();
114
172
  if (this.iterativeRetriever) {
115
173
  const result = await this.iterativeRetriever.retrieve({
116
174
  query: sq.subQuery,
117
175
  retriever,
118
176
  llm,
119
- queryVector,
177
+ queryVector: subQueryVector,
178
+ embedder,
179
+ signal,
120
180
  });
121
181
  return result.results;
122
182
  }
123
183
  return await retriever.retrieve({
124
184
  query: sq.subQuery,
125
- queryVector,
185
+ queryVector: subQueryVector,
126
186
  topK: 10,
187
+ signal,
127
188
  });
128
189
  },
129
190
  { timeout: this.config.timeout / 2, priority: sq.priority },
@@ -132,6 +193,7 @@ class AgenticPipeline extends EventEmitter {
132
193
 
133
194
  // Add merge node
134
195
  dag.addNode('merge', async (inputs) => {
196
+ runAbort.throwIfAborted();
135
197
  const allResults = Array.isArray(inputs)
136
198
  ? inputs.flat()
137
199
  : [inputs].flat();
@@ -155,8 +217,10 @@ class AgenticPipeline extends EventEmitter {
155
217
  // Add critique + generate nodes
156
218
  if (this.config.enableCritique) {
157
219
  dag.addNode('pre-generate', async (mergedResults) => {
220
+ runAbort.throwIfAborted();
158
221
  // Generate initial answer for critique
159
- const answer = await llm.generate(query, mergedResults, options);
222
+ const answer = await llm.generate(query, mergedResults, llmOptions);
223
+ runAbort.throwIfAborted();
160
224
  const critique = this.critiqueChecker.check(
161
225
  String(answer),
162
226
  mergedResults,
@@ -167,6 +231,7 @@ class AgenticPipeline extends EventEmitter {
167
231
  dag.connect('merge', 'pre-generate');
168
232
 
169
233
  dag.addNode('generate', async ({ answer, mergedResults, critique }) => {
234
+ runAbort.throwIfAborted();
170
235
  if (critique.approved) {
171
236
  return { answer, results: mergedResults, critique };
172
237
  }
@@ -178,7 +243,7 @@ class AgenticPipeline extends EventEmitter {
178
243
  const refinedAnswer = await llm.generate(
179
244
  refinedPrompt,
180
245
  mergedResults,
181
- options,
246
+ llmOptions,
182
247
  );
183
248
  return {
184
249
  answer: String(refinedAnswer),
@@ -189,7 +254,8 @@ class AgenticPipeline extends EventEmitter {
189
254
  dag.connect('pre-generate', 'generate');
190
255
  } else {
191
256
  dag.addNode('generate', async (mergedResults) => {
192
- const answer = await llm.generate(query, mergedResults, options);
257
+ runAbort.throwIfAborted();
258
+ const answer = await llm.generate(query, mergedResults, llmOptions);
193
259
  return { answer: String(answer), results: mergedResults };
194
260
  });
195
261
  dag.connect('merge', 'generate');
@@ -206,6 +272,8 @@ class AgenticPipeline extends EventEmitter {
206
272
  continueOnError: true,
207
273
  gracefulDegradation: true,
208
274
  });
275
+ // continueOnError lets nodes degrade; an abort must not look like success.
276
+ runAbort.throwIfAborted();
209
277
 
210
278
  // Extract final result from generate node
211
279
  const output = dagResult?.answer
@@ -3,6 +3,7 @@
3
3
  const { EventEmitter } = require('events');
4
4
  const { retry } = require('../utils/retry');
5
5
  const { tokenize, computeJaccardSimilarity } = require('../evaluate/scoring');
6
+ const { embedQuery, canEmbed } = require('../utils/embed-query');
6
7
 
7
8
  /**
8
9
  * Default configuration for the IterativeRetriever.
@@ -110,10 +111,26 @@ class IterativeRetriever extends EventEmitter {
110
111
  * @param {object} params.retriever - Retriever instance with .retrieve() method
111
112
  * @param {object} [params.llm] - Optional LLM for query refinement
112
113
  * @param {Array<number>} [params.queryVector] - Optional pre-computed query vector
114
+ * @param {object} [params.embedder] - Optional embedder; when given, each
115
+ * refined query is re-embedded instead of reusing queryVector
116
+ * @param {AbortSignal} [params.signal] - Cancels the loop: passed to
117
+ * retriever.retrieve({ ..., signal }), the embedder and llm.generate(...,
118
+ * { signal }); no further iteration, retry or refinement starts once it
119
+ * has aborted, and the call rejects with the signal's reason
113
120
  * @returns {Promise<{results: Array<object>, iterations: number, converged: boolean, coverage: number}>}
114
121
  * @throws {Error} If query or retriever is missing
115
122
  */
116
- async retrieve({ query, retriever, llm, queryVector }) {
123
+ async retrieve({ query, retriever, llm, queryVector, embedder, signal }) {
124
+ const throwIfAborted = () => {
125
+ if (signal && signal.aborted) {
126
+ throw signal.reason instanceof Error
127
+ ? signal.reason
128
+ : new Error('Execution aborted');
129
+ }
130
+ };
131
+ // Only add the signal when there is one, so components keep the
132
+ // argument shapes they had before.
133
+ const withSignal = (obj) => (signal ? { ...obj, signal } : obj);
117
134
  // 1. Validate inputs
118
135
  if (!query || typeof query !== 'string' || !query.trim()) {
119
136
  throw new Error(
@@ -135,13 +152,31 @@ class IterativeRetriever extends EventEmitter {
135
152
 
136
153
  // 3. Retrieval loop
137
154
  while (iterations < this.config.maxIterations) {
155
+ throwIfAborted();
138
156
  // a. Retrieve with retry
139
157
  const retrieveQuery = currentQuery;
140
- const newResults = await retry(
141
- () =>
142
- retriever.retrieve({ query: retrieveQuery, queryVector, topK: 10 }),
143
- this.config.retryOptions,
144
- );
158
+ // Without an embedder the caller's vector is all we have; it only
159
+ // matches the original query, so refined queries need re-embedding.
160
+ const retrieveVector =
161
+ canEmbed(embedder) && (retrieveQuery !== query || !queryVector)
162
+ ? await embedQuery(
163
+ embedder,
164
+ retrieveQuery,
165
+ signal ? { signal } : undefined,
166
+ )
167
+ : queryVector;
168
+ const newResults = await retry(() => {
169
+ // Do not retry (or start) a retrieval after an abort.
170
+ throwIfAborted();
171
+ return retriever.retrieve(
172
+ withSignal({
173
+ query: retrieveQuery,
174
+ queryVector: retrieveVector,
175
+ topK: 10,
176
+ }),
177
+ );
178
+ }, this.config.retryOptions);
179
+ throwIfAborted();
145
180
 
146
181
  // b. Merge and deduplicate
147
182
  const resultsArray = Array.isArray(newResults) ? newResults : [];
@@ -173,16 +208,18 @@ class IterativeRetriever extends EventEmitter {
173
208
  // Try LLM-based refinement first
174
209
  if (llm && typeof llm.generate === 'function') {
175
210
  try {
176
- refined = await llm.generate(
177
- `Given the query "${query}" and that current results don't fully cover the topic, generate a refined search query to find missing information.`,
178
- accumulatedResults,
179
- );
211
+ const prompt = `Given the query "${query}" and that current results don't fully cover the topic, generate a refined search query to find missing information.`;
212
+ refined = signal
213
+ ? await llm.generate(prompt, accumulatedResults, { signal })
214
+ : await llm.generate(prompt, accumulatedResults);
180
215
  if (refined && typeof refined === 'string' && refined.trim()) {
181
216
  refined = refined.trim();
182
217
  } else {
183
218
  refined = null;
184
219
  }
185
220
  } catch (_err) {
221
+ // An abort is not an LLM failure: stop instead of falling back.
222
+ throwIfAborted();
186
223
  // LLM failed -- fall through to heuristic
187
224
  refined = null;
188
225
  }