@devilsdev/rag-pipeline-utils 2.4.5 → 2.5.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 +345 -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 +4992 -1242
- package/dist/index.d.ts +80 -2
- package/dist/index.mjs +1 -1
- 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/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 +92 -0
- package/dist/types/connectors/base-connector.d.ts +87 -0
- package/dist/types/connectors/cohere-connector.d.ts +89 -0
- package/dist/types/connectors/connector-utils.d.ts +110 -0
- package/dist/types/connectors/local-embedder.d.ts +86 -0
- package/dist/types/connectors/memory-retriever.d.ts +91 -0
- package/dist/types/connectors/ollama-connector.d.ts +110 -0
- package/dist/types/connectors/openai-connector.d.ts +114 -0
- package/dist/types/connectors/rag-prompt.d.ts +40 -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/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/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 +87 -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/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 +53 -29
- 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/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 +101 -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 +96 -36
- 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 +72 -217
- 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 +41 -26
- 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/rag-service.js +322 -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 +617 -0
- package/src/config/component-factory.js +125 -0
- package/src/config/load-config.js +29 -10
- package/src/config/load-plugin-config.js +46 -2
- package/src/connectors/anthropic-connector.js +93 -55
- package/src/connectors/base-connector.js +74 -19
- package/src/connectors/cohere-connector.js +88 -32
- package/src/connectors/connector-utils.js +302 -0
- package/src/connectors/local-embedder.js +43 -5
- package/src/connectors/memory-retriever.js +108 -15
- package/src/connectors/ollama-connector.js +83 -49
- package/src/connectors/openai-connector.js +108 -44
- package/src/connectors/rag-prompt.js +88 -0
- package/src/core/create-pipeline.js +209 -60
- package/src/core/plugin-registry.js +87 -11
- package/src/cost/cost-calculator.js +205 -32
- package/src/dev/dev-server.js +166 -11
- package/src/dev/hot-reload.js +40 -1
- 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 +715 -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/experimental.js +71 -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 +36 -1
- package/src/loader/document-reader.js +91 -0
- 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 +212 -0
- package/src/retrieval/hybrid-retriever.js +22 -19
- package/src/security/jwt-config.js +13 -2
- package/src/security/jwt-validator.js +134 -30
- package/src/security/plugin-sandbox.js +18 -1
- package/src/security/plugin-signature-verifier.js +245 -70
- 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
|
@@ -8,6 +8,21 @@ const {
|
|
|
8
8
|
} = require('../security/plugin-signature-verifier.js');
|
|
9
9
|
const { logger } = require('../utils/logger');
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Locate the package's contracts/ directory. This file lives in src/core/ when
|
|
13
|
+
* loaded from source but is inlined into dist/index.cjs when bundled, so
|
|
14
|
+
* __dirname differs between the two layouts.
|
|
15
|
+
*
|
|
16
|
+
* @returns {string} Absolute path to the contracts directory
|
|
17
|
+
*/
|
|
18
|
+
function resolveContractsDir() {
|
|
19
|
+
const candidates = [
|
|
20
|
+
path.join(__dirname, '../../contracts'),
|
|
21
|
+
path.join(__dirname, '../contracts'),
|
|
22
|
+
];
|
|
23
|
+
return candidates.find((dir) => fs.existsSync(dir)) || candidates[0];
|
|
24
|
+
}
|
|
25
|
+
|
|
11
26
|
class PluginRegistry {
|
|
12
27
|
constructor(options = {}) {
|
|
13
28
|
this._plugins = new Map();
|
|
@@ -67,7 +82,11 @@ class PluginRegistry {
|
|
|
67
82
|
failClosed:
|
|
68
83
|
options.failClosed !== undefined ? options.failClosed : isProduction, // Fail closed in production, fail open in development
|
|
69
84
|
trustedKeysPath: options.trustedKeysPath,
|
|
85
|
+
requireContentDigest: options.requireContentDigest === true,
|
|
70
86
|
});
|
|
87
|
+
// Opt-in: when verification is enabled, refuse plugins registered
|
|
88
|
+
// without a signed manifest (default false for backward compatibility).
|
|
89
|
+
this._requireSignedPlugins = options.requireSignedPlugins === true;
|
|
71
90
|
this._loadContracts();
|
|
72
91
|
}
|
|
73
92
|
|
|
@@ -82,8 +101,8 @@ class PluginRegistry {
|
|
|
82
101
|
_loadContractSchema() {
|
|
83
102
|
try {
|
|
84
103
|
const schemaPath = path.join(
|
|
85
|
-
|
|
86
|
-
'
|
|
104
|
+
resolveContractsDir(),
|
|
105
|
+
'contract-schema.json',
|
|
87
106
|
);
|
|
88
107
|
if (fs.existsSync(schemaPath)) {
|
|
89
108
|
const schemaContent = fs.readFileSync(schemaPath, 'utf8');
|
|
@@ -256,7 +275,7 @@ class PluginRegistry {
|
|
|
256
275
|
}
|
|
257
276
|
|
|
258
277
|
_loadContracts() {
|
|
259
|
-
const contractsDir =
|
|
278
|
+
const contractsDir = resolveContractsDir();
|
|
260
279
|
|
|
261
280
|
try {
|
|
262
281
|
const contractFiles = {
|
|
@@ -396,7 +415,20 @@ class PluginRegistry {
|
|
|
396
415
|
return methods;
|
|
397
416
|
}
|
|
398
417
|
|
|
399
|
-
|
|
418
|
+
/**
|
|
419
|
+
* Register a plugin.
|
|
420
|
+
*
|
|
421
|
+
* @param {string} category - Plugin type
|
|
422
|
+
* @param {string} name - Plugin name
|
|
423
|
+
* @param {object} impl - Plugin implementation
|
|
424
|
+
* @param {object|null} [manifest] - Signed manifest; when given, its
|
|
425
|
+
* signature is verified before registration
|
|
426
|
+
* @param {object} [options]
|
|
427
|
+
* @param {object} [options.content] - Plugin content checked against the
|
|
428
|
+
* manifest's contentDigest: `{ files, baseDir, contents }`
|
|
429
|
+
* @returns {Promise<PluginRegistry>}
|
|
430
|
+
*/
|
|
431
|
+
async register(category, name, impl, manifest = null, options = {}) {
|
|
400
432
|
if (!category || typeof category !== 'string') {
|
|
401
433
|
throw new Error('Category must be a non-empty string');
|
|
402
434
|
}
|
|
@@ -412,7 +444,22 @@ class PluginRegistry {
|
|
|
412
444
|
|
|
413
445
|
// Verify plugin signature if manifest provided
|
|
414
446
|
if (manifest) {
|
|
415
|
-
await this._verifyPluginSignature(manifest, name);
|
|
447
|
+
await this._verifyPluginSignature(manifest, name, options.content);
|
|
448
|
+
} else if (
|
|
449
|
+
this._requireSignedPlugins &&
|
|
450
|
+
this.isSignatureVerificationEnabled()
|
|
451
|
+
) {
|
|
452
|
+
this._emitAuditEntry('plugin_verification_failed', {
|
|
453
|
+
pluginName: name,
|
|
454
|
+
signerId: 'unknown',
|
|
455
|
+
version: 'unknown',
|
|
456
|
+
verified: false,
|
|
457
|
+
error: 'missing signed manifest',
|
|
458
|
+
timestamp: new Date().toISOString(),
|
|
459
|
+
});
|
|
460
|
+
throw new Error(
|
|
461
|
+
`Plugin ${name} has no signed manifest and requireSignedPlugins is enabled`,
|
|
462
|
+
);
|
|
416
463
|
}
|
|
417
464
|
|
|
418
465
|
// Validate plugin against contract
|
|
@@ -428,7 +475,7 @@ class PluginRegistry {
|
|
|
428
475
|
* @param {object} manifest - Plugin manifest
|
|
429
476
|
* @param {string} pluginName - Plugin name
|
|
430
477
|
*/
|
|
431
|
-
async _verifyPluginSignature(manifest, pluginName) {
|
|
478
|
+
async _verifyPluginSignature(manifest, pluginName, content = undefined) {
|
|
432
479
|
try {
|
|
433
480
|
// Extract signature information from manifest
|
|
434
481
|
if (!manifest.signature || !manifest.signerId) {
|
|
@@ -437,12 +484,12 @@ class PluginRegistry {
|
|
|
437
484
|
);
|
|
438
485
|
}
|
|
439
486
|
|
|
487
|
+
const args = [manifest, manifest.signature, manifest.signerId];
|
|
488
|
+
if (content) {
|
|
489
|
+
args.push(content);
|
|
490
|
+
}
|
|
440
491
|
const verificationResult =
|
|
441
|
-
await this._signatureVerifier.verifyPluginSignature(
|
|
442
|
-
manifest,
|
|
443
|
-
manifest.signature,
|
|
444
|
-
manifest.signerId,
|
|
445
|
-
);
|
|
492
|
+
await this._signatureVerifier.verifyPluginSignature(...args);
|
|
446
493
|
|
|
447
494
|
if (!verificationResult.verified) {
|
|
448
495
|
throw new Error(
|
|
@@ -473,6 +520,35 @@ class PluginRegistry {
|
|
|
473
520
|
}
|
|
474
521
|
}
|
|
475
522
|
|
|
523
|
+
/**
|
|
524
|
+
* Whether plugin signature verification is enabled for this registry.
|
|
525
|
+
* @returns {boolean}
|
|
526
|
+
*/
|
|
527
|
+
isSignatureVerificationEnabled() {
|
|
528
|
+
return Boolean(this._signatureVerifier && this._signatureVerifier.enabled);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Whether plugins without a signed manifest are rejected.
|
|
533
|
+
* @returns {boolean}
|
|
534
|
+
*/
|
|
535
|
+
requiresSignedPlugins() {
|
|
536
|
+
return this._requireSignedPlugins && this.isSignatureVerificationEnabled();
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Verify a plugin's signed manifest and code before it is loaded (use this
|
|
541
|
+
* before importing plugin files, since importing executes them).
|
|
542
|
+
*
|
|
543
|
+
* @param {string} pluginName - Plugin name (for audit entries)
|
|
544
|
+
* @param {object} manifest - Signed manifest
|
|
545
|
+
* @param {object} content - `{ files, baseDir, contents }`
|
|
546
|
+
* @returns {Promise<void>} Rejects if verification fails
|
|
547
|
+
*/
|
|
548
|
+
async verifyPluginContent(pluginName, manifest, content) {
|
|
549
|
+
await this._verifyPluginSignature(manifest, pluginName, content);
|
|
550
|
+
}
|
|
551
|
+
|
|
476
552
|
/**
|
|
477
553
|
* Emit audit entry
|
|
478
554
|
* @param {string} action - Audit action
|
|
@@ -2,26 +2,134 @@
|
|
|
2
2
|
|
|
3
3
|
const { EventEmitter } = require('events');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Where the built-in prices come from and when they were recorded.
|
|
7
|
+
*
|
|
8
|
+
* The values are the providers' published list prices as copied into this
|
|
9
|
+
* library on `asOf`; they have not been re-verified since and providers
|
|
10
|
+
* change prices often. Pass `pricing` or `customPricing` to CostCalculator
|
|
11
|
+
* to use current or negotiated prices.
|
|
12
|
+
*/
|
|
13
|
+
const PRICING_METADATA = Object.freeze({
|
|
14
|
+
asOf: '2026-04-07',
|
|
15
|
+
verified: false,
|
|
16
|
+
currency: 'USD',
|
|
17
|
+
sources: Object.freeze({
|
|
18
|
+
openai: 'https://openai.com/api/pricing/',
|
|
19
|
+
anthropic: 'https://www.anthropic.com/pricing#api',
|
|
20
|
+
cohere: 'https://cohere.com/pricing',
|
|
21
|
+
}),
|
|
22
|
+
note: 'Built-in list prices, not re-verified since asOf. Override with the `pricing` or `customPricing` option.',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const OPENAI = PRICING_METADATA.sources.openai;
|
|
26
|
+
const ANTHROPIC = PRICING_METADATA.sources.anthropic;
|
|
27
|
+
const COHERE = PRICING_METADATA.sources.cohere;
|
|
28
|
+
const AS_OF = PRICING_METADATA.asOf;
|
|
29
|
+
|
|
5
30
|
/**
|
|
6
31
|
* Built-in pricing table for common LLM and embedding models.
|
|
7
|
-
* Prices are in USD per `unit` tokens.
|
|
8
|
-
*
|
|
32
|
+
* Prices are in USD per `unit` tokens. Each entry records its `source`
|
|
33
|
+
* (provider pricing page) and `asOf` date.
|
|
34
|
+
* @type {Record<string, {input: number, output: number, unit: number, source: string, asOf: string}>}
|
|
9
35
|
*/
|
|
10
36
|
const PRICING = {
|
|
11
|
-
'gpt-4': {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
'
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
'gpt-4': {
|
|
38
|
+
input: 0.03,
|
|
39
|
+
output: 0.06,
|
|
40
|
+
unit: 1000,
|
|
41
|
+
source: OPENAI,
|
|
42
|
+
asOf: AS_OF,
|
|
43
|
+
},
|
|
44
|
+
'gpt-4-turbo': {
|
|
45
|
+
input: 0.01,
|
|
46
|
+
output: 0.03,
|
|
47
|
+
unit: 1000,
|
|
48
|
+
source: OPENAI,
|
|
49
|
+
asOf: AS_OF,
|
|
50
|
+
},
|
|
51
|
+
'gpt-4o': {
|
|
52
|
+
input: 0.005,
|
|
53
|
+
output: 0.015,
|
|
54
|
+
unit: 1000,
|
|
55
|
+
source: OPENAI,
|
|
56
|
+
asOf: AS_OF,
|
|
57
|
+
},
|
|
58
|
+
'gpt-4o-mini': {
|
|
59
|
+
input: 0.00015,
|
|
60
|
+
output: 0.0006,
|
|
61
|
+
unit: 1000,
|
|
62
|
+
source: OPENAI,
|
|
63
|
+
asOf: AS_OF,
|
|
64
|
+
},
|
|
65
|
+
'gpt-3.5-turbo': {
|
|
66
|
+
input: 0.0005,
|
|
67
|
+
output: 0.0015,
|
|
68
|
+
unit: 1000,
|
|
69
|
+
source: OPENAI,
|
|
70
|
+
asOf: AS_OF,
|
|
71
|
+
},
|
|
72
|
+
'claude-3-opus': {
|
|
73
|
+
input: 0.015,
|
|
74
|
+
output: 0.075,
|
|
75
|
+
unit: 1000,
|
|
76
|
+
source: ANTHROPIC,
|
|
77
|
+
asOf: AS_OF,
|
|
78
|
+
},
|
|
79
|
+
'claude-3-sonnet': {
|
|
80
|
+
input: 0.003,
|
|
81
|
+
output: 0.015,
|
|
82
|
+
unit: 1000,
|
|
83
|
+
source: ANTHROPIC,
|
|
84
|
+
asOf: AS_OF,
|
|
85
|
+
},
|
|
86
|
+
'claude-3-haiku': {
|
|
87
|
+
input: 0.00025,
|
|
88
|
+
output: 0.00125,
|
|
89
|
+
unit: 1000,
|
|
90
|
+
source: ANTHROPIC,
|
|
91
|
+
asOf: AS_OF,
|
|
92
|
+
},
|
|
93
|
+
'claude-3.5-sonnet': {
|
|
94
|
+
input: 0.003,
|
|
95
|
+
output: 0.015,
|
|
96
|
+
unit: 1000,
|
|
97
|
+
source: ANTHROPIC,
|
|
98
|
+
asOf: AS_OF,
|
|
99
|
+
},
|
|
100
|
+
'cohere-command': {
|
|
101
|
+
input: 0.001,
|
|
102
|
+
output: 0.002,
|
|
103
|
+
unit: 1000,
|
|
104
|
+
source: COHERE,
|
|
105
|
+
asOf: AS_OF,
|
|
106
|
+
},
|
|
107
|
+
'text-embedding-3-small': {
|
|
108
|
+
input: 0.00002,
|
|
109
|
+
output: 0,
|
|
110
|
+
unit: 1000,
|
|
111
|
+
source: OPENAI,
|
|
112
|
+
asOf: AS_OF,
|
|
113
|
+
},
|
|
114
|
+
'text-embedding-3-large': {
|
|
115
|
+
input: 0.00013,
|
|
116
|
+
output: 0,
|
|
117
|
+
unit: 1000,
|
|
118
|
+
source: OPENAI,
|
|
119
|
+
asOf: AS_OF,
|
|
120
|
+
},
|
|
23
121
|
};
|
|
24
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Canonical form used for model-name matching: lower case, with '.' and '_'
|
|
125
|
+
* treated like '-' so "claude-3-5-sonnet-20241022" matches "claude-3.5-sonnet".
|
|
126
|
+
* @param {string} name
|
|
127
|
+
* @returns {string}
|
|
128
|
+
*/
|
|
129
|
+
function canonical(name) {
|
|
130
|
+
return String(name).toLowerCase().replace(/[._]/g, '-');
|
|
131
|
+
}
|
|
132
|
+
|
|
25
133
|
/**
|
|
26
134
|
* Maps token counts to cost estimates by provider and model.
|
|
27
135
|
*
|
|
@@ -33,34 +141,80 @@ const PRICING = {
|
|
|
33
141
|
class CostCalculator extends EventEmitter {
|
|
34
142
|
/**
|
|
35
143
|
* @param {object} [options]
|
|
144
|
+
* @param {Record<string, {input: number, output: number, unit?: number, source?: string, asOf?: string}>} [options.pricing]
|
|
145
|
+
* Replaces the built-in pricing table entirely (only these models are known).
|
|
36
146
|
* @param {Record<string, {input: number, output: number, unit: number}>} [options.customPricing]
|
|
37
|
-
* Additional or override model pricing entries.
|
|
147
|
+
* Additional or override model pricing entries, merged over the base table.
|
|
38
148
|
*/
|
|
39
149
|
constructor(options = {}) {
|
|
40
150
|
super();
|
|
41
|
-
|
|
151
|
+
const base = options.pricing ? {} : PRICING;
|
|
152
|
+
const overrides = {
|
|
153
|
+
...(options.pricing || {}),
|
|
154
|
+
...(options.customPricing || {}),
|
|
155
|
+
};
|
|
156
|
+
this.pricing = { ...base };
|
|
157
|
+
for (const [model, entry] of Object.entries(overrides)) {
|
|
158
|
+
this.pricing[model] = {
|
|
159
|
+
unit: 1000,
|
|
160
|
+
source: 'custom',
|
|
161
|
+
asOf: null,
|
|
162
|
+
...entry,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/** @private Models already warned about as unknown */
|
|
166
|
+
this._warnedModels = new Set();
|
|
42
167
|
}
|
|
43
168
|
|
|
44
169
|
/**
|
|
45
170
|
* Resolve a model key from the pricing table.
|
|
46
|
-
* Tries exact match (case-insensitive), then
|
|
171
|
+
* Tries an exact match (case-insensitive, '.' and '-' equivalent), then the
|
|
172
|
+
* longest known key contained in the model name (so dated snapshots such
|
|
173
|
+
* as "gpt-4o-mini-2024-07-18" resolve to "gpt-4o-mini", not "gpt-4"), then
|
|
174
|
+
* a known key that contains the model name.
|
|
47
175
|
* @param {string} model
|
|
48
176
|
* @returns {string|null} The matched pricing key or null.
|
|
49
177
|
* @private
|
|
50
178
|
*/
|
|
51
179
|
_resolveModel(model) {
|
|
52
|
-
|
|
180
|
+
if (model == null) return null;
|
|
181
|
+
const wanted = canonical(model);
|
|
53
182
|
const keys = Object.keys(this.pricing);
|
|
54
183
|
|
|
55
|
-
|
|
56
|
-
const exact = keys.find((k) => k.toLowerCase() === lower);
|
|
184
|
+
const exact = keys.find((k) => canonical(k) === wanted);
|
|
57
185
|
if (exact) return exact;
|
|
58
186
|
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
(
|
|
62
|
-
|
|
63
|
-
|
|
187
|
+
let best = null;
|
|
188
|
+
for (const k of keys) {
|
|
189
|
+
if (wanted.includes(canonical(k)) && (!best || k.length > best.length)) {
|
|
190
|
+
best = k;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (best) return best;
|
|
194
|
+
|
|
195
|
+
const containing = keys.find((k) => canonical(k).includes(wanted));
|
|
196
|
+
return containing || null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Emit a one-time warning for a model with no known price.
|
|
201
|
+
* Listeners on 'warning' receive `{ type: 'unknown-model', model, message }`;
|
|
202
|
+
* without a listener the warning goes to process.emitWarning.
|
|
203
|
+
* @param {string} model
|
|
204
|
+
* @private
|
|
205
|
+
*/
|
|
206
|
+
_warnUnknownModel(model) {
|
|
207
|
+
const key = String(model);
|
|
208
|
+
if (this._warnedModels.has(key)) return;
|
|
209
|
+
this._warnedModels.add(key);
|
|
210
|
+
const message =
|
|
211
|
+
`No pricing known for model "${key}"; cost reported as 0. ` +
|
|
212
|
+
'Pass `pricing` or `customPricing` to CostCalculator to set a price.';
|
|
213
|
+
if (this.listenerCount('warning') > 0) {
|
|
214
|
+
this.emit('warning', { type: 'unknown-model', model: key, message });
|
|
215
|
+
} else {
|
|
216
|
+
process.emitWarning(message, { code: 'RAG_UNKNOWN_MODEL_PRICING' });
|
|
217
|
+
}
|
|
64
218
|
}
|
|
65
219
|
|
|
66
220
|
/**
|
|
@@ -70,12 +224,14 @@ class CostCalculator extends EventEmitter {
|
|
|
70
224
|
* @param {number} inputTokens - Number of input / prompt tokens.
|
|
71
225
|
* @param {number} [outputTokens=0] - Number of output / completion tokens.
|
|
72
226
|
* @returns {{inputCost: number, outputCost: number, totalCost: number, model: string,
|
|
73
|
-
* inputTokens: number, outputTokens: number, currency: string,
|
|
227
|
+
* inputTokens: number, outputTokens: number, currency: string,
|
|
228
|
+
* pricingSource?: string, pricingAsOf?: string|null, warning?: string}}
|
|
74
229
|
*/
|
|
75
230
|
estimate(model, inputTokens, outputTokens = 0) {
|
|
76
231
|
const resolvedKey = this._resolveModel(model);
|
|
77
232
|
|
|
78
233
|
if (!resolvedKey) {
|
|
234
|
+
this._warnUnknownModel(model);
|
|
79
235
|
return {
|
|
80
236
|
inputCost: 0,
|
|
81
237
|
outputCost: 0,
|
|
@@ -89,8 +245,9 @@ class CostCalculator extends EventEmitter {
|
|
|
89
245
|
}
|
|
90
246
|
|
|
91
247
|
const price = this.pricing[resolvedKey];
|
|
92
|
-
const
|
|
93
|
-
const
|
|
248
|
+
const unit = price.unit || 1000;
|
|
249
|
+
const inputCost = (inputTokens / unit) * price.input;
|
|
250
|
+
const outputCost = (outputTokens / unit) * price.output;
|
|
94
251
|
const totalCost = inputCost + outputCost;
|
|
95
252
|
|
|
96
253
|
return {
|
|
@@ -101,6 +258,8 @@ class CostCalculator extends EventEmitter {
|
|
|
101
258
|
inputTokens,
|
|
102
259
|
outputTokens,
|
|
103
260
|
currency: 'USD',
|
|
261
|
+
pricingSource: price.source || 'custom',
|
|
262
|
+
pricingAsOf: price.asOf ?? null,
|
|
104
263
|
};
|
|
105
264
|
}
|
|
106
265
|
|
|
@@ -108,7 +267,7 @@ class CostCalculator extends EventEmitter {
|
|
|
108
267
|
* Register custom model pricing.
|
|
109
268
|
*
|
|
110
269
|
* @param {string} model - Model identifier.
|
|
111
|
-
* @param {{input: number, output: number, unit
|
|
270
|
+
* @param {{input: number, output: number, unit?: number, source?: string, asOf?: string}} pricing - Pricing definition.
|
|
112
271
|
*/
|
|
113
272
|
addModel(model, pricing) {
|
|
114
273
|
if (
|
|
@@ -120,7 +279,13 @@ class CostCalculator extends EventEmitter {
|
|
|
120
279
|
'Pricing must include numeric "input" and "output" fields',
|
|
121
280
|
);
|
|
122
281
|
}
|
|
123
|
-
this.pricing[model] = {
|
|
282
|
+
this.pricing[model] = {
|
|
283
|
+
unit: 1000,
|
|
284
|
+
source: 'custom',
|
|
285
|
+
asOf: null,
|
|
286
|
+
...pricing,
|
|
287
|
+
};
|
|
288
|
+
this._warnedModels.clear();
|
|
124
289
|
}
|
|
125
290
|
|
|
126
291
|
/**
|
|
@@ -135,12 +300,20 @@ class CostCalculator extends EventEmitter {
|
|
|
135
300
|
* Retrieve pricing for a specific model.
|
|
136
301
|
*
|
|
137
302
|
* @param {string} model - Model identifier (supports fuzzy resolution).
|
|
138
|
-
* @returns {{input: number, output: number, unit: number}|null}
|
|
303
|
+
* @returns {{input: number, output: number, unit: number, source?: string, asOf?: string|null}|null}
|
|
139
304
|
*/
|
|
140
305
|
getModelPricing(model) {
|
|
141
306
|
const key = this._resolveModel(model);
|
|
142
307
|
return key ? { ...this.pricing[key] } : null;
|
|
143
308
|
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Provenance of the built-in pricing table.
|
|
312
|
+
* @returns {typeof PRICING_METADATA}
|
|
313
|
+
*/
|
|
314
|
+
static getPricingMetadata() {
|
|
315
|
+
return PRICING_METADATA;
|
|
316
|
+
}
|
|
144
317
|
}
|
|
145
318
|
|
|
146
|
-
module.exports = { CostCalculator, PRICING };
|
|
319
|
+
module.exports = { CostCalculator, PRICING, PRICING_METADATA };
|