@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.
- package/.ragrc.schema.json +83 -192
- package/CHANGELOG.md +431 -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 +6442 -1355
- package/dist/index.d.ts +129 -2
- package/dist/index.mjs +1 -1
- package/dist/testing.cjs +2 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.mjs +5 -0
- package/dist/types/adapters/ai-sdk.d.ts +53 -0
- 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/cache/component-keys.d.ts +7 -0
- package/dist/types/cache/memory-cache.d.ts +54 -0
- package/dist/types/cache/pipeline-cache.d.ts +66 -0
- package/dist/types/cache/redis-cache.d.ts +46 -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 +97 -0
- package/dist/types/connectors/base-connector.d.ts +87 -0
- package/dist/types/connectors/cohere-connector.d.ts +94 -0
- package/dist/types/connectors/connector-utils.d.ts +110 -0
- package/dist/types/connectors/local-embedder.d.ts +95 -0
- package/dist/types/connectors/memory-retriever.d.ts +99 -0
- package/dist/types/connectors/ollama-connector.d.ts +115 -0
- package/dist/types/connectors/openai-connector.d.ts +129 -0
- package/dist/types/connectors/rag-prompt.d.ts +40 -0
- package/dist/types/core/create-pipeline.d.ts +8 -0
- package/dist/types/core/plugin-registry.d.ts +136 -0
- package/dist/types/core/stage-contracts.d.ts +36 -0
- package/dist/types/core/validate-plugin.d.ts +45 -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/evaluate/semantic-similarity.d.ts +14 -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/grounding/check-answer.d.ts +101 -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 +99 -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/testing.d.ts +168 -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 +72 -33
- package/src/adapters/ai-sdk.js +215 -0
- 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/cache/component-keys.js +38 -0
- package/src/cache/memory-cache.js +87 -0
- package/src/cache/pipeline-cache.js +260 -0
- package/src/cache/redis-cache.js +83 -0
- 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 +106 -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 +114 -49
- 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 +104 -233
- 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 +203 -25
- 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/instrument.js +83 -0
- package/src/cli/services/rag-service.js +361 -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 +602 -0
- package/src/config/component-factory.js +125 -0
- package/src/config/enhanced-ragrc-schema.js +0 -2
- package/src/config/load-config.js +29 -10
- package/src/config/load-plugin-config.js +46 -2
- package/src/config/validate-schema.js +4 -1
- package/src/connectors/anthropic-connector.js +115 -57
- package/src/connectors/base-connector.js +74 -19
- package/src/connectors/cohere-connector.js +108 -34
- package/src/connectors/connector-utils.js +302 -0
- package/src/connectors/local-embedder.js +63 -5
- package/src/connectors/memory-retriever.js +125 -14
- package/src/connectors/ollama-connector.js +112 -51
- package/src/connectors/openai-connector.js +153 -44
- package/src/connectors/rag-prompt.js +88 -0
- package/src/core/create-pipeline.js +307 -61
- package/src/core/performance/parallel-processor.js +62 -1
- package/src/core/plugin-registry.js +125 -26
- package/src/core/stage-contracts.js +36 -0
- package/src/core/validate-plugin.js +85 -0
- package/src/cost/cost-calculator.js +205 -32
- package/src/debug/execution-tracer.js +14 -4
- package/src/dev/dev-server.js +173 -11
- package/src/dev/hot-reload.js +44 -4
- 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 +716 -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/evaluate/semantic-similarity.js +79 -0
- package/src/experimental.js +71 -0
- package/src/grounding/check-answer.js +173 -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 +58 -1
- package/src/loader/csv-loader.js +3 -2
- package/src/loader/document-reader.js +91 -0
- package/src/loader/html-loader.js +3 -1
- package/src/loader/markdown-loader.js +3 -2
- 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 +241 -0
- package/src/retrieval/hybrid-retriever.js +22 -19
- package/src/security/jwt-config.js +13 -2
- package/src/security/jwt-validator.js +139 -34
- package/src/security/plugin-sandbox.js +18 -1
- package/src/security/plugin-signature-verifier.js +245 -70
- package/src/testing.js +254 -0
- package/src/utils/deprecate.js +69 -0
- package/src/utils/embed-query.js +33 -0
- package/src/utils/input-sanitizer.js +19 -4
- package/src/utils/logger.js +34 -4
- package/src/utils/run-abort.js +102 -0
- package/src/ai/federated-learning.js +0 -211
- package/src/ai/federation.js +0 -71
- package/src/ai/index.js +0 -22
- package/src/ai/model-trainer.js +0 -217
- package/src/ai/model-training.js +0 -202
- package/src/ai/multimodal/multi-modal-processor.js +0 -345
- package/src/ai/orchestrator.js +0 -65
- package/src/ai/retrieval/adaptive-retrieval-engine.js +0 -398
- package/src/cli/commands/ai-ml/adaptive-commands.js +0 -178
- package/src/cli/commands/ai-ml/federated-commands.js +0 -207
- package/src/cli/commands/ai-ml/index.js +0 -17
- package/src/cli/commands/ai-ml/multimodal-commands.js +0 -187
- package/src/cli/commands/ai-ml/shared.js +0 -51
- package/src/cli/commands/ai-ml/training-commands.js +0 -228
- package/src/cli/commands/ai-ml.js +0 -386
- package/src/cli/commands/docs.js +0 -228
- package/src/cli/commands/dx.js +0 -538
- package/src/cli/commands/wizard.js +0 -627
- package/src/cli/input-sanitization-middleware.js +0 -356
- package/src/cli/interactive-wizard.js +0 -373
- package/src/cli/templates/development.template.json +0 -64
- package/src/cli/templates/minimal.template.json +0 -33
- package/src/cli/templates/production.template.json +0 -104
- package/src/cli/templates/testing.template.json +0 -45
- package/src/cli/wizard/configuration.js +0 -148
- package/src/cli/wizard/observability.js +0 -79
- package/src/cli/wizard/performance.js +0 -76
- package/src/cli/wizard/plugin-selection.js +0 -297
- package/src/cli/wizard/preview-save.js +0 -172
- package/src/cli/wizard/project-setup.js +0 -66
- package/src/core/pipeline-factory.js +0 -59
- package/src/dag/enhanced-dag-engine.js +0 -894
- package/src/dx/index.js +0 -40
- package/src/dx/integration-templates.js +0 -662
- package/src/dx/performance-profiler.js +0 -968
- package/src/dx/realtime-debugger.js +0 -755
- package/src/dx/visual-pipeline-builder.js +0 -578
- package/src/ingest.js +0 -59
- package/src/pipeline/pipeline.js +0 -18
- package/src/query.js +0 -117
|
@@ -0,0 +1,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.
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
}
|