@devilsdev/rag-core 3.3.0 → 3.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.
Files changed (66) hide show
  1. package/README.md +1 -1
  2. package/dist/all.d.ts +58 -0
  3. package/dist/index.cjs +275 -15
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.mjs +1 -1
  6. package/dist/types/adapters/ai-sdk-middleware.d.ts +10 -0
  7. package/dist/types/adapters/mastra.d.ts +6 -2
  8. package/dist/types/agentic/agentic-pipeline.d.ts +1 -1
  9. package/dist/types/agentic/iterative-retriever.d.ts +1 -1
  10. package/dist/types/agentic/query-planner.d.ts +1 -1
  11. package/dist/types/agentic/self-critique.d.ts +1 -1
  12. package/dist/types/ai/federation/federated-learning-coordinator.d.ts +1 -1
  13. package/dist/types/ai/training/model-training-orchestrator.d.ts +1 -1
  14. package/dist/types/chunking/chunking-engine.d.ts +1 -1
  15. package/dist/types/citation/citation-tracker.d.ts +1 -1
  16. package/dist/types/connectors/anthropic-connector.d.ts +35 -1
  17. package/dist/types/connectors/base-connector.d.ts +1 -1
  18. package/dist/types/connectors/connector-utils.d.ts +20 -0
  19. package/dist/types/cost/cost-calculator.d.ts +1 -1
  20. package/dist/types/cost/cost-tracker.d.ts +1 -1
  21. package/dist/types/cost/token-budget.d.ts +1 -1
  22. package/dist/types/dashboard/dashboard-generator.d.ts +1 -1
  23. package/dist/types/dashboard/metrics-aggregator.d.ts +1 -1
  24. package/dist/types/debug/execution-tracer.d.ts +1 -1
  25. package/dist/types/debug/trace-inspector.d.ts +1 -1
  26. package/dist/types/dev/dev-server.d.ts +1 -1
  27. package/dist/types/dev/hot-reload.d.ts +1 -1
  28. package/dist/types/ecosystem/plugin-analytics-dashboard.d.ts +1 -1
  29. package/dist/types/ecosystem/plugin-certification.d.ts +1 -1
  30. package/dist/types/ecosystem/plugin-hub.d.ts +1 -1
  31. package/dist/types/enterprise/audit-logging.d.ts +1 -1
  32. package/dist/types/enterprise/data-governance.d.ts +1 -1
  33. package/dist/types/enterprise/multi-tenancy.d.ts +1 -1
  34. package/dist/types/enterprise/sso-integration.d.ts +1 -1
  35. package/dist/types/evaluate/pipeline-evaluator.d.ts +1 -1
  36. package/dist/types/graph/entity-extractor.d.ts +1 -1
  37. package/dist/types/graph/graph-retriever.d.ts +1 -1
  38. package/dist/types/graph/knowledge-graph.d.ts +1 -1
  39. package/dist/types/grounding/check-answer.d.ts +109 -1
  40. package/dist/types/grounding/llm-judge.d.ts +93 -0
  41. package/dist/types/grounding/provider-citations.d.ts +244 -0
  42. package/dist/types/guardrails/detectors.d.ts +88 -0
  43. package/dist/types/guardrails/evaluate-detectors.d.ts +61 -0
  44. package/dist/types/guardrails/guardrails-pipeline.d.ts +2 -9
  45. package/dist/types/guardrails/post-generation-guard.d.ts +33 -3
  46. package/dist/types/guardrails/pre-retrieval-guard.d.ts +29 -1
  47. package/dist/types/guardrails/retrieval-guard.d.ts +30 -1
  48. package/dist/types/mcp/mcp-server.d.ts +1 -1
  49. package/dist/types/observability/otel.d.ts +9 -3
  50. package/dist/types/observability/scores.d.ts +57 -0
  51. package/dist/types/reranker/cascade-reranker.d.ts +1 -1
  52. package/dist/types/reranker/embedding-reranker.d.ts +1 -1
  53. package/dist/types/reranker/scoring-reranker.d.ts +1 -1
  54. package/dist/types/retrieval/bm25-search.d.ts +1 -1
  55. package/dist/types/retrieval/hybrid-retriever.d.ts +1 -1
  56. package/dist/types/security/jwt-validator.d.ts +1 -1
  57. package/dist/types/security/plugin-sandbox.d.ts +1 -1
  58. package/dist/types/streaming/sse-adapter.d.ts +1 -1
  59. package/dist/types/streaming/stream-router.d.ts +1 -1
  60. package/dist/types/streaming/streaming-embedder.d.ts +1 -1
  61. package/dist/types/streaming/websocket-adapter.d.ts +1 -1
  62. package/dist/types/templates/scaffold.d.ts +1 -1
  63. package/dist/types/testing/contract-tests.d.ts +8 -4
  64. package/dist/types/utils/batch-processor.d.ts +1 -1
  65. package/dist/types/utils/connection-pool.d.ts +1 -1
  66. package/package.json +1 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@ Part of [@devilsdev/rag-pipeline-utils](https://www.npmjs.com/package/@devilsdev
8
8
  npm install @devilsdev/rag-core
9
9
  ```
10
10
 
11
- Exports: `createRagPipeline`, `createPipeline`, `packContext`, `createError`, `wrapError`, `ERROR_CODES`, `retry`, `withRetry`, `ChunkingEngine`, `BM25Search`, `reciprocalRankFusion`, `HybridRetriever`, `ScoringReranker`, `EmbeddingReranker`, `CascadeReranker`, `MemoryCache`, `RedisCache`, `CostCalculator`, `PRICING_METADATA`, `CostTracker`, `TokenBudget`, `createModelRouter`, `classifyQuery`, `MemoryRetriever`, `BaseConnector`.
11
+ Exports: `createRagPipeline`, `createPipeline`, `evaluationScores`, `toLangfuseScores`, `packContext`, `createError`, `wrapError`, `ERROR_CODES`, `retry`, `withRetry`, `ChunkingEngine`, `BM25Search`, `reciprocalRankFusion`, `HybridRetriever`, `ScoringReranker`, `EmbeddingReranker`, `CascadeReranker`, `MemoryCache`, `RedisCache`, `CostCalculator`, `PRICING_METADATA`, `CostTracker`, `TokenBudget`, `createModelRouter`, `classifyQuery`, `MemoryRetriever`, `BaseConnector`.
12
12
 
13
13
  Documentation: https://devilsdev.github.io/rag-pipeline-utils/
14
14
 
package/dist/all.d.ts CHANGED
@@ -50,10 +50,26 @@ export interface TelemetryOptions {
50
50
  tracer?: TelemetryTracer;
51
51
  /** The @opentelemetry/api module, for @devilsdev/rag-pipeline-utils/edge, which cannot load it itself. */
52
52
  api?: any;
53
+ /**
54
+ * The @opentelemetry/api-logs module, for evaluation events when your SDK registers its
55
+ * LoggerProvider there (before sdk-logs 3). With @opentelemetry/api 1.10+ its own logs API is used.
56
+ */
57
+ logs?: any;
58
+ /** A Logger (`emit(logRecord)`) for the gen_ai.evaluation.result events. */
59
+ logger?: { emit(logRecord: any): void };
53
60
  /** Also record the query text and the retrieved document ids and scores. Default false. */
54
61
  recordContent?: boolean;
55
62
  }
56
63
 
64
+ /** A run's OpenTelemetry ids, on its result when `telemetry` is on. */
65
+ export interface RunTelemetry {
66
+ traceId: string;
67
+ /** The `rag.pipeline.run` span. */
68
+ spanId: string;
69
+ /** The stage spans, when the stage ran (not served from the cache). */
70
+ spans: { retrieval?: string; generation?: string };
71
+ }
72
+
57
73
  /** A cache store: get / set / delete, optionally stats. */
58
74
  export interface CacheBackend {
59
75
  /** Resolves the stored value, or undefined when the key is missing or expired. */
@@ -151,6 +167,8 @@ export interface PipelineRunResult {
151
167
  timing?: { totalMs: number };
152
168
  /** The model router's decision, when the llm is a createModelRouter() and was called. */
153
169
  route?: RouteDecision;
170
+ /** The run's trace and span ids, when `telemetry` is on. */
171
+ telemetry?: RunTelemetry;
154
172
  }
155
173
 
156
174
  /** Which model a createModelRouter() chose for a run. */
@@ -202,6 +220,8 @@ export interface StreamChunk {
202
220
  timing?: StreamTiming;
203
221
  /** On the last chunk, when the llm is a createModelRouter(). */
204
222
  route?: RouteDecision;
223
+ /** On the last chunk, the run's trace and span ids, when `telemetry` is on. */
224
+ telemetry?: RunTelemetry;
205
225
  }
206
226
 
207
227
  /** A pipeline returned by {@link createRagPipeline}. */
@@ -307,6 +327,33 @@ export interface RerankerPlugin {
307
327
  rerank(query: string, documents: SearchResult[], options?: { topK?: number; signal?: AbortSignal }): SearchResult[] | Promise<SearchResult[]>;
308
328
  }
309
329
 
330
+ /** Where a guard runs a detector, with the document for retrieval guards. */
331
+ export interface DetectorContext {
332
+ stage: "query" | "document" | "output";
333
+ document?: any;
334
+ /** The request's AbortSignal, when the caller passed one to checkAsync() / filterAsync(). */
335
+ signal?: AbortSignal;
336
+ }
337
+
338
+ /** What a detector found. PII detectors must give each match's exact `text`, which is redacted. */
339
+ export interface DetectorResult {
340
+ flagged: boolean;
341
+ score?: number;
342
+ labels?: string[];
343
+ matches?: Array<{ label?: string; text: string; start?: number; end?: number }>;
344
+ }
345
+
346
+ /**
347
+ * A detector for the guards' `detectors` option: prompt injection
348
+ * (PreRetrievalGuard, RetrievalGuard) or PII (PostGenerationGuard). Async
349
+ * detectors need the guards' checkAsync() / filterAsync().
350
+ */
351
+ export interface GuardDetector {
352
+ name: string;
353
+ kind: "injection" | "pii";
354
+ detect(text: string, context: DetectorContext): DetectorResult | Promise<DetectorResult>;
355
+ }
356
+
310
357
  /** The interface of {@link pluginRegistry}. */
311
358
  export interface PluginRegistry {
312
359
  /**
@@ -428,6 +475,11 @@ export { PipelineEvaluator } from "./types/evaluate/pipeline-evaluator";
428
475
  export { ChunkingEngine } from "./types/chunking/chunking-engine";
429
476
  export { CitationTracker } from "./types/citation/citation-tracker";
430
477
  export { checkAnswer } from "./types/grounding/check-answer";
478
+ export { fromAnthropicCitations } from "./types/grounding/provider-citations";
479
+ export { fromOpenAIFileCitations } from "./types/grounding/provider-citations";
480
+ export { verifyCitations } from "./types/grounding/provider-citations";
481
+ export { evaluationScores } from "./types/observability/scores";
482
+ export { toLangfuseScores } from "./types/observability/scores";
431
483
  export { normalizeSources } from "./types/grounding/check-answer";
432
484
  export { createAiSdkAdapter } from "./types/adapters/ai-sdk";
433
485
  export { createMastraScorer } from "./types/adapters/mastra";
@@ -445,6 +497,12 @@ export { HybridRetriever } from "./types/retrieval/hybrid-retriever";
445
497
  export { FileVectorStore } from "./types/retrieval/file-vector-store";
446
498
  export { PreRetrievalGuard } from "./types/guardrails/pre-retrieval-guard";
447
499
  export { detectInjection } from "./types/guardrails/injection-detector";
500
+ export { patternInjectionDetector } from "./types/guardrails/detectors";
501
+ export { patternPIIDetector } from "./types/guardrails/detectors";
502
+ export { detectedText } from "./types/guardrails/detectors";
503
+ export { evaluateDetectors } from "./types/guardrails/evaluate-detectors";
504
+ export { detectorVerdict } from "./types/guardrails/evaluate-detectors";
505
+ export { renderDetectorReport } from "./types/guardrails/evaluate-detectors";
448
506
  export { RetrievalGuard } from "./types/guardrails/retrieval-guard";
449
507
  export { PostGenerationGuard } from "./types/guardrails/post-generation-guard";
450
508
  export { GuardrailsPipeline } from "./types/guardrails/guardrails-pipeline";
package/dist/index.cjs CHANGED
@@ -1291,12 +1291,74 @@ var require_context_packing = __commonJS({
1291
1291
  }
1292
1292
  });
1293
1293
 
1294
+ // src/observability/scores.js
1295
+ var require_scores = __commonJS({
1296
+ "src/observability/scores.js"(exports2, module2) {
1297
+ "use strict";
1298
+ var RETRIEVAL_METRICS = /* @__PURE__ */ new Set(["contextPrecision", "contextRecall"]);
1299
+ var isScore = (v) => typeof v === "number" && Number.isFinite(v);
1300
+ function evaluationScores(result, { explanations = false } = {}) {
1301
+ if (!result || typeof result !== "object") return [];
1302
+ const out = [];
1303
+ const scores = result.evaluation && result.evaluation.scores ? result.evaluation.scores : {};
1304
+ for (const [name, value] of Object.entries(scores)) {
1305
+ if (!isScore(value)) continue;
1306
+ out.push({
1307
+ name,
1308
+ value,
1309
+ target: RETRIEVAL_METRICS.has(name) ? "retrieval" : "generation"
1310
+ });
1311
+ }
1312
+ if (result.citations && isScore(result.citations.groundednessScore)) {
1313
+ out.push({
1314
+ name: "citationCoverage",
1315
+ value: result.citations.groundednessScore,
1316
+ target: "generation"
1317
+ });
1318
+ }
1319
+ if (typeof result.grounded === "boolean" && isScore(result.groundedness)) {
1320
+ const unsupported = Array.isArray(result.unsupported) ? result.unsupported : [];
1321
+ out.push({
1322
+ name: "groundedSentences",
1323
+ value: result.groundedness,
1324
+ target: "generation",
1325
+ label: result.grounded ? "grounded" : "ungrounded",
1326
+ ...explanations && unsupported.length > 0 && {
1327
+ explanation: `Unsupported: ${unsupported.map((s) => JSON.stringify(s)).join(" ")}`
1328
+ }
1329
+ });
1330
+ }
1331
+ return out;
1332
+ }
1333
+ function toLangfuseScores(result, options = {}) {
1334
+ const { observationId, sessionId, comments = false } = options;
1335
+ const tel = result && result.telemetry || {};
1336
+ const traceId = options.traceId || tel.traceId;
1337
+ const fromSameTrace = !options.traceId || options.traceId === tel.traceId;
1338
+ return evaluationScores(result, { explanations: comments }).map((s) => {
1339
+ const observation = observationId || (fromSameTrace && tel.spans ? tel.spans[s.target] : void 0);
1340
+ return {
1341
+ name: s.name,
1342
+ value: s.value,
1343
+ dataType: "NUMERIC",
1344
+ ...traceId && { traceId },
1345
+ ...observation && { observationId: observation },
1346
+ ...sessionId && { sessionId },
1347
+ ...s.explanation && { comment: s.explanation },
1348
+ ...s.label && { metadata: { label: s.label } }
1349
+ };
1350
+ });
1351
+ }
1352
+ module2.exports = { evaluationScores, toLangfuseScores };
1353
+ }
1354
+ });
1355
+
1294
1356
  // package.json
1295
1357
  var require_package = __commonJS({
1296
1358
  "package.json"(exports2, module2) {
1297
1359
  module2.exports = {
1298
1360
  name: "@devilsdev/rag-pipeline-utils",
1299
- version: "3.3.0",
1361
+ version: "3.5.0",
1300
1362
  description: "Composable retrieval-augmented generation (RAG) for Node.js: grounded citations, evaluation, guardrails, hybrid retrieval and a CLI.",
1301
1363
  main: "dist/index.cjs",
1302
1364
  type: "commonjs",
@@ -1392,6 +1454,11 @@ var require_package = __commonJS({
1392
1454
  "benchmark:full": "node scripts/benchmark.js --iterations=500 --verbose",
1393
1455
  "bench:speed": "node benchmarks/speed.js",
1394
1456
  "bench:quality": "node benchmarks/quality.js",
1457
+ "bench:guardrails": "node benchmarks/guardrails.js",
1458
+ "bench:judge": "node benchmarks/judge.js",
1459
+ "bench:claude-citations": "node benchmarks/claude-citations.js",
1460
+ "bench:judge-more": "node benchmarks/judge-more.js",
1461
+ "bench:claude-latency": "node benchmarks/claude-latency.js",
1395
1462
  dev: "node scripts/dev-tools.js menu",
1396
1463
  "dev:health": "node scripts/dev-tools.js health",
1397
1464
  "dev:setup": "node scripts/dev-tools.js setup",
@@ -1410,6 +1477,7 @@ var require_package = __commonJS({
1410
1477
  "tests:ci": "jest --maxWorkers=50% --reporters=default --json --outputFile=test-results-current.json",
1411
1478
  "test:unit": "jest __tests__/unit",
1412
1479
  "test:integration": "jest __tests__/integration",
1480
+ "test:live": "jest --config jest.live.config.js --runInBand",
1413
1481
  "test:simple": "jest --maxWorkers=1 --no-coverage",
1414
1482
  "verify:pack": "node scripts/verify-npm-pack.js",
1415
1483
  "smoke:pack": "node scripts/smoke-test-pack.js",
@@ -1489,6 +1557,8 @@ var require_package = __commonJS({
1489
1557
  "**/*.{json,md,mdx,yml,yaml,css,scss,html}": "prettier --write --ignore-unknown"
1490
1558
  },
1491
1559
  devDependencies: {
1560
+ "@ai-sdk/anthropic": "3.0.138",
1561
+ "@ai-sdk/anthropic-v4": "npm:@ai-sdk/anthropic@4.0.97",
1492
1562
  "@babel/core": "^7.26.10",
1493
1563
  "@babel/preset-env": "^7.26.9",
1494
1564
  "@docusaurus/core": "3.10.2",
@@ -1499,12 +1569,14 @@ var require_package = __commonJS({
1499
1569
  "@fontsource/jetbrains-mono": "5.3.0",
1500
1570
  "@octokit/rest": "^21.1.1",
1501
1571
  "@opentelemetry/api": "^1.9.1",
1572
+ "@opentelemetry/api-logs": "^0.223.0",
1502
1573
  "@opentelemetry/auto-instrumentations-node": "^0.52.1",
1503
1574
  "@opentelemetry/context-async-hooks": "^2.12.0",
1504
1575
  "@opentelemetry/exporter-jaeger": "^1.26.0",
1505
1576
  "@opentelemetry/exporter-prometheus": "^0.53.0",
1506
1577
  "@opentelemetry/exporter-trace-otlp-http": "^0.53.0",
1507
1578
  "@opentelemetry/resources": "^1.26.0",
1579
+ "@opentelemetry/sdk-logs": "^0.223.0",
1508
1580
  "@opentelemetry/sdk-metrics": "^1.26.0",
1509
1581
  "@opentelemetry/sdk-node": "^0.53.0",
1510
1582
  "@opentelemetry/sdk-trace-base": "^2.12.0",
@@ -1550,10 +1622,10 @@ var require_package = __commonJS({
1550
1622
  "why-is-node-running": "^3.2.2"
1551
1623
  },
1552
1624
  dependencies: {
1553
- "@devilsdev/rag-connectors": "3.3.0",
1554
- "@devilsdev/rag-core": "3.3.0",
1555
- "@devilsdev/rag-eval": "3.3.0",
1556
- "@devilsdev/rag-guardrails": "3.3.0",
1625
+ "@devilsdev/rag-connectors": "3.5.0",
1626
+ "@devilsdev/rag-core": "3.5.0",
1627
+ "@devilsdev/rag-eval": "3.5.0",
1628
+ "@devilsdev/rag-guardrails": "3.5.0",
1557
1629
  ajv: "^8.17.1",
1558
1630
  axios: "^1.21.0",
1559
1631
  chokidar: "^4.0.3",
@@ -1636,6 +1708,7 @@ var require_optional_peer = __commonJS({
1636
1708
  var require_otel = __commonJS({
1637
1709
  "src/observability/otel.js"(exports2, module2) {
1638
1710
  "use strict";
1711
+ var { evaluationScores } = require_scores();
1639
1712
  var { version: LIBRARY_VERSION } = require_package();
1640
1713
  var { loadOptionalPeer } = require_optional_peer();
1641
1714
  var TRACER_NAME = "@devilsdev/rag-pipeline-utils";
@@ -1653,7 +1726,7 @@ var require_otel = __commonJS({
1653
1726
  const opts = option === true ? {} : option;
1654
1727
  if (typeof opts !== "object") {
1655
1728
  throw new TypeError(
1656
- "telemetry must be true or { tracer?, api?, recordContent? }"
1729
+ "telemetry must be true or { tracer?, api?, logs?, logger?, recordContent? }"
1657
1730
  );
1658
1731
  }
1659
1732
  const api = opts.api || loadOtelApi();
@@ -1666,7 +1739,33 @@ var require_otel = __commonJS({
1666
1739
  if (!tracer || typeof tracer.startActiveSpan !== "function") {
1667
1740
  throw new TypeError("telemetry.tracer must be an OpenTelemetry Tracer");
1668
1741
  }
1669
- return { api, tracer, recordContent: Boolean(opts.recordContent) };
1742
+ let logger = opts.logger || null;
1743
+ if (logger && typeof logger.emit !== "function") {
1744
+ throw new TypeError("telemetry.logger must be an OpenTelemetry Logger");
1745
+ }
1746
+ if (!logger) {
1747
+ if (opts.logs && !(opts.logs.logs && typeof opts.logs.logs.getLogger === "function")) {
1748
+ throw new TypeError(
1749
+ 'telemetry.logs must be the @opentelemetry/api-logs module (import * as logs from "@opentelemetry/api-logs")'
1750
+ );
1751
+ }
1752
+ const source = [opts.logs, api].find(
1753
+ (m) => m && m.logs && typeof m.logs.getLogger === "function"
1754
+ );
1755
+ if (source) logger = source.logs.getLogger(TRACER_NAME, LIBRARY_VERSION);
1756
+ }
1757
+ return {
1758
+ api,
1759
+ tracer,
1760
+ logger,
1761
+ // Holds the spans of one run, so its scores find them under concurrency
1762
+ runKey: api.createContextKey("rag.pipeline.run"),
1763
+ recordContent: Boolean(opts.recordContent)
1764
+ };
1765
+ }
1766
+ function recordStage(tel, stage, span) {
1767
+ const run = tel.api.context.active().getValue(tel.runKey);
1768
+ if (run) run[stage] = span.spanContext();
1670
1769
  }
1671
1770
  function providerOf(component) {
1672
1771
  const name = component && component.name;
@@ -1749,8 +1848,8 @@ var require_otel = __commonJS({
1749
1848
  retriever(method, [params = {}]) {
1750
1849
  const source = component.collection || (typeof component.table === "string" ? component.table.replace(/"/g, "") : void 0);
1751
1850
  return {
1752
- name: source ? `retrieval ${source}` : "retrieval",
1753
- kind: SpanKind.CLIENT,
1851
+ name: "retrieval",
1852
+ kind: SpanKind.INTERNAL,
1754
1853
  attributes: clean({
1755
1854
  "gen_ai.operation.name": "retrieval",
1756
1855
  "gen_ai.data_source.id": source,
@@ -1808,6 +1907,7 @@ var require_otel = __commonJS({
1808
1907
  };
1809
1908
  }
1810
1909
  };
1910
+ const STAGES = { retriever: "retrieval", llm: "generation" };
1811
1911
  const METHODS = {
1812
1912
  embedder: ["embed", "embedQuery"],
1813
1913
  retriever: ["retrieve"],
@@ -1821,8 +1921,10 @@ var require_otel = __commonJS({
1821
1921
  return (...args) => {
1822
1922
  const spec = spanFor[kind](prop, args);
1823
1923
  const options = { kind: spec.kind, attributes: spec.attributes };
1924
+ const stage = STAGES[kind];
1824
1925
  if (prop === "generateStream") {
1825
1926
  return tel.tracer.startActiveSpan(spec.name, options, (span) => {
1927
+ if (stage) recordStage(tel, stage, span);
1826
1928
  const startedAt = performance.now();
1827
1929
  let source;
1828
1930
  try {
@@ -1836,6 +1938,7 @@ var require_otel = __commonJS({
1836
1938
  });
1837
1939
  }
1838
1940
  return inSpan(tel, spec.name, options, async (span) => {
1941
+ if (stage) recordStage(tel, stage, span);
1839
1942
  const out = await value.apply(target, args);
1840
1943
  spec.after(span, out);
1841
1944
  return out;
@@ -1890,32 +1993,49 @@ var require_otel = __commonJS({
1890
1993
  "rag.pipeline.run",
1891
1994
  { kind: SpanKind.INTERNAL, attributes },
1892
1995
  async (span) => {
1996
+ const stages = {};
1997
+ const runContext = tel.api.context.active().setValue(tel.runKey, stages);
1893
1998
  let result;
1894
1999
  try {
1895
- result = await run(params);
2000
+ result = await tel.api.context.with(runContext, () => run(params));
1896
2001
  } catch (error) {
1897
2002
  markError(tel, span, error);
1898
2003
  span.end();
1899
2004
  throw error;
1900
2005
  }
1901
2006
  if (result && typeof result[Symbol.asyncIterator] === "function") {
2007
+ const iterator = result[Symbol.asyncIterator]();
1902
2008
  return (async function* tracedStream() {
2009
+ let finished = false;
1903
2010
  try {
1904
- for await (const chunk of result) {
2011
+ for (; ; ) {
2012
+ const step = await tel.api.context.with(
2013
+ runContext,
2014
+ () => iterator.next()
2015
+ );
2016
+ if (step.done) break;
2017
+ const chunk = step.value;
1905
2018
  if (chunk && chunk.done) {
1906
2019
  span.setAttributes(runAttributes(tel, chunk));
2020
+ finishRun(tel, span, stages, chunk);
1907
2021
  }
1908
2022
  yield chunk;
1909
2023
  }
2024
+ finished = true;
1910
2025
  } catch (error) {
2026
+ finished = true;
1911
2027
  markError(tel, span, error);
1912
2028
  throw error;
1913
2029
  } finally {
2030
+ if (!finished && typeof iterator.return === "function") {
2031
+ await iterator.return();
2032
+ }
1914
2033
  span.end();
1915
2034
  }
1916
2035
  })();
1917
2036
  }
1918
2037
  span.setAttributes(runAttributes(tel, result));
2038
+ finishRun(tel, span, stages, result);
1919
2039
  if (!result || result.success === false) {
1920
2040
  const message = result && result.error || "run failed";
1921
2041
  span.setAttribute("error.type", errorType(message));
@@ -1927,6 +2047,33 @@ var require_otel = __commonJS({
1927
2047
  );
1928
2048
  };
1929
2049
  }
2050
+ function finishRun(tel, span, stages, result) {
2051
+ if (!result || typeof result !== "object") return;
2052
+ const own = span.spanContext();
2053
+ result.telemetry = {
2054
+ traceId: own.traceId,
2055
+ spanId: own.spanId,
2056
+ spans: Object.fromEntries(
2057
+ Object.entries(stages).map(([stage, ctx]) => [stage, ctx.spanId])
2058
+ )
2059
+ };
2060
+ if (!tel.logger) return;
2061
+ for (const score of evaluationScores(result, {
2062
+ explanations: tel.recordContent
2063
+ })) {
2064
+ const parent = stages[score.target] || own;
2065
+ tel.logger.emit({
2066
+ eventName: "gen_ai.evaluation.result",
2067
+ context: tel.api.trace.setSpanContext(tel.api.context.active(), parent),
2068
+ attributes: clean({
2069
+ "gen_ai.evaluation.name": score.name,
2070
+ "gen_ai.evaluation.score.value": score.value,
2071
+ "gen_ai.evaluation.score.label": score.label,
2072
+ "gen_ai.evaluation.explanation": score.explanation
2073
+ })
2074
+ });
2075
+ }
2076
+ }
1930
2077
  function errorType(message) {
1931
2078
  if (message === "Execution timeout") return "timeout";
1932
2079
  if (message === "Execution aborted") return "aborted";
@@ -5101,7 +5248,79 @@ var require_connector_utils = __commonJS({
5101
5248
  reader.releaseLock();
5102
5249
  }
5103
5250
  }
5251
+ function toConnectorError(err, provider) {
5252
+ if (!err || typeof err !== "object" || err.isAxiosError !== true) {
5253
+ return err;
5254
+ }
5255
+ if (isAbortError(err)) {
5256
+ const aborted = new Error("The operation was aborted");
5257
+ aborted.name = "AbortError";
5258
+ aborted.code = "ABORT_ERR";
5259
+ return aborted;
5260
+ }
5261
+ const response = err.response;
5262
+ let message;
5263
+ if (response) {
5264
+ const data = response.data;
5265
+ const apiError = data && typeof data === "object" && typeof data.pipe !== "function" ? data.error : void 0;
5266
+ let detail = "";
5267
+ if (apiError && typeof apiError === "object") {
5268
+ detail = [apiError.type, apiError.message].filter(Boolean).join(": ");
5269
+ } else if (typeof apiError === "string") {
5270
+ detail = apiError;
5271
+ } else if (data && typeof data === "object" && typeof data.message === "string") {
5272
+ detail = data.message;
5273
+ }
5274
+ message = `${provider} API error ${response.status}${detail ? `: ${detail}` : ""}`;
5275
+ } else {
5276
+ message = `${provider} request failed: ${err.message}`;
5277
+ }
5278
+ const error = new Error(message);
5279
+ if (err.code) error.code = err.code;
5280
+ if (response) {
5281
+ error.status = response.status;
5282
+ error.response = {
5283
+ status: response.status,
5284
+ statusText: response.statusText,
5285
+ headers: response.headers,
5286
+ data: response.data
5287
+ };
5288
+ }
5289
+ return error;
5290
+ }
5291
+ async function readErrorBody(stream) {
5292
+ const chunks = [];
5293
+ let size = 0;
5294
+ try {
5295
+ for await (const chunk of stream) {
5296
+ size += chunk.length;
5297
+ if (size > 65536) break;
5298
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
5299
+ }
5300
+ } catch {
5301
+ } finally {
5302
+ if (typeof stream.destroy === "function") stream.destroy();
5303
+ }
5304
+ const text = Buffer.concat(chunks).toString("utf8");
5305
+ try {
5306
+ return JSON.parse(text);
5307
+ } catch {
5308
+ return text || void 0;
5309
+ }
5310
+ }
5311
+ function cleanAxiosErrors(client, provider) {
5312
+ client.interceptors.response.use(void 0, async (err) => {
5313
+ const data = err && err.response && err.response.data;
5314
+ if (data && typeof data.pipe === "function") {
5315
+ err.response.data = await readErrorBody(data);
5316
+ }
5317
+ throw toConnectorError(err, provider);
5318
+ });
5319
+ return client;
5320
+ }
5104
5321
  module2.exports = {
5322
+ toConnectorError,
5323
+ cleanAxiosErrors,
5105
5324
  textChunks,
5106
5325
  createAbortError,
5107
5326
  isAbortError,
@@ -5640,6 +5859,23 @@ var require_cost_calculator = __commonJS({
5640
5859
  source: "https://platform.claude.com/docs/en/about-claude/pricing",
5641
5860
  asOf: "2026-11-01"
5642
5861
  },
5862
+ // $0.10 / $0.50 per million input / output tokens, for prompts up to
5863
+ // 100,000 tokens ($0.50 / $2.50 above; this table has one price per model).
5864
+ "claude-haiku-5.5": {
5865
+ input: 1e-4,
5866
+ output: 5e-4,
5867
+ unit: 1e3,
5868
+ source: "https://platform.claude.com/docs/en/about-claude/pricing",
5869
+ asOf: "2026-11-05"
5870
+ },
5871
+ // $10 / $50 per million input / output tokens.
5872
+ "claude-fable-5.1": {
5873
+ input: 0.01,
5874
+ output: 0.05,
5875
+ unit: 1e3,
5876
+ source: "https://platform.claude.com/docs/en/about-claude/pricing",
5877
+ asOf: "2026-11-05"
5878
+ },
5643
5879
  "cohere-command": {
5644
5880
  input: 1e-3,
5645
5881
  output: 2e-3,
@@ -6032,6 +6268,8 @@ var require_cost_tracker = __commonJS({
6032
6268
  budgetOptions: {}
6033
6269
  };
6034
6270
  var CHARS_PER_TOKEN = 4;
6271
+ var PROMPT_OVERHEAD_CHARS = 600;
6272
+ var PASSAGE_WRAPPER_CHARS = 40;
6035
6273
  var CostTracker = class extends EventEmitter {
6036
6274
  /**
6037
6275
  * @param {object} [options]
@@ -6071,9 +6309,18 @@ var require_cost_tracker = __commonJS({
6071
6309
  }
6072
6310
  const inputText = result.input || result.query || result.prompt || "";
6073
6311
  const outputText = result.output || result.answer || result.response || "";
6312
+ const passages = Array.isArray(result.results) ? result.results : [];
6313
+ const passageChars = passages.reduce((n, doc) => {
6314
+ const text = typeof doc === "string" ? doc : doc && (doc.content || doc.text) || "";
6315
+ return n + String(text).length + PASSAGE_WRAPPER_CHARS;
6316
+ }, 0);
6317
+ const promptChars = passages.length > 0 ? PROMPT_OVERHEAD_CHARS : 0;
6074
6318
  return {
6075
- inputTokens: Math.ceil(String(inputText).length / CHARS_PER_TOKEN),
6076
- outputTokens: Math.ceil(String(outputText).length / CHARS_PER_TOKEN)
6319
+ inputTokens: Math.ceil(
6320
+ (String(inputText).length + passageChars + promptChars) / CHARS_PER_TOKEN
6321
+ ),
6322
+ outputTokens: Math.ceil(String(outputText).length / CHARS_PER_TOKEN),
6323
+ estimated: true
6077
6324
  };
6078
6325
  }
6079
6326
  /**
@@ -6096,7 +6343,7 @@ var require_cost_tracker = __commonJS({
6096
6343
  tracker.startSession({ autoStarted: true });
6097
6344
  }
6098
6345
  const result = await pipeline.run(...args);
6099
- const { inputTokens, outputTokens } = tracker._extractUsage(result);
6346
+ const { inputTokens, outputTokens, estimated } = tracker._extractUsage(result);
6100
6347
  const model = result.model || result.route && result.route.model || tracker.config.model;
6101
6348
  const costEstimate = tracker.calculator.estimate(
6102
6349
  model,
@@ -6123,8 +6370,14 @@ var require_cost_tracker = __commonJS({
6123
6370
  inputTokens,
6124
6371
  outputTokens,
6125
6372
  estimatedCost: costEstimate.totalCost,
6373
+ // The price-table entry used (as before), and the model the run
6374
+ // asked for, which can differ in form (claude-haiku-4-5 is priced
6375
+ // as claude-haiku-4.5)
6126
6376
  model: costEstimate.model,
6127
- currency: "USD"
6377
+ requestedModel: model,
6378
+ currency: "USD",
6379
+ // Token counts estimated from text length (no usage reported)
6380
+ ...estimated && { tokensEstimated: true }
6128
6381
  };
6129
6382
  const entry = {
6130
6383
  timestamp: Date.now(),
@@ -6811,9 +7064,15 @@ var require_core = __commonJS({
6811
7064
  var { createModelRouter, classifyQuery } = require_model_router();
6812
7065
  var { MemoryRetriever } = require_memory_retriever();
6813
7066
  var { BaseConnector } = require_base_connector();
7067
+ var {
7068
+ evaluationScores,
7069
+ toLangfuseScores
7070
+ } = require_scores();
6814
7071
  module2.exports = {
6815
7072
  createRagPipeline,
6816
7073
  createPipeline: createRagPipeline,
7074
+ evaluationScores,
7075
+ toLangfuseScores,
6817
7076
  packContext,
6818
7077
  createError: errorFormatter.createError,
6819
7078
  wrapError: errorFormatter.wrapError,
@@ -6873,6 +7132,7 @@ var modules = {
6873
7132
  "evaluate/relevance.js": () => require_relevance(),
6874
7133
  "evaluate/scoring.js": () => require_scoring(),
6875
7134
  "observability/otel.js": () => require_otel(),
7135
+ "observability/scores.js": () => require_scores(),
6876
7136
  "packages/core.js": () => require_core(),
6877
7137
  "reranker/cascade-reranker.js": () => require_cascade_reranker(),
6878
7138
  "reranker/embedding-reranker.js": () => require_embedding_reranker(),
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { createRagPipeline, createPipeline, packContext, createError, wrapError, ERROR_CODES, retry, withRetry, ChunkingEngine, BM25Search, reciprocalRankFusion, HybridRetriever, ScoringReranker, EmbeddingReranker, CascadeReranker, MemoryCache, RedisCache, CostCalculator, PRICING_METADATA, CostTracker, TokenBudget, createModelRouter, classifyQuery, MemoryRetriever, BaseConnector } from "./all";
1
+ export { createRagPipeline, createPipeline, evaluationScores, toLangfuseScores, packContext, createError, wrapError, ERROR_CODES, retry, withRetry, ChunkingEngine, BM25Search, reciprocalRankFusion, HybridRetriever, ScoringReranker, EmbeddingReranker, CascadeReranker, MemoryCache, RedisCache, CostCalculator, PRICING_METADATA, CostTracker, TokenBudget, createModelRouter, classifyQuery, MemoryRetriever, BaseConnector } from "./all";
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from 'module';
2
2
  const require = createRequire(import.meta.url);
3
3
  const mod = require('./index.cjs');
4
- export const { createRagPipeline, createPipeline, packContext, createError, wrapError, ERROR_CODES, retry, withRetry, ChunkingEngine, BM25Search, reciprocalRankFusion, HybridRetriever, ScoringReranker, EmbeddingReranker, CascadeReranker, MemoryCache, RedisCache, CostCalculator, PRICING_METADATA, CostTracker, TokenBudget, createModelRouter, classifyQuery, MemoryRetriever, BaseConnector } = mod;
4
+ export const { createRagPipeline, createPipeline, evaluationScores, toLangfuseScores, packContext, createError, wrapError, ERROR_CODES, retry, withRetry, ChunkingEngine, BM25Search, reciprocalRankFusion, HybridRetriever, ScoringReranker, EmbeddingReranker, CascadeReranker, MemoryCache, RedisCache, CostCalculator, PRICING_METADATA, CostTracker, TokenBudget, createModelRouter, classifyQuery, MemoryRetriever, BaseConnector } = mod;
5
5
  export default mod;
@@ -35,6 +35,10 @@ export type GroundingMiddlewareOptions = {
35
35
  * - Run checkAnswer on the answer
36
36
  */
37
37
  check?: boolean;
38
+ /**
39
+ * - An LLM with `generate()` (this library's LLM contract, e.g. `createAiSdkAdapter({ ai, languageModel }).llm`) that rules on the sentences the lexical check failed for lack of word overlap; see checkAnswer. Give it the base model, not the wrapped one, or each judge call is grounded again
40
+ */
41
+ judge?: any;
38
42
  /**
39
43
  * - Return the passages as source parts
40
44
  */
@@ -96,6 +100,10 @@ export type GroundingReport = {
96
100
  * - Per-sentence citations
97
101
  */
98
102
  citations?: Array<any>;
103
+ /**
104
+ * - With the `judge` option: the verdicts, the number of calls, the token usage when known, and `error` when the judge failed (its sentences then stay unsupported)
105
+ */
106
+ judge?: any;
99
107
  /**
100
108
  * - What each enabled guard found
101
109
  */
@@ -135,6 +143,7 @@ export function groundingMiddleware(options?: GroundingMiddlewareOptions): any;
135
143
  * @property {Function} [countTokens] - Token counter for maxContextTokens
136
144
  * @property {string} [systemPrompt] - Replaces the default RAG system prompt
137
145
  * @property {boolean} [check=true] - Run checkAnswer on the answer
146
+ * @property {Object} [judge] - An LLM with `generate()` (this library's LLM contract, e.g. `createAiSdkAdapter({ ai, languageModel }).llm`) that rules on the sentences the lexical check failed for lack of word overlap; see checkAnswer. Give it the base model, not the wrapped one, or each judge call is grounded again
138
147
  * @property {boolean} [sources=true] - Return the passages as source parts
139
148
  * @property {Object|boolean} [preRetrieval] - PreRetrievalGuard options (true for defaults): injection detection on the query
140
149
  * @property {Object|boolean} [retrieval] - RetrievalGuard options (true for defaults): ACL, minimum score, injection in documents
@@ -152,6 +161,7 @@ export function groundingMiddleware(options?: GroundingMiddlewareOptions): any;
152
161
  * @property {number} [groundedness] - Share of supported sentences, 0..1
153
162
  * @property {string[]} [unsupported] - Sentences no passage supports
154
163
  * @property {Array<Object>} [citations] - Per-sentence citations
164
+ * @property {Object} [judge] - With the `judge` option: the verdicts, the number of calls, the token usage when known, and `error` when the judge failed (its sentences then stay unsupported)
155
165
  * @property {Object} guardrails - What each enabled guard found
156
166
  */
157
167
  export class GroundingBlockedError extends Error {