llm-testrunner-components 1.0.6 → 1.0.9

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 (266) hide show
  1. package/README.md +101 -100
  2. package/dist/cjs/app-chips_5.cjs.entry.js +158 -0
  3. package/dist/cjs/app-chips_5.cjs.entry.js.map +1 -0
  4. package/dist/cjs/app-globals-Chb-oJtg.js +34 -0
  5. package/dist/cjs/app-globals-Chb-oJtg.js.map +1 -0
  6. package/dist/cjs/index-By1scwl6.js +25542 -0
  7. package/dist/cjs/index-By1scwl6.js.map +1 -0
  8. package/dist/cjs/index-CgmLNwZO.js +21460 -0
  9. package/dist/cjs/index-CgmLNwZO.js.map +1 -0
  10. package/dist/cjs/index.cjs.js +5 -483
  11. package/dist/cjs/index.cjs.js.map +1 -1
  12. package/dist/cjs/llm-testrunner.cjs.js +6 -4
  13. package/dist/cjs/llm-testrunner.cjs.js.map +1 -1
  14. package/dist/cjs/loader.cjs.js +5 -3
  15. package/dist/collection/collection-manifest.json +8 -3
  16. package/dist/collection/components/error-message/error-message.css +34 -0
  17. package/dist/collection/components/error-message/error-message.js +2 -2
  18. package/dist/collection/components/error-message/error-message.js.map +1 -1
  19. package/dist/collection/components/llm-test-runner/header/llm-test-runner-header.css +60 -0
  20. package/dist/collection/components/llm-test-runner/header/llm-test-runner-header.js +18 -0
  21. package/dist/collection/components/llm-test-runner/header/llm-test-runner-header.js.map +1 -0
  22. package/dist/collection/components/llm-test-runner/llm-test-runner.css +17 -657
  23. package/dist/collection/components/llm-test-runner/llm-test-runner.import-export.test.js +253 -0
  24. package/dist/collection/components/llm-test-runner/llm-test-runner.import-export.test.js.map +1 -0
  25. package/dist/collection/components/llm-test-runner/llm-test-runner.js +191 -200
  26. package/dist/collection/components/llm-test-runner/llm-test-runner.js.map +1 -1
  27. package/dist/collection/components/llm-test-runner/test-cases/actions/row-actions.css +28 -0
  28. package/dist/collection/components/llm-test-runner/test-cases/actions/row-actions.js +6 -0
  29. package/dist/collection/components/llm-test-runner/test-cases/actions/row-actions.js.map +1 -0
  30. package/dist/collection/components/llm-test-runner/test-cases/evaluation/evaluation-summary.css +67 -0
  31. package/dist/collection/components/llm-test-runner/test-cases/evaluation/evaluation-summary.js +5 -0
  32. package/dist/collection/components/llm-test-runner/test-cases/evaluation/evaluation-summary.js.map +1 -0
  33. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.css +42 -0
  34. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js +39 -0
  35. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js.map +1 -0
  36. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.css +39 -0
  37. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js +7 -0
  38. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js.map +1 -0
  39. package/dist/collection/components/llm-test-runner/test-cases/output/response-output.css +51 -0
  40. package/dist/collection/components/llm-test-runner/test-cases/output/response-output.js +5 -0
  41. package/dist/collection/components/llm-test-runner/test-cases/output/response-output.js.map +1 -0
  42. package/dist/collection/global/env.js +3 -1
  43. package/dist/collection/global/env.js.map +1 -1
  44. package/dist/collection/index.js.map +1 -1
  45. package/dist/collection/lib/evaluation/constants.js +14 -0
  46. package/dist/collection/lib/evaluation/constants.js.map +1 -0
  47. package/dist/collection/lib/evaluation/evaluation-engine.js +45 -45
  48. package/dist/collection/lib/evaluation/evaluation-engine.js.map +1 -1
  49. package/dist/collection/lib/evaluation/evaluation-service.js +33 -0
  50. package/dist/collection/lib/evaluation/evaluation-service.js.map +1 -0
  51. package/dist/collection/lib/evaluation/evaluators/bleu/bleu-evaluator.js +116 -0
  52. package/dist/collection/lib/evaluation/evaluators/bleu/bleu-evaluator.js.map +1 -0
  53. package/dist/collection/lib/evaluation/evaluators/bleu/tests/bleu.test.js +352 -0
  54. package/dist/collection/lib/evaluation/evaluators/bleu/tests/bleu.test.js.map +1 -0
  55. package/dist/collection/lib/evaluation/evaluators/exact/exact.js +44 -0
  56. package/dist/collection/lib/evaluation/evaluators/exact/exact.js.map +1 -0
  57. package/dist/collection/lib/evaluation/evaluators/rouge1-evaluator.js +88 -0
  58. package/dist/collection/lib/evaluation/evaluators/rouge1-evaluator.js.map +1 -0
  59. package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.js +82 -0
  60. package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.js.map +1 -0
  61. package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.test.js +326 -0
  62. package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.test.js.map +1 -0
  63. package/dist/collection/lib/evaluation/evaluators/semantic/SemanticEvaluator.js +69 -0
  64. package/dist/collection/lib/evaluation/evaluators/semantic/SemanticEvaluator.js.map +1 -0
  65. package/dist/collection/lib/evaluation/evaluators/semantic/evaluate-keywords.js +56 -0
  66. package/dist/collection/lib/evaluation/evaluators/semantic/evaluate-keywords.js.map +1 -0
  67. package/dist/collection/lib/evaluation/evaluators/semantic/index.js +7 -0
  68. package/dist/collection/lib/evaluation/evaluators/semantic/index.js.map +1 -0
  69. package/dist/collection/lib/evaluation/evaluators/semantic/model-loader.js +19 -0
  70. package/dist/collection/lib/evaluation/evaluators/semantic/model-loader.js.map +1 -0
  71. package/dist/collection/lib/evaluation/evaluators/semantic/similarity-utils.js +16 -0
  72. package/dist/collection/lib/evaluation/evaluators/semantic/similarity-utils.js.map +1 -0
  73. package/dist/collection/lib/evaluation/evaluators/semantic/tests/evaluate-keywords.test.js +65 -0
  74. package/dist/collection/lib/evaluation/evaluators/semantic/tests/evaluate-keywords.test.js.map +1 -0
  75. package/dist/collection/lib/evaluation/evaluators/semantic/text-utils.js +5 -0
  76. package/dist/collection/lib/evaluation/evaluators/semantic/text-utils.js.map +1 -0
  77. package/dist/collection/lib/evaluation/index.js.map +1 -1
  78. package/dist/collection/lib/evaluation/rouge1-evaluator.test.js +117 -0
  79. package/dist/collection/lib/evaluation/rouge1-evaluator.test.js.map +1 -0
  80. package/dist/collection/lib/evaluation/types.js.map +1 -1
  81. package/dist/collection/lib/file/file-download.js +18 -0
  82. package/dist/collection/lib/file/file-download.js.map +1 -0
  83. package/dist/collection/lib/file/file-reader.js +14 -0
  84. package/dist/collection/lib/file/file-reader.js.map +1 -0
  85. package/dist/collection/lib/form/components/app-chips.css +97 -0
  86. package/dist/collection/lib/form/components/app-chips.js +155 -0
  87. package/dist/collection/lib/form/components/app-chips.js.map +1 -0
  88. package/dist/collection/lib/form/components/app-select.css +28 -0
  89. package/dist/collection/lib/form/components/app-select.js +101 -0
  90. package/dist/collection/lib/form/components/app-select.js.map +1 -0
  91. package/dist/collection/lib/form/components/app-textarea.css +38 -0
  92. package/dist/collection/lib/form/components/app-textarea.js +126 -0
  93. package/dist/collection/lib/form/components/app-textarea.js.map +1 -0
  94. package/dist/collection/lib/form/form-builder.js +171 -0
  95. package/dist/collection/lib/form/form-builder.js.map +1 -0
  96. package/dist/collection/lib/form/schema/base-input-field-config.js +2 -0
  97. package/dist/collection/lib/form/schema/base-input-field-config.js.map +1 -0
  98. package/dist/collection/lib/form/schema/form-control-config.js +2 -0
  99. package/dist/collection/lib/form/schema/form-control-config.js.map +1 -0
  100. package/dist/collection/lib/form/schema/index.js +8 -0
  101. package/dist/collection/lib/form/schema/index.js.map +1 -0
  102. package/dist/collection/lib/import-export/test-results-csv.js +65 -0
  103. package/dist/collection/lib/import-export/test-results-csv.js.map +1 -0
  104. package/dist/collection/lib/import-export/test-suite-exporter.js +15 -0
  105. package/dist/collection/lib/import-export/test-suite-exporter.js.map +1 -0
  106. package/dist/collection/lib/import-export/test-suite-importer.js +44 -0
  107. package/dist/collection/lib/import-export/test-suite-importer.js.map +1 -0
  108. package/dist/collection/lib/rate-limited-fetcher/rate-limited-fetcher.js +6 -6
  109. package/dist/collection/lib/rate-limited-fetcher/rate-limited-fetcher.js.map +1 -1
  110. package/dist/collection/lib/test-cases/test-case-factory.js +56 -0
  111. package/dist/collection/lib/test-cases/test-case-factory.js.map +1 -0
  112. package/dist/collection/lib/test-cases/test-case-mutations.js +16 -0
  113. package/dist/collection/lib/test-cases/test-case-mutations.js.map +1 -0
  114. package/dist/collection/lib/ui/button/button.css +113 -0
  115. package/dist/collection/lib/ui/button/button.js +21 -0
  116. package/dist/collection/lib/ui/button/button.js.map +1 -0
  117. package/dist/collection/lib/ui/button/index.js +2 -0
  118. package/dist/collection/lib/ui/button/index.js.map +1 -0
  119. package/dist/collection/lib/ui/icon-button/icon-button.css +77 -0
  120. package/dist/collection/lib/ui/icon-button/icon-button.js +19 -0
  121. package/dist/collection/lib/ui/icon-button/icon-button.js.map +1 -0
  122. package/dist/collection/lib/ui/icon-button/index.js +2 -0
  123. package/dist/collection/lib/ui/icon-button/index.js.map +1 -0
  124. package/dist/collection/services/adapters.js +2 -0
  125. package/dist/collection/services/adapters.js.map +1 -0
  126. package/dist/collection/services/models/gemini.js +17 -0
  127. package/dist/collection/services/models/gemini.js.map +1 -0
  128. package/dist/collection/styles/tokens.css +180 -0
  129. package/dist/collection/types/evaluation.js +2 -0
  130. package/dist/collection/types/evaluation.js.map +1 -0
  131. package/dist/collection/types/llm-test-runner.js +2 -0
  132. package/dist/collection/types/llm-test-runner.js.map +1 -0
  133. package/dist/components/app-chips.d.ts +11 -0
  134. package/dist/components/app-chips.js +2 -0
  135. package/dist/components/app-chips.js.map +1 -0
  136. package/dist/components/app-select.d.ts +11 -0
  137. package/dist/components/app-select.js +2 -0
  138. package/dist/components/app-select.js.map +1 -0
  139. package/dist/components/app-textarea.d.ts +11 -0
  140. package/dist/components/app-textarea.js +2 -0
  141. package/dist/components/app-textarea.js.map +1 -0
  142. package/dist/components/form-builder.d.ts +11 -0
  143. package/dist/components/form-builder.js +2 -0
  144. package/dist/components/form-builder.js.map +1 -0
  145. package/dist/components/index.d.ts +2 -0
  146. package/dist/components/index.js +1 -13
  147. package/dist/components/index.js.map +1 -1
  148. package/dist/components/llm-test-runner.js +1 -8
  149. package/dist/components/llm-test-runner.js.map +1 -1
  150. package/dist/components/p--2rdv_J9.js +2 -0
  151. package/dist/components/p--2rdv_J9.js.map +1 -0
  152. package/dist/components/p-B7J48VNq.js +2 -0
  153. package/dist/components/p-B7J48VNq.js.map +1 -0
  154. package/dist/components/p-BCB1rjPS.js +7 -0
  155. package/dist/components/p-BCB1rjPS.js.map +1 -0
  156. package/dist/components/p-BQhb2H_a.js +2 -0
  157. package/dist/components/p-BQhb2H_a.js.map +1 -0
  158. package/dist/components/p-D9BrlHdP.js +297 -0
  159. package/dist/components/p-D9BrlHdP.js.map +1 -0
  160. package/dist/components/p-DtCkZ1g2.js +2 -0
  161. package/dist/components/p-DtCkZ1g2.js.map +1 -0
  162. package/dist/esm/app-chips_5.entry.js +153 -0
  163. package/dist/esm/app-chips_5.entry.js.map +1 -0
  164. package/dist/esm/app-globals-DbR5vV7d.js +32 -0
  165. package/dist/esm/app-globals-DbR5vV7d.js.map +1 -0
  166. package/dist/esm/index-Bvg6mh1M.js +25539 -0
  167. package/dist/esm/index-Bvg6mh1M.js.map +1 -0
  168. package/dist/esm/index-DxzhGhec.js +21450 -0
  169. package/dist/esm/index-DxzhGhec.js.map +1 -0
  170. package/dist/esm/index.js +4 -486
  171. package/dist/esm/index.js.map +1 -1
  172. package/dist/esm/llm-testrunner.js +7 -5
  173. package/dist/esm/llm-testrunner.js.map +1 -1
  174. package/dist/esm/loader.js +6 -4
  175. package/dist/llm-testrunner/index.esm.js +1 -1
  176. package/dist/llm-testrunner/index.esm.js.map +1 -1
  177. package/dist/llm-testrunner/llm-testrunner.esm.js +1 -1
  178. package/dist/llm-testrunner/llm-testrunner.esm.js.map +1 -1
  179. package/dist/llm-testrunner/p-3f04b0fb.entry.js +2 -0
  180. package/dist/llm-testrunner/p-3f04b0fb.entry.js.map +1 -0
  181. package/dist/llm-testrunner/p-DFds8y01.js +7 -0
  182. package/dist/llm-testrunner/p-DFds8y01.js.map +1 -0
  183. package/dist/llm-testrunner/p-DxzhGhec.js +298 -0
  184. package/dist/llm-testrunner/p-DxzhGhec.js.map +1 -0
  185. package/dist/llm-testrunner/p-GQwFOmwJ.js +2 -0
  186. package/dist/llm-testrunner/p-GQwFOmwJ.js.map +1 -0
  187. package/dist/react/components.d.ts +32 -2
  188. package/dist/react/components.d.ts.map +1 -1
  189. package/dist/react/components.js +44 -2
  190. package/dist/types/components/llm-test-runner/header/llm-test-runner-header.d.ts +14 -0
  191. package/dist/types/components/llm-test-runner/llm-test-runner.d.ts +13 -29
  192. package/dist/types/components/llm-test-runner/llm-test-runner.import-export.test.d.ts +1 -0
  193. package/dist/types/components/llm-test-runner/test-cases/actions/row-actions.d.ts +8 -0
  194. package/dist/types/components/llm-test-runner/test-cases/evaluation/evaluation-summary.d.ts +7 -0
  195. package/dist/types/components/llm-test-runner/test-cases/llm-test-case-row.d.ts +25 -0
  196. package/dist/types/components/llm-test-runner/test-cases/llm-test-cases.d.ts +26 -0
  197. package/dist/types/components/llm-test-runner/test-cases/output/response-output.d.ts +6 -0
  198. package/dist/types/components.d.ts +199 -4
  199. package/dist/types/global/env.d.ts +2 -0
  200. package/dist/types/index.d.ts +1 -1
  201. package/dist/types/lib/evaluation/constants.d.ts +11 -0
  202. package/dist/types/lib/evaluation/evaluation-engine.d.ts +0 -4
  203. package/dist/types/lib/evaluation/evaluation-service.d.ts +15 -0
  204. package/dist/types/lib/evaluation/evaluators/bleu/bleu-evaluator.d.ts +18 -0
  205. package/dist/types/lib/evaluation/evaluators/bleu/tests/bleu.test.d.ts +1 -0
  206. package/dist/types/lib/evaluation/evaluators/exact/exact.d.ts +2 -0
  207. package/dist/types/lib/evaluation/evaluators/rouge1-evaluator.d.ts +17 -0
  208. package/dist/types/lib/evaluation/evaluators/rougeL-evaluator.d.ts +2 -0
  209. package/dist/types/lib/evaluation/evaluators/rougeL-evaluator.test.d.ts +1 -0
  210. package/dist/types/lib/evaluation/evaluators/semantic/SemanticEvaluator.d.ts +6 -0
  211. package/dist/types/lib/evaluation/evaluators/semantic/evaluate-keywords.d.ts +7 -0
  212. package/dist/types/lib/evaluation/evaluators/semantic/index.d.ts +2 -0
  213. package/dist/types/lib/evaluation/evaluators/semantic/model-loader.d.ts +1 -0
  214. package/dist/types/lib/evaluation/evaluators/semantic/similarity-utils.d.ts +1 -0
  215. package/dist/types/lib/evaluation/evaluators/semantic/tests/evaluate-keywords.test.d.ts +1 -0
  216. package/dist/types/lib/evaluation/evaluators/semantic/text-utils.d.ts +1 -0
  217. package/dist/types/lib/evaluation/index.d.ts +2 -2
  218. package/dist/types/lib/evaluation/rouge1-evaluator.test.d.ts +1 -0
  219. package/dist/types/lib/evaluation/types.d.ts +19 -7
  220. package/dist/types/lib/file/file-download.d.ts +7 -0
  221. package/dist/types/lib/file/file-reader.d.ts +6 -0
  222. package/dist/types/lib/form/components/app-chips.d.ts +20 -0
  223. package/dist/types/lib/form/components/app-select.d.ts +7 -0
  224. package/dist/types/lib/form/components/app-textarea.d.ts +14 -0
  225. package/dist/types/lib/form/form-builder.d.ts +24 -0
  226. package/dist/types/lib/form/schema/base-input-field-config.d.ts +37 -0
  227. package/dist/types/lib/form/schema/form-control-config.d.ts +13 -0
  228. package/dist/types/lib/form/schema/index.d.ts +9 -0
  229. package/dist/types/lib/import-export/test-results-csv.d.ts +13 -0
  230. package/dist/types/lib/import-export/test-suite-exporter.d.ts +16 -0
  231. package/dist/types/lib/import-export/test-suite-importer.d.ts +12 -0
  232. package/dist/types/lib/rate-limited-fetcher/rate-limited-fetcher.d.ts +1 -1
  233. package/dist/types/lib/test-cases/test-case-factory.d.ts +12 -0
  234. package/dist/types/lib/test-cases/test-case-mutations.d.ts +9 -0
  235. package/dist/types/lib/ui/button/button.d.ts +13 -0
  236. package/dist/types/lib/ui/button/index.d.ts +2 -0
  237. package/dist/types/lib/ui/icon-button/icon-button.d.ts +11 -0
  238. package/dist/types/lib/ui/icon-button/index.d.ts +2 -0
  239. package/dist/types/services/adapters.d.ts +3 -0
  240. package/dist/types/services/models/gemini.d.ts +11 -0
  241. package/dist/types/stencil-public-runtime.d.ts +110 -6
  242. package/dist/types/types/evaluation.d.ts +9 -0
  243. package/dist/types/types/llm-test-runner.d.ts +22 -0
  244. package/package.json +30 -6
  245. package/dist/cjs/app-globals-CbbEbofA.js +0 -14
  246. package/dist/cjs/app-globals-CbbEbofA.js.map +0 -1
  247. package/dist/cjs/index-D-FySkoV.js +0 -1470
  248. package/dist/cjs/index-D-FySkoV.js.map +0 -1
  249. package/dist/cjs/llm-test-runner.cjs.entry.js +0 -9
  250. package/dist/cjs/llm-test-runner.entry.cjs.js.map +0 -1
  251. package/dist/components/p-CYUbsbxt.js +0 -1770
  252. package/dist/components/p-CYUbsbxt.js.map +0 -1
  253. package/dist/esm/app-globals-BOQOUavG.js +0 -12
  254. package/dist/esm/app-globals-BOQOUavG.js.map +0 -1
  255. package/dist/esm/index-cncubhtM.js +0 -1463
  256. package/dist/esm/index-cncubhtM.js.map +0 -1
  257. package/dist/esm/llm-test-runner.entry.js +0 -3
  258. package/dist/esm/llm-test-runner.entry.js.map +0 -1
  259. package/dist/llm-testrunner/llm-test-runner.entry.esm.js.map +0 -1
  260. package/dist/llm-testrunner/loader.esm.js.map +0 -1
  261. package/dist/llm-testrunner/p-BOQOUavG.js +0 -2
  262. package/dist/llm-testrunner/p-BOQOUavG.js.map +0 -1
  263. package/dist/llm-testrunner/p-cncubhtM.js +0 -3
  264. package/dist/llm-testrunner/p-cncubhtM.js.map +0 -1
  265. package/dist/llm-testrunner/p-f68fd660.entry.js +0 -2
  266. package/dist/llm-testrunner/p-f68fd660.entry.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rougeL-evaluator.js","sourceRoot":"","sources":["../../../../src/lib/evaluation/evaluators/rougeL-evaluator.ts"],"names":[],"mappings":"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","sourcesContent":["import * as rouge from 'js-rouge';\nimport { EvaluationApproachResult } from '../../../types/evaluation';\nimport { EvaluationRequest, EvaluationResult, KeywordMatch } from '../types';\nimport { DEFAULT_ROUGE_PASS_SCORE, EvaluationApproach } from '../constants';\n\nfunction evaluateKeyword(\n keyword: string,\n candidate: string,\n rougeThreshold: number,\n): KeywordMatch {\n let rougeLScore = 0;\n\n try {\n const trimmedKeyword = keyword.trim();\n if (trimmedKeyword.length > 0 && candidate.length > 0) {\n const referenceTokens = trimmedKeyword.toLowerCase().split(/\\s+/);\n const candidateTokens = candidate.toLowerCase().split(/\\s+/);\n\n if (\n referenceTokens.length === 1 &&\n candidateTokens.includes(referenceTokens[0])\n ) {\n rougeLScore = 1;\n } else {\n const lcsResult = rouge.lcs(candidateTokens, referenceTokens);\n const lcsLength =\n typeof lcsResult === 'number' ? lcsResult : (lcsResult?.length ?? 0);\n\n const recall =\n referenceTokens.length > 0 ? lcsLength / referenceTokens.length : 0;\n const precision =\n candidateTokens.length > 0 ? lcsLength / candidateTokens.length : 0;\n const denominator = precision + recall;\n\n const f1Score =\n denominator > 0 ? (2 * precision * recall) / denominator : 0;\n rougeLScore = f1Score;\n }\n } else {\n console.warn(\n `ROUGE-L not computed for keyword \"${keyword}\": Keyword or candidate missing.`,\n );\n }\n } catch (err) {\n console.error(`ROUGE-L computation failed for keyword \"${keyword}\":`, err);\n }\n\n const keywordPassed = rougeLScore >= rougeThreshold;\n\n const keywordApproachResult: EvaluationApproachResult = {\n score: rougeLScore,\n approachUsed: EvaluationApproach.ROUGE_L,\n };\n\n return {\n keyword,\n found: keywordPassed,\n evaluationApproachResult: keywordApproachResult,\n };\n}\n\nexport function performRougeLEvaluation(\n request: EvaluationRequest,\n): EvaluationResult {\n const { testCaseId, actualResponse, expectedOutcome, evaluationParameters } =\n request;\n\n // Split expectedOutcome by newlines, commas, and periods to create keywords array\n let expectedKeywords = expectedOutcome\n ? expectedOutcome\n .split(/[\\n,.]+/)\n .map(k => k.trim())\n .filter(k => k.length > 0)\n : [];\n\n // If no keywords after filtering (e.g., whitespace-only input), treat the original input as a single keyword\n if (expectedKeywords.length === 0 && expectedOutcome) {\n expectedKeywords = [expectedOutcome];\n }\n\n const candidate = (actualResponse || '').trim();\n const rougeThreshold =\n evaluationParameters.threshold ?? DEFAULT_ROUGE_PASS_SCORE;\n\n let keywordsPassed = 0;\n const totalKeywords = expectedKeywords.length;\n\n const keywordMatches: KeywordMatch[] = expectedKeywords.map(keyword => {\n const match = evaluateKeyword(keyword, candidate, rougeThreshold);\n if (match.found) keywordsPassed++;\n return match;\n });\n\n const overallPassed = keywordsPassed === totalKeywords;\n\n const overallApproachResult: EvaluationApproachResult = {\n score: totalKeywords > 0 ? keywordsPassed / totalKeywords : 1,\n approachUsed: EvaluationApproach.ROUGE_L,\n };\n\n return {\n testCaseId,\n passed: overallPassed,\n keywordMatches,\n timestamp: new Date().toISOString(),\n evaluationParameters: {\n ...evaluationParameters,\n threshold: rougeThreshold,\n },\n evaluationApproachResult: overallApproachResult,\n };\n}\n"]}
@@ -0,0 +1,326 @@
1
+ import { jest, describe, it, expect } from "@jest/globals";
2
+ import { performRougeLEvaluation } from "./rougeL-evaluator";
3
+ import { DEFAULT_ROUGE_PASS_SCORE, EvaluationApproach } from "../constants";
4
+ describe('performRougeLEvaluation', () => {
5
+ // Helper function to create a base request with optional overrides
6
+ const createRequest = (overrides = {}) => {
7
+ const defaults = {
8
+ testCaseId: 'test-001',
9
+ question: 'Test question',
10
+ expectedOutcome: 'keyword',
11
+ actualResponse: 'response with keyword',
12
+ evaluationParameters: {
13
+ approach: EvaluationApproach.ROUGE_L,
14
+ threshold: DEFAULT_ROUGE_PASS_SCORE,
15
+ },
16
+ };
17
+ return {
18
+ ...defaults,
19
+ ...overrides,
20
+ evaluationParameters: {
21
+ ...defaults.evaluationParameters,
22
+ ...overrides.evaluationParameters,
23
+ },
24
+ };
25
+ };
26
+ describe('basic functionality', () => {
27
+ it('should return a valid EvaluationResult structure', () => {
28
+ const request = createRequest({
29
+ actualResponse: 'AI stands for artificial intelligence',
30
+ expectedOutcome: 'artificial intelligence',
31
+ });
32
+ const result = performRougeLEvaluation(request);
33
+ expect(result).toMatchObject({
34
+ testCaseId: 'test-001',
35
+ passed: expect.any(Boolean),
36
+ keywordMatches: expect.any(Array),
37
+ timestamp: expect.any(String),
38
+ evaluationParameters: expect.any(Object),
39
+ evaluationApproachResult: expect.any(Object),
40
+ });
41
+ });
42
+ it('should use default threshold when not provided', () => {
43
+ const request = createRequest({
44
+ evaluationParameters: { approach: EvaluationApproach.ROUGE_L },
45
+ });
46
+ const result = performRougeLEvaluation(request);
47
+ expect(result.evaluationParameters.threshold).toBe(DEFAULT_ROUGE_PASS_SCORE);
48
+ });
49
+ it('should use provided threshold when specified', () => {
50
+ const customThreshold = 0.85;
51
+ const request = createRequest({
52
+ actualResponse: 'response',
53
+ evaluationParameters: {
54
+ approach: EvaluationApproach.ROUGE_L,
55
+ threshold: customThreshold,
56
+ },
57
+ });
58
+ const result = performRougeLEvaluation(request);
59
+ expect(result.evaluationParameters.threshold).toBe(customThreshold);
60
+ });
61
+ });
62
+ describe('single keyword evaluation', () => {
63
+ it('should pass when single-word keyword is found in candidate', () => {
64
+ const request = createRequest({
65
+ expectedOutcome: 'machine',
66
+ actualResponse: 'This is about machine learning',
67
+ });
68
+ const result = performRougeLEvaluation(request);
69
+ expect(result).toMatchObject({
70
+ passed: true,
71
+ keywordMatches: [
72
+ {
73
+ keyword: 'machine',
74
+ found: true,
75
+ evaluationApproachResult: {
76
+ score: 1,
77
+ approachUsed: EvaluationApproach.ROUGE_L,
78
+ },
79
+ },
80
+ ],
81
+ });
82
+ });
83
+ it('should fail when single-word keyword is not found in candidate', () => {
84
+ const request = createRequest({
85
+ expectedOutcome: 'quantum',
86
+ actualResponse: 'This is about machine learning',
87
+ });
88
+ const result = performRougeLEvaluation(request);
89
+ expect(result).toMatchObject({
90
+ passed: false,
91
+ keywordMatches: [
92
+ {
93
+ found: false,
94
+ evaluationApproachResult: {
95
+ score: 0,
96
+ },
97
+ },
98
+ ],
99
+ });
100
+ });
101
+ it('should calculate ROUGE-L score for multi-word keywords', () => {
102
+ const request = createRequest({
103
+ expectedOutcome: 'machine learning',
104
+ actualResponse: 'AI and machine learning are related',
105
+ evaluationParameters: {
106
+ approach: EvaluationApproach.ROUGE_L,
107
+ threshold: 0.5,
108
+ },
109
+ });
110
+ const result = performRougeLEvaluation(request);
111
+ expect(result).toMatchObject({
112
+ keywordMatches: [
113
+ {
114
+ found: true,
115
+ evaluationApproachResult: {
116
+ score: expect.closeTo(0.5),
117
+ approachUsed: EvaluationApproach.ROUGE_L,
118
+ },
119
+ },
120
+ ],
121
+ });
122
+ });
123
+ it('should handle LCS result as object with length property', () => {
124
+ const request = createRequest({
125
+ expectedOutcome: 'deep learning',
126
+ actualResponse: 'Deep learning is a subset of machine learning',
127
+ });
128
+ const result = performRougeLEvaluation(request);
129
+ expect(result.keywordMatches[0].evaluationApproachResult.score).toBeGreaterThan(0);
130
+ });
131
+ });
132
+ describe('multiple keywords evaluation', () => {
133
+ it('should pass when all keywords meet threshold', () => {
134
+ const request = createRequest({
135
+ expectedOutcome: 'machine\nlearning\nAI',
136
+ actualResponse: 'Machine learning and AI are transformative technologies',
137
+ });
138
+ const result = performRougeLEvaluation(request);
139
+ expect(result).toMatchObject({
140
+ passed: true,
141
+ evaluationApproachResult: {
142
+ score: 1,
143
+ approachUsed: EvaluationApproach.ROUGE_L,
144
+ },
145
+ });
146
+ expect(result.keywordMatches).toHaveLength(3);
147
+ expect(result.keywordMatches.every(match => match.found)).toBe(true);
148
+ });
149
+ it('should fail when not all keywords meet threshold', () => {
150
+ const request = createRequest({
151
+ expectedOutcome: 'machine\nquantum\nAI',
152
+ actualResponse: 'Machine learning and AI are transformative',
153
+ });
154
+ const result = performRougeLEvaluation(request);
155
+ expect(result.passed).toBe(false);
156
+ expect(result.keywordMatches).toHaveLength(3);
157
+ expect(result.keywordMatches.filter(match => match.found)).toHaveLength(2);
158
+ expect(result.evaluationApproachResult.score).toBeCloseTo(2 / 3);
159
+ });
160
+ it('should calculate overall score as ratio of passed keywords', () => {
161
+ const request = createRequest({
162
+ expectedOutcome: 'alpha\nbeta\ngamma\ndelta',
163
+ actualResponse: 'alpha and beta are here',
164
+ });
165
+ const result = performRougeLEvaluation(request);
166
+ expect(result).toMatchObject({
167
+ passed: false,
168
+ evaluationApproachResult: {
169
+ score: 0.5, // 2 out of 4
170
+ },
171
+ });
172
+ });
173
+ });
174
+ describe('edge cases', () => {
175
+ it('should handle empty keywords array', () => {
176
+ const request = createRequest({
177
+ expectedOutcome: '',
178
+ actualResponse: 'Some response',
179
+ });
180
+ const result = performRougeLEvaluation(request);
181
+ expect(result).toMatchObject({
182
+ passed: true,
183
+ keywordMatches: [],
184
+ evaluationApproachResult: {
185
+ score: 1,
186
+ },
187
+ });
188
+ });
189
+ it('should handle empty actual response', () => {
190
+ const request = createRequest({
191
+ expectedOutcome: 'machine',
192
+ actualResponse: '',
193
+ });
194
+ // Suppress expected warning
195
+ const consoleWarnSpy = jest
196
+ .spyOn(console, 'warn')
197
+ .mockImplementation(() => { });
198
+ const result = performRougeLEvaluation(request);
199
+ expect(result).toMatchObject({
200
+ passed: false,
201
+ keywordMatches: [
202
+ {
203
+ found: false,
204
+ evaluationApproachResult: {
205
+ score: 0,
206
+ },
207
+ },
208
+ ],
209
+ });
210
+ consoleWarnSpy.mockRestore();
211
+ });
212
+ it('should handle whitespace-only keyword', () => {
213
+ const request = createRequest({
214
+ expectedOutcome: ' ',
215
+ actualResponse: 'Some response',
216
+ });
217
+ // Suppress expected warning
218
+ const consoleWarnSpy = jest
219
+ .spyOn(console, 'warn')
220
+ .mockImplementation(() => { });
221
+ const result = performRougeLEvaluation(request);
222
+ expect(result.keywordMatches[0]).toMatchObject({
223
+ found: false,
224
+ evaluationApproachResult: {
225
+ score: 0,
226
+ },
227
+ });
228
+ consoleWarnSpy.mockRestore();
229
+ });
230
+ it('should handle null/undefined actualResponse gracefully', () => {
231
+ const request = createRequest({
232
+ expectedOutcome: 'machine',
233
+ actualResponse: null,
234
+ });
235
+ // Suppress expected warning
236
+ const consoleWarnSpy = jest
237
+ .spyOn(console, 'warn')
238
+ .mockImplementation(() => { });
239
+ const result = performRougeLEvaluation(request);
240
+ expect(result).toMatchObject({
241
+ passed: false,
242
+ keywordMatches: [
243
+ {
244
+ found: false,
245
+ },
246
+ ],
247
+ });
248
+ consoleWarnSpy.mockRestore();
249
+ });
250
+ });
251
+ describe('case insensitivity', () => {
252
+ it('should perform case-insensitive matching', () => {
253
+ const request = createRequest({
254
+ expectedOutcome: 'MACHINE',
255
+ actualResponse: 'machine learning is important',
256
+ });
257
+ const result = performRougeLEvaluation(request);
258
+ expect(result.keywordMatches[0]).toMatchObject({
259
+ found: true,
260
+ evaluationApproachResult: {
261
+ score: 1,
262
+ },
263
+ });
264
+ });
265
+ it('should match keywords with mixed case', () => {
266
+ const request = createRequest({
267
+ expectedOutcome: 'MaChInE LeArNiNg',
268
+ actualResponse: 'MACHINE LEARNING is a field of AI',
269
+ evaluationParameters: {
270
+ approach: EvaluationApproach.ROUGE_L,
271
+ threshold: 0.4,
272
+ }, // Lower threshold for real ROUGE-L behavior
273
+ });
274
+ const result = performRougeLEvaluation(request);
275
+ expect(result.keywordMatches[0]).toMatchObject({
276
+ found: true,
277
+ });
278
+ expect(result.keywordMatches[0].evaluationApproachResult.score).toBeGreaterThanOrEqual(0.4);
279
+ });
280
+ });
281
+ describe('ROUGE-L score calculation', () => {
282
+ it('should calculate correct F-score from precision and recall', () => {
283
+ const request = createRequest({
284
+ expectedOutcome: 'neural network',
285
+ actualResponse: 'A neural network processes data',
286
+ });
287
+ const result = performRougeLEvaluation(request);
288
+ // With actual ROUGE-L: both words 'neural' and 'network' are found
289
+ // LCS length = 2, reference length = 2, candidate length = 5
290
+ // recall = 2/2 = 1.0, precision = 2/5 = 0.4
291
+ // F-score = 2 * (1.0 * 0.4) / (1.0 + 0.4) ≈ 0.571
292
+ const expectedFScore = (2 * 1.0 * 0.4) / (1.0 + 0.4);
293
+ expect(result.keywordMatches[0].evaluationApproachResult.score).toBeCloseTo(expectedFScore, 2);
294
+ });
295
+ it('should handle partial matches', () => {
296
+ const request = createRequest({
297
+ expectedOutcome: 'artificial intelligence systems',
298
+ actualResponse: 'Artificial intelligence is growing',
299
+ evaluationParameters: {
300
+ approach: EvaluationApproach.ROUGE_L,
301
+ threshold: 0.5,
302
+ },
303
+ });
304
+ const result = performRougeLEvaluation(request);
305
+ // With actual ROUGE-L: 'artificial' and 'intelligence' are found, 'systems' is not
306
+ // LCS length = 2, reference length = 3, candidate length = 4
307
+ // recall = 2/3, precision = 2/4 = 0.5
308
+ const recall = 2 / 3;
309
+ const precision = 2 / 4;
310
+ const expectedFScore = (2 * precision * recall) / (precision + recall);
311
+ expect(result.keywordMatches[0].evaluationApproachResult.score).toBeCloseTo(expectedFScore, 2);
312
+ });
313
+ });
314
+ describe('timestamp', () => {
315
+ it('should include a valid ISO timestamp', () => {
316
+ const request = createRequest({
317
+ expectedOutcome: 'test',
318
+ actualResponse: 'test response',
319
+ });
320
+ const result = performRougeLEvaluation(request);
321
+ expect(result.timestamp).toBeDefined();
322
+ expect(new Date(result.timestamp).toISOString()).toBe(result.timestamp);
323
+ });
324
+ });
325
+ });
326
+ //# sourceMappingURL=rougeL-evaluator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rougeL-evaluator.test.js","sourceRoot":"","sources":["../../../../src/lib/evaluation/evaluators/rougeL-evaluator.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { jest, describe, it, expect } from '@jest/globals';\nimport { performRougeLEvaluation } from './rougeL-evaluator';\nimport { EvaluationRequest } from '../types';\nimport { DEFAULT_ROUGE_PASS_SCORE, EvaluationApproach } from '../constants';\n\ndescribe('performRougeLEvaluation', () => {\n // Helper function to create a base request with optional overrides\n const createRequest = (\n overrides: Partial<EvaluationRequest> = {},\n ): EvaluationRequest => {\n const defaults: EvaluationRequest = {\n testCaseId: 'test-001',\n question: 'Test question',\n expectedOutcome: 'keyword',\n actualResponse: 'response with keyword',\n evaluationParameters: {\n approach: EvaluationApproach.ROUGE_L,\n threshold: DEFAULT_ROUGE_PASS_SCORE,\n },\n };\n\n return {\n ...defaults,\n ...overrides,\n evaluationParameters: {\n ...defaults.evaluationParameters,\n ...overrides.evaluationParameters,\n },\n };\n };\n\n describe('basic functionality', () => {\n it('should return a valid EvaluationResult structure', () => {\n const request = createRequest({\n actualResponse: 'AI stands for artificial intelligence',\n expectedOutcome: 'artificial intelligence',\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result).toMatchObject({\n testCaseId: 'test-001',\n passed: expect.any(Boolean),\n keywordMatches: expect.any(Array),\n timestamp: expect.any(String),\n evaluationParameters: expect.any(Object),\n evaluationApproachResult: expect.any(Object),\n });\n });\n\n it('should use default threshold when not provided', () => {\n const request = createRequest({\n evaluationParameters: { approach: EvaluationApproach.ROUGE_L },\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result.evaluationParameters.threshold).toBe(\n DEFAULT_ROUGE_PASS_SCORE,\n );\n });\n\n it('should use provided threshold when specified', () => {\n const customThreshold = 0.85;\n const request = createRequest({\n actualResponse: 'response',\n evaluationParameters: {\n approach: EvaluationApproach.ROUGE_L,\n threshold: customThreshold,\n },\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result.evaluationParameters.threshold).toBe(customThreshold);\n });\n });\n\n describe('single keyword evaluation', () => {\n it('should pass when single-word keyword is found in candidate', () => {\n const request = createRequest({\n expectedOutcome: 'machine',\n actualResponse: 'This is about machine learning',\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result).toMatchObject({\n passed: true,\n keywordMatches: [\n {\n keyword: 'machine',\n found: true,\n evaluationApproachResult: {\n score: 1,\n approachUsed: EvaluationApproach.ROUGE_L,\n },\n },\n ],\n });\n });\n\n it('should fail when single-word keyword is not found in candidate', () => {\n const request = createRequest({\n expectedOutcome: 'quantum',\n actualResponse: 'This is about machine learning',\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result).toMatchObject({\n passed: false,\n keywordMatches: [\n {\n found: false,\n evaluationApproachResult: {\n score: 0,\n },\n },\n ],\n });\n });\n\n it('should calculate ROUGE-L score for multi-word keywords', () => {\n const request = createRequest({\n expectedOutcome: 'machine learning',\n actualResponse: 'AI and machine learning are related',\n evaluationParameters: {\n approach: EvaluationApproach.ROUGE_L,\n threshold: 0.5,\n },\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result).toMatchObject({\n keywordMatches: [\n {\n found: true,\n evaluationApproachResult: {\n score: expect.closeTo(0.5),\n approachUsed: EvaluationApproach.ROUGE_L,\n },\n },\n ],\n });\n });\n\n it('should handle LCS result as object with length property', () => {\n const request = createRequest({\n expectedOutcome: 'deep learning',\n actualResponse: 'Deep learning is a subset of machine learning',\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(\n result.keywordMatches[0].evaluationApproachResult.score,\n ).toBeGreaterThan(0);\n });\n });\n\n describe('multiple keywords evaluation', () => {\n it('should pass when all keywords meet threshold', () => {\n const request = createRequest({\n expectedOutcome: 'machine\\nlearning\\nAI',\n actualResponse:\n 'Machine learning and AI are transformative technologies',\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result).toMatchObject({\n passed: true,\n evaluationApproachResult: {\n score: 1,\n approachUsed: EvaluationApproach.ROUGE_L,\n },\n });\n expect(result.keywordMatches).toHaveLength(3);\n expect(result.keywordMatches.every(match => match.found)).toBe(true);\n });\n\n it('should fail when not all keywords meet threshold', () => {\n const request = createRequest({\n expectedOutcome: 'machine\\nquantum\\nAI',\n actualResponse: 'Machine learning and AI are transformative',\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result.passed).toBe(false);\n expect(result.keywordMatches).toHaveLength(3);\n expect(result.keywordMatches.filter(match => match.found)).toHaveLength(\n 2,\n );\n expect(result.evaluationApproachResult.score).toBeCloseTo(2 / 3);\n });\n\n it('should calculate overall score as ratio of passed keywords', () => {\n const request = createRequest({\n expectedOutcome: 'alpha\\nbeta\\ngamma\\ndelta',\n actualResponse: 'alpha and beta are here',\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result).toMatchObject({\n passed: false,\n evaluationApproachResult: {\n score: 0.5, // 2 out of 4\n },\n });\n });\n });\n\n describe('edge cases', () => {\n it('should handle empty keywords array', () => {\n const request = createRequest({\n expectedOutcome: '',\n actualResponse: 'Some response',\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result).toMatchObject({\n passed: true,\n keywordMatches: [],\n evaluationApproachResult: {\n score: 1,\n },\n });\n });\n\n it('should handle empty actual response', () => {\n const request = createRequest({\n expectedOutcome: 'machine',\n actualResponse: '',\n });\n\n // Suppress expected warning\n const consoleWarnSpy = jest\n .spyOn(console, 'warn')\n .mockImplementation(() => {});\n\n const result = performRougeLEvaluation(request);\n\n expect(result).toMatchObject({\n passed: false,\n keywordMatches: [\n {\n found: false,\n evaluationApproachResult: {\n score: 0,\n },\n },\n ],\n });\n\n consoleWarnSpy.mockRestore();\n });\n\n it('should handle whitespace-only keyword', () => {\n const request = createRequest({\n expectedOutcome: ' ',\n actualResponse: 'Some response',\n });\n\n // Suppress expected warning\n const consoleWarnSpy = jest\n .spyOn(console, 'warn')\n .mockImplementation(() => {});\n\n const result = performRougeLEvaluation(request);\n\n expect(result.keywordMatches[0]).toMatchObject({\n found: false,\n evaluationApproachResult: {\n score: 0,\n },\n });\n\n consoleWarnSpy.mockRestore();\n });\n\n it('should handle null/undefined actualResponse gracefully', () => {\n const request = createRequest({\n expectedOutcome: 'machine',\n actualResponse: null as unknown as string,\n });\n\n // Suppress expected warning\n const consoleWarnSpy = jest\n .spyOn(console, 'warn')\n .mockImplementation(() => {});\n\n const result = performRougeLEvaluation(request);\n\n expect(result).toMatchObject({\n passed: false,\n keywordMatches: [\n {\n found: false,\n },\n ],\n });\n\n consoleWarnSpy.mockRestore();\n });\n });\n\n describe('case insensitivity', () => {\n it('should perform case-insensitive matching', () => {\n const request = createRequest({\n expectedOutcome: 'MACHINE',\n actualResponse: 'machine learning is important',\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result.keywordMatches[0]).toMatchObject({\n found: true,\n evaluationApproachResult: {\n score: 1,\n },\n });\n });\n\n it('should match keywords with mixed case', () => {\n const request = createRequest({\n expectedOutcome: 'MaChInE LeArNiNg',\n actualResponse: 'MACHINE LEARNING is a field of AI',\n evaluationParameters: {\n approach: EvaluationApproach.ROUGE_L,\n threshold: 0.4,\n }, // Lower threshold for real ROUGE-L behavior\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result.keywordMatches[0]).toMatchObject({\n found: true,\n });\n expect(\n result.keywordMatches[0].evaluationApproachResult.score,\n ).toBeGreaterThanOrEqual(0.4);\n });\n });\n\n describe('ROUGE-L score calculation', () => {\n it('should calculate correct F-score from precision and recall', () => {\n const request = createRequest({\n expectedOutcome: 'neural network',\n actualResponse: 'A neural network processes data',\n });\n\n const result = performRougeLEvaluation(request);\n\n // With actual ROUGE-L: both words 'neural' and 'network' are found\n // LCS length = 2, reference length = 2, candidate length = 5\n // recall = 2/2 = 1.0, precision = 2/5 = 0.4\n // F-score = 2 * (1.0 * 0.4) / (1.0 + 0.4) ≈ 0.571\n const expectedFScore = (2 * 1.0 * 0.4) / (1.0 + 0.4);\n expect(\n result.keywordMatches[0].evaluationApproachResult.score,\n ).toBeCloseTo(expectedFScore, 2);\n });\n\n it('should handle partial matches', () => {\n const request = createRequest({\n expectedOutcome: 'artificial intelligence systems',\n actualResponse: 'Artificial intelligence is growing',\n evaluationParameters: {\n approach: EvaluationApproach.ROUGE_L,\n threshold: 0.5,\n },\n });\n\n const result = performRougeLEvaluation(request);\n\n // With actual ROUGE-L: 'artificial' and 'intelligence' are found, 'systems' is not\n // LCS length = 2, reference length = 3, candidate length = 4\n // recall = 2/3, precision = 2/4 = 0.5\n const recall = 2 / 3;\n const precision = 2 / 4;\n const expectedFScore = (2 * precision * recall) / (precision + recall);\n\n expect(\n result.keywordMatches[0].evaluationApproachResult.score,\n ).toBeCloseTo(expectedFScore, 2);\n });\n });\n\n describe('timestamp', () => {\n it('should include a valid ISO timestamp', () => {\n const request = createRequest({\n expectedOutcome: 'test',\n actualResponse: 'test response',\n });\n\n const result = performRougeLEvaluation(request);\n\n expect(result.timestamp).toBeDefined();\n expect(new Date(result.timestamp!).toISOString()).toBe(result.timestamp);\n });\n });\n});\n"]}
@@ -0,0 +1,69 @@
1
+ import { loadSemanticModel } from "./model-loader";
2
+ import { evaluateKeywordsSemantically } from "./evaluate-keywords";
3
+ import { DEFAULT_SEMANTIC_PASS_SCORE, EvaluationApproach, } from "../../constants";
4
+ export class SemanticEvaluator {
5
+ // TODO(LLM-39): Refactor SemanticEvaluator into a singleton pattern.
6
+ static extractor = null;
7
+ async initialize() {
8
+ if (SemanticEvaluator.extractor)
9
+ return;
10
+ try {
11
+ SemanticEvaluator.extractor = await loadSemanticModel();
12
+ }
13
+ catch (error) {
14
+ console.error('Failed to load semantic evaluation model:', error);
15
+ throw error;
16
+ }
17
+ }
18
+ async performEvaluation(request) {
19
+ try {
20
+ await this.initialize();
21
+ // Split expectedOutcome by newlines to create keywords array
22
+ const expectedKeywords = request.expectedOutcome
23
+ ? request.expectedOutcome
24
+ .split(/[\n,]+/)
25
+ .map(k => k.trim())
26
+ .filter(k => k.length > 0)
27
+ : [];
28
+ const keywordMatches = await evaluateKeywordsSemantically(SemanticEvaluator.extractor, request.actualResponse, expectedKeywords, DEFAULT_SEMANTIC_PASS_SCORE);
29
+ const totalItems = keywordMatches.length;
30
+ // calculate the overall score by averaging the score of the keyword matches
31
+ const keywordScore = keywordMatches.reduce((acc, curr) => acc + curr.evaluationApproachResult.score, 0);
32
+ const overallScore = totalItems > 0 ? keywordScore / totalItems : 0; // to avoid division by zero
33
+ const passed = keywordMatches.every(match => match.found);
34
+ const evaluationParameters = {
35
+ approach: EvaluationApproach.SEMANTIC,
36
+ threshold: DEFAULT_SEMANTIC_PASS_SCORE,
37
+ };
38
+ return {
39
+ testCaseId: request.testCaseId,
40
+ passed,
41
+ keywordMatches,
42
+ evaluationParameters,
43
+ evaluationApproachResult: {
44
+ score: overallScore,
45
+ approachUsed: EvaluationApproach.SEMANTIC,
46
+ },
47
+ timestamp: new Date().toISOString(),
48
+ };
49
+ }
50
+ catch (error) {
51
+ console.error('Failed to perform semantic evaluation:', error);
52
+ return {
53
+ testCaseId: request.testCaseId,
54
+ passed: false,
55
+ keywordMatches: [],
56
+ evaluationParameters: {
57
+ approach: EvaluationApproach.SEMANTIC,
58
+ threshold: DEFAULT_SEMANTIC_PASS_SCORE,
59
+ },
60
+ evaluationApproachResult: {
61
+ score: 0,
62
+ approachUsed: EvaluationApproach.SEMANTIC,
63
+ },
64
+ timestamp: new Date().toISOString(),
65
+ };
66
+ }
67
+ }
68
+ }
69
+ //# sourceMappingURL=SemanticEvaluator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SemanticEvaluator.js","sourceRoot":"","sources":["../../../../../src/lib/evaluation/evaluators/semantic/SemanticEvaluator.ts"],"names":[],"mappings":"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","sourcesContent":["import { EvaluationResult, EvaluationRequest } from '../../types';\nimport { loadSemanticModel } from './model-loader';\nimport { evaluateKeywordsSemantically } from './evaluate-keywords';\nimport { FeatureExtractionPipeline } from '@xenova/transformers';\nimport { EvaluationParameters } from '../../../../types/evaluation';\nimport {\n DEFAULT_SEMANTIC_PASS_SCORE,\n EvaluationApproach,\n} from '../../constants';\n\nexport class SemanticEvaluator {\n // TODO(LLM-39): Refactor SemanticEvaluator into a singleton pattern.\n private static extractor: FeatureExtractionPipeline = null;\n\n async initialize(): Promise<void> {\n if (SemanticEvaluator.extractor) return;\n try {\n SemanticEvaluator.extractor = await loadSemanticModel();\n } catch (error) {\n console.error('Failed to load semantic evaluation model:', error);\n throw error;\n }\n }\n\n async performEvaluation(\n request: EvaluationRequest,\n ): Promise<EvaluationResult> {\n try {\n await this.initialize();\n\n // Split expectedOutcome by newlines to create keywords array\n const expectedKeywords = request.expectedOutcome\n ? request.expectedOutcome\n .split(/[\\n,]+/)\n .map(k => k.trim())\n .filter(k => k.length > 0)\n : [];\n\n const keywordMatches = await evaluateKeywordsSemantically(\n SemanticEvaluator.extractor,\n request.actualResponse,\n expectedKeywords,\n DEFAULT_SEMANTIC_PASS_SCORE,\n );\n\n const totalItems = keywordMatches.length;\n // calculate the overall score by averaging the score of the keyword matches\n const keywordScore = keywordMatches.reduce(\n (acc, curr) => acc + curr.evaluationApproachResult.score,\n 0,\n );\n const overallScore = totalItems > 0 ? keywordScore / totalItems : 0; // to avoid division by zero\n const passed = keywordMatches.every(match => match.found);\n\n const evaluationParameters = {\n approach: EvaluationApproach.SEMANTIC,\n threshold: DEFAULT_SEMANTIC_PASS_SCORE,\n } as EvaluationParameters;\n\n return {\n testCaseId: request.testCaseId,\n passed,\n keywordMatches,\n evaluationParameters,\n evaluationApproachResult: {\n score: overallScore,\n approachUsed: EvaluationApproach.SEMANTIC,\n },\n timestamp: new Date().toISOString(),\n };\n } catch (error) {\n console.error('Failed to perform semantic evaluation:', error);\n return {\n testCaseId: request.testCaseId,\n passed: false,\n keywordMatches: [],\n evaluationParameters: {\n approach: EvaluationApproach.SEMANTIC,\n threshold: DEFAULT_SEMANTIC_PASS_SCORE,\n },\n evaluationApproachResult: {\n score: 0,\n approachUsed: EvaluationApproach.SEMANTIC,\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n}\n"]}
@@ -0,0 +1,56 @@
1
+ import { splitIntoWords } from "./text-utils";
2
+ import { cosineSimilarity } from "./similarity-utils";
3
+ import { EvaluationApproach } from "../../constants";
4
+ /**
5
+ * Evaluates whether each keyword is semantically present in the response text.
6
+ * Uses embeddings and cosine similarity instead of direct string matching.
7
+ */
8
+ export async function evaluateKeywordsSemantically(extractor, response, keywords, threshold) {
9
+ if (keywords.length === 0)
10
+ return [];
11
+ const words = splitIntoWords(response);
12
+ // Generate embeddings for both response words and keywords in parallel
13
+ const [wordsEmbeddings, keywordsEmbeddings] = await Promise.all([
14
+ Promise.all(words.map(async (word) => ({
15
+ word,
16
+ emb: await extractor(word, { pooling: 'mean', normalize: true }),
17
+ }))),
18
+ Promise.all(keywords.map(async (keyword) => ({
19
+ keyword,
20
+ emb: await extractor(keyword, { pooling: 'mean', normalize: true }),
21
+ }))),
22
+ ]);
23
+ // For each keyword, find the most semantically similar word in the response
24
+ const matches = keywordsEmbeddings.map(({ keyword, emb: keywordEmb }) => {
25
+ let bestSimilarity = 0;
26
+ try {
27
+ for (const { emb: wordEmb } of wordsEmbeddings) {
28
+ const similarity = cosineSimilarity(Array.from(keywordEmb.data), Array.from(wordEmb.data));
29
+ if (similarity > bestSimilarity)
30
+ bestSimilarity = similarity;
31
+ }
32
+ // Consider the keyword "found" if similarity exceeds the threshold
33
+ return {
34
+ keyword,
35
+ found: bestSimilarity >= threshold,
36
+ evaluationApproachResult: {
37
+ score: bestSimilarity,
38
+ approachUsed: EvaluationApproach.SEMANTIC,
39
+ },
40
+ };
41
+ }
42
+ catch (err) {
43
+ console.error(`Error evaluating "${keyword}":`, err);
44
+ return {
45
+ keyword,
46
+ found: false,
47
+ evaluationApproachResult: {
48
+ score: 0,
49
+ approachUsed: EvaluationApproach.SEMANTIC,
50
+ },
51
+ };
52
+ }
53
+ });
54
+ return matches;
55
+ }
56
+ //# sourceMappingURL=evaluate-keywords.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate-keywords.js","sourceRoot":"","sources":["../../../../../src/lib/evaluation/evaluators/semantic/evaluate-keywords.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,SAAoC,EACpC,QAAgB,EAChB,QAAkB,EAClB,SAAiB;IAEjB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAErC,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAEvC,uEAAuE;IACvE,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC9D,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,KAAK,EAAC,IAAI,EAAC,EAAE,CAAC,CAAC;YACvB,IAAI;YACJ,GAAG,EAAE,MAAM,SAAS,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;SACjE,CAAC,CAAC,CACJ;QAED,OAAO,CAAC,GAAG,CACT,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAC,OAAO,EAAC,EAAE,CAAC,CAAC;YAC7B,OAAO;YACP,GAAG,EAAE,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;SACpE,CAAC,CAAC,CACJ;KACF,CAAC,CAAC;IAEH,4EAA4E;IAC5E,MAAM,OAAO,GAAmB,kBAAkB,CAAC,GAAG,CACpD,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE;QAC/B,IAAI,cAAc,GAAG,CAAC,CAAC;QAEvB,IAAI,CAAC;YACH,KAAK,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,eAAe,EAAE,CAAC;gBAC/C,MAAM,UAAU,GAAG,gBAAgB,CACjC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAC3B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CACzB,CAAC;gBACF,IAAI,UAAU,GAAG,cAAc;oBAAE,cAAc,GAAG,UAAU,CAAC;YAC/D,CAAC;YAED,mEAAmE;YACnE,OAAO;gBACL,OAAO;gBACP,KAAK,EAAE,cAAc,IAAI,SAAS;gBAClC,wBAAwB,EAAE;oBACxB,KAAK,EAAE,cAAc;oBACrB,YAAY,EAAE,kBAAkB,CAAC,QAAQ;iBAC1C;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,qBAAqB,OAAO,IAAI,EAAE,GAAG,CAAC,CAAC;YACrD,OAAO;gBACL,OAAO;gBACP,KAAK,EAAE,KAAK;gBACZ,wBAAwB,EAAE;oBACxB,KAAK,EAAE,CAAC;oBACR,YAAY,EAAE,kBAAkB,CAAC,QAAQ;iBAC1C;aACF,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import { KeywordMatch } from '../../types';\nimport { splitIntoWords } from './text-utils';\nimport { cosineSimilarity } from './similarity-utils';\nimport { FeatureExtractionPipeline } from '@xenova/transformers';\nimport { EvaluationApproach } from '../../constants';\n\n/**\n * Evaluates whether each keyword is semantically present in the response text.\n * Uses embeddings and cosine similarity instead of direct string matching.\n */\nexport async function evaluateKeywordsSemantically(\n extractor: FeatureExtractionPipeline,\n response: string,\n keywords: string[],\n threshold: number,\n): Promise<KeywordMatch[]> {\n if (keywords.length === 0) return [];\n\n const words = splitIntoWords(response);\n\n // Generate embeddings for both response words and keywords in parallel\n const [wordsEmbeddings, keywordsEmbeddings] = await Promise.all([\n Promise.all(\n words.map(async word => ({\n word,\n emb: await extractor(word, { pooling: 'mean', normalize: true }),\n })),\n ),\n\n Promise.all(\n keywords.map(async keyword => ({\n keyword,\n emb: await extractor(keyword, { pooling: 'mean', normalize: true }),\n })),\n ),\n ]);\n\n // For each keyword, find the most semantically similar word in the response\n const matches: KeywordMatch[] = keywordsEmbeddings.map(\n ({ keyword, emb: keywordEmb }) => {\n let bestSimilarity = 0;\n\n try {\n for (const { emb: wordEmb } of wordsEmbeddings) {\n const similarity = cosineSimilarity(\n Array.from(keywordEmb.data),\n Array.from(wordEmb.data),\n );\n if (similarity > bestSimilarity) bestSimilarity = similarity;\n }\n\n // Consider the keyword \"found\" if similarity exceeds the threshold\n return {\n keyword,\n found: bestSimilarity >= threshold,\n evaluationApproachResult: {\n score: bestSimilarity,\n approachUsed: EvaluationApproach.SEMANTIC,\n },\n };\n } catch (err) {\n console.error(`Error evaluating \"${keyword}\":`, err);\n return {\n keyword,\n found: false,\n evaluationApproachResult: {\n score: 0,\n approachUsed: EvaluationApproach.SEMANTIC,\n },\n };\n }\n },\n );\n\n return matches;\n}"]}
@@ -0,0 +1,7 @@
1
+ import { SemanticEvaluator } from "./SemanticEvaluator";
2
+ const semanticEvaluator = new SemanticEvaluator();
3
+ export async function performSemanticEvaluation(request) {
4
+ await semanticEvaluator.initialize();
5
+ return semanticEvaluator.performEvaluation(request);
6
+ }
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/lib/evaluation/evaluators/semantic/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,EAAE,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,OAA0B;IAE1B,MAAM,iBAAiB,CAAC,UAAU,EAAE,CAAC;IACrC,OAAO,iBAAiB,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;AACtD,CAAC","sourcesContent":["import { SemanticEvaluator } from './SemanticEvaluator';\nimport { EvaluationRequest, EvaluationResult } from '../../types';\n\nconst semanticEvaluator = new SemanticEvaluator();\n\nexport async function performSemanticEvaluation(\n request: EvaluationRequest,\n): Promise<EvaluationResult> {\n await semanticEvaluator.initialize();\n return semanticEvaluator.performEvaluation(request);\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import { env, pipeline } from "@xenova/transformers";
2
+ // Force remote loads so Vite dev server does not serve index.html in place of model metadata
3
+ // TODO: LLM-52 Revisit this workaround
4
+ env.useBrowserCache = false;
5
+ env.allowLocalModels = false;
6
+ // Loads a semantic feature extraction model to generate embeddings
7
+ export async function loadSemanticModel() {
8
+ try {
9
+ const extractor = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2', {
10
+ quantized: true, // use quantized model to reduce memory usage
11
+ });
12
+ return extractor;
13
+ }
14
+ catch (error) {
15
+ console.error('Failed to load semantic evaluation model:', error);
16
+ throw error;
17
+ }
18
+ }
19
+ //# sourceMappingURL=model-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-loader.js","sourceRoot":"","sources":["../../../../../src/lib/evaluation/evaluators/semantic/model-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,6FAA6F;AAC7F,uCAAuC;AACvC,GAAG,CAAC,eAAe,GAAG,KAAK,CAAC;AAC5B,GAAG,CAAC,gBAAgB,GAAG,KAAK,CAAC;AAE7B,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,QAAQ,CAC9B,oBAAoB,EACpB,yBAAyB,EACzB;YACE,SAAS,EAAE,IAAI,EAAE,6CAA6C;SAC/D,CACF,CAAC;QACF,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,KAAK,CAAC,CAAC;QAClE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["import { env, pipeline } from '@xenova/transformers';\n\n// Force remote loads so Vite dev server does not serve index.html in place of model metadata\n// TODO: LLM-52 Revisit this workaround\nenv.useBrowserCache = false;\nenv.allowLocalModels = false;\n\n// Loads a semantic feature extraction model to generate embeddings\nexport async function loadSemanticModel() {\n try {\n const extractor = await pipeline(\n 'feature-extraction',\n 'Xenova/all-MiniLM-L6-v2',\n {\n quantized: true, // use quantized model to reduce memory usage\n },\n );\n return extractor;\n } catch (error) {\n console.error('Failed to load semantic evaluation model:', error);\n throw error;\n }\n}\n"]}
@@ -0,0 +1,16 @@
1
+ // Computes cosine similarity between two numeric vectors.
2
+ // Returns a value between -1 and 1 indicating similarity.(1 means identical, 0 means completely different and negative values mean opposite directions)
3
+ export function cosineSimilarity(vecA, vecB) {
4
+ if (vecA.length !== vecB.length)
5
+ throw new Error('Vectors must have the same length');
6
+ let dot = 0, normA = 0, normB = 0;
7
+ for (let i = 0; i < vecA.length; i++) {
8
+ dot += vecA[i] * vecB[i];
9
+ normA += vecA[i] ** 2;
10
+ normB += vecB[i] ** 2;
11
+ }
12
+ if (normA === 0 || normB === 0)
13
+ return 0;
14
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
15
+ }
16
+ //# sourceMappingURL=similarity-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"similarity-utils.js","sourceRoot":"","sources":["../../../../../src/lib/evaluation/evaluators/semantic/similarity-utils.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,wJAAwJ;AACxJ,MAAM,UAAU,gBAAgB,CAAC,IAAc,EAAE,IAAc;IAC7D,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAC7B,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAEvD,IAAI,GAAG,GAAG,CAAC,EACT,KAAK,GAAG,CAAC,EACT,KAAK,GAAG,CAAC,CAAC;IAEZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtB,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IACD,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACzC,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AACrD,CAAC","sourcesContent":["// Computes cosine similarity between two numeric vectors.\n// Returns a value between -1 and 1 indicating similarity.(1 means identical, 0 means completely different and negative values mean opposite directions)\nexport function cosineSimilarity(vecA: number[], vecB: number[]): number {\n if (vecA.length !== vecB.length)\n throw new Error('Vectors must have the same length');\n\n let dot = 0,\n normA = 0,\n normB = 0;\n\n for (let i = 0; i < vecA.length; i++) {\n dot += vecA[i] * vecB[i];\n normA += vecA[i] ** 2;\n normB += vecB[i] ** 2;\n }\n if (normA === 0 || normB === 0) return 0;\n return dot / (Math.sqrt(normA) * Math.sqrt(normB));\n}\n"]}