@kernlang/core 3.5.9-canary.220.1.c398cd95 → 4.0.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 (104) hide show
  1. package/dist/capability-matrix.js +8 -6
  2. package/dist/capability-matrix.js.map +1 -1
  3. package/dist/closure-eligibility.d.ts +104 -0
  4. package/dist/closure-eligibility.js +443 -0
  5. package/dist/closure-eligibility.js.map +1 -0
  6. package/dist/closure-python-lowering.d.ts +26 -0
  7. package/dist/closure-python-lowering.js +132 -22
  8. package/dist/closure-python-lowering.js.map +1 -1
  9. package/dist/codegen/body-ts.js +13 -0
  10. package/dist/codegen/body-ts.js.map +1 -1
  11. package/dist/codegen/type-system.js +50 -3
  12. package/dist/codegen/type-system.js.map +1 -1
  13. package/dist/codegen-core.js +29 -0
  14. package/dist/codegen-core.js.map +1 -1
  15. package/dist/codegen-expression.js +7 -0
  16. package/dist/codegen-expression.js.map +1 -1
  17. package/dist/constructor-super.d.ts +34 -0
  18. package/dist/constructor-super.js +122 -0
  19. package/dist/constructor-super.js.map +1 -0
  20. package/dist/core-contracts/boolean.d.ts +147 -0
  21. package/dist/core-contracts/boolean.js +110 -0
  22. package/dist/core-contracts/boolean.js.map +1 -0
  23. package/dist/core-contracts/function.d.ts +6 -0
  24. package/dist/core-contracts/function.js +7 -0
  25. package/dist/core-contracts/function.js.map +1 -0
  26. package/dist/core-contracts/index.d.ts +1032 -0
  27. package/dist/core-contracts/index.js +30 -0
  28. package/dist/core-contracts/index.js.map +1 -0
  29. package/dist/core-contracts/list.d.ts +87 -0
  30. package/dist/core-contracts/list.js +54 -0
  31. package/dist/core-contracts/list.js.map +1 -0
  32. package/dist/core-contracts/nullish.d.ts +12 -0
  33. package/dist/core-contracts/nullish.js +13 -0
  34. package/dist/core-contracts/nullish.js.map +1 -0
  35. package/dist/core-contracts/number.d.ts +291 -0
  36. package/dist/core-contracts/number.js +214 -0
  37. package/dist/core-contracts/number.js.map +1 -0
  38. package/dist/core-contracts/record.d.ts +60 -0
  39. package/dist/core-contracts/record.js +32 -0
  40. package/dist/core-contracts/record.js.map +1 -0
  41. package/dist/core-contracts/schema.d.ts +71 -0
  42. package/dist/core-contracts/schema.js +98 -0
  43. package/dist/core-contracts/schema.js.map +1 -0
  44. package/dist/core-contracts/semantics.d.ts +7 -0
  45. package/dist/core-contracts/semantics.js +221 -0
  46. package/dist/core-contracts/semantics.js.map +1 -0
  47. package/dist/core-contracts/string.d.ts +414 -0
  48. package/dist/core-contracts/string.js +335 -0
  49. package/dist/core-contracts/string.js.map +1 -0
  50. package/dist/core-runtime/contract-adapter.d.ts +8 -0
  51. package/dist/core-runtime/contract-adapter.js +73 -0
  52. package/dist/core-runtime/contract-adapter.js.map +1 -0
  53. package/dist/core-runtime/index.d.ts +109 -0
  54. package/dist/core-runtime/index.js +1946 -0
  55. package/dist/core-runtime/index.js.map +1 -0
  56. package/dist/core-runtime/shape-validator.d.ts +61 -0
  57. package/dist/core-runtime/shape-validator.js +564 -0
  58. package/dist/core-runtime/shape-validator.js.map +1 -0
  59. package/dist/core-runtime/value-brand.d.ts +2 -0
  60. package/dist/core-runtime/value-brand.js +6 -0
  61. package/dist/core-runtime/value-brand.js.map +1 -0
  62. package/dist/decompiler.js +46 -0
  63. package/dist/decompiler.js.map +1 -1
  64. package/dist/eligibility-taxonomy.d.ts +58 -0
  65. package/dist/eligibility-taxonomy.generated.d.ts +472 -0
  66. package/dist/eligibility-taxonomy.generated.js +566 -0
  67. package/dist/eligibility-taxonomy.generated.js.map +1 -0
  68. package/dist/eligibility-taxonomy.js +55 -0
  69. package/dist/eligibility-taxonomy.js.map +1 -0
  70. package/dist/index.d.ts +19 -2
  71. package/dist/index.js +17 -1
  72. package/dist/index.js.map +1 -1
  73. package/dist/instanceof-rhs.d.ts +53 -0
  74. package/dist/instanceof-rhs.js +74 -0
  75. package/dist/instanceof-rhs.js.map +1 -0
  76. package/dist/ir/semantics/lambda.js +9 -1
  77. package/dist/ir/semantics/lambda.js.map +1 -1
  78. package/dist/native-eligibility-ast.d.ts +5 -0
  79. package/dist/native-eligibility-ast.js +242 -63
  80. package/dist/native-eligibility-ast.js.map +1 -1
  81. package/dist/parser-expression.d.ts +1 -1
  82. package/dist/parser-expression.js +90 -1
  83. package/dist/parser-expression.js.map +1 -1
  84. package/dist/rag-assertions.d.ts +3 -0
  85. package/dist/rag-assertions.js +15 -0
  86. package/dist/rag-assertions.js.map +1 -0
  87. package/dist/rag-runtime.d.ts +169 -0
  88. package/dist/rag-runtime.js +823 -0
  89. package/dist/rag-runtime.js.map +1 -0
  90. package/dist/schema.d.ts +1 -0
  91. package/dist/schema.js +197 -2
  92. package/dist/schema.js.map +1 -1
  93. package/dist/semantic-substrate.d.ts +108 -0
  94. package/dist/semantic-substrate.js +263 -0
  95. package/dist/semantic-substrate.js.map +1 -0
  96. package/dist/semantic-validator.d.ts +332 -0
  97. package/dist/semantic-validator.js +3387 -0
  98. package/dist/semantic-validator.js.map +1 -1
  99. package/dist/spec.d.ts +2 -2
  100. package/dist/spec.js +15 -1
  101. package/dist/spec.js.map +1 -1
  102. package/dist/value-ir.d.ts +11 -1
  103. package/dist/value-ir.js.map +1 -1
  104. package/package.json +5 -1
@@ -0,0 +1,823 @@
1
+ import { RAG_MCP_RETRIEVE_OUTPUT_ITEM_SHAPE, RAG_MCP_RETRIEVE_OUTPUT_SHAPE } from './semantic-validator.js';
2
+ export const MAX_IN_MEMORY_RAG_TOP_K = 1000;
3
+ export class InMemoryRagCorpus {
4
+ chunks = new Map();
5
+ constructor(chunks = []) {
6
+ for (const chunk of chunks)
7
+ this.add(chunk);
8
+ }
9
+ get size() {
10
+ return this.chunks.size;
11
+ }
12
+ add(chunk) {
13
+ if (typeof chunk.id !== 'string' || !chunk.id.trim()) {
14
+ throw new Error('KERN RAG runtime chunk id must be a non-empty string.');
15
+ }
16
+ if (typeof chunk.text !== 'string' || !chunk.text.trim()) {
17
+ throw new Error(`KERN RAG runtime chunk '${chunk.id}' text must be a non-empty string.`);
18
+ }
19
+ if (typeof chunk.source !== 'string' || !chunk.source.trim()) {
20
+ throw new Error(`KERN RAG runtime chunk '${chunk.id}' source must be a non-empty string.`);
21
+ }
22
+ const storedChunk = {
23
+ ...chunk,
24
+ citation: chunk.citation ? { ...chunk.citation } : undefined,
25
+ metadata: chunk.metadata ? cloneMetadata(chunk.metadata) : undefined,
26
+ };
27
+ this.chunks.set(chunk.id, { chunk: storedChunk, terms: tokenizeForRetrieval(storedChunk.text) });
28
+ }
29
+ get(id) {
30
+ const stored = this.chunks.get(id);
31
+ return stored ? cloneChunkInput(stored.chunk) : undefined;
32
+ }
33
+ all() {
34
+ return Array.from(this.chunks.values(), (stored) => cloneChunkInput(stored.chunk));
35
+ }
36
+ retrieve(query, options = {}) {
37
+ if (typeof query !== 'string')
38
+ throw new Error('KERN RAG runtime query must be a string.');
39
+ const { topK, minScore } = normalizeRetrieveOptions(options);
40
+ const queryTerms = tokenizeForRetrieval(query);
41
+ if (queryTerms.size === 0)
42
+ return { query, chunks: [] };
43
+ const chunks = Array.from(this.chunks.values())
44
+ .map((stored) => ({ chunk: stored.chunk, score: jaccardScore(queryTerms, stored.terms) }))
45
+ .filter((candidate) => candidate.score > 0 && candidate.score >= minScore)
46
+ .sort((a, b) => b.score - a.score || a.chunk.id.localeCompare(b.chunk.id))
47
+ .slice(0, topK)
48
+ .map(({ chunk, score }) => retrievedChunk(chunk, score));
49
+ return { query, chunks };
50
+ }
51
+ }
52
+ export function createInMemoryRetriever(corpus) {
53
+ return (query, options = {}) => corpus.retrieve(query, options);
54
+ }
55
+ export function retrieveFromInMemoryCorpus(corpus, query, options = {}) {
56
+ return corpus.retrieve(query, options);
57
+ }
58
+ export function createRagRuntimeProvenance(result, options = {}) {
59
+ const validResult = validateRetrieveResult(result);
60
+ const retrieveOptions = normalizeProvenanceRetrieveOptions(options.retrieveOptions);
61
+ const chunkHashes = validResult.chunks.map((chunk) => hashRetrievedChunkText(chunk.text));
62
+ const chunkProvenance = validResult.chunks.map((chunk, index) => ({
63
+ index,
64
+ id: chunk.id,
65
+ source: chunk.source,
66
+ score: chunk.score,
67
+ citation: { ...chunk.citation },
68
+ textHash: chunkHashes[index],
69
+ }));
70
+ const sources = uniqueOrdered(validResult.chunks.map((chunk) => chunk.source));
71
+ const startedAtMs = options.startedAtMs ?? Date.now();
72
+ const durationMs = options.durationMs ?? 0;
73
+ const contractStatus = options.contractStatus ?? 'success';
74
+ return {
75
+ runId: options.runId ??
76
+ hashRetrievedChunkText(stableStringify({
77
+ retrieverName: options.retrieverName,
78
+ targetKind: options.targetKind,
79
+ targetName: options.targetName,
80
+ query: validResult.query,
81
+ retrieveOptions,
82
+ citationsRequired: options.citationsRequired ?? false,
83
+ chunks: chunkProvenance,
84
+ contractStatus,
85
+ })),
86
+ ...optionalStringValue('retrieverName', options.retrieverName),
87
+ ...(options.targetKind ? { targetKind: options.targetKind } : {}),
88
+ ...optionalStringValue('targetName', options.targetName),
89
+ query: validResult.query,
90
+ retrieveOptions,
91
+ citationsRequired: options.citationsRequired ?? false,
92
+ startedAtMs,
93
+ durationMs,
94
+ chunkCount: validResult.chunks.length,
95
+ chunkHashes,
96
+ sources,
97
+ contractStatus,
98
+ };
99
+ }
100
+ export function withRagRuntimeProvenance(result, options = {}) {
101
+ const validResult = validateRetrieveResult(result);
102
+ return {
103
+ query: validResult.query,
104
+ chunks: validResult.chunks.map(cloneRetrievedChunk),
105
+ provenance: createRagRuntimeProvenance(validResult, options),
106
+ };
107
+ }
108
+ export function ragMcpRetrieveProvenanceMapping(retrieval) {
109
+ if (!retrieval)
110
+ throw new Error('KERN RAG MCP provenance mapping requires a retrieval fact.');
111
+ return {
112
+ ...optionalStringValue('outputShape', retrieval.outputShape),
113
+ ...optionalStringValue('outputItemShape', retrieval.outputItemShape),
114
+ ...optionalStringValue('citationField', retrieval.citationField),
115
+ ...optionalStringValue('sourceField', retrieval.sourceField),
116
+ ...optionalStringValue('scoreField', retrieval.scoreField),
117
+ ...optionalStringValue('provenance', retrieval.provenance),
118
+ citationsRequired: retrieval.effectiveRequiresCitations,
119
+ contractStatus: retrieval.contractStatus,
120
+ compatible: retrieval.contractStatus === 'valid' &&
121
+ retrieval.outputShape === RAG_MCP_RETRIEVE_OUTPUT_SHAPE &&
122
+ (retrieval.outputItemShape === undefined || retrieval.outputItemShape === RAG_MCP_RETRIEVE_OUTPUT_ITEM_SHAPE),
123
+ };
124
+ }
125
+ export function evaluateRagAnswerContract(contract) {
126
+ const base = {
127
+ ...optionalStringValue('id', contract.id),
128
+ ...optionalStringValue('ragName', contract.ragName),
129
+ query: typeof contract.query === 'string' ? contract.query : '',
130
+ };
131
+ let retrieval;
132
+ try {
133
+ retrieval = validateRetrieveResult(contract.retrieval);
134
+ }
135
+ catch (error) {
136
+ return {
137
+ ...base,
138
+ passed: false,
139
+ status: 'invalid',
140
+ groundingCoverage: 0,
141
+ groundedChars: 0,
142
+ answerChars: 0,
143
+ citedChunkIds: [],
144
+ sources: [],
145
+ ...(contract.provenance ? { provenance: contract.provenance } : {}),
146
+ diagnostics: [
147
+ {
148
+ code: 'RETRIEVER_ERROR',
149
+ message: `RAG answer contract retrieval failed: ${error instanceof Error ? error.message : String(error)}.`,
150
+ },
151
+ ],
152
+ };
153
+ }
154
+ const answer = typeof contract.answer === 'string' ? contract.answer : '';
155
+ const answerChars = countAnswerChars(answer);
156
+ const diagnostics = [];
157
+ if (typeof contract.answer !== 'string' || answerChars === 0) {
158
+ diagnostics.push({ code: 'ANSWER_EMPTY', message: 'RAG answer contract answer must be a non-empty string.' });
159
+ }
160
+ const minGroundingCoverage = normalizeGroundingCoverageThreshold(contract.minGroundingCoverage);
161
+ const provenance = contract.provenance ?? retrieveResultProvenance(contract.retrieval);
162
+ if (contract.query !== retrieval.query) {
163
+ diagnostics.push({
164
+ code: 'QUERY_MISMATCH',
165
+ message: 'RAG answer contract query does not match the retrieval result query.',
166
+ });
167
+ }
168
+ if (provenance && !provenanceMatchesRetrieval(provenance, retrieval)) {
169
+ diagnostics.push({
170
+ code: 'PROVENANCE_MISMATCH',
171
+ message: 'RAG answer contract provenance does not match the retrieval result.',
172
+ });
173
+ }
174
+ const chunkById = new Map(retrieval.chunks.map((chunk) => [chunk.id, chunk]));
175
+ const grounded = new Array(answer.length).fill(false);
176
+ const citedChunkIds = new Set();
177
+ const citationBearingChunkIds = new Set();
178
+ const groundingSpans = Array.isArray(contract.groundingSpans) ? contract.groundingSpans : [];
179
+ if (contract.groundingSpans !== undefined && !Array.isArray(contract.groundingSpans)) {
180
+ diagnostics.push({
181
+ code: 'SPAN_INVALID',
182
+ message: 'RAG answer contract groundingSpans must be an array.',
183
+ });
184
+ }
185
+ for (const [spanIndex, span] of groundingSpans.entries()) {
186
+ if (!isValidGroundingSpan(span, answer.length)) {
187
+ diagnostics.push({
188
+ code: 'SPAN_INVALID',
189
+ spanIndex,
190
+ message: `RAG answer grounding span at index ${spanIndex} is invalid or outside the answer text.`,
191
+ });
192
+ continue;
193
+ }
194
+ const validChunkIds = [];
195
+ let spanHasCitation = false;
196
+ for (const chunkId of span.chunkIds) {
197
+ if (chunkById.has(chunkId)) {
198
+ validChunkIds.push(chunkId);
199
+ citedChunkIds.add(chunkId);
200
+ if (chunkHasCitation(chunkById.get(chunkId))) {
201
+ spanHasCitation = true;
202
+ citationBearingChunkIds.add(chunkId);
203
+ }
204
+ }
205
+ else {
206
+ diagnostics.push({
207
+ code: 'CHUNK_REF_UNKNOWN',
208
+ spanIndex,
209
+ chunkId,
210
+ message: `RAG answer grounding span at index ${spanIndex} references unknown chunk '${chunkId}'.`,
211
+ });
212
+ }
213
+ }
214
+ if (validChunkIds.length === 0) {
215
+ diagnostics.push({
216
+ code: span.required ? 'CITATION_REQUIRED' : 'SPAN_UNGROUNDED',
217
+ spanIndex,
218
+ message: `RAG answer grounding span at index ${spanIndex} has no valid retrieved chunk citation.`,
219
+ });
220
+ continue;
221
+ }
222
+ if (contract.requireCitations && !spanHasCitation) {
223
+ diagnostics.push({
224
+ code: 'CITATION_REQUIRED',
225
+ spanIndex,
226
+ message: `RAG answer grounding span at index ${spanIndex} requires a non-empty retrieved chunk citation.`,
227
+ });
228
+ continue;
229
+ }
230
+ for (let index = span.start; index < span.end; index += 1)
231
+ grounded[index] = true;
232
+ }
233
+ const groundedChars = countGroundedAnswerChars(answer, grounded);
234
+ const groundingCoverage = answerChars === 0 ? 0 : groundedChars / answerChars;
235
+ if (answerChars > 0 &&
236
+ contract.requireCitations &&
237
+ citationBearingChunkIds.size === 0 &&
238
+ groundingSpans.length === 0) {
239
+ diagnostics.push({
240
+ code: 'CITATION_REQUIRED',
241
+ message: 'RAG answer contract requires citations but no retrieved chunks were cited.',
242
+ });
243
+ }
244
+ if (answerChars > 0 && groundingCoverage < minGroundingCoverage) {
245
+ diagnostics.push({
246
+ code: 'GROUNDING_BELOW_THRESHOLD',
247
+ message: `RAG answer grounding coverage ${groundingCoverage.toFixed(3)} is below required threshold ${minGroundingCoverage.toFixed(3)}.`,
248
+ });
249
+ }
250
+ const passed = diagnostics.length === 0;
251
+ return {
252
+ ...base,
253
+ passed,
254
+ status: passed ? 'grounded' : ragAnswerStatus(diagnostics, groundingCoverage),
255
+ groundingCoverage,
256
+ groundedChars,
257
+ answerChars,
258
+ citedChunkIds: [...citedChunkIds].sort(stableStringCompare),
259
+ sources: uniqueOrdered([...citedChunkIds].map((chunkId) => chunkById.get(chunkId)?.source).filter(isString)),
260
+ ...(provenance ? { provenance } : {}),
261
+ diagnostics,
262
+ };
263
+ }
264
+ export function ragAnswerContractFromSemanticFact(fact, retrieval, options = {}) {
265
+ return {
266
+ id: fact.name,
267
+ ...optionalStringValue('ragName', fact.ragName),
268
+ ...optionalStringValue('prompt', fact.prompt),
269
+ query: fact.query,
270
+ answer: fact.answer,
271
+ retrieval,
272
+ ...(options.provenance ? { provenance: options.provenance } : {}),
273
+ groundingSpans: fact.spans.map((span) => ({
274
+ start: span.start,
275
+ end: span.end,
276
+ chunkIds: [...span.chunkIds],
277
+ ...(span.required ? { required: true } : {}),
278
+ })),
279
+ requireCitations: fact.requireCitations,
280
+ ...optionalNumberValue('minGroundingCoverage', fact.minGroundingCoverage),
281
+ };
282
+ }
283
+ export function evaluateRagSemanticAnswerContract(fact, retrieval, options = {}) {
284
+ return evaluateRagAnswerContract(ragAnswerContractFromSemanticFact(fact, retrieval, options));
285
+ }
286
+ export function evaluateRagEvalContract(evaluation, retriever, options = {}) {
287
+ const startedAt = runtimeNow(options);
288
+ const cases = (evaluation.cases ?? []).map((evaluationCase) => evaluateRagCase(evaluation, evaluationCase, retriever, options));
289
+ const assertionCount = cases.reduce((count, evaluationCase) => count + evaluationCase.assertions.length, 0);
290
+ const passedAssertionCount = cases.reduce((count, evaluationCase) => count + evaluationCase.assertions.filter((assertion) => assertion.passed).length, 0);
291
+ return {
292
+ // Empty eval contracts fail closed; a vacuous pass would hide unconfigured evals.
293
+ passed: cases.length > 0 && cases.every((evaluationCase) => evaluationCase.passed),
294
+ ...optionalStringValue('ragName', evaluation.ragName),
295
+ ...optionalStringValue('evalName', evaluation.name),
296
+ caseCount: cases.length,
297
+ passedCaseCount: cases.filter((evaluationCase) => evaluationCase.passed).length,
298
+ assertionCount,
299
+ passedAssertionCount,
300
+ durationMs: runtimeNow(options) - startedAt,
301
+ cases,
302
+ };
303
+ }
304
+ export function hashRetrievedChunkText(text) {
305
+ let left = 0xcbf29ce484222325n;
306
+ let right = 0x84222325cbf29ce4n;
307
+ for (const byte of new TextEncoder().encode(text)) {
308
+ const value = BigInt(byte);
309
+ left ^= value;
310
+ left = BigInt.asUintN(64, left * 0x100000001b3n);
311
+ right ^= value + 0x9en;
312
+ right = BigInt.asUintN(64, right * 0x100000001b3n);
313
+ }
314
+ return `${left.toString(16).padStart(16, '0')}${right.toString(16).padStart(16, '0')}`;
315
+ }
316
+ function normalizeRetrieveOptions(options) {
317
+ const topK = options.topK ?? 5;
318
+ const minScore = options.minScore ?? 0;
319
+ if (!Number.isInteger(topK) || topK <= 0 || topK > MAX_IN_MEMORY_RAG_TOP_K) {
320
+ throw new Error(`KERN RAG runtime topK must be a positive integer up to ${MAX_IN_MEMORY_RAG_TOP_K}.`);
321
+ }
322
+ if (!Number.isFinite(minScore) || minScore < 0 || minScore > 1) {
323
+ throw new Error('KERN RAG runtime minScore must be between 0 and 1.');
324
+ }
325
+ return { topK, minScore };
326
+ }
327
+ function normalizeGroundingCoverageThreshold(value) {
328
+ if (value === undefined)
329
+ return 1;
330
+ if (!Number.isFinite(value) || value < 0 || value > 1) {
331
+ throw new Error('KERN RAG answer contract minGroundingCoverage must be between 0 and 1.');
332
+ }
333
+ return value;
334
+ }
335
+ function retrieveResultProvenance(result) {
336
+ return result && typeof result === 'object' && 'provenance' in result ? result.provenance : undefined;
337
+ }
338
+ function provenanceMatchesRetrieval(provenance, retrieval) {
339
+ if (!provenance ||
340
+ typeof provenance.query !== 'string' ||
341
+ typeof provenance.chunkCount !== 'number' ||
342
+ !Array.isArray(provenance.chunkHashes) ||
343
+ !Array.isArray(provenance.sources)) {
344
+ return false;
345
+ }
346
+ const chunkHashes = retrieval.chunks.map((chunk) => hashRetrievedChunkText(chunk.text));
347
+ const sources = uniqueOrdered(retrieval.chunks.map((chunk) => chunk.source));
348
+ return (provenance.query === retrieval.query &&
349
+ provenance.chunkCount === retrieval.chunks.length &&
350
+ arraysEqual(provenance.chunkHashes, chunkHashes) &&
351
+ arraysEqual(provenance.sources, sources));
352
+ }
353
+ function chunkHasCitation(chunk) {
354
+ return (!!chunk &&
355
+ ((typeof chunk.citation.uri === 'string' && chunk.citation.uri.trim().length > 0) ||
356
+ (typeof chunk.citation.locator === 'string' && chunk.citation.locator.trim().length > 0)));
357
+ }
358
+ function isValidGroundingSpan(span, answerLength) {
359
+ return (!!span &&
360
+ Number.isInteger(span.start) &&
361
+ Number.isInteger(span.end) &&
362
+ span.start >= 0 &&
363
+ span.end > span.start &&
364
+ span.end <= answerLength &&
365
+ Array.isArray(span.chunkIds) &&
366
+ span.chunkIds.every(isString));
367
+ }
368
+ function countAnswerChars(answer) {
369
+ if (typeof answer !== 'string')
370
+ return 0;
371
+ let count = 0;
372
+ for (let index = 0; index < answer.length; index += 1) {
373
+ if (!/\s/u.test(answer[index] ?? ''))
374
+ count += 1;
375
+ }
376
+ return count;
377
+ }
378
+ function countGroundedAnswerChars(answer, grounded) {
379
+ let count = 0;
380
+ for (let index = 0; index < answer.length; index += 1) {
381
+ if (grounded[index] && !/\s/u.test(answer[index] ?? ''))
382
+ count += 1;
383
+ }
384
+ return count;
385
+ }
386
+ function ragAnswerStatus(diagnostics, groundingCoverage) {
387
+ if (diagnostics.some((diagnostic) => [
388
+ 'ANSWER_EMPTY',
389
+ 'CITATION_REQUIRED',
390
+ 'QUERY_MISMATCH',
391
+ 'RETRIEVER_ERROR',
392
+ 'PROVENANCE_MISMATCH',
393
+ 'SPAN_INVALID',
394
+ 'CHUNK_REF_UNKNOWN',
395
+ ].includes(diagnostic.code))) {
396
+ return 'invalid';
397
+ }
398
+ if (groundingCoverage === 0)
399
+ return 'ungrounded';
400
+ return 'partially_grounded';
401
+ }
402
+ function evaluateRagCase(evaluation, evaluationCase, retriever, options) {
403
+ const retrieveOptions = caseRetrieveOptions(evaluationCase);
404
+ const startedAt = runtimeNow(options);
405
+ let chunks = [];
406
+ let assertions = [];
407
+ try {
408
+ chunks = validateRetrieveResult(retriever(evaluationCase.query, retrieveOptions)).chunks;
409
+ }
410
+ catch (error) {
411
+ assertions = [
412
+ {
413
+ kind: 'retriever',
414
+ passed: false,
415
+ code: 'RETRIEVER_ERROR',
416
+ message: `RAG eval case '${evaluationCase.name}' retriever failed: ${error instanceof Error ? error.message : String(error)}.`,
417
+ },
418
+ ];
419
+ }
420
+ const durationMs = runtimeNow(options) - startedAt;
421
+ if (assertions.length === 0) {
422
+ assertions = [
423
+ ...evaluateExpectedCaseContracts(evaluationCase, chunks),
424
+ ...(evaluationCase.asserts ?? []).map((assertion) => evaluateRagAssertion(evaluation, evaluationCase, assertion, chunks, durationMs, options)),
425
+ ];
426
+ }
427
+ return {
428
+ name: evaluationCase.name,
429
+ query: evaluationCase.query,
430
+ passed: assertions.every(isPassingOrAdvisoryAssertion),
431
+ durationMs,
432
+ retrieveOptions,
433
+ chunks,
434
+ assertions,
435
+ };
436
+ }
437
+ function caseRetrieveOptions(evaluationCase) {
438
+ return {
439
+ ...optionalNumberValue('topK', evaluationCase.expected?.topK),
440
+ ...optionalNumberValue('minScore', evaluationCase.expected?.minScore),
441
+ };
442
+ }
443
+ function evaluateExpectedCaseContracts(evaluationCase, chunks) {
444
+ const results = [];
445
+ const { topK, minScore, chunkCount, sources } = evaluationCase.expected ?? {};
446
+ if (topK !== undefined) {
447
+ results.push(assertionResult('expected.topK', chunks.length <= topK, `expected at most ${topK} chunks`, topK, chunks.length));
448
+ }
449
+ if (minScore !== undefined) {
450
+ const actual = chunks.length === 0
451
+ ? 0
452
+ : chunks.reduce((minimumScore, chunk) => Math.min(minimumScore, chunk.score), Number.POSITIVE_INFINITY);
453
+ results.push(assertionResult('expected.minScore', chunks.length > 0 && chunks.every((chunk) => chunk.score >= minScore), `expected all retrieved chunks to score >= ${minScore}`, minScore, actual));
454
+ }
455
+ if (chunkCount !== undefined) {
456
+ results.push(assertionResult('expected.chunkCount', chunks.length === chunkCount, `expected ${chunkCount} chunks`, chunkCount, chunks.length));
457
+ }
458
+ if (sources?.length) {
459
+ const actualSources = [...new Set(chunks.map((chunk) => chunk.source))].sort();
460
+ const expectedSources = [...sources].sort();
461
+ const allowed = new Set(expectedSources);
462
+ results.push(assertionResult('expected.sources', expectedSources.every((source) => actualSources.includes(source)) &&
463
+ chunks.every((chunk) => allowed.has(chunk.source)), `expected retrieved chunks to cover only sources ${expectedSources.join(', ')}`, expectedSources, actualSources));
464
+ }
465
+ return results;
466
+ }
467
+ function evaluateRagAssertion(evaluation, evaluationCase, assertion, chunks, durationMs, options) {
468
+ switch (assertion.kind) {
469
+ case 'scoreGte':
470
+ return withRagAssertRequired(assertion, numericChunkAssertion(assertion, chunks, (chunk, value) => chunk.score >= value, 'score >='));
471
+ case 'scoreLte':
472
+ return withRagAssertRequired(assertion, numericChunkAssertion(assertion, chunks, (chunk, value) => chunk.score <= value, 'score <='));
473
+ case 'contains':
474
+ return withRagAssertRequired(assertion, stringChunkAssertion(assertion, chunks, 'text', (chunk, value) => chunk.text.toLowerCase().includes(value.toLowerCase()), 'text contains'));
475
+ case 'sourceEq':
476
+ return withRagAssertRequired(assertion, stringChunkAssertion(assertion, chunks, 'source', (chunk, value) => chunk.source === value, 'source equals'));
477
+ case 'sourceGlob':
478
+ return withRagAssertRequired(assertion, stringChunkAssertion(assertion, chunks, 'source', (chunk, value) => globMatches(value, chunk.source, options.sourceGlobCaseSensitive ?? false), 'source matches'));
479
+ case 'uniqueSourcesGte': {
480
+ const expected = numberAssertionValue(assertion);
481
+ if (expected === undefined)
482
+ return invalidAssertionResult(assertion, 'requires numeric value.');
483
+ const actual = new Set(chunks.map((chunk) => chunk.source)).size;
484
+ return withRagAssertRequired(assertion, assertionResult(assertion.kind, actual >= expected, `expected at least ${expected} unique sources`, expected, actual));
485
+ }
486
+ case 'chunkCountEq': {
487
+ const expected = numberAssertionValue(assertion);
488
+ if (expected === undefined)
489
+ return invalidAssertionResult(assertion, 'requires numeric value.');
490
+ return withRagAssertRequired(assertion, assertionResult(assertion.kind, chunks.length === expected, `expected exactly ${expected} chunks`, expected, chunks.length));
491
+ }
492
+ case 'citesRequired': {
493
+ const actual = chunks.length > 0 && chunks.every((chunk) => !!chunk.citation.uri || !!chunk.citation.locator);
494
+ return withRagAssertRequired(assertion, assertionResult(assertion.kind, actual, 'expected every chunk to carry citation data', true, actual));
495
+ }
496
+ case 'factId': {
497
+ const expected = stringAssertionValue(assertion);
498
+ if (expected === undefined)
499
+ return invalidAssertionResult(assertion, 'requires string value.');
500
+ const actual = ragEvalCaseFactId(evaluation, evaluationCase);
501
+ return withRagAssertRequired(assertion, assertionResult(assertion.kind, actual === expected, `expected fact id ${expected}`, expected, actual));
502
+ }
503
+ case 'chunkHash': {
504
+ const expected = stringAssertionValue(assertion);
505
+ if (expected === undefined)
506
+ return invalidAssertionResult(assertion, 'requires string value.');
507
+ const actual = chunks.map((chunk) => hashRetrievedChunkText(chunk.text));
508
+ return withRagAssertRequired(assertion, assertionResult(assertion.kind, actual.includes(expected), `expected retrieved chunk hash ${expected}`, expected, actual));
509
+ }
510
+ case 'latencyLte': {
511
+ const expected = numberAssertionValue(assertion);
512
+ if (expected === undefined)
513
+ return invalidAssertionResult(assertion, 'requires numeric value.');
514
+ return withRagAssertRequired(assertion, assertionResult(assertion.kind, durationMs <= expected, `expected retrieval latency <= ${expected}ms`, expected, durationMs));
515
+ }
516
+ default:
517
+ return {
518
+ kind: assertion.kind,
519
+ required: assertion.required,
520
+ passed: false,
521
+ code: 'UNSUPPORTED_ASSERTION',
522
+ message: `Unsupported RAG eval assertion kind '${assertion.kind}'.`,
523
+ };
524
+ }
525
+ }
526
+ function numericChunkAssertion(assertion, chunks, check, label) {
527
+ const expected = numberAssertionValue(assertion);
528
+ if (expected === undefined)
529
+ return invalidAssertionResult(assertion, 'requires numeric value.');
530
+ const actual = chunks.map((chunk) => chunk.score);
531
+ return assertionResult(assertion.kind, chunks.length > 0 && chunks.every((chunk) => check(chunk, expected)), `expected every retrieved chunk ${label} ${expected}`, expected, actual);
532
+ }
533
+ function stringChunkAssertion(assertion, chunks, actualField, check, label) {
534
+ const expected = stringAssertionValue(assertion);
535
+ if (expected === undefined)
536
+ return invalidAssertionResult(assertion, 'requires non-empty string value.');
537
+ const actual = chunks.map((chunk) => (actualField === 'source' ? chunk.source : chunk.text));
538
+ return assertionResult(assertion.kind, chunks.some((chunk) => check(chunk, expected)), `expected a retrieved chunk ${label} ${expected}`, expected, actual);
539
+ }
540
+ function invalidAssertionResult(assertion, reason) {
541
+ return {
542
+ kind: assertion.kind,
543
+ required: assertion.required,
544
+ passed: false,
545
+ code: 'INVALID_ASSERTION',
546
+ message: `RAG eval assertion kind=${assertion.kind} ${reason}`,
547
+ };
548
+ }
549
+ function assertionResult(kind, passed, message, expected, actual) {
550
+ return {
551
+ kind,
552
+ passed,
553
+ code: passed ? 'PASS' : 'ASSERTION_FAIL',
554
+ message,
555
+ ...optionalAssertionValue('expected', expected),
556
+ ...optionalAssertionValue('actual', actual),
557
+ };
558
+ }
559
+ function numberAssertionValue(assertion) {
560
+ return typeof assertion.value === 'number' && Number.isFinite(assertion.value) ? assertion.value : undefined;
561
+ }
562
+ function stringAssertionValue(assertion) {
563
+ return typeof assertion.value === 'string' && assertion.value.length > 0 ? assertion.value : undefined;
564
+ }
565
+ function withRagAssertRequired(assertion, result) {
566
+ return { ...result, required: assertion.required };
567
+ }
568
+ function isPassingOrAdvisoryAssertion(assertion) {
569
+ return assertion.passed || (assertion.required === false && assertion.code === 'ASSERTION_FAIL');
570
+ }
571
+ function ragEvalCaseFactId(evaluation, evaluationCase) {
572
+ return [evaluationCase.ragName ?? evaluation.ragName, evaluationCase.evalName ?? evaluation.name, evaluationCase.name]
573
+ .filter((part) => !!part)
574
+ .join(':');
575
+ }
576
+ function globMatches(pattern, value, caseSensitive) {
577
+ const normalizedPattern = caseSensitive ? pattern : pattern.toLowerCase();
578
+ const normalizedValue = caseSensitive ? value : value.toLowerCase();
579
+ return wildcardMatches(normalizedPattern, normalizedValue);
580
+ }
581
+ function wildcardMatches(pattern, value) {
582
+ let patternIndex = 0;
583
+ let valueIndex = 0;
584
+ let starIndex = -1;
585
+ let starValueIndex = 0;
586
+ while (valueIndex < value.length) {
587
+ if (patternIndex < pattern.length &&
588
+ (pattern[patternIndex] === '?' || pattern[patternIndex] === value[valueIndex])) {
589
+ patternIndex += 1;
590
+ valueIndex += 1;
591
+ }
592
+ else if (patternIndex < pattern.length && pattern[patternIndex] === '*') {
593
+ starIndex = patternIndex;
594
+ starValueIndex = valueIndex;
595
+ patternIndex += 1;
596
+ }
597
+ else if (starIndex !== -1) {
598
+ patternIndex = starIndex + 1;
599
+ starValueIndex += 1;
600
+ valueIndex = starValueIndex;
601
+ }
602
+ else {
603
+ return false;
604
+ }
605
+ }
606
+ while (patternIndex < pattern.length && pattern[patternIndex] === '*')
607
+ patternIndex += 1;
608
+ return patternIndex === pattern.length;
609
+ }
610
+ function runtimeNow(options) {
611
+ return options.now?.() ?? Date.now();
612
+ }
613
+ function optionalStringValue(key, value) {
614
+ return value === undefined ? {} : { [key]: value };
615
+ }
616
+ function optionalNumberValue(key, value) {
617
+ return value === undefined ? {} : { [key]: value };
618
+ }
619
+ function optionalAssertionValue(key, value) {
620
+ return value === undefined ? {} : { [key]: value };
621
+ }
622
+ function normalizeProvenanceRetrieveOptions(options) {
623
+ if (options === undefined)
624
+ return {};
625
+ const out = {};
626
+ if (options.topK !== undefined) {
627
+ if (!Number.isInteger(options.topK) || options.topK <= 0 || options.topK > MAX_IN_MEMORY_RAG_TOP_K) {
628
+ throw new Error(`KERN RAG runtime topK must be a positive integer up to ${MAX_IN_MEMORY_RAG_TOP_K}.`);
629
+ }
630
+ out.topK = options.topK;
631
+ }
632
+ if (options.minScore !== undefined) {
633
+ if (!Number.isFinite(options.minScore) || options.minScore < 0 || options.minScore > 1) {
634
+ throw new Error('KERN RAG runtime minScore must be between 0 and 1.');
635
+ }
636
+ out.minScore = options.minScore;
637
+ }
638
+ return out;
639
+ }
640
+ function uniqueOrdered(values) {
641
+ const seen = new Set();
642
+ const out = [];
643
+ for (const value of values) {
644
+ if (seen.has(value))
645
+ continue;
646
+ seen.add(value);
647
+ out.push(value);
648
+ }
649
+ return out;
650
+ }
651
+ function stableStringify(value) {
652
+ return JSON.stringify(stableJsonValue(value, new WeakSet()));
653
+ }
654
+ function stableJsonValue(value, seen) {
655
+ if (typeof value === 'bigint')
656
+ return value.toString();
657
+ if (typeof value === 'symbol')
658
+ return value.description ?? value.toString();
659
+ if (typeof value === 'function')
660
+ return `[Function:${value.name || 'anonymous'}]`;
661
+ if (value === null || typeof value !== 'object')
662
+ return value;
663
+ if (seen.has(value))
664
+ return '[Circular]';
665
+ seen.add(value);
666
+ try {
667
+ if (Array.isArray(value))
668
+ return value.map((item) => stableJsonValue(item, seen));
669
+ if (value instanceof Date)
670
+ return value.toISOString();
671
+ if (value instanceof RegExp)
672
+ return value.toString();
673
+ if (value instanceof Map) {
674
+ return Array.from(value.entries())
675
+ .map(([key, entry]) => [stableJsonValue(key, seen), stableJsonValue(entry, seen)])
676
+ .sort(([left], [right]) => stableStringCompare(left, right));
677
+ }
678
+ if (value instanceof Set) {
679
+ return Array.from(value.values())
680
+ .map((entry) => stableJsonValue(entry, seen))
681
+ .sort(stableStringCompare);
682
+ }
683
+ if (isPlainMetadataObject(value)) {
684
+ const out = {};
685
+ for (const key of Object.keys(value).sort()) {
686
+ const entry = value[key];
687
+ if (entry !== undefined)
688
+ out[key] = stableJsonValue(entry, seen);
689
+ }
690
+ return out;
691
+ }
692
+ return String(value);
693
+ }
694
+ finally {
695
+ seen.delete(value);
696
+ }
697
+ }
698
+ function stableStringCompare(left, right) {
699
+ const leftText = String(left);
700
+ const rightText = String(right);
701
+ return leftText < rightText ? -1 : leftText > rightText ? 1 : 0;
702
+ }
703
+ function arraysEqual(left, right) {
704
+ return left.length === right.length && left.every((value, index) => value === right[index]);
705
+ }
706
+ function validateRetrieveResult(result) {
707
+ if (!result || typeof result.query !== 'string' || !Array.isArray(result.chunks)) {
708
+ throw new Error('retriever result must include query string and chunks array.');
709
+ }
710
+ for (const [index, chunk] of result.chunks.entries()) {
711
+ if (chunk &&
712
+ typeof chunk.score === 'number' &&
713
+ (!Number.isFinite(chunk.score) || chunk.score < 0 || chunk.score > 1)) {
714
+ throw new Error(`retriever chunk at index ${index} score must be between 0 and 1.`);
715
+ }
716
+ if (!chunk ||
717
+ typeof chunk.id !== 'string' ||
718
+ typeof chunk.text !== 'string' ||
719
+ typeof chunk.score !== 'number' ||
720
+ !Number.isFinite(chunk.score) ||
721
+ chunk.score < 0 ||
722
+ chunk.score > 1 ||
723
+ typeof chunk.source !== 'string' ||
724
+ !isValidCitation(chunk.citation)) {
725
+ throw new Error(`retriever chunk at index ${index} is not a RetrievedChunk.`);
726
+ }
727
+ }
728
+ const chunkIds = new Set();
729
+ for (const [index, chunk] of result.chunks.entries()) {
730
+ if (chunkIds.has(chunk.id)) {
731
+ throw new Error(`retriever chunk at index ${index} duplicates chunk id '${chunk.id}'.`);
732
+ }
733
+ chunkIds.add(chunk.id);
734
+ }
735
+ return result;
736
+ }
737
+ function isValidCitation(value) {
738
+ if (!value || typeof value !== 'object' || Array.isArray(value))
739
+ return false;
740
+ const citation = value;
741
+ return ((citation.uri === undefined || typeof citation.uri === 'string') &&
742
+ (citation.locator === undefined || typeof citation.locator === 'string'));
743
+ }
744
+ function isString(value) {
745
+ return typeof value === 'string';
746
+ }
747
+ export function tokenizeForRetrieval(value) {
748
+ return new Set(value
749
+ .normalize('NFKC')
750
+ .toLowerCase()
751
+ .match(/[\p{L}\p{M}\p{N}]+/gu) ?? []);
752
+ }
753
+ function jaccardScore(queryTerms, chunkTerms) {
754
+ if (queryTerms.size === 0 || chunkTerms.size === 0)
755
+ return 0;
756
+ let intersection = 0;
757
+ for (const term of queryTerms) {
758
+ if (chunkTerms.has(term))
759
+ intersection += 1;
760
+ }
761
+ const union = queryTerms.size + chunkTerms.size - intersection;
762
+ return union === 0 ? 0 : intersection / union;
763
+ }
764
+ function retrievedChunk(chunk, score) {
765
+ return {
766
+ id: chunk.id,
767
+ text: chunk.text,
768
+ score,
769
+ source: chunk.source,
770
+ citation: chunk.citation ? { ...chunk.citation } : { uri: chunk.source },
771
+ ...(chunk.metadata ? { metadata: cloneMetadata(chunk.metadata) } : {}),
772
+ };
773
+ }
774
+ function cloneRetrievedChunk(chunk) {
775
+ return {
776
+ ...chunk,
777
+ citation: { ...chunk.citation },
778
+ ...(chunk.metadata ? { metadata: cloneMetadata(chunk.metadata) } : {}),
779
+ };
780
+ }
781
+ function cloneChunkInput(chunk) {
782
+ return {
783
+ ...chunk,
784
+ citation: chunk.citation ? { ...chunk.citation } : undefined,
785
+ metadata: chunk.metadata ? cloneMetadata(chunk.metadata) : undefined,
786
+ };
787
+ }
788
+ function cloneMetadata(metadata) {
789
+ return cloneMetadataValue(metadata, new WeakMap());
790
+ }
791
+ function cloneMetadataValue(value, seen) {
792
+ if (Array.isArray(value)) {
793
+ const existing = seen.get(value);
794
+ if (existing)
795
+ return existing;
796
+ const out = [];
797
+ seen.set(value, out);
798
+ for (const item of value)
799
+ out.push(cloneMetadataValue(item, seen));
800
+ return out;
801
+ }
802
+ if (isPlainMetadataObject(value)) {
803
+ const existing = seen.get(value);
804
+ if (existing)
805
+ return existing;
806
+ const out = {};
807
+ seen.set(value, out);
808
+ for (const [key, entry] of Object.entries(value)) {
809
+ if (key === '__proto__' || key === 'constructor' || key === 'prototype')
810
+ continue;
811
+ out[key] = cloneMetadataValue(entry, seen);
812
+ }
813
+ return out;
814
+ }
815
+ return value;
816
+ }
817
+ function isPlainMetadataObject(value) {
818
+ if (value === null || typeof value !== 'object')
819
+ return false;
820
+ const prototype = Object.getPrototypeOf(value);
821
+ return prototype === Object.prototype || prototype === null;
822
+ }
823
+ //# sourceMappingURL=rag-runtime.js.map