@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,79 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Semantic similarity between an answer and a reference answer, from the
|
|
5
|
+
* cosine similarity of their embeddings. Unlike BLEU and ROUGE it does not
|
|
6
|
+
* penalise a correct answer that is worded differently.
|
|
7
|
+
*
|
|
8
|
+
* Embeddings capture topic and meaning, not facts: "twenty days" and
|
|
9
|
+
* "thirty days" score as very similar. Use it next to the grounding checks
|
|
10
|
+
* (computeFaithfulness, checkAnswer), not instead of them.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
function cosine(a, b) {
|
|
14
|
+
if (
|
|
15
|
+
!Array.isArray(a) ||
|
|
16
|
+
!Array.isArray(b) ||
|
|
17
|
+
a.length !== b.length ||
|
|
18
|
+
a.length === 0
|
|
19
|
+
) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Embeddings must be non-empty arrays of the same length (got ${a && a.length} and ${b && b.length})`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
let dot = 0;
|
|
25
|
+
let na = 0;
|
|
26
|
+
let nb = 0;
|
|
27
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
28
|
+
dot += a[i] * b[i];
|
|
29
|
+
na += a[i] * a[i];
|
|
30
|
+
nb += b[i] * b[i];
|
|
31
|
+
}
|
|
32
|
+
if (na === 0 || nb === 0) return 0;
|
|
33
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function embedText(embedder, text, signal) {
|
|
37
|
+
const options = signal ? { signal } : {};
|
|
38
|
+
if (typeof embedder.embedQuery === 'function') {
|
|
39
|
+
return embedder.embedQuery(text, options);
|
|
40
|
+
}
|
|
41
|
+
return embedder.embed(text, options);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} answer - The generated answer
|
|
46
|
+
* @param {string} reference - The reference (ground-truth) answer
|
|
47
|
+
* @param {Object} options
|
|
48
|
+
* @param {Object} options.embedder - Any embedder with embed() or embedQuery()
|
|
49
|
+
* (a built-in connector, LocalEmbedder fitted to your corpus, an AI SDK
|
|
50
|
+
* adapter, HashEmbedder from /testing)
|
|
51
|
+
* @param {AbortSignal} [options.signal]
|
|
52
|
+
* @returns {Promise<number>} Cosine similarity clamped to 0..1
|
|
53
|
+
*/
|
|
54
|
+
async function computeSemanticSimilarity(
|
|
55
|
+
answer,
|
|
56
|
+
reference,
|
|
57
|
+
{ embedder, signal } = {},
|
|
58
|
+
) {
|
|
59
|
+
if (typeof answer !== 'string' || typeof reference !== 'string') {
|
|
60
|
+
throw new TypeError('answer and reference must be strings');
|
|
61
|
+
}
|
|
62
|
+
if (
|
|
63
|
+
!embedder ||
|
|
64
|
+
(typeof embedder.embed !== 'function' &&
|
|
65
|
+
typeof embedder.embedQuery !== 'function')
|
|
66
|
+
) {
|
|
67
|
+
throw new TypeError(
|
|
68
|
+
'computeSemanticSimilarity needs an embedder with embed() or embedQuery()',
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (answer.trim() === '' || reference.trim() === '') return 0;
|
|
72
|
+
const [a, b] = await Promise.all([
|
|
73
|
+
embedText(embedder, answer, signal),
|
|
74
|
+
embedText(embedder, reference, signal),
|
|
75
|
+
]);
|
|
76
|
+
return Math.min(1, Math.max(0, cosine(a, b)));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { computeSemanticSimilarity };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @devilsdev/rag-pipeline-utils/experimental
|
|
5
|
+
*
|
|
6
|
+
* Modules that are simulated, incomplete or not safe for production. They
|
|
7
|
+
* are kept for experimentation and are NOT covered by the semver guarantees
|
|
8
|
+
* in SEMVER.md. See docs-site/docs/Experimental.md for what each one really
|
|
9
|
+
* does. Do not use them for authentication, tenant isolation, compliance or
|
|
10
|
+
* plugin isolation.
|
|
11
|
+
*
|
|
12
|
+
* - Model training / federated learning: metrics and weights are random.
|
|
13
|
+
* - MultiModalProcessor: returns random embeddings; only stores content.
|
|
14
|
+
* - AdaptiveRetrievalEngine: in-memory placeholder strategy.
|
|
15
|
+
* - SSO providers: OAuth2/OIDC use PKCE and OIDC validates id_tokens, but
|
|
16
|
+
* the flows are lightly tested; SAML refuses to authenticate (no XML
|
|
17
|
+
* signature verification); Active Directory is a development stub. A
|
|
18
|
+
* development bypass (any code accepted, fixed identity) exists only
|
|
19
|
+
* with `devBypass: true` outside production.
|
|
20
|
+
* - TenantManager / ResourceMonitor: usage figures are random.
|
|
21
|
+
* - DataGovernanceManager: random classification; erasure deletes nothing.
|
|
22
|
+
* - PluginSandbox: execute() is not functional; plugins are not isolated.
|
|
23
|
+
* - PluginCertification: automated checks return fixed scores (use
|
|
24
|
+
* `rag-pipeline hub certify check` for computed scores).
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const { MultiModalProcessor } = require('./ai/multimodal');
|
|
28
|
+
const { AdaptiveRetrievalEngine } = require('./ai/retrieval-engine');
|
|
29
|
+
const {
|
|
30
|
+
FederatedLearningCoordinator,
|
|
31
|
+
} = require('./ai/federation/federated-learning-coordinator');
|
|
32
|
+
const {
|
|
33
|
+
ModelTrainingOrchestrator,
|
|
34
|
+
} = require('./ai/training/model-training-orchestrator');
|
|
35
|
+
const { DataGovernanceManager } = require('./enterprise/data-governance');
|
|
36
|
+
const {
|
|
37
|
+
TenantManager,
|
|
38
|
+
ResourceMonitor,
|
|
39
|
+
} = require('./enterprise/multi-tenancy');
|
|
40
|
+
const {
|
|
41
|
+
SSOManager,
|
|
42
|
+
SAMLProvider,
|
|
43
|
+
OAuth2Provider,
|
|
44
|
+
ActiveDirectoryProvider,
|
|
45
|
+
OIDCProvider,
|
|
46
|
+
} = require('./enterprise/sso-integration');
|
|
47
|
+
const { PluginSandbox } = require('./security/plugin-sandbox');
|
|
48
|
+
const { PluginCertification } = require('./ecosystem/plugin-certification');
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
// AI/ML
|
|
52
|
+
MultiModalProcessor,
|
|
53
|
+
AdaptiveRetrievalEngine,
|
|
54
|
+
FederatedLearningCoordinator,
|
|
55
|
+
ModelTrainingOrchestrator,
|
|
56
|
+
|
|
57
|
+
// Enterprise
|
|
58
|
+
DataGovernanceManager,
|
|
59
|
+
DataGovernance: DataGovernanceManager,
|
|
60
|
+
TenantManager,
|
|
61
|
+
ResourceMonitor,
|
|
62
|
+
SSOManager,
|
|
63
|
+
SAMLProvider,
|
|
64
|
+
OAuth2Provider,
|
|
65
|
+
ActiveDirectoryProvider,
|
|
66
|
+
OIDCProvider,
|
|
67
|
+
|
|
68
|
+
// Security / ecosystem
|
|
69
|
+
PluginSandbox,
|
|
70
|
+
PluginCertification,
|
|
71
|
+
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Check an answer that any framework produced (LangChain, LlamaIndex, Mastra,
|
|
5
|
+
* the AI SDK, your own code) against the sources it was given: citations
|
|
6
|
+
* per sentence, groundedness, unsupported sentences and evaluation scores,
|
|
7
|
+
* without using createRagPipeline().
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { CitationTracker } = require('../citation/citation-tracker');
|
|
11
|
+
const { PipelineEvaluator } = require('../evaluate/pipeline-evaluator');
|
|
12
|
+
const {
|
|
13
|
+
computeSemanticSimilarity,
|
|
14
|
+
} = require('../evaluate/semantic-similarity');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Text of one source in the shapes common frameworks use.
|
|
18
|
+
* @private
|
|
19
|
+
*/
|
|
20
|
+
function sourceText(source) {
|
|
21
|
+
if (source == null) return '';
|
|
22
|
+
if (typeof source === 'string') return source;
|
|
23
|
+
if (typeof source.content === 'string') return source.content; // this library
|
|
24
|
+
if (typeof source.pageContent === 'string') return source.pageContent; // LangChain Document
|
|
25
|
+
if (typeof source.text === 'string') return source.text; // LlamaIndex node, plain
|
|
26
|
+
if (source.node) return sourceText(source.node); // LlamaIndex NodeWithScore
|
|
27
|
+
if (typeof source.getContent === 'function')
|
|
28
|
+
return String(source.getContent());
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* An id for a source: its own id when it has one, otherwise its position.
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
function sourceId(source, index) {
|
|
37
|
+
const candidates = [
|
|
38
|
+
source && source.id,
|
|
39
|
+
source && source.id_,
|
|
40
|
+
source && source.node && (source.node.id_ || source.node.id),
|
|
41
|
+
source && source.metadata && (source.metadata.id || source.metadata.source),
|
|
42
|
+
];
|
|
43
|
+
const id = candidates.find(
|
|
44
|
+
(c) => typeof c === 'string' || typeof c === 'number',
|
|
45
|
+
);
|
|
46
|
+
return id !== undefined ? String(id) : String(index + 1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Normalize sources to `{ id, content, metadata }`.
|
|
51
|
+
*
|
|
52
|
+
* Accepts strings, `{ id, content }` (this library), LangChain Documents
|
|
53
|
+
* (`{ pageContent, metadata }`), LlamaIndex nodes (`{ text }`) and
|
|
54
|
+
* `NodeWithScore` (`{ node, score }`).
|
|
55
|
+
*
|
|
56
|
+
* @param {Array<string|Object>} sources
|
|
57
|
+
* @returns {Array<{id: string, content: string, metadata: Object}>}
|
|
58
|
+
*/
|
|
59
|
+
function normalizeSources(sources) {
|
|
60
|
+
if (!Array.isArray(sources)) {
|
|
61
|
+
throw new TypeError('sources must be an array');
|
|
62
|
+
}
|
|
63
|
+
return sources
|
|
64
|
+
.map((source, index) => ({
|
|
65
|
+
id: sourceId(source, index),
|
|
66
|
+
content: sourceText(source),
|
|
67
|
+
metadata:
|
|
68
|
+
(source &&
|
|
69
|
+
(source.metadata || (source.node && source.node.metadata))) ||
|
|
70
|
+
{},
|
|
71
|
+
}))
|
|
72
|
+
.filter((s) => s.content.trim() !== '');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @typedef {Object} AnswerCheck
|
|
77
|
+
* @property {boolean} grounded - Every sentence is supported by a source
|
|
78
|
+
* @property {number} groundedness - Share of supported sentences, 0..1
|
|
79
|
+
* @property {string[]} unsupported - Sentences no source supports (this
|
|
80
|
+
* includes a sentence whose numbers do not match its source)
|
|
81
|
+
* @property {Array<{sentence: string, sources: Array}>} citations - Sources
|
|
82
|
+
* supporting each sentence
|
|
83
|
+
* @property {{scores: Object, details: Object, metadata: Object}|undefined} evaluation
|
|
84
|
+
* - Faithfulness, relevance, context precision (and context recall when a
|
|
85
|
+
* referenceAnswer is given); only when `evaluate` is not false
|
|
86
|
+
* @property {Array<{id: string, content: string, metadata: Object}>} sources
|
|
87
|
+
* - The sources as they were checked
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check an answer against the sources it was generated from.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* // A LangChain chain's output
|
|
95
|
+
* const check = await checkAnswer({
|
|
96
|
+
* question: input,
|
|
97
|
+
* answer: result.answer,
|
|
98
|
+
* sources: result.context, // LangChain Documents
|
|
99
|
+
* });
|
|
100
|
+
* if (!check.grounded) console.warn(check.unsupported);
|
|
101
|
+
*
|
|
102
|
+
* @param {Object} params
|
|
103
|
+
* @param {string} params.answer - The generated answer
|
|
104
|
+
* @param {Array<string|Object>} params.sources - Documents the answer was
|
|
105
|
+
* generated from (see normalizeSources for accepted shapes)
|
|
106
|
+
* @param {string} [params.question] - The question; needed for relevance
|
|
107
|
+
* @param {string} [params.referenceAnswer] - Ground truth, for context recall
|
|
108
|
+
* (and semantic similarity when an embedder is given)
|
|
109
|
+
* @param {Object} [params.embedder] - With a referenceAnswer, adds
|
|
110
|
+
* `evaluation.scores.semanticSimilarity` (computeSemanticSimilarity)
|
|
111
|
+
* @param {boolean} [params.evaluate=true] - Also compute evaluation scores
|
|
112
|
+
* @returns {Promise<AnswerCheck>}
|
|
113
|
+
*/
|
|
114
|
+
async function checkAnswer({
|
|
115
|
+
answer,
|
|
116
|
+
sources,
|
|
117
|
+
question,
|
|
118
|
+
referenceAnswer,
|
|
119
|
+
embedder,
|
|
120
|
+
evaluate = true,
|
|
121
|
+
} = {}) {
|
|
122
|
+
if (typeof answer !== 'string') {
|
|
123
|
+
throw new TypeError('answer must be a string');
|
|
124
|
+
}
|
|
125
|
+
const docs = normalizeSources(sources);
|
|
126
|
+
|
|
127
|
+
// Models sometimes return nothing; that is an ungrounded answer, not an
|
|
128
|
+
// error. (CitationTracker rejects empty text.)
|
|
129
|
+
if (answer.trim() === '') {
|
|
130
|
+
return {
|
|
131
|
+
grounded: false,
|
|
132
|
+
groundedness: 0,
|
|
133
|
+
unsupported: [],
|
|
134
|
+
citations: [],
|
|
135
|
+
evaluation: undefined,
|
|
136
|
+
sources: docs,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const tracked = new CitationTracker().track(answer, docs);
|
|
141
|
+
const sentences =
|
|
142
|
+
(tracked.hallucinationReport && tracked.hallucinationReport.sentences) ||
|
|
143
|
+
[];
|
|
144
|
+
const unsupported = sentences
|
|
145
|
+
.filter((s) => s.classification !== 'grounded')
|
|
146
|
+
.map((s) => s.text);
|
|
147
|
+
|
|
148
|
+
let evaluation;
|
|
149
|
+
if (evaluate) {
|
|
150
|
+
evaluation = await new PipelineEvaluator().evaluate(
|
|
151
|
+
{ query: question || '', answer, results: docs },
|
|
152
|
+
referenceAnswer ? { referenceAnswer } : {},
|
|
153
|
+
);
|
|
154
|
+
if (referenceAnswer && embedder) {
|
|
155
|
+
evaluation.scores.semanticSimilarity = await computeSemanticSimilarity(
|
|
156
|
+
answer,
|
|
157
|
+
referenceAnswer,
|
|
158
|
+
{ embedder },
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
grounded: sentences.length > 0 && unsupported.length === 0,
|
|
165
|
+
groundedness: tracked.groundednessScore,
|
|
166
|
+
unsupported,
|
|
167
|
+
citations: tracked.citations,
|
|
168
|
+
evaluation,
|
|
169
|
+
sources: docs,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = { checkAnswer, normalizeSources };
|
|
@@ -16,6 +16,20 @@ const DEFAULT_CONFIG = {
|
|
|
16
16
|
strict: false, // If true, block on any guard failure; if false, warn and continue
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Reduce removed documents to their ids so filtered-out (possibly
|
|
21
|
+
* access-controlled) content is never returned to the caller.
|
|
22
|
+
*
|
|
23
|
+
* @param {{results: Array, removed: Array, metadata: object}} filterOutput
|
|
24
|
+
* @returns {{results: Array, removed: Array<{id: *}>, metadata: object}}
|
|
25
|
+
*/
|
|
26
|
+
function summarizeFilter(filterOutput) {
|
|
27
|
+
return {
|
|
28
|
+
...filterOutput,
|
|
29
|
+
removed: filterOutput.removed.map((doc) => ({ id: doc.id })),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
/**
|
|
20
34
|
* Pipeline wrapper that applies all three guardrail layers (pre-retrieval,
|
|
21
35
|
* retrieval, and post-generation) around an existing RAG pipeline. In strict
|
|
@@ -92,21 +106,49 @@ class GuardrailsPipeline extends EventEmitter {
|
|
|
92
106
|
query = guardrails.preRetrieval.sanitizedQuery;
|
|
93
107
|
}
|
|
94
108
|
|
|
95
|
-
// 2. Run the underlying pipeline with (possibly sanitized) query
|
|
109
|
+
// 2. Run the underlying pipeline with (possibly sanitized) query. The
|
|
110
|
+
// retrieval guard is passed in as options.filterResults so it runs
|
|
111
|
+
// between retrieval and generation (supported by createRagPipeline).
|
|
112
|
+
let preGenerationFilter = null;
|
|
113
|
+
const filterOptions = this.retrievalGuard
|
|
114
|
+
? {
|
|
115
|
+
filterResults: (docs) => {
|
|
116
|
+
preGenerationFilter = this.retrievalGuard.filter(
|
|
117
|
+
docs,
|
|
118
|
+
input.context,
|
|
119
|
+
);
|
|
120
|
+
return preGenerationFilter.results;
|
|
121
|
+
},
|
|
122
|
+
}
|
|
123
|
+
: {};
|
|
96
124
|
const result = await this.pipeline.run({
|
|
97
125
|
...input,
|
|
98
126
|
query,
|
|
99
|
-
options: { ...input.options, citations: true },
|
|
127
|
+
options: { ...input.options, citations: true, ...filterOptions },
|
|
100
128
|
});
|
|
101
129
|
|
|
102
130
|
// 3. Retrieval guard on results
|
|
103
|
-
if (this.retrievalGuard &&
|
|
104
|
-
guardrails.retrieval =
|
|
105
|
-
|
|
106
|
-
|
|
131
|
+
if (this.retrievalGuard && preGenerationFilter) {
|
|
132
|
+
guardrails.retrieval = summarizeFilter(preGenerationFilter);
|
|
133
|
+
this.emit('retrieval', guardrails.retrieval);
|
|
134
|
+
} else if (this.retrievalGuard && result.results) {
|
|
135
|
+
// The wrapped pipeline ignored options.filterResults, so it generated
|
|
136
|
+
// from unfiltered documents. Filtering now only fixes result.results;
|
|
137
|
+
// if ACL removed anything the answer may contain restricted content.
|
|
138
|
+
guardrails.retrieval = summarizeFilter(
|
|
139
|
+
this.retrievalGuard.filter(result.results, input.context),
|
|
107
140
|
);
|
|
108
141
|
this.emit('retrieval', guardrails.retrieval);
|
|
109
142
|
result.results = guardrails.retrieval.results;
|
|
143
|
+
|
|
144
|
+
if (guardrails.retrieval.metadata.reasons.acl) {
|
|
145
|
+
return {
|
|
146
|
+
success: false,
|
|
147
|
+
error:
|
|
148
|
+
'Retrieval guard removed access-controlled documents after generation; answer withheld',
|
|
149
|
+
guardrails,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
110
152
|
}
|
|
111
153
|
|
|
112
154
|
// 4. Post-generation guard on answer
|
|
@@ -118,19 +160,39 @@ class GuardrailsPipeline extends EventEmitter {
|
|
|
118
160
|
this.emit('postGeneration', guardrails.postGeneration);
|
|
119
161
|
|
|
120
162
|
if (!guardrails.postGeneration.safe && this.config.strict) {
|
|
163
|
+
// Do not spread result: it carries the raw answer and citations.
|
|
121
164
|
return {
|
|
122
|
-
...result,
|
|
123
165
|
success: false,
|
|
124
166
|
error: 'Post-generation guard flagged output',
|
|
125
167
|
guardrails,
|
|
126
168
|
};
|
|
127
169
|
}
|
|
128
170
|
result.answer = guardrails.postGeneration.sanitizedOutput;
|
|
171
|
+
this._redactDeep(result.citations);
|
|
172
|
+
this._redactDeep(result.evaluation);
|
|
129
173
|
}
|
|
130
174
|
|
|
131
175
|
result.guardrails = guardrails;
|
|
132
176
|
return result;
|
|
133
177
|
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Citations and evaluation details copy sentences from the raw answer, so
|
|
181
|
+
* scrub every string in them with the same PII patterns as the answer.
|
|
182
|
+
*
|
|
183
|
+
* @param {*} value - Object, array or string to redact in place
|
|
184
|
+
* @returns {*} The redacted value
|
|
185
|
+
* @private
|
|
186
|
+
*/
|
|
187
|
+
_redactDeep(value) {
|
|
188
|
+
if (typeof value === 'string') return this.postGuard.redact(value);
|
|
189
|
+
if (value && typeof value === 'object') {
|
|
190
|
+
for (const key of Object.keys(value)) {
|
|
191
|
+
value[key] = this._redactDeep(value[key]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return value;
|
|
195
|
+
}
|
|
134
196
|
}
|
|
135
197
|
|
|
136
198
|
module.exports = { GuardrailsPipeline, DEFAULT_CONFIG };
|